From 0927a514d237fb838a6ade0d747dd3e48cf9bc13 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Sun, 11 Oct 2026 13:14:56 +0800 Subject: [PATCH] chore: add the mobile market contract dependency closure. --- mobile-oa-h5/src/market/module-registry.js | 18 +++ mobile-oa-h5/src/market/modules/freight.js | 21 ++++ .../src/market/modules/payment-chase.js | 19 +++ mobile-oa-h5/src/market/modules/payment.js | 28 +++++ mobile-oa-h5/src/market/modules/presign.js | 42 +++++++ mobile-oa-h5/src/market/modules/protocol.js | 78 +++++++++++++ mobile-oa-h5/src/market/modules/sample.js | 30 +++++ mobile-oa-h5/src/market/modules/shared.js | 13 +++ mobile-oa-h5/src/market/routes.js | 108 ++++++++++++++++++ 9 files changed, 357 insertions(+) create mode 100644 mobile-oa-h5/src/market/module-registry.js create mode 100644 mobile-oa-h5/src/market/modules/freight.js create mode 100644 mobile-oa-h5/src/market/modules/payment-chase.js create mode 100644 mobile-oa-h5/src/market/modules/payment.js create mode 100644 mobile-oa-h5/src/market/modules/presign.js create mode 100644 mobile-oa-h5/src/market/modules/protocol.js create mode 100644 mobile-oa-h5/src/market/modules/sample.js create mode 100644 mobile-oa-h5/src/market/modules/shared.js create mode 100644 mobile-oa-h5/src/market/routes.js diff --git a/mobile-oa-h5/src/market/module-registry.js b/mobile-oa-h5/src/market/module-registry.js new file mode 100644 index 00000000..ce4ee1aa --- /dev/null +++ b/mobile-oa-h5/src/market/module-registry.js @@ -0,0 +1,18 @@ +import freight from './modules/freight.js' +import paymentChase from './modules/payment-chase.js' +import payment from './modules/payment.js' +import presign from './modules/presign.js' +import protocol from './modules/protocol.js' +import sample from './modules/sample.js' + +const MODULES = Object.freeze({ presign, protocol, 'payment-chase': paymentChase, payment, sample, freight }) + +export function getMarketModule(module) { + const value = MODULES[module] + if (!value) throw new Error('市场页面配置无效') + return value +} + +export function getMarketListParams(module, filters) { + return getMarketModule(module).listParams?.(filters) || {} +} diff --git a/mobile-oa-h5/src/market/modules/freight.js b/mobile-oa-h5/src/market/modules/freight.js new file mode 100644 index 00000000..7dc8a8c2 --- /dev/null +++ b/mobile-oa-h5/src/market/modules/freight.js @@ -0,0 +1,21 @@ +export default { + key: 'freight', + resource: 'freights', + actions: { + specialist: ['view', 'create', 'edit', 'link', 'unlink'], + manager: ['view', 'create', 'edit', 'link', 'unlink'] + }, + listParams: ({ status } = {}) => ({ transportMode: status || '' }), + statusOptions: [{ text: '全部运输方式', value: '' }], + usesPagedSampleOptions(action) { + return ['create', 'link'].includes(action) + }, + sampleOptions(action, detailItems, optionRows) { + if (action === 'unlink') return Array.isArray(detailItems) ? detailItems : [] + return (Array.isArray(optionRows) ? optionRows : []).filter((item) => !item.freightId) + }, + formSections: [{ key: 'base', title: '运费批次', fields: [ + { key: 'transportMode', label: '运输方式', type: 'select', optionKey: 'transportModes', required: true }, + { key: 'freightAmount', label: '运费金额', type: 'number', required: true } + ] }] +} diff --git a/mobile-oa-h5/src/market/modules/payment-chase.js b/mobile-oa-h5/src/market/modules/payment-chase.js new file mode 100644 index 00000000..fd3439d2 --- /dev/null +++ b/mobile-oa-h5/src/market/modules/payment-chase.js @@ -0,0 +1,19 @@ +export default { + key: 'payment-chase', + resource: 'payment-chase', + readOnly: true, + actions: { specialist: ['view-payment'], manager: ['view-payment'] }, + listParams: ({ status } = {}) => ({ chaseType: status || '' }), + statusOptions: [ + { text: '全部催缴', value: '' }, + { text: '已逾期', value: 'overdue' }, + { text: '未来 30 天', value: 'upcoming' } + ], + detailTarget(item) { + return { + module: 'payment', + id: String(item?.samplePaymentId || ''), + query: item?.scheduleId ? { scheduleId: String(item.scheduleId) } : {} + } + } +} diff --git a/mobile-oa-h5/src/market/modules/payment.js b/mobile-oa-h5/src/market/modules/payment.js new file mode 100644 index 00000000..88b565df --- /dev/null +++ b/mobile-oa-h5/src/market/modules/payment.js @@ -0,0 +1,28 @@ +export default { + key: 'payment', + resource: 'payments', + actions: { + specialist: ['view', 'register-transfer', 'edit-transfer', 'submit', 'withdraw'], + manager: ['view', 'register-transfer', 'edit-transfer', 'submit', 'withdraw'] + }, + listParams: ({ status } = {}) => ({ payMode: status || '' }), + statusOptions: [{ text: '全部缴费模式', value: '' }], + initialForm(detail, target = {}) { + const transfer = (detail?.schedules || []) + .flatMap((row) => row.transfers || []) + .find((row) => String(row.id || '') === String(target.transferId || '')) || {} + const fields = ['transferAmount', 'transferTime', 'transferType', 'transferAttachments', 'remark'] + return { + transfer: Object.fromEntries(fields + .filter((key) => Object.prototype.hasOwnProperty.call(transfer, key)) + .map((key) => [key, transfer[key]])) + } + }, + transferFields: [ + { key: 'transferAmount', label: '转账金额', type: 'number', required: true }, + { key: 'transferTime', label: '转账时间', type: 'datetime' }, + { key: 'transferType', label: '转账方式', type: 'select', optionKey: 'transferTypes', required: true }, + { key: 'transferAttachments', label: '转账凭证', type: 'upload' }, + { key: 'remark', label: '备注', type: 'textarea' } + ] +} diff --git a/mobile-oa-h5/src/market/modules/presign.js b/mobile-oa-h5/src/market/modules/presign.js new file mode 100644 index 00000000..9032d8e3 --- /dev/null +++ b/mobile-oa-h5/src/market/modules/presign.js @@ -0,0 +1,42 @@ +import { commonFields } from './shared.js' + +export default { + key: 'presign', + resource: 'presign-orders', + actions: { + specialist: ['view', 'create', 'edit', 'convert'], + manager: ['view', 'create', 'edit', 'convert'] + }, + listParams: ({ status } = {}) => ({ orderStatus: status || '' }), + statusOptions: [ + { text: '全部状态', value: '' }, + { text: '预签约', value: 'yqy' }, + { text: '已转正式', value: 'yzs' } + ], + formSections: [ + { + key: 'base', title: '预签信息', fields: [ + { key: 'orderDate', label: '预签日期', type: 'date' }, + { key: 'protocolNo', label: '协议编号' }, + { key: 'motherName', label: '客户姓名', required: true }, + { key: 'expectedDeliveryDate', label: '预产期', type: 'date' }, + { key: 'regionId', label: '采集地区', type: 'select', optionKey: 'regions' }, + { key: 'hospitalId', label: '采集医院', type: 'select', optionKey: 'hospitals' }, + { key: 'sampleCollectTime', label: '采集时间', type: 'datetime' }, + { key: 'payMode', label: '缴费模式' }, + { key: 'sampleTypeIds', label: '样本类型', type: 'multi-select', optionKey: 'sampleTypes' }, + { key: 'contractAmount', label: '合同金额', type: 'number' }, + { key: 'remark', ...commonFields.remark } + ] + }, + { + key: 'transfers', title: '意向金', repeat: true, createOnly: true, fields: [ + { key: 'transferAmount', label: '转账金额', type: 'number' }, + { key: 'transferTime', label: '转账时间', type: 'datetime' }, + { key: 'transferType', label: '转账方式' }, + { key: 'transferAttachments', label: '转账附件', type: 'upload' }, + { key: 'remark', ...commonFields.remark } + ] + } + ] +} diff --git a/mobile-oa-h5/src/market/modules/protocol.js b/mobile-oa-h5/src/market/modules/protocol.js new file mode 100644 index 00000000..adecf6f4 --- /dev/null +++ b/mobile-oa-h5/src/market/modules/protocol.js @@ -0,0 +1,78 @@ +import { section } from './shared.js' + +const labels = { + protocolNo: '协议编号', donationNo: '捐赠编号', registerDate: '登记日期', + donationDate: '捐赠日期', motherName: '母亲姓名', motherPhone: '联系电话', + regionName: '采集地区', hospitalName: '采集医院', sampleCode: '样本编码', + payMode: '缴费模式', protocolAttachments: '协议附件' +} + +export default { + key: 'protocol', + resource: 'protocols', + actions: { specialist: ['view', 'create', 'edit'], manager: ['view', 'create', 'edit'] }, + listParams: ({ status } = {}) => ({ source: status || '' }), + statusOptions: [ + { text: '全部来源', value: '' }, + { text: '细胞存储', value: 'cell_storage' }, + { text: '科研捐赠', value: 'research_donation' } + ], + detailSections(detail) { + if (Array.isArray(detail?.sections)) return detail.sections + const values = [ + section('protocol', '协议信息', detail?.protocol || detail?.basic || detail, labels), + section('collection', '采集信息', detail?.collection, labels), + section('customer', '客户信息', detail?.customer, labels), + section('samples', '样本信息', { samples: detail?.samples }, labels) + ] + if (detail?.source === 'cell_storage' || detail?.payment) { + values.push(section('payment', '缴费信息', detail?.payment, labels)) + } + values.push(section('attachments', '附件信息', detail?.attachments, labels)) + return values + }, + formSections: [ + { key: 'protocol', title: '协议信息', fields: [ + { key: 'source', label: '业务来源', type: 'tabs', required: true, createOnly: true, options: [ + { text: '细胞存储', value: 'cell_storage' }, { text: '科研捐赠', value: 'research_donation' } + ] }, + { key: 'protocolNo', label: '协议编号', source: 'cell_storage' }, + { key: 'donationNo', label: '捐赠编号', source: 'research_donation' }, + { key: 'registerDate', label: '登记日期', type: 'date', source: 'cell_storage' }, + { key: 'donationDate', label: '捐赠日期', type: 'date', source: 'research_donation' }, + { key: 'protocolTotalAmount', label: '协议总金额', type: 'number', source: 'cell_storage' }, + { key: 'remark', label: '备注', type: 'textarea' } + ] }, + { key: 'collection', title: '采集信息', nested: true, fields: [ + { key: 'regionId', label: '采集地区', type: 'select', optionKey: 'regions' }, + { key: 'hospitalId', label: '采集医院', type: 'select', optionKey: 'hospitals' }, + { key: 'sampleCollectTime', label: '采集时间', type: 'datetime' } + ] }, + { key: 'customer', title: '客户信息', nested: true, fields: [ + { key: 'motherName', label: '母亲姓名', required: true }, + { key: 'motherPhone', label: '联系电话', type: 'tel' }, + { key: 'motherIdCard', label: '证件号码' }, + { key: 'expectedDeliveryDate', label: '预产期', type: 'date' } + ] }, + { key: 'samples', title: '样本信息', repeat: true, fields: [ + { key: 'sampleTypeId', label: '样本类型', type: 'select', optionKey: 'sampleTypes' }, + { key: 'sampleCode', label: '样本编码' }, + { key: 'storageYears', label: '存储年限', type: 'number', source: 'cell_storage' } + ] }, + { key: 'payment', title: '缴费信息(只读)', nested: true, readOnly: true, editOnly: true, source: 'cell_storage', fields: [ + { key: 'payMode', label: '缴费模式', readOnly: true }, + { key: 'contractAmount', label: '合同金额', readOnly: true }, + { key: 'remark', label: '缴费备注', readOnly: true } + ] }, + { key: 'attachments', title: '附件与确认', nested: true, fields: [ + { key: 'protocolAttachments', label: '协议附件', type: 'upload', source: 'cell_storage' }, + { key: 'consentFormUrls', label: '知情同意书', type: 'upload' }, + { key: 'healthSurveyFormUrls', label: '健康调查表', type: 'upload', source: 'cell_storage' }, + { key: 'attachmentUrls', label: '其他附件', type: 'upload', source: 'research_donation' }, + { key: 'infectiousDiseaseReportUrls', label: '传染病报告', type: 'upload', source: 'research_donation' } + ] }, + { key: 'confirm', title: '提交确认', nested: true, uiOnly: true, fields: [ + { key: 'confirmed', label: '我已核对以上信息', type: 'checkbox', required: true } + ] } + ] +} diff --git a/mobile-oa-h5/src/market/modules/sample.js b/mobile-oa-h5/src/market/modules/sample.js new file mode 100644 index 00000000..57019487 --- /dev/null +++ b/mobile-oa-h5/src/market/modules/sample.js @@ -0,0 +1,30 @@ +export default { + key: 'sample', + resource: 'samples', + readOnlySources: ['research_donation'], + actions: { specialist: ['view', 'storage-cert', 'insurance'], manager: ['view', 'storage-cert', 'insurance'] }, + listParams: ({ status } = {}) => ({ status: status || '' }), + statusOptions: [{ text: '全部状态', value: '' }], + formOptionsParams(sampleId) { + return { sampleId: String(sampleId || '').trim() } + }, + initialForm(detail, action) { + const handles = (detail?.sections || []).find((section) => section.key === 'market-handles') || {} + const handle = action === 'insurance' ? handles.insurance || {} : handles.storageCert || {} + const fields = ['handleTime', 'handlerUserId', 'attachments', 'remark'] + return { + handle: Object.fromEntries(fields + .filter((key) => Object.prototype.hasOwnProperty.call(handle, key)) + .map((key) => [key, handle[key]])) + } + }, + detailSections(detail) { + return Array.isArray(detail?.sections) ? detail.sections : [] + }, + handleFields: [ + { key: 'handleTime', label: '办理时间', type: 'datetime' }, + { key: 'handlerUserId', label: '办理人员', type: 'select', optionKey: 'users' }, + { key: 'attachments', label: '附件', type: 'upload' }, + { key: 'remark', label: '备注', type: 'textarea' } + ] +} diff --git a/mobile-oa-h5/src/market/modules/shared.js b/mobile-oa-h5/src/market/modules/shared.js new file mode 100644 index 00000000..4f1b8e6e --- /dev/null +++ b/mobile-oa-h5/src/market/modules/shared.js @@ -0,0 +1,13 @@ +export function objectFields(source, labels = {}) { + return Object.entries(source || {}) + .filter(([, value]) => value != null && typeof value !== 'object') + .map(([key, value]) => ({ label: labels[key] || key, value })) +} + +export function section(key, title, value, labels) { + return { key, title, fields: objectFields(value, labels), raw: value } +} + +export const commonFields = { + remark: { label: '备注', type: 'textarea' } +} diff --git a/mobile-oa-h5/src/market/routes.js b/mobile-oa-h5/src/market/routes.js new file mode 100644 index 00000000..919cdc9e --- /dev/null +++ b/mobile-oa-h5/src/market/routes.js @@ -0,0 +1,108 @@ +export const MARKET_MODULES = [ + 'presign', + 'protocol', + 'payment-chase', + 'payment', + 'sample', + 'freight' +] + +export const MARKET_ROLES = ['specialist', 'manager'] + +export const MARKET_MODULE_TITLES = Object.freeze({ + presign: '预签约订单', + protocol: '协议单', + 'payment-chase': '催缴信息', + payment: '缴费信息', + sample: '样本信息', + freight: '运费信息' +}) + +export const MARKET_ROLE_TITLES = Object.freeze({ + specialist: '市场专员', + manager: '地区经理' +}) + +function isMarketRole(value) { + return MARKET_ROLES.includes(value) +} + +function isMarketModule(value) { + return MARKET_MODULES.includes(value) +} + +export function getMarketRouteContext(route) { + const marketRole = route?.meta?.marketRole + const module = route?.meta?.module + if (!isMarketRole(marketRole) || !isMarketModule(module)) { + throw new Error('市场页面配置无效') + } + return { marketRole, module } +} + +export function shouldLoadMarketListAfterDetail({ previousDetailId, detailId }) { + return Boolean(String(previousDetailId || '').trim()) && !String(detailId || '').trim() +} + +export function pickMarketNavigationQuery(query) { + const returnPath = String(query?.returnPath || '').trim() + return returnPath.startsWith('/pages/') && !returnPath.startsWith('//') + ? { returnPath } + : {} +} + +export function marketRecordNavigationQuery(module, item, query) { + const result = pickMarketNavigationQuery(query) + const source = String(item?.source || '').trim() + if (module === 'protocol' && ['cell_storage', 'research_donation'].includes(source)) { + result.source = source + } + return result +} + +export function resolveMarketSavedDetailId(module, currentId, result, action) { + const current = String(currentId || '').trim() + if (module === 'freight' && action === 'unlink' && result?.disappeared === true) return '' + if (module === 'payment' && current) return current + return String(result?.id || current).trim() +} + +const listComponent = () => import('../views/market/MarketModuleList.vue') +const detailComponent = () => import('../views/market/MarketModuleDetail.vue') +const formComponent = () => import('../views/market/MarketModuleForm.vue') + +export const marketRoutes = MARKET_ROLES.flatMap((marketRole) => + MARKET_MODULES.map((module) => { + const meta = { + marketRole, + module, + title: MARKET_MODULE_TITLES[module] + } + return { + path: `/market/${marketRole}/${module}`, + name: `market-${marketRole}-${module}`, + component: listComponent, + meta, + children: [ + { + path: 'new', + name: `market-${marketRole}-${module}-new`, + component: formComponent, + meta: { ...meta, formMode: 'create' } + }, + { + path: ':id/edit', + name: `market-${marketRole}-${module}-edit`, + component: formComponent, + meta: { ...meta, formMode: 'edit' } + }, + { + path: ':id', + name: `market-${marketRole}-${module}-detail`, + component: detailComponent, + meta: { ...meta, detail: true } + } + ] + } + }) +)