feat(platform-admin): align frontend with backend contracts

This commit is contained in:
david
2026-07-28 14:26:09 +08:00
parent bc8138f978
commit 4cc17c9efb
6 changed files with 438 additions and 68 deletions

View File

@@ -3,15 +3,16 @@
<template #extra>
<a-space>
<a-button @click="load">刷新</a-button>
<a-button v-if="canWrite" type="primary" @click="openCreate">新增</a-button>
<a-button v-if="canCreate" type="primary" @click="openCreate">新增</a-button>
</a-space>
</template>
<a-tree :data="tree" :loading="loading" :field-names="{ key: 'identity', title: 'name', children: 'children' }">
<template #title="node">
<a-space>
{{ node.title }}
<a-button v-if="canWrite" size="mini" @click.stop="openEdit(node)">编辑</a-button>
<a-button v-if="canWrite" size="mini" status="danger" @click.stop="confirmArchive(node)">归档</a-button>
<a-button v-if="canEdit" size="mini" @click.stop="openEdit(node)">编辑</a-button>
<a-button v-if="canChangeStatus" size="mini" @click.stop="confirmStatus(node)">{{ node.status === 'enabled' ? '停用' : '启用' }}</a-button>
<a-button v-if="canArchive" size="mini" status="danger" @click.stop="confirmArchive(node)">归档</a-button>
</a-space>
</template>
</a-tree>
@@ -19,8 +20,13 @@
<a-drawer :visible="formVisible" :title="editingIdentity ? `编辑${definition.title}` : `新增${definition.title}`" :width="480" @cancel="formVisible = false" @ok="save">
<a-form :model="form" layout="vertical">
<a-form-item v-for="field in definition.fields" :key="field.key" :label="field.label" :required="field.required">
<a-input-number v-if="field.type === 'number'" v-model="form[field.key]" />
<a-input-number v-if="field.type === 'number' || field.type === 'money'" v-model="form[field.key]" :precision="field.type === 'money' ? 2 : 0" />
<a-switch v-else-if="field.type === 'boolean'" v-model="form[field.key]" />
<a-select v-else-if="field.type === 'identity'" v-model="form[field.key]" allow-clear allow-search>
<a-option v-for="option in list.filter((item) => item.identity !== editingIdentity)" :key="option.identity" :value="option.identity">
{{ option.name ?? option.menu_code ?? option.identity }}
</a-option>
</a-select>
<a-input v-else v-model="form[field.key]" />
</a-form-item>
</a-form>
@@ -33,6 +39,7 @@ import { computed, onMounted, reactive, ref } from 'vue';
import { resourceApi } from '@/api/resource';
import { buildResourcePayload, isMissingField } from '@/api/resource-form';
import type { ResourceUiDefinition } from '@/api/resources';
import { useUserStore } from '@/store';
type Node = Record<string, unknown> & {
identity: string;
@@ -40,12 +47,25 @@ type Node = Record<string, unknown> & {
children: Node[];
};
const props = defineProps<{ definition: ResourceUiDefinition }>();
const userStore = useUserStore();
const loading = ref(false);
const list = ref<Node[]>([]);
const formVisible = ref(false);
const editingIdentity = ref('');
const form = reactive<Record<string, any>>({});
const canWrite = computed(() => props.definition.mode === 'writable');
const rootAllowed = computed(
() => props.definition.name !== 'platform_menu' || userStore.role === 'root',
);
const canCreate = computed(
() => props.definition.canCreate && rootAllowed.value,
);
const canEdit = computed(() => props.definition.canEdit && rootAllowed.value);
const canChangeStatus = computed(
() => props.definition.canChangeStatus && rootAllowed.value,
);
const canArchive = computed(
() => props.definition.canArchive && rootAllowed.value,
);
const tree = computed(() => {
const byIdentity = new Map<string, Node>();
const roots: Node[] = [];
@@ -68,6 +88,27 @@ function reset(data?: Node) {
}
}
function confirmStatus(node: Node) {
const status = node.status === 'enabled' ? 'disabled' : 'enabled';
Modal.warning({
title: status === 'enabled' ? '确认启用' : '确认停用',
content: `确定要${status === 'enabled' ? '启用' : '停用'}${String(node.name ?? node.identity)}”吗?`,
onOk: async () => {
try {
await resourceApi.updateStatus(
props.definition.resource,
node.identity,
status,
);
Message.success('状态已更新');
await load();
} catch (error) {
Message.error((error as Error).message);
}
},
});
}
function openCreate() {
editingIdentity.value = '';
reset();