From 8793fa07cc1d87cc4a9af9f0ffbd320701fe3fc7 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Mon, 10 Aug 2026 14:03:36 +0800 Subject: [PATCH] feat(mobile-h5): add styles, mocks, components, and demo pages Land Token-based SCSS modules, menu/biz mocks, reusable shell components, and home plus list/detail/form/comps template pages aligned with the Vant Weapp UI demo. Co-authored-by: Cursor --- mobile-h5/components/AttachList.vue | 75 ++++ mobile-h5/components/BizListCard.vue | 61 ++++ mobile-h5/components/FlowTimeline.vue | 44 +++ mobile-h5/components/MenuGrid.vue | 25 ++ mobile-h5/components/TodoPanel.vue | 61 ++++ mobile-h5/pages/demo/comps.vue | 113 ++++++ mobile-h5/pages/demo/detail.vue | 139 ++++++++ mobile-h5/pages/demo/form.vue | 164 +++++++++ mobile-h5/pages/demo/list.vue | 101 ++++++ mobile-h5/pages/index/index.vue | 100 ++++++ mobile-h5/styles/components/attach.scss | 186 +++++++++- mobile-h5/styles/components/detail.scss | 242 ++++++++++++- mobile-h5/styles/components/flow.scss | 143 +++++++- mobile-h5/styles/components/form-shell.scss | 117 +++++- mobile-h5/styles/components/list.scss | 375 +++++++++++++++++++- mobile-h5/styles/components/menu-grid.scss | 55 ++- mobile-h5/styles/components/todo.scss | 185 +++++++++- mobile-h5/utils/biz-mock.js | 262 ++++++++++++++ mobile-h5/utils/menu-mock.js | 19 + 19 files changed, 2460 insertions(+), 7 deletions(-) create mode 100644 mobile-h5/components/AttachList.vue create mode 100644 mobile-h5/components/BizListCard.vue create mode 100644 mobile-h5/components/FlowTimeline.vue create mode 100644 mobile-h5/components/MenuGrid.vue create mode 100644 mobile-h5/components/TodoPanel.vue create mode 100644 mobile-h5/pages/demo/comps.vue create mode 100644 mobile-h5/pages/demo/detail.vue create mode 100644 mobile-h5/pages/demo/form.vue create mode 100644 mobile-h5/pages/demo/list.vue create mode 100644 mobile-h5/pages/index/index.vue create mode 100644 mobile-h5/utils/biz-mock.js create mode 100644 mobile-h5/utils/menu-mock.js diff --git a/mobile-h5/components/AttachList.vue b/mobile-h5/components/AttachList.vue new file mode 100644 index 00000000..ae2f0ab7 --- /dev/null +++ b/mobile-h5/components/AttachList.vue @@ -0,0 +1,75 @@ + + + diff --git a/mobile-h5/components/BizListCard.vue b/mobile-h5/components/BizListCard.vue new file mode 100644 index 00000000..d43c686c --- /dev/null +++ b/mobile-h5/components/BizListCard.vue @@ -0,0 +1,61 @@ + + + diff --git a/mobile-h5/components/FlowTimeline.vue b/mobile-h5/components/FlowTimeline.vue new file mode 100644 index 00000000..261c4c11 --- /dev/null +++ b/mobile-h5/components/FlowTimeline.vue @@ -0,0 +1,44 @@ + + + diff --git a/mobile-h5/components/MenuGrid.vue b/mobile-h5/components/MenuGrid.vue new file mode 100644 index 00000000..fd9c4416 --- /dev/null +++ b/mobile-h5/components/MenuGrid.vue @@ -0,0 +1,25 @@ + + + diff --git a/mobile-h5/components/TodoPanel.vue b/mobile-h5/components/TodoPanel.vue new file mode 100644 index 00000000..a684e0b8 --- /dev/null +++ b/mobile-h5/components/TodoPanel.vue @@ -0,0 +1,61 @@ + + + diff --git a/mobile-h5/pages/demo/comps.vue b/mobile-h5/pages/demo/comps.vue new file mode 100644 index 00000000..2df26d55 --- /dev/null +++ b/mobile-h5/pages/demo/comps.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/mobile-h5/pages/demo/detail.vue b/mobile-h5/pages/demo/detail.vue new file mode 100644 index 00000000..9dd22f07 --- /dev/null +++ b/mobile-h5/pages/demo/detail.vue @@ -0,0 +1,139 @@ + + + diff --git a/mobile-h5/pages/demo/form.vue b/mobile-h5/pages/demo/form.vue new file mode 100644 index 00000000..df4b644a --- /dev/null +++ b/mobile-h5/pages/demo/form.vue @@ -0,0 +1,164 @@ + + + diff --git a/mobile-h5/pages/demo/list.vue b/mobile-h5/pages/demo/list.vue new file mode 100644 index 00000000..b67039fa --- /dev/null +++ b/mobile-h5/pages/demo/list.vue @@ -0,0 +1,101 @@ + + + diff --git a/mobile-h5/pages/index/index.vue b/mobile-h5/pages/index/index.vue new file mode 100644 index 00000000..0e457153 --- /dev/null +++ b/mobile-h5/pages/index/index.vue @@ -0,0 +1,100 @@ + + + diff --git a/mobile-h5/styles/components/attach.scss b/mobile-h5/styles/components/attach.scss index 710ddc7d..fed82544 100644 --- a/mobile-h5/styles/components/attach.scss +++ b/mobile-h5/styles/components/attach.scss @@ -1 +1,185 @@ -// Task 4: attachment styles (.d-attach-list, .d-attach-upload, .d-attach-card, file type classes) +.d-attach-list { + display: flex; + flex-direction: column; + gap: 20rpx; +} + +.d-attach-upload { + width: 100%; + border: 3rpx dashed #9ad9be; + border-radius: 28rpx; + background: linear-gradient(135deg, rgba(233, 249, 242, 0.95), rgba(255, 255, 255, 0.95)); + padding: 28rpx 24rpx; + display: flex; + align-items: center; + gap: 24rpx; +} + +.d-attach-upload:active { + border-color: #0a9f6d; +} + +.d-attach-upload .up-icon { + width: 88rpx; + height: 88rpx; + border-radius: 24rpx; + background: linear-gradient(145deg, #2ecf8f, #0a9f6d); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + font-size: 44rpx; + box-shadow: 0 12rpx 28rpx rgba(10, 159, 109, 0.28); + flex-shrink: 0; +} + +.d-attach-upload .up-text { + flex: 1; + min-width: 0; +} + +.d-attach-upload .up-title { + font-size: 26rpx; + font-weight: 700; + color: #087f57; +} + +.d-attach-upload .up-sub { + margin-top: 4rpx; + font-size: 22rpx; + color: #6b7b74; + line-height: 1.4; +} + +.d-attach-card { + display: flex; + align-items: center; + gap: 24rpx; + padding: 24rpx; + border-radius: 28rpx; + background: #fff; + border: 1rpx solid #e4eee9; + box-shadow: 0 8rpx 28rpx rgba(20, 55, 40, 0.05); + position: relative; + overflow: hidden; +} + +.d-attach-card::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 6rpx; + background: var(--file-accent, #0a9f6d); +} + +.d-attach-card.ft-pdf { + --file-accent: #d14343; +} +.d-attach-card.ft-img { + --file-accent: #0a9f6d; +} +.d-attach-card.ft-xls { + --file-accent: #189b65; +} +.d-attach-card.ft-doc { + --file-accent: #3b82f6; +} +.d-attach-card.ft-other { + --file-accent: #d6a343; +} + +.d-attach-card .file-icon { + width: 92rpx; + height: 92rpx; + border-radius: 26rpx; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 2rpx; + flex-shrink: 0; + color: #fff; + font-weight: 800; +} + +.d-attach-card.ft-pdf .file-icon { + background: linear-gradient(145deg, #f07171, #d14343); +} +.d-attach-card.ft-img .file-icon { + background: linear-gradient(145deg, #3dca96, #0a9f6d); +} +.d-attach-card.ft-xls .file-icon { + background: linear-gradient(145deg, #4fbf8a, #189b65); +} +.d-attach-card.ft-doc .file-icon { + background: linear-gradient(145deg, #6ea8ff, #3b82f6); +} +.d-attach-card.ft-other .file-icon { + background: linear-gradient(145deg, #e4bc6a, #d6a343); +} + +.d-attach-card .file-icon .ext { + font-size: 20rpx; + letter-spacing: 0.04em; + line-height: 1; + text-transform: uppercase; +} + +.d-attach-card .file-body { + flex: 1; + min-width: 0; +} + +.d-attach-card .file-name { + font-size: 26rpx; + font-weight: 650; + color: #173128; + line-height: 1.35; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.d-attach-card .file-meta { + margin-top: 8rpx; + display: flex; + flex-wrap: wrap; + gap: 16rpx; + font-size: 22rpx; + color: #8aa198; +} + +.d-attach-card .file-ops { + display: flex; + flex-direction: column; + gap: 12rpx; + flex-shrink: 0; +} + +.d-attach-card .file-ops .op-btn { + width: 64rpx; + height: 64rpx; + border-radius: 20rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; +} + +.d-attach-card .file-ops .btn-preview { + background: var(--brand-green-soft, #e9f9f2); + color: var(--brand-green-dark, #087f57); +} + +.d-attach-card .file-ops .btn-download { + background: #f0f5f3; + color: #4d635a; +} + +.d-attach-card .file-ops .icon-download { + width: 28rpx; + height: 28rpx; + display: block; +} diff --git a/mobile-h5/styles/components/detail.scss b/mobile-h5/styles/components/detail.scss index 7215d023..beb9211e 100644 --- a/mobile-h5/styles/components/detail.scss +++ b/mobile-h5/styles/components/detail.scss @@ -1 +1,241 @@ -// Task 4: detail page styles (.detail-page, .detail-hero, .detail-metric-dock, .detail-section, .detail-footer) +@import '../mixins.scss'; + +.detail-page { + min-height: 100vh; + background: + radial-gradient(760rpx 400rpx at 100% 8%, rgba(214, 163, 67, 0.14), transparent 55%), + linear-gradient(180deg, #e8f5ef 0%, #f4f7f6 42%, #f7f9f8 100%); + padding-bottom: 176rpx; +} + +.detail-hero { + position: relative; + overflow: hidden; + padding: 88rpx 28rpx 56rpx; + border-radius: 0 0 56rpx 56rpx; + color: #fff; +} + +.detail-hero.status-warning { + background: linear-gradient(155deg, #a67b20 0%, #d6a343 45%, #e8c06a 100%); +} +.detail-hero.status-primary { + @include hero-green; + border-radius: 0 0 56rpx 56rpx; +} +.detail-hero.status-danger { + background: linear-gradient(155deg, #9c2f2f 0%, #d14343 50%, #e36a6a 100%); +} +.detail-hero.status-success { + background: linear-gradient(155deg, #0f6b48 0%, #189b65 52%, #3bb883 100%); +} + +.detail-hero-body { + position: relative; + z-index: 1; + margin-top: 12rpx; +} + +.detail-hero .type-line { + display: flex; + align-items: center; + gap: 16rpx; + margin-bottom: 20rpx; +} + +.detail-hero .type-chip, +.detail-hero .status-chip { + font-size: 22rpx; + font-weight: 650; + padding: 6rpx 18rpx; + border-radius: 999rpx; +} + +.detail-hero .type-chip { + background: rgba(255, 255, 255, 0.2); + color: #fff; +} + +.detail-hero .status-chip { + background: #fff; + color: #173128; +} + +.detail-hero .hero-title { + font-size: 40rpx; + font-weight: 750; + line-height: 1.35; +} + +.detail-hero .code { + margin-top: 16rpx; + font-size: 24rpx; + opacity: 0.92; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; +} + +.detail-metric-dock { + margin: -36rpx 24rpx 0; + position: relative; + z-index: 3; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16rpx; + padding: 24rpx; + border-radius: 36rpx; + background: rgba(255, 255, 255, 0.94); + border: 1rpx solid rgba(220, 235, 228, 0.95); + box-shadow: 0 24rpx 60rpx rgba(15, 55, 40, 0.1); +} + +.detail-metric { + text-align: center; + padding: 8rpx 4rpx; +} + +.detail-metric .lab { + display: block; + font-size: 20rpx; + color: #8aa198; + margin-bottom: 8rpx; +} + +.detail-metric .val { + display: block; + font-size: 28rpx; + font-weight: 750; + color: #173128; + line-height: 1.25; + word-break: break-all; +} + +.detail-metric .val.money { + color: #9a7424; + font-variant-numeric: tabular-nums; +} + +.detail-body { + padding: 32rpx 24rpx 0; +} + +.detail-section { + @include surface-card; + border-radius: 36rpx; + padding: 28rpx 28rpx 24rpx; + margin-bottom: 24rpx; +} + +.detail-section .sec-hd { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24rpx; +} + +.detail-section .sec-hd .sec-title { + font-size: 28rpx; + font-weight: 700; + color: #173128; + display: flex; + align-items: center; + gap: 16rpx; +} + +.detail-section .sec-hd .sec-title::before { + content: ''; + width: 8rpx; + height: 28rpx; + border-radius: 999rpx; + background: linear-gradient(180deg, #12b37a, #087f57); +} + +.detail-section .sec-hd .sec-more { + font-size: 24rpx; + color: var(--brand-green, #0a9f6d); +} + +.kv-list { + display: flex; + flex-direction: column; + gap: 20rpx; +} + +.kv-row { + display: flex; + align-items: flex-start; + gap: 24rpx; + font-size: 26rpx; + line-height: 1.45; +} + +.kv-row .k { + width: 144rpx; + flex-shrink: 0; + color: #8aa198; +} + +.kv-row .v { + flex: 1; + color: #173128; + font-weight: 550; + word-break: break-all; +} + +.detail-note { + margin-top: 24rpx; + padding: 20rpx 24rpx; + border-radius: 24rpx; + background: linear-gradient(135deg, #fff8ec, #fffdf8); + border: 1rpx solid #f0e0b8; + color: #8a6520; + font-size: 24rpx; + line-height: 1.5; +} + +.pay-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 20rpx; + padding: 20rpx 24rpx; + border-radius: 24rpx; + background: #f7faf9; + margin-bottom: 16rpx; +} + +.pay-row .name { + font-size: 26rpx; + font-weight: 650; + color: #173128; +} + +.pay-row .sub { + margin-top: 4rpx; + font-size: 22rpx; + color: #8aa198; +} + +.pay-row .amt { + font-size: 28rpx; + font-weight: 750; + color: #9a7424; + font-variant-numeric: tabular-nums; + text-align: right; +} + +.detail-footer { + position: fixed; + left: 0; + right: 0; + bottom: 0; + display: flex; + gap: 20rpx; + padding: 24rpx 28rpx calc(24rpx + env(safe-area-inset-bottom)); + background: rgba(255, 255, 255, 0.96); + border-top: 1rpx solid var(--border, #dcebe4); + z-index: 20; +} + +.detail-footer .btn-flex { + flex: 1; +} diff --git a/mobile-h5/styles/components/flow.scss b/mobile-h5/styles/components/flow.scss index 0e801396..20652837 100644 --- a/mobile-h5/styles/components/flow.scss +++ b/mobile-h5/styles/components/flow.scss @@ -1 +1,142 @@ -// Task 4: flow timeline styles (.flow-list, .flow-item, .flow-dot, .flow-card) +.flow-list { + display: flex; + flex-direction: column; +} + +.flow-item { + position: relative; + display: flex; + gap: 24rpx; + padding-bottom: 28rpx; +} + +.flow-item:last-child { + padding-bottom: 0; +} + +.flow-item:not(:last-child) .flow-rail::after { + content: ''; + position: absolute; + left: 50%; + top: 72rpx; + bottom: -28rpx; + width: 4rpx; + transform: translateX(-50%); + background: linear-gradient(180deg, rgba(10, 159, 109, 0.45), rgba(220, 235, 228, 0.9)); + border-radius: 4rpx; +} + +.flow-rail { + position: relative; + width: 72rpx; + flex-shrink: 0; + display: flex; + justify-content: center; +} + +.flow-dot { + width: 72rpx; + height: 72rpx; + border-radius: 24rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + color: #fff; + box-shadow: 0 12rpx 28rpx rgba(8, 127, 87, 0.2); + position: relative; + z-index: 1; +} + +.flow-item.done .flow-dot { + background: linear-gradient(145deg, #2ecf8f, #0a9f6d); +} +.flow-item.warn .flow-dot { + background: linear-gradient(145deg, #ecc06a, #d6a343); + box-shadow: 0 12rpx 28rpx rgba(214, 163, 67, 0.28); +} +.flow-item.danger .flow-dot { + background: linear-gradient(145deg, #ef7a7a, #d14343); + box-shadow: 0 12rpx 28rpx rgba(209, 67, 67, 0.25); +} + +.flow-card { + flex: 1; + min-width: 0; + border-radius: 28rpx; + padding: 22rpx 24rpx; + background: linear-gradient(160deg, #f7fbf9 0%, #ffffff 55%); + border: 1rpx solid #e4eee9; + box-shadow: 0 8rpx 24rpx rgba(20, 55, 40, 0.04); +} + +.flow-item.warn .flow-card { + background: linear-gradient(160deg, #fff9ef 0%, #ffffff 60%); + border-color: #f0e0b8; +} +.flow-item.danger .flow-card { + background: linear-gradient(160deg, #fff4f4 0%, #ffffff 60%); + border-color: #f3d0d0; +} + +.flow-card .flow-top { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16rpx; + margin-bottom: 8rpx; +} + +.flow-card .flow-title { + font-size: 26rpx; + font-weight: 700; + color: #173128; + line-height: 1.35; +} + +.flow-card .flow-badge { + flex-shrink: 0; + font-size: 20rpx; + font-weight: 700; + padding: 4rpx 14rpx; + border-radius: 999rpx; +} + +.flow-item.done .flow-badge { + background: rgba(10, 159, 109, 0.12); + color: #087f57; +} +.flow-item.warn .flow-badge { + background: rgba(214, 163, 67, 0.16); + color: #8f6718; +} +.flow-item.danger .flow-badge { + background: rgba(209, 67, 67, 0.12); + color: #b13535; +} + +.flow-card .flow-desc { + font-size: 24rpx; + color: #5d746a; + line-height: 1.5; +} + +.flow-card .flow-meta { + margin-top: 16rpx; + display: flex; + flex-wrap: wrap; + gap: 16rpx; + align-items: center; +} + +.flow-card .flow-meta .chip { + display: inline-flex; + align-items: center; + gap: 8rpx; + font-size: 22rpx; + color: #6b7b74; + background: rgba(255, 255, 255, 0.9); + border: 1rpx solid #e8f0ec; + border-radius: 999rpx; + padding: 4rpx 16rpx; +} diff --git a/mobile-h5/styles/components/form-shell.scss b/mobile-h5/styles/components/form-shell.scss index 52ec05e1..d1be8665 100644 --- a/mobile-h5/styles/components/form-shell.scss +++ b/mobile-h5/styles/components/form-shell.scss @@ -1 +1,116 @@ -// Task 4: form shell styles (.form-page, .form-section-title, .form-footer) +@import '../mixins.scss'; + +.form-page { + min-height: 100vh; + background: var(--bg-page, #f5f8f7); + padding-bottom: 176rpx; +} + +.form-nav { + @include hero-green; + padding: 88rpx 16rpx 20rpx; +} + +.form-section-title { + margin: 28rpx 32rpx 16rpx; + font-size: 26rpx; + font-weight: 600; + color: var(--text-muted, #6b7b74); +} + +.form-tip { + margin: 16rpx 32rpx 0; + font-size: 22rpx; + color: var(--text-muted, #6b7b74); + line-height: 1.45; +} + +.form-footer { + position: fixed; + left: 0; + right: 0; + bottom: 0; + display: flex; + gap: 20rpx; + padding: 24rpx 28rpx calc(24rpx + env(safe-area-inset-bottom)); + background: rgba(255, 255, 255, 0.96); + border-top: 1rpx solid var(--border, #dcebe4); + z-index: 20; +} + +.form-footer .btn-flex { + flex: 1; +} + +.home-header { + @include hero-green; + padding: 108rpx 32rpx 56rpx; +} + +.home-header .brand { + font-size: 24rpx; + opacity: 0.88; + letter-spacing: 0.04em; + margin-bottom: 16rpx; +} + +.home-header .hello { + font-size: 44rpx; + font-weight: 700; +} + +.home-header .meta { + margin-top: 20rpx; + display: flex; + align-items: center; + gap: 16rpx; + font-size: 24rpx; + opacity: 0.92; +} + +.home-header .meta .dot { + width: 8rpx; + height: 8rpx; + border-radius: 50%; + background: rgba(255, 255, 255, 0.7); +} + +.home-body { + padding: 28rpx 24rpx 48rpx; +} + +.panel { + @include surface-card; + padding: 28rpx 24rpx; + margin-bottom: 24rpx; +} + +.panel-hd { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24rpx; + padding: 0 8rpx; +} + +.panel-hd .panel-title { + font-size: 30rpx; + font-weight: 650; + color: var(--text-primary, #1f2a26); +} + +.panel-hd .hint { + font-size: 22rpx; + color: var(--text-muted, #6b7b74); +} + +.perm-banner { + padding: 20rpx 24rpx; + border-radius: 24rpx; + background: var(--barley-yellow-soft, #fff7e8); + border: 1rpx solid #f0e0b8; + font-size: 24rpx; + color: #8a6520; + margin-bottom: 24rpx; + line-height: 1.45; +} diff --git a/mobile-h5/styles/components/list.scss b/mobile-h5/styles/components/list.scss index d1799715..27af28f0 100644 --- a/mobile-h5/styles/components/list.scss +++ b/mobile-h5/styles/components/list.scss @@ -1 +1,374 @@ -// Task 4: list page styles (.list-hero, .list-filter-dock, .seg-tabs, .biz-card, status variants) +@import '../mixins.scss'; + +.list-hero { + @include hero-green; + padding: 88rpx 28rpx 44rpx; +} + +.list-hero::after { + content: ''; + position: absolute; + width: 360rpx; + height: 360rpx; + right: -100rpx; + bottom: -140rpx; + border-radius: 50%; + background: rgba(255, 255, 255, 0.08); + pointer-events: none; +} + +.list-hero-copy { + position: relative; + z-index: 1; + margin: 8rpx 4rpx 28rpx; + color: #fff; +} + +.list-hero-copy .eyebrow { + font-size: 22rpx; + letter-spacing: 0.08em; + opacity: 0.82; + margin-bottom: 8rpx; +} + +.list-hero-copy .title { + font-size: 44rpx; + font-weight: 700; +} + +.list-hero-copy .subline { + margin-top: 12rpx; + font-size: 24rpx; + opacity: 0.88; + display: flex; + align-items: center; + gap: 16rpx; +} + +.list-hero-copy .subline .em { + font-style: normal; + font-weight: 700; + background: rgba(255, 255, 255, 0.18); + border-radius: 999rpx; + padding: 2rpx 16rpx; +} + +.list-search { + position: relative; + z-index: 1; +} + +.list-filter-dock { + margin: -28rpx 24rpx 0; + position: relative; + z-index: 3; + background: rgba(255, 255, 255, 0.92); + border: 1rpx solid rgba(220, 235, 228, 0.95); + border-radius: 36rpx; + padding: 20rpx; + box-shadow: 0 20rpx 56rpx rgba(15, 55, 40, 0.08); +} + +.seg-tabs { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 8rpx; + padding: 8rpx; + border-radius: 28rpx; + background: #eef6f2; +} + +.seg-tab { + border-radius: 22rpx; + padding: 16rpx 8rpx; + color: #6b7b74; + display: flex; + flex-direction: column; + align-items: center; + gap: 4rpx; + font-size: 24rpx; +} + +.seg-tab .count { + font-size: 22rpx; + font-weight: 700; + color: #9aada4; + font-variant-numeric: tabular-nums; +} + +.seg-tab.active { + background: #fff; + color: var(--brand-green-dark, #087f57); + font-weight: 650; + box-shadow: 0 8rpx 24rpx rgba(8, 127, 87, 0.14); +} + +.seg-tab.active .count { + color: var(--brand-green, #0a9f6d); +} + +.list-wrap { + padding: 32rpx 24rpx 0; +} + +.biz-card { + position: relative; + border-radius: 40rpx; + margin-bottom: 28rpx; + overflow: hidden; + box-shadow: 0 20rpx 56rpx rgba(20, 55, 40, 0.08); + isolation: isolate; +} + +.biz-card.status-warning { + background: linear-gradient(145deg, #fff9ef 0%, #ffffff 46%, #fffaf3 100%); +} +.biz-card.status-primary { + background: linear-gradient(145deg, #eaf8f1 0%, #ffffff 48%, #f3fbf7 100%); +} +.biz-card.status-danger { + background: linear-gradient(145deg, #fff1f1 0%, #ffffff 48%, #fff7f7 100%); +} +.biz-card.status-success { + background: linear-gradient(145deg, #eef8f3 0%, #ffffff 50%, #f6fbf8 100%); +} + +.biz-card .card-inner { + position: relative; + z-index: 1; + padding: 28rpx 28rpx 24rpx; +} + +.biz-card .row-top { + display: flex; + align-items: flex-start; + gap: 20rpx; + margin-bottom: 24rpx; +} + +.biz-card .type-icon { + width: 88rpx; + height: 88rpx; + border-radius: 28rpx; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + font-size: 44rpx; + color: #fff; +} + +.biz-card.status-warning .type-icon { + background: linear-gradient(145deg, #ffe8b8, #f6d089); + color: #7a5610; +} +.biz-card.status-primary .type-icon { + background: linear-gradient(145deg, #8fe0bf, #0a9f6d); +} +.biz-card.status-danger .type-icon { + background: linear-gradient(145deg, #ffb4b4, #e85a5a); +} +.biz-card.status-success .type-icon { + background: linear-gradient(145deg, #b8efd4, #189b65); +} + +.biz-card .title-wrap { + flex: 1; + min-width: 0; +} + +.biz-card .type-chip { + display: inline-flex; + font-size: 20rpx; + font-weight: 650; + letter-spacing: 0.04em; + padding: 4rpx 14rpx; + border-radius: 999rpx; + margin-bottom: 10rpx; +} + +.biz-card.status-warning .type-chip { + background: rgba(214, 163, 67, 0.16); + color: #8f6718; +} +.biz-card.status-primary .type-chip { + background: rgba(10, 159, 109, 0.12); + color: #087f57; +} +.biz-card.status-danger .type-chip { + background: rgba(209, 67, 67, 0.12); + color: #b13535; +} +.biz-card.status-success .type-chip { + background: rgba(24, 155, 101, 0.12); + color: #147a50; +} + +.biz-card .title { + font-size: 30rpx; + font-weight: 700; + line-height: 1.35; + color: #173128; +} + +.biz-card .status-pill { + flex-shrink: 0; + font-size: 22rpx; + font-weight: 700; + padding: 8rpx 18rpx; + border-radius: 999rpx; +} + +.biz-card.status-warning .status-pill { + background: #fff; + color: #a67b20; + box-shadow: 0 4rpx 16rpx rgba(214, 163, 67, 0.22); +} +.biz-card.status-primary .status-pill { + background: #0a9f6d; + color: #fff; +} +.biz-card.status-danger .status-pill { + background: #d14343; + color: #fff; +} +.biz-card.status-success .status-pill { + background: rgba(24, 155, 101, 0.12); + color: #147a50; +} + +.biz-card .body-row { + display: flex; + gap: 24rpx; + align-items: stretch; +} + +.biz-card .desc-box { + flex: 1; + margin: 0; + padding: 20rpx 22rpx; + border-radius: 24rpx; + background: rgba(255, 255, 255, 0.72); + border: 1rpx solid rgba(255, 255, 255, 0.9); + font-size: 24rpx; + color: #3f564c; + line-height: 1.5; +} + +.biz-card .side-metric { + width: 172rpx; + flex-shrink: 0; + border-radius: 24rpx; + padding: 16rpx; + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-end; + text-align: right; + background: rgba(255, 255, 255, 0.65); + border: 1rpx solid rgba(255, 255, 255, 0.95); +} + +.biz-card .side-metric .amt-label { + font-size: 20rpx; + color: #8aa198; +} + +.biz-card .side-metric .amt { + margin-top: 4rpx; + font-size: 30rpx; + font-weight: 800; + color: #173128; + font-variant-numeric: tabular-nums; +} + +.biz-card.status-warning .side-metric .amt { + color: #9a7424; +} +.biz-card.status-primary .side-metric .amt { + color: #087f57; +} +.biz-card.status-danger .side-metric .amt { + color: #b13535; +} + +.biz-card .side-metric .time { + margin-top: 12rpx; + font-size: 20rpx; + color: #8aa198; +} + +.biz-card .meta-row { + display: flex; + flex-wrap: wrap; + gap: 16rpx; + margin-top: 24rpx; +} + +.biz-card .meta-row .mi { + display: inline-flex; + align-items: center; + gap: 8rpx; + font-size: 22rpx; + color: #5d746a; + background: rgba(255, 255, 255, 0.7); + border-radius: 999rpx; + padding: 6rpx 16rpx; +} + +.biz-card .code-pill { + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 20rpx; +} + +.biz-card .footer { + margin-top: 24rpx; + padding-top: 20rpx; + border-top: 1rpx dashed rgba(140, 170, 155, 0.28); + display: flex; + justify-content: space-between; + align-items: center; + gap: 16rpx; +} + +.biz-card .footer .left-hint { + font-size: 22rpx; + color: #7f968c; + display: inline-flex; + align-items: center; + gap: 8rpx; +} + +.biz-card .footer .left-hint .dot { + width: 12rpx; + height: 12rpx; + border-radius: 50%; + background: currentColor; + opacity: 0.75; +} + +.biz-card.status-warning .footer .left-hint { + color: #a67b20; +} +.biz-card.status-primary .footer .left-hint { + color: #0a9f6d; +} +.biz-card.status-danger .footer .left-hint { + color: #d14343; +} +.biz-card.status-success .footer .left-hint { + color: #189b65; +} + +.biz-card .footer .ops { + display: flex; + gap: 16rpx; +} + +.list-end { + padding: 16rpx 0 56rpx; + text-align: center; + font-size: 22rpx; + color: #8aa198; + letter-spacing: 0.08em; +} diff --git a/mobile-h5/styles/components/menu-grid.scss b/mobile-h5/styles/components/menu-grid.scss index bae6d08c..d5f6a4f7 100644 --- a/mobile-h5/styles/components/menu-grid.scss +++ b/mobile-h5/styles/components/menu-grid.scss @@ -1 +1,54 @@ -// Task 4: menu-grid styles (.menu-grid, .menu-card, .menu-icon, .menu-title) +@import '../mixins.scss'; + +.menu-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 20rpx 12rpx; +} + +.menu-card { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + gap: 16rpx; + min-height: 168rpx; + padding: 20rpx 8rpx 12rpx; + border-radius: 24rpx; +} + +.menu-card:active { + background: var(--brand-green-soft, #e9f9f2); +} + +.menu-icon { + width: 96rpx; + height: 96rpx; + border-radius: 28rpx; + display: flex; + align-items: center; + justify-content: center; + background: var(--brand-green-soft, #e9f9f2); + color: var(--brand-green-dark, #087f57); + font-size: 48rpx; + box-shadow: inset 0 0 0 1rpx rgba(10, 159, 109, 0.08); +} + +.menu-icon.tone-yellow { + background: var(--barley-yellow-soft, #fff7e8); + color: #a67b20; +} + +.menu-icon.tone-slate { + background: #eef2f1; + color: #3d524a; +} + +.menu-title { + font-size: 24rpx; + line-height: 1.25; + color: var(--text-primary, #1f2a26); + text-align: center; + max-width: 144rpx; + word-break: break-all; +} diff --git a/mobile-h5/styles/components/todo.scss b/mobile-h5/styles/components/todo.scss index 9b30bb01..50d2ac24 100644 --- a/mobile-h5/styles/components/todo.scss +++ b/mobile-h5/styles/components/todo.scss @@ -1 +1,184 @@ -// Task 4: todo panel styles (.todo-panel, .todo-stats, .todo-stat, .todo-list, .todo-item) +.todo-panel { + padding: 28rpx 24rpx 24rpx; + background: linear-gradient(165deg, rgba(233, 249, 242, 0.95) 0%, #fff 42%), #fff; +} + +.todo-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16rpx; + margin-bottom: 24rpx; +} + +.todo-stat { + position: relative; + border-radius: 24rpx; + padding: 24rpx 20rpx 20rpx; + text-align: left; + background: #fff; + box-shadow: 0 4rpx 20rpx rgba(8, 127, 87, 0.06); + border: 1rpx solid rgba(220, 235, 228, 0.9); + overflow: hidden; +} + +.todo-stat::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 6rpx; + border-radius: 6rpx 0 0 6rpx; +} + +.todo-stat.tone-warn::before { + background: var(--warning, #cf8a19); +} +.todo-stat.tone-pay::before { + background: var(--barley-yellow, #d6a343); +} +.todo-stat.tone-danger::before { + background: var(--danger, #d14343); +} + +.todo-stat.tone-warn { + background: linear-gradient(180deg, #fffaf0 0%, #fff 70%); +} +.todo-stat.tone-pay { + background: linear-gradient(180deg, #fff8ec 0%, #fff 70%); +} +.todo-stat.tone-danger { + background: linear-gradient(180deg, #fff5f5 0%, #fff 70%); +} + +.todo-stat .num { + display: block; + font-size: 44rpx; + font-weight: 700; + line-height: 1.1; + color: var(--text-primary, #1f2a26); + font-variant-numeric: tabular-nums; +} + +.todo-stat.tone-warn .num { + color: #b77412; +} +.todo-stat.tone-pay .num { + color: #9a7424; +} +.todo-stat.tone-danger .num { + color: var(--danger, #d14343); +} + +.todo-stat .label { + display: block; + margin-top: 8rpx; + font-size: 22rpx; + color: var(--text-muted, #6b7b74); +} + +.todo-stat .stat-icon { + position: absolute; + right: 16rpx; + top: 16rpx; + width: 52rpx; + height: 52rpx; + border-radius: 16rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; +} + +.todo-stat.tone-warn .stat-icon { + background: rgba(207, 138, 25, 0.12); + color: var(--warning, #cf8a19); +} +.todo-stat.tone-pay .stat-icon { + background: rgba(214, 163, 67, 0.16); + color: #a67b20; +} +.todo-stat.tone-danger .stat-icon { + background: rgba(209, 67, 67, 0.12); + color: var(--danger, #d14343); +} + +.todo-list { + display: flex; + flex-direction: column; + gap: 16rpx; +} + +.todo-item { + display: flex; + align-items: center; + gap: 20rpx; + width: 100%; + padding: 20rpx; + border: 1rpx solid var(--border, #dcebe4); + border-radius: 24rpx; + background: #fff; +} + +.todo-item:active { + background: var(--brand-green-soft, #e9f9f2); + border-color: #b7e5d0; +} + +.todo-item .ti-icon { + width: 72rpx; + height: 72rpx; + border-radius: 20rpx; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + font-size: 36rpx; +} + +.todo-item .ti-icon.i-approve { + background: #fff3e0; + color: var(--warning, #cf8a19); +} +.todo-item .ti-icon.i-pay { + background: var(--barley-yellow-soft, #fff7e8); + color: #a67b20; +} +.todo-item .ti-icon.i-alert { + background: #fdecec; + color: var(--danger, #d14343); +} + +.todo-item .ti-body { + flex: 1; + min-width: 0; +} + +.todo-item .ti-title { + font-size: 26rpx; + font-weight: 600; + color: var(--text-primary, #1f2a26); + line-height: 1.3; +} + +.todo-item .ti-desc { + margin-top: 4rpx; + font-size: 22rpx; + color: var(--text-muted, #6b7b74); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.todo-item .ti-right { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 8rpx; + flex-shrink: 0; +} + +.todo-item .ti-time { + font-size: 20rpx; + color: #9aada4; +} diff --git a/mobile-h5/utils/biz-mock.js b/mobile-h5/utils/biz-mock.js new file mode 100644 index 00000000..2a2a0180 --- /dev/null +++ b/mobile-h5/utils/biz-mock.js @@ -0,0 +1,262 @@ +const listData = [ + { + id: 1, + title: '脐带血采集协议-王女士', + code: 'XY20260810001', + typeLabel: '采集协议', + icon: 'orders-o', + owner: '李四', + time: '08-10 09:20', + amount: '12,800', + progress: '', + desc: '分期缴费 · 待财务确认首期款项', + status: '待处理', + tagType: 'warning', + hint: '需尽快确认', + customer: '王女士', + dept: '样本运营部', + createdAt: '2026-08-08 14:20', + stepActive: 1, + steps: ['建档', '缴费确认', '样本采集', '入库'], + payments: [ + { + id: 'p1', + name: '第 1 期(首期)', + sub: '应缴 08-10 · 微信转账', + amount: '6,400', + status: '待确认', + tagType: 'warning' + }, + { + id: 'p2', + name: '第 2 期', + sub: '应缴 09-10 · 未到期', + amount: '6,400', + status: '待缴费', + tagType: 'primary' + } + ], + attachments: [ + { name: '协议扫描件.pdf', size: '2.4 MB', time: '08-08 14:18' }, + { name: '身份证明.jpg', size: '860 KB', time: '08-08 14:19' } + ], + logs: [ + { + title: '提交审批', + desc: '提交采集协议,等待财务确认首期', + time: '08-08 14:22', + tone: 'done', + actor: '李四', + icon: 'edit', + badge: '已完成' + }, + { + title: '流转至财务', + desc: '系统自动分配至财务确认岗', + time: '08-08 14:23', + tone: 'done', + actor: '系统', + icon: 'cluster-o', + badge: '已完成' + }, + { + title: '待确认缴费', + desc: '首期 ¥6,400 等待财务核对附件', + time: '08-10 09:20', + tone: 'warn', + actor: '财务岗', + icon: 'balance-list-o', + badge: '进行中' + } + ] + }, + { + id: 2, + title: '样本接收单-批次 A12', + code: 'YS20260809018', + typeLabel: '样本接收', + icon: 'logistics', + owner: '王五', + time: '08-09 16:40', + amount: '', + progress: '在途', + desc: '冷链在途 · 预计今日 18:00 前到达库房', + status: '进行中', + tagType: 'primary', + hint: '跟踪物流', + customer: '批次 A12', + dept: '仓储物流部', + createdAt: '2026-08-09 09:10', + stepActive: 2, + steps: ['发运', '在途', '到库签收', '入库'], + payments: [], + attachments: [{ name: '冷链温度记录.pdf', size: '1.1 MB', time: '08-09 11:28' }], + logs: [ + { + title: '创建接收单', + desc: '创建样本接收单并绑定批次 A12', + time: '08-09 09:10', + tone: 'done', + actor: '王五', + icon: 'orders-o', + badge: '已完成' + }, + { + title: '冷链发出', + desc: '承运方已取件,全程温控开启', + time: '08-09 11:30', + tone: 'done', + actor: '承运方', + icon: 'logistics', + badge: '已完成' + }, + { + title: '运输中', + desc: '当前位置:城东中转站', + time: '08-09 16:40', + tone: 'warn', + actor: '物流跟踪', + icon: 'location-o', + badge: '进行中' + } + ] + }, + { + id: 3, + title: '质检工单-QC-8821', + code: 'ZJ20260808007', + typeLabel: '质检工单', + icon: 'certificate', + owner: '赵六', + time: '08-08 11:05', + amount: '', + progress: '阻塞', + desc: '检测结论待录入,阻塞下游入库流程', + status: '异常', + tagType: 'danger', + hint: '优先处理', + customer: '样本 S-8821', + dept: '质检中心', + createdAt: '2026-08-07 16:40', + stepActive: 1, + steps: ['取样', '检测', '结论录入', '关闭'], + payments: [], + attachments: [{ name: '初检原始数据.xlsx', size: '428 KB', time: '08-08 10:48' }], + logs: [ + { + title: '工单创建', + desc: '系统根据接收单自动生成质检工单', + time: '08-07 16:40', + tone: 'done', + actor: '系统', + icon: 'add-o', + badge: '已完成' + }, + { + title: '检测完成', + desc: '完成实验操作,待录入结论', + time: '08-08 10:50', + tone: 'done', + actor: '赵六', + icon: 'certificate', + badge: '已完成' + }, + { + title: '异常挂起', + desc: '结论超时未录入,已阻塞入库', + time: '08-08 11:05', + tone: 'danger', + actor: '质控规则', + icon: 'warning-o', + badge: '异常' + } + ] + }, + { + id: 4, + title: '出库申请-库位 B-03', + code: 'CK20260807002', + typeLabel: '库位出库', + icon: 'send-gift-o', + owner: '张三', + time: '08-07 14:22', + amount: '', + progress: '完结', + desc: '复核已完成,可归档关闭', + status: '已完成', + tagType: 'success', + hint: '已完结', + customer: '库位 B-03', + dept: '仓储物流部', + createdAt: '2026-08-06 10:00', + stepActive: 3, + steps: ['申请', '复核', '出库', '归档'], + payments: [], + attachments: [{ name: '出库单签收.pdf', size: '980 KB', time: '08-07 14:18' }], + logs: [ + { + title: '提交申请', + desc: '发起出库申请,指定库位 B-03', + time: '08-06 10:00', + tone: 'done', + actor: '张三', + icon: 'edit', + badge: '已完成' + }, + { + title: '复核通过', + desc: '库管复核通过,允许出库', + time: '08-07 11:10', + tone: 'done', + actor: '库管', + icon: 'passed', + badge: '已完成' + }, + { + title: '出库完成', + desc: '实物已交接,单据归档', + time: '08-07 14:22', + tone: 'done', + actor: '张三', + icon: 'checked', + badge: '已完成' + } + ] + } +] + +export function getList() { + return listData.slice() +} + +export function getById(id) { + const n = Number(id) + return listData.find((r) => r.id === n) || listData[0] +} + +function rowsByTab(idx) { + if (idx === 1) return listData.filter((r) => r.status === '进行中') + if (idx === 2) return listData.filter((r) => r.status === '待处理') + if (idx === 3) return listData.filter((r) => r.status === '已完成') + return listData +} + +export function tabCount(idx) { + return rowsByTab(idx).length +} + +export function filterByTab(tabIndex, keyword) { + const kw = (keyword || '').trim() + let rows = rowsByTab(tabIndex) + if (!kw) return rows + return rows.filter( + (r) => r.title.includes(kw) || r.code.includes(kw) || r.owner.includes(kw) + ) +} + +export const LIST_TABS = [ + { key: 'all', label: '全部' }, + { key: 'doing', label: '进行中' }, + { key: 'pending', label: '待处理' }, + { key: 'done', label: '已完成' } +] diff --git a/mobile-h5/utils/menu-mock.js b/mobile-h5/utils/menu-mock.js new file mode 100644 index 00000000..c26a8662 --- /dev/null +++ b/mobile-h5/utils/menu-mock.js @@ -0,0 +1,19 @@ +/** 模拟「菜单管理 → 移动端菜单」授权结果 */ +export const ALL_MOBILE_MENUS = [ + { id: 'm1', title: '待办审批', icon: 'todo-list-o', path: '/pages/demo/list' }, + { id: 'm2', title: '采集协议', icon: 'orders-o', path: '/pages/demo/list' }, + { id: 'm3', title: '缴费确认', icon: 'balance-list-o', path: '/pages/demo/list' }, + { id: 'm4', title: '样本接收', icon: 'logistics', path: '/pages/demo/list' }, + { id: 'm5', title: '质检工单', icon: 'certificate', path: '/pages/demo/list' }, + { id: 'm6', title: '生产任务', icon: 'cluster-o', path: '/pages/demo/list' }, + { id: 'm7', title: '库位出库', icon: 'send-gift-o', path: '/pages/demo/list' }, + { id: 'm8', title: '组件示例', icon: 'apps-o', path: '/pages/demo/comps' } +] + +/** @param {string[]} grantedIds */ +export function filterMenusByPermission(grantedIds) { + const set = new Set(grantedIds || []) + return ALL_MOBILE_MENUS.filter((m) => set.has(m.id)) +} + +export const DEFAULT_GRANTED_IDS = ['m1', 'm2', 'm3', 'm4', 'm5', 'm7', 'm8']