feat: 移除mock并接入真实权限控制

This commit is contained in:
湛兮
2026-05-26 16:24:03 +08:00
parent 5003628017
commit 304589bf8b
30 changed files with 965 additions and 1037 deletions
+27 -17
View File
@@ -12,7 +12,7 @@ import {
getEmployee,
listEmployees,
listRoles,
listStores,
listStoreOptions,
updateEmployee,
updateEmployeeStatus,
type Employee,
@@ -21,7 +21,7 @@ import {
type RoleOption,
type StoreOption
} from "@/api/access";
import { hasPerms } from "@/utils/auth";
import { hasMenuAction, hasPerms } from "@/utils/auth";
import Plus from "~icons/ep/plus";
import Search from "~icons/ep/search";
@@ -107,7 +107,12 @@ const inactiveCount = computed(
() => employees.value.filter(item => item.status === "INACTIVE").length
);
const dialogTitle = computed(() => (form.id ? "编辑员工" : "新增员工"));
const canManageEmployees = computed(() => hasPerms("employee:manage"));
const canCreateEmployee = computed(() => hasMenuAction("employees", "create"));
const canUpdateEmployee = computed(() => hasMenuAction("employees", "update"));
const canDeleteEmployee = computed(() => hasMenuAction("employees", "delete"));
const canOperateEmployee = computed(
() => canUpdateEmployee.value || canDeleteEmployee.value
);
const canViewAllEmployees = computed(() => hasPerms("employee:view:all"));
function getErrorMessage(error: unknown, fallback: string) {
@@ -158,15 +163,17 @@ function buildPayload(): EmployeePayload {
/** 门店和角色是员工表单的基础字典,打开弹窗前必须先加载。 */
async function fetchCatalog() {
const shouldLoadStores =
canViewAllEmployees.value || canManageEmployees.value;
const shouldLoadRoles = canManageEmployees.value;
canViewAllEmployees.value ||
canCreateEmployee.value ||
canUpdateEmployee.value;
const shouldLoadRoles = canCreateEmployee.value || canUpdateEmployee.value;
if (!shouldLoadStores && !shouldLoadRoles) return;
catalogLoading.value = true;
try {
const [storeResult, roleResult] = await Promise.all([
shouldLoadStores ? listStores() : Promise.resolve({ data: [] }),
shouldLoadStores ? listStoreOptions() : Promise.resolve({ data: [] }),
shouldLoadRoles ? listRoles() : Promise.resolve({ data: [] })
]);
stores.value = storeResult.data;
@@ -227,14 +234,14 @@ function handleSizeChange(pageSize: number) {
}
function openCreateDialog() {
if (!canManageEmployees.value) return;
if (!canCreateEmployee.value) return;
resetFormState();
dialogVisible.value = true;
}
/** 编辑前重新拉详情,确保角色绑定不是来自列表摘要的过期数据。 */
async function openEditDialog(row: Employee) {
if (!canManageEmployees.value) return;
if (!canUpdateEmployee.value) return;
try {
const result = await getEmployee(row.id);
const employee = result.data;
@@ -255,7 +262,7 @@ async function openEditDialog(row: Employee) {
}
async function submitForm() {
if (!canManageEmployees.value) return;
if (form.id ? !canUpdateEmployee.value : !canCreateEmployee.value) return;
await formRef.value?.validate();
submitLoading.value = true;
@@ -280,7 +287,7 @@ async function submitForm() {
}
async function toggleStatus(row: Employee) {
if (!canManageEmployees.value) return;
if (!canUpdateEmployee.value) return;
const nextStatus: EmployeeStatus =
row.status === "ACTIVE" ? "INACTIVE" : "ACTIVE";
const action = nextStatus === "ACTIVE" ? "启用" : "停用";
@@ -306,7 +313,7 @@ async function toggleStatus(row: Employee) {
}
async function removeEmployee(row: Employee) {
if (!canManageEmployees.value) return;
if (!canDeleteEmployee.value) return;
try {
await ElMessageBox.confirm(
`删除后员工「${row.name}」会被软删除并停用,确认继续?`,
@@ -345,7 +352,7 @@ onMounted(async () => {
<h1>员工管理</h1>
</div>
<el-button
v-if="canManageEmployees"
v-if="canCreateEmployee"
type="primary"
:icon="Plus"
@click="openCreateDialog"
@@ -470,13 +477,14 @@ onMounted(async () => {
</template>
</el-table-column>
<el-table-column
v-if="canManageEmployees"
v-if="canOperateEmployee"
label="操作"
width="260"
fixed="right"
>
<template #default="{ row }">
<el-button
v-if="canUpdateEmployee"
link
type="primary"
:icon="EditPen"
@@ -485,6 +493,7 @@ onMounted(async () => {
编辑
</el-button>
<el-button
v-if="canUpdateEmployee"
link
:type="row.status === 'ACTIVE' ? 'warning' : 'success'"
:icon="row.status === 'ACTIVE' ? CircleClose : CircleCheck"
@@ -493,6 +502,7 @@ onMounted(async () => {
{{ row.status === "ACTIVE" ? "停用" : "启用" }}
</el-button>
<el-button
v-if="canDeleteEmployee"
link
type="danger"
:icon="Delete"
@@ -618,9 +628,9 @@ onMounted(async () => {
.page-heading {
display: flex;
gap: 16px;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
h1 {
@@ -733,9 +743,9 @@ onMounted(async () => {
.pagination-row {
display: flex;
gap: 16px;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 14px 16px 0;
font-size: 13px;
color: #64748b;
@@ -761,8 +771,8 @@ onMounted(async () => {
}
.page-heading {
align-items: flex-start;
flex-direction: column;
align-items: flex-start;
}
.summary-strip {
@@ -781,8 +791,8 @@ onMounted(async () => {
}
.pagination-row {
align-items: flex-start;
flex-direction: column;
align-items: flex-start;
}
.form-grid {