群控系统服务端开发模式-应用开发-前端管理员功能开发
一、添加视图
在根目录下src文件夹下views文件夹下permission文件夹下admin文件夹下,新建index.vue,代码如下
<template>
<div class="app-container">
<div class="filter-container" style="float:left;">
<el-form :inline="true" :model="searchParams" class="demo-form-inline">
<el-form-item>
<el-input v-model="searchParams.id" style="width: 160px;" placeholder="请输入ID" clearable/>
</el-form-item>
<el-form-item>
<el-input v-model="searchParams.username" style="width: 180px;" placeholder="请输入账号名称" clearable/>
</el-form-item>
<el-form-item>
<el-input v-model="searchParams.email" style="width: 180px;" placeholder="请输入登录账号" clearable/>
</el-form-item>
<el-form-item>
<el-select
v-model="searchParams.role_id"
placeholder="请选择角色组"
>
<el-option v-for="item in roleList" :key="item.id" :label="item.rolename" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button class="search-btn el-button--infoSearch" type="primary" @click="search()">搜索</el-button>
<el-button class="search-btn el-button--infoSearch" style="background:#F2F6FC;"
@click="clearSearch">重置
</el-button>
</el-form-item>
</el-form>
</div>
<div class="filter-container" style="float:right;">
<el-button v-if="$store.getters.butts.includes('PermissionAdminIndexAdd')" class="filter-item" style="margin-left: 10px;" type="primary" @click="handleAdd">添加</el-button>
<el-button class="filter-item" style="margin-left: 10px;" @click="search(currentPage)">刷新</el-button>
<el-button v-if="$store.getters.butts.includes('PermissionAdminIndexTostatus')" class="filter-item" style="margin-left: 10px;" @click="handleStatus(1)" type="success">启用</el-button>
<el-button v-if="$store.getters.butts.includes('PermissionAdminIndexTostatus')" class="filter-item" style="margin-left: 10px;" @click="handleStatus(0)" type="warning">禁用</el-button>
<el-button v-if="$store.getters.butts.includes('PermissionAdminIndexDelete')" class="filter-item" @click="handleDelete" type="danger">删除
</el-button>
</div>
<el-table
ref="resTable"
v-loading="listLoading"
:data="list"
row-key="id"
highlight-current-row
max-height="750"
default-expand-all
style="width: 100%;margin-top:10px;"
border
:default-sort="{prop: 'id', order: 'descending'}"
>
<el-table-column type="selection" width="50" align="center" :selectable="canSelect"/>
<el-table-column align="center" label="ID" sortable prop="id">
<template slot-scope="{row}">
<span>{{ row.id }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="账号名称">
<template slot-scope="{row}">
<span>{{ row.username }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="登录账号">
<template slot-scope="{row}">
<span>{{ row.email }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="真实名称">
<template slot-scope="{row}">
<span>{{ row.realname }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="头像">
<template slot-scope="{row}">
<div class="demo-image__preview">
<el-image style="width:40px;height:40px" :src="row.avatar"/>
</div>
</template>
</el-table-column>
<el-table-column align="center" label="角色组">
<template slot-scope="{row}">
<span>{{ row.role_name }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="状态">
<template slot-scope="{row}">
<el-tag type="success" v-if="row.status === 1">启用</el-tag>
<el-tag type="warning" v-if="row.status === 0">禁用</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="添加时间">
<template slot-scope="{row}">
<span>{{ row.create_time }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="修改时间">
<template slot-scope="{row}">
<span>{{ row.update_time }}</span>
</template>
</el-table-column>
<el-table-column prop="action" align="center" label="操作" width="210">
<template slot-scope="{row}">
<el-button v-if="$store.getters.butts.includes('PermissionAdminIndexDetails')" size="mini" @click="handleDetails(row.id)" type="info">详情</el-button>
<el-button v-if="$store.getters.butts.includes('PermissionAdminIndexEdit')" size="mini" @click="handleEdit(row.id)" type="primary">编辑</el-button>
<el-button v-if="$store.getters.butts.includes('PermissionAdminIndexDelete') && row.id > 1" size="mini" @click="handleDelete(row.id)" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="block">
<el-pagination
:hide-on-single-page="true"
:current-page="currentPage"
:page-sizes="pageSizes"
:page-size="currentSize"
layout="total, sizes, prev, pager, next, jumper"
:total="dataTotal"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<transition name="dialog-fade">
<el-dialog :visible.sync="dialogVisible" :title="resTemp.id === 0 ? '添加' : '编辑'"
:close-on-click-modal="false" :close-on-press-escape="false">
<el-form ref="resForm" :rules="formRules" :model="resTemp" label-position="right" label-width="100px">
<el-form-item label="角色分组" prop="role_id" class="roleId">
<el-select
v-model="resTemp.role_id"
placeholder="请选择角色组"
>
<el-option v-for="item in roleList" :key="item.id" :label="item.rolename" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item label="账号名称" prop="username">
<el-input v-model="resTemp.username" placeholder="账号名称必须填写"/>
</el-form-item>
<el-form-item label="登录账号" prop="email">
<el-input v-model="resTemp.email" placeholder="登录账号必须是邮箱形式"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input :placeholder="resTemp.id === 0 ? '密码必须是两种形式以上' : '留空则不修改'" v-model="resTemp.password"
show-password/>
</el-form-item>
<el-form-item label="确认密码" prop="confirm_password">
<el-input :placeholder="resTemp.id === 0 ? '请再次输入密码' : '留空则不修改'"
v-model="resTemp.confirm_password" show-password/>
</el-form-item>
<el-form-item label="头像" prop="avatar">
<el-upload
:action="action"
:headers="customHeaders"
list-type="picture-card"
:on-success="uploadSuccess"
:on-error="uploadError"
:show-file-list="false"
:before-upload="beforeUpload"
:accept="imageAccept"
:on-progress="uploadProcess"
:data="uploadData"
>
<img v-if="resTemp.avatar != '' && !uploadFlag" ref="avatar" :src="resTemp.avatar" style="width:148px;height:148px;">
<i v-else-if="resTemp.avatar =='' && !uploadFlag" class="el-icon-plus"></i>
<el-progress v-if="uploadFlag == true" type="circle" :percentage="uploadPercent" style="width:148px;height:148px;margin:10px 0 0 10px;"></el-progress>
</el-upload>
</el-form-item>
<el-form-item label="部门" prop="department_id">
<el-radio-group v-model="resTemp.department_id">
<el-radio-button v-for="item in departmentList" :key="item.id" :label="item.id">{{item.title}}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="数据级别" prop="grade_id">
<el-radio-group v-model="resTemp.grade_id">
<el-radio-button v-for="item in gradeList" :key="item.id" :label="item.id">{{item.title}}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="真实姓名" prop="realname">
<el-input v-model="resTemp.realname" placeholder="真实姓名必须填写"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible=false">取消</el-button>
<el-button v-if="$store.getters.butts.includes('PermissionAdminIndexSave')" type="primary"
@click="saveInfo()">提交
</el-button>
</div>
</el-dialog>
</transition>
<transition name="dialog-fade">
<el-dialog :visible.sync="dialogDetails" title="详情" :close-on-click-modal="false" :close-on-press-escape="false">
<el-form ref="resForm" :model="resTemp" label-position="right" label-width="100px">
<el-form-item label="角色分组" prop="role_id" class="roleId">
<el-select
v-model="resTemp.role_id"
placeholder="请选择角色组"
disabled
>
<el-option v-for="item in roleList" :key="item.id" :label="item.rolename" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item label="账号名称" prop="username">
<el-input v-model="resTemp.username" placeholder="账号名称必须填写" disabled />
</el-form-item>
<el-form-item label="登录账号" prop="email">
<el-input v-model="resTemp.email" placeholder="登录账号必须是邮箱形式" disabled />
</el-form-item>
<el-form-item label="头像" prop="avatar">
<el-upload
:action="action"
:headers="customHeaders"
list-type="picture-card"
:on-success="uploadSuccess"
:on-error="uploadError"
:show-file-list="false"
:before-upload="beforeUpload"
:accept="imageAccept"
:on-progress="uploadProcess"
:data="uploadData"
disabled
>
<img v-if="resTemp.avatar != '' && !uploadFlag" ref="avatar" :src="resTemp.avatar" style="width:148px;height:148px;">
<i v-else-if="resTemp.avatar =='' && !uploadFlag" class="el-icon-plus"></i>
<el-progress v-if="uploadFlag == true" type="circle" :percentage="uploadPercent" style="width:148px;height:148px;margin:10px 0 0 10px;"></el-progress>
</el-upload>
</el-form-item>
<el-form-item label="部门" prop="department_id">
<el-radio-group v-model="resTemp.department_id" disabled >
<el-radio-button v-for="item in departmentList" :key="item.id" :label="item.id">{{item.title}}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="数据级别" prop="grade_id">
<el-radio-group v-model="resTemp.grade_id" disabled >
<el-radio-button v-for="item in gradeList" :key="item.id" :label="item.id">{{item.title}}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="真实姓名" prop="realname">
<el-input v-model="resTemp.realname" placeholder="真实姓名必须填写" disabled />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-switch
v-model="resTemp.status"
active-value="1"
inactive-value="0"
disabled
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogDetails=false">取消</el-button>
</div>
</el-dialog>
</transition>
</div>
</template>
<script>
import {validEmail, validPassword} from '@/utils/validate'
import {err, succ, warn} from '@/utils/message'
import { deleteInfo, getInfo, getList, saveInfo, statusInfo } from '@/api/permission/admin'
import { getAll } from '@/api/permission/role'
import { getAll as getDepartmentAll } from '@/api/param/department'
import { getAll as getGradeAll } from '@/api/param/grade'
import { get_info } from '@/api/param/upload'
import {getToken} from '@/utils/auth'
import Encrypt from 'encryptlong' // encryptlong是基于jsencrypt扩展的长文本分段加解密功能。
const baseUrl = process.env.VUE_APP_BASE_API
export default {
name: 'PermissionAdminIndex', // 名空间
// 初始化数据
data() {
const validateEmail = (rule, value, callback) => {
if (!validEmail(value)) {
callback(new Error('请输入正确的邮箱'))
} else {
callback()
}
}
// 密码验证 添加时必须填写 编辑时如果没有填写则不修改,填写了则修改
const validatePassword = (rule, value, callback) => {
if (this.resTemp.id === 0) {
if (value === '') {
callback(new Error('请填写密码'))
} else {
const pwRel = validPassword(value)
if (pwRel !== true) {
callback(new Error(pwRel))
} else {
callback()
}
}
} else {
if (value !== undefined && value !== '') {
const pwRel = validPassword(value)
if (pwRel !== true) {
callback(new Error(pwRel))
} else {
callback()
}
} else {
callback()
}
}
}
// 确认密码验证 添加时必须填写 编辑时如果没有填写则不验证,填写了且密码也填写了才与密码配对
const validateConfirmPassword = (rule, value, callback) => {
if (this.resTemp.id === 0) {
if (value === '') {
callback(new Error('请再次输入密码'))
} else {
if (value !== this.resTemp.password) {
callback(new Error('两次密码不一致,请重新输入'))
} else {
callback()
}
}
} else {
if (value !== undefined && this.resTemp.password !== undefined) {
if (value !== this.resTemp.password) {
callback(new Error('两次密码不一致,请重新输入'))
} else {
callback()
}
} else {
callback()
}
}
}
return {
customHeaders:{
Authorization: ""
},
upload_number: 0,//最大上传大小
uploadFlag:false,//上传是否显示滚动条
uploadPercent:"",//上传滚动条进度
action:baseUrl + '/upload/file', //用来强制刷新
imageAccept:'',//图片上传类型
imageOptions: [],//图片类型
uploadData:{type:'image'},
list: [], // 初始化管理员列表
roleList: [], // 初始化权限组列表
departmentList: [], // 初始化部门列表
gradeList: [], // 初始化数据级别列表
pageSizes: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500],
currentPage: 1, // 当前页数
currentSize: 10, // 每页条数
dataTotal: 0, // 总数据
searchParams: {
id: '',
username: '',
email: '',
role_id: ''
},
// 要提交数据
resTemp: {
id: 0, // 0 添加 >0 编辑
username: '',
email: '',
password: '',
confirm_password: '',
avatar: '',
realname: '',
status: '1',
department_id: 1,
grade_id:1,
role_id: 1
},
// 要验证数据
formRules: {
role_id: [{required: true, trigger: 'change', message: '权限必须选择'}],
username: [
{required: true, trigger: 'blur', message: '账号名称必须填写'},
{min: 2, max: 40, message: '长度在 2 到 40 个字符', trigger: 'blur'}
],
email: [{required: true, trigger: 'blur', validator: validateEmail}],
password: [{required: true, trigger: 'blur', validator: validatePassword}],
confirm_password: [{required: true, trigger: 'blur', validator: validateConfirmPassword}],
avatar: [{required: true, trigger: 'blur', message: '头像必须上传'}],
department_id: [{required: true, trigger: 'change', message: '部门必须选择'}],
grade_id: [{required: true, trigger: 'change', message: '数据级别必须选择'}],
realname: [
{required: true, trigger: 'blur', message: '真实名称必须填写'},
{min: 2, max: 30, message: '长度在 2 到 30 个字符', trigger: 'blur'}
],
},
listLoading: true, // 显示list加载框
dialogVisible: false, // 初始化弹窗是否显示
dialogDetails: false
}
},
// 初始化执行的
created() {
this.getList()// 获取管理员数据
this.getAll()// 获取权限组数据
this.getParamUpload()// 获取参数上传数据
this.accToken()// 获取token
this.getDepartmentAll(); // 获取部门
this.getGradeAll(); // 获取数据级别
},
// 所有的方法都需要放到这个里面
methods: {
// 搜索
search() {
this.getList(1)
},
// 管理员数据 --异步
async getList(page = 0) {
this.listLoading = true
const params = {
currentPage: page === 0 ? this.currentPage : page,
currentSize: this.currentSize,
...this.searchParams
}
await getList(params).then(res => {
this.list = res.data.list
this.dataTotal = res.data.meat.total * 1
})
// 延时
setTimeout(() => {
this.listLoading = false
}, 0.5 * 1000)
},
// 权限数据 --异步
async getAll() {
const lists = await getAll()
this.roleList = lists.data
},
// 部门数据 --异步
async getDepartmentAll(){
const lists = await getDepartmentAll()
this.departmentList = lists.data
},
// 数据级别 --异步
async getGradeAll(){
const lists = await getGradeAll()
this.gradeList = lists.data
},
//token生成
accToken(){
// 公钥key
const publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYW5f4L5yz1myyGXrvEmrAlfpV\n" +
"HHRrZKsYdAGBvx0IHRV3zwCnACAFnpBQqfreZX3TKUBbA7Kfyh1XG7ytW6reBGLO\n" +
"xjJC2SncYXjtEydCMdjMaY0BSAV2J9238If5K5htt0pGWkUVt6BnTPAWUDzwRYQN\n" +
"XZaDIIkQ1yBLvJPfQQIDAQAB";
const PUBLIC_KEY = publicKey
var encryptor = new Encrypt()
encryptor.setPublicKey(PUBLIC_KEY)
// 如果是对象/数组的话,需要先JSON.stringify转换成字符串
var Base64 = require('js-base64').Base64
this.customHeaders.Authorization= encryptor.encryptLong(Base64.decode(getToken()))
},
//获取上传文件
async getParamUpload() {
await get_info({id: 1}).then(res => {
var row = res.data
const checkedImage = row.checked_images.split(',')// 拆分成数组
for (let i=0;i<checkedImage.length;i++){
if(checkedImage[i].length <= 3){
this.imageOptions.push('.'+checkedImage[i]);
}else{
this.imageOptions.push(checkedImage[i]);
}
this.imageAccept += '.'+checkedImage[i]+',';
}
this.upload_number = row.upload_number * 1;
})
},
// 清除搜索功能
clearSearch() {
this.searchParams = {
id: '',
username: '',
email: '',
role_id: ''
}
this.getList()
},
// 重置表单数据 ---添加时候需要使用
resetTemp() {
this.resTemp = {
id: 0, // 0 添加 >0 编辑
username: '',
realname: '',
email: '',
password: '',
confirm_password: '',
avatar: '',
status: '1',
department_id: 1,
grade_id: 1,
role_id: 1
}
},
// 添加方法
handleAdd() {
this.resetTemp()
this.dialogVisible = true
this.$nextTick(() => {
this.$refs['resForm'].clearValidate()
})
},
// 编辑方法
handleEdit(id) {
getInfo({id: id}).then(res => {
const row = res.data
this.resTemp = Object.assign({}, row)
this.resTemp.status = row.status + ''// 将数字强行转换成字符串
this.dialogVisible = true
this.$nextTick(() => {
this.$refs['resForm'].clearValidate()
})
})
},
// 详情
handleDetails(id){
getInfo({ id: id }).then(res => {
const row = res.data
this.resTemp = Object.assign({}, row)
this.resTemp.status = row.status + ''// 将数字强行转换成字符串
this.dialogDetails = true
this.$nextTick(() => {
this.$refs['resForm'].clearValidate()
})
})
},
// 禁止选择超级管理员账户
canSelect(row) {
return row.id === 1 ? 0 : 1
},
// 新建保存数据
async saveInfo() {
this.$refs.resForm.validate(valid => {
if (valid) {
this.loading = true
saveInfo(this.resTemp).then(res => {
this.loading = false
succ(res.message)
this.getList()
this.dialogVisible = false
})
} else {
return false
}
})
},
// 删除
handleDelete(id) {
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(async () => {
const ids = []
if (id > 0) { // 单个删除
ids.push(id)
} else { // 批量删除
const select = this.$refs.resTable.selection
if (select.length === 0) {
warn('批量删除必须选择指定产品')
return false
}
// 组合数据
select.forEach(item => {
ids.push(item.id)
})
}
// 删除
deleteInfo({id: ids}).then(res => {
this.getList()// 更新列表
succ(res.message)// 提示结果
})
}).catch(err => {
err(err.message)
return false
})
},
// 启禁用
handleStatus(status){
let statusText = status == 1 ? '启用' : '禁用';
this.$confirm('此操作将永久'+ statusText +'该产品, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(async() => {
const ids = []
const select = this.$refs.resTable.selection
if (select.length === 0) {
warn('批量'+ statusText +'必须选择指定产品')
return false
}
// 组合数据
select.forEach(item => {
ids.push(item.id)
})
// 删除
statusInfo({ id: ids, status:status }).then(res => {
this.getList()//更新列表
succ(res.message)// 提示结果
})
}).catch(err => {
err(err.message)
return false
})
},
// 每页条数切换
handleSizeChange(val) {
this.currentSize = val
this.getList()
},
// 页数切换
handleCurrentChange(val) {
this.currentPage = val
this.getList()
},
// 上传前验证
beforeUpload(file) {
let fileName = file.name;
let fileType = fileName.substring(fileName.length*1 - 4,fileName.length);
if(this.imageOptions.findIndex((v)=>(v===fileType)) === -1){
err('图片格式只能是 JPG|png|gif 格式!')
return false
}
const isLt3M = file.size / 1024 / 1024 < this.upload_number
if (!isLt3M) {
err('上传文件大小不能超过'+this.upload_number+'MB!')
return false
}
},
//上传进度条
uploadProcess(event, file){
this.uploadFlag = true;
this.uploadPercent = file.percentage.toFixed(0)*1;
this.accToken()//获取token
},
//上传成功提醒
uploadSuccess(res){
this.uploadFlag = false;
this.uploadPercent = 0;
if(res.code == 20000){
this.resTemp.avatar = res.data.url
succ(res.message)
}else{
err(res.message)
}
return false
},
//上传错误提醒
uploadError(er){
if(er != ''){
err('上传失败')
}
return false
}
}
}
</script>
<style lang="scss" scoped>
.app-container {
padding: 10px;
.roles-table {
margin-top: 30px;
}
.permission-tree {
margin-bottom: 30px;
}
}
.el-image-viewer__close {
color: #fff;
}
.roleId .el-select {
width: 100%;
}
.avatar-uploader {
border: 1px dotted #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
width: 102px;
height: 102px;
}
.avatar-uploader:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 100px;
height: 100px;
line-height: 100px;
text-align: center;
}
.avatar {
width: 100px;
height: 100px;
border: 1px dotted #d9d9d9;
cursor: pointer;
display: block;
}
</style>
二、添加ajax请求
在根目录下src文件夹下api文件夹下permission文件夹下admin.js,代码如下
import request from '@/utils/request'
// 列表
export function getList(params) {
return request({
url: '/permission/admin/get_list',
method: 'get',
params:params
})
}
// 所有
export function getAll() {
return request({
url: '/permission/admin/get_all',
method: 'post'
})
}
// 获取
export function getInfo(data) {
return request({
url: '/permission/admin/get_info',
method: 'post',
data
})
}
// 保存
export function saveInfo(data) {
return request({
url: '/permission/admin/save_info',
method: 'post',
data
})
}
// 删除
export function deleteInfo(data) {
return request({
url: '/permission/admin/delete_info',
method: 'post',
data
})
}
// 启禁用
export function statusInfo(data) {
return request({
url: '/permission/admin/status_info',
method: 'post',
data
})
}