简介:本资源是一套基于纯前端技术实现的轻量级审批流程图可视化方案,面向Web前端初学者、企业信息化系统开发者及低代码流程设计人员,解决无插件依赖、快速搭建可交互审批流程图的核心需求。压缩包共17个文件,含3个CSS(负责节点样式、连线布局与响应式适配)、3个JS(含jQuery基础库与自定义拖拽/点击交互逻辑)、2个HTML(主流程页与示例页)及字体、图标、图片等辅助资源,整体仅75KB,加载高效、部署便捷。目前已有790人学习下载,适合希望理解流程图DOM动态构建、事件绑定与CSS定位协同机制的学习者。读者可直接运行HTML文件查看完整交互效果,深入分析ding.js中节点增删逻辑、ding.css中SVG连线渲染技巧,以及iconfont字体图标在流程状态标识中的实际应用,掌握不依赖第三方库的审批流程前端落地方法。
1. 审批流程 HTML 静态页面包:开箱即用的表单流、状态跳转与多角色视图,适合快速原型验证与离线演示
你有没有遇到过这样的场景:业务方急着要一个审批链路的可视化demo,但后端接口还没联调,数据库也没建好,连登录态都只是草稿?这时候扔出一个「审批流程html.zip」,双击打开就能看到带节点高亮、进度条、角色切换按钮、驳回/通过弹窗的完整交互——不是Figma线框图,不是PPT动画,是真能点、能选、能模拟流转的HTML页面。这个资源不是框架脚手架,也不是需要npm install的工程,它是一套纯前端静态实现的审批流程模拟系统:所有状态存在localStorage里,流程逻辑写在JavaScript里,样式用原生CSS控制,兼容Chrome/Firefox/Edge最新三个大版本。它不解决真实系统的权限校验或数据持久化,但能10分钟内让产品经理确认节点顺序、让开发对齐字段命名、让测试梳理异常分支。如果你正卡在需求评审后的“先跑通一版看看”阶段,或者需要给非技术干系人做无依赖演示,这个zip就是你的后悔药。
2. 结构解析与核心模块定位:从文件树到状态机,看清它如何用纯静态方式模拟审批流
2.1 文件组织与职责划分:5个HTML + 3个JS + 1个CSS的协作逻辑
解压审批流程html.zip后,你会看到如下结构(已去除无关隐藏文件):
├── index.html # 主入口:流程总览页(含全局进度条、当前节点高亮、操作按钮) ├── apply.html # 提交申请页:表单填写、附件上传模拟、提交触发 ├── approve-1.html # 审批人A页:显示申请人信息、审批意见输入框、通过/驳回按钮 ├── approve-2.html # 审批人B页:同上,但节点ID和权限标识不同 ├── complete.html # 流程结束页:显示最终状态、历史操作记录、重新开始链接 ├── js/ │ ├── main.js # 全局状态管理:读写localStorage、初始化流程、跳转路由 │ ├── flow-logic.js # 核心状态机:定义节点顺序、转移条件、驳回回退规则 │ └── utils.js # 工具函数:日期格式化、表单校验、弹窗控制 ├── css/ │ └── style.css # 响应式布局:节点连线SVG、状态卡片、按钮悬停动效 └── assets/ └── icons/ # 简洁线性图标(svg sprite),用于状态指示器提示:所有HTML页面共享同一套
main.js和flow-logic.js,通过<script src="js/main.js"></script>引入,避免代码重复。index.html中通过<div id="current-node">动态渲染当前节点内容,而非服务端模板渲染。
2.2 状态机设计:用JSON对象定义流程拓扑,而非硬编码if-else
真正的灵活性藏在flow-logic.js里。它没有用switch-case判断当前页面再决定下一步,而是把整个流程抽象为一个可配置的对象:
// js/flow-logic.js const APPROVAL_FLOW = { nodes: [ { id: 'apply', name: '提交申请', next: ['approve-1'], prev: [] }, { id: 'approve-1', name: '一级审批', next: ['approve-2', 'apply'], prev: ['apply'] }, { id: 'approve-2', name: '二级审批', next: ['complete'], prev: ['approve-1'] }, { id: 'complete', name: '流程完成', next: [], prev: ['approve-2'] } ], // 驳回时的特殊路径:从任意审批节点驳回,都回到apply页并清空历史 rejectTo: 'apply', // 当前状态存储键名(避免与其他项目冲突) storageKey: 'approval-flow-state-v1' };这个对象直接决定了:
apply.html点击“提交”后,自动跳转到approve-1.html;approve-1.html点击“驳回”,清除所有中间状态,跳回apply.html并重置表单;approve-2.html点击“通过”,跳转complete.html且禁用所有操作按钮。
参数说明:
next数组定义合法后继节点,prev数组定义合法前驱节点(用于校验返回路径),rejectTo是全局驳回目标。修改nodes数组即可增删节点,无需改任何HTML或JS逻辑——这是它能快速适配新流程的关键。
2.3 localStorage状态持久化:不依赖后端,也能记住你走到哪一步
main.js在页面加载时执行loadState(),从localStorage读取APPROVAL_FLOW.storageKey对应的JSON字符串:
// js/main.js function loadState() { const saved = localStorage.getItem(APPROVAL_FLOW.storageKey); if (saved) { try { const state = JSON.parse(saved); // 校验state结构是否匹配当前流程定义 if (state.currentNode && isValidNode(state.currentNode)) { return state; } } catch (e) { console.warn('Invalid state in localStorage, resetting...'); } } // 初始化:从第一个节点开始 return { currentNode: 'apply', history: [], formData: {} }; }history数组记录每一步操作(如{ from: 'apply', to: 'approve-1', action: 'submit', timestamp: '2024-06-15T10:22:33' }),formData保存表单字段。每次跳转前调用saveState()写入,确保刷新页面不丢失进度。
注意:localStorage容量有限(通常5MB),本包所有数据加起来不足10KB,但若你计划存大量附件base64,需自行替换为IndexedDB——这不是本包设计目标,但留了扩展钩子(
utils.js中saveLargeData()函数已预留占位)。
3. 快速定制指南:三步修改,让流程匹配你的实际业务节点与字段
3.1 修改流程节点:调整flow-logic.js中的nodes数组
假设你的实际流程是「申请人 → 部门主管 → 财务部 → 归档」,共4个环节。你需要:
- 打开
js/flow-logic.js,找到APPROVAL_FLOW.nodes数组; - 替换为以下内容(保持
id唯一且与HTML文件名一致):
nodes: [ { id: 'apply', name: '填写申请', next: ['dept-lead'], prev: [] }, { id: 'dept-lead', name: '部门主管审批', next: ['finance', 'apply'], prev: ['apply'] }, { id: 'finance', name: '财务部审核', next: ['archived'], prev: ['dept-lead'] }, { id: 'archived', name: '已归档', next: [], prev: ['finance'] } ], rejectTo: 'apply', storageKey: 'my-dept-approval-v1' // 修改key避免与旧流程冲突- 将对应HTML文件重命名为
dept-lead.html、finance.html、archived.html,并确保每个页面<body>中包含<div id="node-id"><!-- dept-lead.html --> <h1>部门主管审批</h1> <p class="desc">请审核申请人提交的报销单,确认金额与事由是否符合部门预算。</p> 表单字段(删除原“审批意见”改为“预算符合性”下拉框):
<label for="budget-fit">预算符合性</label> <select id="budget-fit" name="budget-fit" required> <option value="">请选择</option> <option value="yes">符合部门预算</option> <option value="no">超出预算,需调整</option> </select>操作按钮文案与绑定事件:
<button onclick="handleApprove('dept-lead', 'finance')">同意并转交财务</button> <button onclick="handleReject('dept-lead')">退回申请人</button>- 在项目根目录安装MSW:
npm init -y && npm install msw --save-dev - 创建
mocks/browser.js: - 在
main.js中启动worker(仅开发环境):
参数说明:
handleApprove(currentId, nextId)是main.js导出的通用函数,自动校验nextId是否在currentId的next列表中;handleReject(currentId)则强制跳转至APPROVAL_FLOW.rejectTo。
3.3 自定义样式与状态指示:用CSS变量控制节点视觉反馈
css/style.css顶部定义了4个CSS变量,控制整个流程的视觉基调:
:root { --primary-color: #2563eb; /* 主色调:节点高亮、按钮背景 */ --success-color: #10b981; /* 通过色:已完成节点边框 */ --warning-color: #f59e0b; /* 驳回色:驳回节点背景 */ --line-color: #94a3b8; /* 连线色:流程箭头颜色 */ }修改--primary-color即可一键切换主题色。节点状态通过><!-- 在index.html的节点列表中 --> <div class="node">.node[data-status="current"] { border-color: var(--primary-color); } .node[data-status="done"] { border-color: var(--success-color); background: #f1f5f9; } .node[data-status="rejected"] { background: var(--warning-color); color: white; }
技巧:若需为某个节点添加特殊样式(如财务节点加锁图标),可在
index.html中为该<div>添加><script src="js/utils.js"></script> <script src="js/flow-logic.js"></script> <script src="js/main.js"></script>
utils.js必须最先加载(提供基础工具),flow-logic.js次之(定义流程),main.js最后(依赖前两者)。4.2 现象:在
approve-1.html点击“驳回”,跳转回apply.html后,表单数据未清空原因:
handleReject()函数只重置了流程状态(currentNode和history),但未清空formData对象,导致apply.html加载时从localStorage读取到旧数据并填充表单。
解决:在main.js的handleReject()函数末尾添加:// 清空表单数据,避免驳回后残留 state.formData = {}; saveState(state); // 强制刷新apply.html以重置DOM if (targetPage === 'apply') { window.location.href = 'apply.html?r=' + Date.now(); // 加时间戳防缓存 }4.3 现象:流程走到
complete.html后,点击“重新开始”按钮,页面仍显示旧的申请人姓名和金额原因:
complete.html的“重新开始”链接是静态的<a href="index.html">,未触发main.js的状态重置逻辑,导致index.html加载时直接读取localStorage中的旧formData。
解决:将链接改为按钮并绑定JS事件:<!-- complete.html --> <button onclick="restartFlow()">重新开始审批</button>并在
main.js中添加:function restartFlow() { localStorage.removeItem(APPROVAL_FLOW.storageKey); window.location.href = 'index.html'; }4.4 现象:在Safari浏览器中,流程跳转后进度条不更新,节点高亮失效
原因:Safari对
localStorage的同步读写有更严格的策略,main.js中loadState()在DOM加载完成前执行,导致index.html中节点元素尚未渲染,document.querySelectorAll('.node')返回空集合。
解决:将状态加载与DOM渲染解耦,在DOMContentLoaded事件后执行:// main.js 末尾 document.addEventListener('DOMContentLoaded', function() { const state = loadState(); initPage(state); });同时确保
initPage()函数中所有DOM操作(如querySelector)都在此事件回调内执行。4.5 现象:修改
flow-logic.js后,本地双击打开HTML页面,流程完全不工作,所有按钮点击无响应原因:现代浏览器(Chrome/Firefox)出于安全限制,禁止本地
file://协议下的AJAX请求和部分localStorage操作。main.js尝试读取localStorage时被静默拒绝,loadState()返回undefined,后续逻辑全部中断。
解决:必须通过本地HTTP服务器访问。最简方案:# Python 3.x python3 -m http.server 8000 # 然后浏览器打开 http://localhost:8000/index.html或使用VS Code插件“Live Server”,右键
index.html选择“Open with Live Server”。这是本包唯一强制依赖的运行环境,务必牢记。5. 进阶技巧:嵌入真实表单验证、对接Mock API、生成流程图谱
5.1 在表单提交前注入业务校验:用
utils.js的钩子函数拦截非法数据
apply.html的提交按钮绑定的是handleSubmit(),它默认只检查必填项。若你的业务要求“报销金额必须大于100元且小于10000元”,可在utils.js中扩展校验:// js/utils.js function validateForm(formData) { // 原有必填校验... if (formData.amount && (parseFloat(formData.amount) < 100 || parseFloat(formData.amount) > 10000)) { showNotification('报销金额应在100-10000元之间', 'error'); return false; } // 新增:发票图片必须上传(模拟文件存在) if (!formData.invoiceFile) { showNotification('请上传发票图片', 'error'); return false; } return true; } // 在 handleSubmit() 中调用 if (!validateForm(formData)) return;参数说明:
showNotification(message, type)是utils.js内置函数,支持'success'/'error'/'info'类型,3秒后自动消失。formData对象由serializeForm()生成,字段名与表单name属性一致。5.2 用Mock Service Worker(MSW)模拟后端API,让静态页具备“伪联调”能力
虽然本包是纯静态,但你可以用 Mock Service Worker 让它假装调用真实API。步骤如下:
import { setupWorker, rest } from 'msw'; export const worker = setupWorker( rest.post('/api/submit', (req, res, ctx) => { // 模拟提交成功 return res(ctx.status(200), ctx.json({ success: true, taskId: 'TASK-2024-001' })); }), rest.get('/api/task/:id', (req, res, ctx) => { // 模拟查询任务状态 return res(ctx.status(200), ctx.json({ status: 'approved', approver: '张主管' })); }) );
if (location.hostname === 'localhost') { const { worker } = await import('./mocks/browser.js'); worker.start(); }效果:当
apply.html点击提交时,handleSubmit()可发起fetch('/api/submit', ...),MSW会拦截并返回模拟响应,前端拿到taskId后存入formData,后续页面即可用该ID查询状态——这比纯localStorage更贴近真实联调场景。5.3 从
flow-logic.js自动生成Mermaid流程图,实现文档与代码同步流程定义在
flow-logic.js中,但业务文档常需流程图。用一段脚本即可自动生成:# 保存为 generate-mermaid.sh #!/bin/bash echo "flowchart TD" > flow.mmd grep -oE "'[a-z-]+': \{[^}]*\}" js/flow-logic.js | while read node; do id=$(echo $node | grep -oE "'[a-z-]+'") name=$(echo $node | grep -oE "name: '[^']*'" | sed "s/name: '\([^']*\)'/\1/") next=$(echo $node | grep -oE "next: \[[^]]*\]" | sed "s/next: \[\([^]]*\)\]/\1/" | tr -d "'" | tr -d " ") echo " ${id//\'/}([\"${name}\"]) --> $next" >> flow.mmd done echo "Generated flow.mmd"运行后生成
flow.mmd,可用 Mermaid Live Editor 粘贴预览,或集成进VuePress/VitePress自动生成文档。这解决了“流程改了但文档没更新”的经典矛盾。从那以后我每次接到新审批流程需求,第一件事就是解压这个zip,跑通
python3 -m http.server,然后打开flow-logic.js,花3分钟把节点数组按需求重写——不是从零敲代码,而是像搭积木一样重组状态机。它不替代真实系统,但把“确认需求”这件事从3天压缩到30分钟。希望帮到你。本文还有配套的精品资源,点击获取