用纯HTML+少量JS可实现工单提交页的交互原型:通过表单属性(required/pattern/type="email")和JS控制(禁用按钮、localStorage存草稿与工单、URL参数查单、状态映射与高亮),在无后端时模拟真实体验。

怎么用纯 HTML + 少量 JS 实现可交互的工单提交页
纯静态 HTML 无法真正提交数据,但可以模拟表单结构、校验逻辑和本地状态反馈,适合原型演示或对接后端前的界面验证。关键不是“做完”,而是“让字段行为符合业务直觉”。
常见错误是把 <form></form> 当成提交终点,其实它只是容器;没加 required 或 pattern 导致空提交、格式错漏;下拉选项写死却没设默认值,用户一提交就卡在未选状态。
-
<select></select>必须带<option value="" disabled selected>请选择问题类型</option>,否则首次提交会把空字符串当有效值 - 邮箱字段用
type="email"+required,浏览器自动提示格式错误,比 JS 校验更轻量 - 描述文本域加
maxlength="2000",防止前端过长内容导致后端截断或报错 - 提交按钮加
disabled状态 + 文字临时改为“提交中…”,用 JS 控制,避免重复点击
用 localStorage 模拟工单号生成与进度存储
没有后端时,localStorage 是最简单的“伪持久化”方案,能支撑页面刷新后查历史记录。但它不加密、无过期、跨域不可读——仅限演示。
容易踩的坑:直接用 Date.now() 当工单号,看起来唯一,但并发提交时可能重复;用 JSON.stringify() 存对象没问题,但取出来必须 JSON.parse(),否则是字符串不是对象。
立即学习“前端免费学习笔记(深入)”;
- 生成工单号建议拼接:
`TK-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,降低冲突概率 - 每条工单存为对象,键名统一用
ticketId、title、status(值为"待处理"/"处理中"/"已解决")、createdAt - 查询页读取时,用
Object.values(JSON.parse(localStorage.getItem('tickets') || '{}'))转成数组,方便filter和map - 不要用
localStorage.setItem('tickets', [...old, newTicket]),会丢失 key,应先parse再 push 再stringify
进度查询页如何按状态分类并高亮当前工单
用户最关心“我的单在哪一步”,所以不能只平铺列表,得按 status 分组,且当前查看的工单要视觉突出。纯 CSS 可实现基础高亮,但动态匹配需 JS。
典型问题:用 id 做 URL 参数(如 ?id=TK-171xxxxx),但没做防错处理,参数缺失或非法时页面白屏或报错;状态文案硬编码在 HTML 里,后期改“处理中”为“技术审核中”就得全量替换。
- URL 参数解析用
new URLSearchParams(window.location.search).get('id'),返回null时默认展示全部工单 - 状态显示用映射对象:
const statusMap = { 'pending': '待处理', 'processing': '处理中', 'resolved': '已解决' },HTML 中用statusMap[ticket.status] || ticket.status - 当前工单高亮靠添加 class:
ticket.ticketId === targetId ? 'active-ticket' : '',CSS 里定义.active-ticket { border-left: 4px solid #007bff; background-color: #f8f9fa; } - “更新进度”按钮只对非“已解决”状态显示,避免误操作
为什么不用框架而坚持原生 HTML/JS
因为工单页本质是表单+列表+状态流转,DOM 操作足够清晰。引入 Vue/React 反而要配打包、路由、状态管理,小项目里 80% 的代码都在填框架约定,而非解决业务问题。
真正复杂的是状态同步逻辑:比如用户在提交页填了一半关掉页面,再进来要恢复;或者多个标签页同时操作 localStorage 导致覆盖。这些不是框架能自动解决的,得靠 beforeunload 事件存草稿、用 storage 事件监听变更。
- 草稿保存:监听
input和change事件,节流 500ms 后写入localStorage.setItem('ticket-draft', JSON.stringify(formData)) - 恢复草稿:页面加载时检查
localStorage.getItem('ticket-draft'),存在则填充表单并删掉该 key - 多页同步:监听
window.addEventListener('storage', e => { if (e.key === 'tickets') location.reload(); }),强制刷新列表
这些细节不显眼,但用户感知最强:关掉页面再打开还能接着填,查进度时新提交的单立刻出现——这才是“像真的系统”的临界点。


















