UserModal.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. <template>
  2. <a-drawer
  3. :title="title"
  4. :maskClosable="true"
  5. :width="drawerWidth"
  6. placement="right"
  7. :closable="true"
  8. @close="handleCancel"
  9. :visible="visible"
  10. >
  11. <template slot="title">
  12. <div style="width: 100%">
  13. <span>{{ title }}</span>
  14. <span style="display: inline-block; width: calc(100% - 51px); padding-right: 10px; text-align: right">
  15. <a-button @click="toggleScreen" icon="appstore" style="height: 20px; width: 20px; border: 0px"></a-button>
  16. </span>
  17. </div>
  18. </template>
  19. <a-spin :spinning="confirmLoading">
  20. <a-form :form="form">
  21. <a-form-item label="用户账号" :labelCol="labelCol" :wrapperCol="wrapperCol">
  22. <a-input placeholder="请输入用户账号" v-decorator="['username', validatorRules.username]" :disabled="disableSubmit"/>
  23. </a-form-item>
  24. <template v-if="!model.id">
  25. <a-form-item label="登录密码" :labelCol="labelCol" :wrapperCol="wrapperCol">
  26. <a-input type="password" placeholder="请输入登录密码" v-decorator="['password', validatorRules.password]" :disabled="disableSubmit"/>
  27. </a-form-item>
  28. <a-form-item label="确认密码" :labelCol="labelCol" :wrapperCol="wrapperCol">
  29. <a-input
  30. type="password"
  31. @blur="handleConfirmBlur"
  32. placeholder="请重新输入登录密码"
  33. v-decorator="['confirmpassword', validatorRules.confirmpassword]"
  34. :disabled="disableSubmit"
  35. />
  36. </a-form-item>
  37. </template>
  38. <a-form-item label="用户名字" :labelCol="labelCol" :wrapperCol="wrapperCol">
  39. <a-input placeholder="请输入用户名称" v-decorator="['realname', validatorRules.realname]" :disabled="disableSubmit"/>
  40. </a-form-item>
  41. <a-form-item label="邮箱" :labelCol="labelCol" :wrapperCol="wrapperCol">
  42. <a-input placeholder="请输入邮箱" v-decorator="['email', validatorRules.email]" :disabled="disableSubmit"/>
  43. </a-form-item>
  44. <a-form-item label="角色分配" :labelCol="labelCol" :wrapperCol="wrapperCol" v-show="!roleDisabled">
  45. <a-select style="width: 100%" placeholder="请选择用户角色" v-model="selectedRole" :disabled="disableSubmit">
  46. <a-select-option v-for="(role, roleindex) in roleList" :key="roleindex.toString()" :value="role.id">
  47. {{ role.roleName }}
  48. </a-select-option>
  49. </a-select>
  50. </a-form-item>
  51. <!--部门分配-->
  52. <a-form-item label="部门分配" :labelCol="labelCol" :wrapperCol="wrapperCol" v-show="!departDisabled">
  53. <a-input placeholder="点击右侧按钮选择部门" v-model="checkedDepartNameString" disabled @search="onSearch">
  54. <span slot="addonAfter" icon="search" @click="onSearch" style="cursor: pointer">选择</span>
  55. </a-input>
  56. </a-form-item>
  57. <a-form-item label="头像" :labelCol="labelCol" :wrapperCol="wrapperCol">
  58. <a-upload
  59. listType="picture-card"
  60. class="avatar-uploader"
  61. :showUploadList="false"
  62. :action="uploadAction"
  63. :data="{ isup: 1 }"
  64. :headers="headers"
  65. :beforeUpload="beforeUpload"
  66. @change="handleChange"
  67. :disabled="disableSubmit"
  68. >
  69. <img v-if="picUrl" :src="getAvatarView()" alt="头像" style="height: 104px; max-width: 300px" />
  70. <div v-else>
  71. <a-icon :type="uploadLoading ? 'loading' : 'plus'" />
  72. <div class="ant-upload-text">上传</div>
  73. </div>
  74. </a-upload>
  75. </a-form-item>
  76. <a-form-item label="汇报上级" :labelCol="labelCol" :wrapperCol="wrapperCol">
  77. <a-select
  78. :disabled="disableSubmit"
  79. showSearch
  80. :filterOption="filterOption"
  81. style="width: 100%"
  82. placeholder="请选择汇报上级"
  83. v-decorator="['leader', { rules: [{ required: true, message: '汇报上级必选' }] }]"
  84. labelInValue
  85. >
  86. <a-select-option v-for="(people, peopleindex) in peopleList" :key="peopleindex" :value="people.key">
  87. {{ people.label }}
  88. </a-select-option>
  89. </a-select>
  90. </a-form-item>
  91. <!-- <a-form-item label="生日" :labelCol="labelCol" :wrapperCol="wrapperCol">
  92. <a-date-picker
  93. style="width: 100%"
  94. placeholder="请选择生日"
  95. v-decorator="['birthday', {initialValue:!model.birthday?null:moment(model.birthday,dateFormat)}]"/>
  96. </a-form-item> -->
  97. <a-form-item label="性别" :labelCol="labelCol" :wrapperCol="wrapperCol">
  98. <a-select v-decorator="['sex', {}]" placeholder="请选择性别" :disabled="disableSubmit">
  99. <a-select-option :value="1">男</a-select-option>
  100. <a-select-option :value="2">女</a-select-option>
  101. </a-select>
  102. </a-form-item>
  103. <a-form-item label="手机号码" :labelCol="labelCol" :wrapperCol="wrapperCol">
  104. <a-input
  105. placeholder="请输入手机号码"
  106. :disabled="disableSubmit"
  107. v-decorator="['phone', validatorRules.phone]"
  108. />
  109. </a-form-item>
  110. <a-form-item label="工作流引擎" :labelCol="labelCol" :wrapperCol="wrapperCol">
  111. <j-dict-select-tag
  112. :disabled="disableSubmit"
  113. v-decorator="['activitiSync', {}]"
  114. placeholder="请选择是否同步工作流引擎"
  115. :type="'radio'"
  116. :triggerChange="true"
  117. dictCode="activiti_sync"
  118. />
  119. </a-form-item>
  120. </a-form>
  121. </a-spin>
  122. <depart-window ref="departWindow" @ok="modalFormOk"></depart-window>
  123. <div class="drawer-bootom-button" v-show="!disableSubmit">
  124. <a-popconfirm title="确定放弃编辑?" @confirm="handleCancel" okText="确定" cancelText="取消">
  125. <a-button style="margin-right: 0.8rem">取消</a-button>
  126. </a-popconfirm>
  127. <a-button @click="handleSubmit" type="primary" :loading="confirmLoading">提交</a-button>
  128. </div>
  129. </a-drawer>
  130. </template>
  131. <script>
  132. import pick from 'lodash.pick';
  133. import moment from 'moment';
  134. import Vue from 'vue';
  135. // 引入搜索部门弹出框的组件
  136. import departWindow from './DepartWindow';
  137. import {ACCESS_TOKEN} from '@/store/mutation-types';
  138. import {getAction} from '@/api/manage';
  139. import {addUser, editUser, queryUserRole, queryall} from '@/api/api';
  140. import {disabledAuthFilter} from '@/utils/authFilter';
  141. import {duplicateCheck} from '@/api/api';
  142. export default {
  143. name: 'RoleModal',
  144. components: {
  145. departWindow
  146. },
  147. data() {
  148. return {
  149. departDisabled: false, //是否是我的部门调用该页面
  150. roleDisabled: false, //是否是角色维护调用该页面
  151. modalWidth: 800,
  152. drawerWidth: 700,
  153. modaltoggleFlag: true,
  154. confirmDirty: false,
  155. selectedDepartKeys: [], //保存用户选择部门id
  156. checkedDepartKeys: [],
  157. checkedDepartNames: [], // 保存部门的名称 =>title
  158. checkedDepartNameString: '', // 保存部门的名称 =>title
  159. userId: '', //保存用户id
  160. disableSubmit: false,
  161. userDepartModel: { userId: '', departIdList: [] }, // 保存SysUserDepart的用户部门中间表数据需要的对象
  162. dateFormat: 'YYYY-MM-DD',
  163. validatorRules: {
  164. username: {
  165. rules: [
  166. {
  167. required: true,
  168. message: '请输入用户账号!',
  169. },
  170. {
  171. validator: this.validateUsername,
  172. },
  173. ],
  174. },
  175. password: {
  176. rules: [
  177. {
  178. required: true,
  179. pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,./]).{8,}$/,
  180. message: '密码由8位数字、大小写字母和特殊符号组成!',
  181. },
  182. {
  183. validator: this.validateToNextPassword,
  184. },
  185. ],
  186. },
  187. confirmpassword: {
  188. rules: [
  189. {
  190. required: true,
  191. message: '请重新输入登录密码!',
  192. },
  193. {
  194. validator: this.compareToFirstPassword,
  195. },
  196. ],
  197. },
  198. realname: { rules: [{ required: true, message: '请输入用户名称!' }] },
  199. phone: { rules: [{ validator: this.validatePhone }] },
  200. email: {
  201. rules: [
  202. {
  203. required: true,
  204. validator: this.validateEmail,
  205. },
  206. ],
  207. },
  208. roles: {},
  209. // sex:{initialValue:((!this.model.sex)?"": (this.model.sex+""))}
  210. },
  211. title: '操作',
  212. visible: false,
  213. model: {},
  214. roleList: [],
  215. peopleList: [],
  216. selectedRole: '',
  217. labelCol: {
  218. xs: { span: 24 },
  219. sm: { span: 5 },
  220. },
  221. wrapperCol: {
  222. xs: { span: 24 },
  223. sm: { span: 16 },
  224. },
  225. uploadLoading: false,
  226. confirmLoading: false,
  227. headers: {},
  228. form: this.$form.createForm(this),
  229. picUrl: '',
  230. url: {
  231. fileUpload: window._CONFIG['domianURL'] + '/sys/common/upload',
  232. imgerver: window._CONFIG['domianURL'] + '/sys/common/view',
  233. userWithDepart: '/sys/user/userDepartList', // 引入为指定用户查看部门信息需要的url
  234. userId: '/sys/user/generateUserId', // 引入生成添加用户情况下的url
  235. syncUserByUserName: '/process/extActProcess/doSyncUserByUserName', //同步用户到工作流
  236. },
  237. };
  238. },
  239. created() {
  240. const token = Vue.ls.get(ACCESS_TOKEN);
  241. this.headers = {'X-Access-Token': token};
  242. },
  243. computed: {
  244. uploadAction: function () {
  245. return this.url.fileUpload;
  246. }
  247. },
  248. methods: {
  249. filterOption(input, option) {
  250. return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0;
  251. },
  252. isDisabledAuth(code) {
  253. return disabledAuthFilter(code);
  254. },
  255. // 窗口最大化切换
  256. toggleScreen() {
  257. if (this.modaltoggleFlag) {
  258. this.modalWidth = window.innerWidth;
  259. }
  260. else {
  261. this.modalWidth = 800;
  262. }
  263. this.modaltoggleFlag = !this.modaltoggleFlag;
  264. },
  265. initialRoleList() {
  266. queryall().then((res) => {
  267. if (res.success) {
  268. this.roleList = res.result;
  269. }
  270. else {
  271. console.log(res.message);
  272. }
  273. });
  274. },
  275. getAllPeople(id) {
  276. this.getAction('/sys/user/getAllUser', {status:1, userId: id}).then((res) => {
  277. if (res.success) {
  278. this.peopleList = res.result.map((item) => {
  279. return {key: item.userId, label: item.realName};
  280. });
  281. }
  282. });
  283. },
  284. loadUserRoles(userid) {
  285. queryUserRole({userid: userid}).then((res) => {
  286. if (res.success) {
  287. this.selectedRole = res.result[0];
  288. }
  289. else {
  290. console.log(res.message);
  291. }
  292. });
  293. },
  294. refresh() {
  295. this.selectedDepartKeys = [];
  296. this.checkedDepartKeys = [];
  297. this.checkedDepartNames = [];
  298. this.checkedDepartNameString = '';
  299. this.userId = '';
  300. },
  301. add() {
  302. this.disableSubmit = false;
  303. this.selectedRole = '';
  304. this.userDepartModel = {userId: '', departIdList: []};
  305. this.checkedDepartNames = [];
  306. this.checkedDepartNameString = '';
  307. this.checkedDepartKeys = [];
  308. this.selectedDepartKeys = [];
  309. this.picUrl = '';
  310. this.refresh();
  311. this.edit({activitiSync: '1'});
  312. },
  313. edit(record) {
  314. this.resetScreenSize() // 调用此方法,根据屏幕宽度自适应调整抽屉的宽度
  315. let that = this;
  316. that.initialRoleList();
  317. that.getAllPeople(record.id);
  318. that.form.resetFields()
  319. if (record.hasOwnProperty('id')) {
  320. that.loadUserRoles(record.id);
  321. this.picUrl = 'Has no pic url yet';
  322. }
  323. if (record.leaderId) {
  324. record.leader = {key:record.leaderId,label:record.leaderName};
  325. }else{
  326. record.leader = undefined;
  327. }
  328. that.userId = record.id;
  329. that.visible = true;
  330. that.model = Object.assign({}, record);
  331. that.$nextTick(() => {
  332. that.form.setFieldsValue(pick(this.model, 'username', 'sex', 'realname', 'email', 'phone', 'activitiSync','leader'));
  333. })
  334. // 调用查询用户对应的部门信息的方法
  335. that.checkedDepartKeys = [];
  336. that.loadCheckedDeparts();
  337. },
  338. //
  339. loadCheckedDeparts() {
  340. let that = this;
  341. if (!that.userId) {
  342. return;
  343. }
  344. getAction(that.url.userWithDepart, { userId: that.userId }).then((res) => {
  345. that.checkedDepartNames = [];
  346. if (res.success) {
  347. for (let i = 0; i < res.result.length; i++) {
  348. that.checkedDepartNames.push(res.result[i].title);
  349. this.checkedDepartNameString = this.checkedDepartNames.join(',');
  350. that.checkedDepartKeys.push(res.result[i].key);
  351. }
  352. that.userDepartModel.departIdList = that.checkedDepartKeys;
  353. }
  354. else {
  355. console.log(res.message);
  356. }
  357. });
  358. },
  359. close() {
  360. this.$emit('close');
  361. this.visible = false;
  362. this.disableSubmit = false;
  363. this.selectedRole = '';
  364. this.userDepartModel = {userId: '', departIdList: []};
  365. this.checkedDepartNames = [];
  366. this.checkedDepartNameString = '';
  367. this.checkedDepartKeys = [];
  368. this.selectedDepartKeys = [];
  369. },
  370. moment,
  371. handleSubmit() {
  372. const that = this;
  373. that.confirmLoading = true;
  374. // 触发表单验证
  375. this.form.validateFields((err, values) => {
  376. if (!err) {
  377. let avatar = that.model.avatar;
  378. if (!values.birthday) {
  379. values.birthday = '';
  380. }
  381. else {
  382. values.birthday = values.birthday.format(this.dateFormat);
  383. }
  384. let formData = Object.assign(this.model, values);
  385. formData.avatar = avatar;
  386. formData.selectedroles = this.selectedRole ? this.selectedRole : '';
  387. formData.roleName = this.selectedRole ? this.roleList.filter(item => item.id === this.selectedRole)[0].roleName : '';
  388. formData.departName = this.checkedDepartNameString;
  389. formData.selecteddeparts =
  390. this.userDepartModel.departIdList.length > 0 ? this.userDepartModel.departIdList.join(',') : '';
  391. formData.leaderId = values.leader.key;
  392. formData.leaderName = values.leader.label.replace(/[\r\n]/g, "");
  393. let obj
  394. if (!this.model.id) {
  395. formData.id = this.userId;
  396. obj = addUser(formData);
  397. }
  398. else {
  399. obj = editUser(formData);
  400. }
  401. obj.then((res) => {
  402. if (res.success) {
  403. that.$message.success(res.message);
  404. that.$emit('ok');
  405. that.visible = false;
  406. that.confirmLoading = false;
  407. }
  408. else {
  409. that.$message.warning(res.message)
  410. }
  411. });
  412. }
  413. else {
  414. that.confirmLoading = false;
  415. }
  416. });
  417. },
  418. handleCancel() {
  419. this.close()
  420. },
  421. validateToNextPassword(rule, value, callback) {
  422. const form = this.form;
  423. const confirmpassword = form.getFieldValue('confirmpassword');
  424. if (value && confirmpassword && value !== confirmpassword) {
  425. callback('两次输入的密码不一样!');
  426. }
  427. if (value && this.confirmDirty) {
  428. form.validateFields(['confirm'], {force: true});
  429. }
  430. callback();
  431. },
  432. compareToFirstPassword(rule, value, callback) {
  433. const form = this.form;
  434. if (value && value !== form.getFieldValue('password')) {
  435. callback('两次输入的密码不一样!');
  436. }
  437. else {
  438. callback();
  439. }
  440. },
  441. validatePhone(rule, value, callback) {
  442. if (!value) {
  443. callback()
  444. }
  445. else {
  446. if (new RegExp(/^1[3|4|5|7|8][0-9]\d{8}$/).test(value)) {
  447. var params = {
  448. tableName: 'sys_user',
  449. fieldName: 'phone',
  450. fieldVal: value,
  451. dataId: this.userId
  452. }
  453. duplicateCheck(params).then((res) => {
  454. if (res.success) {
  455. callback();
  456. }
  457. else {
  458. callback('手机号已存在!');
  459. }
  460. })
  461. }
  462. else {
  463. callback('请输入正确格式的手机号码!');
  464. }
  465. }
  466. },
  467. validateEmail(rule, value, callback) {
  468. if (!value) {
  469. callback()
  470. }
  471. else {
  472. if (new RegExp(/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/).test(value)) {
  473. var params = {
  474. tableName: 'sys_user',
  475. fieldName: 'email',
  476. fieldVal: value,
  477. dataId: this.userId,
  478. }
  479. duplicateCheck(params).then((res) => {
  480. console.log(res)
  481. if (res.success) {
  482. callback()
  483. } else {
  484. callback('邮箱已存在!')
  485. }
  486. })
  487. }
  488. else {
  489. callback('请输入正确格式的邮箱!')
  490. }
  491. }
  492. },
  493. validateUsername(rule, value, callback) {
  494. var params = {
  495. tableName: 'sys_user',
  496. fieldName: 'username',
  497. fieldVal: value,
  498. dataId: this.userId
  499. };
  500. duplicateCheck(params).then((res) => {
  501. if (res.success) {
  502. callback()
  503. }
  504. else {
  505. callback('用户名已存在!')
  506. }
  507. })
  508. },
  509. handleConfirmBlur(e) {
  510. const value = e.target.value;
  511. this.confirmDirty = this.confirmDirty || !!value;
  512. },
  513. normFile(e) {
  514. if (Array.isArray(e)) {
  515. return e;
  516. }
  517. return e && e.fileList;
  518. },
  519. beforeUpload: function (file) {
  520. var fileType = file.type;
  521. if (fileType.indexOf('image') < 0) {
  522. this.$message.warning('请上传图片');
  523. return false;
  524. }
  525. },
  526. handleChange(info) {
  527. this.picUrl = '';
  528. if (info.file.status === 'uploading') {
  529. this.uploadLoading = true;
  530. return;
  531. }
  532. if (info.file.status === 'done') {
  533. var response = info.file.response;
  534. this.uploadLoading = false;
  535. if (response.success) {
  536. this.model.avatar = response.message;
  537. this.picUrl = 'Has no pic url yet';
  538. }
  539. else {
  540. this.$message.warning(response.message);
  541. }
  542. }
  543. },
  544. getAvatarView() {
  545. return this.url.imgerver + '/' + this.model.avatar;
  546. },
  547. // 搜索用户对应的部门API
  548. onSearch(value) {
  549. this.$refs.departWindow.add(this.checkedDepartKeys, this.userId);
  550. },
  551. // 获取用户对应部门弹出框提交给返回的数据
  552. modalFormOk(formData) {
  553. this.checkedDepartNames = [];
  554. this.selectedDepartKeys = [];
  555. this.checkedDepartNameString = '';
  556. this.userId = formData.userId;
  557. this.userDepartModel.userId = formData.userId;
  558. for (let i = 0; i < formData.departIdList.length; i++) {
  559. this.selectedDepartKeys.push(formData.departIdList[i].key);
  560. this.checkedDepartNames.push(formData.departIdList[i].title);
  561. this.checkedDepartNameString = this.checkedDepartNames.join(',');
  562. }
  563. this.userDepartModel.departIdList = this.selectedDepartKeys;
  564. this.checkedDepartKeys = this.selectedDepartKeys; // 更新当前的选择keys
  565. },
  566. // 根据屏幕变化,设置抽屉尺寸
  567. resetScreenSize() {
  568. let screenWidth = document.body.clientWidth;
  569. if (screenWidth < 500) {
  570. this.drawerWidth = screenWidth;
  571. }
  572. else {
  573. this.drawerWidth = 700;
  574. }
  575. },
  576. getData(className) {
  577. return this.form.getFieldValue(className)
  578. }
  579. }
  580. };
  581. </script>
  582. <style scoped>
  583. .avatar-uploader > .ant-upload {
  584. width: 104px;
  585. height: 104px;
  586. }
  587. .ant-upload-select-picture-card i {
  588. font-size: 49px;
  589. color: #999;
  590. }
  591. .ant-upload-select-picture-card .ant-upload-text {
  592. margin-top: 8px;
  593. color: #666;
  594. }
  595. .ant-table-tbody .ant-table-row td {
  596. padding-top: 10px;
  597. padding-bottom: 10px;
  598. }
  599. .drawer-bootom-button {
  600. position: absolute;
  601. bottom: -8px;
  602. width: 100%;
  603. border-top: 1px solid #e8e8e8;
  604. padding: 10px 16px;
  605. text-align: right;
  606. left: 0;
  607. background: #fff;
  608. border-radius: 0 0 2px 2px;
  609. }
  610. </style>