Files
zkxw/mobile-ui/zkxw-mobile-oa-detail-actions-demo.html
T
zlwuandCursor 1c844398a9 docs: add mobile OA detail footer fold and default comment design
Capture scheme A (⋯ + return + approve), ActionSheet more actions, and default remark copy for pending approval detail.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-12 18:09:59 +08:00

965 lines
28 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" />
<title>待办详情 · 底栏操作折叠 & 默认备注 Demo</title>
<style>
:root {
--ink: #0f172a;
--muted: #64748b;
--line: #e2e8f0;
--brand: #22c55e;
--brand-dark: #16a34a;
--brand-deep: #15803d;
--brand-soft: #ecfdf5;
--danger: #ef4444;
--warn: #d97706;
}
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body {
font-family: "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", -apple-system, system-ui, sans-serif;
color: var(--ink);
background:
radial-gradient(900px 420px at 8% -8%, rgba(52, 211, 153, 0.16), transparent 55%),
radial-gradient(700px 380px at 92% 0%, rgba(148, 163, 184, 0.14), transparent 50%),
linear-gradient(180deg, #f8fffc 0%, #f4f7fb 50%, #f7f8fa 100%);
line-height: 1.45;
}
.wrap {
max-width: 1180px;
margin: 0 auto;
padding: 28px 18px 96px;
}
h1 {
margin: 0 0 8px;
font-size: 24px;
letter-spacing: -0.02em;
font-weight: 720;
}
.sub {
margin: 0;
max-width: 760px;
color: var(--muted);
font-size: 14px;
}
.note {
margin: 14px 0 22px;
padding: 10px 12px;
border-radius: 10px;
background: #fff;
border: 1px solid var(--line);
font-size: 13px;
color: var(--muted);
}
.note strong { color: var(--brand-deep); }
.layout {
display: grid;
grid-template-columns: 1fr minmax(300px, 360px);
gap: 22px;
align-items: start;
}
@media (max-width: 920px) {
.layout { grid-template-columns: 1fr; }
}
.schemes { display: grid; gap: 12px; }
.scheme {
appearance: none;
width: 100%;
text-align: left;
border: 1px solid var(--line);
background: #fff;
border-radius: 16px;
padding: 14px 16px;
cursor: pointer;
transition: border-color .15s, box-shadow .15s, background .15s;
font-family: inherit;
color: inherit;
}
.scheme:hover {
border-color: #cbd5e1;
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.05);
}
.scheme.is-on {
border-color: var(--brand);
background: linear-gradient(180deg, #f0fdf4, #fff);
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18);
}
.scheme-top {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 6px;
}
.letter {
width: 28px; height: 28px;
border-radius: 8px;
display: grid; place-items: center;
font-size: 13px; font-weight: 750;
background: #f1f5f9;
color: #334155;
flex-shrink: 0;
}
.scheme.is-on .letter {
background: var(--brand-soft);
color: var(--brand-deep);
}
.scheme h2 {
margin: 0;
font-size: 15px;
font-weight: 680;
}
.scheme p {
margin: 0 0 10px;
font-size: 12.5px;
color: var(--muted);
padding-left: 38px;
}
.tag {
display: inline-block;
margin: 0 0 0 38px;
padding: 2px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 650;
background: #f1f5f9;
color: #475569;
}
.scheme.is-on .tag {
background: var(--brand-soft);
color: var(--brand-deep);
}
.tag.rec {
background: #ecfdf5;
color: #047857;
}
.defaults {
margin-top: 18px;
padding: 14px 16px;
border-radius: 16px;
background: #fff;
border: 1px solid var(--line);
}
.defaults h3 {
margin: 0 0 10px;
font-size: 14px;
font-weight: 680;
}
.defaults table {
width: 100%;
border-collapse: collapse;
font-size: 12.5px;
}
.defaults th,
.defaults td {
text-align: left;
padding: 8px 6px;
border-bottom: 1px solid #f1f5f9;
vertical-align: top;
}
.defaults th {
color: var(--muted);
font-weight: 600;
font-size: 11px;
}
.defaults code {
display: inline-block;
padding: 2px 6px;
border-radius: 6px;
background: #f8fafc;
border: 1px solid #e2e8f0;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 12px;
color: #0f172a;
}
.defaults .hint {
margin: 10px 0 0;
font-size: 12px;
color: var(--muted);
}
/* —— Phone preview —— */
.preview-col { position: sticky; top: 16px; }
.phone {
width: 100%;
max-width: 360px;
margin: 0 auto;
border-radius: 28px;
border: 1px solid #cbd5e1;
background: #0f172a;
padding: 10px;
box-shadow: 0 24px 60px rgba(15, 23, 42, 0.18);
}
.phone-inner {
position: relative;
height: 640px;
border-radius: 20px;
overflow: hidden;
background: #fff;
display: flex;
flex-direction: column;
}
.status-bar {
display: flex;
justify-content: space-between;
padding: 8px 16px 4px;
font-size: 11px;
font-weight: 650;
color: #334155;
background: #fff;
}
.nav {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px 10px;
border-bottom: 1px solid #f1f5f9;
background: #fff;
}
.nav .back {
width: 28px; height: 28px;
border-radius: 8px;
display: grid; place-items: center;
background: #f1f5f9;
color: #475569;
font-size: 14px;
}
.nav h4 {
margin: 0;
font-size: 15px;
font-weight: 700;
}
.scroll {
flex: 1;
overflow: auto;
padding: 12px 12px 108px;
background: #f8fafc;
-webkit-overflow-scrolling: touch;
}
.card {
background: #fff;
border-radius: 14px;
border: 1px solid #eef2f6;
padding: 12px 14px;
margin-bottom: 10px;
}
.card .title-row {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
}
.card h5 {
margin: 0;
font-size: 15px;
font-weight: 700;
}
.pill {
padding: 2px 8px;
border-radius: 6px;
font-size: 11px;
font-weight: 650;
color: var(--warn);
background: #fffbeb;
}
.field {
display: flex;
justify-content: space-between;
gap: 12px;
padding: 8px 0;
border-top: 1px solid #f1f5f9;
font-size: 13px;
}
.field:first-of-type { border-top: none; }
.field .k { color: var(--muted); }
.field .v { color: #0f172a; font-weight: 560; text-align: right; }
.timeline {
font-size: 12.5px;
color: #334155;
}
.timeline .item {
display: grid;
grid-template-columns: 14px 1fr;
gap: 8px;
margin-bottom: 10px;
}
.dot {
width: 10px; height: 10px;
margin-top: 4px;
border-radius: 50%;
background: var(--brand);
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18);
}
.dot.mute { background: #94a3b8; box-shadow: none; }
.timeline .t { font-weight: 650; }
.timeline .m { color: var(--muted); margin-top: 2px; }
.ft {
position: absolute;
left: 0; right: 0; bottom: 0;
z-index: 5;
display: flex;
align-items: center;
gap: 8px;
padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
background: #fff;
border-top: 1px solid #eef2f6;
box-shadow: 0 -4px 16px rgba(15, 23, 42, 0.04);
}
.btn {
appearance: none;
border: none;
height: 40px;
border-radius: 12px;
font: inherit;
font-size: 14px;
font-weight: 650;
cursor: pointer;
padding: 0 12px;
transition: opacity .12s, transform .12s;
}
.btn:active { opacity: 0.88; transform: scale(0.98); }
.btn-close {
flex: 0 0 auto;
min-width: 56px;
background: #f1f5f9;
color: #334155;
}
.btn-ghost {
flex: 1 1 0;
min-width: 0;
border: 1px solid #e2e8f0;
background: #fff;
color: #334155;
}
.btn-primary {
flex: 1.2 1 0;
min-width: 0;
background: var(--brand);
color: #fff;
font-weight: 750;
}
.btn-more {
flex: 0 0 auto;
width: 44px;
padding: 0;
border: 1px solid #e2e8f0;
background: #fff;
color: #334155;
font-size: 18px;
letter-spacing: 1px;
line-height: 1;
}
.btn-more.wide {
width: auto;
min-width: 72px;
font-size: 14px;
letter-spacing: 0;
}
/* before (crowded) */
.ft.before .btn {
flex: 1 1 auto;
min-width: 0;
padding: 0 6px;
font-size: 12px;
}
.ft.before .btn-primary { flex: 1.1 1 auto; }
/* overlay */
.mask {
position: absolute;
inset: 0;
background: rgba(15, 23, 42, 0.42);
z-index: 10;
opacity: 0;
pointer-events: none;
transition: opacity .18s;
}
.mask.is-on {
opacity: 1;
pointer-events: auto;
}
.sheet {
position: absolute;
left: 0; right: 0; bottom: 0;
z-index: 12;
background: #fff;
border-radius: 18px 18px 0 0;
transform: translateY(110%);
transition: transform .22s ease;
padding: 8px 0 calc(12px + env(safe-area-inset-bottom, 0px));
box-shadow: 0 -8px 28px rgba(15, 23, 42, 0.12);
}
.sheet.is-on { transform: translateY(0); }
.sheet .handle {
width: 36px; height: 4px;
border-radius: 999px;
background: #e2e8f0;
margin: 4px auto 10px;
}
.sheet .row {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
padding: 14px 18px;
border: none;
background: transparent;
font: inherit;
font-size: 15px;
font-weight: 600;
color: #0f172a;
cursor: pointer;
text-align: left;
}
.sheet .row:active { background: #f8fafc; }
.sheet .row .ico {
width: 32px; height: 32px;
border-radius: 10px;
display: grid; place-items: center;
background: #f1f5f9;
color: #475569;
font-size: 14px;
}
.sheet .row.danger { color: var(--danger); }
.sheet .row.danger .ico { background: #fef2f2; color: var(--danger); }
.sheet .cancel {
margin: 8px 12px 0;
height: 44px;
border-radius: 12px;
border: none;
background: #f1f5f9;
font: inherit;
font-size: 15px;
font-weight: 650;
color: #334155;
cursor: pointer;
width: calc(100% - 24px);
}
.popover {
position: absolute;
right: 12px;
bottom: 62px;
z-index: 12;
min-width: 148px;
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 12px;
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);
padding: 6px;
opacity: 0;
pointer-events: none;
transform: translateY(6px);
transition: opacity .15s, transform .15s;
}
.popover.is-on {
opacity: 1;
pointer-events: auto;
transform: translateY(0);
}
.popover button {
display: block;
width: 100%;
text-align: left;
border: none;
background: transparent;
padding: 10px 12px;
border-radius: 8px;
font: inherit;
font-size: 13.5px;
font-weight: 600;
color: #0f172a;
cursor: pointer;
}
.popover button:active { background: #f8fafc; }
.popover button.danger { color: var(--danger); }
.dialog {
position: absolute;
left: 50%;
top: 42%;
z-index: 14;
width: calc(100% - 48px);
max-width: 300px;
transform: translate(-50%, -50%) scale(0.96);
background: #fff;
border-radius: 16px;
padding: 16px 16px 12px;
box-shadow: 0 20px 48px rgba(15, 23, 42, 0.2);
opacity: 0;
pointer-events: none;
transition: opacity .15s, transform .15s;
}
.dialog.is-on {
opacity: 1;
pointer-events: auto;
transform: translate(-50%, -50%) scale(1);
}
.dialog h3 {
margin: 0 0 10px;
text-align: center;
font-size: 16px;
font-weight: 720;
}
.dialog textarea {
width: 100%;
min-height: 84px;
resize: none;
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 10px 12px;
font: inherit;
font-size: 14px;
color: #0f172a;
background: #f8fafc;
outline: none;
}
.dialog textarea:focus {
border-color: #86efac;
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.15);
background: #fff;
}
.dialog .tip {
margin: 8px 0 0;
font-size: 11.5px;
color: var(--muted);
}
.dialog .acts {
display: grid;
grid-template-columns: 1fr 1fr;
margin-top: 12px;
border-top: 1px solid #f1f5f9;
}
.dialog .acts button {
border: none;
background: transparent;
height: 42px;
font: inherit;
font-size: 15px;
font-weight: 650;
cursor: pointer;
}
.dialog .acts .cancel { color: #64748b; border-right: 1px solid #f1f5f9; }
.dialog .acts .ok { color: var(--brand-deep); font-weight: 750; }
.toast {
position: absolute;
left: 50%;
bottom: 88px;
z-index: 20;
transform: translateX(-50%) translateY(8px);
background: rgba(15, 23, 42, 0.88);
color: #fff;
padding: 8px 14px;
border-radius: 999px;
font-size: 12.5px;
font-weight: 600;
opacity: 0;
pointer-events: none;
transition: opacity .18s, transform .18s;
white-space: nowrap;
max-width: 90%;
overflow: hidden;
text-overflow: ellipsis;
}
.toast.is-on {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.actions {
margin-top: 14px;
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
}
.chip {
appearance: none;
border: 1px solid var(--line);
background: #fff;
border-radius: 999px;
padding: 8px 12px;
font: inherit;
font-size: 12px;
font-weight: 650;
color: #475569;
cursor: pointer;
}
.chip:hover { border-color: #94a3b8; }
</style>
</head>
<body>
<div class="wrap">
<h1>待办详情 · 底栏操作折叠 & 默认备注</h1>
<p class="sub">
对齐现网 <code>mobile-oa-h5</code> 审批详情底栏:默认只露出「同意 / 打回」,其余收入「更多」;
需要意见的操作弹窗预填默认备注,用户可改。
</p>
<div class="note">
<strong>推荐方案 A</strong>:底栏「⋯ / 打回 / 同意」;更多用底部 ActionSheet。
右侧手机可点击交互。默认备注见下方表格,可在弹窗内修改。
</div>
<div class="layout">
<div>
<div class="schemes" id="schemes">
<button type="button" class="scheme is-on" data-scheme="A">
<div class="scheme-top">
<span class="letter">A</span>
<h2>⋯ + 打回 + 同意(推荐)</h2>
</div>
<p>主路径最清晰:主操作同意、次操作打回常显;拒绝 / 加签 / 转交 / 打印 / 关闭进更多(底部 ActionSheet)。</p>
<span class="tag rec">推荐 · 对齐「默认只展示同意、打回」</span>
</button>
<button type="button" class="scheme" data-scheme="B">
<div class="scheme-top">
<span class="letter">B</span>
<h2>关闭常显 + ⋯ + 打回 + 同意</h2>
</div>
<p>保留「关闭」快捷退出;更多里放拒绝 / 加签 / 转交 / 打印。比 A 多一颗常驻按钮。</p>
<span class="tag">可选 · 退出路径更短</span>
</button>
<button type="button" class="scheme" data-scheme="C">
<div class="scheme-top">
<span class="letter">C</span>
<h2>⋯文字「更多」+ 气泡菜单</h2>
</div>
<p>底栏文案「更多」替代三点;点击后右下角 Popover 列出次要操作,不遮挡整屏。</p>
<span class="tag">可选 · 轻量气泡</span>
</button>
<button type="button" class="scheme" data-scheme="before">
<div class="scheme-top">
<span class="letter">旧</span>
<h2>现状:全部平铺</h2>
</div>
<p>关闭 / 打印 / 拒绝 / 打回 / 加签 / 转交 / 同意 一排挤开,字号被迫缩小。</p>
<span class="tag">对比</span>
</button>
</div>
<div class="defaults">
<h3>默认备注文案(弹窗预填,可改)</h3>
<table>
<thead>
<tr><th>操作</th><th>默认备注</th><th>说明</th></tr>
</thead>
<tbody>
<tr><td>同意</td><td><code>同意</code></td><td>意见可选,预填后可直接确定</td></tr>
<tr><td>打回</td><td><code>请修改后重新提交</code></td><td>意见必填;预填满足必填,可改</td></tr>
<tr><td>拒绝</td><td><code>拒绝</code></td><td>意见可选</td></tr>
<tr><td>加签</td><td><code>加签</code></td><td>说明可选;选人后仍可改备注</td></tr>
<tr><td>转交</td><td><code>转交</code></td><td>说明可选;选人后仍可改备注</td></tr>
</tbody>
</table>
<p class="hint">点右侧「同意 / 打回 / 拒绝」可看预填弹窗;加签 / 转交在本 Demo 仅 Toast 示意(真实页仍为选人面板 + 预填说明)。</p>
</div>
</div>
<div class="preview-col">
<div class="phone">
<div class="phone-inner" id="phone">
<div class="status-bar"><span>9:41</span><span>●●●</span></div>
<div class="nav">
<span class="back">‹</span>
<h4>审批详情</h4>
</div>
<div class="scroll">
<div class="card">
<div class="title-row">
<h5>请假申请 · 张三</h5>
<span class="pill">处理中</span>
</div>
<div class="field"><span class="k">请假类型</span><span class="v">年假</span></div>
<div class="field"><span class="k">开始时间</span><span class="v">2026-08-18 09:00</span></div>
<div class="field"><span class="k">结束时间</span><span class="v">2026-08-19 18:00</span></div>
<div class="field"><span class="k">时长</span><span class="v">2 天</span></div>
</div>
<div class="card timeline">
<div class="item"><span class="dot"></span><div><div class="t">发起人 · 张三</div><div class="m">已提交 · 08-12 10:02</div></div></div>
<div class="item"><span class="dot mute"></span><div><div class="t">审批人 · 我</div><div class="m">待处理</div></div></div>
</div>
</div>
<footer class="ft" id="ft"></footer>
<div class="mask" id="mask"></div>
<div class="sheet" id="sheet">
<div class="handle"></div>
<div id="sheetRows"></div>
<button type="button" class="cancel" id="sheetCancel">取消</button>
</div>
<div class="popover" id="popover"></div>
<div class="dialog" id="dialog">
<h3 id="dialogTitle">同意</h3>
<textarea id="dialogText" maxlength="500"></textarea>
<p class="tip" id="dialogTip">可修改默认备注后确定</p>
<div class="acts">
<button type="button" class="cancel" id="dialogCancel">取消</button>
<button type="button" class="ok" id="dialogOk">确定</button>
</div>
</div>
<div class="toast" id="toast"></div>
</div>
</div>
<div class="actions">
<button type="button" class="chip" data-demo="approve">演示 · 同意弹窗</button>
<button type="button" class="chip" data-demo="return">演示 · 打回弹窗</button>
<button type="button" class="chip" data-demo="reject">演示 · 拒绝弹窗</button>
<button type="button" class="chip" data-demo="more">演示 · 打开更多</button>
</div>
</div>
</div>
</div>
<script>
const DEFAULTS = {
approve: { title: '同意', text: '同意', tip: '意见可选,已预填默认备注', required: false },
return: { title: '打回', text: '请修改后重新提交', tip: '意见必填;已预填,可直接确定或修改', required: true },
reject: { title: '拒绝', text: '拒绝', tip: '意见可选,已预填默认备注', required: false },
addSign: { title: '加签', text: '加签', tip: '说明可选', required: false },
transfer: { title: '转交', text: '转交', tip: '说明可选', required: false }
}
const MORE_ITEMS = [
{ key: 'reject', label: '拒绝', danger: true, ico: '✕' },
{ key: 'addSign', label: '加签', ico: '+' },
{ key: 'transfer', label: '转交', ico: '⇄' },
{ key: 'print', label: '打印', ico: '🖨' },
{ key: 'close', label: '关闭', ico: '◁' }
]
let scheme = 'A'
let commentKind = 'approve'
let toastTimer = null
const ft = document.getElementById('ft')
const mask = document.getElementById('mask')
const sheet = document.getElementById('sheet')
const sheetRows = document.getElementById('sheetRows')
const popover = document.getElementById('popover')
const dialog = document.getElementById('dialog')
const dialogTitle = document.getElementById('dialogTitle')
const dialogText = document.getElementById('dialogText')
const dialogTip = document.getElementById('dialogTip')
const toastEl = document.getElementById('toast')
function toast(msg) {
toastEl.textContent = msg
toastEl.classList.add('is-on')
clearTimeout(toastTimer)
toastTimer = setTimeout(() => toastEl.classList.remove('is-on'), 1600)
}
function closeOverlays() {
mask.classList.remove('is-on')
sheet.classList.remove('is-on')
popover.classList.remove('is-on')
dialog.classList.remove('is-on')
}
function openComment(kind) {
const cfg = DEFAULTS[kind]
if (!cfg) return
commentKind = kind
closeOverlays()
dialogTitle.textContent = cfg.title
dialogText.value = cfg.text
dialogTip.textContent = cfg.tip
mask.classList.add('is-on')
dialog.classList.add('is-on')
setTimeout(() => {
dialogText.focus()
dialogText.select()
}, 50)
}
function onMoreAction(key) {
closeOverlays()
if (key === 'reject') {
openComment('reject')
return
}
if (key === 'addSign') {
toast('加签:选人面板 + 默认备注「加签」')
return
}
if (key === 'transfer') {
toast('转交:选人面板 + 默认备注「转交」')
return
}
if (key === 'print') {
toast('打印')
return
}
if (key === 'close') {
toast('关闭详情')
}
}
function renderMoreContent(includeClose) {
const items = MORE_ITEMS.filter((x) => includeClose || x.key !== 'close')
sheetRows.innerHTML = items.map((it) => `
<button type="button" class="row ${it.danger ? 'danger' : ''}" data-more="${it.key}">
<span class="ico">${it.ico}</span>
<span>${it.label}</span>
</button>
`).join('')
popover.innerHTML = items.map((it) => `
<button type="button" class="${it.danger ? 'danger' : ''}" data-more="${it.key}">${it.label}</button>
`).join('')
}
function openMore() {
dialog.classList.remove('is-on')
if (scheme === 'C') {
mask.classList.add('is-on')
popover.classList.add('is-on')
sheet.classList.remove('is-on')
return
}
mask.classList.add('is-on')
sheet.classList.add('is-on')
popover.classList.remove('is-on')
}
function renderFooter() {
closeOverlays()
ft.className = 'ft'
if (scheme === 'before') {
ft.classList.add('before')
ft.innerHTML = `
<button type="button" class="btn btn-close" data-act="close">关闭</button>
<button type="button" class="btn btn-ghost" data-act="print">打印</button>
<button type="button" class="btn btn-ghost" data-act="reject">拒绝</button>
<button type="button" class="btn btn-ghost" data-act="return">打回</button>
<button type="button" class="btn btn-ghost" data-act="addSign">加签</button>
<button type="button" class="btn btn-ghost" data-act="transfer">转交</button>
<button type="button" class="btn btn-primary" data-act="approve">同意</button>
`
renderMoreContent(true)
return
}
if (scheme === 'A') {
ft.innerHTML = `
<button type="button" class="btn btn-more" data-act="more" aria-label="更多">⋯</button>
<button type="button" class="btn btn-ghost" data-act="return">打回</button>
<button type="button" class="btn btn-primary" data-act="approve">同意</button>
`
renderMoreContent(true)
return
}
if (scheme === 'B') {
ft.innerHTML = `
<button type="button" class="btn btn-close" data-act="close">关闭</button>
<button type="button" class="btn btn-more" data-act="more" aria-label="更多">⋯</button>
<button type="button" class="btn btn-ghost" data-act="return">打回</button>
<button type="button" class="btn btn-primary" data-act="approve">同意</button>
`
renderMoreContent(false)
return
}
if (scheme === 'C') {
ft.innerHTML = `
<button type="button" class="btn btn-more wide" data-act="more">更多</button>
<button type="button" class="btn btn-ghost" data-act="return">打回</button>
<button type="button" class="btn btn-primary" data-act="approve">同意</button>
`
renderMoreContent(true)
}
}
document.getElementById('schemes').addEventListener('click', (e) => {
const btn = e.target.closest('.scheme')
if (!btn) return
document.querySelectorAll('.scheme').forEach((el) => el.classList.toggle('is-on', el === btn))
scheme = btn.dataset.scheme
renderFooter()
})
ft.addEventListener('click', (e) => {
const btn = e.target.closest('[data-act]')
if (!btn) return
const act = btn.dataset.act
if (act === 'more') {
openMore()
return
}
if (act === 'approve' || act === 'return' || act === 'reject') {
openComment(act)
return
}
if (act === 'addSign') {
toast('加签:选人面板 + 默认备注「加签」')
return
}
if (act === 'transfer') {
toast('转交:选人面板 + 默认备注「转交」')
return
}
if (act === 'print') {
toast('打印')
return
}
if (act === 'close') {
toast('关闭详情')
}
})
sheetRows.addEventListener('click', (e) => {
const btn = e.target.closest('[data-more]')
if (!btn) return
onMoreAction(btn.dataset.more)
})
popover.addEventListener('click', (e) => {
const btn = e.target.closest('[data-more]')
if (!btn) return
onMoreAction(btn.dataset.more)
})
mask.addEventListener('click', closeOverlays)
document.getElementById('sheetCancel').addEventListener('click', closeOverlays)
document.getElementById('dialogCancel').addEventListener('click', closeOverlays)
document.getElementById('dialogOk').addEventListener('click', () => {
const cfg = DEFAULTS[commentKind]
const text = String(dialogText.value || '').trim()
if (cfg?.required && !text) {
toast('请填写意见')
return
}
closeOverlays()
toast(`${cfg.title} · ${text || '(无备注)'}`)
})
document.querySelector('.actions').addEventListener('click', (e) => {
const chip = e.target.closest('[data-demo]')
if (!chip) return
const d = chip.dataset.demo
if (d === 'more') {
if (scheme === 'before') {
toast('请先切到方案 A/B/C')
return
}
openMore()
return
}
openComment(d)
})
renderFooter()
</script>
</body>
</html>