376 lines
20 KiB
HTML
376 lines
20 KiB
HTML
<!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>
|