teamUser.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. <template>
  2. <div class="full_page">
  3. <div class="content">
  4. <div class="top adfac">
  5. <div class="t_l adfac">
  6. <img src="@/assets/images/agent/arrow_left.png" @click="handleBack">
  7. <div class="spans">
  8. <span>项目管理</span>
  9. <span class="last">&nbsp;/&nbsp;成员管理</span>
  10. </div>
  11. </div>
  12. </div>
  13. <div class="c_bottom adf">
  14. <div class="cb_l">
  15. <el-select v-model="programid" placeholder="请选择项目" @change="handleChange">
  16. <el-option v-for="item in useAgentStore().companyList" :key="item.id" :label="item.enterpriseName" :value="item.id"></el-option>
  17. </el-select>
  18. <div class="cbl_list">
  19. <div class="cbl_item" :class="{'active':tidx===index}" v-for="(item, index) in teamList" :key="index" @click="handleTeam(item,index)">
  20. {{ item.teamName }}
  21. </div>
  22. </div>
  23. </div>
  24. <div class="cb_r">
  25. <div class="cbr_top adfacjb">
  26. <div class="ct_l">团队成员</div>
  27. <div class="ct_r adfac">
  28. <div class="tr_btn" @click="handleDownloadExcel" v-hasPermi="['core:project:downloadExcel']" v-if="programid&&teamid">模板下载</div>
  29. <el-upload v-if="programid&&teamid"
  30. :action="uploadUrl"
  31. :data="{enterpriseId: programid, teamId: teamid}"
  32. :headers="uploadHeaders"
  33. :before-upload="handleBeforeUpload"
  34. :on-success="handleSuccess"
  35. >
  36. <div class="tr_btn" v-hasPermi="['sys:user:export']">导入成员</div>
  37. </el-upload>
  38. <div class="tr_btn" @click="handleAddUser" v-hasPermi="['sys:user:add']">添加成员</div>
  39. </div>
  40. </div>
  41. <el-table :data="userList" border cell-class-name="vertical-top-cell" v-loading="loading" empty-text="暂无人员" max-height="578px" style="margin-top: 16px;">
  42. <el-table-column label="序号" width="50">
  43. <template #default="scope">
  44. {{ scope.$index + 1 }}
  45. </template>
  46. </el-table-column>
  47. <el-table-column label="姓名" prop="realName"></el-table-column>
  48. <el-table-column label="性别" prop="gender">
  49. <template #default="{ row }">{{ genderCfg[row.gender]||'未知' }}</template>
  50. </el-table-column>
  51. <el-table-column label="类型" prop="category">
  52. <template #default="{ row }">{{ UserCategory.find(u=>u.value===row.category)?.label ||'未知' }}</template>
  53. </el-table-column>
  54. <el-table-column label="人物简介" prop="introduction" width="400" show-overflow-tooltip></el-table-column>
  55. <el-table-column label="人物故事" prop="userStory" width="400" show-overflow-tooltip></el-table-column>
  56. <el-table-column label="操作" width="150">
  57. <template #default="scope">
  58. <el-button link type="text" size="mini" @click="handleEdit(scope.row)" v-hasPermi="['sys:user:update']">编辑</el-button>
  59. <el-button link type="text" size="mini" @click="handleDelete(scope.row)" v-hasPermi="['sys:user:delete']">删除</el-button>
  60. </template>
  61. </el-table-column>
  62. </el-table>
  63. <el-row style="display: flex;justify-content: center;">
  64. <el-pagination
  65. @size-change="handleSizeChange"
  66. @current-change="handleCurrentChange"
  67. :current-page="queryParams.page"
  68. :page-sizes="[5, 10, 20, 50]"
  69. :page-size="10"
  70. layout="total, sizes, prev, pager, next, jumper"
  71. :total="total"
  72. v-show="total > 0">
  73. </el-pagination>
  74. </el-row>
  75. </div>
  76. </div>
  77. </div>
  78. <el-drawer :title="userTitle" :visible.sync="userShow" append-to-body size="60%" @close="userShow=false">
  79. <el-form ref="userRef" :model="userForm" :rules="userRules" label-width="100px" style="width: 90%;margin: 0 auto;">
  80. <el-row>
  81. <el-col :span="12">
  82. <el-form-item label="姓名" prop="realName">
  83. <el-input v-model="userForm.realName" placeholder="请输入姓名" />
  84. </el-form-item>
  85. </el-col>
  86. <el-col :span="12">
  87. <el-form-item label="性别" prop="gender">
  88. <el-select v-model="userForm.gender" placeholder="请选择性别" style="width: 100%;">
  89. <el-option label="男" :value="0"></el-option>
  90. <el-option label="女" :value="1"></el-option>
  91. <el-option label="保密" :value="2"></el-option>
  92. </el-select>
  93. </el-form-item>
  94. </el-col>
  95. </el-row>
  96. <el-row>
  97. <el-col :span="12">
  98. <el-form-item label="类型" prop="category">
  99. <el-select v-model="userForm.category" placeholder="请选择类型" style="width: 100%;">
  100. <el-option v-for="item in UserCategory" :label="item.label" :value="item.value"></el-option>
  101. </el-select>
  102. </el-form-item>
  103. </el-col>
  104. <el-col :span="12">
  105. <el-form-item label="手机号码" prop="mobile">
  106. <el-input type="number" v-model="userForm.mobile" placeholder="请输入手机号码" />
  107. </el-form-item>
  108. </el-col>
  109. </el-row>
  110. <el-row>
  111. <el-col :span="12">
  112. <el-form-item label="所属部门" prop="dept">
  113. <el-input v-model="userForm.dept" placeholder="请输入所属部门" />
  114. </el-form-item>
  115. </el-col>
  116. <el-col :span="12">
  117. <el-form-item label="教育程度" prop="education">
  118. <el-input v-model="userForm.education" placeholder="请输入教育程度" />
  119. </el-form-item>
  120. </el-col>
  121. </el-row>
  122. <el-row>
  123. <el-col :span="12">
  124. <el-form-item label="分工" prop="divisionOfLabour">
  125. <el-input v-model="userForm.divisionOfLabour" placeholder="请输入分工" />
  126. </el-form-item>
  127. </el-col>
  128. <el-col :span="12">
  129. <el-form-item label="职位" prop="post">
  130. <el-input v-model="userForm.post" placeholder="请输入职位" />
  131. </el-form-item>
  132. </el-col>
  133. </el-row>
  134. <el-row>
  135. <el-col :span="12">
  136. <el-form-item label="级别" prop="level">
  137. <el-input v-model="userForm.level" placeholder="请输入级别" />
  138. </el-form-item>
  139. </el-col>
  140. </el-row>
  141. <el-row>
  142. <el-col :span="24">
  143. <el-form-item label="人物简介" prop="introduction">
  144. <el-input type="textarea" :rows="2" v-model="userForm.introduction" placeholder="请输入人物简介" />
  145. </el-form-item>
  146. </el-col>
  147. </el-row>
  148. <el-row>
  149. <el-col :span="24">
  150. <el-form-item label="人物故事" prop="userStory">
  151. <el-input type="textarea" :rows="2" v-model="userForm.userStory" placeholder="请输入人物故事" />
  152. </el-form-item>
  153. </el-col>
  154. </el-row>
  155. </el-form>
  156. <div class="demo-drawer__footer" style="display: flex;justify-content: end;">
  157. <el-button :loading="buttonLoading2" type="primary" @click="submitForm2" v-hasPermi="['sys:user:save']">保 存</el-button>
  158. <el-button @click="cancel2" style="margin-right: 5%;">取 消</el-button>
  159. </div>
  160. </el-drawer>
  161. </div>
  162. </template>
  163. <script setup name="">
  164. import Cookies from "js-cookie";
  165. import { ref, getCurrentInstance, onMounted } from 'vue'
  166. const { proxy } = getCurrentInstance();
  167. import {useAgentStore} from "@/store_v3/modules/agent";
  168. useAgentStore().getCompanyData();
  169. import {
  170. getCoachList,
  171. updateCoach,
  172. getCoachInfo,
  173. deleteCoach,
  174. addCoach,
  175. getTeamListById,
  176. } from '@/api/agent/index.js'
  177. const uploadUrl = `${window.SITE_CONFIG["apiURL"]}/sys/user/import`
  178. const uploadHeaders = {token:Cookies.get("token")};
  179. const { companyIndustry, staffSize, UserCategory} = proxy.useDict("companyIndustry", "staffSize", "UserCategory");
  180. const programid = ref('')
  181. const teamid = ref('')
  182. const teamList = ref([]);
  183. const tidx = ref('')
  184. const userList = ref([]);
  185. const total = ref(0);
  186. const loading = ref(false);
  187. const genderCfg = {
  188. '0': '男',
  189. '1': '女',
  190. '2': '保密',
  191. }
  192. const userTitle = ref('添加成员信息');
  193. const userShow = ref(false);
  194. const buttonLoading2 = ref(false);
  195. const userRef = ref(null);
  196. const queryParams = ref({
  197. page:1,
  198. limit:10,
  199. teamId:'',
  200. realName:'',
  201. })
  202. const userForm = ref({
  203. id:'',
  204. enterpriseId:'',
  205. teamId:'',
  206. realName:'',
  207. username:'',
  208. gender:'',
  209. birthdate:'',
  210. mobile:'',
  211. dept:'',
  212. education:'',
  213. divisionOfLabour:'',
  214. post:'',
  215. level:'',
  216. userType:2,
  217. category:'',
  218. introduction:'',
  219. userStory:''
  220. });
  221. const userRules = ref({
  222. realName: [
  223. { required: true, message: '请输入姓名', trigger: 'blur' }
  224. ],
  225. gender: [
  226. { required: true, message: '请选择性别', trigger: 'change' }
  227. ],
  228. birthdate: [
  229. { required: true, message: '请选择出生日期', trigger: 'change' }
  230. ],
  231. mobile: [
  232. { required: true, message: '请输入手机号码', trigger: 'blur' },
  233. { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }
  234. ],
  235. dept: [
  236. { required: true, message: '请输入所属部门', trigger: 'blur' }
  237. ],
  238. education: [
  239. { required: true, message: '请输入教育程度', trigger: 'blur' }
  240. ],
  241. divisionOfLabour: [
  242. { required: true, message: '请输入分工', trigger: 'blur' }
  243. ],
  244. post: [
  245. { required: true, message: '请输入职位', trigger: 'blur' }
  246. ],
  247. level:[
  248. { required: true, message: '请输入级别', trigger: 'blur' }
  249. ],
  250. category:[
  251. { required: true, message: '请选择成员类型', trigger: 'change' }
  252. ],
  253. introduction:[
  254. { required: true, message: '请输入成员简介', trigger: 'blur' }
  255. ],
  256. userStory:[
  257. { required: true, message: '请输入成员故事', trigger: 'blur' }
  258. ]
  259. });
  260. const handleBack = () => {
  261. proxy.$router.back()
  262. }
  263. const handleBeforeUpload = (e,node,data)=>{
  264. let type = e.name.split('.')[e.name.split('.').length-1];
  265. let isExcel = e.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
  266. if(type.toLowerCase() !== 'xlsx' && type.toLowerCase() !== 'xls' && !isExcel){
  267. proxy?.$modal.msgError('请上传xlsx或xls格式的Excel文件!');
  268. return false;
  269. }
  270. setTimeout(()=>{
  271. getUserList();
  272. },1000)
  273. }
  274. const handleSuccess = (e,node,data)=>{
  275. if(e.code===0){
  276. proxy?.$modal.msgSuccess('导入成功!'+e.msg);
  277. }else {
  278. proxy?.$modal.msgError('导入失败!'+e.msg);
  279. }
  280. }
  281. const handleChange = (val) => {
  282. programid.value = val;
  283. getTeamListByCompanyId();
  284. }
  285. const getTeamListByCompanyId = ()=> {
  286. getTeamListById({enterpriseId: programid.value}).then(res=>{
  287. if(res.code!==0) proxy.$message.error(res.msg);
  288. teamList.value = res.data;
  289. if(teamid.value){
  290. let idx = teamList.value.findIndex(item=>{
  291. return item.id === teamid.value;
  292. });
  293. if(idx>-1){
  294. tidx.value = idx;
  295. }
  296. }
  297. })
  298. }
  299. const handleTeam = (item, index) => {
  300. tidx.value = index;
  301. teamid.value = item.id;
  302. getUserList();
  303. }
  304. const getUserList = async () => {
  305. queryParams.value.teamId = teamid.value;
  306. let query = {...queryParams.value};
  307. loading.value = true;
  308. const res = await getCoachList(query);
  309. userList.value = res.data.list;
  310. total.value = res.data.total;
  311. loading.value = false;
  312. }
  313. const handleSizeChange = (e)=>{
  314. queryParams.value.limit = e;
  315. getUserList();
  316. }
  317. const handleCurrentChange = (e)=>{
  318. queryParams.value.page = e;
  319. getUserList();
  320. }
  321. const handleDownloadExcel = () => {
  322. window.location.href = `${window.SITE_CONFIG["apiURL"]}/sys/user/download?token=${Cookies.get("token")}`;
  323. }
  324. const handleAddUser = () => {
  325. if(!teamid.value) return proxy.$message.error('请先选择团队');
  326. reset();
  327. userTitle.value = "新增成员详情";
  328. userShow.value = true;
  329. }
  330. const reset = () => {
  331. userForm.value = {
  332. id:'',
  333. enterpriseId:'',
  334. teamId:'',
  335. realName:'',
  336. username:'',
  337. gender:'',
  338. birthdate:'',
  339. mobile:'',
  340. dept:'',
  341. education:'',
  342. divisionOfLabour:'',
  343. post:'',
  344. level:'',
  345. userType:2,
  346. category:'',
  347. introduction:'',
  348. userStory:''
  349. };
  350. proxy.resetForm("userRef");
  351. }
  352. const submitForm2 = () => {
  353. proxy.$refs.userRef.validate((valid) => {
  354. if (valid) {
  355. buttonLoading2.value = true;
  356. userForm.value.username = userForm.value.mobile;
  357. userForm.value.enterpriseId = programid.value;
  358. userForm.value.teamId = teamid.value;
  359. if(userForm.value.id){
  360. updateCoach(userForm.value).then((res)=>{
  361. if(res.code!==0) return proxy.$message.error(res.msg);
  362. buttonLoading2.value = false;
  363. userShow.value = false;
  364. getUserList();
  365. })
  366. }else{
  367. addCoach(userForm.value).then((res)=>{
  368. if(res.code!==0) return proxy.$message.error(res.msg);
  369. buttonLoading2.value = false;
  370. userShow.value = false;
  371. getUserList();
  372. })
  373. }
  374. } else {
  375. return false;
  376. }
  377. });
  378. }
  379. const cancel2 = () => {
  380. userShow.value = false;
  381. reset();
  382. }
  383. const handleEdit = (row) => {
  384. reset();
  385. getCoachInfo(row.id).then(response => {
  386. userForm.value = {...userForm.value,...response.data};
  387. userTitle.value = "编辑成员详情";
  388. userShow.value = true;
  389. });
  390. }
  391. async function handleDelete(row) {
  392. await proxy?.$modal.confirm('确认删除成员【' + row.realName + '】吗?').finally(() => loading.value = false);
  393. let res = await deleteCoach([row.id]);
  394. if (res.code === 0) proxy?.$modal.msgSuccess("删除成功");
  395. else return proxy?.$modal.msgError(res.msg);
  396. await getUserList();
  397. }
  398. onMounted(()=>{
  399. programid.value = proxy.$route?.query?.enterpriseId;
  400. teamid.value = proxy.$route?.query?.teamId;
  401. getTeamListByCompanyId();
  402. getUserList();
  403. })
  404. </script>
  405. <style scoped lang="scss">
  406. .full_page{
  407. background: #FAFAFA;
  408. .content{
  409. width: calc(100% - 24px);
  410. height: calc(100vh - 30px);
  411. margin: 12px;
  412. background: #FFFFFF;
  413. border-radius: 6px 6px 0px 0px;
  414. border: 1px solid #F3F4F6;
  415. position: relative;
  416. display: flex;
  417. flex-direction: column;
  418. .top{
  419. width: 100%;
  420. height: 64px;
  421. background: #FFFFFF;
  422. border-bottom: 1px solid #F3F4F6;
  423. .t_l{
  424. width: 310px;
  425. img{
  426. width: 36px;
  427. height: 36px;
  428. margin-left: 16px;
  429. cursor: pointer;
  430. }
  431. .spans{
  432. display: flex;
  433. align-items: center;
  434. margin-left: 23px;
  435. span{
  436. font-family: PingFangSC, PingFang SC;
  437. font-weight: 400;
  438. font-size: 14px;
  439. color: #6B7280;
  440. line-height: 14px;
  441. &.last{
  442. color: #111111;
  443. }
  444. }
  445. }
  446. }
  447. }
  448. .c_bottom{
  449. width: 100%;
  450. flex: 1;
  451. display: flex;
  452. .cb_l{
  453. width: 280px;
  454. height: 100%;
  455. border-right: 1px solid #E5E7EB;
  456. padding: 24px 16px;
  457. box-sizing: border-box;
  458. .cbl_list{
  459. margin-top: 24px;
  460. width: 100%;
  461. flex: 1;
  462. overflow-y: auto;
  463. .cbl_item{
  464. width: 100%;
  465. padding: 24px 16px;
  466. box-sizing: border-box;
  467. box-shadow: inset 0px -1px 0px 0px #EFEFEF;
  468. font-family: PingFangSC, PingFang SC;
  469. font-weight: 400;
  470. font-size: 14px;
  471. color: #252525;
  472. line-height: 14px;
  473. overflow: hidden;
  474. text-overflow: ellipsis;
  475. white-space: nowrap;
  476. cursor: pointer;
  477. &.active,&:hover{
  478. background: rgba(118,30,106,0.06);
  479. font-weight: bold;
  480. color: #761E6A;
  481. }
  482. }
  483. }
  484. }
  485. .cb_r{
  486. padding: 30px 16px 24px;
  487. flex: 1;
  488. box-sizing: border-box;
  489. .ct_l{
  490. font-family: PingFang-SC, PingFang-SC;
  491. font-weight: bold;
  492. font-size: 16px;
  493. color: #252525;
  494. line-height: 16px;
  495. }
  496. .ct_r{
  497. .tr_btn{
  498. width: 92px;
  499. height: 36px;
  500. border-radius: 6px;
  501. border: 1px solid #C1C7D2;
  502. margin-left: 20px;
  503. cursor: pointer;
  504. font-family: PingFangSC, PingFang SC;
  505. font-weight: 400;
  506. font-size: 14px;
  507. color: #111111;
  508. display: flex;
  509. align-items: center;
  510. justify-content: center;
  511. &:last-child{
  512. background: #761E6A;
  513. color: #FFFFFF;
  514. }
  515. }
  516. }
  517. }
  518. }
  519. }
  520. }
  521. </style>