| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695 |
- <!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>待办中心 · 批量通过 / 打回 Demo</title>
- <style>
- :root {
- --brand: #0a9f6d;
- --brand-dark: #087f57;
- --brand-soft: #e9f9f2;
- --warn: #d97706;
- --warn-soft: #fff7e8;
- --danger: #ef4444;
- --danger-soft: #fef2f2;
- --nav-bg: #ecfdf5;
- --page-bg: #f5f8f7;
- --text: #1f2a26;
- --text-muted: #6b7b74;
- --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: 820px; }
- .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);
- }
- .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 .pick {
- margin-top: 12px; padding: 10px 12px; border-radius: 10px;
- background: #fff7e8; border: 1px solid #fde68a;
- font-size: 12px; color: #92400e;
- }
- .panel .ok {
- margin-top: 10px; padding: 10px 12px; border-radius: 10px;
- background: #ecfdf5; border: 1px solid #a7f3d0;
- font-size: 12px; color: #047857;
- }
- .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%);
- position: relative;
- }
- .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;
- }
- .nav-action {
- margin-left: auto;
- min-width: 48px;
- height: 36px;
- padding: 0 10px;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- font-size: 14px;
- font-weight: 650;
- color: var(--brand-dark);
- cursor: pointer;
- user-select: none;
- }
- .nav-action.is-done { color: #64748b; }
- .nav-action:active { opacity: 0.72; }
- .page {
- min-height: 640px;
- background: var(--page-bg);
- padding-bottom: 24px;
- position: relative;
- }
- .toolbar {
- padding: 10px 12px;
- background: #fff;
- border-bottom: 1px solid #f1f5f9;
- }
- .search-box {
- height: 36px;
- display: flex; align-items: center; gap: 8px;
- padding: 0 10px; border-radius: 10px;
- background: #f1f5f9; border: 1px solid transparent;
- }
- .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; }
- .batch-bar {
- display: none;
- align-items: center; justify-content: space-between; gap: 8px;
- margin: 8px 12px 0; padding: 10px 12px;
- border-radius: 10px; background: #fff;
- border: 1px solid var(--border);
- box-shadow: 0 2px 10px rgba(15, 23, 42, 0.04);
- }
- .batch-bar.is-show { display: flex; }
- .batch-bar__hint { font-size: 13px; color: #646a73; flex-shrink: 0; }
- .batch-bar__actions { display: flex; gap: 8px; margin-left: auto; }
- .batch-bar__btn {
- min-height: 32px; padding: 0 14px;
- display: inline-flex; align-items: center; justify-content: center;
- border-radius: 8px; font-size: 13px; font-weight: 600;
- border: none; cursor: pointer;
- }
- .batch-bar__btn.is-ok {
- color: #fff; background: var(--brand);
- }
- .batch-bar__btn.is-return {
- color: var(--warn); background: var(--warn-soft);
- border: 1px solid #fde68a;
- }
- .batch-bar__btn:disabled {
- opacity: 0.45; cursor: not-allowed;
- }
- .list {
- padding: 10px 12px 0;
- display: flex; flex-direction: column; gap: 10px;
- }
- .row {
- display: flex; align-items: stretch; gap: 0;
- }
- .check {
- display: none; align-items: flex-start;
- padding: 18px 0 0 2px; margin-right: 6px; flex-shrink: 0;
- cursor: pointer;
- }
- .page.is-manage .check { display: flex; }
- .check__box {
- width: 20px; height: 20px; border-radius: 50%;
- border: 1.5px solid #cbd5e1; background: #fff;
- display: flex; align-items: center; justify-content: center;
- font-size: 12px; color: #fff; font-weight: 700;
- }
- .check__box.on {
- background: var(--brand); border-color: var(--brand);
- }
- .card {
- flex: 1; min-width: 0;
- background: #fff; border-radius: 14px;
- border: 1px solid #d8f3e5;
- box-shadow: 0 4px 16px rgba(22, 163, 74, 0.07);
- padding: 12px 14px; cursor: pointer;
- }
- .card.is-selected {
- border-color: #86efac;
- box-shadow: 0 0 0 2px rgba(10, 159, 109, 0.18);
- }
- .card__head {
- display: flex; align-items: flex-start; justify-content: space-between; gap: 8px;
- }
- .card__title {
- font-size: 15px; font-weight: 700; color: #0f291e; line-height: 1.35;
- }
- .card__time { flex-shrink: 0; font-size: 11px; color: #94a3b8; margin-top: 2px; }
- .card__meta { margin-top: 8px; }
- .card__meta-line {
- display: block; font-size: 12px; color: var(--text-muted); line-height: 1.5;
- white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
- }
- .card__foot {
- margin-top: 10px; display: flex; align-items: center; justify-content: space-between;
- }
- .card__user { display: flex; align-items: center; gap: 6px; min-width: 0; }
- .card__avatar {
- width: 22px; height: 22px; border-radius: 6px;
- background: var(--brand); color: #fff;
- font-size: 10px; font-weight: 700;
- display: flex; align-items: center; justify-content: center;
- }
- .card__user-text { font-size: 12px; color: #64748b; }
- .card__status {
- font-size: 11px; font-weight: 650; padding: 3px 8px; border-radius: 999px;
- background: rgba(217, 119, 6, 0.12); color: var(--warn);
- }
- /* 底部意见弹层 */
- .sheet-mask {
- display: none; position: absolute; inset: 0;
- background: rgba(15, 23, 42, 0.45); z-index: 20;
- }
- .sheet-mask.is-show { display: block; }
- .sheet {
- position: absolute; left: 0; right: 0; bottom: 0; z-index: 21;
- background: #fff; border-radius: 16px 16px 0 0;
- padding: 14px 16px calc(14px + env(safe-area-inset-bottom, 0px));
- transform: translateY(110%);
- transition: transform .22s ease;
- box-shadow: 0 -8px 28px rgba(15, 23, 42, 0.12);
- }
- .sheet.is-show { transform: translateY(0); }
- .sheet__hd {
- display: flex; align-items: center; justify-content: space-between;
- margin-bottom: 10px;
- }
- .sheet__title { font-size: 16px; font-weight: 700; }
- .sheet__close {
- border: none; background: transparent; color: #64748b;
- font-size: 13px; cursor: pointer; padding: 4px 0;
- }
- .sheet__hint {
- margin: 0 0 8px; font-size: 12px; color: #64748b;
- }
- .sheet__hint strong { color: var(--brand-dark); }
- .sheet__ta {
- width: 100%; min-height: 96px; resize: none;
- border: 1px solid #e2e8f0; border-radius: 10px;
- padding: 10px 12px; font: inherit; font-size: 14px;
- color: var(--text); outline: none;
- background: #f8fafc;
- }
- .sheet__ta:focus {
- border-color: #86efac; background: #fff;
- box-shadow: 0 0 0 3px rgba(10, 159, 109, 0.12);
- }
- .sheet__ft {
- margin-top: 12px; display: flex; gap: 10px;
- }
- .sheet__btn {
- flex: 1; height: 40px; border-radius: 10px;
- border: none; font-size: 15px; font-weight: 650; cursor: pointer;
- }
- .sheet__btn.is-cancel {
- background: #f1f5f9; color: #475569;
- }
- .sheet__btn.is-ok {
- background: var(--brand); color: #fff;
- }
- .sheet__btn.is-return {
- background: var(--warn); color: #fff;
- }
- .toast {
- position: absolute; left: 50%; bottom: 88px; z-index: 30;
- transform: translateX(-50%);
- padding: 8px 14px; border-radius: 8px;
- background: rgba(15, 23, 42, 0.88); color: #fff;
- font-size: 13px; white-space: nowrap;
- opacity: 0; pointer-events: none; transition: opacity .2s;
- }
- .toast.is-show { opacity: 1; }
- .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; }
- </style>
- </head>
- <body>
- <div class="stage">
- <div class="hero">
- <h1>待办中心 · 批量通过 / 打回</h1>
- <p>
- 交互对齐抄送「管理 + 多选 + 底栏批量操作」。右侧手机可点击演示:进入管理 → 勾选 → 批量通过 / 打回 → 填写统一意见。
- </p>
- <div class="rec">
- <b>推荐方案(本 Demo):</b>
- <b>标题栏右侧「管理 / 完成」</b>(对齐草稿箱自定义顶栏);底栏「批量通过」「批量打回」;
- 统一意见框(通过默认「同意」可空,打回必填);前端串行调现有单条接口,汇总成功/失败。不含批量拒绝。
- </div>
- </div>
- <div class="tabs" id="sceneTabs">
- <button type="button" class="tab is-active" data-scene="browse">① 浏览态</button>
- <button type="button" class="tab" data-scene="manage">② 管理多选</button>
- <button type="button" class="tab" data-scene="approve">③ 批量通过弹层</button>
- <button type="button" class="tab" data-scene="return">④ 批量打回弹层</button>
- </div>
- <div class="layout">
- <aside class="panel" id="sidePanel">
- <h2>① 浏览态</h2>
- <p class="sub">日常查看 / 点卡片进详情审批</p>
- <ul>
- <li>标题栏右侧「管理」(对齐草稿箱自定义顶栏)</li>
- <li>搜索区仅保留检索,不再放管理按钮</li>
- <li>点卡片仍进入审批详情(webview)</li>
- </ul>
- <div class="pick">右侧手机可点标题栏「管理」亲自走一遍流程。</div>
- </aside>
- <div class="phones">
- <div class="phone">
- <div class="cap">可交互 Demo · 375px</div>
- <div class="nav">
- <div class="nav-back" aria-hidden="true"></div>
- <div class="nav-title">待办中心</div>
- <div class="nav-action" id="manageBtn">管理</div>
- </div>
- <div class="page" id="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="batch-bar" id="batchBar">
- <span class="batch-bar__hint" id="batchHint">已选 0 项</span>
- <div class="batch-bar__actions">
- <button type="button" class="batch-bar__btn is-return" id="btnReturn">批量打回</button>
- <button type="button" class="batch-bar__btn is-ok" id="btnApprove">批量通过</button>
- </div>
- </div>
- <div class="list" id="list"></div>
- <div class="sheet-mask" id="sheetMask"></div>
- <div class="sheet" id="sheet">
- <div class="sheet__hd">
- <div class="sheet__title" id="sheetTitle">批量通过</div>
- <button type="button" class="sheet__close" id="sheetClose">取消</button>
- </div>
- <p class="sheet__hint" id="sheetHint">将对 <strong>2</strong> 条待办执行操作</p>
- <textarea class="sheet__ta" id="sheetInput" placeholder="审批意见"></textarea>
- <div class="sheet__ft">
- <button type="button" class="sheet__btn is-cancel" id="sheetCancel">取消</button>
- <button type="button" class="sheet__btn is-ok" id="sheetConfirm">确定</button>
- </div>
- </div>
- <div class="toast" id="toast"></div>
- </div>
- </div>
- </div>
- </div>
- <div class="legend">
- <div class="box">
- <h3>意见规则</h3>
- 批量通过:默认填入「同意」,可改,可空(与单条一致)。<br />
- 批量打回:意见必填,占位「请填写打回意见(必填)」。
- </div>
- <div class="box">
- <h3>接口策略</h3>
- 暂无批量 API:前端对选中 taskId 串行调用<br />
- <code>POST /tasks/{id}/approve</code>、<code>/return</code>。<br />
- 结束后 toast:「成功 n,失败 m」。
- </div>
- <div class="box">
- <h3>边界</h3>
- 仅当前页已加载列表可勾选;不包含「拒绝」。<br />
- 若流程关闭打回(allowReturn=false),单条失败计入失败数。
- </div>
- </div>
- </div>
- <script>
- const TASKS = [
- {
- id: 't1',
- title: '请假申请',
- time: '今天 09:12',
- meta: ['请假类型:事假', '请假天数:1'],
- user: '王品龙',
- avatar: '王品'
- },
- {
- id: 't2',
- title: '费用报销',
- time: '昨天 18:40',
- meta: ['报销金额:1280.00', '事由:差旅交通'],
- user: '吴家猛',
- avatar: '吴家'
- },
- {
- id: 't3',
- title: '加班申请',
- time: '08-10',
- meta: ['加班时长:3小时', '加班日期:2026-08-09'],
- user: '郑德禄',
- avatar: '郑德'
- },
- {
- id: 't4',
- title: '出差申请',
- time: '08-09',
- meta: ['目的地:合肥', '出差天数:2'],
- user: '李明',
- avatar: '李明'
- }
- ]
- const state = {
- manage: false,
- selected: new Set(),
- sheetKind: null // approve | return
- }
- const page = document.getElementById('page')
- const listEl = document.getElementById('list')
- const manageBtn = document.getElementById('manageBtn')
- const batchBar = document.getElementById('batchBar')
- const batchHint = document.getElementById('batchHint')
- const btnApprove = document.getElementById('btnApprove')
- const btnReturn = document.getElementById('btnReturn')
- const sheet = document.getElementById('sheet')
- const sheetMask = document.getElementById('sheetMask')
- const sheetTitle = document.getElementById('sheetTitle')
- const sheetHint = document.getElementById('sheetHint')
- const sheetInput = document.getElementById('sheetInput')
- const sheetConfirm = document.getElementById('sheetConfirm')
- const toastEl = document.getElementById('toast')
- const sidePanel = document.getElementById('sidePanel')
- const SCENE_COPY = {
- browse: {
- title: '① 浏览态',
- sub: '日常查看 / 点卡片进详情审批',
- items: [
- '标题栏右侧「管理」(对齐草稿箱自定义顶栏)',
- '搜索区仅保留检索,不再放管理按钮',
- '点卡片仍进入审批详情(webview)'
- ],
- tip: '右侧手机可点标题栏「管理」亲自走一遍流程。',
- tipClass: 'pick'
- },
- manage: {
- title: '② 管理多选',
- sub: '勾选多条待办,底栏出现批量操作',
- items: [
- '标题栏按钮变为「完成」,点按退出并清空勾选',
- '左侧圆形勾选,选中卡片高亮描边',
- '底栏:已选 N 项 + 批量打回 + 批量通过',
- '未勾选时两个按钮禁用'
- ],
- tip: '主行动为「批量通过」(品牌绿);打回为次要警告色。',
- tipClass: 'ok'
- },
- approve: {
- title: '③ 批量通过弹层',
- sub: '统一意见,一次确认',
- items: [
- '标题「批量通过」,提示将对 N 条执行',
- '默认意见「同意」,可修改或清空',
- '确定后串行 approve,刷新列表'
- ],
- tip: '与详情页单条「同意」语义一致。',
- tipClass: 'ok'
- },
- return: {
- title: '④ 批量打回弹层',
- sub: '打回意见必填',
- items: [
- '标题「批量打回」,主按钮琥珀警告色',
- '意见为空时 toast「打回意见必填」',
- '部分失败时提示成功/失败条数'
- ],
- tip: '不在此 Demo 做「批量拒绝」,避免误操作风险。',
- tipClass: 'pick'
- }
- }
- function renderSide(scene) {
- const c = SCENE_COPY[scene]
- sidePanel.innerHTML =
- '<h2>' + c.title + '</h2>' +
- '<p class="sub">' + c.sub + '</p>' +
- '<ul>' + c.items.map((x) => '<li>' + x + '</li>').join('') + '</ul>' +
- '<div class="' + c.tipClass + '">' + c.tip + '</div>'
- }
- function renderList() {
- listEl.innerHTML = TASKS.map((t) => {
- const on = state.selected.has(t.id)
- return (
- '<div class="row" data-id="' + t.id + '">' +
- '<div class="check" data-check="' + t.id + '">' +
- '<div class="check__box' + (on ? ' on' : '') + '">' + (on ? '✓' : '') + '</div>' +
- '</div>' +
- '<div class="card' + (on ? ' is-selected' : '') + '" data-card="' + t.id + '">' +
- '<div class="card__head">' +
- '<div class="card__title">' + t.title + '</div>' +
- '<div class="card__time">' + t.time + '</div>' +
- '</div>' +
- '<div class="card__meta">' +
- t.meta.map((m) => '<span class="card__meta-line">' + m + '</span>').join('') +
- '</div>' +
- '<div class="card__foot">' +
- '<div class="card__user">' +
- '<div class="card__avatar">' + t.avatar + '</div>' +
- '<div class="card__user-text">由' + t.user + '提交</div>' +
- '</div>' +
- '<div class="card__status">进行中</div>' +
- '</div>' +
- '</div>' +
- '</div>'
- )
- }).join('')
- }
- function syncChrome() {
- page.classList.toggle('is-manage', state.manage)
- manageBtn.textContent = state.manage ? '完成' : '管理'
- manageBtn.classList.toggle('is-done', state.manage)
- const n = state.selected.size
- batchBar.classList.toggle('is-show', state.manage && n > 0)
- batchHint.textContent = '已选 ' + n + ' 项'
- btnApprove.disabled = n === 0
- btnReturn.disabled = n === 0
- }
- function toast(msg) {
- toastEl.textContent = msg
- toastEl.classList.add('is-show')
- clearTimeout(toast._t)
- toast._t = setTimeout(() => toastEl.classList.remove('is-show'), 1800)
- }
- function openSheet(kind) {
- const n = state.selected.size
- if (!n) return
- state.sheetKind = kind
- sheetTitle.textContent = kind === 'approve' ? '批量通过' : '批量打回'
- sheetHint.innerHTML = '将对 <strong>' + n + '</strong> 条待办执行操作'
- sheetInput.value = kind === 'approve' ? '同意' : ''
- sheetInput.placeholder =
- kind === 'approve' ? '审批意见(可选)' : '请填写打回意见(必填)'
- sheetConfirm.textContent = '确定'
- sheetConfirm.className =
- 'sheet__btn ' + (kind === 'approve' ? 'is-ok' : 'is-return')
- sheetMask.classList.add('is-show')
- sheet.classList.add('is-show')
- setTimeout(() => sheetInput.focus(), 80)
- }
- function closeSheet() {
- state.sheetKind = null
- sheetMask.classList.remove('is-show')
- sheet.classList.remove('is-show')
- }
- function setManage(on) {
- state.manage = on
- if (!on) state.selected.clear()
- renderList()
- syncChrome()
- }
- function applyScene(scene) {
- document.querySelectorAll('#sceneTabs .tab').forEach((el) => {
- el.classList.toggle('is-active', el.dataset.scene === scene)
- })
- renderSide(scene)
- closeSheet()
- if (scene === 'browse') {
- setManage(false)
- } else if (scene === 'manage') {
- state.selected = new Set(['t1', 't2'])
- setManage(true)
- } else if (scene === 'approve') {
- state.selected = new Set(['t1', 't2'])
- setManage(true)
- openSheet('approve')
- } else if (scene === 'return') {
- state.selected = new Set(['t1', 't3'])
- setManage(true)
- openSheet('return')
- }
- }
- manageBtn.addEventListener('click', () => setManage(!state.manage))
- listEl.addEventListener('click', (e) => {
- const check = e.target.closest('[data-check]')
- const card = e.target.closest('[data-card]')
- const id = (check && check.dataset.check) || (card && card.dataset.card)
- if (!id) return
- if (state.manage) {
- if (state.selected.has(id)) state.selected.delete(id)
- else state.selected.add(id)
- renderList()
- syncChrome()
- return
- }
- if (card) toast('进入审批详情(Demo)')
- })
- btnApprove.addEventListener('click', () => openSheet('approve'))
- btnReturn.addEventListener('click', () => openSheet('return'))
- sheetMask.addEventListener('click', closeSheet)
- document.getElementById('sheetClose').addEventListener('click', closeSheet)
- document.getElementById('sheetCancel').addEventListener('click', closeSheet)
- sheetConfirm.addEventListener('click', () => {
- const kind = state.sheetKind
- const comment = String(sheetInput.value || '').trim()
- const n = state.selected.size
- if (kind === 'return' && !comment) {
- toast('打回意见必填')
- return
- }
- closeSheet()
- const label = kind === 'approve' ? '通过' : '打回'
- toast('已' + label + ' ' + n + ' 条(Demo)')
- state.selected.clear()
- setManage(false)
- })
- document.getElementById('sceneTabs').addEventListener('click', (e) => {
- const tab = e.target.closest('.tab')
- if (!tab) return
- applyScene(tab.dataset.scene)
- })
- renderList()
- syncChrome()
- renderSide('browse')
- </script>
- </body>
- </html>
|