feat(admin): add msh_single_admin project and harden ignore rules

Introduce the new Vue admin project into version control while tightening gitignore patterns to keep env files, logs, build artifacts, and test outputs out of commits.

Made-with: Cursor
This commit is contained in:
msh-agent
2026-04-15 19:31:32 +08:00
parent a840045fc1
commit ceac1c0340
713 changed files with 119926 additions and 0 deletions

View File

@@ -0,0 +1,202 @@
<template>
<div>
<el-form ref="pram" :model="pram" :rules="rules" label-width="100px" @submit.native.prevent>
<el-form-item label="管理员账号" prop="account">
<el-input v-model="pram.account" placeholder="管理员账号" />
</el-form-item>
<el-form-item label="管理员密码" prop="pwd">
<el-input v-model="pram.pwd" placeholder="管理员密码" clearable @input="handlerPwdInput" @clear="handlerPwdInput" />
</el-form-item>
<el-form-item v-if="pram.pwd" label="确认密码" prop="repwd">
<el-input v-model="pram.repwd" placeholder="确认密码" clearable />
</el-form-item>
<el-form-item label="管理员姓名" prop="realName">
<el-input v-model="pram.realName" placeholder="管理员姓名" />
</el-form-item>
<el-form-item label="管理员身份" prop="roles">
<el-select v-model="pram.roles" placeholder="身份" clearable multiple style="width: 100%">
<el-option v-for="(item, index) in roleList.list" :key="index" :label="item.roleName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input type="text" v-model="pram.phone" prefix="ios-contact-outline" placeholder="请输入手机号" size="large" />
</el-form-item>
<el-form-item label="状态">
<el-switch v-model="pram.status" :active-value="true" :inactive-value="false" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handlerSubmit('pram')"
v-hasPermi="['admin:system:admin:update', 'admin:system:admin:save']">{{ isCreate === 0 ? '確定' : '更新'
}}</el-button>
<el-button @click="close">取消</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import * as roleApi from '@/api/role.js';
import * as systemAdminApi from '@/api/systemadmin.js';
import { Debounce } from '@/utils/validate';
import { checkPermi } from '@/utils/permission'; // 权限判断函数
export default {
// name: "edit"
components: {},
props: {
isCreate: {
type: Number,
required: true,
},
editData: {
type: Object,
default: () => {
return { rules: [] };
},
},
},
data() {
const validatePhone = (rule, value, callback) => {
if (!value) {
return callback(new Error('请填写手机号'));
} else if (!/^1[3456789]\d{9}$/.test(value)) {
callback(new Error('手机号格式不正确!'));
} else {
callback();
}
};
const validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.pram.pwd) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
constants: this.$constants,
pram: {
account: null,
level: null,
pwd: null,
repwd: null,
realName: null,
roles: [],
status: null,
id: null,
phone: null,
},
roleList: [],
rules: {
account: [{ required: true, message: '请填管理员账号', trigger: ['blur', 'change'] }],
pwd: [{ required: true, message: '请填管理员密码', trigger: ['blur', 'change'] }],
repwd: [{ required: true, message: '确认密码密码', validator: validatePass, trigger: ['blur', 'change'] }],
realName: [{ required: true, message: '管理员姓名', trigger: ['blur', 'change'] }],
roles: [{ required: true, message: '管理员身份', trigger: ['blur', 'change'] }],
phone: [{ required: true, message: '请输入手机号', trigger: ['blur', 'change'] }],
},
};
},
mounted() {
this.initEditData();
if (checkPermi(['admin:system:role:list'])) this.handleGetRoleList();
},
methods: {
close() {
this.$emit('hideEditDialog');
},
handleGetRoleList() {
const _pram = {
page: 1,
limit: this.constants.page.limit[4],
status: 1,
};
roleApi.getRoleList(_pram).then((data) => {
this.roleList = data;
let arr = [];
data.list.forEach((item) => {
arr.push(item.id);
});
if (!arr.includes(Number.parseInt(this.pram.roles))) {
this.$set(this.pram, 'roles', []);
}
});
},
initEditData() {
if (this.isCreate !== 1) return;
const { account, realName, roles, level, status, id, phone } = this.editData;
this.pram.account = account;
this.pram.realName = realName;
const _roles = [];
if (roles.length > 0 && !roles.includes(',')) {
//如果权限id集合有长度并且是只有一个就将它Push进_roles这个数组
_roles.push(Number.parseInt(roles));
} else {
//否则就将多个id集合解构以后push进roles并且转换为整型
_roles.push(...roles.split(',').map((item) => Number.parseInt(item)));
}
this.pram.roles = _roles;
this.pram.status = status;
this.pram.id = id;
this.pram.phone = phone;
this.rules.pwd = [];
this.rules.repwd = [];
},
handlerSubmit: Debounce(function (form) {
this.$refs[form].validate((valid) => {
if (!valid) return;
if (this.isCreate === 0) {
this.handlerSave();
} else {
this.handlerEdit();
}
});
}),
handlerSave() {
systemAdminApi.adminAdd(this.pram).then((data) => {
this.$message.success('创建管理员成功');
this.$emit('hideEditDialog');
});
},
handlerEdit() {
this.pram.roles = this.pram.roles.join(',');
systemAdminApi.adminUpdate(this.pram).then((data) => {
this.$message.success('更新管理员成功');
this.$emit('hideEditDialog');
});
},
rulesSelect(selectKeys) {
this.pram.rules = selectKeys;
},
handlerPwdInput(val) {
if (!val) {
this.rules.pwd = [];
this.rules.repwd = [];
return;
}
this.rules.pwd = [
{ required: true, message: '请填管理员密码', trigger: ['blur', 'change'] },
{ min: 6, max: 20, message: '长度6-20个字符', trigger: ['blur', 'change'] },
];
this.rules.repwd = [
{
required: true,
message: '两次输入密码不一致',
validator: (rule, value, callback) => {
if (value === '') {
callback(new Error('两次输入密码不一致!'));
} else if (value !== this.pram.pwd) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
},
trigger: ['blur', 'change'],
},
];
},
},
};
</script>
<style scoped></style>

View File

@@ -0,0 +1,294 @@
<template>
<div class="divBox">
<el-card class="box-card">
<el-form inline size="small" @submit.native.prevent>
<el-form-item>
<el-select v-model="listPram.roles" placeholder="身份" clearable class="selWidth">
<el-option v-for="item in roleList.list" :key="item.id" :label="item.roleName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="listPram.status" placeholder="状态" clearable class="selWidth">
<el-option
v-for="item in constants.roleListStatus"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="listPram.realName" placeholder="姓名或者账号" clearable class="selWidth" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
</el-form-item>
</el-form>
<el-form inline @submit.native.prevent>
<el-form-item>
<el-button type="primary" @click="handlerOpenEdit(0)" v-hasPermi="['admin:system:admin:save']"
>添加管理员</el-button
>
</el-form-item>
</el-form>
<el-table :data="listData.list" size="mini" :header-cell-style="{ fontWeight: 'bold' }">
<el-table-column prop="id" label="ID" width="50" />
<el-table-column label="姓名" prop="realName" min-width="120" />
<el-table-column label="账号" prop="account" min-width="120" />
<el-table-column label="手机号" prop="lastTime" min-width="120">
<template slot-scope="scope">
<span>{{ scope.row.phone | filterEmpty }}</span>
</template>
</el-table-column>
<el-table-column label="身份" prop="realName" min-width="230">
<template slot-scope="scope" v-if="scope.row.roleNames">
<el-tag
size="small"
type="info"
v-for="(item, index) in scope.row.roleNames.split(',')"
:key="index"
class="mr5"
>{{ item }}</el-tag
>
</template>
</el-table-column>
<el-table-column label="最后登录时间" prop="lastTime" min-width="180">
<template slot-scope="scope">
<span>{{ scope.row.lastTime | filterEmpty }}</span>
</template>
</el-table-column>
<el-table-column label="最后登录IP" prop="lastIp" min-width="150">
<template slot-scope="scope">
<span>{{ scope.row.lastIp | filterEmpty }}</span>
</template>
</el-table-column>
<el-table-column label="状态" min-width="100">
<template slot-scope="scope">
<el-switch
v-if="checkPermi(['admin:system:admin:update:status'])"
v-model="scope.row.status"
:active-value="true"
:inactive-value="false"
active-text="开启"
inactive-text="关闭"
@change="onchangeIsShow(scope.row)"
/>
<span v-else>{{scope.row.status?'开启':'关闭'}}</span>
</template>
</el-table-column>
<el-table-column label="是否接收短信" min-width="100">
<template slot-scope="scope">
<el-switch
v-if="checkPermi(['admin:system:admin:update:sms'])"
v-model="scope.row.isSms"
:active-value="true"
:inactive-value="false"
active-text="开启"
inactive-text="关闭"
:disabled="!scope.row.phone"
@click.native="onchangeIsSms(scope.row)"
/>
<span v-else>{{scope.row.isSms?'开启':'关闭'}}</span>
</template>
</el-table-column>
<el-table-column label="删除标记" prop="status" min-width="100">
<template slot-scope="scope">
<span>{{ scope.row.isDel | filterYesOrNo }}</span>
</template>
</el-table-column>
<el-table-column label="操作" min-width="130" fixed="right">
<template slot-scope="scope">
<template v-if="scope.row.isDel">
<span>-</span>
</template>
<template v-else>
<el-button
type="text"
@click="handlerOpenEdit(1, scope.row)"
v-hasPermi="['admin:system:admin:info']"
>编辑</el-button
>
<el-button
type="text"
@click="handlerOpenDel(scope.row)"
v-hasPermi="['admin:system:admin:delete']"
>删除</el-button
>
</template>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="listPram.page"
:page-sizes="constants.page.limit"
:layout="constants.page.layout"
:total="listData.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
<el-dialog
:visible.sync="editDialogConfig.visible"
:title="editDialogConfig.isCreate === 0 ? '创建身份' : '编辑身份'"
destroy-on-close
:close-on-click-modal="false"
width="700px"
>
<edit
v-if="editDialogConfig.visible"
:is-create="editDialogConfig.isCreate"
:edit-data="editDialogConfig.editData"
@hideEditDialog="hideEditDialog"
/>
</el-dialog>
</div>
</template>
<script>
import * as systemAdminApi from '@/api/systemadmin.js';
import * as roleApi from '@/api/role.js';
import edit from './edit';
import { checkPermi } from '@/utils/permission'; // 权限判断函数
export default {
// name: "index"
components: { edit },
data() {
return {
constants: this.$constants,
listData: { list: [] },
listPram: {
account: null,
addTime: null,
lastIp: null,
lastTime: null,
level: null,
loginCount: null,
realName: null,
roles: null,
status: null,
page: 1,
limit: this.$constants.page.limit[0],
},
roleList: [],
menuList: [],
editDialogConfig: {
visible: false,
isCreate: 0, // 0=创建1=编辑
editData: {},
},
};
},
mounted() {
this.handleGetAdminList();
this.handleGetRoleList();
},
methods: {
checkPermi,
onchangeIsShow(row) {
systemAdminApi
.updateStatusApi({ id: row.id, status: row.status })
.then(async () => {
this.$message.success('修改成功');
this.handleGetAdminList();
})
.catch(() => {
row.status = !row.status;
});
},
onchangeIsSms(row) {
// this.$confirm(`此操作将${!row.isSms ? '开启' : '关闭'}验证, 是否继续?`, "提示", {
// confirmButtonText: "确定",
// cancelButtonText: "取消",
// type: "warning"
// }).then(async () => {
// row.isSms = !row.isSms
// }).catch(() => {
// this.$message.error('取消操作')
// })
if (!row.phone)
return this.$message({
message: '请先为管理员添加手机号!',
type: 'warning',
});
systemAdminApi
.updateIsSmsApi({ id: row.id })
.then(async () => {
this.$message.success('修改成功');
this.handleGetAdminList();
})
.catch(() => {
row.isSms = !row.isSms;
});
},
handleSearch() {
this.listPram.page = 1;
this.handleGetAdminList();
},
handleSizeChange(val) {
this.listPram.limit = val;
this.handleGetAdminList();
this.handleGetRoleList(this.listPram);
},
handleCurrentChange(val) {
this.listPram.page = val;
this.handleGetAdminList();
this.handleGetRoleList(this.listPram);
},
handleGetRoleList() {
const _pram = {
page: 1,
limit: this.constants.page.limit[4],
};
roleApi.getRoleList(_pram).then((data) => {
this.roleList = data;
});
},
handlerOpenDel(rowData) {
this.$confirm('确认删除当前数据').then(() => {
const _pram = { id: rowData.id };
systemAdminApi.adminDel(_pram).then((data) => {
this.$message.success('删除数据成功');
this.handleGetAdminList();
});
});
},
handleGetAdminList() {
systemAdminApi.adminList(this.listPram).then((data) => {
this.listData = data;
// this.handlerGetMenuList()
});
},
handlerOpenEdit(isCreate, editDate) {
this.editDialogConfig.editData = editDate;
this.editDialogConfig.isCreate = isCreate;
this.editDialogConfig.visible = true;
},
handlerGetMenuList() {
// 获取菜单全部数据后做menu翻译使用
systemAdminApi.listCategroy({ page: 1, limit: 999, type: 5 }).then((data) => {
this.menuList = data.list;
this.listData.list.forEach((item) => {
const _muneText = [];
const menuids = item.rules.split(',');
menuids.map((muid) => {
this.menuList.filter((menu) => {
if (menu.id == muid) {
_muneText.push(menu.name);
}
});
});
item.rulesView = _muneText.join(',');
this.$set(item, 'rulesViews', item.rulesView);
});
});
},
hideEditDialog() {
this.editDialogConfig.visible = false;
this.handleGetAdminList();
},
},
};
</script>
<style scoped></style>

View File

@@ -0,0 +1,210 @@
<template>
<div>
<el-form ref="pram" :model="pram" label-width="100px" @submit.native.prevent>
<el-form-item
label="角色名称"
prop="roleName"
:rules="[{ required: true, message: '请填写角色名称', trigger: ['blur', 'change'] }]"
>
<el-input v-model="pram.roleName" placeholder="身份名称" />
</el-form-item>
<el-form-item label="状态">
<el-switch v-model="pram.status" :active-value="true" :inactive-value="false" />
</el-form-item>
<el-form-item label="菜单权限">
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">展开/折叠</el-checkbox>
<!-- <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox> -->
<el-checkbox v-model="menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')"
>父子联动</el-checkbox
>
<el-tree
class="tree-border"
:data="menuOptions"
show-checkbox
ref="menu"
node-key="id"
:check-strictly="!menuCheckStrictly"
empty-text="加载中请稍候"
:props="defaultProps"
></el-tree>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="handlerSubmit('pram')"
v-hasPermi="['admin:system:role:update', 'admin:system:role:save']"
>{{ isCreate === 0 ? '確定' : '更新' }}</el-button
>
<el-button @click="close">取消</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import * as roleApi from '@/api/role.js';
import { Debounce } from '@/utils/validate';
export default {
name: 'roleEdit',
props: {
isCreate: {
type: Number,
required: true,
},
editData: {
type: Object,
default: null,
},
},
data() {
return {
pram: {
roleName: null,
rules: '',
status: null,
id: null,
},
menuExpand: false,
menuNodeAll: false,
menuOptions: [],
menuCheckStrictly: true,
currentNodeId: [],
defaultProps: {
children: 'childList',
label: 'name',
},
menuIds: [],
};
},
mounted() {
this.initEditData();
this.getCacheMenu();
},
methods: {
close() {
this.$emit('hideEditDialog');
},
initEditData() {
if (this.isCreate !== 1) return;
const { roleName, status, id } = this.editData;
this.pram.roleName = roleName;
this.pram.status = status;
this.pram.id = id;
const loading = this.$loading({
lock: true,
text: 'Loading',
});
roleApi.getInfo(id).then((res) => {
this.menuOptions = res.menuList;
this.checkDisabled(this.menuOptions);
loading.close();
this.getTreeId(res.menuList);
this.$nextTick(() => {
this.menuIds.forEach((i, n) => {
var node = this.$refs.menu.getNode(i);
if (node.isLeaf) {
this.$refs.menu.setChecked(node, true);
}
});
});
});
},
handlerSubmit: Debounce(function (form) {
this.$refs[form].validate((valid) => {
if (!valid) return;
let roles = this.getMenuAllCheckedKeys().toString();
this.pram.rules = roles;
if (this.isCreate === 0) {
this.handlerSave();
} else {
this.handlerEdit();
}
});
}),
handlerSave() {
roleApi.addRole(this.pram).then((data) => {
this.$message.success('创建身份成功');
this.$emit('hideEditDialog');
});
},
handlerEdit() {
roleApi.updateRole(this.pram).then((data) => {
this.$message.success('更新身份成功');
this.$emit('hideEditDialog');
});
},
rulesSelect(selectKeys) {
this.pram.rules = selectKeys;
},
// 树权限(展开/折叠)
handleCheckedTreeExpand(value, type) {
if (type == 'menu') {
let treeList = this.menuOptions;
for (let i = 0; i < treeList.length; i++) {
this.$refs.menu.store.nodesMap[treeList[i].id].expanded = value;
}
}
},
// 树权限(全选/全不选)
handleCheckedTreeNodeAll(value, type) {
if (type == 'menu') {
this.$refs.menu.setCheckedNodes(value ? this.menuOptions : []);
}
},
// 树权限(父子联动)
handleCheckedTreeConnect(value, type) {
if (type == 'menu') {
this.menuCheckStrictly = value ? true : false;
}
},
// 所有菜单节点数据
getMenuAllCheckedKeys() {
// 目前被选中的菜单节点
let checkedKeys = this.$refs.menu.getCheckedKeys();
// 半选中的菜单节点
let halfCheckedKeys = this.$refs.menu.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
return checkedKeys;
},
getCacheMenu() {
if (this.isCreate !== 0) return;
const loading = this.$loading({
lock: true,
text: 'Loading',
});
roleApi.menuCacheList().then((res) => {
this.menuOptions = res;
this.checkDisabled(this.menuOptions);
loading.close();
});
},
getTreeId(datas) {
for (var i in datas) {
if (datas[i].checked) this.menuIds.push(datas[i].id);
if (datas[i].childList) {
this.getTreeId(datas[i].childList);
}
}
},
checkDisabled(data) {
//设置公共权限默认勾选且不可操作
data.forEach((item) => {
if (item.id === 280 || item.id === 294 || item.id === 344) {
item.disabled = true;
item.childList.forEach((item1) => {
item1.disabled = true;
this.$nextTick(() => {
var node = this.$refs.menu.getNode(item1.id);
if (node.isLeaf) {
this.$refs.menu.setChecked(node, true);
}
});
});
}
});
},
},
};
</script>
<style scoped></style>

View File

@@ -0,0 +1,132 @@
<template>
<div class="divBox">
<el-card class="box-card">
<el-form inline size="small" @submit.native.prevent>
<el-form-item>
<el-input v-model="listPram.roleName" placeholder="请输入角色昵称" clearable class="selWidth" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click.native="handleGetRoleList">查询</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-form inline @submit.native.prevent>
<el-form-item>
<el-button type="primary" @click="handlerOpenEdit(0)"
v-hasPermi="['admin:system:role:save', 'admin:system:menu:cache:tree']">添加角色</el-button>
</el-form-item>
</el-form>
<el-table :data="listData.list" size="mini"
:header-cell-style="{ fontWeight: 'bold', background: '#f8f8f9', color: '#515a6e', height: '40px' }">
<el-table-column label="角色编号" prop="id" width="120"></el-table-column>
<el-table-column label="角色昵称" prop="roleName" min-width="130" />
<el-table-column label="状态" prop="status">
<template slot-scope="scope">
<el-switch v-if="checkPermi(['admin:system:role:update:status'])" v-model="scope.row.status"
:active-value="true" :inactive-value="false" style="width: 40px"
@change="handleStatusChange(scope.row)"></el-switch>
<span v-else>{{ scope.row.status ? '显示' : '隐藏' }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" min-width="150" />
<el-table-column label="更新时间" prop="updateTime" min-width="150" />
<el-table-column label="操作" min-width="130" fixed="right">
<template slot-scope="scope">
<el-button size="small" type="text" @click="handlerOpenEdit(1, scope.row)"
v-hasPermi="['admin:system:role:info']">编辑</el-button>
<el-button size="small" type="text" @click="handlerOpenDel(scope.row)"
v-hasPermi="['admin:system:role:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination :current-page="listPram.page" :page-sizes="constants.page.limit" :layout="constants.page.layout"
:total="listData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</el-card>
<el-dialog :visible.sync="editDialogConfig.visible" :title="editDialogConfig.isCreate === 0 ? '创建身份' : '编辑身份'"
destroy-on-close :close-on-click-modal="false" width="500px">
<edit v-if="editDialogConfig.visible" :is-create="editDialogConfig.isCreate"
:edit-data="editDialogConfig.editData" @hideEditDialog="hideEditDialog" ref="editForm" />
</el-dialog>
</div>
</template>
<script>
import * as roleApi from '@/api/role.js';
import edit from './edit';
import { checkPermi } from '@/utils/permission'; // 权限判断函数
export default {
// name: "index"
components: { edit },
data() {
return {
constants: this.$constants,
listData: { list: [] },
listPram: {
createTime: null,
updateTime: null,
level: null,
page: 1,
limit: this.$constants.page.limit[0],
roleName: null,
rules: null,
status: null,
},
menuList: [],
editDialogConfig: {
visible: false,
isCreate: 0, // 0=创建1=编辑
editData: {},
},
};
},
mounted() {
this.handleGetRoleList();
},
methods: {
checkPermi,
handlerOpenDel(rowData) {
this.$confirm('确认删除当前数据').then(() => {
roleApi.delRole(rowData).then((data) => {
this.$message.success('删除数据成功');
this.handleGetRoleList();
});
});
},
handleGetRoleList() {
roleApi.getRoleList(this.listPram).then((data) => {
this.listData = data;
});
},
handlerOpenEdit(isCreate, editDate) {
isCreate === 1 ? (this.editDialogConfig.editData = editDate) : (this.editDialogConfig.editData = {});
this.editDialogConfig.isCreate = isCreate;
this.editDialogConfig.visible = true;
},
hideEditDialog() {
this.editDialogConfig.visible = false;
this.handleGetRoleList();
},
handleSizeChange(val) {
this.listPram.limit = val;
this.handleGetRoleList(this.listPram);
},
handleCurrentChange(val) {
this.listPram.page = val;
this.handleGetRoleList(this.listPram);
},
//修改状态
handleStatusChange(row) {
roleApi.updateRoleStatus(row).then((res) => {
this.$message.success('更新状态成功');
this.handleGetRoleList();
});
},
resetQuery() {
this.listPram.roleName = '';
this.handleGetRoleList();
},
},
};
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,11 @@
<template>
<div>
<router-view />
</div>
</template>
<script>
export default {};
</script>
<style lang="sass" scoped></style>

View File

@@ -0,0 +1,383 @@
<template>
<div class="divBox">
<el-card class="box-card">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
<el-form-item label="菜单名称" prop="menuName">
<el-input v-model="queryParams.name" placeholder="请输入菜单名称" clearable size="small" />
</el-form-item>
<el-form-item label="状态" prop="menuType">
<el-select v-model="queryParams.menuType" placeholder="菜单状态" clearable size="small">
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-sort" @click="toggleExpandAll">展开/折叠</el-button>
</el-col>
</el-row>
<el-table v-if="refreshTable" v-loading="listLoading" :data="menuList" row-key="id"
:default-expand-all="isExpandAll" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
:header-cell-style="{ fontWeight: 'bold' }">
<el-table-column prop="name" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
<el-table-column prop="icon" label="图标" align="center" width="100">
<template slot-scope="scope">
<i :class="'el-icon-' + scope.row.icon" style="font-size: 20px" />
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" width="60"></el-table-column>
<el-table-column prop="perms" label="权限标识" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="component" label="组件路径" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="isShow" label="状态" width="80">
<template slot-scope="scope">
<el-tag :type="scope.row.isShow ? '' : 'danger'">{{ scope.row.isShow ? '显示' : '隐藏' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column prop="menuType" label="类型" width="80">
<template slot-scope="scope">
<span class="type_tag one" v-if="scope.row.menuType == 'M'">目录</span>
<span class="type_tag two" v-else-if="scope.row.menuType == 'C'">菜单</span>
<span class="type_tag three" v-else type="info">按钮</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['admin:system:menu:info']">修改</el-button>
<el-button type="text" icon="el-icon-plus" @click="handleAdd(scope.row)"
v-hasPermi="['admin:system:menu:add']">新增</el-button>
<el-button type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['admin:system:menu:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加或修改菜单对话框 -->
<el-dialog :title="title" :visible.sync="open" width="680px" append-to-body :close-on-click-modal="false">
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-row>
<el-col :span="24">
<el-form-item label="上级菜单">
<treeselect v-model="form.pid" :options="menuOptions" :normalizer="normalizer" :show-count="true"
placeholder="选择上级菜单" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="菜单类型" prop="menuType">
<el-radio-group v-model="form.menuType">
<el-radio label="M">目录</el-radio>
<el-radio label="C">菜单</el-radio>
<el-radio label="A">按钮</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item v-if="form.menuType != 'A'" label="菜单图标">
<el-form-item>
<el-input placeholder="请选择菜单图标" v-model="form.icon">
<el-button slot="append" icon="el-icon-circle-plus-outline" @click="addIcon"></el-button>
</el-input>
</el-form-item>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单名称" prop="menuName">
<el-input v-model="form.name" placeholder="请输入菜单名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="显示排序" prop="sort">
<el-input-number v-model="form.sort" controls-position="right" :min="0" />
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.menuType !== 'A'">
<!-- v-if="form.menuType == 'C'" -->
<el-form-item prop="component">
<span slot="label">
<el-tooltip content="访问的组件路径,如:`system/user/index`,默认在`views`目录下" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
组件路径
</span>
<el-input v-model="form.component" placeholder="请输入组件路径" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.menuType != 'M'">
<el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
<span slot="label">
<el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi('system:user:list')`)" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
权限字符
</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<!-- v-if="form.menuType != 'A'" -->
<span slot="label">
<el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
显示状态
</span>
<el-radio-group v-model="form.isShow">
<el-radio v-for="item in showStatus" :key="item.value" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" v-hasPermi="['admin:system:menu:update']"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</el-card>
</div>
</template>
<script>
import { menuListApi, menuInfo, menuUpdate, menuAdd, menuDelete } from '@/api/systemadmin';
import Treeselect from '@riophae/vue-treeselect';
import '@riophae/vue-treeselect/dist/vue-treeselect.css';
import { Debounce } from '@/utils/validate';
import { checkPermi } from '@/utils/permission'; // 权限判断函数
export default {
name: 'Menu',
components: { Treeselect },
data() {
return {
// 遮罩层
listLoading: true,
// 显示搜索条件
showSearch: true,
// 菜单表格树数据
menuList: [],
// 菜单树选项
menuOptions: [],
// 弹出层标题
title: '',
// 是否显示弹出层
open: false,
// 是否展开,默认全部折叠
isExpandAll: false,
// 重新渲染表格状态
refreshTable: true,
// 查询参数
queryParams: {
name: '',
menuType: '',
},
// 表单参数
form: {},
//请求到的menu数据
menuDataList: [],
// 表单校验
rules: {
name: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
sort: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
},
statusOptions: [
{ value: 'M', label: '目录' },
{ value: 'C', label: '菜单' },
{ value: 'A', label: '按钮' },
],
showStatus: [
{ label: '显示', value: true },
{ label: '隐藏', value: false },
],
};
},
created() {
this.getList();
},
methods: {
checkPermi,
// 点击图标
addIcon() {
const _this = this;
_this.$modalIcon(function (icon) {
_this.form.icon = icon;
});
},
/** 查询菜单列表 */
getList(num) {
this.listLoading = true;
menuListApi(this.queryParams).then((res) => {
let obj = {},
menuList = [];
res.forEach((item) => {
obj = item;
obj.parentId = item.pid;
obj.children = [];
menuList.push(obj);
});
this.menuDataList = menuList;
this.menuList = this.handleTree(menuList, 'menuId');
this.getTreeselect();
this.listLoading = false;
});
},
/** 转换菜单数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
}
return {
id: node.id ? node.id : 0,
label: node.name ? node.name : '主目录',
children: node.children,
};
},
/** 查询菜单下拉树结构 */
getTreeselect() {
this.menuOptions = [];
const menu = { menuId: 0, menuName: '主类目', children: [] };
menu.children = this.handleTree(this.menuDataList, 'menuId');
this.menuOptions.push(menu);
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
menuId: '',
parentId: 0,
name: '',
icon: '',
menuType: 'M',
sort: 0,
isShow: true,
component: '',
perms: '',
};
this.resetForm('form');
},
/** 搜索按钮操作 */
handleQuery() {
this.getList(1);
},
/** 重置按钮操作 */
resetQuery() {
(this.queryParams = { name: '', menuType: '' }), this.handleQuery();
},
/** 新增按钮操作 */
handleAdd(row) {
this.reset();
if (row != null && row.id) {
this.form.pid = row.id;
} else {
this.form.pid = 0;
}
this.open = true;
this.title = '添加菜单';
},
/** 展开/折叠操作 */
toggleExpandAll() {
this.refreshTable = false;
this.isExpandAll = !this.isExpandAll;
this.$nextTick(() => {
this.refreshTable = true;
});
},
/** 修改按钮操作 */
handleUpdate(row) {
const loading = this.$loading({
lock: true,
text: 'Loading',
});
this.reset();
this.getTreeselect();
menuInfo(row.id).then((response) => {
this.form = response;
this.open = true;
this.title = '修改菜单';
loading.close();
});
},
/** 提交按钮 */
submitForm: Debounce(function () {
this.$refs['form'].validate((valid) => {
if (valid) {
if (this.form.id != undefined) {
menuUpdate(this.form).then((response) => {
this.$modal.msgSuccess('修改成功');
this.open = false;
this.getList(1);
});
} else {
menuAdd(this.form).then((response) => {
this.$modal.msgSuccess('新增成功');
this.open = false;
this.getList(1);
});
}
}
});
}),
/** 删除按钮操作 */
handleDelete(row) {
this.$modal
.confirm('是否确认删除名称为"' + row.name + '"的数据项?')
.then(function () {
return menuDelete(row.id);
})
.then(() => {
this.getList(1);
this.$modal.msgSuccess('删除成功');
})
.catch(() => { });
},
},
};
</script>
<style lang="scss">
.mb8 {
margin-bottom: 8px;
}
.type_tag {
display: inline-block;
height: 32px;
padding: 0 10px;
line-height: 30px;
font-size: 12px;
border-radius: 4px;
box-sizing: border-box;
white-space: nowrap;
}
.two {
background: rgba(239, 156, 32, 0.1);
color: rgba(239, 156, 32, 1);
}
.one {
background: rgba(75, 202, 213, 0.1);
color: rgba(75, 202, 213, 1);
}
.three {
color: rgba(120, 128, 160, 1);
background: rgba(120, 128, 160, 0.1);
}
</style>