fix(mobile-oa-h5): skin market neutral tags as barley soft.
Map Element Plus info tones through Vant default and style them as 浅底大麦黄 so unpaid/pending tags stay visible and distinct from solid semantic tags. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
be2226e4ca
commit
19dffbaa49
13 files changed
+598
-33
No files matched your search
@@ -0,0 +1,460 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>市场部 · 中性 Tag 样式对比</title>
|
||||
<style>
|
||||
:root {
|
||||
--brand: #0a9f6d;
|
||||
--brand-dark: #087f57;
|
||||
--brand-soft: #e9f9f2;
|
||||
--barley: #d6a343;
|
||||
--barley-soft: #fff7e8;
|
||||
--text: #18332a;
|
||||
--muted: #6b7f77;
|
||||
--border: #dce9e4;
|
||||
--surface: #fff;
|
||||
--page: #f4f8f6;
|
||||
--danger: #ef4444;
|
||||
--danger-soft: #fef2f2;
|
||||
--success: #10b981;
|
||||
--vant-gray: #969799;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "PingFang SC", "Noto Sans SC", system-ui, sans-serif;
|
||||
color: var(--text);
|
||||
background:
|
||||
radial-gradient(900px 360px at 0% -10%, #d1fae5 0%, transparent 55%),
|
||||
radial-gradient(700px 320px at 100% 0%, #ecfdf5 0%, transparent 50%),
|
||||
var(--page);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.wrap { max-width: 980px; margin: 0 auto; padding: 24px 16px 72px; }
|
||||
h1 { margin: 0 0 8px; font-size: 24px; letter-spacing: -0.02em; }
|
||||
.lead { margin: 0 0 20px; color: var(--muted); max-width: 720px; }
|
||||
.note {
|
||||
margin: 0 0 24px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
}
|
||||
.note strong { color: var(--text); }
|
||||
.section { margin-bottom: 28px; }
|
||||
.section h2 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.section h2 .pill {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
background: var(--brand-soft);
|
||||
color: var(--brand-dark);
|
||||
}
|
||||
.card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
background: var(--surface);
|
||||
box-shadow: 0 6px 20px rgb(8 127 87 / 7%);
|
||||
padding: 14px;
|
||||
}
|
||||
.card + .card { margin-top: 12px; }
|
||||
.card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #eef4f1;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.idx {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
background: var(--brand-soft);
|
||||
color: var(--brand-dark);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
flex: 0 0 30px;
|
||||
}
|
||||
.id-block { flex: 1; min-width: 0; }
|
||||
.id-block strong { display: block; font-size: 16px; }
|
||||
.id-block small { color: var(--muted); font-size: 12px; }
|
||||
.row-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.hint {
|
||||
margin: 8px 0 0;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
}
|
||||
/* —— shared capsule —— */
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
max-width: 128px;
|
||||
padding: 5px 12px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
/* existing filled tones (keep as-is) */
|
||||
.tag--primary { background: var(--brand); color: #fff; }
|
||||
.tag--success { background: var(--success); color: #fff; }
|
||||
.tag--warning { background: #ff976a; color: #fff; }
|
||||
.tag--danger { background: var(--danger); color: #fff; }
|
||||
|
||||
/* CURRENT problem: Vant default */
|
||||
.tag--current-default {
|
||||
background: var(--vant-gray);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Option A: soft slate (Element-like info light) */
|
||||
.tag--opt-a {
|
||||
background: #f1f5f9;
|
||||
color: #475569;
|
||||
border-color: #e2e8f0;
|
||||
}
|
||||
|
||||
/* Option B: soft mint-muted (brand family, not solid primary) */
|
||||
.tag--opt-b {
|
||||
background: #eef7f3;
|
||||
color: #3d5c52;
|
||||
border-color: #d5e8df;
|
||||
}
|
||||
|
||||
/* Option C: outline / plain neutral */
|
||||
.tag--opt-c {
|
||||
background: transparent;
|
||||
color: #5b6f67;
|
||||
border-color: #c5d4cd;
|
||||
}
|
||||
|
||||
/* Option D: barley soft (warm pending, distinct from warning orange fill) */
|
||||
.tag--opt-d {
|
||||
background: var(--barley-soft);
|
||||
color: #9a7020;
|
||||
border-color: #f0e0b8;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
@media (min-width: 720px) {
|
||||
.options { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
.option {
|
||||
position: relative;
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 16px;
|
||||
background: #fff;
|
||||
padding: 14px;
|
||||
cursor: pointer;
|
||||
transition: border-color .15s, box-shadow .15s;
|
||||
text-align: left;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
width: 100%;
|
||||
}
|
||||
.option:hover { border-color: #b7d4c8; }
|
||||
.option.is-selected {
|
||||
border-color: var(--brand);
|
||||
box-shadow: 0 0 0 3px var(--brand-soft);
|
||||
}
|
||||
.option .opt-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
.option .opt-title span {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--brand-dark);
|
||||
background: var(--brand-soft);
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
.option .opt-desc {
|
||||
margin: 8px 0 0;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
}
|
||||
.compare-existing {
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px dashed var(--border);
|
||||
}
|
||||
.compare-existing .label {
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.web-admin {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
@media (min-width: 720px) {
|
||||
.web-admin { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
.el-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
padding: 0 9px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.el-tag--info {
|
||||
background: #f4f4f5;
|
||||
border-color: #e9e9eb;
|
||||
color: #909399;
|
||||
}
|
||||
.el-tag--success {
|
||||
background: #f0f9eb;
|
||||
border-color: #e1f3d8;
|
||||
color: #67c23a;
|
||||
}
|
||||
.el-tag--warning {
|
||||
background: #fdf6ec;
|
||||
border-color: #faecd8;
|
||||
color: #e6a23c;
|
||||
}
|
||||
.el-tag--primary {
|
||||
background: #ecf5ff;
|
||||
border-color: #d9ecff;
|
||||
color: #409eff;
|
||||
}
|
||||
.el-tag--danger {
|
||||
background: #fef0f0;
|
||||
border-color: #fde2e2;
|
||||
color: #f56c6c;
|
||||
}
|
||||
.pick-bar {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
margin: 28px -16px -24px;
|
||||
padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
|
||||
background: rgba(255,255,255,.92);
|
||||
backdrop-filter: blur(8px);
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.pick-bar b { color: var(--brand-dark); }
|
||||
.pick-bar button {
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
padding: 8px 16px;
|
||||
background: var(--brand);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pick-bar button:disabled {
|
||||
opacity: .45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<h1>市场部 · 中性 Tag 样式对比</h1>
|
||||
<p class="lead">
|
||||
上一版把 Element Plus 的 <code>info</code> 映射成了 Vant <code>default</code>(灰底白字),可读但偏「硬」。
|
||||
请点选一种<strong>中性态</strong>样式;实心绿 / 橙 / 红等已有语义色保持不变。
|
||||
</p>
|
||||
|
||||
<div class="note">
|
||||
<strong>web-admin「存储服务」说明:</strong>
|
||||
Element Plus 默认 <code>el-tag type="info"</code> 是<strong>浅灰底 + 灰字</strong>(light),
|
||||
<strong>不是</strong>灰底白字,也<strong>不是</strong>无底白字。
|
||||
存储服务里大量「待缴费 / 待生效 / 草稿」走 <code>info</code>,视觉接近下方「Web 对照」区。
|
||||
</div>
|
||||
|
||||
<section class="section">
|
||||
<h2>现状问题 <span class="pill">Vant default</span></h2>
|
||||
<div class="card">
|
||||
<div class="card-head">
|
||||
<span class="idx">1</span>
|
||||
<span class="id-block">
|
||||
<strong>欧阳妮妮</strong>
|
||||
<small>ZKZX34000194</small>
|
||||
</span>
|
||||
<span class="tag tag--current-default">待缴费</span>
|
||||
</div>
|
||||
<div class="row-tags">
|
||||
<span class="tag tag--current-default">待缴费</span>
|
||||
<span class="tag tag--current-default">待生效</span>
|
||||
<span class="tag tag--current-default">草稿</span>
|
||||
<span class="tag tag--primary">部分已缴</span>
|
||||
<span class="tag tag--success">已缴清</span>
|
||||
<span class="tag tag--warning">催缴中</span>
|
||||
</div>
|
||||
<p class="hint">灰底白字对比度高,但和「语义实心色」挤在一起时显得脏、抢戏。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>方案选择 <span class="pill">点一张卡片</span></h2>
|
||||
<div class="options" id="options">
|
||||
<button type="button" class="option" data-choice="A" data-label="浅灰底深灰字">
|
||||
<div class="opt-title">方案 A · 浅灰底深灰字 <span>推荐贴近 Web</span></div>
|
||||
<div class="row-tags">
|
||||
<span class="tag tag--opt-a">待缴费</span>
|
||||
<span class="tag tag--opt-a">待生效</span>
|
||||
<span class="tag tag--opt-a">草稿</span>
|
||||
</div>
|
||||
<div class="compare-existing">
|
||||
<div class="label">同屏已有语义色(不变)</div>
|
||||
<div class="row-tags">
|
||||
<span class="tag tag--primary">部分已缴</span>
|
||||
<span class="tag tag--success">已缴清</span>
|
||||
<span class="tag tag--warning">催缴中</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="opt-desc">接近 Element Plus info light;中性、不抢绿/橙实心标。</p>
|
||||
</button>
|
||||
|
||||
<button type="button" class="option" data-choice="B" data-label="薄荷绿灰底深字">
|
||||
<div class="opt-title">方案 B · 薄荷绿灰底深字 <span>品牌系</span></div>
|
||||
<div class="row-tags">
|
||||
<span class="tag tag--opt-b">待缴费</span>
|
||||
<span class="tag tag--opt-b">待生效</span>
|
||||
<span class="tag tag--opt-b">草稿</span>
|
||||
</div>
|
||||
<div class="compare-existing">
|
||||
<div class="label">同屏已有语义色(不变)</div>
|
||||
<div class="row-tags">
|
||||
<span class="tag tag--primary">部分已缴</span>
|
||||
<span class="tag tag--success">已缴清</span>
|
||||
<span class="tag tag--warning">催缴中</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="opt-desc">偏三墙绿氛围,但仍是浅底深字,和实心 primary 区分明显。</p>
|
||||
</button>
|
||||
|
||||
<button type="button" class="option" data-choice="C" data-label="描边中性">
|
||||
<div class="opt-title">方案 C · 描边中性 <span>最轻</span></div>
|
||||
<div class="row-tags">
|
||||
<span class="tag tag--opt-c">待缴费</span>
|
||||
<span class="tag tag--opt-c">待生效</span>
|
||||
<span class="tag tag--opt-c">草稿</span>
|
||||
</div>
|
||||
<div class="compare-existing">
|
||||
<div class="label">同屏已有语义色(不变)</div>
|
||||
<div class="row-tags">
|
||||
<span class="tag tag--primary">部分已缴</span>
|
||||
<span class="tag tag--success">已缴清</span>
|
||||
<span class="tag tag--warning">催缴中</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="opt-desc">无填充描边;层级最低,适合「未开始」态,但在密集列表里可能偏淡。</p>
|
||||
</button>
|
||||
|
||||
<button type="button" class="option is-selected" data-choice="D" data-label="大麦黄浅底">
|
||||
<div class="opt-title">方案 D · 大麦黄浅底 <span>已采纳</span></div>
|
||||
<div class="row-tags">
|
||||
<span class="tag tag--opt-d">待缴费</span>
|
||||
<span class="tag tag--opt-d">待生效</span>
|
||||
<span class="tag tag--opt-d">草稿</span>
|
||||
</div>
|
||||
<div class="compare-existing">
|
||||
<div class="label">同屏已有语义色(不变)</div>
|
||||
<div class="row-tags">
|
||||
<span class="tag tag--primary">部分已缴</span>
|
||||
<span class="tag tag--success">已缴清</span>
|
||||
<span class="tag tag--warning">催缴中</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="opt-desc">用三墙大麦黄浅底表达「待处理」;需与 warning 实心橙区分(浅底 vs 实心)。</p>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>Web 对照 <span class="pill">Element Plus info</span></h2>
|
||||
<div class="web-admin">
|
||||
<div class="card">
|
||||
<div class="label" style="font-size:12px;color:var(--muted);margin-bottom:8px">存储服务常见 info(浅底深/灰字)</div>
|
||||
<div class="row-tags">
|
||||
<span class="el-tag el-tag--info">待缴费</span>
|
||||
<span class="el-tag el-tag--info">待生效</span>
|
||||
<span class="el-tag el-tag--info">草稿</span>
|
||||
<span class="el-tag el-tag--success">已缴清</span>
|
||||
<span class="el-tag el-tag--warning">部分已缴</span>
|
||||
</div>
|
||||
<p class="hint">无「无底白字」问题;默认也不是灰底白字。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="label" style="font-size:12px;color:var(--muted);margin-bottom:8px">若选方案 A,移动端中性态将更接近 Web</div>
|
||||
<div class="row-tags">
|
||||
<span class="tag tag--opt-a">待缴费</span>
|
||||
<span class="el-tag el-tag--info">待缴费</span>
|
||||
</div>
|
||||
<p class="hint">左:移动方案 A · 右:Web info light(圆角规范不同,色感接近)</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="pick-bar">
|
||||
<div>已选:<b id="picked">未选择</b></div>
|
||||
<button type="button" id="confirm" disabled>确认此方案</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const options = document.getElementById('options')
|
||||
const picked = document.getElementById('picked')
|
||||
const confirmBtn = document.getElementById('confirm')
|
||||
let current = null
|
||||
|
||||
options.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.option')
|
||||
if (!btn) return
|
||||
options.querySelectorAll('.option').forEach((el) => el.classList.remove('is-selected'))
|
||||
btn.classList.add('is-selected')
|
||||
current = { choice: btn.dataset.choice, label: btn.dataset.label }
|
||||
picked.textContent = '方案 ' + current.choice + ' · ' + current.label
|
||||
confirmBtn.disabled = false
|
||||
if (window.parent !== window) {
|
||||
window.parent.postMessage({ type: 'brainstorm-select', choice: current.choice, label: current.label }, '*')
|
||||
}
|
||||
})
|
||||
|
||||
confirmBtn.addEventListener('click', () => {
|
||||
if (!current) return
|
||||
alert('已记录偏好:方案 ' + current.choice + '(' + current.label + ')。请回到对话告诉助手你的选择。')
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
# 市场部移动端中性 Tag · 大麦黄浅底(方案 D)
|
||||
|
||||
## 背景
|
||||
|
||||
`info`(Element Plus 语义)映射为 Vant `default` 后,中性态呈现「灰底白字」,与实心语义色同屏时观感偏硬。用户选定方案 D:三墙大麦黄浅底。
|
||||
|
||||
## 决策
|
||||
|
||||
| 项 | 选择 |
|
||||
|----|------|
|
||||
| 方案 | D · 大麦黄浅底 |
|
||||
| 范围 | 仅 `mobile-oa-h5` 市场部(`.market-page` / `.presign-order-page`) |
|
||||
| 不改 | `primary` / `success` / `warning` / `danger` 实心色;web-admin |
|
||||
| 数据层 | 登记表继续 `tagType: 'info'`;展示层仍 `info` → Vant `default` |
|
||||
|
||||
## 视觉 Token
|
||||
|
||||
- 背景:`#fff7e8`(`--market` 大麦浅底,与规范 `#fff7e8` 一致)
|
||||
- 文字:`#9a7020`
|
||||
- 描边:`#f0e0b8`
|
||||
- 形状:沿用现有胶囊(`.market-tag` / round)
|
||||
|
||||
与 `warning` 区分:中性 = 浅底深字;warning = 实心橙 + 白字。
|
||||
|
||||
## 实现
|
||||
|
||||
在 `market.css` 覆盖:
|
||||
|
||||
```css
|
||||
.market-page .van-tag--default,
|
||||
.presign-order-page .van-tag--default { ... }
|
||||
```
|
||||
|
||||
不新增 tone 枚举;`resolveStatusTone('…', 'info')` 继续返回 `default`。
|
||||
|
||||
## 验收
|
||||
|
||||
- 缴费列表「待缴费」等中性态为大麦黄浅底,非灰底白字
|
||||
- 已缴清 / 部分已缴 / 催缴等实心色不变
|
||||
- 相关单测通过
|
||||
@@ -124,7 +124,7 @@
|
||||
<van-tag
|
||||
v-else-if="field.tag"
|
||||
class="market-tag"
|
||||
:type="field.tagType || resolveStatusTone(field.value)"
|
||||
:type="resolveStatusTone(field.value, field.tagType)"
|
||||
round
|
||||
:title="field.value"
|
||||
>
|
||||
|
||||
@@ -10,7 +10,13 @@
|
||||
:model-value="field.value"
|
||||
:editable="false"
|
||||
/>
|
||||
<van-tag v-else-if="field.tag" class="market-tag" :type="field.tagType || resolveStatusTone(field.value)" round :title="field.value">
|
||||
<van-tag
|
||||
v-else-if="field.tag"
|
||||
class="market-tag"
|
||||
:type="resolveStatusTone(field.value, field.tagType)"
|
||||
round
|
||||
:title="field.value"
|
||||
>
|
||||
{{ field.value }}
|
||||
</van-tag>
|
||||
<span v-else>{{ field.value }}</span>
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
<strong class="market-list-card__title">{{ card.title }}</strong>
|
||||
<small class="market-list-card__subtitle">{{ card.subtitle }}</small>
|
||||
</span>
|
||||
<van-tag v-if="card.status" class="market-tag" :type="statusTone" round :title="card.status">{{ card.status }}</van-tag>
|
||||
<van-tag v-if="card.status" class="market-tag" :type="statusTone" round :title="card.status">
|
||||
{{ card.status }}
|
||||
</van-tag>
|
||||
</header>
|
||||
|
||||
<div class="market-list-card__fields">
|
||||
@@ -28,7 +30,14 @@
|
||||
:class="{ 'is-wide': field.wide }"
|
||||
>
|
||||
<span>{{ field.label }}</span>
|
||||
<van-tag v-if="field.tag" class="market-tag" :type="field.tagType || resolveStatusTone(field.value)" size="medium" round :title="field.value">{{ field.value }}</van-tag>
|
||||
<van-tag
|
||||
v-if="field.tag"
|
||||
class="market-tag"
|
||||
:type="resolveStatusTone(field.value, field.tagType)"
|
||||
size="medium"
|
||||
round
|
||||
:title="field.value"
|
||||
>{{ field.value }}</van-tag>
|
||||
<b v-else :class="{ 'is-money': field.money }">{{ field.value }}</b>
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,7 +109,14 @@
|
||||
:class="{ 'is-wide': field.wide }"
|
||||
>
|
||||
<span>{{ field.label }}</span>
|
||||
<van-tag v-if="field.tag" class="market-tag" :type="field.tagType || resolveStatusTone(field.value)" size="medium" round :title="field.value">{{ field.value }}</van-tag>
|
||||
<van-tag
|
||||
v-if="field.tag"
|
||||
class="market-tag"
|
||||
:type="resolveStatusTone(field.value, field.tagType)"
|
||||
size="medium"
|
||||
round
|
||||
:title="field.value"
|
||||
>{{ field.value }}</van-tag>
|
||||
<b v-else :class="{ 'is-money': field.money }">{{ field.value }}</b>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,7 +130,14 @@
|
||||
:class="{ 'is-wide': field.wide }"
|
||||
>
|
||||
<span>{{ field.label }}</span>
|
||||
<van-tag v-if="field.tag" class="market-tag" :type="field.tagType || resolveStatusTone(field.value)" size="medium" round :title="field.value">{{ field.value }}</van-tag>
|
||||
<van-tag
|
||||
v-if="field.tag"
|
||||
class="market-tag"
|
||||
:type="resolveStatusTone(field.value, field.tagType)"
|
||||
size="medium"
|
||||
round
|
||||
:title="field.value"
|
||||
>{{ field.value }}</van-tag>
|
||||
<b v-else :class="{ 'is-money': field.money }">{{ field.value }}</b>
|
||||
</div>
|
||||
</div>
|
||||
@@ -151,7 +174,7 @@
|
||||
<van-tag
|
||||
v-if="block.status && block.status !== '—'"
|
||||
class="market-tag"
|
||||
:type="block.statusTone || resolveStatusTone(block.status)"
|
||||
:type="resolveStatusTone(block.status, block.statusTone)"
|
||||
size="medium"
|
||||
round
|
||||
:title="block.status"
|
||||
@@ -217,7 +240,9 @@ const FIELD_PREVIEW_COUNT = 4
|
||||
const FIELD_FOLD_MODULES = new Set(['protocol', 'payment-chase', 'payment'])
|
||||
|
||||
const card = computed(() => buildMarketCard(props.module, props.role, props.item))
|
||||
const statusTone = computed(() => card.value.statusTone || resolveStatusTone(card.value.status))
|
||||
const statusTone = computed(() =>
|
||||
resolveStatusTone(card.value.status, card.value.statusTone)
|
||||
)
|
||||
const allFields = computed(() => (Array.isArray(card.value.fields) ? card.value.fields : []))
|
||||
const footerFieldCount = computed(() =>
|
||||
Array.isArray(card.value.footerFields) ? card.value.footerFields.length : 0
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import { formatMoney } from '../money.js'
|
||||
import { resolveStatusTag, paymentScheduleStatus } from '../status-tag-registry.js'
|
||||
import { resolveStatusTone } from '../status-tone.js'
|
||||
import ScheduleTransferEditor from './ScheduleTransferEditor.vue'
|
||||
|
||||
const props = defineProps({
|
||||
@@ -78,7 +79,8 @@ function scheduleStatusLabel(schedule) {
|
||||
}
|
||||
|
||||
function scheduleStatusTone(schedule) {
|
||||
return resolveStatusTag(paymentScheduleStatus, schedule?.scheduleStatus).tagType
|
||||
const tag = resolveStatusTag(paymentScheduleStatus, schedule?.scheduleStatus)
|
||||
return resolveStatusTone(tag.label, tag.tagType)
|
||||
}
|
||||
|
||||
function onScheduleUpdate(index, nextSchedule) {
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
<strong class="presign-order-card__title">{{ card.title }}</strong>
|
||||
<small>母亲姓名</small>
|
||||
</span>
|
||||
<van-tag class="market-tag" :type="statusField.tone" round>{{ statusField.value }}</van-tag>
|
||||
<van-tag
|
||||
class="market-tag"
|
||||
:type="resolveStatusTone(statusField.value, statusField.tone)"
|
||||
round
|
||||
>{{ statusField.value }}</van-tag>
|
||||
<van-icon name="arrow" class="presign-order-card__arrow" />
|
||||
</button>
|
||||
|
||||
@@ -78,6 +82,7 @@ import {
|
||||
buildPresignCardModel,
|
||||
presignCardActionEvent
|
||||
} from '../presign/presign-list-model.js'
|
||||
import { resolveStatusTone } from '../status-tone.js'
|
||||
|
||||
/** 默认展示两行(4 项),其余折叠 */
|
||||
const FIELD_PREVIEW_COUNT = 4
|
||||
|
||||
@@ -108,6 +108,7 @@ import OaDatePicker from '../../oa/components/OaDatePicker.vue'
|
||||
import OaMoneyField from '../../oa/components/OaMoneyField.vue'
|
||||
import { removeMarketAttachmentAt } from '../attachment-upload-state.js'
|
||||
import { resolveStatusTag, payStatus } from '../status-tag-registry.js'
|
||||
import { resolveStatusTone } from '../status-tone.js'
|
||||
import PresignAttachmentUploader from './PresignAttachmentUploader.vue'
|
||||
import PresignSelectField from './PresignSelectField.vue'
|
||||
|
||||
@@ -144,7 +145,9 @@ const statusTag = computed(() =>
|
||||
resolveStatusTag(payStatus, props.modelValue.payStatus || 'dfj')
|
||||
)
|
||||
const statusLabel = computed(() => statusTag.value.label)
|
||||
const statusTone = computed(() => statusTag.value.tagType)
|
||||
const statusTone = computed(() =>
|
||||
resolveStatusTone(statusTag.value.label, statusTag.value.tagType)
|
||||
)
|
||||
|
||||
function can(action) {
|
||||
return !props.disabled && actionSet.value.has(action)
|
||||
|
||||
@@ -288,6 +288,7 @@ import {
|
||||
} from '../attachment-upload-policy.js'
|
||||
import MarketAttachmentCards from './MarketAttachmentCards.vue'
|
||||
import { resolveStatusTag, payStatus } from '../status-tag-registry.js'
|
||||
import { resolveStatusTone } from '../status-tone.js'
|
||||
|
||||
const props = defineProps({
|
||||
payment: { type: Object, default: () => ({}) },
|
||||
@@ -365,7 +366,8 @@ function statusLabel(value) {
|
||||
}
|
||||
|
||||
function statusTone(value) {
|
||||
return resolveStatusTag(payStatus, value).tagType
|
||||
const tag = resolveStatusTag(payStatus, value)
|
||||
return resolveStatusTone(tag.label, tag.tagType)
|
||||
}
|
||||
|
||||
function transferTypeLabel(value) {
|
||||
|
||||
@@ -73,6 +73,11 @@ test('market list visual contract keeps search white, tags filled and actions ri
|
||||
styles,
|
||||
/\.market-page \.van-tag,\s*\n\.presign-order-page \.van-tag\s*\{[\s\S]*?padding:\s*5px 12px;[\s\S]*?border-radius:\s*999px;/
|
||||
)
|
||||
// Neutral info→default: barley soft (not Vant gray-on-white)
|
||||
assert.match(
|
||||
styles,
|
||||
/\.market-page \.van-tag--default,\s*\n\.presign-order-page \.van-tag--default\s*\{[\s\S]*?background:\s*#fff7e8;[\s\S]*?color:\s*#9a7020;[\s\S]*?border:\s*1px solid #f0e0b8;/
|
||||
)
|
||||
assert.match(card, /class="market-tag"/)
|
||||
assert.doesNotMatch(card, /<van-tag[^>]*\bplain\b/)
|
||||
for (const component of [
|
||||
@@ -205,7 +210,8 @@ test('freight list cards collapse sample blocks by default and allow expand', ()
|
||||
|
||||
assert.match(presenter, /sampleBlocks:\s*rows\.map\(freightSampleBlock\)/)
|
||||
assert.match(card, /card\.sampleBlocks/)
|
||||
assert.match(card, /block\.statusTone \|\| resolveStatusTone\(block\.status\)/)
|
||||
assert.match(card, /resolveStatusTone\(block\.status, block\.statusTone\)/)
|
||||
assert.match(card, /resolveStatusTone\(card\.value\.status, card\.value\.statusTone\)/)
|
||||
assert.match(card, /samplesOpen/)
|
||||
assert.match(card, /关联样本(/)
|
||||
assert.match(card, /点击展开/)
|
||||
@@ -539,7 +545,7 @@ test('nested market detail cards use explicit labels instead of API property nam
|
||||
const detailSection = source('./components/MarketDetailSection.vue')
|
||||
|
||||
assert.match(detailSection, /cards:/)
|
||||
assert.match(detailSection, /field\.tagType \|\| resolveStatusTone\(field\.value\)/)
|
||||
assert.match(detailSection, /resolveStatusTone\(field\.value, field\.tagType\)/)
|
||||
assert.doesNotMatch(detailSection, /function flattenFields/)
|
||||
})
|
||||
|
||||
@@ -684,7 +690,7 @@ test('protocol create and edit forms expose every writable web business field',
|
||||
assert.equal(bySection.samples, undefined)
|
||||
assert.deepEqual(bySection.attachments, [
|
||||
'protocolAttachments', 'consentFormUrls', 'healthSurveyFormUrls',
|
||||
'attachmentUrls', 'infectiousDiseaseReportUrls'
|
||||
'infectiousDiseaseReportUrls', 'attachmentUrls'
|
||||
])
|
||||
const customer = module.formSections.find((section) => section.key === 'customer')
|
||||
assert.deepEqual(
|
||||
@@ -708,6 +714,9 @@ test('protocol create and edit forms expose every writable web business field',
|
||||
assert.match(form, /protocolTotalFollowSamples/)
|
||||
assert.match(form, /syncProtocolTotalFromSamples/)
|
||||
assert.match(form, /sampleContractSum/)
|
||||
assert.match(form, /refreshSampleCodes/)
|
||||
assert.match(form, /@refresh-sample-codes="refreshSampleCodes"/)
|
||||
assert.match(form, /codeFilter/)
|
||||
const samplePanel = source('../market/components/ProtocolSampleTransportPanel.vue')
|
||||
assert.match(samplePanel, /添加样本/)
|
||||
assert.match(samplePanel, /配置运费/)
|
||||
@@ -722,6 +731,9 @@ test('protocol create and edit forms expose every writable web business field',
|
||||
assert.match(samplePanel, /codeSelectOptions/)
|
||||
assert.match(samplePanel, /searchable/)
|
||||
assert.match(samplePanel, /market-form-field/)
|
||||
assert.match(samplePanel, /resolveSampleCodeFilter/)
|
||||
assert.match(samplePanel, /refresh-sample-codes/)
|
||||
assert.match(samplePanel, /sampleCodeFilter/)
|
||||
const payPanel = source('../market/components/ProtocolSamplePaymentPanel.vue')
|
||||
const transferEditor = source('../market/components/ScheduleTransferEditor.vue')
|
||||
assert.match(payPanel, /applyPreferentialPriceToPayment/)
|
||||
@@ -755,6 +767,10 @@ test('protocol create and edit forms expose every writable web business field',
|
||||
assert.doesNotMatch(form, /:can-manage-transfers="true"/)
|
||||
assert.match(form, /presignDepositHint/)
|
||||
assert.match(form, /depositTransfers/)
|
||||
assert.match(form, /PresignTransferCard/)
|
||||
assert.match(form, /showDepositSection/)
|
||||
assert.match(form, /<PresignTransferCard[\s\S]*disabled/)
|
||||
assert.doesNotMatch(form, /class="market-deposit-transfer"/)
|
||||
const protocolModule = source('../market/modules/protocol.js')
|
||||
assert.match(protocolModule, /意向金(关联预签约)/)
|
||||
assert.match(protocolModule, /审核记录/)
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
const VALID_TONES = new Set(['primary', 'success', 'warning', 'danger', 'info'])
|
||||
/** Vant Tag types only — no Element-Plus-style `info` (white text / no bg if used raw). */
|
||||
const VALID_TONES = new Set(['primary', 'success', 'warning', 'danger', 'default'])
|
||||
|
||||
export function resolveStatusTone(statusText, backendTone) {
|
||||
const preferred = String(backendTone || '').trim().toLowerCase()
|
||||
if (preferred === 'info') return 'default'
|
||||
if (VALID_TONES.has(preferred)) return preferred
|
||||
const status = String(statusText || '').trim().toLowerCase()
|
||||
if (/不通过|异常|拒绝|失败|逾期|作废|danger|error|reject/.test(status)) return 'danger'
|
||||
if (/待|未|部分|到期|过期|进行|warning|pending|processing/.test(status)) return 'warning'
|
||||
if (/完成|通过|正常|启用|生效|已缴清|已入库|已采集|已接收|已办理|success|completed/.test(status)) return 'success'
|
||||
if (/停用|默认|info/.test(status)) return 'info'
|
||||
if (/停用|默认|info/.test(status)) return 'default'
|
||||
return 'primary'
|
||||
}
|
||||
@@ -4,10 +4,18 @@ import test from 'node:test'
|
||||
import { resolveStatusTone } from './status-tone.js'
|
||||
|
||||
test('prefers a valid backend status tone', () => {
|
||||
assert.equal(resolveStatusTone('待处理', 'info'), 'info')
|
||||
assert.equal(resolveStatusTone('待处理', 'warning'), 'warning')
|
||||
assert.equal(resolveStatusTone('失败', 'not-a-tone'), 'danger')
|
||||
})
|
||||
|
||||
test('maps Element Plus info tone to Vant default so unpaid tags stay visible', () => {
|
||||
// Vant Tag has no `info`; bare van-tag--info is white text on transparent → invisible.
|
||||
// Market CSS then skins van-tag--default as barley soft (#fff7e8 / #9a7020).
|
||||
assert.equal(resolveStatusTone('待缴费', 'info'), 'default')
|
||||
assert.equal(resolveStatusTone('停用'), 'default')
|
||||
assert.equal(resolveStatusTone('', 'info'), 'default')
|
||||
})
|
||||
|
||||
test('matches negative status before positive substrings', () => {
|
||||
assert.equal(resolveStatusTone('不通过'), 'danger')
|
||||
assert.equal(resolveStatusTone('通过'), 'success')
|
||||
|
||||
@@ -1214,22 +1214,10 @@ body.van-toast--unclickable .presign-order-page * {
|
||||
border-color: #f0c989;
|
||||
}
|
||||
|
||||
.market-deposit-transfer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 12px;
|
||||
align-items: center;
|
||||
.market-deposit-transfer-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
margin-top: 10px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #e3f0ea;
|
||||
border-radius: 12px;
|
||||
background: #f7fffb;
|
||||
color: #18332a;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.market-deposit-transfer strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.market-form-field {
|
||||
@@ -1511,6 +1499,14 @@ body.van-toast--unclickable .presign-order-page * {
|
||||
background: var(--market-primary);
|
||||
}
|
||||
|
||||
/* 中性态(info→default):大麦黄浅底,区别于 Vant 灰底白字与 warning 实心橙 */
|
||||
.market-page .van-tag--default,
|
||||
.presign-order-page .van-tag--default {
|
||||
background: #fff7e8;
|
||||
color: #9a7020;
|
||||
border: 1px solid #f0e0b8;
|
||||
}
|
||||
|
||||
@media (min-width: 414px) {
|
||||
.market-page__body {
|
||||
padding-right: 18px;
|
||||
|
||||
Reference in new issue
Block a user