teamUser.vue 26 KB

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