Regression / regression (push) Canceled after 0s
## 品牌标识(本次会话) 起因:品牌此前没有任何图形标识 —— 唯一 favicon 是内联 data-URI 里的字母「A」, 那是 v2.0.0「Aurora Admin → Kole UI」改名漏掉的一处(PC 顶栏也是「A」, 移动端站已是「K」;移动端文档站则完全没有 favicon)。 - 几何:24 网格三个互不接触的笔画(竖 + 两斜),圆头描边; 描边 2.25 → 16px 标签页尺寸下正好 1.5px = 规范原文「描边1.5px」 - 取色分两套(刻意):favicon 硬编码品牌蓝/白(渲染在浏览器标签栏,不继承 kole-dark); 顶栏标记走 currentColor(实测暗色下自动转 rgb(20,22,28)) - 新增 theme-color 双条(light #FFFFFF / dark #1C1F26,取 --kole-color-card-bg) - 修 site/app.js hero 标语 KOLE ADMIN → KOLE UI(改名变形残留) - 移动端 7 个模板补 favicon(此前计数 0) 验收:门禁 9 条全 OK(site-routing/site-routes/mobile-docs/mobile-site/isolation/ theme/nav/i18n/icons);PC 回归 1464/1464 · 移动端 807/807,各连跑 8 次一致; 两端 favicon 405 字节逐字节一致;PC 站控制台错误 1→0。 ## 并行会话成果(本次一并入库) - 图标系统:2576 图标(TDesign/Element Plus,MIT)+ 11 端注入 + 5 个构建门禁工具 + IconPreview 预览页 + ICON-SPEC.md 冻结规格 - 移动端平台:47 组件 × 6 端 + 文档站 53 页 + 隔离门禁 - PC 组件:103 个大后台组件 / 组件11 批次 - uni-app:PC 端试点 + 移动端端实现 + 真实编译验证 ## 工程 - .gitignore 补 .scratch/ 与 .zcode-preexisting-*.txt(会话中间产物,实测 9.1MB,不入库) - CHANGELOG 补品牌标识条目 - ROADMAP 登 S8-P4(品牌标识任务包 + og:image/apple-touch-icon 未做部分)
558 lines
29 KiB
HTML
558 lines
29 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>模板 · 系统设置 - Kole UI</title>
|
||
<meta name="description" content="Kole UI 模板页库 · 系统设置:侧边分区 + 表单 + 开关组 + 单选组 + 保存反馈,全部视觉来自设计令牌与 components.css。">
|
||
<link rel="stylesheet" href="../../.design_library/kole-ui/colors_and_type.css">
|
||
<link rel="stylesheet" href="../../.design_library/kole-ui/components.css">
|
||
<!-- 扩展组件样式:components.css 只聚合 6 个核心组件;下列组件各自的样式表按需引入 -->
|
||
<link rel="stylesheet" href="../../frameworks/Breadcrumb.css">
|
||
<link rel="stylesheet" href="../../frameworks/Segmented.css">
|
||
<link rel="stylesheet" href="../../frameworks/Radio.css">
|
||
<link rel="stylesheet" href="../../frameworks/Checkbox.css">
|
||
<link rel="stylesheet" href="../../frameworks/Switch.css">
|
||
<link rel="stylesheet" href="../../frameworks/MessagePro.css">
|
||
<style>
|
||
/* 模板页布局:仅排版类样式,组件视觉全部来自 colors_and_type.css 令牌 + components.css */
|
||
* { box-sizing: border-box; }
|
||
body {
|
||
margin: 0; background: var(--kole-color-page-bg);
|
||
font-family: var(--kole-font-family); color: var(--kole-color-text-body);
|
||
font-size: var(--kole-type-body-size); line-height: var(--kole-type-body-height);
|
||
}
|
||
a { color: var(--kole-color-brand); text-decoration: none; }
|
||
a:hover { color: var(--kole-color-brand-hover); }
|
||
|
||
.st-topbar {
|
||
height: 56px; background: var(--kole-color-card-bg); border-bottom: 1px solid var(--kole-color-border);
|
||
display: flex; align-items: center; gap: var(--kole-space-16); padding: 0 var(--kole-space-24);
|
||
position: sticky; top: 0; z-index: 20;
|
||
}
|
||
.st-mark {
|
||
width: 28px; height: 28px; border-radius: var(--kole-radius-large);
|
||
background: var(--kole-color-brand); color: var(--kole-color-text-inverse);
|
||
display: flex; align-items: center; justify-content: center; font-weight: 600; flex-shrink: 0;
|
||
}
|
||
.st-brand { font-size: var(--kole-type-h2-size); font-weight: var(--kole-type-h2-weight); color: var(--kole-color-text-title); }
|
||
.st-topnav { margin-left: auto; display: flex; gap: var(--kole-space-20); font-size: var(--kole-type-caption-size); }
|
||
.st-topnav a { color: var(--kole-color-text-secondary); }
|
||
.st-topnav a[aria-current="page"] { color: var(--kole-color-brand); font-weight: 500; }
|
||
|
||
/* 面包屑 */
|
||
.st-crumb { max-width: 1200px; margin: 0 auto; padding: var(--kole-space-16) var(--kole-space-24) 0; }
|
||
|
||
.st-shell { max-width: 1200px; margin: 0 auto; padding: var(--kole-space-16) var(--kole-space-24) var(--kole-space-32);
|
||
display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: var(--kole-space-24); align-items: start; }
|
||
|
||
/* 左侧分区导航 */
|
||
.st-sidenav { display: flex; flex-direction: column; gap: var(--kole-space-4); position: sticky; top: 72px; }
|
||
.st-sidenav button {
|
||
display: flex; align-items: center; height: var(--kole-control-height-default); padding: 0 var(--kole-space-12);
|
||
border: none; background: none; border-radius: var(--kole-radius-base); font-family: inherit;
|
||
font-size: var(--kole-type-body-size); color: var(--kole-color-text-body); cursor: pointer; text-align: left; width: 100%;
|
||
transition: background var(--kole-duration-fast) var(--kole-ease-standard);
|
||
}
|
||
.st-sidenav button:hover { background: var(--kole-color-brand-bg); }
|
||
.st-sidenav button[aria-selected="true"] { background: var(--kole-color-brand-bg); color: var(--kole-color-brand); font-weight: 500; }
|
||
|
||
/* 右侧内容 */
|
||
.st-panels { display: flex; flex-direction: column; gap: var(--kole-space-16); min-width: 0; }
|
||
.st-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--kole-space-16); flex-wrap: wrap; }
|
||
.st-title { margin: 0; font-size: var(--kole-type-display-size); line-height: var(--kole-type-display-height); font-weight: var(--kole-type-display-weight); color: var(--kole-color-text-title); }
|
||
.st-sub { margin: var(--kole-space-4) 0 0; font-size: var(--kole-type-caption-size); color: var(--kole-color-text-secondary); }
|
||
|
||
.st-card { display: flex; flex-direction: column; gap: var(--kole-space-16); }
|
||
.st-card + .st-card { margin-top: var(--kole-space-16); }
|
||
.st-card-title { font-size: var(--kole-type-h2-size); font-weight: var(--kole-type-h2-weight); color: var(--kole-color-text-title); }
|
||
.st-card-desc { margin: var(--kole-space-4) 0 0; font-size: var(--kole-type-caption-size); color: var(--kole-color-text-secondary); }
|
||
|
||
/* 表单行:左标签 + 右控件 */
|
||
.st-form { display: flex; flex-direction: column; gap: var(--kole-space-16); }
|
||
.st-row { display: grid; grid-template-columns: 180px minmax(0, 1fr); gap: var(--kole-space-16); align-items: start; }
|
||
.st-row > label, .st-row > .st-label { font-size: var(--kole-type-body-size); color: var(--kole-color-text-body); padding-top: 5px; }
|
||
.st-row .hint { margin: var(--kole-space-4) 0 0; font-size: var(--kole-type-caption-size); color: var(--kole-color-text-secondary); }
|
||
.st-control { display: flex; flex-direction: column; gap: var(--kole-space-4); min-width: 0; }
|
||
.st-control .kole-input { width: 100%; max-width: 380px; }
|
||
.st-control .kole-input input, .st-control .kole-input select { flex: 1; min-width: 0; }
|
||
.st-control select.native {
|
||
height: var(--kole-control-height-default); max-width: 380px; width: 100%;
|
||
border: 1px solid var(--kole-color-border); border-radius: var(--kole-radius-base);
|
||
background: var(--kole-color-card-bg); color: var(--kole-color-text-body);
|
||
font-family: inherit; font-size: var(--kole-type-body-size); padding: 0 var(--kole-space-8); cursor: pointer;
|
||
}
|
||
.st-control select.native:focus-visible { outline: 2px solid var(--kole-color-focus-ring); outline-offset: 1px; border-color: var(--kole-color-brand); }
|
||
|
||
/* 开关行(复用 Switch 组件样式 .kole-switch-switch) */
|
||
.st-switch-row { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--kole-space-16); padding: var(--kole-space-12) 0; border-bottom: 1px solid var(--kole-color-border); }
|
||
.st-switch-row:last-child { border-bottom: none; padding-bottom: 0; }
|
||
.st-switch-text strong { display: block; font-size: var(--kole-type-body-size); font-weight: 400; color: var(--kole-color-text-body); }
|
||
.st-switch-text span { display: block; margin-top: var(--kole-space-4); font-size: var(--kole-type-caption-size); color: var(--kole-color-text-secondary); }
|
||
.st-switch-row .kole-switch-switch { cursor: pointer; flex-shrink: 0; }
|
||
.st-switch-row.is-locked .kole-switch-text { opacity: .6; }
|
||
|
||
/* 单选 / 复选组(复用 Radio / Checkbox 组件样式) */
|
||
.st-group { display: flex; flex-wrap: wrap; gap: var(--kole-space-16); }
|
||
.kole-radio-option, .kole-checkbox-option {
|
||
display: inline-flex; align-items: center; gap: var(--kole-space-8);
|
||
font-size: var(--kole-type-body-size); color: var(--kole-color-text-body); cursor: pointer;
|
||
}
|
||
.kole-radio-option input, .kole-checkbox-option input {
|
||
width: var(--kole-icon-size-16); height: var(--kole-icon-size-16); margin: 0;
|
||
accent-color: var(--kole-color-brand); cursor: pointer;
|
||
}
|
||
|
||
/* 底部保存栏 */
|
||
.st-footer {
|
||
position: sticky; bottom: 0; margin-top: var(--kole-space-16);
|
||
background: var(--kole-color-card-bg); border: 1px solid var(--kole-color-border);
|
||
border-radius: var(--kole-radius-large); box-shadow: var(--kole-shadow-low);
|
||
padding: var(--kole-space-12) var(--kole-space-16);
|
||
display: flex; align-items: center; justify-content: space-between; gap: var(--kole-space-12); flex-wrap: wrap;
|
||
}
|
||
.st-footer .dirty { font-size: var(--kole-type-caption-size); color: var(--kole-color-warning); }
|
||
.st-footer .saved { font-size: var(--kole-type-caption-size); color: var(--kole-color-text-secondary); }
|
||
.st-footer .btns { display: flex; gap: var(--kole-space-8); }
|
||
|
||
/* 危险区 */
|
||
.st-danger { border-color: var(--kole-color-error); }
|
||
.st-danger .st-card-title { color: var(--kole-color-error); }
|
||
|
||
[hidden] { display: none !important; }
|
||
|
||
@media (max-width: 900px) {
|
||
.st-shell { grid-template-columns: minmax(0, 1fr); }
|
||
.st-sidenav { position: static; flex-direction: row; flex-wrap: wrap; }
|
||
.st-sidenav button { width: auto; }
|
||
.st-row { grid-template-columns: minmax(0, 1fr); gap: var(--kole-space-4); }
|
||
.st-row > label, .st-row > .st-label { padding-top: 0; }
|
||
.st-topnav { display: none; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header class="st-topbar">
|
||
<span class="st-mark">K</span>
|
||
<span class="st-brand">Kole UI · 管理控制台</span>
|
||
<nav class="st-topnav">
|
||
<a href="dashboard.html">工作台</a>
|
||
<a href="order-list.html">订单列表</a>
|
||
<a href="user-management.html">用户管理</a>
|
||
<a href="settings.html" aria-current="page">系统设置</a>
|
||
</nav>
|
||
</header>
|
||
|
||
<nav class="st-crumb kole-breadcrumb" aria-label="面包屑">
|
||
<a class="kole-breadcrumb-item" href="dashboard.html">工作台</a>
|
||
<span class="kole-breadcrumb-sep">/</span>
|
||
<span class="kole-breadcrumb-item is-current" aria-current="page">系统设置</span>
|
||
</nav>
|
||
|
||
<div class="st-shell">
|
||
<nav class="st-sidenav" id="st-nav" role="tablist" aria-label="设置分区">
|
||
<button type="button" role="tab" aria-selected="true" data-panel="basic">基本信息</button>
|
||
<button type="button" role="tab" aria-selected="false" data-panel="security">安全策略</button>
|
||
<button type="button" role="tab" aria-selected="false" data-panel="notify">通知设置</button>
|
||
<button type="button" role="tab" aria-selected="false" data-panel="danger">危险操作</button>
|
||
</nav>
|
||
|
||
<main class="st-panels">
|
||
<div class="st-head">
|
||
<div>
|
||
<h1 class="st-title">系统设置</h1>
|
||
<p class="st-sub">模板页库示范:侧边分区 + 表单 + 开关组 + 单选组 + 保存反馈</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 基本信息 -->
|
||
<section class="st-panel" id="st-panel-basic" role="tabpanel" aria-label="基本信息">
|
||
<div class="card card-bordered card-p24">
|
||
<div class="st-card">
|
||
<div>
|
||
<h2 class="st-card-title">基本信息</h2>
|
||
<p class="st-card-desc">这些信息会出现在导出的报表页眉与通知邮件中。</p>
|
||
</div>
|
||
<div class="st-form">
|
||
<div class="st-row">
|
||
<label for="st-name">企业名称</label>
|
||
<div class="st-control">
|
||
<div class="kole-input"><input id="st-name" type="text" value="华信精密制造有限公司" placeholder="请输入企业全称"></div>
|
||
<p class="hint">将显示在报表页眉,建议填写工商登记全称</p>
|
||
</div>
|
||
</div>
|
||
<div class="st-row">
|
||
<label for="st-code">企业编码</label>
|
||
<div class="st-control">
|
||
<div class="kole-input"><input id="st-code" type="text" value="KOLE-8823" placeholder="请输入企业编码"></div>
|
||
<p class="hint">创建后不可修改,用于对接内部 ERP 系统</p>
|
||
</div>
|
||
</div>
|
||
<div class="st-row">
|
||
<span class="st-label" id="st-tz-label">时区</span>
|
||
<div class="st-control">
|
||
<select class="native" id="st-tz" aria-labelledby="st-tz-label">
|
||
<option value="Asia/Shanghai" selected>(UTC+08:00) 中国标准时间 · 北京</option>
|
||
<option value="Asia/Tokyo">(UTC+09:00) 日本标准时间 · 东京</option>
|
||
<option value="Europe/London">(UTC+00:00) 格林尼治标准时间 · 伦敦</option>
|
||
</select>
|
||
<p class="hint">影响报表统计口径与操作日志时间戳</p>
|
||
</div>
|
||
</div>
|
||
<div class="st-row">
|
||
<span class="st-label" id="st-lang-label">界面语言</span>
|
||
<div class="st-control">
|
||
<div class="st-group" role="radiogroup" aria-labelledby="st-lang-label">
|
||
<label class="kole-radio-option"><input type="radio" name="st-lang" value="zh" checked> 简体中文</label>
|
||
<label class="kole-radio-option"><input type="radio" name="st-lang" value="en"> English</label>
|
||
</div>
|
||
<p class="hint">新成员首次登录时的默认界面语言</p>
|
||
</div>
|
||
</div>
|
||
<div class="st-row">
|
||
<span class="st-label" id="st-density-label">信息密度</span>
|
||
<div class="st-control">
|
||
<div class="kole-segmented" role="group" aria-labelledby="st-density-label" id="st-density">
|
||
<button class="kole-segmented-item is-active" type="button" data-density="default">默认</button>
|
||
<button class="kole-segmented-item" type="button" data-density="compact">紧凑</button>
|
||
<button class="kole-segmented-item" type="button" data-density="loose">宽松</button>
|
||
</div>
|
||
<p class="hint">影响列表与表格的行高,不改动字号</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 安全策略 -->
|
||
<section class="st-panel" id="st-panel-security" role="tabpanel" aria-label="安全策略" hidden>
|
||
<div class="card card-bordered card-p24">
|
||
<div class="st-card">
|
||
<div>
|
||
<h2 class="st-card-title">登录与安全</h2>
|
||
<p class="st-card-desc">面向全企业的登录策略,保存后对新登录生效。</p>
|
||
</div>
|
||
<div class="st-form">
|
||
<div class="st-row">
|
||
<span class="st-label" id="st-pwd-label">密码强度</span>
|
||
<div class="st-control">
|
||
<div class="st-group" role="radiogroup" aria-labelledby="st-pwd-label">
|
||
<label class="kole-radio-option"><input type="radio" name="st-pwd" value="normal" checked> 标准(≥8 位)</label>
|
||
<label class="kole-radio-option"><input type="radio" name="st-pwd" value="strong"> 强(≥12 位含大小写与符号)</label>
|
||
</div>
|
||
<p class="hint">影响成员修改密码时的校验规则</p>
|
||
</div>
|
||
</div>
|
||
<div class="st-row">
|
||
<label for="st-timeout">登录有效期</label>
|
||
<div class="st-control">
|
||
<select class="native" id="st-timeout">
|
||
<option value="30">30 分钟</option>
|
||
<option value="120" selected>2 小时</option>
|
||
<option value="480">8 小时</option>
|
||
</select>
|
||
<p class="hint">超过该时长无操作需重新登录</p>
|
||
</div>
|
||
</div>
|
||
<div class="st-row">
|
||
<span class="st-label" id="st-ip-label">IP 白名单</span>
|
||
<div class="st-control">
|
||
<label class="kole-checkbox-option" style="margin-bottom:8px"><input type="checkbox" id="st-ip-on"> 仅允许白名单 IP 登录</label>
|
||
<div class="kole-input" id="st-ip-box" style="max-width:380px; opacity:.55" aria-disabled="true">
|
||
<textarea id="st-ip" rows="2" disabled placeholder="每行一个 IP 或网段,例如 192.168.5.0/24"
|
||
style="border:none;outline:none;background:transparent;font:inherit;color:inherit;flex:1;min-width:0;resize:vertical;padding:6px 0"></textarea>
|
||
</div>
|
||
<p class="hint">启用前请确认当前 IP 在白名单内,否则会把自己锁在系统外</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card card-bordered card-p24">
|
||
<div class="st-card">
|
||
<div>
|
||
<h2 class="st-card-title">登录方式</h2>
|
||
<p class="st-card-desc">开关即时生效,不需要点击保存。</p>
|
||
</div>
|
||
<div class="st-switches" data-immediate="1">
|
||
<div class="st-switch-row">
|
||
<span class="st-switch-text"><strong>企业微信扫码登录</strong><span>成员可用企业微信扫码进入控制台</span></span>
|
||
<button class="kole-switch-switch is-on" type="button" role="switch" aria-checked="true" aria-label="企业微信扫码登录"></button>
|
||
</div>
|
||
<div class="st-switch-row">
|
||
<span class="st-switch-text"><strong>短信验证码登录</strong><span>接收短信验证码作为第二种登录方式</span></span>
|
||
<button class="kole-switch-switch" type="button" role="switch" aria-checked="false" aria-label="短信验证码登录"></button>
|
||
</div>
|
||
<div class="st-switch-row is-locked">
|
||
<span class="st-switch-text"><strong>单点登录(SSO)</strong><span>需接入方配置 IdP 元数据后启用,当前不可用</span></span>
|
||
<button class="kole-switch-switch" type="button" role="switch" aria-checked="false" aria-label="单点登录" disabled style="opacity:.55;cursor:not-allowed"></button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 通知设置 -->
|
||
<section class="st-panel" id="st-panel-notify" role="tabpanel" aria-label="通知设置" hidden>
|
||
<div class="card card-bordered card-p24">
|
||
<div class="st-card">
|
||
<div>
|
||
<h2 class="st-card-title">通知渠道</h2>
|
||
<p class="st-card-desc">选择接收系统提醒的方式,开关即时生效。</p>
|
||
</div>
|
||
<div class="st-switches" data-immediate="1">
|
||
<div class="st-switch-row">
|
||
<span class="st-switch-text"><strong>站内通知</strong><span>控制台右上角的消息中心</span></span>
|
||
<button class="kole-switch-switch is-on" type="button" role="switch" aria-checked="true" aria-label="站内通知"></button>
|
||
</div>
|
||
<div class="st-switch-row">
|
||
<span class="st-switch-text"><strong>邮件通知</strong><span>发送至账号绑定邮箱</span></span>
|
||
<button class="kole-switch-switch is-on" type="button" role="switch" aria-checked="true" aria-label="邮件通知"></button>
|
||
</div>
|
||
<div class="st-switch-row">
|
||
<span class="st-switch-text"><strong>短信通知</strong><span>仅用于紧急告警,会产生短信费用</span></span>
|
||
<button class="kole-switch-switch" type="button" role="switch" aria-checked="false" aria-label="短信通知"></button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card card-bordered card-p24">
|
||
<div class="st-card">
|
||
<div>
|
||
<h2 class="st-card-title">接收的事件</h2>
|
||
<p class="st-card-desc">选择哪些业务事件需要提醒。</p>
|
||
</div>
|
||
<div class="st-group" style="flex-direction:column; gap:10px">
|
||
<label class="kole-checkbox-option"><input type="checkbox" checked> 订单状态变更</label>
|
||
<label class="kole-checkbox-option"><input type="checkbox" checked> 待审核事项提醒</label>
|
||
<label class="kole-checkbox-option"><input type="checkbox"> 成员权限变更</label>
|
||
<label class="kole-checkbox-option"><input type="checkbox"> 系统维护公告</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 危险操作 -->
|
||
<section class="st-panel" id="st-panel-danger" role="tabpanel" aria-label="危险操作" hidden>
|
||
<div class="card card-bordered card-p24 st-danger">
|
||
<div class="st-card">
|
||
<div>
|
||
<h2 class="st-card-title">危险操作</h2>
|
||
<p class="st-card-desc">以下操作不可撤销,请谨慎执行。删除前建议先导出数据。</p>
|
||
</div>
|
||
<div class="st-switch-row">
|
||
<span class="st-switch-text"><strong>清空操作日志</strong><span>将删除全部历史操作记录,审计追溯能力将失效</span></span>
|
||
<button class="btn btn-default btn-md" type="button" id="st-clear-log">清空日志</button>
|
||
</div>
|
||
<div class="st-switch-row">
|
||
<span class="st-switch-text"><strong>停用当前企业</strong><span>停用后全部成员无法登录,数据保留 30 天后删除</span></span>
|
||
<button class="btn btn-danger btn-md" type="button" id="st-close-org">停用企业</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 保存栏 -->
|
||
<div class="st-footer">
|
||
<span class="dirty" id="st-dirty" hidden>● 有未保存的修改</span>
|
||
<span class="saved" id="st-saved">所有修改已保存 · 上次保存 09:30</span>
|
||
<div class="btns">
|
||
<button class="btn btn-default btn-md" type="button" id="st-reset" disabled>放弃修改</button>
|
||
<button class="btn btn-primary btn-md" type="button" id="st-save" disabled>保存</button>
|
||
</div>
|
||
</div>
|
||
|
||
<p style="margin:0; font-size:var(--kole-type-caption-size); color:var(--kole-color-text-secondary)">
|
||
模板页库 · 全部视觉来自 <a href="../../.design_library/kole-ui/colors_and_type.css">设计令牌</a> 与
|
||
<a href="../../.design_library/kole-ui/components.css">components.css</a>;
|
||
<a href="../index.html">← 返回组件文档</a>
|
||
</p>
|
||
</main>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
'use strict';
|
||
function esc(s) { return String(s).replace(/&/g, '&').replace(/</g, '<'); }
|
||
|
||
/* ── 分区切换 ─────────────────────────────────────────── */
|
||
var nav = document.getElementById('st-nav');
|
||
nav.addEventListener('click', function (e) {
|
||
var btn = e.target.closest('button[data-panel]');
|
||
if (!btn) return;
|
||
var key = btn.getAttribute('data-panel');
|
||
nav.querySelectorAll('button[data-panel]').forEach(function (b) { b.setAttribute('aria-selected', String(b === btn)); });
|
||
document.querySelectorAll('.st-panel').forEach(function (p) { p.hidden = p.id !== 'st-panel-' + key; });
|
||
var head = document.querySelector('.st-title');
|
||
head.textContent = btn.textContent.trim();
|
||
});
|
||
|
||
/* ── 保存栏:快照式脏检查 ───────────────────────────────
|
||
每个受控字段都登记进 collect()/apply(),脏 = 当前值与基线不等。
|
||
这样「改回原值」会自动回到干净态,新增字段也不必再补一遍还原代码。 */
|
||
var dirtyEl = document.getElementById('st-dirty');
|
||
var savedEl = document.getElementById('st-saved');
|
||
var saveBtn = document.getElementById('st-save');
|
||
var resetBtn = document.getElementById('st-reset');
|
||
var DENSITY_DEFAULT = 'default';
|
||
|
||
function activeDensity() {
|
||
var on = document.querySelector('#st-density .kole-segmented-item.is-active');
|
||
return on ? on.getAttribute('data-density') : DENSITY_DEFAULT;
|
||
}
|
||
function collect() {
|
||
var notify = [];
|
||
document.querySelectorAll('#st-panel-notify .kole-checkbox-option input').forEach(function (el, i) {
|
||
if (el.checked) notify.push(i);
|
||
});
|
||
return {
|
||
name: document.getElementById('st-name').value,
|
||
code: document.getElementById('st-code').value,
|
||
tz: document.getElementById('st-tz').value,
|
||
timeout: document.getElementById('st-timeout').value,
|
||
lang: (document.querySelector('input[name="st-lang"]:checked') || {}).value || '',
|
||
pwd: (document.querySelector('input[name="st-pwd"]:checked') || {}).value || '',
|
||
density: activeDensity(),
|
||
ipOn: document.getElementById('st-ip-on').checked,
|
||
ipList: document.getElementById('st-ip').value,
|
||
notify: notify.join(',')
|
||
};
|
||
}
|
||
function apply(s) {
|
||
/* 程序化赋值不会触发 input/change 事件,故不会反过来把自己标脏 */
|
||
document.getElementById('st-name').value = s.name;
|
||
document.getElementById('st-code').value = s.code;
|
||
document.getElementById('st-tz').value = s.tz;
|
||
document.getElementById('st-timeout').value = s.timeout;
|
||
var lang = document.querySelector('input[name="st-lang"][value="' + s.lang + '"]');
|
||
if (lang) lang.checked = true;
|
||
var pwd = document.querySelector('input[name="st-pwd"][value="' + s.pwd + '"]');
|
||
if (pwd) pwd.checked = true;
|
||
document.querySelectorAll('#st-density .kole-segmented-item').forEach(function (b) {
|
||
b.classList.toggle('is-active', b.getAttribute('data-density') === s.density);
|
||
});
|
||
var ipOn = document.getElementById('st-ip-on');
|
||
ipOn.checked = s.ipOn;
|
||
document.getElementById('st-ip').value = s.ipList;
|
||
syncIpBox();
|
||
var want = s.notify ? s.notify.split(',') : [];
|
||
document.querySelectorAll('#st-panel-notify .kole-checkbox-option input').forEach(function (el, i) {
|
||
el.checked = want.indexOf(String(i)) >= 0;
|
||
});
|
||
}
|
||
var baseline = collect();
|
||
|
||
function refreshDirty() {
|
||
var dirty = JSON.stringify(collect()) !== JSON.stringify(baseline);
|
||
dirtyEl.hidden = !dirty;
|
||
saveBtn.disabled = !dirty;
|
||
resetBtn.disabled = !dirty;
|
||
}
|
||
function markDirty() { refreshDirty(); }
|
||
function clearDirty() { refreshDirty(); }
|
||
|
||
['st-name', 'st-code', 'st-tz', 'st-timeout', 'st-ip'].forEach(function (id) {
|
||
var el = document.getElementById(id);
|
||
el.addEventListener('input', markDirty);
|
||
el.addEventListener('change', markDirty);
|
||
});
|
||
document.querySelectorAll('input[name="st-lang"], input[name="st-pwd"]').forEach(function (el) {
|
||
el.addEventListener('change', markDirty);
|
||
});
|
||
document.getElementById('st-density').addEventListener('click', function (e) {
|
||
var btn = e.target.closest('.kole-segmented-item');
|
||
if (!btn) return;
|
||
this.querySelectorAll('.kole-segmented-item').forEach(function (b) { b.classList.remove('is-active'); });
|
||
btn.classList.add('is-active');
|
||
markDirty();
|
||
});
|
||
document.querySelectorAll('#st-panel-notify .kole-checkbox-option input, #st-ip-on').forEach(function (el) {
|
||
el.addEventListener('change', markDirty);
|
||
});
|
||
|
||
saveBtn.addEventListener('click', function () {
|
||
/* 保存进行中:两个按钮都禁用 —— 否则用户会在「还没提交完」的窗口里点放弃修改,
|
||
拿到的是上一份基线,看起来像数据被还原成了旧值。 */
|
||
saveBtn.disabled = true;
|
||
resetBtn.disabled = true;
|
||
saveBtn.textContent = '保存中…';
|
||
setTimeout(function () {
|
||
saveBtn.textContent = '保存';
|
||
/* 保存成功:当前值成为新基线,脏态自然消失 */
|
||
baseline = collect();
|
||
clearDirty();
|
||
var t = new Date();
|
||
savedEl.textContent = '所有修改已保存 · 上次保存 ' + String(t.getHours()).padStart(2, '0') + ':' + String(t.getMinutes()).padStart(2, '0');
|
||
flash('设置已保存', 'success');
|
||
}, 700);
|
||
});
|
||
resetBtn.addEventListener('click', function () {
|
||
apply(baseline);
|
||
clearDirty();
|
||
flash('已放弃未保存的修改', 'info');
|
||
});
|
||
|
||
/* ── 开关:即时生效(不进入保存栏) ───────────────────── */
|
||
document.querySelectorAll('.st-switches .kole-switch-switch').forEach(function (sw) {
|
||
if (sw.disabled) return;
|
||
sw.addEventListener('click', function () {
|
||
var on = sw.getAttribute('aria-checked') !== 'true';
|
||
sw.setAttribute('aria-checked', String(on));
|
||
sw.classList.toggle('is-on', on);
|
||
var label = sw.getAttribute('aria-label');
|
||
flash(label + (on ? ' 已开启' : ' 已关闭'), on ? 'success' : 'info');
|
||
});
|
||
});
|
||
|
||
/* ── 安全策略:IP 白名单联动 ──────────────────────────── */
|
||
function syncIpBox() {
|
||
var box = document.getElementById('st-ip-box');
|
||
var area = document.getElementById('st-ip');
|
||
var on = document.getElementById('st-ip-on').checked;
|
||
area.disabled = !on;
|
||
box.style.opacity = on ? '1' : '.55';
|
||
box.setAttribute('aria-disabled', String(!on));
|
||
}
|
||
document.getElementById('st-ip-on').addEventListener('change', function () {
|
||
syncIpBox();
|
||
if (this.checked) document.getElementById('st-ip').focus();
|
||
});
|
||
|
||
/* ── 危险操作:二次确认 ───────────────────────────────── */
|
||
document.getElementById('st-clear-log').addEventListener('click', function () {
|
||
confirmDanger('清空操作日志', '将删除全部历史操作记录,审计追溯能力将失效。此操作不可撤销。');
|
||
});
|
||
document.getElementById('st-close-org').addEventListener('click', function () {
|
||
confirmDanger('停用当前企业', '停用后全部成员无法登录,数据保留 30 天后删除。此操作不可撤销。');
|
||
});
|
||
function confirmDanger(title, desc) {
|
||
if (window.confirm(title + '\n\n' + desc + '\n\n确认继续?')) {
|
||
flash('已确认「' + title + '」(示范交互,未真正执行)', 'warning');
|
||
}
|
||
}
|
||
|
||
/* ── 轻提示 ───────────────────────────────────────────── */
|
||
var msgWrap = null, msgTimer = null;
|
||
function flash(text, kind) {
|
||
if (!msgWrap) {
|
||
msgWrap = document.createElement('div');
|
||
msgWrap.className = 'kole-message-wrap';
|
||
msgWrap.setAttribute('role', 'status');
|
||
msgWrap.setAttribute('aria-live', 'polite');
|
||
document.body.appendChild(msgWrap);
|
||
}
|
||
var k = kind || 'success';
|
||
var icon = k === 'success' ? '✓' : k === 'info' ? 'i' : '!';
|
||
msgWrap.innerHTML = '<div class="kole-message is-' + k + '"><span class="kole-message-icon">' + icon + '</span>' +
|
||
'<span class="kole-message-content">' + esc(text) + '</span></div>';
|
||
clearTimeout(msgTimer);
|
||
msgTimer = setTimeout(function () { msgWrap.innerHTML = ''; }, 2000);
|
||
}
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|