重构配送点独立资源页面
This commit is contained in:
86
frontend/delivery_admin/src/api/resource-display.ts
Normal file
86
frontend/delivery_admin/src/api/resource-display.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 功能描述:统一配送点资源独立页面的字段名称和值展示。
|
||||
* 版本:v1.0.0。
|
||||
*/
|
||||
import dayjs from 'dayjs';
|
||||
import { resourceFieldLabel, type ResourceUiDefinition } from './resources';
|
||||
import type { ResourceRow } from './resource-record-form';
|
||||
|
||||
const aliases: Record<string, string> = {
|
||||
id: 'ID',
|
||||
identity: '系统唯一标识',
|
||||
created_at: '创建时间',
|
||||
updated_at: '更新时间',
|
||||
items: '订单明细',
|
||||
assignments: '分配记录',
|
||||
statuses: '状态记录',
|
||||
tracks: '运行轨迹',
|
||||
confirmations: '确认记录',
|
||||
payments: '支付记录',
|
||||
revisions: '修订记录',
|
||||
products: '合同气瓶',
|
||||
};
|
||||
|
||||
const moneyKeys = new Set([
|
||||
'amount', 'unit_price', 'balance', 'withdrawal_balance', 'delivery_fee',
|
||||
'discount_amount', 'total_amount', 'default_delivery_fee', 'fee',
|
||||
]);
|
||||
|
||||
/** 返回聚合详情中的主记录。 */
|
||||
export function primaryRecord(detail: ResourceRow): ResourceRow {
|
||||
for (const key of ['order', 'contract', 'track', 'user']) {
|
||||
if (detail[key] && typeof detail[key] === 'object') return detail[key] as ResourceRow;
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
|
||||
/** 返回字段中文名称。 */
|
||||
export function detailFieldLabel(key: string) {
|
||||
return aliases[key] ?? resourceFieldLabel(key);
|
||||
}
|
||||
|
||||
/** 返回通用记录状态名称。 */
|
||||
export function recordStatusLabel(status: number) {
|
||||
return { 0: '待审核', 1: '启用', 2: '停用', 3: '已归档', 4: '已冻结' }[status] ?? '未知状态';
|
||||
}
|
||||
|
||||
/** 返回通用记录状态颜色。 */
|
||||
export function recordStatusColor(status: number) {
|
||||
return { 0: 'orange', 1: 'green', 2: 'red', 3: 'gray', 4: 'purple' }[status] ?? 'gray';
|
||||
}
|
||||
|
||||
/** 将接口字段值转换为中文可读内容。 */
|
||||
export function displayResourceValue(
|
||||
definition: ResourceUiDefinition,
|
||||
key: string,
|
||||
value: unknown,
|
||||
) {
|
||||
if (value == null || value === '') return '-';
|
||||
const field = definition.fields.find((item) => item.key === key);
|
||||
const option = field?.options?.find((item) => String(item.value) === String(value));
|
||||
if (option) return option.label;
|
||||
if (key === 'role_code') {
|
||||
return { delivery: '配送人员', installer: '安装人员', operations: '运维人员' }[String(value)]
|
||||
?? String(value);
|
||||
}
|
||||
if (typeof value === 'boolean') return value ? '是' : '否';
|
||||
if (key === 'status') return recordStatusLabel(Number(value));
|
||||
if (moneyKeys.has(key) || key.endsWith('_amount') || key.endsWith('_balance_after')) {
|
||||
const amount = Number(value);
|
||||
return Number.isFinite(amount) ? `¥${(amount / 100).toFixed(2)}` : String(value);
|
||||
}
|
||||
if (key.endsWith('_at') || key === 'created_at' || key === 'updated_at') {
|
||||
const date = dayjs(String(value));
|
||||
return date.isValid() ? date.format('YYYY-MM-DD HH:mm:ss') : String(value);
|
||||
}
|
||||
if (typeof value === 'object') return JSON.stringify(value, null, 2);
|
||||
return String(value);
|
||||
}
|
||||
|
||||
/** 返回合同编辑阻止原因。 */
|
||||
export function recordEditReason(definition: ResourceUiDefinition, row: ResourceRow) {
|
||||
if (definition.name !== 'gasorder_contract') return '';
|
||||
if (Number(row.contract_status) !== 0) return '只有草稿状态的合同可以编辑';
|
||||
if (![0, 1].includes(Number(row.status))) return '停用、已归档或已冻结的合同不可编辑';
|
||||
return '';
|
||||
}
|
||||
39
frontend/delivery_admin/src/api/resource-navigation.ts
Normal file
39
frontend/delivery_admin/src/api/resource-navigation.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 功能描述:构造配送点资源列表与独立记录页之间的安全导航地址。
|
||||
* 版本:v1.0.0。
|
||||
*/
|
||||
import type { RouteLocationRaw, Router } from 'vue-router';
|
||||
|
||||
export type RecordNavigationMode = 'create' | 'detail' | 'edit';
|
||||
|
||||
/** 只接受站内绝对路径,防止 return_to 触发外部跳转。 */
|
||||
export function safeReturnPath(value: unknown) {
|
||||
if (
|
||||
typeof value !== 'string' ||
|
||||
!value.startsWith('/') ||
|
||||
value.startsWith('//') ||
|
||||
value.includes('\\') ||
|
||||
/[\r\n]/.test(value)
|
||||
)
|
||||
return '';
|
||||
return value;
|
||||
}
|
||||
|
||||
/** 返回指定资源记录模式对应的命名路由。 */
|
||||
export function recordRouteLocation(
|
||||
listRouteName: string,
|
||||
mode: RecordNavigationMode,
|
||||
identity: string,
|
||||
returnTo: string,
|
||||
): RouteLocationRaw {
|
||||
return {
|
||||
name: `${listRouteName}-${mode}`,
|
||||
...(mode === 'create' ? {} : { params: { identity } }),
|
||||
query: returnTo ? { return_to: returnTo } : {},
|
||||
};
|
||||
}
|
||||
|
||||
/** 返回来源列表;没有安全来源时回退到列表命名路由。 */
|
||||
export function returnToList(router: Router, returnPath: string, listRouteName: string) {
|
||||
return router.push(returnPath || { name: listRouteName });
|
||||
}
|
||||
72
frontend/delivery_admin/src/api/resource-record-form.ts
Normal file
72
frontend/delivery_admin/src/api/resource-record-form.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 功能描述:初始化与校验配送点资源独立页面表单。
|
||||
* 版本:v1.0.0。
|
||||
*/
|
||||
import { isMissingField } from './resource-form';
|
||||
import type { ResourceField } from './resources';
|
||||
|
||||
export type ResourceRow = Record<string, unknown>;
|
||||
|
||||
const editableFieldKeys: Record<string, Set<string>> = {
|
||||
staff_account: new Set(['name', 'phone', 'avatar', 'work_status']),
|
||||
staff_credential: new Set(['staff_account_identity', 'credential_type', 'credential_no', 'expired_at']),
|
||||
user_account: new Set(['name', 'phone', 'avatar', 'real_name']),
|
||||
user_address: new Set(['user_account_identity', 'address', 'longitude', 'latitude', 'is_default']),
|
||||
gasorder_contract: new Set([
|
||||
'title', 'terms', 'file_uri', 'default_delivery_fee',
|
||||
'signed_at', 'effective_at', 'expired_at',
|
||||
]),
|
||||
};
|
||||
|
||||
/** 按后端更新契约返回当前页面允许提交的字段,避免展示无效的只读字段。 */
|
||||
export function recordFormFields(
|
||||
resourceName: string,
|
||||
fields: ResourceField[],
|
||||
mode: 'create' | 'edit',
|
||||
) {
|
||||
const available = fields.filter((field) => field.key !== 'identity');
|
||||
if (mode === 'create') return available;
|
||||
const editable = editableFieldKeys[resourceName];
|
||||
return editable
|
||||
? available.filter((field) => editable.has(field.key))
|
||||
: available.filter((field) => field.type !== 'password');
|
||||
}
|
||||
|
||||
/** 使用详情快照初始化表单,并应用来源关系预填。 */
|
||||
export function resetResourceRecordForm(
|
||||
form: Record<string, any>,
|
||||
fields: ResourceField[],
|
||||
row: ResourceRow,
|
||||
prefill: { relationKey?: string; ownerIdentity?: string },
|
||||
) {
|
||||
for (const key of Object.keys(form)) delete form[key];
|
||||
for (const field of fields) {
|
||||
const value = row[field.key];
|
||||
form[field.key] =
|
||||
value == null
|
||||
? field.defaultValue
|
||||
: field.type === 'money'
|
||||
? Number(value) / 100
|
||||
: value;
|
||||
}
|
||||
if (prefill.relationKey && prefill.ownerIdentity)
|
||||
form[prefill.relationKey] = prefill.ownerIdentity;
|
||||
}
|
||||
|
||||
/** 返回表单首个校验错误;空字符串表示可以提交。 */
|
||||
export function validateResourceRecordForm(
|
||||
form: Record<string, any>,
|
||||
fields: ResourceField[],
|
||||
mode: 'create' | 'edit',
|
||||
) {
|
||||
const required = fields.filter(
|
||||
(field) => field.required && !(mode === 'edit' && field.type === 'password'),
|
||||
);
|
||||
if (required.some((field) => isMissingField(form[field.key]))) return '请填写必填字段';
|
||||
const password = fields.find(
|
||||
(field) => field.type === 'password' && !isMissingField(form[field.key]),
|
||||
);
|
||||
if (password && Array.from(String(form[password.key])).length < 6)
|
||||
return '密码长度不能少于 6 个字符';
|
||||
return '';
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* 功能描述:定义配送点后台资源能力、字段契约和受控业务动作。
|
||||
* 版本:v1.1.0。
|
||||
*/
|
||||
export type ResourceMode =
|
||||
| 'writable'
|
||||
| 'readonly'
|
||||
@@ -234,6 +238,12 @@ const fieldLabels: Record<string, string> = {
|
||||
withdrawable: '计入可提现余额',
|
||||
};
|
||||
|
||||
/** 返回资源字段的中文名称,未知字段保留原键用于排障。 */
|
||||
export function resourceFieldLabel(key: string) {
|
||||
const normalized = key.endsWith('_masked') ? key.slice(0, -7) : key;
|
||||
return fieldLabels[normalized] ?? normalized;
|
||||
}
|
||||
|
||||
const numbers = new Set([
|
||||
'sort_no', 'stock_quantity', 'quantity', 'score', 'version_no',
|
||||
'attempt_no', 'target_product_status',
|
||||
@@ -525,9 +535,9 @@ const deliveryOverrides: ResourceUiDefinition[] = [
|
||||
]),
|
||||
define('wallet_basic', '钱包', 'readonly', []),
|
||||
define('wallet_bank', '银行卡', 'readonly', []),
|
||||
define('wallet_payment', '支付记录', 'readonly', []),
|
||||
define('payment_order', '支付记录', 'readonly', []),
|
||||
define('wallet_record', '钱包流水', 'readonly', []),
|
||||
define('wallet_refund', '退款记录', 'readonly', []),
|
||||
define('payment_refund', '退款记录', 'readonly', []),
|
||||
define('wallet_recharge', '钱包充值', 'append_only', [f('request_no', { required: true }), f('amount', { required: true }), ...reason, f('remark')]),
|
||||
define('wallet_apply_cash', '提现申请', 'append_only', [relation('wallet_bank_identity', '/wallet_bank'), f('request_no', { required: true }), f('amount', { required: true }), withdrawalChannelField, f('remark')]),
|
||||
define('fin_settlement', '结算结果', 'readonly', []),
|
||||
|
||||
Reference in New Issue
Block a user