Files
zkxw/mobile-ui/zkxw-mobile-oa-list-circle-index-demo.html
T
2026-08-13 14:31:57 +08:00

594 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<title>OA 五列表 · 序号圆圈背景 Demo</title>
<style>
:root {
--brand: #0a9f6d;
--brand-dark: #087f57;
--brand-soft: #e9f9f2;
--warn: #d97706;
--danger: #ef4444;
--nav-bg: #ecfdf5;
--page-bg: #f5f6f8;
--text: #1f2329;
--text-muted: #8f959e;
--border: #e8eaed;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", -apple-system, system-ui, sans-serif;
color: var(--text);
background:
radial-gradient(900px 420px at 8% -8%, rgba(10, 159, 109, 0.12), transparent 55%),
linear-gradient(180deg, #f8fffc 0%, #f4f7fb 50%, #f7f8fa 100%);
line-height: 1.45;
}
.stage { max-width: 1180px; margin: 0 auto; padding: 28px 16px 64px; }
.hero h1 { margin: 0 0 8px; font-size: 22px; font-weight: 720; }
.hero p { margin: 0; color: #64748b; font-size: 14px; max-width: 920px; }
.rec {
margin: 16px 0 0;
padding: 14px 16px;
border-radius: 14px;
background: var(--brand-soft);
border: 1px solid #b7ebd4;
font-size: 13px;
color: #166534;
line-height: 1.55;
}
.rec b { color: var(--brand-dark); }
.tabs {
display: flex; flex-wrap: wrap; gap: 10px;
margin: 20px 0 18px;
}
.tab {
appearance: none; border: 1px solid #e2e8f0; background: #fff;
border-radius: 999px; padding: 10px 16px; font-size: 13px;
font-weight: 600; color: #475569; cursor: pointer;
}
.tab.is-active {
color: var(--brand-dark); border-color: var(--brand);
background: var(--brand-soft);
box-shadow: 0 0 0 3px rgba(10, 159, 109, 0.16);
}
.tab .badge {
display: inline-block; margin-left: 6px;
padding: 1px 7px; border-radius: 999px;
font-size: 11px; font-weight: 700;
background: #fef3c7; color: #b45309;
}
.tab.is-active .badge {
background: #fff; color: var(--brand-dark);
}
.layout {
display: grid;
grid-template-columns: minmax(240px, 300px) 1fr;
gap: 20px; align-items: start;
}
@media (max-width: 900px) { .layout { grid-template-columns: 1fr; } }
.panel {
background: rgba(255,255,255,.94);
border: 1px solid #e2e8f0; border-radius: 16px; padding: 16px;
box-shadow: 0 10px 28px rgba(15,23,42,.05);
}
.panel h2 { margin: 0 0 4px; font-size: 15px; }
.panel .sub { margin: 0 0 10px; font-size: 12px; color: #64748b; }
.panel ul { margin: 0; padding-left: 18px; font-size: 13px; color: #475569; }
.panel li { margin: 6px 0; }
.panel .ok {
margin-top: 12px; padding: 10px 12px; border-radius: 10px;
background: #ecfdf5; border: 1px solid #a7f3d0;
font-size: 12px; color: #047857;
}
.panel .pick {
margin-top: 10px; padding: 10px 12px; border-radius: 10px;
background: #fff7e8; border: 1px solid #fde68a;
font-size: 12px; color: #92400e;
}
.phones { display: flex; flex-wrap: wrap; gap: 18px; justify-content: center; }
.phone {
width: 375px; background: #fff; border-radius: 28px; overflow: hidden;
box-shadow: 0 0 0 1px rgb(15 23 42 / 6%), 0 18px 40px rgb(15 23 42 / 12%);
}
.cap {
text-align: center; font-size: 11px; font-weight: 650; color: #64748b;
padding: 8px; background: #f8fafc; border-bottom: 1px solid #eef2f0;
}
.nav {
height: 44px; display: flex; align-items: center; padding: 0 8px 0 4px;
background: var(--nav-bg); border-bottom: 1px solid #d8f3e5;
position: relative;
}
.nav-back {
width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
color: #0f291e; flex-shrink: 0;
}
.nav-back::before {
content: ""; width: 10px; height: 10px;
border-left: 2px solid #0f291e; border-bottom: 2px solid #0f291e;
transform: rotate(45deg); margin-left: 4px;
}
.nav-title {
position: absolute; left: 50%; transform: translateX(-50%);
font-size: 16px; font-weight: 650; color: #0f291e;
}
.page {
min-height: 640px;
background: var(--page-bg);
padding-bottom: 24px;
}
.toolbar {
padding: 8px 12px;
background: #fff;
border-bottom: 1px solid #f1f5f9;
}
.search-box {
height: 36px;
display: flex; align-items: center; gap: 8px;
padding: 0 12px; border-radius: 10px;
background: #fafafa; border: 1px solid #e8eaed;
}
.search-ico {
width: 13px; height: 13px; border: 1.5px solid #94a3b8; border-radius: 50%;
position: relative; flex-shrink: 0;
}
.search-ico::after {
content: ""; position: absolute; width: 6px; height: 1.5px;
right: -4px; bottom: -2px; background: #94a3b8; border-radius: 2px;
transform: rotate(45deg);
}
.search-ph { font-size: 13px; color: #b0b7c3; }
.list {
padding: 10px 12px 0;
display: flex; flex-direction: column; gap: 10px;
}
.dd-card {
position: relative;
width: 100%;
background: #fff;
border-radius: 14px;
padding: 14px 14px 12px;
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.04);
overflow: hidden;
}
.dd-card__head {
display: flex; align-items: flex-start; gap: 8px;
}
.dd-card__title {
flex: 1; min-width: 0;
font-size: 15px; font-weight: 700; line-height: 1.4; color: #1f2329;
}
.dd-card__time {
flex-shrink: 0; font-size: 12px; line-height: 1.4; color: #8f959e; padding-top: 2px;
}
.dd-card__meta { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.dd-card__meta-line {
font-size: 13px; line-height: 1.45; color: #8f959e;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dd-card__foot {
margin-top: 12px; display: flex; align-items: center;
justify-content: space-between; gap: 8px;
}
.dd-card__hint-tag {
max-width: 70%;
font-size: 11px; font-weight: 650; line-height: 1.2;
padding: 4px 9px; border-radius: 999px;
background: rgba(217, 119, 6, 0.12); color: #d97706;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dd-card__hint-tag.is-success {
background: rgba(10, 159, 109, 0.12); color: #0a9f6d;
}
.dd-card__hint-tag.is-danger {
background: rgba(239, 68, 68, 0.12); color: #ef4444;
}
.dd-card__status {
flex-shrink: 0; font-size: 12px; font-weight: 600; color: #d97706;
}
.dd-card__status.is-success { color: #0a9f6d; }
.dd-card__status.is-danger { color: #ef4444; }
/* ---------- 0:现状折角 ---------- */
.style-0 .idx {
position: absolute; top: 0; left: 0;
width: 0; height: 0;
border-style: solid;
border-width: 34px 34px 0 0;
border-color: var(--brand) transparent transparent transparent;
pointer-events: none;
}
.style-0 .idx span {
position: absolute; top: -30px; left: 4px;
font-size: 10px; font-weight: 800; color: #fff;
line-height: 1; letter-spacing: -0.02em;
}
/* 圆圈共用:左上绝对定位 */
.circle-idx {
position: absolute;
top: 10px;
left: 10px;
width: 24px;
height: 24px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 10px;
font-weight: 800;
line-height: 1;
letter-spacing: -0.02em;
font-variant-numeric: tabular-nums;
pointer-events: none;
z-index: 1;
}
.style-a .dd-card,
.style-b .dd-card,
.style-c .dd-card,
.style-d .dd-card {
padding-left: 42px;
}
/* A:实心品牌绿圆(推荐) */
.style-a .circle-idx {
color: #fff;
background: var(--brand);
box-shadow: 0 2px 6px rgba(10, 159, 109, 0.28);
}
/* B:浅绿底 + 深绿字 */
.style-b .circle-idx {
color: var(--brand-dark);
background: var(--brand-soft);
border: 1px solid #b7ebd4;
}
/* C:描边空心圆 */
.style-c .circle-idx {
color: var(--brand-dark);
background: #fff;
border: 1.5px solid var(--brand);
}
/* D:贴角半出入圆(更像「角标」) */
.style-d .dd-card { overflow: visible; }
.style-d .circle-idx {
top: -6px;
left: -6px;
width: 28px;
height: 28px;
font-size: 11px;
color: #fff;
background: linear-gradient(145deg, #12b981 0%, #0a9f6d 55%, #087f57 100%);
box-shadow: 0 4px 10px rgba(10, 159, 109, 0.28);
border: 2px solid #fff;
}
.style-d .dd-card { padding-left: 14px; }
.swatches {
margin-top: 18px;
display: flex; flex-wrap: wrap; gap: 10px;
}
.swatch {
display: inline-flex; align-items: center; gap: 8px;
padding: 8px 12px; border-radius: 999px;
background: #fff; border: 1px solid #e2e8f0;
font-size: 12px; color: #475569;
}
.swatch .dot {
width: 22px; height: 22px; border-radius: 50%;
display: inline-flex; align-items: center; justify-content: center;
font-size: 9px; font-weight: 800;
}
.swatch .dot.a { color: #fff; background: var(--brand); }
.swatch .dot.b { color: var(--brand-dark); background: var(--brand-soft); border: 1px solid #b7ebd4; }
.swatch .dot.c { color: var(--brand-dark); background: #fff; border: 1.5px solid var(--brand); }
.swatch .dot.d {
color: #fff;
background: linear-gradient(145deg, #12b981, #087f57);
border: 2px solid #fff;
box-shadow: 0 2px 6px rgba(10, 159, 109, 0.25);
}
.legend {
margin-top: 22px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 12px;
}
.legend .box {
background: #fff; border: 1px solid #e2e8f0; border-radius: 12px;
padding: 12px 14px; font-size: 12px; color: #475569;
}
.legend h3 { margin: 0 0 6px; font-size: 13px; color: #0f291e; }
code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 11px; background: #f1f5f9; padding: 1px 5px; border-radius: 4px;
}
</style>
</head>
<body>
<div class="stage">
<div class="hero">
<h1>OA 五列表 · 序号改为圆圈背景</h1>
<p>
覆盖草稿箱 / 待办 / 已办 / 我发起的 / 抄送我的。现状为左上三角折角;
本 Demo 对比「圆圈背景」几种落法。序号规则不变:<code>01</code>、<code>02</code>…跨页连续。
</p>
<div class="rec">
<b>推荐:A 实心绿圆</b> —— 仍在卡片左上,品牌绿实心圆 + 白字两位序号;
只改 <code>.dd-card__corner</code> 样式,五页 DOM 可基本不动。
</div>
</div>
<div class="tabs" id="tabs">
<button type="button" class="tab" data-k="0">0 现状折角</button>
<button type="button" class="tab is-active" data-k="a">A 实心绿圆<span class="badge">推荐</span></button>
<button type="button" class="tab" data-k="b">B 浅绿圆</button>
<button type="button" class="tab" data-k="c">C 描边圆</button>
<button type="button" class="tab" data-k="d">D 贴角出入圆</button>
</div>
<div class="swatches" aria-hidden="true">
<span class="swatch"><span class="dot a">01</span>A 实心</span>
<span class="swatch"><span class="dot b">02</span>B 浅绿</span>
<span class="swatch"><span class="dot c">03</span>C 描边</span>
<span class="swatch"><span class="dot d">04</span>D 贴角</span>
</div>
<div class="layout" style="margin-top: 18px;">
<aside class="panel" id="side"></aside>
<div class="phones">
<div class="phone">
<div class="cap" id="cap">A 实心绿圆 · 推荐 · 375px</div>
<div class="nav">
<div class="nav-back" aria-hidden="true"></div>
<div class="nav-title">待办中心</div>
</div>
<div class="page">
<div class="toolbar">
<div class="search-box">
<span class="search-ico" aria-hidden="true"></span>
<span class="search-ph">搜索流程名称</span>
</div>
</div>
<div class="list style-a" id="list"></div>
</div>
</div>
</div>
</div>
<div class="legend">
<div class="box">
<h3>落地建议</h3>
仅改 <code>oa-task-list.scss</code> 中 <code>.dd-card__corner</code> /
<code>.dd-card__corner-num</code>;五页模板结构可保留,避免重复改 Vue。
</div>
<div class="box">
<h3>序号规则</h3>
仍用 <code>formatCornerIndex</code>:&lt;10 补零;
跨页 <code>(page-1)*pageSize + index + 1</code>。
</div>
<div class="box">
<h3>注意</h3>
A/B/C 需给标题区留左内边距(约 42px),避免圆圈压住标题;
D 贴角方案可不加左 padding,但卡片需 <code>overflow: visible</code>。
</div>
</div>
</div>
<script>
const ROWS = [
{
title: '费用报销',
time: '09:12',
meta: ['报销金额:1280.00', '事由:差旅交通'],
hint: '待张敏处理',
status: '进行中',
tone: 'warn'
},
{
title: '请假申请',
time: '昨天',
meta: ['请假类型:事假', '请假天数:1'],
hint: '待李华处理',
status: '进行中',
tone: 'warn'
},
{
title: '采购申请',
time: '前天',
meta: ['采购金额:8600.00', '供应商:华东物资'],
hint: '审批完成',
status: '已完成',
tone: 'success'
},
{
title: '出差申请',
time: '3天前',
meta: ['目的地:上海', '天数:2'],
hint: '已打回',
status: '已打回',
tone: 'danger'
},
{
title: '用章申请 · 两位数序号样例',
time: '2026.08.01',
meta: ['用章类型:公章', '份数:2'],
hint: '抄送我的',
status: '已撤销',
tone: 'mute',
forceN: 12
}
]
const SIDE = {
'0': {
title: '0 现状折角',
sub: '三角折角 + 白字',
items: [
'当前五列表已上线样式',
'视觉强,但标题区被三角切角',
'两位数在三角里偏挤'
],
note: 'pick',
noteText: '将被圆圈方案替换。'
},
a: {
title: 'A 实心绿圆',
sub: '左上 24px 圆 · 白字',
items: [
'品牌绿实心,辨识度接近折角',
'圆内两位序号更规整',
'卡片加左 padding,标题不被遮'
],
note: 'ok',
noteText: '推荐默认方案;改动面最小。'
},
b: {
title: 'B 浅绿圆',
sub: '浅绿底 + 深绿字',
items: [
'更轻、更「信息角标」',
'与三墙 soft 色一致',
'在白卡片上对比略弱于 A'
],
note: 'ok',
noteText: '若希望序号不抢眼,选 B。'
},
c: {
title: 'C 描边圆',
sub: '白底 + 绿描边',
items: [
'最轻量',
'适合信息密度高的列表',
'远看不如实心醒目'
],
note: 'pick',
noteText: '备选;偏「弱序号」。'
},
d: {
title: 'D 贴角出入圆',
sub: '压住左上圆角 · 半出入',
items: [
'更像「角标徽章」',
'不挤标题左 padding',
'需 overflow:visible,注意阴影裁切'
],
note: 'ok',
noteText: '想保留「角标感」时优先于 A。'
}
}
const CAP = {
'0': '0 现状折角 · 375px',
a: 'A 实心绿圆 · 推荐 · 375px',
b: 'B 浅绿圆 · 375px',
c: 'C 描边圆 · 375px',
d: 'D 贴角出入圆 · 375px'
}
function pad(n) {
return n < 10 ? '0' + n : String(n)
}
function render(style) {
const list = document.getElementById('list')
list.className = 'list style-' + style
list.innerHTML = ROWS.map((row, i) => {
const n = row.forceN != null ? row.forceN : i + 1
const hintClass =
'dd-card__hint-tag' +
(row.tone === 'success' ? ' is-success' : '') +
(row.tone === 'danger' ? ' is-danger' : '')
const statusClass =
'dd-card__status' +
(row.tone === 'success' ? ' is-success' : '') +
(row.tone === 'danger' ? ' is-danger' : '')
let badge = ''
if (style === '0') {
badge = '<div class="idx" aria-hidden="true"><span>' + pad(n) + '</span></div>'
} else {
badge = '<div class="circle-idx" aria-hidden="true">' + pad(n) + '</div>'
}
const meta = row.meta
.map((m) => '<span class="dd-card__meta-line">' + m + '</span>')
.join('')
return (
'<div class="dd-card">' +
badge +
'<div class="dd-card__head">' +
'<div class="dd-card__title">' +
row.title +
'</div>' +
'<div class="dd-card__time">' +
row.time +
'</div>' +
'</div>' +
'<div class="dd-card__meta">' +
meta +
'</div>' +
'<div class="dd-card__foot">' +
'<span class="' +
hintClass +
'">' +
row.hint +
'</span>' +
'<span class="' +
statusClass +
'">' +
row.status +
'</span>' +
'</div>' +
'</div>'
)
}).join('')
const s = SIDE[style]
document.getElementById('side').innerHTML =
'<h2>' +
s.title +
'</h2>' +
'<p class="sub">' +
s.sub +
'</p>' +
'<ul>' +
s.items.map((t) => '<li>' + t + '</li>').join('') +
'</ul>' +
'<div class="' +
s.note +
'">' +
s.noteText +
'</div>'
document.getElementById('cap').textContent = CAP[style]
}
document.getElementById('tabs').addEventListener('click', (e) => {
const btn = e.target.closest('.tab')
if (!btn) return
document.querySelectorAll('.tab').forEach((t) => t.classList.remove('is-active'))
btn.classList.add('is-active')
render(btn.dataset.k)
})
render('a')
</script>
</body>
</html>