fix platform authorization and workflow integrity
This commit is contained in:
@@ -68,7 +68,7 @@ const cards: { key: CountKey; label: string; hint: string; money?: boolean }[] =
|
||||
{ key: 'paid_amount', label: '累计实收金额', hint: '支付成功口径', money: true },
|
||||
];
|
||||
const actions = [
|
||||
{ label: '新建气站', route: 'gas-basic', menu: 'gas_basic', icon: IconPlus },
|
||||
{ label: '新建气站', route: 'organization-gas-basic', menu: 'gas_basic', icon: IconPlus },
|
||||
{ label: '配送订单', route: 'gasorder-orders', menu: 'gasorder_basic', icon: IconFile },
|
||||
{ label: '智能气阀', route: 'product-info', menu: 'product_info', icon: IconStorage },
|
||||
{ label: '用户管理', route: 'user-account', menu: 'user_account', icon: IconUser },
|
||||
|
||||
@@ -14,19 +14,6 @@
|
||||
<a-button type="primary" html-type="submit">查询</a-button>
|
||||
<a-button @click="resetSearch">重置</a-button>
|
||||
</a-form>
|
||||
<a-form v-if="definition.name === 'wallet_basic'" :model="walletOwner" layout="inline" class="wallet-owner" @submit.prevent="getOwnerWallet">
|
||||
<a-form-item label="归属类型">
|
||||
<a-select v-model="walletOwner.type" style="width: 140px">
|
||||
<a-option value="user">用户</a-option>
|
||||
<a-option value="staff">工作人员</a-option>
|
||||
<a-option value="delivery">配送站</a-option>
|
||||
<a-option value="gas">气站</a-option>
|
||||
<a-option value="platform">平台</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="归属标识"><a-input v-model="walletOwner.identity" placeholder="请输入 identity" /></a-form-item>
|
||||
<a-button type="primary" html-type="submit">获取或创建钱包</a-button>
|
||||
</a-form>
|
||||
<a-table :data="list" :loading="loading" :pagination="false" row-key="identity">
|
||||
<template #columns>
|
||||
<a-table-column title="业务标识" data-index="identity" :width="220" ellipsis tooltip />
|
||||
@@ -114,7 +101,7 @@
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-space class="detail-actions">
|
||||
<a-button v-for="action in definition.detailActions" :key="action.name" type="primary" :status="action.danger ? 'danger' : 'normal'" @click="openDetailAction(action)">{{ action.name }}</a-button>
|
||||
<a-button v-for="action in visibleDetailActions" :key="action.name" type="primary" :status="action.danger ? 'danger' : 'normal'" @click="openDetailAction(action)">{{ action.name }}</a-button>
|
||||
</a-space>
|
||||
</a-drawer>
|
||||
|
||||
@@ -173,7 +160,6 @@ const walletByOwner = ref<Record<string, Row>>({});
|
||||
const filters = reactive({
|
||||
keyword: typeof route.query.keyword === 'string' ? route.query.keyword : '',
|
||||
});
|
||||
const walletOwner = reactive({ type: 'user', identity: '' });
|
||||
const formVisible = ref(false);
|
||||
const detailVisible = ref(false);
|
||||
const editingIdentity = ref('');
|
||||
@@ -289,6 +275,14 @@ const currentIdentity = computed(() =>
|
||||
'',
|
||||
),
|
||||
);
|
||||
const visibleDetailActions = computed(() =>
|
||||
(props.definition.detailActions ?? []).filter((action) => {
|
||||
if (!action.visibleFor) return true;
|
||||
return action.visibleFor.values.includes(
|
||||
detail.value[action.visibleFor.field] as string | number,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
function resetForm(data?: Row) {
|
||||
for (const field of props.definition.fields) {
|
||||
@@ -306,31 +300,36 @@ async function load() {
|
||||
loading.value = true;
|
||||
try {
|
||||
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),
|
||||
)),
|
||||
const result = await resourceApi.list<Row>(
|
||||
props.definition.resource,
|
||||
page.value,
|
||||
pageSize,
|
||||
{
|
||||
role_code: staffType.value,
|
||||
...(filters.keyword ? { keyword: filters.keyword } : {}),
|
||||
},
|
||||
);
|
||||
list.value = filtered.slice((page.value - 1) * pageSize, page.value * pageSize);
|
||||
total.value = filtered.length;
|
||||
list.value = result.list;
|
||||
total.value = result.total;
|
||||
} else 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),
|
||||
)),
|
||||
const serverFilters: Record<string, string> = props.definition.name === 'staff_credential'
|
||||
? { staff_account_identity: managedOwnerIdentity.value }
|
||||
: props.definition.name === 'gas_account'
|
||||
? { gas_basic_identities: managedOwnerIdentity.value }
|
||||
: props.definition.name === 'delivery_account'
|
||||
? { delivery_basic_identities: managedOwnerIdentity.value }
|
||||
: {};
|
||||
const result = await resourceApi.list<Row>(
|
||||
props.definition.resource,
|
||||
page.value,
|
||||
pageSize,
|
||||
{
|
||||
...serverFilters,
|
||||
...(filters.keyword ? { keyword: filters.keyword } : {}),
|
||||
},
|
||||
);
|
||||
list.value = filtered.slice((page.value - 1) * pageSize, page.value * pageSize);
|
||||
total.value = filtered.length;
|
||||
list.value = result.list;
|
||||
total.value = result.total;
|
||||
} else {
|
||||
const result = await resourceApi.list<Row>(
|
||||
props.definition.resource,
|
||||
@@ -356,7 +355,17 @@ async function loadWallets() {
|
||||
walletByOwner.value = {};
|
||||
return;
|
||||
}
|
||||
const wallets = await loadAllRows('/wallet_basic');
|
||||
const ownerIdentities = list.value.map((row) => String(row.identity ?? '')).filter(Boolean);
|
||||
if (!ownerIdentities.length) {
|
||||
walletByOwner.value = {};
|
||||
return;
|
||||
}
|
||||
const wallets = (
|
||||
await resourceApi.list<Row>('/wallet_basic', 1, 100, {
|
||||
owner_type: ownerType,
|
||||
owner_identities: ownerIdentities.join(','),
|
||||
})
|
||||
).list;
|
||||
walletByOwner.value = wallets.reduce<Record<string, Row>>((result, wallet) => {
|
||||
if (wallet.owner_type === ownerType) {
|
||||
result[String(wallet.owner_identity ?? '')] = wallet;
|
||||
@@ -365,10 +374,10 @@ async function loadWallets() {
|
||||
}, {});
|
||||
}
|
||||
|
||||
async function loadAllRows(resource: string) {
|
||||
async function loadAllRows(resource: string, filters: Record<string, string> = {}) {
|
||||
const rows: Row[] = [];
|
||||
for (let currentPage = 1; currentPage <= 100; currentPage += 1) {
|
||||
const result = await resourceApi.list<Row>(resource, currentPage, 100);
|
||||
const result = await resourceApi.list<Row>(resource, currentPage, 100, filters);
|
||||
rows.push(...result.list);
|
||||
if (rows.length >= result.total || result.list.length < 100) break;
|
||||
}
|
||||
@@ -381,7 +390,17 @@ async function loadAccountCounts() {
|
||||
accountCounts.value = {};
|
||||
return;
|
||||
}
|
||||
const accounts = await loadAllRows(management.resource);
|
||||
const ownerIdentities = list.value.map((row) => String(row.identity ?? '')).filter(Boolean);
|
||||
if (!ownerIdentities.length) {
|
||||
accountCounts.value = {};
|
||||
return;
|
||||
}
|
||||
const filterKey = management.relationKey === 'gas_basic_identity'
|
||||
? 'gas_basic_identities'
|
||||
: 'delivery_basic_identities';
|
||||
const accounts = await loadAllRows(management.resource, {
|
||||
[filterKey]: ownerIdentities.join(','),
|
||||
});
|
||||
accountCounts.value = accounts.reduce<Record<string, number>>((counts, account) => {
|
||||
const identity = String(account[management.relationKey] ?? '');
|
||||
if (identity) counts[identity] = (counts[identity] ?? 0) + 1;
|
||||
@@ -619,23 +638,6 @@ async function changePage(next: number) {
|
||||
await load();
|
||||
}
|
||||
|
||||
async function getOwnerWallet() {
|
||||
if (!walletOwner.identity.trim()) {
|
||||
Message.warning('请输入归属标识');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
detail.value = await resourceApi.detail<Row>(
|
||||
'/wallet_basic/owner',
|
||||
`${walletOwner.type}/${walletOwner.identity.trim()}`,
|
||||
);
|
||||
detailVisible.value = true;
|
||||
await load();
|
||||
} catch (error) {
|
||||
Message.error((error as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
function formatValue(value: unknown) {
|
||||
return JSON.stringify(value, null, 2);
|
||||
}
|
||||
@@ -718,12 +720,14 @@ function displayValue(key: string, value: unknown) {
|
||||
async function loadRelation(resource: string, keyword = '') {
|
||||
relationLoading[resource] = true;
|
||||
try {
|
||||
const extraFilters: Record<string, string> = keyword ? { keyword } : {};
|
||||
if (resource === '/staff_account') extraFilters.role_code = 'delivery';
|
||||
relationOptions[resource] = (
|
||||
await resourceApi.list<Row>(
|
||||
resource,
|
||||
1,
|
||||
100,
|
||||
keyword ? { keyword } : {},
|
||||
extraFilters,
|
||||
)
|
||||
).list;
|
||||
} catch (error) {
|
||||
@@ -745,7 +749,9 @@ function searchRelation(resource: string | undefined, keyword: string) {
|
||||
|
||||
onMounted(async () => {
|
||||
if (!route.meta.createMode) await load();
|
||||
const actionFields = props.definition.detailActions?.flatMap((item) => item.fields ?? []) ?? [];
|
||||
const actionFields = route.meta.createMode
|
||||
? []
|
||||
: props.definition.detailActions?.flatMap((item) => item.fields ?? []) ?? [];
|
||||
const relationPaths = new Set(
|
||||
[...props.definition.fields, ...actionFields]
|
||||
.map((field) => field.relation)
|
||||
@@ -789,11 +795,6 @@ function optionLabel(option: Row) {
|
||||
.detail-actions {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.wallet-owner {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background: var(--color-fill-1);
|
||||
}
|
||||
.muted-text {
|
||||
color: var(--color-text-3);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user