Files
zkxw/docs/demos/oa-form-designer-outline-styles.html
T
zlwuandCursor de22678c87 feat: ship OA drafts/badges, role dual-track, and test-env deploy tooling.
Add approval instance drafts with menu badges, dual-track user/role read model and role bound names, form-designer outline docs, and bare-metal test deploy scripts. Ignore local test-server credential file.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 13:58:47 +08:00

685 lines
22 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 表单设计器 · 画布描边方案对比</title>
<style>
:root {
--mint: #10b981;
--mint-dark: #059669;
--mint-bg: #ecfdf5;
--mint-soft: rgba(16, 185, 129, 0.35);
--mint-softer: rgba(16, 185, 129, 0.18);
--slate-50: #f8fafc;
--slate-100: #f1f5f9;
--slate-200: #e2e8f0;
--slate-400: #94a3b8;
--slate-500: #64748b;
--slate-700: #334155;
--slate-900: #0f172a;
--blue: #2e73ff;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: "PingFang SC", "Helvetica Neue", Arial, sans-serif;
color: var(--slate-900);
background: linear-gradient(180deg, #f0fdf9 0%, var(--slate-50) 40%, #fff 100%);
line-height: 1.5;
}
.page {
max-width: 1180px;
margin: 0 auto;
padding: 32px 24px 64px;
}
header {
margin-bottom: 28px;
}
header h1 {
margin: 0 0 8px;
font-size: 24px;
font-weight: 700;
letter-spacing: -0.02em;
}
header p {
margin: 0;
color: var(--slate-500);
font-size: 14px;
max-width: 720px;
}
.hint {
display: inline-flex;
align-items: center;
gap: 6px;
margin-top: 12px;
padding: 6px 12px;
border-radius: 8px;
background: var(--mint-bg);
color: var(--mint-dark);
font-size: 13px;
font-weight: 500;
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
@media (max-width: 900px) {
.grid { grid-template-columns: 1fr; }
}
.card {
background: #fff;
border: 1px solid var(--slate-200);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
transition: box-shadow 0.2s, border-color 0.2s;
cursor: pointer;
}
.card:hover {
border-color: #a7f3d0;
box-shadow: 0 8px 24px rgba(16, 185, 129, 0.1);
}
.card.selected {
border-color: var(--mint);
box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.25);
}
.card-head {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 14px 16px 12px;
border-bottom: 1px solid var(--slate-100);
background: var(--slate-50);
}
.badge {
flex-shrink: 0;
width: 28px;
height: 28px;
border-radius: 8px;
background: var(--mint);
color: #fff;
font-size: 13px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
.card.baseline .badge { background: var(--slate-400); }
.card-head h2 {
margin: 0 0 2px;
font-size: 15px;
font-weight: 650;
}
.card-head p {
margin: 0;
font-size: 12px;
color: var(--slate-500);
}
.tag-row {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
}
.tag {
font-size: 11px;
padding: 2px 8px;
border-radius: 999px;
background: var(--slate-100);
color: var(--slate-500);
}
.tag.rec {
background: var(--mint-bg);
color: var(--mint-dark);
font-weight: 600;
}
.tag.warn {
background: #fff7ed;
color: #c2410c;
}
.canvas {
padding: 16px;
min-height: 220px;
background: #fafafa;
user-select: none;
}
.canvas-label {
font-size: 11px;
color: var(--slate-400);
margin-bottom: 10px;
letter-spacing: 0.02em;
}
/* —— 通用控件外观 —— */
.field {
padding: 8px 10px;
background: #fff;
border-radius: 4px;
}
.field-label {
font-size: 12px;
color: var(--slate-700);
margin-bottom: 6px;
}
.field-ctrl {
height: 32px;
border: 1px solid var(--slate-200);
border-radius: 6px;
background: #fff;
display: flex;
align-items: center;
padding: 0 10px;
font-size: 12px;
color: var(--slate-400);
}
.layout {
padding: 8px;
background: rgba(255, 255, 255, 0.6);
border-radius: 4px;
}
.layout-title {
font-size: 11px;
color: var(--slate-400);
margin-bottom: 8px;
padding-left: 2px;
}
.row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.stack > * + * { margin-top: 8px; }
/* DragTool 模拟:操作钮 */
.dt {
position: relative;
padding: 2px;
border-radius: 2px;
}
.dt-btns {
display: none;
position: absolute;
right: 2px;
bottom: 2px;
z-index: 5;
gap: 2px;
}
.dt-btns span {
width: 18px;
height: 18px;
background: var(--mint);
color: #fff;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 2px;
}
.dt.active > .dt-btns,
.dt:hover > .dt-btns { display: flex; }
/* ========== A 现状:始终虚线 ========== */
.style-a .dt {
outline: 1px dashed var(--mint);
}
.style-a .dt.active {
outline: 1px solid var(--mint);
}
.style-a .dt:hover:not(.active) {
outline-style: solid;
outline-color: var(--mint-soft);
}
/* ========== B 悬停才显:默认无框 ========== */
.style-b .dt {
outline: 1px dashed transparent;
}
.style-b .dt:hover {
outline-color: var(--mint-soft);
outline-style: solid;
}
.style-b .dt.active {
outline: 1px solid var(--mint);
}
/* ========== C 仅选中:最干净 ========== */
.style-c .dt {
outline: 1px solid transparent;
}
.style-c .dt.active {
outline-color: var(--mint);
}
.style-c .dt:hover:not(.active) {
background: rgba(16, 185, 129, 0.04);
}
/* ========== D 淡化常驻:浅虚线 ========== */
.style-d .dt {
outline: 1px dashed var(--mint-softer);
}
.style-d .dt:hover {
outline-color: var(--mint-soft);
outline-style: solid;
}
.style-d .dt.active {
outline: 1px solid var(--mint);
}
/* ========== E 混合:布局淡框 + 字段悬停 ========== */
.style-e .dt.leaf {
outline: 1px dashed transparent;
}
.style-e .dt.leaf:hover {
outline-color: var(--mint-soft);
outline-style: solid;
}
.style-e .dt.leaf.active {
outline: 1px solid var(--mint);
}
.style-e .dt.container {
outline: 1px dashed var(--mint-softer);
}
.style-e .dt.container:hover {
outline-color: var(--mint-soft);
}
.style-e .dt.container.active {
outline: 1px solid var(--mint);
}
/* ========== F 父级指示条:左侧色条代替四周虚线 ========== */
.style-f .dt {
outline: none;
border-left: 2px solid transparent;
padding-left: 6px;
}
.style-f .dt.container {
border-left-color: var(--mint-softer);
background: rgba(16, 185, 129, 0.03);
}
.style-f .dt:hover {
border-left-color: var(--mint-soft);
background: rgba(16, 185, 129, 0.05);
}
.style-f .dt.active {
border-left-color: var(--mint);
background: rgba(16, 185, 129, 0.08);
box-shadow: inset 0 0 0 1px var(--mint);
}
footer {
margin-top: 28px;
padding: 16px 18px;
border-radius: 12px;
background: #fff;
border: 1px solid var(--slate-200);
font-size: 13px;
color: var(--slate-500);
}
footer strong { color: var(--slate-700); }
#choice {
margin-top: 8px;
font-weight: 600;
color: var(--mint-dark);
min-height: 1.5em;
}
</style>
</head>
<body>
<div class="page">
<header>
<h1>表单设计画布 · 描边方案对比</h1>
<p>
模拟「布局容器嵌套字段」场景。请在每个画布里<strong>点击选中</strong>、<strong>悬停</strong>感受差异。
选中整张卡片表示你倾向的方案(可多选对比后口头反馈)。
</p>
<div class="hint">色值对齐本项目 Mint:#10b981(已替换 FormCreate 默认蓝 #2E73FF)</div>
</header>
<div class="grid" id="options">
<!-- A -->
<article class="card baseline" data-choice="A">
<div class="card-head">
<div class="badge">A</div>
<div>
<h2>现状:始终蓝色/绿色虚线</h2>
<p>每个组件常驻 dashed outline,嵌套时多层叠加</p>
<div class="tag-row">
<span class="tag warn">噪音大</span>
<span class="tag">层级一目了然</span>
</div>
</div>
</div>
<div class="canvas style-a" data-demo>
<div class="canvas-label">点击组件试试 · 悬停看 hover</div>
<div class="stack">
<div class="dt container" tabindex="0">
<div class="layout">
<div class="layout-title">栅格布局</div>
<div class="row">
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">申请人</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">部门</div>
<div class="field-ctrl">请选择</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
<div class="dt-btns"><span>+</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">请假事由</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
</article>
<!-- B -->
<article class="card" data-choice="B">
<div class="card-head">
<div class="badge">B</div>
<div>
<h2>悬停显框</h2>
<p>默认无描边;悬停出现浅色实线;选中为实线主色</p>
<div class="tag-row">
<span class="tag rec">推荐候选</span>
<span class="tag">操作时才出现辅助线</span>
</div>
</div>
</div>
<div class="canvas style-b" data-demo>
<div class="canvas-label">默认干净 · 鼠标移上去才有框</div>
<div class="stack">
<div class="dt container" tabindex="0">
<div class="layout">
<div class="layout-title">栅格布局</div>
<div class="row">
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">申请人</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">部门</div>
<div class="field-ctrl">请选择</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
<div class="dt-btns"><span>+</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">请假事由</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
</article>
<!-- C -->
<article class="card" data-choice="C">
<div class="card-head">
<div class="badge">C</div>
<div>
<h2>仅选中显框</h2>
<p>默认完全无框;悬停仅微弱底色;只有选中才有实线</p>
<div class="tag-row">
<span class="tag">最干净</span>
<span class="tag">嵌套边界稍难辨认</span>
</div>
</div>
</div>
<div class="canvas style-c" data-demo>
<div class="canvas-label">几乎像真实表单 · 点一下才高亮</div>
<div class="stack">
<div class="dt container" tabindex="0">
<div class="layout">
<div class="layout-title">栅格布局</div>
<div class="row">
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">申请人</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">部门</div>
<div class="field-ctrl">请选择</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
<div class="dt-btns"><span>+</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">请假事由</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
</article>
<!-- D -->
<article class="card" data-choice="D">
<div class="card-head">
<div class="badge">D</div>
<div>
<h2>淡化常驻虚线</h2>
<p>保留常驻提示,但透明度大幅降低;选中再加重</p>
<div class="tag-row">
<span class="tag">改动最小</span>
<span class="tag">多层仍在,只是变淡</span>
</div>
</div>
</div>
<div class="canvas style-d" data-demo>
<div class="canvas-label">虚线还在,但不再抢眼</div>
<div class="stack">
<div class="dt container" tabindex="0">
<div class="layout">
<div class="layout-title">栅格布局</div>
<div class="row">
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">申请人</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">部门</div>
<div class="field-ctrl">请选择</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
<div class="dt-btns"><span>+</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">请假事由</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
</article>
<!-- E -->
<article class="card" data-choice="E">
<div class="card-head">
<div class="badge">E</div>
<div>
<h2>混合:布局淡框 + 字段悬停</h2>
<p>容器保留极淡虚线帮辨层级;普通字段默认无框</p>
<div class="tag-row">
<span class="tag rec">推荐候选</span>
<span class="tag">兼顾布局与干净</span>
</div>
</div>
</div>
<div class="canvas style-e" data-demo>
<div class="canvas-label">只有「栅格」常驻淡框 · 字段悬停才出</div>
<div class="stack">
<div class="dt container" tabindex="0">
<div class="layout">
<div class="layout-title">栅格布局</div>
<div class="row">
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">申请人</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">部门</div>
<div class="field-ctrl">请选择</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
<div class="dt-btns"><span>+</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">请假事由</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
</article>
<!-- F -->
<article class="card" data-choice="F">
<div class="card-head">
<div class="badge">F</div>
<div>
<h2>左侧指示条(无四周虚线)</h2>
<p>用左边色条 + 选中内描边,彻底去掉虚线网格感</p>
<div class="tag-row">
<span class="tag">视觉更现代</span>
<span class="tag">实现略多一点</span>
</div>
</div>
</div>
<div class="canvas style-f" data-demo>
<div class="canvas-label">无虚线框 · 左侧色条表示可编辑区域</div>
<div class="stack">
<div class="dt container" tabindex="0">
<div class="layout">
<div class="layout-title">栅格布局</div>
<div class="row">
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">申请人</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">部门</div>
<div class="field-ctrl">请选择</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
<div class="dt-btns"><span>+</span><span>×</span></div>
</div>
<div class="dt leaf" tabindex="0">
<div class="field">
<div class="field-label">请假事由</div>
<div class="field-ctrl">请输入</div>
</div>
<div class="dt-btns"><span>⧉</span><span>×</span></div>
</div>
</div>
</div>
</article>
</div>
<footer>
<strong>怎么选?</strong>
想尽快落地、改动小 → 看 B / D;要保留布局层级感 → 看 E;想最接近真实表单 → 看 C;想换交互隐喻 → 看 F。
<div id="choice">请点击卡片标题区域选择倾向方案…</div>
</footer>
</div>
<script>
// 画布内:点击选中组件(互斥)
document.querySelectorAll('[data-demo]').forEach((canvas) => {
canvas.addEventListener('click', (e) => {
const dt = e.target.closest('.dt')
if (!dt || !canvas.contains(dt)) return
e.stopPropagation()
canvas.querySelectorAll('.dt.active').forEach((el) => el.classList.remove('active'))
dt.classList.add('active')
})
})
// 卡片选择(方案偏好)
const choiceEl = document.getElementById('choice')
const selected = new Set()
document.querySelectorAll('.card').forEach((card) => {
card.addEventListener('click', (e) => {
// 若点在画布控件上,不切换卡片选中(避免冲突)
if (e.target.closest('[data-demo] .dt')) return
const key = card.dataset.choice
if (selected.has(key)) {
selected.delete(key)
card.classList.remove('selected')
} else {
selected.add(key)
card.classList.add('selected')
}
choiceEl.textContent = selected.size
? '当前倾向:方案 ' + [...selected].sort().join('、')
: '请点击卡片标题区域选择倾向方案…'
})
})
</script>
</body>
</html>