feat: implement gas and delivery admin systems
This commit is contained in:
12
frontend/delivery_admin/src/api/auth.ts
Normal file
12
frontend/delivery_admin/src/api/auth.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { request } from './http';
|
||||
|
||||
/** 配送点登录和当前账号资料的接口模型。 */
|
||||
export type LoginData = { username: string; password: string };
|
||||
export type LoginReply = { access_token: string; token_type: string; identity: string; display_name: string; role_code: string };
|
||||
export type Profile = { identity: string; username: string; display_name: string; avatar: string; role_code: string; menu_codes: string[] };
|
||||
|
||||
export const authApi = {
|
||||
login: (data: LoginData) => request<LoginReply>('/auth/login', { method: 'POST', body: JSON.stringify(data) }),
|
||||
profile: () => request<Profile>('/auth/profile'),
|
||||
changePassword: (currentPassword: string, newPassword: string) => request<{ changed: boolean }>('/auth/password', { method: 'PUT', body: JSON.stringify({ current_password: currentPassword, new_password: newPassword }) }),
|
||||
};
|
||||
26
frontend/delivery_admin/src/api/http.ts
Normal file
26
frontend/delivery_admin/src/api/http.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/** 配送点后台 HTTP 客户端,使用独立 API 前缀和 JWT 存储键。 */
|
||||
const apiBaseURL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:12426/heqi/delivery/v1';
|
||||
|
||||
export const tokenStorageKey = 'delivery_admin_token';
|
||||
|
||||
export type PageResult<T> = { total: number; list: T[] };
|
||||
|
||||
export async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const token = localStorage.getItem(tokenStorageKey);
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch(`${apiBaseURL}${path}`, {
|
||||
...init,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: token } : {}),
|
||||
...(init?.headers ?? {}),
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
throw new Error('无法连接服务器,请确认服务已启动');
|
||||
}
|
||||
const payload = (await response.json()) as { code?: number; message?: string; details?: T };
|
||||
if (!response.ok || payload.code !== 0) throw new Error(payload.message || '请求失败');
|
||||
return payload.details as T;
|
||||
}
|
||||
51
frontend/delivery_admin/src/api/platform.ts
Normal file
51
frontend/delivery_admin/src/api/platform.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { request } from './http';
|
||||
import { resourceApi } from './resource';
|
||||
|
||||
/** 配送点菜单和工作台接口。 */
|
||||
export interface DashboardMetric {
|
||||
name: string;
|
||||
value: number;
|
||||
}
|
||||
export interface DashboardDailyMetric {
|
||||
date: string;
|
||||
order_count: number;
|
||||
order_amount: number;
|
||||
}
|
||||
export interface DashboardOverview {
|
||||
gas_basic_count: number;
|
||||
delivery_basic_count: number;
|
||||
staff_count: number;
|
||||
user_count: number;
|
||||
product_count: number;
|
||||
active_contract_count: number;
|
||||
today_order_count: number;
|
||||
today_order_amount: number;
|
||||
pending_ticket_count: number;
|
||||
paid_amount: number;
|
||||
order_statuses: DashboardMetric[];
|
||||
product_statuses: DashboardMetric[];
|
||||
payment_channels: DashboardMetric[];
|
||||
recent_orders: DashboardDailyMetric[];
|
||||
}
|
||||
export type PlatformRole = { identity: string; role_code: string; name: string; location_scope: string; is_system: boolean; status: number };
|
||||
export type PlatformMenu = { identity: string; parent_identity?: string; group_code: string; name: string; icon: string; path: string; sort_no: number };
|
||||
|
||||
export const platformApi = {
|
||||
overview: () => request<DashboardOverview>('/dashboard/overview'),
|
||||
listAccount: () => request<{ total: number; list: Record<string, unknown>[] }>('/platform_account'),
|
||||
listRole: () => resourceApi.list<PlatformRole>('/platform_role'),
|
||||
createRole: (data: Record<string, unknown>) => resourceApi.create<PlatformRole>('/platform_role', data),
|
||||
listMenu: () => request<{ total: number; list: PlatformMenu[] }>('/delivery_menu'),
|
||||
listRoleMenuIdentities: (identity: string) =>
|
||||
request<{ menu_identities: string[] }>(
|
||||
`/platform_role/${identity}/menu`,
|
||||
),
|
||||
replaceRoleMenus: (identity: string, menuIdentities: string[]) =>
|
||||
request<{ updated: boolean }>(
|
||||
`/platform_role/${identity}/menu`,
|
||||
{
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ menu_identities: menuIdentities }),
|
||||
},
|
||||
),
|
||||
};
|
||||
66
frontend/delivery_admin/src/api/resource-form.ts
Normal file
66
frontend/delivery_admin/src/api/resource-form.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import type { ResourceField } from './resources';
|
||||
|
||||
export type ResourceFormValue = string | number | boolean | string[] | undefined;
|
||||
export type ResourceFormMode = 'create' | 'edit';
|
||||
|
||||
export function isMissingField(value: ResourceFormValue | null): boolean {
|
||||
return value === '' || value === null || value === undefined || (Array.isArray(value) && value.length === 0);
|
||||
}
|
||||
|
||||
export function isResourceFieldRequired(
|
||||
field: ResourceField,
|
||||
mode: ResourceFormMode,
|
||||
): boolean {
|
||||
return Boolean(field.required && !(mode === 'edit' && field.type === 'password'));
|
||||
}
|
||||
|
||||
export function buildResourcePayload(
|
||||
fields: ResourceField[],
|
||||
form: Record<string, ResourceFormValue>,
|
||||
mode: ResourceFormMode = 'create',
|
||||
): Record<string, unknown> {
|
||||
const payload: Record<string, unknown> = {};
|
||||
for (const field of fields) {
|
||||
if (mode === 'edit' && field.type === 'password') continue;
|
||||
const value = form[field.key];
|
||||
if (field.type === 'identity-list' && Array.isArray(value)) {
|
||||
payload[field.key] = value;
|
||||
continue;
|
||||
}
|
||||
if (isMissingField(value)) {
|
||||
if (!field.required) continue;
|
||||
payload[field.key] = value;
|
||||
continue;
|
||||
}
|
||||
switch (field.type) {
|
||||
case 'number': {
|
||||
const number = Number(value);
|
||||
if (!Number.isFinite(number))
|
||||
throw new Error(`${field.label}必须是数字`);
|
||||
payload[field.key] = number;
|
||||
break;
|
||||
}
|
||||
case 'money': {
|
||||
const amount = Number(value);
|
||||
if (!Number.isFinite(amount))
|
||||
throw new Error(`${field.label}必须是有效金额`);
|
||||
payload[field.key] = Math.round(amount * 100);
|
||||
break;
|
||||
}
|
||||
case 'boolean':
|
||||
payload[field.key] = value === true || value === 'true';
|
||||
break;
|
||||
case 'date':
|
||||
case 'datetime': {
|
||||
const date = new Date(String(value));
|
||||
if (Number.isNaN(date.getTime()))
|
||||
throw new Error(`${field.label}必须是有效日期`);
|
||||
payload[field.key] = date.toISOString();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
payload[field.key] = value;
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
16
frontend/delivery_admin/src/api/resource.ts
Normal file
16
frontend/delivery_admin/src/api/resource.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { request, type PageResult } from './http';
|
||||
|
||||
/** 所有标准 CRUD 资源共享的调用方法。 */
|
||||
export const resourceApi = {
|
||||
list: <T>(resource: string, page = 1, size = 20, filters: Record<string, string> = {}) => {
|
||||
const query = new URLSearchParams({ page: String(page), size: String(size), ...filters });
|
||||
return request<PageResult<T>>(`${resource}?${query.toString()}`);
|
||||
},
|
||||
detail: <T>(resource: string, identity: string) => request<T>(`${resource}/${identity}`),
|
||||
create: <T>(resource: string, data: Record<string, unknown>) => request<T>(resource, { method: 'POST', body: JSON.stringify(data) }),
|
||||
update: <T>(resource: string, identity: string, data: Record<string, unknown>) => request<T>(`${resource}/${identity}`, { method: 'PUT', body: JSON.stringify(data) }),
|
||||
updateStatus: (resource: string, identity: string, status: number) => request<{ updated: boolean }>(`${resource}/${identity}/status`, { method: 'PATCH', body: JSON.stringify({ status }) }),
|
||||
archive: (resource: string, identity: string) => request<{ updated: boolean }>(`${resource}/${identity}`, { method: 'DELETE' }),
|
||||
action: <T>(resource: string, method: 'POST' | 'PUT' | 'PATCH', data: Record<string, unknown>) =>
|
||||
request<T>(resource, { method, body: JSON.stringify(data) }),
|
||||
};
|
||||
480
frontend/delivery_admin/src/api/resources.ts
Normal file
480
frontend/delivery_admin/src/api/resources.ts
Normal file
@@ -0,0 +1,480 @@
|
||||
export type ResourceMode =
|
||||
| 'writable'
|
||||
| 'readonly'
|
||||
| 'append_only'
|
||||
| 'editable'
|
||||
| 'managed';
|
||||
export type ResourcePageKind = 'list' | 'tree';
|
||||
export type ResourceFieldType =
|
||||
| 'text'
|
||||
| 'password'
|
||||
| 'identity'
|
||||
| 'identity-list'
|
||||
| 'number'
|
||||
| 'money'
|
||||
| 'boolean'
|
||||
| 'date'
|
||||
| 'datetime'
|
||||
| 'textarea'
|
||||
| 'select';
|
||||
|
||||
export type ResourceField = {
|
||||
key: string;
|
||||
label: string;
|
||||
type?: ResourceFieldType;
|
||||
required?: boolean;
|
||||
relation?: string;
|
||||
options?: Array<{ label: string; value: string | number }>;
|
||||
};
|
||||
|
||||
export type DetailAction = {
|
||||
name: string;
|
||||
resource: string;
|
||||
method?: 'POST' | 'PUT' | 'PATCH';
|
||||
danger?: boolean;
|
||||
fields?: ResourceField[];
|
||||
visibleFor?: { field: string; values: Array<string | number> };
|
||||
};
|
||||
|
||||
export type ResourceUiDefinition = {
|
||||
key: string;
|
||||
name: string;
|
||||
resource: `/${string}`;
|
||||
title: string;
|
||||
mode: ResourceMode;
|
||||
pageKind: ResourcePageKind;
|
||||
fields: ResourceField[];
|
||||
detailActions?: DetailAction[];
|
||||
canCreate: boolean;
|
||||
canEdit: boolean;
|
||||
canChangeStatus: boolean;
|
||||
canArchive: boolean;
|
||||
accountManagement?: {
|
||||
resource: `/${string}`;
|
||||
relationKey: string;
|
||||
title: string;
|
||||
};
|
||||
walletOwnerType?: 'gas' | 'delivery' | 'staff' | 'user';
|
||||
};
|
||||
|
||||
const fieldLabels: Record<string, string> = {
|
||||
code: '编码',
|
||||
name: '名称',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
display_name: '显示名称',
|
||||
role_code: '角色编码',
|
||||
platform_role_code: '平台角色',
|
||||
credit_code: '统一社会信用代码',
|
||||
principal: '负责人',
|
||||
manager: '库房负责人',
|
||||
phone: '联系电话',
|
||||
address: '地址',
|
||||
longitude: '经度',
|
||||
latitude: '纬度',
|
||||
delivery_code: '配送站编码',
|
||||
avatar: '头像',
|
||||
work_status: '工作状态',
|
||||
credential_type: '资质类型',
|
||||
credential_no: '资质编号',
|
||||
expired_at: '到期时间',
|
||||
real_name: '实名姓名',
|
||||
is_default: '默认地址',
|
||||
params: '智能气阀参数',
|
||||
produced_at: '生产时间',
|
||||
enabled_at: '启用时间',
|
||||
product_status: '智能气阀状态',
|
||||
contract_status: '合同状态',
|
||||
order_status: '订单状态',
|
||||
previous_order_status: '异常前订单状态',
|
||||
payment_status: '支付状态',
|
||||
refund_status: '退款状态',
|
||||
apply_status: '提现状态',
|
||||
ticket_status: '工单状态',
|
||||
reconciliation_status: '对账状态',
|
||||
repair_no: '检修单号',
|
||||
repair_type: '检修类型',
|
||||
started_at: '开始时间',
|
||||
completed_at: '完成时间',
|
||||
result: '检修结果',
|
||||
target_product_status: '目标产品状态',
|
||||
content: '内容',
|
||||
operator: '操作人员',
|
||||
remark: '备注',
|
||||
action: '动作',
|
||||
occurred_at: '发生时间',
|
||||
reason: '原因',
|
||||
operator_identity: '操作人标识',
|
||||
operator_name: '操作人',
|
||||
owner_type: '归属类型',
|
||||
owner_identity: '归属标识',
|
||||
alipay_id: '支付宝账号',
|
||||
alipay_name: '支付宝账户名',
|
||||
wxpay_id: '微信账号',
|
||||
wxpay_name: '微信账户名',
|
||||
balance: '余额(元)',
|
||||
withdrawal_balance: '可提现余额(元)',
|
||||
card_no_last4: '银行卡末四位',
|
||||
bank_name: '银行名称',
|
||||
card_owner: '持卡人',
|
||||
payment_no: '支付单号',
|
||||
order_no: '订单号',
|
||||
trade_no: '第三方流水号',
|
||||
payment_type: '支付业务类型',
|
||||
pay_channel: '支付渠道',
|
||||
pay_type: '支付类型',
|
||||
amount: '金额(元)',
|
||||
fee: '手续费(元)',
|
||||
args: '支付参数',
|
||||
callback_msg: '回调信息',
|
||||
record_no: '流水号',
|
||||
request_no: '请求流水号',
|
||||
direction: '收支方向',
|
||||
trade_type: '交易类型',
|
||||
balance_after: '变动后余额(元)',
|
||||
withdrawal_balance_after: '变动后可提现余额(元)',
|
||||
refund_no: '退款单号',
|
||||
cash_no: '提现单号',
|
||||
channel: '渠道',
|
||||
review_reason: '审核原因',
|
||||
reviewed_at: '审核时间',
|
||||
reviewer_name: '审核人',
|
||||
reviewer_identity: '审核人标识',
|
||||
ymd: '日期',
|
||||
ym: '月份',
|
||||
contract_no: '合同编号',
|
||||
title: '标题',
|
||||
terms: '合同条款',
|
||||
file_uri: '附件地址',
|
||||
default_delivery_fee: '默认配送费(元)',
|
||||
signed_at: '签署时间',
|
||||
effective_at: '生效时间',
|
||||
unit_price: '单价(元)',
|
||||
unbound_at: '解绑时间',
|
||||
revision_no: '修订号',
|
||||
creator_type: '创建方类型',
|
||||
creator_identity: '创建方标识',
|
||||
contact_name: '联系人',
|
||||
contact_phone: '联系电话',
|
||||
discount_amount: '优惠金额(元)',
|
||||
total_amount: '总金额(元)',
|
||||
delivery_fee: '配送费(元)',
|
||||
assigned_at: '分配时间',
|
||||
from_status: '原状态',
|
||||
to_status: '新状态',
|
||||
track_no: '轨迹编号',
|
||||
attempt_no: '尝试次数',
|
||||
point_type: '轨迹点类型',
|
||||
confirmed_at: '确认时间',
|
||||
sort_no: '排序',
|
||||
product_code: '商品编码',
|
||||
price_amount: '售价(元)',
|
||||
stock_quantity: '库存',
|
||||
value: '属性值',
|
||||
image_uri: '图片地址',
|
||||
is_cover: '封面图',
|
||||
quantity: '数量',
|
||||
selected: '是否选中',
|
||||
product_snapshot: '商品快照',
|
||||
sale_amount: '成交金额(元)',
|
||||
score: '评分',
|
||||
paid_at: '支付时间',
|
||||
settlement_no: '结算单号',
|
||||
subject_type: '结算主体类型',
|
||||
period_start: '结算开始时间',
|
||||
period_end: '结算结束时间',
|
||||
bill_date: '账单日期',
|
||||
difference_amount: '差异金额(元)',
|
||||
content_type: '内容类型',
|
||||
body: '正文',
|
||||
version_no: '版本号',
|
||||
publish_status: '发布状态',
|
||||
ticket_no: '工单号',
|
||||
category: '分类',
|
||||
priority: '优先级',
|
||||
location_scope: '坐标权限',
|
||||
parent_identity: '父级',
|
||||
group_code: '菜单分组编码',
|
||||
icon: '图标',
|
||||
path: '路由',
|
||||
menu_identities: '菜单权限',
|
||||
status: '状态',
|
||||
};
|
||||
|
||||
const numbers = new Set([
|
||||
'sort_no', 'stock_quantity', 'quantity', 'score', 'version_no',
|
||||
'attempt_no', 'target_product_status',
|
||||
]);
|
||||
const money = new Set([
|
||||
'unit_price', 'amount', 'fee', 'balance', 'withdrawal_balance',
|
||||
'balance_after', 'withdrawal_balance_after', 'default_delivery_fee',
|
||||
'discount_amount', 'total_amount', 'delivery_fee', 'price_amount',
|
||||
'sale_amount', 'difference_amount',
|
||||
]);
|
||||
const booleans = new Set(['is_default', 'is_cover', 'selected', 'withdrawable']);
|
||||
const dates = new Set(['bill_date']);
|
||||
const datetimes = new Set([
|
||||
'expired_at', 'produced_at', 'enabled_at', 'started_at', 'completed_at',
|
||||
'occurred_at', 'reviewed_at', 'signed_at', 'effective_at', 'unbound_at',
|
||||
'assigned_at', 'confirmed_at', 'paid_at', 'period_start', 'period_end',
|
||||
]);
|
||||
const textareas = new Set([
|
||||
'params', 'content', 'remark', 'reason', 'terms', 'args', 'callback_msg',
|
||||
'review_reason', 'product_snapshot', 'body',
|
||||
]);
|
||||
|
||||
function f(key: string, options: Partial<ResourceField> = {}): ResourceField {
|
||||
const type: ResourceFieldType = key.endsWith('_identity')
|
||||
? 'identity'
|
||||
: money.has(key)
|
||||
? 'money'
|
||||
: numbers.has(key)
|
||||
? 'number'
|
||||
: booleans.has(key)
|
||||
? 'boolean'
|
||||
: dates.has(key)
|
||||
? 'date'
|
||||
: datetimes.has(key)
|
||||
? 'datetime'
|
||||
: textareas.has(key)
|
||||
? 'textarea'
|
||||
: key === 'password'
|
||||
? 'password'
|
||||
: 'text';
|
||||
return { key, label: fieldLabels[key] ?? key, type, ...options };
|
||||
}
|
||||
|
||||
function relation(key: string, resource: string, required = false): ResourceField {
|
||||
return f(key, { type: 'identity', relation: resource, required });
|
||||
}
|
||||
|
||||
function define(
|
||||
name: string,
|
||||
title: string,
|
||||
mode: ResourceMode,
|
||||
fields: ResourceField[],
|
||||
pageKind: ResourcePageKind = 'list',
|
||||
detailActions?: DetailAction[],
|
||||
capabilities: Partial<
|
||||
Pick<
|
||||
ResourceUiDefinition,
|
||||
'canCreate' | 'canEdit' | 'canChangeStatus' | 'canArchive'
|
||||
>
|
||||
> = {},
|
||||
): ResourceUiDefinition {
|
||||
const defaults = {
|
||||
canCreate: mode === 'writable' || mode === 'editable' || mode === 'append_only',
|
||||
canEdit: mode === 'writable' || mode === 'editable',
|
||||
canChangeStatus: mode === 'writable' || mode === 'editable',
|
||||
canArchive: mode === 'writable',
|
||||
};
|
||||
return {
|
||||
key: name.replace(/_/g, '-'),
|
||||
name,
|
||||
resource: `/${name}`,
|
||||
title,
|
||||
mode,
|
||||
pageKind,
|
||||
fields,
|
||||
...defaults,
|
||||
...capabilities,
|
||||
...(detailActions ? { detailActions } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
const reason = [f('reason', { required: true })];
|
||||
|
||||
const platformResources: ResourceUiDefinition[] = [
|
||||
{ ...define('gas_basic', '气站管理', 'writable', [f('code', { required: true }), f('name', { required: true }), f('credit_code'), f('principal'), f('address'), f('longitude'), f('latitude')]), accountManagement: { resource: '/gas_account', relationKey: 'gas_basic_identity', title: '气站账户' }, walletOwnerType: 'gas' },
|
||||
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: '配送点账户' }, walletOwnerType: 'delivery' },
|
||||
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', { 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' }] })]), walletOwnerType: 'staff' },
|
||||
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')]), walletOwnerType: 'user' },
|
||||
define('user_address', '用户地址', 'writable', [relation('user_account_identity', '/user_account', true), f('address', { required: true }), f('longitude'), f('latitude'), f('is_default')]),
|
||||
define('user_service_relation', '用户服务关系', 'writable', [relation('user_account_identity', '/user_account', true), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('staff_account_identity', '/staff_account')]),
|
||||
|
||||
define('product_type', '智能气阀类型', 'editable', [f('code', { required: true }), f('name', { required: true })]),
|
||||
define('product_warehouse', '智能气阀库房', 'editable', [f('code', { required: true }), f('name', { required: true }), f('address'), f('manager'), f('phone')]),
|
||||
define('product_info', '智能气阀', 'editable', [f('code', { required: true }), f('name', { required: true }), relation('product_type_identity', '/product_type', true), f('params', { required: true }), relation('warehouse_identity', '/product_warehouse'), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('user_account_identity', '/user_account'), f('produced_at', { required: true })], 'list', [
|
||||
{ name: '修改智能气阀状态', resource: '/product_info/:identity/lifecycle', method: 'PATCH', fields: [f('product_status', { required: true, type: 'select', options: [{ label: '待处理', value: 10 }, { label: '在库', value: 28 }, { label: '运输中', value: 29 }, { label: '使用中', value: 30 }, { label: '维修中', value: 31 }, { label: '已报废', value: 27 }] })] },
|
||||
]),
|
||||
define('product_repair', '智能气阀检修记录', 'editable', [relation('product_info_identity', '/product_info', true), f('repair_no', { required: true }), f('repair_type', { required: true }), f('started_at', { required: true }), f('completed_at'), f('result', { type: 'select', options: [{ label: '待处理', value: 'pending' }, { label: '通过', value: 'passed' }, { label: '未通过', value: 'failed' }] }), f('target_product_status', { type: 'select', options: [{ label: '在库', value: 28 }, { label: '运输中', value: 29 }, { label: '使用中', value: 30 }, { label: '报废', value: 27 }] }), f('content'), f('operator'), f('remark')]),
|
||||
define('product_owner', '智能气阀归属记录', 'readonly', []),
|
||||
|
||||
define('gasorder_contract', '配送合同', 'managed', [f('contract_no', { required: true }), relation('user_account_identity', '/user_account', true), relation('gas_basic_identity', '/gas_basic', true), relation('delivery_basic_identity', '/delivery_basic'), f('title', { required: true }), f('terms'), f('file_uri'), f('default_delivery_fee'), f('signed_at', { required: true }), f('effective_at', { required: true }), f('expired_at')], 'list', [
|
||||
{ name: '启用合同', resource: '/gasorder_contract/:identity/activate', fields: reason, visibleFor: { field: 'contract_status', values: [10] } },
|
||||
{ name: '续签合同', resource: '/gasorder_contract/:identity/renew', fields: [f('effective_at', { required: true }), f('expired_at'), ...reason], visibleFor: { field: 'contract_status', values: [11, 12] } },
|
||||
{ name: '终止合同', resource: '/gasorder_contract/:identity/terminate', danger: true, fields: reason, visibleFor: { field: 'contract_status', values: [11] } },
|
||||
], { canCreate: true, canEdit: true }),
|
||||
define('gasorder_contract_product', '合同气瓶', 'append_only', [relation('gasorder_contract_identity', '/gasorder_contract', true), relation('product_info_identity', '/product_info', true), f('unit_price')], 'list', [
|
||||
{ name: '解绑气瓶', resource: '/gasorder_contract_product/:identity/unbind', danger: true, fields: reason },
|
||||
]),
|
||||
define('gasorder_contract_revision', '合同修订记录', 'readonly', []),
|
||||
define('gasorder_basic', '气体配送订单', 'append_only', [f('request_no', { required: true }), relation('gasorder_contract_identity', '/gasorder_contract', true), f('creator_type', { required: true, type: 'select', options: [{ label: '用户', value: 'user' }, { label: '工作人员', value: 'staff' }, { label: '配送站', value: 'delivery' }, { label: '气站', value: 'gas' }] }), f('creator_identity', { required: true }), relation('user_address_identity', '/user_address', true), f('gasorder_contract_product_identities', { required: true, type: 'identity-list', relation: '/gasorder_contract_product' }), f('contact_name', { required: true }), f('contact_phone', { required: true }), f('discount_amount'), f('remark')], 'list', [
|
||||
{ name: '分配订单', resource: '/gasorder_basic/:identity/assign', fields: [relation('delivery_basic_identity', '/delivery_basic', true), relation('staff_account_identity', '/staff_account', true), ...reason], visibleFor: { field: 'order_status', values: [16, 18] } },
|
||||
{ name: '开始罐装', resource: '/gasorder_basic/:identity/filling', fields: reason, visibleFor: { field: 'order_status', values: [18] } },
|
||||
{ name: '待配送', resource: '/gasorder_basic/:identity/ready', fields: reason, visibleFor: { field: 'order_status', values: [19] } },
|
||||
{ name: '开始配送', resource: '/gasorder_basic/:identity/delivering', fields: reason, visibleFor: { field: 'order_status', values: [20] } },
|
||||
{ name: '等待签收', resource: '/gasorder_basic/:identity/awaiting-confirmation', fields: reason, visibleFor: { field: 'order_status', values: [33] } },
|
||||
{ name: '完成订单', resource: '/gasorder_basic/:identity/complete', fields: [...reason, f('confirm_type', { required: true }), f('recipient_name', { required: true }), f('recipient_phone'), f('proof_uri'), f('remark')], visibleFor: { field: 'order_status', values: [34] } },
|
||||
{ name: '标记异常', resource: '/gasorder_basic/:identity/exception', fields: reason, visibleFor: { field: 'order_status', values: [19, 20, 33, 34] } },
|
||||
{ name: '恢复订单', resource: '/gasorder_basic/:identity/recover', fields: reason, visibleFor: { field: 'order_status', values: [21] } },
|
||||
{ name: '取消订单', resource: '/gasorder_basic/:identity/cancel', danger: true, fields: reason, visibleFor: { field: 'order_status', values: [16, 18] } },
|
||||
]),
|
||||
define('gasorder_item', '订单明细', 'readonly', []),
|
||||
define('gasorder_assign', '分配记录', 'readonly', []),
|
||||
define('gasorder_status', '状态记录', 'readonly', []),
|
||||
define('gasorder_track', '运行轨迹', 'readonly', []),
|
||||
define('gasorder_track_point', '轨迹点', 'readonly', []),
|
||||
define('gasorder_confirm', '确认记录', 'readonly', []),
|
||||
define('gasorder_payment', '支付记录', 'readonly', []),
|
||||
|
||||
define('ec_category', '商品分类', 'writable', [relation('parent_identity', '/ec_category'), f('name', { required: true }), f('sort_no')], 'tree'),
|
||||
define('ec_product', '商品', 'writable', [relation('ec_category_identity', '/ec_category', true), f('product_code', { required: true }), f('name', { required: true }), f('price_amount', { required: true }), f('stock_quantity')]),
|
||||
define('ec_product_attribute', '商品属性', 'writable', [relation('ec_product_identity', '/ec_product', true), f('name', { required: true }), f('value', { required: true }), f('sort_no')]),
|
||||
define('ec_product_image', '商品图片', 'writable', [relation('ec_product_identity', '/ec_product', true), f('image_uri', { required: true }), f('sort_no'), f('is_cover')]),
|
||||
define('ec_cart', '购物车', 'readonly', []),
|
||||
define('ec_order', '商城订单', 'readonly', []),
|
||||
define('ec_order_item', '商城订单明细', 'readonly', []),
|
||||
define('ec_review', '商品评价', 'readonly', []),
|
||||
|
||||
define('wallet_basic', '钱包', 'readonly', [f('owner_type'), f('owner_identity'), f('alipay_id'), f('alipay_name'), f('wxpay_id'), f('wxpay_name'), f('balance'), f('withdrawal_balance')], 'list', [
|
||||
{ name: '后台充值', resource: '/wallet_basic/:identity/recharge', fields: [f('request_no', { required: true }), f('amount', { required: true }), f('withdrawable'), ...reason, f('remark')] },
|
||||
{ name: '修改钱包状态', resource: '/wallet_basic/:identity/status', method: 'PATCH', fields: [f('status', { required: true, type: 'select', options: [{ label: '启用', value: 1 }, { label: '停用', value: 2 }, { label: '冻结', value: 4 }] })] },
|
||||
]),
|
||||
define('wallet_bank', '银行卡', 'readonly', []),
|
||||
define('wallet_payment', '钱包支付记录', 'readonly', []),
|
||||
define('wallet_record', '钱包流水', 'readonly', []),
|
||||
define('wallet_refund', '退款记录', 'readonly', []),
|
||||
define('wallet_apply_cash', '提现记录', 'readonly', [
|
||||
f('cash_no'),
|
||||
f('wallet_basic_identity', { type: 'identity' }),
|
||||
f('amount'),
|
||||
f('apply_status', { type: 'select', options: [
|
||||
{ label: '待处理', value: 10 },
|
||||
{ label: '已通过', value: 25 },
|
||||
{ label: '已驳回', value: 26 },
|
||||
{ label: '已完成', value: 23 },
|
||||
] }),
|
||||
f('reviewer_name'),
|
||||
f('review_reason'),
|
||||
f('reviewed_at'),
|
||||
f('completed_at'),
|
||||
f('channel'),
|
||||
f('trade_no'),
|
||||
f('remark'),
|
||||
], 'list', [
|
||||
{ name: '审核通过', resource: '/wallet_apply_cash/:identity/approve', fields: reason, visibleFor: { field: 'apply_status', values: [10] } },
|
||||
{ name: '审核驳回', resource: '/wallet_apply_cash/:identity/reject', danger: true, fields: reason, visibleFor: { field: 'apply_status', values: [10] } },
|
||||
{ name: '标记处理完成', resource: '/wallet_apply_cash/:identity/complete', fields: [f('trade_no', { required: true }), f('callback_msg')], visibleFor: { field: 'apply_status', values: [25] } },
|
||||
]),
|
||||
|
||||
define('fin_payment', '财务支付记录', 'readonly', []),
|
||||
define('fin_settlement', '财务结算', 'writable', [f('settlement_no', { required: true }), f('subject_type', { required: true }), f('subject_identity', { required: true }), f('period_start', { required: true }), f('period_end', { required: true })]),
|
||||
define('fin_reconciliation', '财务对账', 'readonly', []),
|
||||
define('cms_content', '内容', 'writable', [f('content_type', { required: true }), f('title', { required: true }), f('body', { required: true }), f('version_no'), f('publish_status')]),
|
||||
define('cs_ticket', '客服工单', 'writable', [relation('user_account_identity', '/user_account', true), f('ticket_no', { required: true }), f('category', { required: true }), f('priority', { required: true })]),
|
||||
define('platform_account', '平台账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('display_name'), f('avatar'), f('platform_role_code', { required: true }), f('phone')]),
|
||||
define('platform_role', '平台角色', 'writable', [f('role_code', { required: true }), f('name', { required: true }), f('location_scope', { required: true, type: 'select', options: [{ label: '脱敏坐标', value: 'standard' }, { label: '精确坐标', value: 'precise' }] })], 'list', [
|
||||
{ name: '分配菜单', resource: '/platform_role/:identity/menu', method: 'PUT', fields: [f('menu_identities', { type: 'identity-list', relation: '/platform_menu' })] },
|
||||
]),
|
||||
define('platform_menu', '平台菜单', 'readonly', [], 'tree'),
|
||||
];
|
||||
|
||||
const gasOverrides: ResourceUiDefinition[] = [
|
||||
{ ...define('delivery_basic', '配送点管理', 'writable', [f('delivery_code', { required: true }), f('name', { required: true }), 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')], 'list', [
|
||||
{ name: '重置密码', resource: '/delivery_account/:identity/password', method: 'PUT', fields: [f('password', { required: true })] },
|
||||
]),
|
||||
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('delivery_basic_identity', '/delivery_basic'), f('work_status', { type: 'select', options: [{ label: '在岗', value: 'on_duty' }, { label: '离岗', value: 'off_duty' }] })], 'list', [
|
||||
{ name: '重置密码', resource: '/staff_account/:identity/password', method: 'PUT', fields: [f('password', { required: true })] },
|
||||
]),
|
||||
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'), relation('delivery_basic_identity', '/delivery_basic', true)], 'list', [
|
||||
{ name: '重置密码', resource: '/user_account/:identity/password', method: 'PUT', fields: [f('password', { required: true })] },
|
||||
]),
|
||||
define('user_address', '用户地址', 'writable', [relation('user_account_identity', '/user_account', true), f('address', { required: true }), f('longitude'), f('latitude'), f('is_default')]),
|
||||
define('gasorder_contract', '配送合同', 'managed', [f('contract_no', { required: true }), relation('user_account_identity', '/user_account', true), relation('delivery_basic_identity', '/delivery_basic'), f('title', { required: true }), f('terms'), f('file_uri'), f('default_delivery_fee'), f('signed_at', { required: true }), f('effective_at', { required: true }), f('expired_at')], 'list', [
|
||||
{ name: '启用合同', resource: '/gasorder_contract/:identity/activate', fields: reason, visibleFor: { field: 'contract_status', values: [10] } },
|
||||
{ name: '续签合同', resource: '/gasorder_contract/:identity/renew', fields: [f('effective_at', { required: true }), f('expired_at'), ...reason], visibleFor: { field: 'contract_status', values: [11, 12, 13] } },
|
||||
{ name: '终止合同', resource: '/gasorder_contract/:identity/terminate', danger: true, fields: reason, visibleFor: { field: 'contract_status', values: [11] } },
|
||||
], { canCreate: true, canEdit: true }),
|
||||
define('gasorder_contract_product', '合同气瓶', 'append_only', [relation('gasorder_contract_identity', '/gasorder_contract', true), relation('product_info_identity', '/product_info', true), f('unit_price')], 'list', [
|
||||
{ name: '解绑气瓶', resource: '/gasorder_contract_product/:identity/unbind', danger: true, fields: reason },
|
||||
]),
|
||||
define('gasorder_contract_revision', '合同修订记录', 'readonly', []),
|
||||
define('product_info', '合同可选气瓶', 'readonly', []),
|
||||
define('gasorder_basic', '燃气配送订单', 'append_only', [f('request_no', { required: true }), relation('gasorder_contract_identity', '/gasorder_contract', true), relation('user_address_identity', '/user_address', true), f('gasorder_contract_product_identities', { required: true, type: 'identity-list', relation: '/gasorder_contract_product' }), f('contact_name', { required: true }), f('contact_phone', { required: true }), f('discount_amount'), f('remark')], 'list', [
|
||||
{ name: '分配订单', resource: '/gasorder_basic/:identity/assign', fields: [relation('delivery_basic_identity', '/delivery_basic', true), relation('staff_account_identity', '/staff_account', true), ...reason], visibleFor: { field: 'order_status', values: [16, 18] } },
|
||||
{ name: '开始罐装', resource: '/gasorder_basic/:identity/filling', fields: reason, visibleFor: { field: 'order_status', values: [18] } },
|
||||
{ name: '待配送', resource: '/gasorder_basic/:identity/ready', fields: reason, visibleFor: { field: 'order_status', values: [19] } },
|
||||
{ name: '标记异常', resource: '/gasorder_basic/:identity/exception', fields: reason, visibleFor: { field: 'order_status', values: [19, 20, 33, 34] } },
|
||||
{ name: '恢复订单', resource: '/gasorder_basic/:identity/recover', fields: reason, visibleFor: { field: 'order_status', values: [21] } },
|
||||
{ name: '取消订单', resource: '/gasorder_basic/:identity/cancel', danger: true, fields: reason, visibleFor: { field: 'order_status', values: [16, 18] } },
|
||||
]),
|
||||
define('wallet_basic', '钱包', 'readonly', []),
|
||||
define('wallet_bank', '银行卡', 'readonly', []),
|
||||
define('wallet_payment', '支付记录', 'readonly', []),
|
||||
define('wallet_record', '钱包流水', 'readonly', []),
|
||||
define('wallet_refund', '退款记录', 'readonly', []),
|
||||
define('wallet_apply_cash', '提现申请', 'append_only', [relation('wallet_bank_identity', '/wallet_bank'), f('request_no', { required: true }), f('amount', { required: true }), f('channel', { required: true }), f('remark')]),
|
||||
define('fin_settlement', '财务结算', 'readonly', []),
|
||||
define('fin_reconciliation', '财务对账', 'readonly', []),
|
||||
define('cs_ticket', '客服工单', 'writable', [relation('user_account_identity', '/user_account', true), f('ticket_no', { required: true }), f('category', { required: true }), f('priority', { required: true })]),
|
||||
];
|
||||
|
||||
const deliveryOverrides: ResourceUiDefinition[] = [
|
||||
define('delivery_profile', '配送点资料', 'readonly', []),
|
||||
define('staff_account', '配送人员', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('work_status', { required: true, type: 'select', options: [{ label: '在岗', value: 'on_duty' }, { label: '离岗', value: 'off_duty' }] })], 'list', [
|
||||
{ name: '重置密码', resource: '/staff_account/:identity/password', method: 'PUT', fields: [f('password', { required: true })] },
|
||||
]),
|
||||
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')], 'list', [
|
||||
{ name: '重置密码', resource: '/user_account/:identity/password', method: 'PUT', fields: [f('password', { required: true })] },
|
||||
]),
|
||||
define('user_address', '用户地址', 'writable', [relation('user_account_identity', '/user_account', true), f('address', { required: true }), f('longitude'), f('latitude'), f('is_default')]),
|
||||
define('gasorder_contract', '配送合同', 'managed', [f('contract_no', { required: true }), relation('user_account_identity', '/user_account', true), f('title', { required: true }), f('terms'), f('file_uri'), f('default_delivery_fee'), f('signed_at', { required: true }), f('effective_at', { required: true }), f('expired_at')], 'list', [
|
||||
{ name: '启用合同', resource: '/gasorder_contract/:identity/activate', fields: reason, visibleFor: { field: 'contract_status', values: [10] } },
|
||||
{ name: '续签合同', resource: '/gasorder_contract/:identity/renew', fields: [f('effective_at', { required: true }), f('expired_at'), ...reason], visibleFor: { field: 'contract_status', values: [11, 12, 13] } },
|
||||
{ name: '终止合同', resource: '/gasorder_contract/:identity/terminate', danger: true, fields: reason, visibleFor: { field: 'contract_status', values: [11] } },
|
||||
], { canCreate: true, canEdit: true }),
|
||||
define('gasorder_contract_product', '合同气瓶', 'append_only', [relation('gasorder_contract_identity', '/gasorder_contract', true), relation('product_info_identity', '/product_info', true), f('unit_price')], 'list', [
|
||||
{ name: '解绑气瓶', resource: '/gasorder_contract_product/:identity/unbind', danger: true, fields: reason },
|
||||
]),
|
||||
define('gasorder_contract_revision', '合同修订记录', 'readonly', []),
|
||||
define('product_info', '合同可选气瓶', 'readonly', []),
|
||||
define('gasorder_basic', '配送订单', 'append_only', [f('request_no', { required: true }), relation('gasorder_contract_identity', '/gasorder_contract', true), relation('user_address_identity', '/user_address', true), f('gasorder_contract_product_identities', { required: true, type: 'identity-list', relation: '/gasorder_contract_product' }), f('contact_name', { required: true }), f('contact_phone', { required: true }), f('discount_amount'), f('remark')], 'list', [
|
||||
{ name: '分配/改派', resource: '/gasorder_basic/:identity/assign', fields: [relation('staff_account_identity', '/staff_account', true), ...reason], visibleFor: { field: 'order_status', values: [16, 18] } },
|
||||
{ name: '回收任务', resource: '/gasorder_basic/:identity/reclaim', danger: true, fields: reason, visibleFor: { field: 'order_status', values: [18] } },
|
||||
{ name: '调整金额', resource: '/gasorder_basic/:identity/adjust-amount', fields: [f('delivery_fee', { required: true }), f('discount_amount', { required: true }), ...reason], visibleFor: { field: 'order_status', values: [16, 18] } },
|
||||
{ name: '标记异常', resource: '/gasorder_basic/:identity/exception', fields: reason, visibleFor: { field: 'order_status', values: [18, 19, 20, 33, 34] } },
|
||||
{ name: '恢复订单', resource: '/gasorder_basic/:identity/recover', fields: reason, visibleFor: { field: 'order_status', values: [21] } },
|
||||
{ name: '取消订单', resource: '/gasorder_basic/:identity/cancel', danger: true, fields: reason, visibleFor: { field: 'order_status', values: [16, 18] } },
|
||||
]),
|
||||
define('wallet_basic', '钱包', 'readonly', []),
|
||||
define('wallet_bank', '银行卡', 'readonly', []),
|
||||
define('wallet_payment', '支付记录', 'readonly', []),
|
||||
define('wallet_record', '钱包流水', 'readonly', []),
|
||||
define('wallet_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 }), f('channel', { required: true }), f('remark')]),
|
||||
define('fin_settlement', '结算结果', 'readonly', []),
|
||||
];
|
||||
|
||||
const deliveryResourceNames = new Set(deliveryOverrides.map((item) => item.name));
|
||||
export const resources = deliveryOverrides.map((override) => {
|
||||
const source = platformResources.find((item) => item.name === override.name);
|
||||
return source ? { ...source, ...override } : override;
|
||||
}).filter((item) => deliveryResourceNames.has(item.name));
|
||||
|
||||
export const resourceByPath = Object.fromEntries(
|
||||
resources.map((definition) => [definition.resource, definition]),
|
||||
) as Record<string, ResourceUiDefinition>;
|
||||
|
||||
export function getResource(resourcePath: string): ResourceUiDefinition {
|
||||
const definition = resourceByPath[resourcePath];
|
||||
if (!definition) throw new Error(`未知资源:${resourcePath}`);
|
||||
return definition;
|
||||
}
|
||||
Reference in New Issue
Block a user