20260804223152

This commit is contained in:
oneao committed 2026-08-04 22:31:52 +08:00
1 parent 75b1ccaa6d
commit 7c9761a4fc
71 files changed
+5534 -9113

No files matched your search

+375
View File
@@ -0,0 +1,375 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>模块管理原型</title>
<style>
:root {
--bg: #f4f6f8;
--surface: #ffffff;
--surface-muted: #f8fafb;
--line: #e3e8ed;
--line-strong: #cbd5df;
--text: #1f2a37;
--muted: #6b7785;
--primary: #2166d1;
--primary-soft: #eaf2ff;
--green: #198754;
--amber: #b7791f;
--shadow: 0 8px 24px rgba(29, 43, 58, 0.06);
}
* { box-sizing: border-box; }
body {
margin: 0;
color: var(--text);
background: var(--bg);
font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", sans-serif;
}
button, input, select { font: inherit; }
button { cursor: pointer; }
.app-shell { min-height: 100vh; padding: 20px; }
.app-bar {
display: flex;
align-items: center;
justify-content: space-between;
max-width: 1440px;
margin: 0 auto 14px;
}
.brand { display: flex; align-items: center; gap: 10px; }
.brand-mark {
display: grid;
width: 34px;
height: 34px;
place-items: center;
border-radius: 8px;
color: #fff;
background: #174a96;
font-weight: 700;
}
.brand-title { font-size: 18px; font-weight: 650; }
.brand-subtitle { margin-left: 8px; color: var(--muted); }
.app-actions { display: flex; align-items: center; gap: 8px; }
.user-chip {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 10px;
border: 1px solid var(--line);
border-radius: 7px;
background: var(--surface);
color: var(--muted);
}
.avatar {
display: grid;
width: 24px;
height: 24px;
place-items: center;
border-radius: 50%;
color: #174a96;
background: #dbe9ff;
font-size: 12px;
font-weight: 700;
}
.workspace {
display: grid;
grid-template-columns: 330px minmax(0, 1fr);
max-width: 1440px;
min-height: 720px;
margin: 0 auto;
overflow: hidden;
border: 1px solid var(--line);
border-radius: 9px;
background: var(--surface);
box-shadow: var(--shadow);
}
.sidebar { display: flex; min-width: 0; flex-direction: column; border-right: 1px solid var(--line); }
.sidebar-head { padding: 18px 16px 12px; border-bottom: 1px solid var(--line); }
.sidebar-title { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.sidebar-title strong { font-size: 15px; }
.count { color: var(--muted); font-size: 12px; }
.search { position: relative; }
.search input {
width: 100%;
height: 34px;
padding: 0 10px 0 32px;
border: 1px solid var(--line-strong);
border-radius: 6px;
outline: none;
color: var(--text);
}
.search input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(33, 102, 209, 0.11); }
.search-icon { position: absolute; top: 6px; left: 11px; color: var(--muted); font-size: 16px; }
.tree { flex: 1; overflow: auto; padding: 10px 8px 16px; }
.tree-group { margin-bottom: 16px; }
.tree-label {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 8px;
color: var(--muted);
font-size: 12px;
font-weight: 650;
letter-spacing: 0.02em;
}
.tree-row {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
min-height: 36px;
padding: 7px 10px 7px 18px;
border: 0;
border-radius: 6px;
color: var(--text);
background: transparent;
text-align: left;
}
.tree-row:hover { background: var(--surface-muted); }
.tree-row.active { color: #174a96; background: var(--primary-soft); font-weight: 600; }
.tree-row.child { padding-left: 38px; }
.tree-row.grandchild { padding-left: 62px; }
.node-icon { width: 17px; color: var(--muted); text-align: center; font-size: 13px; }
.tree-row.active .node-icon { color: var(--primary); }
.node-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.node-code { margin-left: auto; color: #98a3ae; font-size: 11px; font-weight: 400; }
.sidebar-foot { padding: 11px 16px; border-top: 1px solid var(--line); color: var(--muted); font-size: 12px; }
.content { min-width: 0; overflow: auto; background: #fbfcfd; }
.content-head { padding: 22px 28px 0; background: var(--surface); border-bottom: 1px solid var(--line); }
.breadcrumb { color: var(--muted); font-size: 12px; }
.title-line { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; margin-top: 8px; }
h1 { margin: 0; font-size: 22px; line-height: 1.3; font-weight: 650; }
.title-meta { display: flex; align-items: center; gap: 8px; margin-top: 6px; color: var(--muted); }
.tag { display: inline-flex; align-items: center; height: 23px; padding: 0 8px; border-radius: 5px; color: #315a8b; background: #edf4fc; font-size: 12px; }
.tag.green { color: #176b47; background: #e8f6ef; }
.tag.amber { color: #8d641e; background: #fff6dc; }
.head-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
min-height: 34px;
padding: 0 12px;
border: 1px solid var(--line-strong);
border-radius: 6px;
color: var(--text);
background: var(--surface);
}
.btn:hover { border-color: var(--primary); color: var(--primary); }
.btn.primary { border-color: var(--primary); color: #fff; background: var(--primary); }
.btn.primary:hover { background: #1956b5; }
.btn.ghost { border-color: transparent; color: var(--muted); }
.btn.danger { color: #b42318; }
.tabs { display: flex; gap: 24px; margin-top: 18px; }
.tab {
position: relative;
padding: 10px 2px 12px;
border: 0;
color: var(--muted);
background: transparent;
}
.tab.active { color: var(--primary); font-weight: 600; }
.tab.active::after { position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; background: var(--primary); content: ""; }
.content-body { max-width: 1020px; padding: 24px 28px 36px; }
.notice { display: flex; align-items: center; gap: 8px; margin-bottom: 18px; padding: 10px 12px; border: 1px solid #cfe0f8; border-radius: 6px; color: #315a8b; background: #f2f7ff; }
.section { margin-bottom: 22px; }
.section-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.section-head h2 { margin: 0; font-size: 15px; font-weight: 650; }
.section-head span { color: var(--muted); font-size: 12px; }
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 18px; padding: 18px; border: 1px solid var(--line); border-radius: 7px; background: var(--surface); }
.field.full { grid-column: 1 / -1; }
label { display: block; margin-bottom: 6px; color: #4f5d6b; font-size: 12px; font-weight: 600; }
.input, .select {
width: 100%;
height: 34px;
padding: 0 10px;
border: 1px solid var(--line-strong);
border-radius: 6px;
outline: none;
color: var(--text);
background: #fff;
}
.input:focus, .select:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(33, 102, 209, 0.11); }
.switch-line { display: flex; align-items: center; justify-content: space-between; min-height: 34px; padding: 0 10px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-muted); }
.switch { position: relative; width: 34px; height: 20px; border: 0; border-radius: 12px; background: #b7c1cb; }
.switch::after { position: absolute; top: 3px; left: 3px; width: 14px; height: 14px; border-radius: 50%; background: #fff; content: ""; transition: transform .16s; }
.switch.on { background: var(--primary); }
.switch.on::after { transform: translateX(14px); }
.data-table { width: 100%; border-collapse: collapse; overflow: hidden; border: 1px solid var(--line); border-radius: 7px; background: var(--surface); }
.data-table th, .data-table td { padding: 11px 13px; border-bottom: 1px solid var(--line); text-align: left; }
.data-table th { color: var(--muted); background: var(--surface-muted); font-size: 12px; font-weight: 600; }
.data-table tr:last-child td { border-bottom: 0; }
.data-table .muted { color: var(--muted); }
.drag { color: #9aa6b2; }
.check { color: var(--green); font-weight: 650; }
.bottom-note { margin-top: 20px; color: var(--muted); font-size: 12px; }
.toast { position: fixed; right: 24px; bottom: 24px; display: none; padding: 10px 14px; border: 1px solid #cfe0f8; border-radius: 6px; color: #315a8b; background: #f2f7ff; box-shadow: var(--shadow); }
.toast.show { display: block; }
@media (max-width: 900px) {
.app-shell { padding: 10px; }
.workspace { grid-template-columns: 260px minmax(0, 1fr); min-height: 680px; }
.content-head, .content-body { padding-right: 18px; padding-left: 18px; }
.title-line { flex-direction: column; }
.head-actions { justify-content: flex-start; }
}
@media (max-width: 680px) {
.app-bar { align-items: flex-start; gap: 12px; flex-direction: column; }
.workspace { display: block; }
.sidebar { min-height: 280px; border-right: 0; border-bottom: 1px solid var(--line); }
.form-grid { grid-template-columns: 1fr; }
.field.full { grid-column: auto; }
.content { min-height: 600px; }
}
</style>
</head>
<body>
<div class="app-shell">
<header class="app-bar">
<div class="brand">
<div class="brand-mark">F</div>
<div><span class="brand-title">模块管理</span><span class="brand-subtitle">统一维护目录、页面和数据模块</span></div>
</div>
<div class="app-actions">
<button class="btn ghost" type="button" data-action="refresh">↻ 刷新</button>
<div class="user-chip"><span class="avatar">管</span><span>系统管理员</span><span class="tag green">可维护</span></div>
</div>
</header>
<main class="workspace">
<aside class="sidebar">
<div class="sidebar-head">
<div class="sidebar-title"><strong>业务结构</strong><span class="count">12 个资源</span></div>
<div class="search"><span class="search-icon">⌕</span><input id="tree-search" type="search" placeholder="搜索目录、页面或模块" /></div>
</div>
<div class="tree" id="tree">
<div class="tree-group">
<div class="tree-label">⌄ 业务目录</div>
<button class="tree-row" data-node="directory"><span class="node-icon">□</span><span class="node-text">基础资料</span><span class="node-code">base</span></button>
<button class="tree-row child" data-node="page"><span class="node-icon">▣</span><span class="node-text">往来单位</span><span class="node-code">contact</span></button>
<button class="tree-row grandchild active" data-node="data"><span class="node-icon">▤</span><span class="node-text">客户联系人</span><span class="node-code">contact_person</span></button>
<button class="tree-row child" data-node="page"><span class="node-icon">▣</span><span class="node-text">其他公司</span><span class="node-code">othercompany</span></button>
<button class="tree-row grandchild" data-node="data"><span class="node-icon">▤</span><span class="node-text">公司联系人</span><span class="node-code">company_contact</span></button>
<button class="tree-row" data-node="directory"><span class="node-icon">□</span><span class="node-text">系统管理</span><span class="node-code">system</span></button>
<button class="tree-row child" data-node="page"><span class="node-icon">▣</span><span class="node-text">用户管理</span><span class="node-code">user</span></button>
<button class="tree-row grandchild" data-node="data"><span class="node-icon">▤</span><span class="node-text">用户列表</span><span class="node-code">sys_user</span></button>
</div>
<div class="tree-group">
<div class="tree-label">⌄ 未挂载资源</div>
<button class="tree-row child" data-node="orphan-data"><span class="node-icon">▤</span><span class="node-text">登录日志</span><span class="node-code">login_log</span></button>
<button class="tree-row child" data-node="orphan-page"><span class="node-icon">▣</span><span class="node-text">多语言类型</span><span class="node-code">i18n_type</span></button>
</div>
</div>
<div class="sidebar-foot">拖动节点可以调整目录和页面顺序</div>
</aside>
<section class="content">
<div class="content-head">
<div class="breadcrumb">基础资料 / 往来单位 / 数据模块</div>
<div class="title-line">
<div>
<h1 id="page-title">客户联系人</h1>
<div class="title-meta"><span class="tag green">数据模块</span><span>contact_person</span><span>已启用</span></div>
</div>
<div class="head-actions">
<button class="btn" type="button" data-action="link">关联页面</button>
<button class="btn danger" type="button" data-action="delete">删除</button>
<button class="btn primary" type="button" data-action="save">保存更改</button>
</div>
</div>
<nav class="tabs" aria-label="当前对象配置">
<button class="tab active" data-tab="overview">基本信息</button>
<button class="tab" data-tab="fields">字段</button>
<button class="tab" data-tab="list">列表</button>
<button class="tab" data-tab="form">表单</button>
<button class="tab" data-tab="query">查询</button>
<button class="tab" data-tab="actions">操作权限</button>
</nav>
</div>
<div class="content-body" id="panel-overview">
<div class="notice"><span>✓</span><span>当前使用方式:默认。显示名称支持多语言,未翻译时自动回退到中文名称。</span><button class="btn ghost" type="button" data-action="i18n">管理多语言</button></div>
<section class="section">
<div class="section-head"><h2>模块基本信息</h2><span>保存后立即对运行页面生效</span></div>
<div class="form-grid">
<div class="field"><label>技术编码</label><input class="input" value="contact_person" disabled /></div>
<div class="field"><label>显示名称</label><input class="input" value="客户联系人" /></div>
<div class="field"><label>查询对象</label><input class="input" value="v_contact_person" /></div>
<div class="field"><label>保存对象</label><input class="input" value="b_contact_person" /></div>
<div class="field"><label>主键字段</label><input class="input" value="b_id" /></div>
<div class="field"><label>默认排序</label><input class="input" value="b_xh ASC, b_id DESC" /></div>
<div class="field"><label>默认使用方式</label><select class="select"><option>默认(default)</option><option>只读(readonly)</option></select></div>
<div class="field"><label>启用状态</label><div class="switch-line"><span>允许页面使用</span><button class="switch on" type="button" aria-label="切换启用状态"></button></div></div>
</div>
</section>
<section class="section">
<div class="section-head"><h2>字段概览</h2><button class="btn" type="button" data-tab-jump="fields">查看全部字段</button></div>
<table class="data-table">
<thead><tr><th>字段</th><th>名称</th><th>类型</th><th>列表</th><th>表单</th><th>状态</th></tr></thead>
<tbody>
<tr><td><strong>b_name</strong></td><td>姓名</td><td class="muted">varchar</td><td class="check">显示</td><td class="check">编辑</td><td><span class="tag green">启用</span></td></tr>
<tr><td><strong>b_mobile</strong></td><td>手机</td><td class="muted">varchar</td><td class="check">显示</td><td class="check">编辑</td><td><span class="tag green">启用</span></td></tr>
<tr><td><strong>b_remark</strong></td><td>备注</td><td class="muted">nvarchar</td><td class="muted">隐藏</td><td class="check">编辑</td><td><span class="tag green">启用</span></td></tr>
</tbody>
</table>
</section>
<div class="bottom-note">权限范围:当前账号可以编辑模块配置、同步字段和管理操作权限。</div>
</div>
</section>
</main>
</div>
<div class="toast" id="toast" role="status"></div>
<script>
const toast = document.getElementById("toast");
const title = document.getElementById("page-title");
const search = document.getElementById("tree-search");
const rows = [...document.querySelectorAll(".tree-row")];
let toastTimer;
function showToast(text) {
toast.textContent = text;
toast.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(() => toast.classList.remove("show"), 2200);
}
rows.forEach((row) => row.addEventListener("click", () => {
rows.forEach((item) => item.classList.remove("active"));
row.classList.add("active");
const text = row.querySelector(".node-text").textContent;
title.textContent = text;
showToast(`已切换到「${text}」`);
}));
document.querySelectorAll(".tab").forEach((tab) => tab.addEventListener("click", () => {
document.querySelectorAll(".tab").forEach((item) => item.classList.remove("active"));
tab.classList.add("active");
showToast(`打开${tab.textContent}配置`);
}));
document.querySelectorAll("[data-tab-jump]").forEach((button) => button.addEventListener("click", () => {
document.querySelector(`[data-tab='${button.dataset.tabJump}']`).click();
}));
document.querySelectorAll("[data-action]").forEach((button) => button.addEventListener("click", () => {
const messages = { save: "更改已保存", refresh: "配置已刷新", link: "打开关联页面窗口", delete: "删除操作需要再次确认", i18n: "打开多语言管理" };
showToast(messages[button.dataset.action]);
}));
document.querySelectorAll(".switch").forEach((button) => button.addEventListener("click", () => button.classList.toggle("on")));
search.addEventListener("input", () => {
const keyword = search.value.trim().toLowerCase();
rows.forEach((row) => {
row.hidden = keyword && !row.textContent.toLowerCase().includes(keyword);
});
});
</script>
</body>
</html>