feat platform dashboard and organization management
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
<a-card :title="definition.title" :bordered="false">
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button v-if="managedOwnerIdentity" @click="router.back()">返回机构列表</a-button>
|
||||
<a-button @click="load">刷新</a-button>
|
||||
<a-button v-if="canCreate" type="primary" @click="openCreate">新建</a-button>
|
||||
</a-space>
|
||||
@@ -34,13 +35,17 @@
|
||||
{{ displayFieldValue(field, record) }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="操作" :width="260" fixed="right">
|
||||
<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 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 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>
|
||||
<a-button v-if="canArchive" size="mini" status="danger" :disabled="isProtectedRecord(record)" @click="confirmArchive(record)">归档</a-button>
|
||||
<a-button v-if="canChangeStatus" size="mini" :disabled="isProtectedRecord(record)" @click="openStatus(record)">审核</a-button>
|
||||
<a-button v-if="canArchive" size="mini" status="danger" :disabled="isProtectedRecord(record)" @click="confirmArchive(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table-column>
|
||||
@@ -147,6 +152,7 @@ const page = ref(Math.max(1, Number(route.query.page) || 1));
|
||||
const pageSize = 20;
|
||||
const total = ref(0);
|
||||
const list = ref<Row[]>([]);
|
||||
const accountCounts = ref<Record<string, number>>({});
|
||||
const filters = reactive({
|
||||
keyword: typeof route.query.keyword === 'string' ? route.query.keyword : '',
|
||||
});
|
||||
@@ -163,6 +169,12 @@ const roleOptions = ref<PlatformRole[]>([]);
|
||||
const relationOptions = reactive<Record<string, Row[]>>({});
|
||||
const relationLoading = reactive<Record<string, boolean>>({});
|
||||
const relationSearchTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
const managedOwnerIdentity = computed(() =>
|
||||
typeof route.query.owner_identity === 'string' ? route.query.owner_identity : '',
|
||||
);
|
||||
const managedRelationKey = computed(() =>
|
||||
typeof route.query.relation_key === 'string' ? route.query.relation_key : '',
|
||||
);
|
||||
const platformRootWriteResources = new Set([
|
||||
'platform_account',
|
||||
'platform_role',
|
||||
@@ -263,14 +275,30 @@ function resetForm(data?: Row) {
|
||||
async function load() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await resourceApi.list<Row>(
|
||||
props.definition.resource,
|
||||
page.value,
|
||||
pageSize,
|
||||
filters.keyword ? { keyword: filters.keyword } : {},
|
||||
);
|
||||
list.value = result.list;
|
||||
total.value = result.total;
|
||||
if (managedOwnerIdentity.value && managedRelationKey.value) {
|
||||
const accounts = await loadAllRows(props.definition.resource);
|
||||
const keyword = filters.keyword.trim().toLowerCase();
|
||||
const filtered = accounts.filter(
|
||||
(row) =>
|
||||
String(row[managedRelationKey.value] ?? '') === managedOwnerIdentity.value &&
|
||||
(!keyword ||
|
||||
['username', 'display_name', 'role_code'].some((key) =>
|
||||
String(row[key] ?? '').toLowerCase().includes(keyword),
|
||||
)),
|
||||
);
|
||||
list.value = filtered.slice((page.value - 1) * pageSize, page.value * pageSize);
|
||||
total.value = filtered.length;
|
||||
} else {
|
||||
const result = await resourceApi.list<Row>(
|
||||
props.definition.resource,
|
||||
page.value,
|
||||
pageSize,
|
||||
filters.keyword ? { keyword: filters.keyword } : {},
|
||||
);
|
||||
list.value = result.list;
|
||||
total.value = result.total;
|
||||
}
|
||||
await loadAccountCounts();
|
||||
} catch (error) {
|
||||
Message.error((error as Error).message);
|
||||
} finally {
|
||||
@@ -278,8 +306,49 @@ async function load() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAllRows(resource: string) {
|
||||
const rows: Row[] = [];
|
||||
for (let currentPage = 1; currentPage <= 100; currentPage += 1) {
|
||||
const result = await resourceApi.list<Row>(resource, currentPage, 100);
|
||||
rows.push(...result.list);
|
||||
if (rows.length >= result.total || result.list.length < 100) break;
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
async function loadAccountCounts() {
|
||||
const management = props.definition.accountManagement;
|
||||
if (!management) {
|
||||
accountCounts.value = {};
|
||||
return;
|
||||
}
|
||||
const accounts = await loadAllRows(management.resource);
|
||||
accountCounts.value = accounts.reduce<Record<string, number>>((counts, account) => {
|
||||
const identity = String(account[management.relationKey] ?? '');
|
||||
if (identity) counts[identity] = (counts[identity] ?? 0) + 1;
|
||||
return counts;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function manageAccounts(row: Row) {
|
||||
const management = props.definition.accountManagement;
|
||||
if (!management) return;
|
||||
const routeName = management.resource === '/gas_account'
|
||||
? 'organization-gas-account'
|
||||
: 'organization-delivery-account';
|
||||
router.push({
|
||||
name: routeName,
|
||||
query: {
|
||||
owner_identity: String(row.identity ?? ''),
|
||||
relation_key: management.relationKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function syncQuery() {
|
||||
const query: Record<string, string> = {};
|
||||
if (managedOwnerIdentity.value) query.owner_identity = managedOwnerIdentity.value;
|
||||
if (managedRelationKey.value) query.relation_key = managedRelationKey.value;
|
||||
if (page.value > 1) query.page = String(page.value);
|
||||
if (filters.keyword.trim()) query.keyword = filters.keyword.trim();
|
||||
await router.replace({ query });
|
||||
@@ -299,6 +368,9 @@ async function resetSearch() {
|
||||
function openCreate() {
|
||||
editingIdentity.value = '';
|
||||
resetForm();
|
||||
if (managedOwnerIdentity.value && managedRelationKey.value) {
|
||||
form[managedRelationKey.value] = managedOwnerIdentity.value;
|
||||
}
|
||||
formVisible.value = true;
|
||||
}
|
||||
|
||||
@@ -429,7 +501,7 @@ async function save() {
|
||||
function openStatus(row: Row) {
|
||||
detail.value = row;
|
||||
openDetailAction({
|
||||
name: '修改状态',
|
||||
name: '审核状态',
|
||||
resource: `${props.definition.resource}/:identity/status`,
|
||||
method: 'PATCH',
|
||||
fields: [
|
||||
@@ -441,7 +513,6 @@ function openStatus(row: Row) {
|
||||
options: [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '停用', value: 2 },
|
||||
{ label: '归档', value: 3 },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -450,15 +521,15 @@ function openStatus(row: Row) {
|
||||
|
||||
function confirmArchive(row: Row) {
|
||||
Modal.warning({
|
||||
title: '确认归档',
|
||||
content: '归档后该记录将不再参与日常业务。',
|
||||
title: '确认删除',
|
||||
content: '删除后该记录将被归档,不再参与日常业务。',
|
||||
onOk: async () => {
|
||||
try {
|
||||
await resourceApi.archive(
|
||||
props.definition.resource,
|
||||
String(row.identity),
|
||||
);
|
||||
Message.success('已归档');
|
||||
Message.success('已删除');
|
||||
await load();
|
||||
} catch (error) {
|
||||
Message.error((error as Error).message);
|
||||
|
||||
Reference in New Issue
Block a user