懒羊羊
2024-01-31 e57a8990ae56f657a59c435a0613c5f7a8728003
提交 | 用户 | 时间
e57a89 1 <template>
2   <div class="app-container">
3     <el-row :gutter="20">
4       <!--部门数据-->
5       <el-col :span="4" :xs="24">
6         <div class="head-container">
7           <el-input
8             v-model="deptName"
9             placeholder="请输入部门名称"
10             clearable
11             size="small"
12             prefix-icon="el-icon-search"
13             style="margin-bottom: 20px"
14           />
15         </div>
16         <div class="head-container">
17           <el-tree
18             :data="deptOptions"
19             :props="defaultProps"
20             :expand-on-click-node="false"
21             :filter-node-method="filterNode"
22             ref="tree"
23             node-key="id"
24             default-expand-all
25             highlight-current
26             @node-click="handleNodeClick"
27           />
28         </div>
29       </el-col>
30       <!--用户数据-->
31       <el-col :span="20" :xs="24">
32         <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
33           <el-form-item label="用户名称" prop="userName">
34             <el-input
35               v-model="queryParams.userName"
36               placeholder="请输入用户名称"
37               clearable
38               style="width: 240px"
39               @keyup.enter.native="handleQuery"
40             />
41           </el-form-item>
42           <el-form-item label="手机号码" prop="phonenumber">
43             <el-input
44               v-model="queryParams.phonenumber"
45               placeholder="请输入手机号码"
46               clearable
47               style="width: 240px"
48               @keyup.enter.native="handleQuery"
49             />
50           </el-form-item>
51           <el-form-item label="状态" prop="status">
52             <el-select
53               v-model="queryParams.status"
54               placeholder="用户状态"
55               clearable
56               style="width: 240px"
57             >
58               <el-option
59                 v-for="dict in dict.type.sys_normal_disable"
60                 :key="dict.value"
61                 :label="dict.label"
62                 :value="dict.value"
63               />
64             </el-select>
65           </el-form-item>
66           <el-form-item label="创建时间">
67             <el-date-picker
68               v-model="dateRange"
69               style="width: 240px"
70               value-format="yyyy-MM-dd"
71               type="daterange"
72               range-separator="-"
73               start-placeholder="开始日期"
74               end-placeholder="结束日期"
75             ></el-date-picker>
76           </el-form-item>
77           <el-form-item>
78             <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
79             <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
80           </el-form-item>
81         </el-form>
82
83         <el-row :gutter="10" class="mb8">
84           <el-col :span="1.5">
85             <el-button
86               type="primary"
87               plain
88               icon="el-icon-plus"
89               size="mini"
90               @click="handleAdd"
91               v-hasPermi="['system:user:add']"
92             >新增</el-button>
93           </el-col>
94           <el-col :span="1.5">
95             <el-button
96               type="success"
97               plain
98               icon="el-icon-edit"
99               size="mini"
100               :disabled="single"
101               @click="handleUpdate"
102               v-hasPermi="['system:user:edit']"
103             >修改</el-button>
104           </el-col>
105           <el-col :span="1.5">
106             <el-button
107               type="danger"
108               plain
109               icon="el-icon-delete"
110               size="mini"
111               :disabled="multiple"
112               @click="handleDelete"
113               v-hasPermi="['system:user:remove']"
114             >删除</el-button>
115           </el-col>
116           <el-col :span="1.5">
117             <el-button
118               type="info"
119               plain
120               icon="el-icon-upload2"
121               size="mini"
122               @click="handleImport"
123               v-hasPermi="['system:user:import']"
124             >导入</el-button>
125           </el-col>
126           <el-col :span="1.5">
127             <el-button
128               type="warning"
129               plain
130               icon="el-icon-download"
131               size="mini"
132               @click="handleExport"
133               v-hasPermi="['system:user:export']"
134             >导出</el-button>
135           </el-col>
136           <right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
137         </el-row>
138
139         <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
140           <el-table-column type="selection" width="50" align="center" />
141           <el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
142           <el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
143           <el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
144           <el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
145           <el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
146           <el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
147             <template slot-scope="scope">
148               <el-switch
149                 v-model="scope.row.status"
150                 active-value="0"
151                 inactive-value="1"
152                 @change="handleStatusChange(scope.row)"
153               ></el-switch>
154             </template>
155           </el-table-column>
156           <el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[6].visible" width="160">
157             <template slot-scope="scope">
158               <span>{{ parseTime(scope.row.createTime) }}</span>
159             </template>
160           </el-table-column>
161           <el-table-column
162             label="操作"
163             align="center"
164             width="160"
165             class-name="small-padding fixed-width"
166           >
167             <template slot-scope="scope" v-if="scope.row.userId !== 1">
168               <el-button
169                 size="mini"
170                 type="text"
171                 icon="el-icon-edit"
172                 @click="handleUpdate(scope.row)"
173                 v-hasPermi="['system:user:edit']"
174               >修改</el-button>
175               <el-button
176                 size="mini"
177                 type="text"
178                 icon="el-icon-delete"
179                 @click="handleDelete(scope.row)"
180                 v-hasPermi="['system:user:remove']"
181               >删除</el-button>
182               <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:user:resetPwd', 'system:user:edit']">
183                 <el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button>
184                 <el-dropdown-menu slot="dropdown">
185                   <el-dropdown-item command="handleResetPwd" icon="el-icon-key"
186                     v-hasPermi="['system:user:resetPwd']">重置密码</el-dropdown-item>
187                   <el-dropdown-item command="handleAuthRole" icon="el-icon-circle-check"
188                     v-hasPermi="['system:user:edit']">分配角色</el-dropdown-item>
189                 </el-dropdown-menu>
190               </el-dropdown>
191             </template>
192           </el-table-column>
193         </el-table>
194
195         <pagination
196           v-show="total>0"
197           :total="total"
198           :page.sync="queryParams.pageNum"
199           :limit.sync="queryParams.pageSize"
200           @pagination="getList"
201         />
202       </el-col>
203     </el-row>
204
205     <!-- 添加或修改用户配置对话框 -->
206     <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
207       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
208         <el-row>
209           <el-col :span="12">
210             <el-form-item label="用户昵称" prop="nickName">
211               <el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
212             </el-form-item>
213           </el-col>
214           <el-col :span="12">
215             <el-form-item label="归属部门" prop="deptId">
216               <treeselect v-model="form.deptId" :options="deptOptions" :show-count="true" placeholder="请选择归属部门" />
217             </el-form-item>
218           </el-col>
219         </el-row>
220         <el-row>
221           <el-col :span="12">
222             <el-form-item label="手机号码" prop="phonenumber">
223               <el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
224             </el-form-item>
225           </el-col>
226           <el-col :span="12">
227             <el-form-item label="邮箱" prop="email">
228               <el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
229             </el-form-item>
230           </el-col>
231         </el-row>
232         <el-row>
233           <el-col :span="12">
234             <el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
235               <el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
236             </el-form-item>
237           </el-col>
238           <el-col :span="12">
239             <el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
240               <el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password/>
241             </el-form-item>
242           </el-col>
243         </el-row>
244         <el-row>
245           <el-col :span="12">
246             <el-form-item label="用户性别">
247               <el-select v-model="form.sex" placeholder="请选择性别">
248                 <el-option
249                   v-for="dict in dict.type.sys_user_sex"
250                   :key="dict.value"
251                   :label="dict.label"
252                   :value="dict.value"
253                 ></el-option>
254               </el-select>
255             </el-form-item>
256           </el-col>
257           <el-col :span="12">
258             <el-form-item label="状态">
259               <el-radio-group v-model="form.status">
260                 <el-radio
261                   v-for="dict in dict.type.sys_normal_disable"
262                   :key="dict.value"
263                   :label="dict.value"
264                 >{{dict.label}}</el-radio>
265               </el-radio-group>
266             </el-form-item>
267           </el-col>
268         </el-row>
269         <el-row>
270           <el-col :span="12">
271             <el-form-item label="岗位">
272               <el-select v-model="form.postIds" multiple placeholder="请选择岗位">
273                 <el-option
274                   v-for="item in postOptions"
275                   :key="item.postId"
276                   :label="item.postName"
277                   :value="item.postId"
278                   :disabled="item.status == 1"
279                 ></el-option>
280               </el-select>
281             </el-form-item>
282           </el-col>
283           <el-col :span="12">
284             <el-form-item label="角色">
285               <el-select v-model="form.roleIds" multiple placeholder="请选择角色">
286                 <el-option
287                   v-for="item in roleOptions"
288                   :key="item.roleId"
289                   :label="item.roleName"
290                   :value="item.roleId"
291                   :disabled="item.status == 1"
292                 ></el-option>
293               </el-select>
294             </el-form-item>
295           </el-col>
296         </el-row>
297         <el-row>
298           <el-col :span="24">
299             <el-form-item label="备注">
300               <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
301             </el-form-item>
302           </el-col>
303         </el-row>
304       </el-form>
305       <div slot="footer" class="dialog-footer">
306         <el-button type="primary" @click="submitForm">确 定</el-button>
307         <el-button @click="cancel">取 消</el-button>
308       </div>
309     </el-dialog>
310
311     <!-- 用户导入对话框 -->
312     <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
313       <el-upload
314         ref="upload"
315         :limit="1"
316         accept=".xlsx, .xls"
317         :headers="upload.headers"
318         :action="upload.url + '?updateSupport=' + upload.updateSupport"
319         :disabled="upload.isUploading"
320         :on-progress="handleFileUploadProgress"
321         :on-success="handleFileSuccess"
322         :auto-upload="false"
323         drag
324       >
325         <i class="el-icon-upload"></i>
326         <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
327         <div class="el-upload__tip text-center" slot="tip">
328           <div class="el-upload__tip" slot="tip">
329             <el-checkbox v-model="upload.updateSupport" /> 是否更新已经存在的用户数据
330           </div>
331           <span>仅允许导入xls、xlsx格式文件。</span>
332           <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
333         </div>
334       </el-upload>
335       <div slot="footer" class="dialog-footer">
336         <el-button type="primary" @click="submitFileForm">确 定</el-button>
337         <el-button @click="upload.open = false">取 消</el-button>
338       </div>
339     </el-dialog>
340   </div>
341 </template>
342
343 <script>
344 import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus, deptTreeSelect } from "@/api/system/user";
345 import { getToken } from "@/utils/auth";
346 import Treeselect from "@riophae/vue-treeselect";
347 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
348
349 export default {
350   name: "User",
351   dicts: ['sys_normal_disable', 'sys_user_sex'],
352   components: { Treeselect },
353   data() {
354     return {
355       // 遮罩层
356       loading: true,
357       // 选中数组
358       ids: [],
359       // 非单个禁用
360       single: true,
361       // 非多个禁用
362       multiple: true,
363       // 显示搜索条件
364       showSearch: true,
365       // 总条数
366       total: 0,
367       // 用户表格数据
368       userList: null,
369       // 弹出层标题
370       title: "",
371       // 部门树选项
372       deptOptions: undefined,
373       // 是否显示弹出层
374       open: false,
375       // 部门名称
376       deptName: undefined,
377       // 默认密码
378       initPassword: undefined,
379       // 日期范围
380       dateRange: [],
381       // 岗位选项
382       postOptions: [],
383       // 角色选项
384       roleOptions: [],
385       // 表单参数
386       form: {},
387       defaultProps: {
388         children: "children",
389         label: "label"
390       },
391       // 用户导入参数
392       upload: {
393         // 是否显示弹出层(用户导入)
394         open: false,
395         // 弹出层标题(用户导入)
396         title: "",
397         // 是否禁用上传
398         isUploading: false,
399         // 是否更新已经存在的用户数据
400         updateSupport: 0,
401         // 设置上传的请求头部
402         headers: { Authorization: "Bearer " + getToken() },
403         // 上传的地址
404         url: process.env.VUE_APP_BASE_API + "/system/user/importData"
405       },
406       // 查询参数
407       queryParams: {
408         pageNum: 1,
409         pageSize: 10,
410         userName: undefined,
411         phonenumber: undefined,
412         status: undefined,
413         deptId: undefined
414       },
415       // 列信息
416       columns: [
417         { key: 0, label: `用户编号`, visible: true },
418         { key: 1, label: `用户名称`, visible: true },
419         { key: 2, label: `用户昵称`, visible: true },
420         { key: 3, label: `部门`, visible: true },
421         { key: 4, label: `手机号码`, visible: true },
422         { key: 5, label: `状态`, visible: true },
423         { key: 6, label: `创建时间`, visible: true }
424       ],
425       // 表单校验
426       rules: {
427         userName: [
428           { required: true, message: "用户名称不能为空", trigger: "blur" },
429           { min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' }
430         ],
431         nickName: [
432           { required: true, message: "用户昵称不能为空", trigger: "blur" }
433         ],
434         password: [
435           { required: true, message: "用户密码不能为空", trigger: "blur" },
436           { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }
437         ],
438         email: [
439           {
440             type: "email",
441             message: "请输入正确的邮箱地址",
442             trigger: ["blur", "change"]
443           }
444         ],
445         phonenumber: [
446           {
447             pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
448             message: "请输入正确的手机号码",
449             trigger: "blur"
450           }
451         ]
452       }
453     };
454   },
455   watch: {
456     // 根据名称筛选部门树
457     deptName(val) {
458       this.$refs.tree.filter(val);
459     }
460   },
461   created() {
462     this.getList();
463     this.getDeptTree();
464     this.getConfigKey("sys.user.initPassword").then(response => {
465       this.initPassword = response.msg;
466     });
467   },
468   methods: {
469     /** 查询用户列表 */
470     getList() {
471       this.loading = true;
472       listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
473           this.userList = response.rows;
474           this.total = response.total;
475           this.loading = false;
476         }
477       );
478     },
479     /** 查询部门下拉树结构 */
480     getDeptTree() {
481       deptTreeSelect().then(response => {
482         this.deptOptions = response.data;
483       });
484     },
485     // 筛选节点
486     filterNode(value, data) {
487       if (!value) return true;
488       return data.label.indexOf(value) !== -1;
489     },
490     // 节点单击事件
491     handleNodeClick(data) {
492       this.queryParams.deptId = data.id;
493       this.handleQuery();
494     },
495     // 用户状态修改
496     handleStatusChange(row) {
497       let text = row.status === "0" ? "启用" : "停用";
498       this.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function() {
499         return changeUserStatus(row.userId, row.status);
500       }).then(() => {
501         this.$modal.msgSuccess(text + "成功");
502       }).catch(function() {
503         row.status = row.status === "0" ? "1" : "0";
504       });
505     },
506     // 取消按钮
507     cancel() {
508       this.open = false;
509       this.reset();
510     },
511     // 表单重置
512     reset() {
513       this.form = {
514         userId: undefined,
515         deptId: undefined,
516         userName: undefined,
517         nickName: undefined,
518         password: undefined,
519         phonenumber: undefined,
520         email: undefined,
521         sex: undefined,
522         status: "0",
523         remark: undefined,
524         postIds: [],
525         roleIds: []
526       };
527       this.resetForm("form");
528     },
529     /** 搜索按钮操作 */
530     handleQuery() {
531       this.queryParams.pageNum = 1;
532       this.getList();
533     },
534     /** 重置按钮操作 */
535     resetQuery() {
536       this.dateRange = [];
537       this.resetForm("queryForm");
538       this.queryParams.deptId = undefined;
539       this.$refs.tree.setCurrentKey(null);
540       this.handleQuery();
541     },
542     // 多选框选中数据
543     handleSelectionChange(selection) {
544       this.ids = selection.map(item => item.userId);
545       this.single = selection.length != 1;
546       this.multiple = !selection.length;
547     },
548     // 更多操作触发
549     handleCommand(command, row) {
550       switch (command) {
551         case "handleResetPwd":
552           this.handleResetPwd(row);
553           break;
554         case "handleAuthRole":
555           this.handleAuthRole(row);
556           break;
557         default:
558           break;
559       }
560     },
561     /** 新增按钮操作 */
562     handleAdd() {
563       this.reset();
564       getUser().then(response => {
565         this.postOptions = response.posts;
566         this.roleOptions = response.roles;
567         this.open = true;
568         this.title = "添加用户";
569         this.form.password = this.initPassword;
570       });
571     },
572     /** 修改按钮操作 */
573     handleUpdate(row) {
574       this.reset();
575       const userId = row.userId || this.ids;
576       getUser(userId).then(response => {
577         this.form = response.data;
578         this.postOptions = response.posts;
579         this.roleOptions = response.roles;
580         this.$set(this.form, "postIds", response.postIds);
581         this.$set(this.form, "roleIds", response.roleIds);
582         this.open = true;
583         this.title = "修改用户";
584         this.form.password = "";
585       });
586     },
587     /** 重置密码按钮操作 */
588     handleResetPwd(row) {
589       this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
590         confirmButtonText: "确定",
591         cancelButtonText: "取消",
592         closeOnClickModal: false,
593         inputPattern: /^.{5,20}$/,
594         inputErrorMessage: "用户密码长度必须介于 5 和 20 之间"
595       }).then(({ value }) => {
596           resetUserPwd(row.userId, value).then(response => {
597             this.$modal.msgSuccess("修改成功,新密码是:" + value);
598           });
599         }).catch(() => {});
600     },
601     /** 分配角色操作 */
602     handleAuthRole: function(row) {
603       const userId = row.userId;
604       this.$router.push("/system/user-auth/role/" + userId);
605     },
606     /** 提交按钮 */
607     submitForm: function() {
608       this.$refs["form"].validate(valid => {
609         if (valid) {
610           if (this.form.userId != undefined) {
611             updateUser(this.form).then(response => {
612               this.$modal.msgSuccess("修改成功");
613               this.open = false;
614               this.getList();
615             });
616           } else {
617             addUser(this.form).then(response => {
618               this.$modal.msgSuccess("新增成功");
619               this.open = false;
620               this.getList();
621             });
622           }
623         }
624       });
625     },
626     /** 删除按钮操作 */
627     handleDelete(row) {
628       const userIds = row.userId || this.ids;
629       this.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?').then(function() {
630         return delUser(userIds);
631       }).then(() => {
632         this.getList();
633         this.$modal.msgSuccess("删除成功");
634       }).catch(() => {});
635     },
636     /** 导出按钮操作 */
637     handleExport() {
638       this.download('system/user/export', {
639         ...this.queryParams
640       }, `user_${new Date().getTime()}.xlsx`)
641     },
642     /** 导入按钮操作 */
643     handleImport() {
644       this.upload.title = "用户导入";
645       this.upload.open = true;
646     },
647     /** 下载模板操作 */
648     importTemplate() {
649       this.download('system/user/importTemplate', {
650       }, `user_template_${new Date().getTime()}.xlsx`)
651     },
652     // 文件上传中处理
653     handleFileUploadProgress(event, file, fileList) {
654       this.upload.isUploading = true;
655     },
656     // 文件上传成功处理
657     handleFileSuccess(response, file, fileList) {
658       this.upload.open = false;
659       this.upload.isUploading = false;
660       this.$refs.upload.clearFiles();
661       this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
662       this.getList();
663     },
664     // 提交上传文件
665     submitFileForm() {
666       this.$refs.upload.submit();
667     }
668   }
669 };
670 </script>