From 321dd3ca43c344009431b2a2c1e8463ea1699764 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Wed, 14 Oct 2026 14:41:34 +0800 Subject: [PATCH] feat(mobile-oa-h5): collapse payment installment rows on list cards Render periodBlocks as independent expandable summary rows with footer totals after the period section. Co-authored-by: Cursor --- .../src/market/components/MarketListCard.vue | 131 +++++++- .../src/market/mobile-parity-ui.test.js | 98 +++++- mobile-oa-h5/src/styles/market.css | 314 +++++++++++++++++- 3 files changed, 529 insertions(+), 14 deletions(-) diff --git a/mobile-oa-h5/src/market/components/MarketListCard.vue b/mobile-oa-h5/src/market/components/MarketListCard.vue index f5ebb99a..3d35493a 100644 --- a/mobile-oa-h5/src/market/components/MarketListCard.vue +++ b/mobile-oa-h5/src/market/components/MarketListCard.vue @@ -33,6 +33,113 @@ +
+
+ +
+
+
+ {{ field.label }} + {{ field.value }} +
+
+
+
+
+ +
+
+ {{ field.label }} + {{ field.value }} + {{ field.value }} +
+
+ +
+ +
+
+
+ {{ block.title }} + {{ block.status }} +
+
+
+ {{ field.label }} + {{ field.value }} +
+
+
+
+
+
{{ value }}
@@ -50,7 +157,6 @@ size="small" :type="action === 'view' || action === 'view-payment-detail' ? 'primary' : action === 'delete' || action === 'unlink' ? 'danger' : 'default'" :plain="action !== 'view' && action !== 'view-payment-detail'" - :icon="actionIcon(action)" @click="$emit('action', action, item)" >{{ actionLabel(action) }} @@ -58,7 +164,7 @@ 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 99fc3a25..0da72a13 100644 --- a/mobile-oa-h5/src/market/mobile-parity-ui.test.js +++ b/mobile-oa-h5/src/market/mobile-parity-ui.test.js @@ -57,6 +57,13 @@ test('market list visual contract keeps search white, tags filled and actions ri assert.match(styles, /\.market-list__filters\s*\{[\s\S]*?background:\s*#fff;/) assert.match(styles, /\.presign-order-page__search\s*\{[\s\S]*?top:\s*0;/) assert.match(styles, /\.market-list-card__actions\s*\{[\s\S]*?justify-content:\s*flex-end;/) + assert.match(styles, /\.market-list-card__actions \.van-button\s*\{[\s\S]*?padding:\s*0 8px;/) + const listCard = source('./components/MarketListCard.vue') + assert.doesNotMatch(listCard, /#icon/) + assert.doesNotMatch(listCard, /actionIcon/) + const presignCard = source('./components/PresignOrderCard.vue') + assert.doesNotMatch(presignCard, /icon="edit"/) + assert.doesNotMatch(presignCard, /icon="delete-o"/) assert.match(styles, /\.presign-order-card__actions\s*\{[\s\S]*?justify-content:\s*flex-end;/) assert.match(styles, /\.market-tag\s*\{[\s\S]*?align-self:\s*flex-start;/) assert.match(styles, /\.market-tag\s*\{[\s\S]*?max-width:\s*128px;/) @@ -123,6 +130,86 @@ test('market lists expose a mobile filter sheet for web-equivalent conditions', assert.match(styles, /min-height:\s*44px/) }) +test('freight list uses inline dropdown filters like presign instead of filter sheet', () => { + const freight = source('./modules/freight.js') + const genericList = source('../views/market/MarketModuleList.vue') + const styles = source('../styles/market.css') + + assert.match(freight, /inlineDropdownFilters/) + assert.match(freight, /全部运输方式/) + assert.match(freight, /全部样本来源/) + assert.doesNotMatch(freight, /createTimeRange/) + assert.match(genericList, /inlineDropdownFilters/) + assert.match(genericList, /market-list__filter-row/) + assert.match(genericList, /van-dropdown-menu/) + assert.match(genericList, /applyInlineFilters/) + assert.match(styles, /\.market-list__filter-row\s*\{/) + assert.match(styles, /\.market-list__filter-row\s+\.van-dropdown-menu__bar/) +}) + +test('payment list uses inline dropdown filters like presign instead of filter sheet', () => { + const payment = source('./modules/payment.js') + const filters = source('./list-filters.js') + const genericList = source('../views/market/MarketModuleList.vue') + + assert.match(payment, /inlineDropdownFilters/) + assert.match(payment, /全部缴费方式/) + assert.match(payment, /全部缴费进度/) + assert.doesNotMatch(payment, /dueDateRange/) + assert.doesNotMatch(payment, /应缴费时间/) + assert.match(filters, /payment: Object\.freeze\(\{[\s\S]*?payMode: ''[\s\S]*?progressStatus: ''/) + assert.doesNotMatch(filters, /installmentDueDateStart/) + assert.match(genericList, /inlineDropdownFilters/) + assert.match(genericList, /market-list__filter-row/) +}) + +test('freight list and detail wire cascade delete with confirm', () => { + const freight = source('./modules/freight.js') + const list = source('../views/market/MarketModuleList.vue') + const detail = source('../views/market/MarketModuleDetail.vue') + const presenter = source('./list-presenter.js') + const api = source('../api/market.js') + + assert.match(freight, /'delete'/) + assert.match(presenter, /\['edit', 'delete', 'link', 'unlink'\]/) + assert.match(list, /deleteFreight/) + assert.match(list, /删除运费单/) + assert.match(detail, /async function deleteFreight/) + assert.match(api, /module === 'freight'[\s\S]*action === 'delete'/) +}) + +test('freight list cards collapse sample blocks by default and allow expand', () => { + const card = source('./components/MarketListCard.vue') + const presenter = source('./list-presenter.js') + const styles = source('../styles/market.css') + + assert.match(presenter, /sampleBlocks:\s*rows\.map\(freightSampleBlock\)/) + assert.match(card, /card\.sampleBlocks/) + assert.match(card, /samplesOpen/) + assert.match(card, /关联样本(/) + assert.match(card, /点击展开/) + assert.match(card, /点击收起/) + assert.match(styles, /\.market-list-card__samples\s*\{/) + assert.match(styles, /\.market-list-card__samples-toggle\s*\{[\s\S]*?min-height:\s*44px;/) +}) + +test('payment list cards collapse period blocks by default and allow independent expand', () => { + const card = source('./components/MarketListCard.vue') + const presenter = source('./list-presenter.js') + const styles = source('../styles/market.css') + + assert.match(presenter, /periodBlocks:\s*periods\.map\(paymentPeriodBlock\)/) + assert.match(presenter, /footerFields:/) + assert.match(card, /card\.periodBlocks/) + assert.match(card, /card\.footerFields/) + assert.match(card, /periodOpenKeys/) + assert.match(card, /aria-expanded/) + assert.match(card, /应缴费金额/) + assert.match(card, /已缴费金额/) + assert.match(styles, /\.market-list-card__periods\s*\{/) + assert.match(styles, /\.market-list-card__period-toggle\s*\{[\s\S]*?min-height:\s*44px;/) +}) + test('dedicated presign list wires orderStatus dropdown without reset on mobile', () => { const presignList = source('../views/market/presign/PresignOrderList.vue') const styles = source('../styles/market.css') @@ -140,15 +227,24 @@ test('dedicated presign list wires orderStatus dropdown without reset on mobile' test('freight sample selector supports source filters, server paging and confirmations', () => { const form = source('../views/market/MarketModuleForm.vue') + const freight = source('./modules/freight.js') + const formSection = source('./components/MarketFormSection.vue') const styles = source('../styles/market.css') assert.match(form, /v-model="sampleSource"/) + assert.match(form, /market-sample-toolbar__filter-row/) + assert.match(form, /shape="round"/) 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(form, /确认取消样本关联/) + assert.match(freight, /fieldsLayout:\s*'grid-2'/) + assert.match(formSection, /market-form-fields--grid-2/) + assert.match(styles, /\.market-form-fields--grid-2\s*\{/) + assert.match(styles, /\.market-sample-toolbar__filter-row\s*\{/) + assert.match(styles, /\.market-sample-toolbar__filter-row\s+\.van-dropdown-menu__bar/) 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;/) diff --git a/mobile-oa-h5/src/styles/market.css b/mobile-oa-h5/src/styles/market.css index c2326cfc..7e58bf17 100644 --- a/mobile-oa-h5/src/styles/market.css +++ b/mobile-oa-h5/src/styles/market.css @@ -92,6 +92,58 @@ gap: 8px; } +.market-list__filter-row { + display: flex; + align-items: center; + margin-top: 2px; + margin-left: -4px; +} + +.market-list__filter-row .van-dropdown-menu { + width: auto; + height: 40px; + background: transparent; +} + +.market-list__filter-row .van-dropdown-menu__bar { + height: 40px; + box-shadow: none; + background: transparent; +} + +.market-list__filter-row .van-dropdown-menu__item { + flex: none; + justify-content: flex-start; + padding: 0 10px; +} + +.market-list__filter-row .van-dropdown-menu__title { + padding: 0 14px 0 0; + font-size: 13px; + font-weight: 500; + color: var(--market-text, #1f2937); +} + +.market-list__filter-row .van-dropdown-menu__title::after { + top: 50%; + right: 0; + margin-top: 0; + border-color: transparent transparent currentColor currentColor; + opacity: 0.7; + transform: translateY(-50%) rotate(-45deg); +} + +.market-list__filter-row .van-dropdown-menu__title--down::after { + margin-top: 0; + transform: translateY(-50%) rotate(135deg); +} + +.market-list__filter-row .van-dropdown-menu__title--active, +.market-list__filter-row .van-dropdown-menu__title--down { + color: var(--market-primary, #0a9f6d); + font-weight: 600; +} + .market-list__filter-button { position: relative; display: inline-flex; @@ -445,6 +497,176 @@ body.van-toast--unclickable .presign-order-page * { font-weight: 700; } +.market-list-card__samples { + margin: 0 0 12px; + overflow: hidden; + border: 1px solid #eef4f1; + border-radius: 12px; + background: #fafcfb; +} + +.market-list-card__samples-toggle { + display: flex; + width: 100%; + min-height: 44px; + align-items: center; + gap: 8px; + padding: 10px 12px; + border: 0; + background: transparent; + color: var(--market-text); + font: inherit; + text-align: left; + cursor: pointer; +} + +.market-list-card__samples-toggle:active { + background: #f0f7f3; +} + +.market-list-card__samples-icon { + display: grid; + width: 22px; + height: 22px; + flex: 0 0 22px; + place-items: center; + border-radius: 6px; + background: var(--market-primary-soft); + color: var(--market-primary-dark); + font-size: 12px; + font-weight: 700; +} + +.market-list-card__samples-title { + flex: 1; + font-size: 13px; + font-weight: 600; +} + +.market-list-card__samples-meta { + color: var(--market-muted); + font-size: 12px; +} + +.market-list-card__samples-chevron { + color: var(--market-muted); + font-size: 14px; + transition: transform 0.2s ease; +} + +.market-list-card__samples.is-open .market-list-card__samples-chevron { + transform: rotate(180deg); +} + +.market-list-card__samples-body { + padding: 0 10px 10px; + border-top: 1px solid #eef4f1; +} + +.market-list-card__sample-block { + margin-top: 10px; + padding: 10px; + border: 1px solid #eef4f1; + border-radius: 10px; + background: #fff; +} + +.market-list-card__sample-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 8px; +} + +.market-list-card__sample-title { + min-width: 0; + overflow: hidden; + font-size: 13px; + font-weight: 600; + text-overflow: ellipsis; + white-space: nowrap; +} + +.market-list-card__fields--compact { + padding: 0; + gap: 10px 12px; +} + +.market-list-card__periods { + margin: 0 0 12px; + overflow: hidden; + border: 1px solid #eef4f1; + border-radius: 12px; + background: #fafcfb; +} + +.market-list-card__period + .market-list-card__period { + border-top: 1px solid #eef4f1; +} + +.market-list-card__period-toggle { + display: grid; + width: 100%; + min-height: 44px; + grid-template-columns: 64px 1fr 1fr 20px; + align-items: center; + gap: 8px; + padding: 10px 12px; + border: 0; + background: transparent; + color: var(--market-text); + font: inherit; + text-align: left; + cursor: pointer; +} + +.market-list-card__period-toggle:active { + background: #f0f7f3; +} + +.market-list-card__period-no { + font-size: 13px; + font-weight: 700; + color: var(--market-primary-dark); +} + +.market-list-card__period-col { + display: flex; + min-width: 0; + flex-direction: column; + gap: 2px; +} + +.market-list-card__period-col > span { + color: var(--market-muted); + font-size: 11px; +} + +.market-list-card__period-col > b { + overflow: hidden; + color: #c98312; + font-size: 13px; + font-weight: 700; + text-overflow: ellipsis; + white-space: nowrap; +} + +.market-list-card__period-chevron { + color: var(--market-muted); + font-size: 14px; + transition: transform 0.2s ease; +} + +.market-list-card__period.is-open .market-list-card__period-chevron { + transform: rotate(180deg); +} + +.market-list-card__period-body { + padding: 0 12px 12px; + border-top: 1px dashed #eef4f1; +} + .market-list-card__tags { display: flex; flex-wrap: wrap; @@ -469,15 +691,26 @@ body.van-toast--unclickable .presign-order-page * { display: flex; flex-wrap: wrap; justify-content: flex-end; - gap: 8px; - padding-top: 12px; + gap: 6px; + padding-top: 8px; border-top: 1px solid #eef4f1; } .market-list-card__actions .van-button { - min-width: 76px; - min-height: 44px; - border-radius: 9px; + min-width: 0; + min-height: 32px; + padding: 0 8px; + border-radius: 8px; + font-size: 12px; +} + +.market-list-card__actions .van-button .van-button__icon { + margin-right: 3px; + font-size: 14px; +} + +.market-list-card__actions .van-button .van-button__content { + gap: 3px; } .market-list__fab { @@ -632,6 +865,77 @@ body.van-toast--unclickable .presign-order-page * { font-size: 15px; } +.market-form-fields--grid-2 { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0 12px; +} + +.market-form-fields--grid-2 .market-form-field { + min-width: 0; +} + +.market-sample-toolbar { + margin: 0 0 12px; +} + +.market-sample-toolbar .van-search { + padding: 0; + background: transparent; +} + +.market-sample-toolbar__filter-row { + display: flex; + align-items: center; + margin-top: 2px; + margin-left: -4px; +} + +.market-sample-toolbar__filter-row .van-dropdown-menu { + width: auto; + height: 40px; + background: transparent; +} + +.market-sample-toolbar__filter-row .van-dropdown-menu__bar { + height: 40px; + box-shadow: none; + background: transparent; +} + +.market-sample-toolbar__filter-row .van-dropdown-menu__item { + flex: none; + justify-content: flex-start; + padding: 0 10px; +} + +.market-sample-toolbar__filter-row .van-dropdown-menu__title { + padding: 0 14px 0 0; + font-size: 13px; + font-weight: 500; + color: var(--market-text); +} + +.market-sample-toolbar__filter-row .van-dropdown-menu__title::after { + top: 50%; + right: 0; + margin-top: 0; + border-color: transparent transparent currentColor currentColor; + opacity: 0.7; + transform: translateY(-50%) rotate(-45deg); +} + +.market-sample-toolbar__filter-row .van-dropdown-menu__title--down::after { + margin-top: 0; + transform: translateY(-50%) rotate(135deg); +} + +.market-sample-toolbar__filter-row .van-dropdown-menu__title--active, +.market-sample-toolbar__filter-row .van-dropdown-menu__title--down { + color: var(--market-primary); + font-weight: 600; +} + .market-protocol-total-sync { margin: 0 0 8px; color: var(--market-muted);