| | |
| | | </div> |
| | | </div> |
| | | <el-col> |
| | | <el-table v-loading="loading" :data="userList" :header-cell-style="{ background: '#f8f8f9', color: '#515a6e' }" border> |
| | | <el-table ref="dragTable" v-loading="loading" row-key="userId" :data="userList" :header-cell-style="{ background: '#f8f8f9', color: '#515a6e' }" border> |
| | | <el-table-column label="拖拽" align="center" width="60"> |
| | | <template slot-scope="scope"> |
| | | <i class="el-icon-rank drag-handle" :data-user-id="scope.row.userId"></i> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="序号" align="center" type="index" /> |
| | | <el-table-column label="姓名" align="center" key="nickName" prop="nickName" :show-overflow-tooltip="true" /> |
| | | <el-table-column label="账号" align="center" key="userName" prop="userName" :show-overflow-tooltip="true" /> |
| | |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" |
| | | :page-sizes="[20,50,100,200,500]" |
| | | :limit.sync="queryParams.pageSize" @pagination="getList" /> |
| | | </el-col> |
| | | </pane> |
| | |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="岗位"> |
| | | <el-select style="width:100%" v-model="form.postIds" multiple placeholder="请选择"> |
| | | <el-option |
| | | v-for="item in postOptions" |
| | | :key="item.postId" |
| | | :label="item.postName" |
| | | :value="item.postId" |
| | | :disabled="item.status == 1" |
| | | ></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="角色" prop="roleIds"> |
| | | <el-select v-model="form.roleIds" multiple placeholder="请选择角色" clearable> |
| | | <el-select style="width:100%" v-model="form.roleIds" multiple placeholder="请选择角色" clearable> |
| | | <el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName" :value="item.roleId" |
| | | :disabled="item.status == 1"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="密码" prop="password"> |
| | | <el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="姓名EN" prop="nameEn"> |
| | | <el-input v-model="form.nameEn" placeholder="请输入姓名EN" maxlength="50" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="邮箱" prop="email"> |
| | | <el-input v-model="form.email" placeholder="请输入内容"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="单位" prop="company"> |
| | | <el-select v-model="form.company" placeholder="请选择单位" style="width: 100%" clearable> |
| | | <el-option v-for="item in postOptions" :key="item.id" :label="item.company" |
| | | <el-option v-for="item in companyOptions" :key="item.id" :label="item.company" |
| | | :value="item.id"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="归属部门" prop="deptId"> |
| | | <treeselect v-model="form.deptId" :options="enabledDeptOptions" :show-count="true" |
| | | placeholder="请选择归属部门" /> |
| | | placeholder="请选择归属部门" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="签名"> |
| | | <el-upload class="avatar-uploader" :action="uploadAction" :show-file-list="false" |
| | |
| | | </el-upload> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="个人照片"> |
| | | <el-upload class="avatar-uploader" :action="uploadAction" :show-file-list="false" |
| | | :headers="upload.headers" accept=".png, .jpg, .jpeg, .gif" :on-error="handleUploadError1" |
| | | :on-success="handleUploadSuccess1" :before-upload="handleBeforeUpload1"> |
| | | :headers="upload.headers" accept=".png, .jpg, .jpeg, .gif" :on-error="handleUploadError1" |
| | | :on-success="handleUploadSuccess1" :before-upload="handleBeforeUpload1"> |
| | | <img v-if="form.pictureUrl" :src="javaApi + '/img/' + form.pictureUrl" class="avatar" alt=""> |
| | | <i v-else class="el-icon-plus avatar-uploader-icon"></i> |
| | | </el-upload> |
| | |
| | | resetUserPwd, |
| | | changeUserStatus, |
| | | deptTreeSelect, |
| | | selectCompaniesList, selectSimpleList, addPersonUser, uploadFile, selectRoleList, selectCustomEnum, addDepartment |
| | | selectCompaniesList, |
| | | selectSimpleList, |
| | | addPersonUser, |
| | | uploadFile, |
| | | selectRoleList, |
| | | selectCustomEnum, |
| | | addDepartment, |
| | | updateUserSort |
| | | } from "@/api/system/user"; |
| | | import {optionSelect} from '@/api/system/post' |
| | | import { getToken } from "@/utils/auth"; |
| | | import Treeselect from "@riophae/vue-treeselect"; |
| | | import "@riophae/vue-treeselect/dist/vue-treeselect.css"; |
| | | import { Splitpanes, Pane } from "splitpanes"; |
| | | import "splitpanes/dist/splitpanes.css"; |
| | | import Sortable from "sortablejs"; |
| | | |
| | | export default { |
| | | nickName: "User", |
| | |
| | | dateRange: [], |
| | | // 岗位选项 |
| | | postOptions: [], |
| | | //单位选项 |
| | | companyOptions:[], |
| | | // 角色选项 |
| | | roleOptions: [], |
| | | // 表单参数 |
| | |
| | | // 查询参数 |
| | | queryParams: { |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | pageSize: 20, |
| | | nickName: undefined, |
| | | phonenumber: undefined, |
| | | status: undefined, |
| | |
| | | roleIds: [ |
| | | { required: true, message: "请选择角色", trigger: "change" } |
| | | ], |
| | | // password: [ |
| | | // { required: true, message: "密码不能为空", trigger: "blur" }, |
| | | // ], |
| | | password: [ |
| | | { required: false, message: "密码不能为空", trigger: "blur" }, |
| | | { min: 8, max: 20, message: "密码长度必须在8-20个字符之间", trigger: "blur" }, |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | if (!value) { |
| | | callback(); |
| | | return; |
| | | } |
| | | // 检查是否包含大写字母 |
| | | const hasUpperCase = /[A-Z]/.test(value); |
| | | // 检查是否包含小写字母 |
| | | const hasLowerCase = /[a-z]/.test(value); |
| | | // 检查是否包含特殊符号 |
| | | const hasSpecialChar = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(value); |
| | | |
| | | if (!hasUpperCase) { |
| | | callback(new Error('密码必须包含至少一个大写字母')); |
| | | } else if (!hasLowerCase) { |
| | | callback(new Error('密码必须包含至少一个小写字母')); |
| | | } else if (!hasSpecialChar) { |
| | | callback(new Error('密码必须包含至少一个特殊符号')); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | trigger: "blur" |
| | | } |
| | | ], |
| | | phonenumber: [ |
| | | { |
| | | required: true, |
| | |
| | | fatherId: 10001, |
| | | nickName: '', |
| | | }, |
| | | sortTable: null, |
| | | sortSaving: false, |
| | | }; |
| | | }, |
| | | watch: { |
| | |
| | | this.initPassword = response.msg; |
| | | }); |
| | | }, |
| | | beforeDestroy() { |
| | | this.destroyDrag() |
| | | }, |
| | | methods: { |
| | | // 表格行拖拽排序 |
| | | destroyDrag() { |
| | | if (this.sortTable) { |
| | | this.sortTable.destroy() |
| | | this.sortTable = null |
| | | } |
| | | }, |
| | | syncDragDisabledState() { |
| | | if (this.sortTable) { |
| | | this.sortTable.option('disabled', this.loading || this.sortSaving) |
| | | } |
| | | }, |
| | | initDrag() { |
| | | this.destroyDrag() |
| | | if (!this.$refs.dragTable || !this.userList || this.userList.length === 0) { |
| | | return |
| | | } |
| | | |
| | | // 获取 el-table 的 tbody 元素(拖拽的目标容器) |
| | | const tbody = this.$refs.dragTable.$el.querySelector( |
| | | '.el-table__body-wrapper tbody' |
| | | ) |
| | | if (!tbody) { |
| | | return |
| | | } |
| | | |
| | | // 初始化 Sortable |
| | | this.sortTable = Sortable.create(tbody, { |
| | | animation: 150, // 拖拽动画过渡时长 |
| | | ghostClass: 'sortable-ghost', // 拖拽占位符样式 |
| | | chosenClass: 'sortable-chosen', // 选中行样式 |
| | | dragClass: 'sortable-drag', // 拖拽元素样式 |
| | | handle: '.drag-handle', |
| | | disabled: this.loading || this.sortSaving, |
| | | // 拖拽结束触发(核心逻辑) |
| | | onEnd: async({ oldIndex, newIndex }) => { |
| | | if ( |
| | | this.loading || |
| | | this.sortSaving || |
| | | oldIndex === newIndex || |
| | | oldIndex === undefined || |
| | | newIndex === undefined |
| | | ) { |
| | | return |
| | | } |
| | | |
| | | const previousList = [...this.userList] |
| | | const row = this.userList.splice(oldIndex, 1)[0] |
| | | if (!row) { |
| | | this.userList = previousList |
| | | return |
| | | } |
| | | this.userList.splice(newIndex, 0, row) |
| | | |
| | | const pageOffset = |
| | | (this.queryParams.pageNum - 1) * this.queryParams.pageSize |
| | | const data = this.userList.map((item, index) => ({ |
| | | id: item.userId, |
| | | sort: pageOffset + index + 1 |
| | | })) |
| | | |
| | | this.sortSaving = true |
| | | this.syncDragDisabledState() |
| | | try { |
| | | await updateUserSort(data) |
| | | this.$message.success('更新排序成功') |
| | | } catch (error) { |
| | | this.userList = previousList |
| | | this.$message.error('更新排序失败') |
| | | } finally { |
| | | this.sortSaving = false |
| | | this.$nextTick(() => { |
| | | this.syncDragDisabledState() |
| | | }) |
| | | } |
| | | } |
| | | }) |
| | | }, |
| | | /** 查询用户列表 */ |
| | | getList() { |
| | | this.loading = true; |
| | | this.loading = true |
| | | this.syncDragDisabledState() |
| | | listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => { |
| | | this.userList = response.rows; |
| | | this.total = response.total; |
| | | this.loading = false; |
| | | } |
| | | ); |
| | | this.userList = response.rows |
| | | this.total = response.total |
| | | this.loading = false |
| | | this.$nextTick(() => { |
| | | this.initDrag() |
| | | }) |
| | | }).catch(() => { |
| | | this.loading = false |
| | | this.destroyDrag() |
| | | }) |
| | | }, |
| | | // 打开添加架构弹框 |
| | | addSchema() { |
| | |
| | | this.reset(); |
| | | this.open = true; |
| | | selectCustomEnum().then(res => { |
| | | this.postOptions = res.data; |
| | | this.companyOptions = res.data; |
| | | }) |
| | | getUser().then(response => { |
| | | this.roleOptions = response.roles; |
| | | this.postOptions = response.posts |
| | | this.title = "添加用户"; |
| | | }); |
| | | }, |
| | |
| | | handleUpdate(row) { |
| | | this.reset(); |
| | | selectCustomEnum().then(res => { |
| | | this.postOptions = res.data; |
| | | this.companyOptions = res.data; |
| | | }) |
| | | const userId = row.userId || this.ids; |
| | | getUser(userId).then(response => { |
| | |
| | | this.form.password = '' |
| | | this.roleOptions = response.roles; |
| | | this.$set(this.form, "roleIds", response.roleIds); |
| | | this.postOptions = response.posts |
| | | this.$set(this.form, "postIds", response.postIds); |
| | | this.open = true; |
| | | this.title = "修改用户"; |
| | | }); |
| | |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | :deep(.drag-handle) { |
| | | cursor: grab; |
| | | color: #909399; |
| | | font-size: 16px; |
| | | display: inline-block; |
| | | user-select: none; |
| | | } |
| | | :deep(.drag-handle:hover) { |
| | | color: #409EFF; |
| | | cursor: grab; |
| | | } |
| | | :deep(.drag-handle:active) { |
| | | cursor: grabbing; |
| | | } |
| | | :deep(.sortable-ghost) { |
| | | opacity: 0.8; |
| | | background: #f0f9eb; |
| | | } |
| | | :deep(.sortable-chosen) { |
| | | cursor: move; |
| | | background: #e1f3d8; |
| | | } |
| | | .search_form { |
| | | display: flex; |
| | | justify-content: space-between; |