Files
bj_power/bj_power_mes/frontend/src/pages/RoleManage.vue
T
SunYF b6799c9de4 feat: 完成产线与仓储系统多模块迭代升级
本次迭代覆盖MES与WMS核心业务:
1. 新增接驳台托盘传感器读取与AGV对接能力
2. 完善工单排产、备料流程与权限体系拆分
3. 优化看板接口与前端路由、样式
4. 新增操作日志、库存盘点与角色保护逻辑
5. 修复代理地址、BOM保存等已知问题
2026-09-04 14:18:53 +08:00

217 lines
8.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div>
<el-card shadow="never" class="mb12">
<div style="display:flex;justify-content:space-between;align-items:center">
<div>
<b>角色管理</b>
<span style="margin-left:8px;color:#909399;font-size:12px">
角色决定账号的菜单与按钮权限内置角色超级管理员/生产操作员/质检员不可删除
</span>
</div>
<el-button v-if="can('sys.role:add')" type="primary" @click="openCreate">新增角色</el-button>
</div>
</el-card>
<el-card shadow="never">
<el-table :data="roles" border size="small" v-loading="loading" empty-text="暂无角色">
<el-table-column prop="id" label="ID" width="64" align="center" />
<el-table-column label="名称" min-width="150">
<template #default="{ row }">
<el-tag :type="row.code === 'SUPER_ADMIN' ? 'danger' : 'primary'" size="small">{{ row.name }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="180">
<template #default="{ row }">{{ row.remark || '-' }}</template>
</el-table-column>
<el-table-column label="权限数" width="90" align="center">
<template #default="{ row }">{{ permCount(row) }} </template>
</el-table-column>
<el-table-column label="权限明细" min-width="240">
<template #default="{ row }">
<el-tooltip :content="permSummary(row)" placement="top" :show-after="300">
<span style="color:#606266">{{ permSummary(row) }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="{ row }">
<el-button v-if="can('sys.role:edit')" link type="primary" size="small"
:disabled="row.code === 'SUPER_ADMIN'" @click="openEdit(row)">编辑权限</el-button>
<el-button v-if="can('sys.role:delete')" link type="danger" size="small"
:disabled="['SUPER_ADMIN', 'OPERATOR', 'INSPECTOR'].includes(row.code)" @click="removeRole(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="dialog.show" :title="dialog.editingId ? '编辑角色权限' : '新增角色'" width="600px"
append-to-body destroy-on-close>
<el-form label-width="90px">
<el-form-item label="角色名称" required>
<el-input v-model="dialog.name" placeholder="如 库房管理员" />
</el-form-item>
<el-form-item label="角色编码" required>
<el-input v-model="dialog.code" :disabled="!!dialog.editingId" placeholder="如 intro-manager(创建后不可改)" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="dialog.remark" placeholder="备注(可空)" />
</el-form-item>
<el-form-item label="权限设置">
<div class="perm-tree" v-loading="!perms.length">
<template v-if="perms.length">
<div class="perm-tip">勾选菜单=可见该菜单其下按钮勾选=可用该按钮勾选互不影响</div>
<el-tree ref="treeRef" :data="permTree" show-checkbox node-key="code"
check-strictly default-expand-all :props="{ label: 'label', children: 'children' }" />
</template>
<el-empty v-else description="暂无权限数据" :image-size="60" />
</div>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialog.show = false">取消</el-button>
<el-button type="primary" :loading="saving" @click="save">保存</el-button>
</template>
</el-dialog>
</div>
<PageHelp :data="helpRole" page="RoleManage" />
</template>
<script setup>
defineOptions({ name: 'RoleManage' })
import { computed, onMounted, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import request from '../utils/request'
import { can } from '../utils/perm'
import PageHelp from '../components/PageHelp.vue'
import { helpRole } from '../help'
const loading = ref(false)
const roles = ref([])
const perms = ref([]) // 全部权限定义(MENU + BUTTON
const treeRef = ref()
const codeName = computed(() => {
const m = {}
for (const p of perms.value) m[p.code] = p.name
return m
})
// MES 无 parentCode:按钮码形如 菜单码:动作,父菜单 = 冒号前段(菜单码不含冒号)
const permTree = computed(() => {
const menus = perms.value.filter((p) => p.type === 'MENU')
const buttons = perms.value.filter((p) => p.type === 'BUTTON')
const nodes = menus.map((m) => ({
label: m.name,
code: m.code,
children: buttons
.filter((b) => b.code.startsWith(m.code + ':'))
.map((b) => ({ label: b.name, code: b.code }))
}))
const stray = buttons.filter((b) => !menus.some((m) => b.code.startsWith(m.code + ':')))
if (stray.length) {
nodes.push({ label: '通用', global: true, children: stray.map((b) => ({ label: b.name, code: b.code })) })
}
return nodes
})
function permCount(row) {
const codes = row.permissionCodes || []
if (row.code === 'SUPER_ADMIN') return codes
return codes.filter((c) => c !== '*').length
}
function permSummary(row) {
const codes = row.permissionCodes || []
if (codes.includes('*')) return '全部权限'
return codes.map((c) => codeName.value[c] || c).join('、') || '-'
}
async function loadRoles() {
loading.value = true
try {
roles.value = (await request.get('/roles')) || []
} finally {
loading.value = false
}
}
async function loadPerms() {
try {
perms.value = (await request.get('/permissions')) || []
} catch { /* 拦截器已提示 */ }
}
/* ---------- 新增 / 编辑 ---------- */
const dialog = reactive({ show: false, editingId: 0, name: '', code: '', remark: '', checked: [] })
const saving = ref(false)
function openCreate() {
Object.assign(dialog, { show: true, editingId: 0, name: '', code: '', remark: '', checked: [] })
}
function openEdit(row) {
Object.assign(dialog, {
show: true, editingId: row.id, name: row.name || '', code: row.code || '',
remark: row.remark || '', checked: [...(row.permissionCodes || [])]
})
// 弹窗打开待树渲染后回显
setTimeout(() => {
if (treeRef.value) treeRef.value.setCheckedKeys(dialog.checked)
}, 60)
}
function treeCheckedCodes() {
if (!treeRef.value) return dialog.checked
const keys = treeRef.value.getCheckedKeys() || []
return [...new Set(keys)]
}
async function save() {
if (!dialog.name.trim()) return ElMessage.warning('请输入角色名称')
if (!dialog.editingId && !dialog.code.trim()) return ElMessage.warning('请输入角色编码')
const payload = {
id: dialog.editingId,
name: dialog.name.trim(),
code: dialog.code.trim(),
remark: dialog.remark.trim() || undefined,
permissionCodes: treeCheckedCodes()
}
saving.value = true
try {
if (dialog.editingId) {
await request.put('/roles', payload)
ElMessage.success('保存成功')
} else {
await request.post('/roles', payload)
ElMessage.success('角色创建成功')
}
dialog.show = false
await loadRoles()
} finally {
saving.value = false
}
}
async function removeRole(row) {
try {
await ElMessageBox.confirm(`确定删除角色「${row.name}」?该角色下的用户将受影响。`, '删除确认', {
type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消'
})
} catch {
return
}
try {
await request.delete(`/roles/${row.id}`)
ElMessage.success('已删除')
await loadRoles()
} catch { /* 拦截器已提示 */ }
}
onMounted(async () => {
await Promise.all([loadRoles(), loadPerms()])
})
</script>
<style scoped>
.mb12 { margin-bottom: 12px; }
.perm-tree { border: 1px solid #ebeef5; border-radius: 4px; padding: 12px; max-height: 360px; overflow: auto; }
.perm-tip { color: #909399; font-size: 12px; margin-bottom: 8px; }
</style>