<template> <div class="app-container"> <div class="filter-container"> <el-input v-model="query.keyword" placeholder="关键词" style="width: 200px;" class="filter-item" @keyup.enter.native="handleFilter" /> <el-select v-model="query.role" placeholder="权限" clearable style="width: 90px" class="filter-item" @change="handleFilter"> <el-option v-for="item in roles" :key="item" :label="item | uppercaseFirst" :value="item" /> </el-select> <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> {{ $t('table.search') }} </el-button> <el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-plus" @click="handleCreate"> {{ $t('table.add') }} </el-button> <el-button v-waves :loading="downloading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload"> {{ $t('table.export') }} </el-button> </div> <el-table v-loading="loading" :data="list" border fit highlight-current-row style="width: 100%"> <el-table-column align="center" label="ID" width="80"> <template slot-scope="scope"> <span>{{ scope.row.index }}</span> </template> </el-table-column> <el-table-column align="center" label="Name"> <template slot-scope="scope"> <span>{{ scope.row.name }}</span> </template> </el-table-column> <el-table-column align="center" label="Email"> <template slot-scope="scope"> <span>{{ scope.row.email }}</span> </template> </el-table-column> <el-table-column align="center" label="Role" width="120"> <template slot-scope="scope"> <span>{{ scope.row.roles.join(', ') }}</span> </template> </el-table-column> <el-table-column align="center" label="Actions" width="350"> <template slot-scope="scope"> <router-link v-if="!scope.row.roles.includes('admin')" :to="'/administrator/users/edit/'+scope.row.id"> <el-button v-permission="['manage user']" type="primary" size="small" icon="el-icon-edit"> Edit </el-button> </router-link> <el-button v-if="!scope.row.roles.includes('admin')" v-permission="['manage permission']" type="warning" size="small" icon="el-icon-edit" @click="handleEditPermissions(scope.row.id);"> Permissions </el-button> <el-button v-if="scope.row.roles.includes('visitor')" v-permission="['manage user']" type="danger" size="small" icon="el-icon-delete" @click="handleDelete(scope.row.id, scope.row.name);"> Delete </el-button> </template> </el-table-column> </el-table> <pagination v-show="total>0" :total="total" :page.sync="query.page" :limit.sync="query.limit" @pagination="getList" /> <el-dialog :visible.sync="dialogPermissionVisible" :title="'Edit Permissions - ' + currentUser.name"> <div v-if="currentUser.name" v-loading="dialogPermissionLoading" class="form-container"> <div class="permissions-container"> <div class="block"> <el-form :model="currentUser" label-width="80px" label-position="top"> <el-form-item label="Menus"> <el-tree ref="menuPermissions" :data="normalizedMenuPermissions" :default-checked-keys="permissionKeys(userMenuPermissions)" :props="permissionProps" show-checkbox node-key="id" class="permission-tree" /> </el-form-item> </el-form> </div> <div class="block"> <el-form :model="currentUser" label-width="80px" label-position="top"> <el-form-item label="Permissions"> <el-tree ref="otherPermissions" :data="normalizedOtherPermissions" :default-checked-keys="permissionKeys(userOtherPermissions)" :props="permissionProps" show-checkbox node-key="id" class="permission-tree" /> </el-form-item> </el-form> </div> <div class="clear-left" /> </div> <div style="text-align:right;"> <el-button type="danger" @click="dialogPermissionVisible=false"> {{ $t('permission.cancel') }} </el-button> <el-button type="primary" @click="confirmPermission"> {{ $t('permission.confirm') }} </el-button> </div> </div> </el-dialog> <el-drawer title="添加用户" :visible.sync="drawer" :direction="direction" :before-close="handleClose" custom-class="zl-drawer"> <el-card class="box-card" style="height: 20000px;"> <div v-loading="userCreating" class="form-container"> <el-form ref="userForm" :rules="rules" :model="newUser" label-position="left" label-width="150px" style="max-width: 500px;"> <el-form-item label="选择角色" prop="role"> <el-select v-model="newUser.role" class="filter-item" placeholder="Please select role"> <el-option v-for="item in nonAdminRoles" :key="item" :label="item | uppercaseFirst" :value="item" /> </el-select> </el-form-item> <el-form-item label="用户名" prop="name"> <el-input v-model="newUser.name" /> </el-form-item> <el-form-item label="账号名称" prop="username"> <el-input v-model="newUser.username" /> </el-form-item> <el-form-item label="邮箱" prop="email"> <el-input v-model="newUser.email" /> </el-form-item> <el-form-item label="自定义标题" prop="title"> <el-input v-model="newUser.title" /> </el-form-item> <el-form-item label="自定义公司名称" prop="company"> <el-input v-model="newUser.company" /> </el-form-item> <el-form-item label="坐标" prop="mapcenter"> <el-input v-model="newUser.mapcenter" style="width:155px" /> <el-button type="primary" @click="drawerMap = true"> 拾取坐标 </el-button> </el-form-item> <el-form-item label="选择地址" prop="role"> <el-select v-model="newUser.province" class="filter-item" placeholder="省" @change="postcity"> <el-option v-for="item in province" :key="item.areaid" :label="item.area_name" :value="item.areaid" /> </el-select> <div style="margin-top: 12px;"></div> <el-select v-model="newUser.city" class="filter-item" placeholder="市" @change="postarea"> <el-option v-for="item in city" :key="item.areaid" :label="item.area_name" :value="item.areaid" /> </el-select> <div style="margin-top: 12px;"></div> <el-select v-model="newUser.area" class="filter-item" placeholder="区"> <el-option v-for="item in area" :key="item.areaid" :label="item.area_name" :value="item.areaid" /> </el-select> </el-form-item> <el-form-item label="密码" prop="password"> <el-input v-model="newUser.password" show-password /> </el-form-item> <el-form-item label="确认密码" prop="confirmPassword"> <el-input v-model="newUser.confirmPassword" show-password /> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="drawer = false"> {{ $t('table.cancel') }} </el-button> <el-button type="primary" @click="createUser()"> {{ $t('table.confirm') }} </el-button> </div> </div> </el-card> <el-drawer title="拾取坐标" :visible.sync="drawerMap" :modal="false" :direction="directionMap" :before-close="handleClose" size="70%" :with-header="false"> <div><CoordinateMap @map-confirm="mapConfirmData" @giveLnglat="getLnglat"></CoordinateMap></div> </el-drawer> </el-drawer> </div> </template> <script> import { areachina, areachinapost } from '@/api/users'; import Pagination from '@/components/Pagination'; // Secondary package based on el-pagination import UserResource from '@/api/user'; import Resource from '@/api/resource'; import waves from '@/directive/waves'; // Waves directive import permission from '@/directive/permission'; // Permission directive import checkPermission from '@/utils/permission'; // Permission checking import CoordinateMap from '@/components/coordinateMap'; const userResource = new UserResource(); const permissionResource = new Resource('permissions'); export default { name: 'UserList', components: { Pagination, CoordinateMap }, directives: { waves, permission }, data() { var validateConfirmPassword = (rule, value, callback) => { if (value !== this.newUser.password) { callback(new Error('Password is mismatched!')); } else { callback(); } }; return { drawerMap: false, directionMap: 'rtl', list: null, total: 0, drawer: false, direction: 'ltr', loading: true, downloading: false, userCreating: false, query: { page: 1, limit: 15, keyword: '', role: '', }, province: [], city: [], area: [], roles: ['admin', 'manager', 'editor', 'user', 'visitor'], nonAdminRoles: ['editor', 'user', 'visitor'], newUser: {}, // drawer: false, dialogPermissionVisible: false, dialogPermissionLoading: false, currentUserId: 0, currentUser: { name: '', permissions: [], rolePermissions: [], }, rules: { role: [{ required: true, message: 'Role is required', trigger: 'change' }], name: [{ required: true, message: 'Name is required', trigger: 'blur' }], username: [{ required: true, message: 'username is required', trigger: 'blur' }], title: [{ required: true, message: 'title is required', trigger: 'blur' }], company: [{ required: true, message: 'company is required', trigger: 'blur' }], mapcenter: [{ required: true, message: 'mapcenter is required', trigger: 'blur' }], province: [{ required: true, message: 'province is required', trigger: 'blur' }], city: [{ required: true, message: 'city is required', trigger: 'blur' }], area: [{ required: true, message: 'area is required', trigger: 'blur' }], email: [ { required: true, message: 'Email is required', trigger: 'blur' }, { type: 'email', message: 'Please input correct email address', trigger: ['blur', 'change'] }, ], password: [{ required: true, message: 'Password is required', trigger: 'blur' }], confirmPassword: [{ validator: validateConfirmPassword, trigger: 'blur' }], }, permissionProps: { children: 'children', label: 'name', disabled: 'disabled', }, permissions: [], menuPermissions: [], otherPermissions: [], }; }, computed: { normalizedMenuPermissions() { let tmp = []; this.currentUser.permissions.role.forEach(permission => { tmp.push({ id: permission.id, name: permission.name, disabled: true, }); }); const rolePermissions = { id: -1, // Just a faked ID name: 'Inherited from role', disabled: true, children: this.classifyPermissions(tmp).menu, }; tmp = this.menuPermissions.filter(permission => !this.currentUser.permissions.role.find(p => p.id === permission.id)); const userPermissions = { id: 0, // Faked ID name: 'Extra menus', children: tmp, disabled: tmp.length === 0, }; return [rolePermissions, userPermissions]; }, normalizedOtherPermissions() { let tmp = []; this.currentUser.permissions.role.forEach(permission => { tmp.push({ id: permission.id, name: permission.name, disabled: true, }); }); const rolePermissions = { id: -1, name: 'Inherited from role', disabled: true, children: this.classifyPermissions(tmp).other, }; tmp = this.otherPermissions.filter(permission => !this.currentUser.permissions.role.find(p => p.id === permission.id)); const userPermissions = { id: 0, name: 'Extra permissions', children: tmp, disabled: tmp.length === 0, }; return [rolePermissions, userPermissions]; }, userMenuPermissions() { return this.classifyPermissions(this.userPermissions).menu; }, userOtherPermissions() { return this.classifyPermissions(this.userPermissions).other; }, userPermissions() { return this.currentUser.permissions.role.concat(this.currentUser.permissions.user); }, }, created() { this.resetNewUser(); this.getList(); if (checkPermission(['manage permission'])) { this.getPermissions(); } }, methods: { mapConfirmData(text) { if (text.state === true) { this.newUser.mapcenter = text.location; this.drawerMap = false; } if (text.state === false) { this.drawerMap = false; } }, getLnglat(lnglnt) { console.log(lnglnt); }, checkPermission, async getPermissions() { const { data } = await permissionResource.list({}); const { all, menu, other } = this.classifyPermissions(data); this.permissions = all; this.menuPermissions = menu; this.otherPermissions = other; }, async getList() { const { limit, page } = this.query; this.loading = true; const { data, meta } = await userResource.list(this.query); this.list = data; this.list.forEach((element, index) => { element['index'] = (page - 1) * limit + index + 1; }); this.total = meta.total; this.loading = false; }, handleFilter() { this.query.page = 1; this.getList(); }, handleCreate() { this.resetNewUser(); this.areachinas(); this.drawer = true; this.$nextTick(() => { this.$refs['userForm'].clearValidate(); }); }, // 获取地址 areachinas() { areachina() .then(response => { this.province = response.data; }) .catch(err => { console.log(err); }); }, postcity(id) { var data = { areaid: id, }; areachinapost(data) .then(response => { this.city = response.data; }) .catch(err => { console.log(err); }); }, positionUrl() { window.open('latlge.html'); }, postarea(id) { var data = { areaid: id, }; areachinapost(data) .then(response => { this.area = response.data; }) .catch(err => { console.log(err); }); }, handleClose(done) { this.$confirm('确认关闭?') .then(_ => { done(); }) .catch(_ => { }); }, handleDelete(id, name) { this.$confirm('This will permanently delete user ' + name + '. Continue?', 'Warning', { confirmButtonText: 'OK', cancelButtonText: 'Cancel', type: 'warning', }).then(() => { userResource.destroy(id).then(response => { this.$message({ type: 'success', message: 'Delete completed', }); this.handleFilter(); }).catch(error => { console.log(error); }); }).catch(() => { this.$message({ type: 'info', message: 'Delete canceled', }); }); }, async handleEditPermissions(id) { this.currentUserId = id; this.dialogPermissionLoading = true; this.dialogPermissionVisible = true; const found = this.list.find(user => user.id === id); const { data } = await userResource.permissions(id); this.currentUser = { id: found.id, name: found.name, permissions: data, }; this.dialogPermissionLoading = false; this.$nextTick(() => { this.$refs.menuPermissions.setCheckedKeys(this.permissionKeys(this.userMenuPermissions)); this.$refs.otherPermissions.setCheckedKeys(this.permissionKeys(this.userOtherPermissions)); }); }, createUser() { this.$refs['userForm'].validate((valid) => { if (valid) { this.newUser.roles = [this.newUser.role]; this.userCreating = true; userResource .store(this.newUser) .then(response => { this.$message({ message: 'New user ' + this.newUser.name + '(' + this.newUser.email + ') has been created successfully.', type: 'success', duration: 5 * 1000, }); this.resetNewUser(); this.drawer = false; this.handleFilter(); }) .catch(error => { console.log(error); }) .finally(() => { this.userCreating = false; }); } else { console.log('error submit!!'); return false; } }); }, resetNewUser() { this.newUser = { name: '', email: '', password: '', confirmPassword: '', role: 'user', }; }, handleDownload() { this.downloading = true; import('@/vendor/Export2Excel').then(excel => { const tHeader = ['id', 'user_id', 'name', 'email', 'role']; const filterVal = ['index', 'id', 'name', 'email', 'role']; const data = this.formatJson(filterVal, this.list); excel.export_json_to_excel({ header: tHeader, data, filename: 'user-list', }); this.downloading = false; }); }, formatJson(filterVal, jsonData) { return jsonData.map(v => filterVal.map(j => v[j])); }, permissionKeys(permissions) { return permissions.map(permssion => permssion.id); }, classifyPermissions(permissions) { const all = []; const menu = []; const other = []; permissions.forEach(permission => { const permissionName = permission.name; all.push(permission); if (permissionName.startsWith('view menu')) { menu.push(this.normalizeMenuPermission(permission)); } else { other.push(this.normalizePermission(permission)); } }); return { all, menu, other }; }, normalizeMenuPermission(permission) { return { id: permission.id, name: this.$options.filters.uppercaseFirst(permission.name.substring(10)), disabled: permission.disabled || false }; }, normalizePermission(permission) { const disabled = permission.disabled || permission.name === 'manage permission'; return { id: permission.id, name: this.$options.filters.uppercaseFirst(permission.name), disabled: disabled }; }, confirmPermission() { const checkedMenu = this.$refs.menuPermissions.getCheckedKeys(); const checkedOther = this.$refs.otherPermissions.getCheckedKeys(); const checkedPermissions = checkedMenu.concat(checkedOther); this.dialogPermissionLoading = true; userResource.updatePermission(this.currentUserId, { permissions: checkedPermissions }).then(response => { this.$message({ message: 'Permissions has been updated successfully', type: 'success', duration: 5 * 1000, }); this.dialogPermissionLoading = false; this.dialogPermissionVisible = false; }); }, }, }; </script> <style lang="scss" scoped> .edit-input { padding-right: 100px; } .cancel-btn { position: absolute; right: 15px; top: 10px; } .dialog-footer { text-align: left; padding-top: 0; margin-left: 150px; } .app-container { flex: 1; justify-content: space-between; font-size: 14px; padding-right: 8px; .block { float: left; min-width: 250px; } .clear-left { clear: left; } .zl-drawer .el-drawer__body { overflow-y: auto !important; } } </style>