feat organize staff management by role

This commit is contained in:
david
2026-07-29 15:00:09 +08:00
parent 5c5bc49e86
commit 31811ad7e3
7 changed files with 53 additions and 8 deletions

View File

@@ -32,8 +32,10 @@ var PlatformMenus = [][]Menu{
},
{
{Identity: "staff", GroupCode: "staff", Name: "工作人员管理", Icon: "icon-user-group", Path: "/staff", SortNo: 40, Status: common.StatusEnable},
{Identity: "staff_account", ParentIdentity: "staff", GroupCode: "staff", Name: "工作人员", Path: "/staff/staff-account", SortNo: 1, Status: common.StatusEnable},
{Identity: "staff_credential", ParentIdentity: "staff", GroupCode: "staff", Name: "人员资质", Path: "/staff/staff-credential", SortNo: 2, Status: common.StatusEnable},
{Identity: "staff_add", ParentIdentity: "staff", GroupCode: "staff", Name: "新增工作人员", Path: "/staff/add", SortNo: 1, Status: common.StatusEnable},
{Identity: "staff_installer", ParentIdentity: "staff", GroupCode: "staff", Name: "安装人员管理", Path: "/staff/installers", SortNo: 2, Status: common.StatusEnable},
{Identity: "staff_delivery", ParentIdentity: "staff", GroupCode: "staff", Name: "配送人员管理", Path: "/staff/delivery", SortNo: 3, Status: common.StatusEnable},
{Identity: "staff_operations", ParentIdentity: "staff", GroupCode: "staff", Name: "运维人员管理", Path: "/staff/operations", SortNo: 4, Status: common.StatusEnable},
},
{
{Identity: "user", GroupCode: "user", Name: "用户管理", Icon: "icon-user", Path: "/user", SortNo: 50, Status: common.StatusEnable},

View File

@@ -36,6 +36,8 @@ func platformRouteMenuIdentity(resource string) string {
return "gas_basic"
case resource == "delivery_account":
return "delivery_basic"
case resource == "staff_account" || resource == "staff_credential":
return "staff"
case strings.HasPrefix(resource, "gasorder_contract"):
return "gasorder_contract"
case resource == "gasorder_track" || resource == "gasorder_track_point":

View File

@@ -285,7 +285,7 @@ export const resources: ResourceUiDefinition[] = [
define('gas_account', '气站账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('display_name'), f('role_code'), relation('gas_basic_identity', '/gas_basic', true)]),
{ ...define('delivery_basic', '配送点管理', 'writable', [f('delivery_code', { required: true }), f('name', { required: true }), relation('gas_basic_identity', '/gas_basic'), f('principal'), f('address')]), accountManagement: { resource: '/delivery_account', relationKey: 'delivery_basic_identity', title: '配送点账户' } },
define('delivery_account', '配送站账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('display_name'), f('role_code'), relation('delivery_basic_identity', '/delivery_basic', true)]),
define('staff_account', '工作人员', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('role_code'), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), f('work_status')]),
define('staff_account', '工作人员', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('role_code', { required: true, type: 'select', options: [{ label: '安装人员', value: 'installer' }, { label: '配送人员', value: 'delivery' }, { label: '运维人员', value: 'operations' }] }), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), f('work_status', { type: 'select', options: [{ label: '在岗', value: 'on_duty' }, { label: '离岗', value: 'off_duty' }] })]),
define('staff_credential', '工作人员资质', 'writable', [relation('staff_account_identity', '/staff_account', true), f('credential_type', { required: true }), f('credential_no'), f('expired_at')]),
define('user_account', '用户账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('real_name')]),
define('user_address', '用户地址', 'writable', [relation('user_account_identity', '/user_account', true), f('address', { required: true }), f('longitude'), f('latitude'), f('is_default')]),

View File

@@ -66,8 +66,11 @@ const routes: AppRouteRecordRaw[] = [
child('organization', 'delivery-account', 'delivery-account', '配送点账户', '/delivery_account', 'delivery_basic', true, 'delivery-basic'),
]),
group('staff', 'staff', '工作人员管理', 'icon-user-group', 30, [
child('staff', 'staff-account', 'account', '工作人员', '/staff_account', 'staff_account'),
child('staff', 'staff-credential', 'credential', '人员资质', '/staff_credential', 'staff_credential'),
{ ...child('staff', 'add', 'add', '新增工作人员', '/staff_account', 'staff_add'), meta: { title: '新增工作人员', resource: '/staff_account', requiresAuth: true, menuCode: 'staff_add', createMode: true } },
{ ...child('staff', 'installers', 'installers', '安装人员管理', '/staff_account', 'staff_installer'), meta: { title: '安装人员管理', resource: '/staff_account', requiresAuth: true, menuCode: 'staff_installer', staffType: 'installer' } },
{ ...child('staff', 'delivery', 'delivery', '配送人员管理', '/staff_account', 'staff_delivery'), meta: { title: '配送人员管理', resource: '/staff_account', requiresAuth: true, menuCode: 'staff_delivery', staffType: 'delivery' } },
{ ...child('staff', 'operations', 'operations', '运维人员管理', '/staff_account', 'staff_operations'), meta: { title: '运维人员管理', resource: '/staff_account', requiresAuth: true, menuCode: 'staff_operations', staffType: 'operations' } },
child('staff', 'credential', 'credential', '人员资质', '/staff_credential', 'staff', true, 'staff-installers'),
]),
group('user', 'user', '用户管理', 'icon-user', 40, [
child('user', 'user-account', 'account', '用户账户', '/user_account', 'user_account'),

View File

@@ -4,6 +4,8 @@ declare module 'vue-router' {
interface RouteMeta {
roles?: string[]; // Controls roles that have access to the page
menuCode?: string; // Server-assigned menu domain required by this route
staffType?: 'installer' | 'delivery' | 'operations';
createMode?: boolean;
requiresAuth: boolean; // Whether login is required to access the current page (every route must declare)
icon?: string; // The icon show in the side menu
locale?: string; // The locale name show in side menu and breadcrumb

View File

@@ -38,10 +38,18 @@
<a-table-column v-if="definition.accountManagement" title="账户数" :width="90">
<template #cell="{ record }">{{ accountCounts[String(record.identity)] ?? 0 }}</template>
</a-table-column>
<a-table-column v-if="definition.name === 'staff_account'" title="审批状态" :width="100">
<template #cell="{ record }">
<a-tag :color="Number(record.status) === 1 ? 'green' : Number(record.status) === 2 ? 'red' : 'orange'">
{{ Number(record.status) === 1 ? '已通过' : Number(record.status) === 2 ? '已禁用' : '待审批' }}
</a-tag>
</template>
</a-table-column>
<a-table-column title="操作" :width="definition.accountManagement ? 350 : 280" fixed="right">
<template #cell="{ record }">
<a-space>
<a-button v-if="definition.accountManagement" size="mini" type="primary" @click="manageAccounts(record)">账户管理</a-button>
<a-button v-if="definition.name === 'staff_account'" size="mini" @click="viewCredentials(record)">查看资质</a-button>
<a-button size="mini" @click="openDetail(record)">详情</a-button>
<a-button v-if="canEdit" size="mini" :disabled="isProtectedRecord(record)" @click="openEdit(record)">编辑</a-button>
<a-button v-if="canChangeStatus" size="mini" :disabled="isProtectedRecord(record)" @click="openStatus(record)">审核</a-button>
@@ -175,6 +183,9 @@ const managedOwnerIdentity = computed(() =>
const managedRelationKey = computed(() =>
typeof route.query.relation_key === 'string' ? route.query.relation_key : '',
);
const staffType = computed(() =>
typeof route.meta.staffType === 'string' ? route.meta.staffType : '',
);
const platformRootWriteResources = new Set([
'platform_account',
'platform_role',
@@ -275,7 +286,20 @@ function resetForm(data?: Row) {
async function load() {
loading.value = true;
try {
if (managedOwnerIdentity.value && managedRelationKey.value) {
if (staffType.value) {
const staff = await loadAllRows(props.definition.resource);
const keyword = filters.keyword.trim().toLowerCase();
const filtered = staff.filter(
(row) =>
String(row.role_code ?? '') === staffType.value &&
(!keyword ||
['username', 'name', 'phone'].some((key) =>
String(row[key] ?? '').toLowerCase().includes(keyword),
)),
);
list.value = filtered.slice((page.value - 1) * pageSize, page.value * pageSize);
total.value = filtered.length;
} else if (managedOwnerIdentity.value && managedRelationKey.value) {
const accounts = await loadAllRows(props.definition.resource);
const keyword = filters.keyword.trim().toLowerCase();
const filtered = accounts.filter(
@@ -345,6 +369,16 @@ function manageAccounts(row: Row) {
});
}
function viewCredentials(row: Row) {
router.push({
name: 'staff-credential',
query: {
owner_identity: String(row.identity ?? ''),
relation_key: 'staff_account_identity',
},
});
}
async function syncQuery() {
const query: Record<string, string> = {};
if (managedOwnerIdentity.value) query.owner_identity = managedOwnerIdentity.value;
@@ -371,6 +405,7 @@ function openCreate() {
if (managedOwnerIdentity.value && managedRelationKey.value) {
form[managedRelationKey.value] = managedOwnerIdentity.value;
}
if (staffType.value) form.role_code = staffType.value;
formVisible.value = true;
}
@@ -680,6 +715,7 @@ onMounted(async () => {
(role) => !role.is_system && role.status === 1,
);
}
if (route.meta.createMode) openCreate();
});
watch(

View File

@@ -1,12 +1,12 @@
<template>
<TreePage
v-if="definition.pageKind === 'tree'"
:key="definition.resource"
:key="String(route.name)"
:definition="definition"
/>
<CrudListPage
v-else
:key="definition.resource"
:key="String(route.name)"
:definition="definition"
/>
</template>