Files
zkxw/docs/mockups/oa-approval-dingtalk-screens-demo.html
T
2026-07-15 16:20:48 +08:00

655 lines
26 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>OA 审批 · 前端界面规划 Demo(Fresh Mint)</title>
<style>
:root {
--primary: #10b981;
--primary-dark: #059669;
--primary-bg: #ecfdf5;
--text: #0f172a;
--muted: #64748b;
--border: #e2e8f0;
--bg: #f1f5f9;
--card: #fff;
--warn: #d97706;
--danger: #dc2626;
--sidebar: 220px;
--top: 52px;
--radius: 10px;
}
* { box-sizing: border-box; }
body {
margin: 0;
font: 14px/1.5 Inter, system-ui, -apple-system, "PingFang SC", sans-serif;
color: var(--text);
background: var(--bg);
}
.shell { display: flex; min-height: 100vh; }
.sidebar {
width: var(--sidebar);
background: #fff;
border-right: 1px solid var(--border);
padding: 16px 0;
flex-shrink: 0;
}
.brand {
padding: 0 16px 16px;
font-weight: 700;
color: var(--primary-dark);
border-bottom: 1px solid var(--border);
margin-bottom: 8px;
}
.nav-group {
font-size: 11px;
color: var(--muted);
padding: 12px 16px 6px;
text-transform: uppercase;
letter-spacing: .04em;
}
.nav-item {
display: block;
width: 100%;
text-align: left;
border: 0;
background: transparent;
padding: 10px 16px;
cursor: pointer;
color: var(--text);
font-size: 14px;
}
.nav-item:hover { background: var(--primary-bg); }
.nav-item.active {
background: var(--primary-bg);
color: var(--primary-dark);
font-weight: 600;
border-right: 3px solid var(--primary);
}
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.topbar {
height: var(--top);
background: #fff;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
padding: 0 24px;
gap: 12px;
}
.topbar h1 { margin: 0; font-size: 16px; font-weight: 600; }
.badge {
font-size: 11px;
padding: 2px 8px;
border-radius: 999px;
background: var(--primary-bg);
color: var(--primary-dark);
}
.content { padding: 24px; flex: 1; overflow: auto; }
.panel {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 16px 20px;
margin-bottom: 16px;
}
.panel h2 {
margin: 0 0 12px;
font-size: 16px;
display: flex;
align-items: center;
gap: 8px;
}
.panel h2::before {
content: "";
width: 3px;
height: 16px;
background: var(--primary);
border-radius: 2px;
}
.muted { color: var(--muted); font-size: 13px; }
.toolbar {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 12px;
flex-wrap: wrap;
}
.btn {
border: 1px solid var(--border);
background: #fff;
border-radius: 8px;
padding: 7px 14px;
cursor: pointer;
font-size: 13px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.btn.primary {
background: var(--primary);
border-color: var(--primary);
color: #fff;
}
.btn.ghost { background: #fff; }
table { width: 100%; border-collapse: collapse; }
th, td {
text-align: left;
padding: 10px 12px;
border-bottom: 1px solid var(--border);
font-size: 13px;
}
th { color: var(--muted); font-weight: 500; background: #f8fafc; }
.tag {
display: inline-block;
padding: 2px 8px;
border-radius: 6px;
font-size: 12px;
background: var(--primary-bg);
color: var(--primary-dark);
}
.tag.warn { background: #fff7ed; color: var(--warn); }
.tag.info { background: #f1f5f9; color: var(--muted); }
.grid-apps {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 12px;
}
.app-card {
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px 12px;
text-align: center;
cursor: pointer;
background: #fff;
}
.app-card:hover { border-color: var(--primary); background: var(--primary-bg); }
.app-icon {
width: 48px; height: 48px; margin: 0 auto 8px;
border-radius: 12px; background: #fef3c7;
display: flex; align-items: center; justify-content: center;
font-size: 22px;
}
.steps {
display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px;
}
.step {
padding: 6px 12px; border-radius: 8px; background: #f8fafc;
border: 1px solid var(--border); font-size: 13px; cursor: pointer;
}
.step.on {
background: var(--primary-bg);
border-color: #a7f3d0;
color: var(--primary-dark);
font-weight: 600;
}
.form-stack label {
display: block; font-size: 12px; color: var(--muted); margin: 12px 0 4px;
}
.form-stack input, .form-stack textarea, .form-stack select {
width: 100%; max-width: 480px;
border: 1px solid var(--border);
border-radius: 8px;
padding: 8px 10px;
font: inherit;
}
.designer {
display: grid;
grid-template-columns: 180px 1fr 220px;
gap: 0;
border: 1px solid var(--border);
border-radius: 10px;
min-height: 360px;
overflow: hidden;
}
.designer aside {
background: #f8fafc;
border-right: 1px solid var(--border);
padding: 12px;
font-size: 12px;
}
.designer aside:last-child { border-right: 0; border-left: 1px solid var(--border); }
.ctrl {
display: inline-block;
margin: 4px;
padding: 6px 8px;
background: #fff;
border: 1px solid var(--border);
border-radius: 6px;
}
.phone {
width: 280px;
margin: 16px auto;
border: 1px solid var(--border);
border-radius: 16px;
background: #fff;
padding: 12px;
min-height: 280px;
box-shadow: 0 8px 24px rgba(15, 23, 42, .06);
}
.flow {
display: flex;
flex-direction: column;
align-items: center;
gap: 0;
padding: 24px;
background: #f8fafc;
border-radius: 10px;
}
.flow-node {
width: 220px;
border-radius: 8px;
overflow: hidden;
border: 1px solid var(--border);
background: #fff;
box-shadow: 0 1px 2px rgba(0,0,0,.04);
}
.flow-node .hd { padding: 8px 12px; color: #fff; font-size: 13px; font-weight: 600; }
.flow-node .bd { padding: 10px 12px; font-size: 13px; color: var(--muted); }
.flow-node.start .hd { background: #0f766e; }
.flow-node.approval .hd { background: #d97706; }
.flow-node.cc .hd { background: #0891b2; }
.flow-end {
margin-top: 8px;
padding: 6px 16px;
border-radius: 999px;
background: #e2e8f0;
font-size: 12px;
color: var(--muted);
}
.plus {
width: 24px; height: 24px; border-radius: 50%;
background: #fff; border: 1px solid var(--primary);
color: var(--primary); display: flex; align-items: center; justify-content: center;
margin: 8px 0; font-weight: 700;
}
.split-detail {
display: grid;
grid-template-columns: 1.2fr .8fr;
gap: 16px;
}
.timeline { border-left: 2px solid #a7f3d0; padding-left: 16px; margin-left: 8px; }
.tl-item { margin-bottom: 16px; position: relative; }
.tl-item::before {
content: "";
width: 10px; height: 10px; border-radius: 50%;
background: var(--primary);
position: absolute; left: -22px; top: 4px;
}
.overview {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 12px;
}
.ov-card {
border: 1px solid var(--border);
border-radius: 10px;
padding: 14px;
background: #fff;
cursor: pointer;
}
.ov-card:hover { border-color: var(--primary); }
.ov-card b { display: block; margin-bottom: 4px; }
.ov-num { font-size: 22px; font-weight: 700; color: var(--primary-dark); }
.screen { display: none; }
.screen.on { display: block; }
.note {
background: #fffbeb;
border: 1px solid #fde68a;
border-radius: 8px;
padding: 10px 12px;
font-size: 13px;
margin-bottom: 16px;
color: #92400e;
}
@media (max-width: 900px) {
.designer { grid-template-columns: 1fr; }
.split-detail { grid-template-columns: 1fr; }
.sidebar { width: 180px; }
}
</style>
</head>
<body>
<div class="shell">
<aside class="sidebar">
<div class="brand">OA 审批 Demo</div>
<div class="nav-group">总览</div>
<button class="nav-item active" data-screen="overview">界面规划总览</button>
<div class="nav-group">配置侧(管理)</div>
<button class="nav-item" data-screen="apps">① 审批管理</button>
<button class="nav-item" data-screen="roles">② 工作流角色</button>
<button class="nav-item" data-screen="designer">③ 四步设计器</button>
<div class="nav-group">办理侧(全员)</div>
<button class="nav-item" data-screen="start">④ 发起审批</button>
<button class="nav-item" data-screen="pending">⑤ 待处理</button>
<button class="nav-item" data-screen="done">⑥ 已处理</button>
<button class="nav-item" data-screen="mine">⑦ 我发起的</button>
<button class="nav-item" data-screen="cc">⑧ 抄送我的</button>
<button class="nav-item" data-screen="detail">⑨ 审批详情</button>
</aside>
<div class="main">
<header class="topbar">
<h1 id="title">界面规划总览</h1>
<span class="badge">Fresh Mint · 仿钉钉交互</span>
</header>
<div class="content">
<!-- OVERVIEW -->
<div class="screen on" id="screen-overview">
<div class="note">
产品前端规划:侧栏 <b>7</b> 个菜单入口 + 配置 <b>4</b> 步设计器 + <b>1</b> 个详情页(无独立菜单)= <b>共 12 个主要界面视图</b>。
下方可点卡片跳转各 Demo 屏(线框级示意,非像素级还原)。
</div>
<div class="panel">
<h2>配置侧 · 4 个入口 / 视图</h2>
<div class="overview">
<div class="ov-card" data-go="apps"><span class="ov-num">01</span><b>审批管理</b><span class="muted">应用列表,新增/编辑进入设计器</span></div>
<div class="ov-card" data-go="roles"><span class="ov-num">02</span><b>工作流角色</b><span class="muted">自定义角色 + 预置总经理/董事长</span></div>
<div class="ov-card" data-go="designer"><span class="ov-num">03–06</span><b>四步设计器</b><span class="muted">基础 → 表单 → 流程 → 高级</span></div>
</div>
</div>
<div class="panel">
<h2>办理侧 · 6 个入口 / 视图</h2>
<div class="overview">
<div class="ov-card" data-go="start"><span class="ov-num">07</span><b>发起审批</b><span class="muted">模板宫格 → 填单提交</span></div>
<div class="ov-card" data-go="pending"><span class="ov-num">08</span><b>待处理</b><span class="muted">我的审批待办列表</span></div>
<div class="ov-card" data-go="done"><span class="ov-num">09</span><b>已处理</b><span class="muted">我已办过的任务</span></div>
<div class="ov-card" data-go="mine"><span class="ov-num">10</span><b>我发起的</b><span class="muted">我提交的申请</span></div>
<div class="ov-card" data-go="cc"><span class="ov-num">11</span><b>抄送我的</b><span class="muted">知会只读</span></div>
<div class="ov-card" data-go="detail"><span class="ov-num">12</span><b>审批详情</b><span class="muted">表单+进度+操作(无菜单)</span></div>
</div>
</div>
</div>
<!-- APPS -->
<div class="screen" id="screen-apps">
<div class="panel">
<h2>审批管理</h2>
<p class="muted">管理系统内所有审批应用(模板)。点「新建」或「设计」进入四步配置。</p>
<div class="toolbar">
<span class="muted">已选 0 项</span>
<button class="btn primary" data-go="designer">+ 新建审批</button>
<button class="btn">批量停用</button>
</div>
<table>
<thead>
<tr><th>应用名称</th><th>分组</th><th>状态</th><th>更新时间</th><th>操作</th></tr>
</thead>
<tbody>
<tr>
<td>请假申请</td><td>人事</td><td><span class="tag">已发布</span></td><td>2026-07-14</td>
<td><a href="#" data-go="designer">设计</a> · 停用</td>
</tr>
<tr>
<td>用章申请</td><td>行政</td><td><span class="tag warn">草稿</span></td><td>2026-07-13</td>
<td><a href="#" data-go="designer">设计</a> · 发布</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ROLES -->
<div class="screen" id="screen-roles">
<div class="panel">
<h2>工作流角色</h2>
<p class="muted">独立于系统菜单角色。预置「总经理 / 董事长」不可删,只维护成员;另可自定义如「财务审批」。</p>
<div class="toolbar">
<button class="btn primary">+ 新建角色</button>
</div>
<table>
<thead><tr><th>名称</th><th>编码</th><th>类型</th><th>成员数</th><th>操作</th></tr></thead>
<tbody>
<tr><td>总经理</td><td>GM</td><td><span class="tag">预置</span></td><td>1</td><td>维护成员</td></tr>
<tr><td>董事长</td><td>CHAIRMAN</td><td><span class="tag">预置</span></td><td>1</td><td>维护成员</td></tr>
<tr><td>财务审批</td><td>FINANCE</td><td><span class="tag info">自定义</span></td><td>3</td><td>编辑 · 成员 · 删除</td></tr>
</tbody>
</table>
</div>
</div>
<!-- DESIGNER -->
<div class="screen" id="screen-designer">
<div class="panel">
<div class="toolbar" style="justify-content:space-between">
<div class="steps" id="designer-steps">
<div class="step on" data-step="1">① 基础设置</div>
<div class="step" data-step="2">② 表单设计</div>
<div class="step" data-step="3">③ 流程设计</div>
<div class="step" data-step="4">④ 高级设置</div>
</div>
<div>
<button class="btn">预览</button>
<button class="btn primary">发布</button>
</div>
</div>
<div id="dstep-1">
<h2>基础设置</h2>
<div class="form-stack">
<label>图标 / 表单名称 *</label>
<input value="请假申请" maxlength="50" />
<label>所在分组 *</label>
<select><option>人事</option><option>行政</option></select>
<label>表单说明</label>
<textarea rows="2" placeholder="请输入"></textarea>
<label>谁可以发起 *</label>
<div><label style="display:inline"><input type="radio" checked name="who"/> 全部</label>
&nbsp;<label style="display:inline"><input type="radio" name="who"/> 指定成员</label></div>
<label>表单管理员 *</label>
<div><label style="display:inline"><input type="radio" checked name="adm"/> 全部 OA 管理员</label>
&nbsp;<label style="display:inline"><input type="radio" name="adm"/> 指定管理员</label></div>
</div>
</div>
<div id="dstep-2" style="display:none">
<h2>表单设计</h2>
<p class="muted">左控件库 · 中画布(PC/手机)· 右属性。Web/移动共用 Schema。</p>
<div class="designer">
<aside>
<div class="muted">布局</div>
<span class="ctrl">分栏</span>
<div class="muted" style="margin-top:8px">基础</div>
<span class="ctrl">单行</span><span class="ctrl">多行</span><span class="ctrl">数字</span>
<span class="ctrl">单选</span><span class="ctrl">多选</span><span class="ctrl">日期</span>
<span class="ctrl">电话</span>
<div class="muted" style="margin-top:8px">增强</div>
<span class="ctrl">附件</span><span class="ctrl">金额</span><span class="ctrl">部门</span>
<span class="ctrl">省市区</span><span class="ctrl">公式</span><span class="ctrl">关联审批</span>
</aside>
<div>
<div class="toolbar" style="padding:8px 12px;border-bottom:1px solid var(--border)">
<button class="btn primary" style="padding:4px 10px">手机</button>
<button class="btn" style="padding:4px 10px">PC</button>
</div>
<div class="phone">
<div style="margin-bottom:12px"><span class="muted">请假类型</span><br/>年假 ▾</div>
<div style="margin-bottom:12px"><span class="muted">开始日期</span><br/>2026-07-20</div>
<div style="border:1px dashed #a7f3d0;border-radius:8px;padding:20px;text-align:center;color:var(--muted)">拖拽控件到此处</div>
</div>
</div>
<aside>
<b>单行输入框</b>
<label style="display:block;margin-top:8px" class="muted">标题</label>
<input style="width:100%" value="事由" />
<label style="display:block;margin-top:8px" class="muted">必填</label>
<input type="checkbox" checked /> 是
</aside>
</div>
</div>
<div id="dstep-3" style="display:none">
<h2>流程设计</h2>
<p class="muted">竖向树:发起人 → 审批/抄送/条件 → 结束。选人:成员 / 工作流角色 / 部门负责人 / 主管 / 自选。</p>
<div class="flow">
<div class="flow-node start"><div class="hd">发起人</div><div class="bd">全员可发起 ›</div></div>
<div class="plus">+</div>
<div class="flow-node approval"><div class="hd">审批人</div><div class="bd">发起人部门负责人 · 或签 ›</div></div>
<div class="plus">+</div>
<div class="flow-node approval"><div class="hd">审批人</div><div class="bd">工作流角色 · 总经理 ›</div></div>
<div class="plus">+</div>
<div class="flow-node cc"><div class="hd">抄送人</div><div class="bd">发起人自选 ›</div></div>
<div class="flow-end">流程结束</div>
</div>
</div>
<div id="dstep-4" style="display:none">
<h2>高级设置</h2>
<div style="display:grid;grid-template-columns:200px 1fr;gap:16px">
<div style="border-right:1px solid var(--border);padding-right:12px">
<div style="padding:8px;background:var(--primary-bg);border-radius:8px;color:var(--primary-dark)">高级流程设置</div>
<div class="muted" style="padding:8px">数据标题(二期)</div>
<div class="muted" style="padding:8px">打印模板(二期)</div>
</div>
<div class="form-stack">
<label><input type="checkbox" checked /> 允许撤销进行中的审批</label>
<label><input type="checkbox" /> 审批人去重</label>
<label><input type="checkbox" checked /> 允许加签</label>
<label><input type="checkbox" checked /> 无直属主管时回退部门负责人(默认开)</label>
<label>审批意见</label>
<select><option>可不填,展示意见页</option><option>必须填写</option></select>
</div>
</div>
</div>
</div>
</div>
<!-- START -->
<div class="screen" id="screen-start">
<div class="panel">
<h2>发起审批</h2>
<p class="muted">按分组展示模板 → 打开填单(Schema 渲染,含附件)→ 提交。</p>
<p style="font-weight:600;margin:12px 0 8px">人事</p>
<div class="grid-apps">
<div class="app-card" data-go="detail"><div class="app-icon">📋</div>请假申请</div>
<div class="app-card"><div class="app-icon">📝</div>加班申请</div>
</div>
<p style="font-weight:600;margin:16px 0 8px">行政</p>
<div class="grid-apps">
<div class="app-card"><div class="app-icon">🔏</div>用章申请</div>
</div>
</div>
</div>
<!-- PENDING -->
<div class="screen" id="screen-pending">
<div class="panel">
<h2>待处理</h2>
<p class="muted">当前用户待审批任务。点进详情可同意/拒绝/加签。</p>
<table>
<thead><tr><th>标题</th><th>发起人</th><th>类型</th><th>到达时间</th><th></th></tr></thead>
<tbody>
<tr><td>张三的请假申请</td><td>张三</td><td>请假</td><td>07-14 10:20</td><td><a href="#" data-go="detail">处理</a></td></tr>
<tr><td>李四的用章申请</td><td>李四</td><td>用章</td><td>07-14 09:01</td><td><a href="#" data-go="detail">处理</a></td></tr>
</tbody>
</table>
</div>
</div>
<!-- DONE / MINE / CC -->
<div class="screen" id="screen-done">
<div class="panel">
<h2>已处理</h2>
<p class="muted">我已审批过的任务,详情只读。</p>
<table>
<thead><tr><th>标题</th><th>结论</th><th>处理时间</th></tr></thead>
<tbody>
<tr><td>王五的请假</td><td><span class="tag">已同意</span></td><td>07-13 18:00</td></tr>
</tbody>
</table>
</div>
</div>
<div class="screen" id="screen-mine">
<div class="panel">
<h2>我发起的</h2>
<p class="muted">我提交的申请;按高级设置可撤销进行中的单。</p>
<table>
<thead><tr><th>标题</th><th>状态</th><th>发起时间</th><th></th></tr></thead>
<tbody>
<tr><td>我的请假申请</td><td><span class="tag warn">审批中</span></td><td>07-14 11:00</td><td>撤销 · <a href="#" data-go="detail">详情</a></td></tr>
</tbody>
</table>
</div>
</div>
<div class="screen" id="screen-cc">
<div class="panel">
<h2>抄送我的</h2>
<p class="muted">知会类任务,只读查看。</p>
<table>
<thead><tr><th>标题</th><th>发起人</th><th>抄送时间</th></tr></thead>
<tbody>
<tr><td>赵六的用章申请</td><td>赵六</td><td>07-12 15:30</td></tr>
</tbody>
</table>
</div>
</div>
<!-- DETAIL -->
<div class="screen" id="screen-detail">
<div class="panel">
<h2>审批详情</h2>
<p class="muted">无独立侧栏菜单,从列表进入。表单回显 + 进度时间轴 + 操作区。</p>
<div class="split-detail">
<div>
<div style="margin-bottom:12px"><b>请假类型</b><div>年假</div></div>
<div style="margin-bottom:12px"><b>日期</b><div>2026-07-20 ~ 2026-07-22</div></div>
<div style="margin-bottom:12px"><b>事由</b><div>回家办事</div></div>
<div style="margin-bottom:12px"><b>附件</b><div class="muted">行程单.pdf(预览 / 下载)</div></div>
<div class="toolbar" style="margin-top:20px">
<button class="btn primary">同意</button>
<button class="btn">拒绝</button>
<button class="btn">加签</button>
</div>
</div>
<div>
<b>审批进度</b>
<div class="timeline" style="margin-top:12px">
<div class="tl-item"><b>发起人 · 张三</b><div class="muted">07-14 10:00 已提交</div></div>
<div class="tl-item"><b>部门负责人 · 赵六</b><div class="muted">待处理(部门负责人)</div></div>
<div class="tl-item"><b>总经理</b><div class="muted">未到达</div></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
const titles = {
overview: '界面规划总览',
apps: '① 审批管理',
roles: '② 工作流角色',
designer: '③ 四步设计器',
start: '④ 发起审批',
pending: '⑤ 待处理',
done: '⑥ 已处理',
mine: '⑦ 我发起的',
cc: '⑧ 抄送我的',
detail: '⑨ 审批详情'
};
function show(id) {
document.querySelectorAll('.screen').forEach(el => el.classList.toggle('on', el.id === 'screen-' + id));
document.querySelectorAll('.nav-item').forEach(el => el.classList.toggle('active', el.dataset.screen === id));
document.getElementById('title').textContent = titles[id] || id;
}
document.querySelectorAll('[data-screen]').forEach(btn => {
btn.addEventListener('click', () => show(btn.dataset.screen));
});
document.querySelectorAll('[data-go]').forEach(el => {
el.addEventListener('click', (e) => { e.preventDefault(); show(el.dataset.go); });
});
document.querySelectorAll('#designer-steps .step').forEach(step => {
step.addEventListener('click', () => {
document.querySelectorAll('#designer-steps .step').forEach(s => s.classList.remove('on'));
step.classList.add('on');
for (let i = 1; i <= 4; i++) {
document.getElementById('dstep-' + i).style.display = (String(i) === step.dataset.step) ? 'block' : 'none';
}
});
});
</script>
</body>
</html>