diff --git a/mobile-oa-h5/src/market/mobile-parity-ui.test.js b/mobile-oa-h5/src/market/mobile-parity-ui.test.js index 78cb790e..637917fe 100644 --- a/mobile-oa-h5/src/market/mobile-parity-ui.test.js +++ b/mobile-oa-h5/src/market/mobile-parity-ui.test.js @@ -84,7 +84,8 @@ test('protocol lists wire dual source tabs into listParams and create navigation assert.doesNotMatch(genericList, /bindNativeTabListeners/) assert.doesNotMatch(genericList, /installGlobalPointerProbe/) assert.doesNotMatch(genericList, / { + const genericList = source('../views/market/MarketModuleList.vue') + const sheet = source('./components/MarketFilterSheet.vue') + const styles = source('../styles/market.css') + + assert.match(genericList, / { + const presignList = source('../views/market/presign/PresignOrderList.vue') + const styles = source('../styles/market.css') + + assert.match(presignList, /v-model="orderStatus"/) + assert.match(presignList, /buildPresignListParams/) + assert.match(presignList, /orderStatusOptions/) + assert.match(presignList, /van-dropdown-menu/) + assert.doesNotMatch(presignList, /resetFilters/) + assert.doesNotMatch(presignList, />重置 { + const form = source('../views/market/MarketModuleForm.vue') + const styles = source('../styles/market.css') + assert.match(form, /v-model="sampleSource"/) + assert.match(form, /sampleOptionsPage/) + assert.match(form, /sampleOptionsRequestPending/) + assert.match(form, /loadMoreSampleOptions/) + assert.match(form, /pageSize:\s*SAMPLE_OPTION_PAGE_SIZE/) + assert.doesNotMatch(form, /page:\s*1,\s*pageSize:\s*100/) + assert.match(form, /确认关联样本/) + assert.match(form, /确认取消运费/) + assert.match(styles, /\.market-sample-option\s*\{[\s\S]*?min-height:\s*44px;/) + assert.match(styles, /\.market-sample-option\s*\{[\s\S]*?min-width:\s*0;/) + assert.match(styles, /\.market-sample-option \.van-checkbox__label\s*\{[\s\S]*?overflow-wrap:\s*anywhere;/) +}) + +test('protocol list exposes mobile batch selection and sequential authorized deletion', () => { + const genericList = source('../views/market/MarketModuleList.vue') + assert.match(genericList, /batchSelectionMode/) + assert.match(genericList, /selectedRecordIds/) + assert.match(genericList, /deleteMarketItemsSequentially/) + assert.match(genericList, /批量删除/) + assert.match(genericList, /未处理/) +}) + +test('market detail contains no protocol audit interaction', () => { + const detail = source('../views/market/MarketModuleDetail.vue') + assert.doesNotMatch(detail, /auditSheetVisible|submitProtocolAudit|审核协议单/) +}) + test('all ten market list entries keep cards inside the horizontal content area', () => { const styles = source('../styles/market.css') const routes = source('./routes.js') @@ -129,12 +190,42 @@ test('market create and edit bottom actions are centered', () => { assert.match(styles, /\.presign-form-bottom\s*\{[\s\S]*?justify-content:\s*center;/) }) +test('market retry and presign action targets are explicitly at least 44 by 44 pixels', () => { + const styles = source('../styles/market.css') + const orderCard = source('./components/PresignOrderCard.vue') + const transferCard = source('./components/PresignTransferCard.vue') + const presignList = source('../views/market/presign/PresignOrderList.vue') + const presignForm = source('../views/market/presign/PresignOrderForm.vue') + const genericList = source('../views/market/MarketModuleList.vue') + const genericForm = source('../views/market/MarketModuleForm.vue') + + assert.match(styles, /\.presign-order-card__actions \.van-button\s*\{[^}]*min-width:\s*(?:[4-9]\d|\d{3,})px;[^}]*min-height:\s*(?:[4-9]\d|\d{3,})px;/) + assert.match(styles, /\.presign-order-list__error \.van-button\s*\{[^}]*min-width:\s*(?:[4-9]\d|\d{3,})px;[^}]*min-height:\s*(?:[4-9]\d|\d{3,})px;/) + assert.match(styles, /\.presign-form-section__heading \.van-button\s*\{[^}]*min-width:\s*(?:[4-9]\d|\d{3,})px;[^}]*min-height:\s*(?:[4-9]\d|\d{3,})px;/) + assert.match(styles, /\.market-state \.van-button\s*\{[^}]*min-width:\s*(?:[4-9]\d|\d{3,})px;[^}]*min-height:\s*(?:[4-9]\d|\d{3,})px;/) + assert.match(transferCard, /\.presign-transfer-card__actions \.van-button\s*\{[^}]*min-width:\s*(?:[4-9]\d|\d{3,})px;[^}]*min-height:\s*(?:[4-9]\d|\d{3,})px;/) + + assert.doesNotMatch(orderCard, /]*size="small"/) + assert.doesNotMatch(presignForm, /presign-form-section__heading[\s\S]*?]*size="small"/) + assert.match(genericList, /class="market-state"[\s\S]*?]*>重新加载<\/van-button>/) + assert.doesNotMatch(genericList, /class="market-state"[\s\S]*?]*size="small"/) + assert.match(genericForm, /v-else-if="errorMessage" class="market-state"/) + assert.doesNotMatch(genericForm, /v-else-if="errorMessage"[\s\S]*?]*size="small"/) +}) + test('market attachments use the workflow thumbnail and file-icon contract', () => { const attachmentCards = source('./components/MarketAttachmentCards.vue') assert.match(attachmentCards, /normalizeMarketAttachments/) assert.match(attachmentCards, /downloadAttachmentFile/) assert.match(attachmentCards, /fetchMarketAttachmentDownloadUrl/) + assert.match(attachmentCards, /createAuthorizedImagePreviewResolver/) + assert.match(attachmentCards, /:src="authorizedImageUrl\(file\)"/) + assert.match(attachmentCards, /previewResolver\.resolve/) + assert.match(attachmentCards, /showImagePreview\(\{ images: \[authorizedUrl\]/) + assert.match(attachmentCards, /previewResolver\.dispose\(\)/) assert.match(attachmentCards, /showImagePreview/) assert.match(attachmentCards, /market-attachment-card__download-action/) assert.match(attachmentCards, /@click\.stop="downloadAttachment\(file\)"/) @@ -146,6 +237,8 @@ test('market attachments use the workflow thumbnail and file-icon contract', () assert.match(attachmentCards, /market-attachment-card__main[\s\S]*?overflow:\s*hidden/) assert.match(attachmentCards, /\.market-attachment-card__download-action\s*\{[\s\S]*?width:\s*44px;[\s\S]*?height:\s*44px;/) assert.match(attachmentCards, /file\.explicitName\s*\?\s*file\.name\s*:\s*undefined/) + assert.doesNotMatch(attachmentCards, /attachmentFullUrl/) + assert.doesNotMatch(attachmentCards, /:src="fullUrl\(/) assert.doesNotMatch(attachmentCards, /#10b981/i) for (const kind of ['pdf', 'xls', 'doc', 'zip', 'other']) { assert.match(attachmentCards, new RegExp(`ft-${kind}`)) @@ -199,7 +292,8 @@ test('payment detail presents every web field and nested transfer field with Chi transferAmount: '3000.00', transferTime: '2026-10-11 10:00:00', transferAttachments: 'proof.pdf', - remark: '首笔' + remark: '首笔', + financeRemark: '财务已核对' }] }] }) @@ -215,7 +309,7 @@ test('payment detail presents every web field and nested transfer field with Chi ) assert.deepEqual( sections[1].cards[0].cards[0].fields.map((field) => field.label), - ['转账方式', '转账金额', '转账时间', '转账凭证', '备注'] + ['转账方式', '转账金额', '转账时间', '转账凭证', '备注', '财务备注'] ) }) @@ -257,9 +351,9 @@ test('protocol cards expose the same business fields as the web list', async () }) assert.deepEqual(card.fields.map((field) => field.label), [ - '状态', '登记日期', '母亲姓名', '母亲电话', '母亲身份证号', '父亲姓名', + '状态', '协议编号', '登记日期', '母亲姓名', '母亲电话', '母亲身份证号', '父亲姓名', '新生儿性别', '分娩方式', '新生儿体重(克)', '采集地区', '采集医院', - '采集专员', '市场专员', '样本采集时间', '创建时间' + '采集专员', '市场专员', '样本采集时间', '样本编码', '创建时间' ]) assert.deepEqual(card.actions, ['view', 'edit', 'delete']) }) @@ -270,7 +364,10 @@ test('protocol, freight and presign details define explicit Chinese nested cards basic: { protocolNo: 'P-1', protocolStatus: 'sxz' }, customer: { motherName: '张三' }, samples: [{ id: 's1', sampleCode: 'S-1', sampleTypeName: '脐带血' }], - attachments: { protocolAttachments: 'a.pdf' } + attachments: { protocolAttachments: 'a.pdf' }, + storageCert: { makeStatus: '0', certImageUrls: 'cert.jpg' }, + fullmoonTodo: { handled: false, insuranceAttachments: 'ins.pdf', handleTime: '2026-10-01' }, + audits: [] }) const freight = getMarketModule('freight').detailSections({ freightNo: 'F-1', transportModeLabel: '冷链', freightAmount: '20', @@ -291,6 +388,15 @@ test('protocol, freight and presign details define explicit Chinese nested cards } } assert.ok(protocol.find((section) => section.key === 'samples').cards.length) + const storageCert = protocol.find((section) => section.key === 'storageCert') + const insurance = protocol.find((section) => section.key === 'insurance') + assert.ok(storageCert) + assert.ok(insurance) + assert.ok(storageCert.fields.some((field) => field.type === 'attachments' && field.label === '储存证图片')) + assert.ok(insurance.fields.some((field) => field.type === 'attachments' && field.label === '保险附件')) + assert.ok(insurance.fields.some((field) => field.label === '办理时间')) + assert.ok(protocol.find((section) => section.key === 'audits')) + assert.ok(protocol.find((section) => section.key === 'statusChange')) assert.ok(freight.find((section) => section.key === 'items').cards.length) assert.ok(presign.find((section) => section.key === 'transfers').cards.length) }) @@ -316,27 +422,69 @@ test('protocol create and edit forms expose every writable web business field', 'newbornIdCard', 'deliveryMode', 'birthOrder', 'newbornWeightGram', 'apgar1min', 'apgar5min', 'remark' ]) - assert.deepEqual(bySection.samples, [ - 'sampleTypeId', 'sampleCodeId', 'sampleCode', 'collectAmount', 'unitId', - 'price', 'originalPrice', 'storageYears', 'etaTime', 'isSpecial', 'specialRemark', 'remark' - ]) + assert.equal(bySection.samples, undefined) assert.deepEqual(bySection.attachments, [ 'protocolAttachments', 'consentFormUrls', 'healthSurveyFormUrls', 'attachmentUrls', 'infectiousDiseaseReportUrls' ]) const customer = module.formSections.find((section) => section.key === 'customer') - const samples = module.formSections.find((section) => section.key === 'samples') + assert.deepEqual( + customer.fields.find((field) => field.key === 'motherName'), + { key: 'motherName', label: '母亲姓名', type: 'customerSearch', role: 'mother', required: true } + ) + assert.deepEqual( + customer.fields.find((field) => field.key === 'fatherName'), + { key: 'fatherName', label: '父亲姓名', type: 'customerSearch', role: 'father' } + ) assert.deepEqual( customer.fields.find((field) => field.key === 'motherEthnic'), { key: 'motherEthnic', label: '母亲民族', type: 'select', optionKey: 'ethnicities' } ) - assert.deepEqual( - samples.fields.find((field) => field.key === 'sampleCodeId'), - { key: 'sampleCodeId', label: '样本编码记录', type: 'select', optionKey: 'sampleCodes' } - ) const form = source('../views/market/MarketModuleForm.vue') - assert.match(form, /field\.key === 'sampleCodeId'/) - assert.match(form, /sampleCode:\s*option\?\.sampleCode/) + assert.match(form, /ProtocolSampleTransportPanel/) + assert.match(form, /buildProtocolSamplesPayload/) + assert.match(form, /handleCustomerSelect/) + assert.match(form, /calcBirthYmFromIdCard/) + assert.match(form, /fetchMarketProtocolCustomers/) + assert.match(form, /protocolTotalFollowSamples/) + assert.match(form, /syncProtocolTotalFromSamples/) + assert.match(form, /sampleContractSum/) + const samplePanel = source('../market/components/ProtocolSampleTransportPanel.vue') + assert.match(samplePanel, /添加样本/) + assert.match(samplePanel, /配置运费/) + assert.match(samplePanel, /取消运费/) + assert.match(samplePanel, /buildFreightUnlinkCandidates/) + assert.match(samplePanel, /(共用)/) + assert.match(samplePanel, /样本状态/) + assert.match(samplePanel, /特殊说明/) + assert.match(samplePanel, /showPrice/) + assert.match(samplePanel, /autoGenerateCode/) + const payPanel = source('../market/components/ProtocolSamplePaymentPanel.vue') + assert.match(payPanel, /applyPreferentialPriceToPayment/) + assert.match(payPanel, /lastPreferentialByCode/) + assert.match(payPanel, /添加转账/) + assert.match(payPanel, /提交财务确认/) + assert.match(payPanel, /撤回/) + assert.match(payPanel, /protocolId/) + assert.match(payPanel, /canUseProtocolScheduleAction/) + assert.match(payPanel, /canUseProtocolTransferAction/) + assert.doesNotMatch(payPanel, /canManageTransfers/) + assert.match(payPanel, /performMarketAction|register-transfer/) + assert.match(payPanel, /module:\s*'payment'/) + assert.doesNotMatch(payPanel, /module:\s*'protocol'[\s\S]*uploadTransferFile/) + assert.match(form, /:protocol-id=/) + assert.doesNotMatch(form, /:can-manage-transfers="true"/) + assert.match(form, /presignDepositHint/) + assert.match(form, /depositTransfers/) + const protocolModule = source('../market/modules/protocol.js') + assert.match(protocolModule, /意向金(关联预签约)/) + assert.match(protocolModule, /储存证/) + assert.match(protocolModule, /审核记录/) + assert.match(protocolModule, /状态变更记录/) + assert.match(protocolModule, /运输方式/) + assert.match(protocolModule, /运费金额/) + assert.match(form, /mergeProtocolCustomerLinks/) + assert.match(form, /initLinkedCustomerNames/) }) test('presign delete action calls the delete API exactly once', () => {