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[]; }; 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; }; }; const fieldLabels: Record = { 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: '审核时间', 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 { 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 })]; export const resources: 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: '气站账户' } }, 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: '配送点账户' } }, 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' }] })]), 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')]), 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 }, { name: '续签合同', resource: '/gasorder_contract/:identity/renew', fields: [f('effective_at', { required: true }), f('expired_at'), ...reason] }, { name: '终止合同', resource: '/gasorder_contract/:identity/terminate', danger: true, fields: reason }, ], { 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] }, { name: '开始罐装', resource: '/gasorder_basic/:identity/filling', fields: reason }, { name: '待配送', resource: '/gasorder_basic/:identity/ready', fields: reason }, { name: '开始配送', resource: '/gasorder_basic/:identity/delivering', fields: reason }, { name: '等待签收', resource: '/gasorder_basic/:identity/awaiting-confirmation', fields: reason }, { 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')] }, { name: '标记异常', resource: '/gasorder_basic/:identity/exception', fields: reason }, { name: '恢复订单', resource: '/gasorder_basic/:identity/recover', fields: reason }, { name: '取消订单', resource: '/gasorder_basic/:identity/cancel', danger: true, fields: reason }, ]), 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', [], 'list', [ { name: '审核通过', resource: '/wallet_apply_cash/:identity/approve', fields: reason }, { name: '审核驳回', resource: '/wallet_apply_cash/:identity/reject', danger: true, fields: reason }, ]), 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'), ]; export const resourceByPath = Object.fromEntries( resources.map((definition) => [definition.resource, definition]), ) as Record; export function getResource(resourcePath: string): ResourceUiDefinition { const definition = resourceByPath[resourcePath]; if (!definition) throw new Error(`未知资源:${resourcePath}`); return definition; }