feat(after-sale): 处理记录时间线改为「节点 + 状态 + 时间」同一行
原实现把状态与时间竖排在节点右侧的 88px 固定列里,状态文字一长就折行、 时间被挤到第三行,和右侧备注也对不齐。改为节点独占一列,状态与时间包在 head 里紧随其后,处理备注另起一行: - [节点][处理状态][时间] 一行,状态色仍按工单状态走 --as-accent - 竖线改挂到条目的 ::before(原来是 aside 的),top 从 12px 调到 14px, 与加了 4px 上边距的节点底部对齐,绳子依旧穿过每个节且末项不收口 - head 用 flex + baseline + gap:8px,状态过长时时间自然换行而不重叠 - 售后详细弹窗与订单详情弹窗共用同一套标记,两侧同步改 验证:tests/widget-system/ 462 项、tests/admin/ 76 项通过;用 node 分别抽取 两个 JS 里的时间线构造函数跑样例载荷,确认输出为 [节点][状态][时间] + 另起一行的备注;重建 public.css / public.min.css, diff 仅限时间线规则。未做浏览器视觉验收。
This commit is contained in:
+11
-12
@@ -6693,27 +6693,26 @@ body.aksrt-modal-runtime-open {
|
|||||||
.aksrt-after-sale-status.is-rejected,
|
.aksrt-after-sale-status.is-rejected,
|
||||||
.aksrt-after-sale-status.is-cancelled { color: var(--color-error, #ef4444); }
|
.aksrt-after-sale-status.is-cancelled { color: var(--color-error, #ef4444); }
|
||||||
|
|
||||||
/* 售后处理记录时间线:左列绳结状态条(节点 + 状态 + 时间),右列处理备注。
|
/* 售后处理记录时间线:[节点][处理状态][时间] 一行,处理备注另起一行并对齐状态文字。
|
||||||
售后详细弹窗与订单详情弹窗的「售后记录」共用同一套标记;竖线为 aside 的
|
售后详细弹窗与订单详情弹窗的「售后记录」共用同一套标记;竖线为条目的
|
||||||
::before,末项不收口,避免出现悬空的线段。 */
|
::before,末项不收口,避免出现悬空的线段。 */
|
||||||
.aksrt-after-sale-log { display: grid; gap: 6px; }
|
.aksrt-after-sale-log { display: grid; gap: 6px; }
|
||||||
.aksrt-after-sale-log__empty { color: var(--modal-muted); font-size: 12px; }
|
.aksrt-after-sale-log__empty { color: var(--modal-muted); font-size: 12px; }
|
||||||
.aksrt-after-sale-timeline { display: grid; margin: 0; padding: 0; list-style: none; }
|
.aksrt-after-sale-timeline { display: grid; margin: 0; padding: 0; list-style: none; }
|
||||||
.aksrt-after-sale-timeline__item { --as-accent: var(--modal-muted); display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: 10px; }
|
.aksrt-after-sale-timeline__item { --as-accent: var(--modal-muted); position: relative; display: grid; grid-template-columns: 10px minmax(0, 1fr); gap: 10px; padding: 0 0 14px; }
|
||||||
|
.aksrt-after-sale-timeline__item:last-child { padding-bottom: 0; }
|
||||||
.aksrt-after-sale-timeline__item.is-pending { --as-accent: var(--modal-warning); }
|
.aksrt-after-sale-timeline__item.is-pending { --as-accent: var(--modal-warning); }
|
||||||
.aksrt-after-sale-timeline__item.is-processing { --as-accent: var(--modal-primary); }
|
.aksrt-after-sale-timeline__item.is-processing { --as-accent: var(--modal-primary); }
|
||||||
.aksrt-after-sale-timeline__item.is-completed { --as-accent: var(--modal-success); }
|
.aksrt-after-sale-timeline__item.is-completed { --as-accent: var(--modal-success); }
|
||||||
.aksrt-after-sale-timeline__item.is-rejected,
|
.aksrt-after-sale-timeline__item.is-rejected,
|
||||||
.aksrt-after-sale-timeline__item.is-cancelled { --as-accent: var(--modal-danger, #ef4444); }
|
.aksrt-after-sale-timeline__item.is-cancelled { --as-accent: var(--modal-danger, #ef4444); }
|
||||||
.aksrt-after-sale-timeline__aside { position: relative; display: grid; gap: 2px; justify-items: start; padding: 0 0 14px; }
|
.aksrt-after-sale-timeline__item::before { content: ""; position: absolute; top: 14px; bottom: -2px; left: 4px; width: 2px; border-radius: 1px; background: color-mix(in srgb, var(--as-accent) 30%, var(--modal-line)); }
|
||||||
.aksrt-after-sale-timeline__item:last-child .aksrt-after-sale-timeline__aside { padding-bottom: 0; }
|
.aksrt-after-sale-timeline__item:last-child::before { display: none; }
|
||||||
.aksrt-after-sale-timeline__aside::before { content: ""; position: absolute; top: 12px; bottom: -2px; left: 4px; width: 2px; border-radius: 1px; background: color-mix(in srgb, var(--as-accent) 30%, var(--modal-line)); }
|
.aksrt-after-sale-timeline__knot { position: relative; z-index: 1; width: 10px; height: 10px; margin-top: 4px; border: 2px solid var(--as-accent); border-radius: 50%; background: var(--modal-surface, #fff); }
|
||||||
.aksrt-after-sale-timeline__item:last-child .aksrt-after-sale-timeline__aside::before { display: none; }
|
.aksrt-after-sale-timeline__body { display: grid; gap: 3px; min-width: 0; }
|
||||||
.aksrt-after-sale-timeline__knot { position: relative; z-index: 1; width: 10px; height: 10px; border: 2px solid var(--as-accent); border-radius: 50%; background: var(--modal-surface, #fff); }
|
.aksrt-after-sale-timeline__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
|
||||||
.aksrt-after-sale-timeline__status { color: var(--as-accent); font-size: 12px; font-weight: 700; line-height: 1.4; }
|
.aksrt-after-sale-timeline__status { color: var(--as-accent); font-size: 12px; font-weight: 700; line-height: 1.5; }
|
||||||
.aksrt-after-sale-timeline__time { color: var(--modal-muted); font-size: 11px; line-height: 1.4; font-variant-numeric: tabular-nums; }
|
.aksrt-after-sale-timeline__time { color: var(--modal-muted); font-size: 11px; line-height: 1.5; font-variant-numeric: tabular-nums; }
|
||||||
.aksrt-after-sale-timeline__main { min-width: 0; padding: 0 0 14px; }
|
|
||||||
.aksrt-after-sale-timeline__item:last-child .aksrt-after-sale-timeline__main { padding-bottom: 0; }
|
|
||||||
.aksrt-after-sale-timeline__note { margin: 0; color: var(--modal-ink); font-size: 12.5px; line-height: 1.65; word-break: break-word; }
|
.aksrt-after-sale-timeline__note { margin: 0; color: var(--modal-ink); font-size: 12.5px; line-height: 1.65; word-break: break-word; }
|
||||||
|
|
||||||
/* 状态行右侧单元格:状态徽章 + 「撤销售后」入口(仅待审核工单渲染)。 */
|
/* 状态行右侧单元格:状态徽章 + 「撤销售后」入口(仅待审核工单渲染)。 */
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -816,27 +816,26 @@ body.aksrt-modal-runtime-open {
|
|||||||
.aksrt-after-sale-status.is-rejected,
|
.aksrt-after-sale-status.is-rejected,
|
||||||
.aksrt-after-sale-status.is-cancelled { color: var(--color-error, #ef4444); }
|
.aksrt-after-sale-status.is-cancelled { color: var(--color-error, #ef4444); }
|
||||||
|
|
||||||
/* 售后处理记录时间线:左列绳结状态条(节点 + 状态 + 时间),右列处理备注。
|
/* 售后处理记录时间线:[节点][处理状态][时间] 一行,处理备注另起一行并对齐状态文字。
|
||||||
售后详细弹窗与订单详情弹窗的「售后记录」共用同一套标记;竖线为 aside 的
|
售后详细弹窗与订单详情弹窗的「售后记录」共用同一套标记;竖线为条目的
|
||||||
::before,末项不收口,避免出现悬空的线段。 */
|
::before,末项不收口,避免出现悬空的线段。 */
|
||||||
.aksrt-after-sale-log { display: grid; gap: 6px; }
|
.aksrt-after-sale-log { display: grid; gap: 6px; }
|
||||||
.aksrt-after-sale-log__empty { color: var(--modal-muted); font-size: 12px; }
|
.aksrt-after-sale-log__empty { color: var(--modal-muted); font-size: 12px; }
|
||||||
.aksrt-after-sale-timeline { display: grid; margin: 0; padding: 0; list-style: none; }
|
.aksrt-after-sale-timeline { display: grid; margin: 0; padding: 0; list-style: none; }
|
||||||
.aksrt-after-sale-timeline__item { --as-accent: var(--modal-muted); display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: 10px; }
|
.aksrt-after-sale-timeline__item { --as-accent: var(--modal-muted); position: relative; display: grid; grid-template-columns: 10px minmax(0, 1fr); gap: 10px; padding: 0 0 14px; }
|
||||||
|
.aksrt-after-sale-timeline__item:last-child { padding-bottom: 0; }
|
||||||
.aksrt-after-sale-timeline__item.is-pending { --as-accent: var(--modal-warning); }
|
.aksrt-after-sale-timeline__item.is-pending { --as-accent: var(--modal-warning); }
|
||||||
.aksrt-after-sale-timeline__item.is-processing { --as-accent: var(--modal-primary); }
|
.aksrt-after-sale-timeline__item.is-processing { --as-accent: var(--modal-primary); }
|
||||||
.aksrt-after-sale-timeline__item.is-completed { --as-accent: var(--modal-success); }
|
.aksrt-after-sale-timeline__item.is-completed { --as-accent: var(--modal-success); }
|
||||||
.aksrt-after-sale-timeline__item.is-rejected,
|
.aksrt-after-sale-timeline__item.is-rejected,
|
||||||
.aksrt-after-sale-timeline__item.is-cancelled { --as-accent: var(--modal-danger, #ef4444); }
|
.aksrt-after-sale-timeline__item.is-cancelled { --as-accent: var(--modal-danger, #ef4444); }
|
||||||
.aksrt-after-sale-timeline__aside { position: relative; display: grid; gap: 2px; justify-items: start; padding: 0 0 14px; }
|
.aksrt-after-sale-timeline__item::before { content: ""; position: absolute; top: 14px; bottom: -2px; left: 4px; width: 2px; border-radius: 1px; background: color-mix(in srgb, var(--as-accent) 30%, var(--modal-line)); }
|
||||||
.aksrt-after-sale-timeline__item:last-child .aksrt-after-sale-timeline__aside { padding-bottom: 0; }
|
.aksrt-after-sale-timeline__item:last-child::before { display: none; }
|
||||||
.aksrt-after-sale-timeline__aside::before { content: ""; position: absolute; top: 12px; bottom: -2px; left: 4px; width: 2px; border-radius: 1px; background: color-mix(in srgb, var(--as-accent) 30%, var(--modal-line)); }
|
.aksrt-after-sale-timeline__knot { position: relative; z-index: 1; width: 10px; height: 10px; margin-top: 4px; border: 2px solid var(--as-accent); border-radius: 50%; background: var(--modal-surface, #fff); }
|
||||||
.aksrt-after-sale-timeline__item:last-child .aksrt-after-sale-timeline__aside::before { display: none; }
|
.aksrt-after-sale-timeline__body { display: grid; gap: 3px; min-width: 0; }
|
||||||
.aksrt-after-sale-timeline__knot { position: relative; z-index: 1; width: 10px; height: 10px; border: 2px solid var(--as-accent); border-radius: 50%; background: var(--modal-surface, #fff); }
|
.aksrt-after-sale-timeline__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
|
||||||
.aksrt-after-sale-timeline__status { color: var(--as-accent); font-size: 12px; font-weight: 700; line-height: 1.4; }
|
.aksrt-after-sale-timeline__status { color: var(--as-accent); font-size: 12px; font-weight: 700; line-height: 1.5; }
|
||||||
.aksrt-after-sale-timeline__time { color: var(--modal-muted); font-size: 11px; line-height: 1.4; font-variant-numeric: tabular-nums; }
|
.aksrt-after-sale-timeline__time { color: var(--modal-muted); font-size: 11px; line-height: 1.5; font-variant-numeric: tabular-nums; }
|
||||||
.aksrt-after-sale-timeline__main { min-width: 0; padding: 0 0 14px; }
|
|
||||||
.aksrt-after-sale-timeline__item:last-child .aksrt-after-sale-timeline__main { padding-bottom: 0; }
|
|
||||||
.aksrt-after-sale-timeline__note { margin: 0; color: var(--modal-ink); font-size: 12.5px; line-height: 1.65; word-break: break-word; }
|
.aksrt-after-sale-timeline__note { margin: 0; color: var(--modal-ink); font-size: 12.5px; line-height: 1.65; word-break: break-word; }
|
||||||
|
|
||||||
/* 状态行右侧单元格:状态徽章 + 「撤销售后」入口(仅待审核工单渲染)。 */
|
/* 状态行右侧单元格:状态徽章 + 「撤销售后」入口(仅待审核工单渲染)。 */
|
||||||
|
|||||||
@@ -192,14 +192,17 @@
|
|||||||
var status = String((entry && entry.status) || '');
|
var status = String((entry && entry.status) || '');
|
||||||
var note = String((entry && entry.note) || '');
|
var note = String((entry && entry.note) || '');
|
||||||
html += '<li class="aksrt-after-sale-timeline__item is-' + escapeHtml(status) + '">';
|
html += '<li class="aksrt-after-sale-timeline__item is-' + escapeHtml(status) + '">';
|
||||||
html += '<div class="aksrt-after-sale-timeline__aside">' +
|
html += '<span class="aksrt-after-sale-timeline__knot"></span>';
|
||||||
'<span class="aksrt-after-sale-timeline__knot"></span>' +
|
html += '<div class="aksrt-after-sale-timeline__body">';
|
||||||
|
// [节点][处理状态][时间] 同一行,处理备注另起一行
|
||||||
|
html += '<div class="aksrt-after-sale-timeline__head">' +
|
||||||
'<span class="aksrt-after-sale-timeline__status">' + escapeHtml(handlingEntryTitle(entry)) + '</span>' +
|
'<span class="aksrt-after-sale-timeline__status">' + escapeHtml(handlingEntryTitle(entry)) + '</span>' +
|
||||||
'<time class="aksrt-after-sale-timeline__time">' + escapeHtml(String((entry && entry.created_at) || '')) + '</time>' +
|
'<time class="aksrt-after-sale-timeline__time">' + escapeHtml(String((entry && entry.created_at) || '')) + '</time>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
html += '<div class="aksrt-after-sale-timeline__main">' +
|
if (note !== '') {
|
||||||
(note !== '' ? '<p class="aksrt-after-sale-timeline__note">' + escapeHtml(note).replace(/\r\n|\r|\n/g, '<br>') + '</p>' : '') +
|
html += '<p class="aksrt-after-sale-timeline__note">' + escapeHtml(note).replace(/\r\n|\r|\n/g, '<br>') + '</p>';
|
||||||
'</div>';
|
}
|
||||||
|
html += '</div>';
|
||||||
html += '</li>';
|
html += '</li>';
|
||||||
});
|
});
|
||||||
html += '</ol></div>';
|
html += '</ol></div>';
|
||||||
|
|||||||
@@ -129,14 +129,17 @@
|
|||||||
var status = String((entry && entry.status) || "");
|
var status = String((entry && entry.status) || "");
|
||||||
var note = String((entry && entry.note) || "");
|
var note = String((entry && entry.note) || "");
|
||||||
html += '<li class="aksrt-after-sale-timeline__item is-' + escapeHtml(status) + '">';
|
html += '<li class="aksrt-after-sale-timeline__item is-' + escapeHtml(status) + '">';
|
||||||
html += '<div class="aksrt-after-sale-timeline__aside">' +
|
html += '<span class="aksrt-after-sale-timeline__knot"></span>';
|
||||||
'<span class="aksrt-after-sale-timeline__knot"></span>' +
|
html += '<div class="aksrt-after-sale-timeline__body">';
|
||||||
|
// [节点][处理状态][时间] 同一行,处理备注另起一行
|
||||||
|
html += '<div class="aksrt-after-sale-timeline__head">' +
|
||||||
'<span class="aksrt-after-sale-timeline__status">' + escapeHtml(handlingEntryTitle(entry)) + '</span>' +
|
'<span class="aksrt-after-sale-timeline__status">' + escapeHtml(handlingEntryTitle(entry)) + '</span>' +
|
||||||
'<time class="aksrt-after-sale-timeline__time">' + escapeHtml(String((entry && entry.created_at) || "")) + '</time>' +
|
'<time class="aksrt-after-sale-timeline__time">' + escapeHtml(String((entry && entry.created_at) || "")) + '</time>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
html += '<div class="aksrt-after-sale-timeline__main">' +
|
if (note !== "") {
|
||||||
(note !== "" ? '<p class="aksrt-after-sale-timeline__note">' + escapeHtml(note).replace(/\r\n|\r|\n/g, "<br>") + '</p>' : "") +
|
html += '<p class="aksrt-after-sale-timeline__note">' + escapeHtml(note).replace(/\r\n|\r|\n/g, "<br>") + '</p>';
|
||||||
'</div></li>';
|
}
|
||||||
|
html += '</div></li>';
|
||||||
});
|
});
|
||||||
return html + '</ol></div>';
|
return html + '</ol></div>';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -524,6 +524,10 @@ test('frontend renders the handling log as a rope-knot timeline', () => {
|
|||||||
assert.match(js, /aksrt-after-sale-timeline__status/)
|
assert.match(js, /aksrt-after-sale-timeline__status/)
|
||||||
assert.match(js, /aksrt-after-sale-timeline__time/)
|
assert.match(js, /aksrt-after-sale-timeline__time/)
|
||||||
assert.match(js, /aksrt-after-sale-timeline__note/)
|
assert.match(js, /aksrt-after-sale-timeline__note/)
|
||||||
|
// [节点][处理状态][时间] 同一行:节点独占一列,状态与时间包在 head 里
|
||||||
|
assert.match(js, /'<span class="aksrt-after-sale-timeline__knot"><\/span>'/)
|
||||||
|
assert.match(js, /'<div class="aksrt-after-sale-timeline__head">' \+/)
|
||||||
|
assert.match(js, /'<div class="aksrt-after-sale-timeline__body">';/)
|
||||||
|
|
||||||
// 订单详情弹窗:每条工单同样渲染时间线,载荷带上 handling_log
|
// 订单详情弹窗:每条工单同样渲染时间线,载荷带上 handling_log
|
||||||
assert.ok(!center.includes('gatewayResultText'), 'dead gateway-result renderer must be removed')
|
assert.ok(!center.includes('gatewayResultText'), 'dead gateway-result renderer must be removed')
|
||||||
@@ -535,11 +539,15 @@ test('frontend renders the handling log as a rope-knot timeline', () => {
|
|||||||
|
|
||||||
// 时间线样式:竖线穿节点、末项不收口,左列固定宽度、右列自适应
|
// 时间线样式:竖线穿节点、末项不收口,左列固定宽度、右列自适应
|
||||||
const css = read('assets/css/frontend/modal-runtime.css')
|
const css = read('assets/css/frontend/modal-runtime.css')
|
||||||
assert.match(css, /\.aksrt-after-sale-timeline__item \{ --as-accent: var\(--modal-muted\); display: grid; grid-template-columns: 88px minmax\(0, 1fr\)/)
|
assert.match(css, /\.aksrt-after-sale-timeline__item \{ --as-accent: var\(--modal-muted\); position: relative; display: grid; grid-template-columns: 10px minmax\(0, 1fr\); gap: 10px; padding: 0 0 14px; \}/)
|
||||||
assert.match(css, /\.aksrt-after-sale-timeline__aside::before \{ content: ""; position: absolute; top: 12px; bottom: -2px/)
|
assert.match(css, /\.aksrt-after-sale-timeline__item::before \{ content: ""; position: absolute; top: 14px; bottom: -2px; left: 4px/)
|
||||||
assert.match(css, /\.aksrt-after-sale-timeline__item:last-child \.aksrt-after-sale-timeline__aside::before \{ display: none; \}/)
|
assert.match(css, /\.aksrt-after-sale-timeline__item:last-child::before \{ display: none; \}/)
|
||||||
|
assert.match(css, /\.aksrt-after-sale-timeline__head \{ display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; \}/)
|
||||||
assert.match(css, /\.aksrt-after-sale-timeline__knot/)
|
assert.match(css, /\.aksrt-after-sale-timeline__knot/)
|
||||||
assert.match(css, /\.aksrt-after-sale-timeline__note/)
|
assert.match(css, /\.aksrt-after-sale-timeline__note/)
|
||||||
|
// 旧的两列结构(aside / main)已移除
|
||||||
|
assert.ok(!css.includes('aksrt-after-sale-timeline__aside'), 'left-column aside must be removed')
|
||||||
|
assert.ok(!css.includes('aksrt-after-sale-timeline__main'), 'right-column main must be removed')
|
||||||
|
|
||||||
// 打包产物同步:public.css / public.min.css 都含时间线规则
|
// 打包产物同步:public.css / public.min.css 都含时间线规则
|
||||||
assert.ok(read('assets/css/build/public.css').includes('.aksrt-after-sale-timeline__knot'))
|
assert.ok(read('assets/css/build/public.css').includes('.aksrt-after-sale-timeline__knot'))
|
||||||
|
|||||||
Reference in New Issue
Block a user