Refine brand management modal UX
This commit is contained in:
+360
-46
@@ -14,23 +14,34 @@
|
||||
--brand: #0f6fff;
|
||||
--ok: #0a7f3f;
|
||||
--warn: #b16a00;
|
||||
--nav-height: 52px;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
padding-top: var(--nav-height);
|
||||
font-family: "PingFang SC", "Noto Sans SC", "Microsoft YaHei", sans-serif;
|
||||
background: radial-gradient(circle at 0 0, #eef4ff 0, var(--bg) 40%), var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
body.modal-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
.top-nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
background: linear-gradient(180deg, #1f2a3a, #1a2431);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 10px 30px rgba(14, 25, 42, 0.16);
|
||||
}
|
||||
.top-nav-inner {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
height: 52px;
|
||||
height: var(--nav-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@@ -66,6 +77,9 @@
|
||||
margin: 24px auto;
|
||||
padding: 0 16px 32px;
|
||||
}
|
||||
.page-card {
|
||||
padding: 16px;
|
||||
}
|
||||
.card {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--line);
|
||||
@@ -84,6 +98,20 @@
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.page-sub {
|
||||
margin-bottom: 0;
|
||||
max-width: 720px;
|
||||
}
|
||||
.compact-note-list {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
color: var(--sub);
|
||||
font-size: 13px;
|
||||
line-height: 1.65;
|
||||
}
|
||||
.compact-note-list li + li {
|
||||
margin-top: 4px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin: 10px 0 6px;
|
||||
@@ -128,18 +156,39 @@
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.pill-btn {
|
||||
border: 1px solid #b9cae8;
|
||||
border-radius: 999px;
|
||||
padding: 4px 10px;
|
||||
background: #f5f9ff;
|
||||
color: #254575;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
.brand-toolbar {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.pill-btn:hover {
|
||||
background: #ebf3ff;
|
||||
.brand-toolbar-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
.brand-toolbar-btn {
|
||||
width: 100%;
|
||||
min-height: 46px;
|
||||
border: 1px solid #c9d7ee;
|
||||
border-radius: 12px;
|
||||
padding: 10px 14px;
|
||||
background: #f3f7ff;
|
||||
color: #24416d;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.72);
|
||||
transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
.brand-toolbar-btn:hover {
|
||||
background: #eaf1ff;
|
||||
border-color: #abc0e7;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.brand-toolbar-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.brand-toolbar-btn.stat-btn {
|
||||
background: #f7faff;
|
||||
}
|
||||
.brand-toolbar-btn.action-btn {
|
||||
background: #eef3fc;
|
||||
}
|
||||
.table-wrap {
|
||||
overflow: auto;
|
||||
@@ -240,7 +289,7 @@
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
position: sticky;
|
||||
top: 12px;
|
||||
top: calc(var(--nav-height) + 20px);
|
||||
}
|
||||
.tab-btn {
|
||||
width: 100%;
|
||||
@@ -257,6 +306,58 @@
|
||||
.manage-panel.hidden {
|
||||
display: none;
|
||||
}
|
||||
.panel-stack {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.section-card {
|
||||
padding: 16px;
|
||||
}
|
||||
.section-card .title:last-child,
|
||||
.section-card .sub:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.collapse-card {
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.collapse-card summary {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 14px 16px;
|
||||
font-weight: 700;
|
||||
color: #1f355a;
|
||||
cursor: pointer;
|
||||
}
|
||||
.collapse-card summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
.collapse-card summary::after {
|
||||
content: "展开";
|
||||
flex: 0 0 auto;
|
||||
border: 1px solid #c8d6ee;
|
||||
border-radius: 999px;
|
||||
padding: 3px 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #47648f;
|
||||
background: #f6f9ff;
|
||||
}
|
||||
.collapse-card[open] summary::after {
|
||||
content: "收起";
|
||||
}
|
||||
.collapse-card[open] summary {
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: #fbfcff;
|
||||
}
|
||||
.collapse-body {
|
||||
padding: 14px 16px 16px;
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
.sync-log {
|
||||
min-height: 240px;
|
||||
white-space: pre-wrap;
|
||||
@@ -269,6 +370,9 @@
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
.sync-log.compact {
|
||||
min-height: 180px;
|
||||
}
|
||||
.sync-schedule-card {
|
||||
margin: 14px 0;
|
||||
padding: 12px;
|
||||
@@ -304,36 +408,112 @@
|
||||
inset: 0;
|
||||
background: rgba(10, 20, 38, 0.45);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 16px;
|
||||
z-index: 999;
|
||||
padding: calc(var(--nav-height) + 24px) 16px 20px;
|
||||
overflow-y: auto;
|
||||
z-index: 10000;
|
||||
}
|
||||
.modal-backdrop.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
.modal-card {
|
||||
width: min(920px, 100%);
|
||||
max-height: 90vh;
|
||||
overflow: auto;
|
||||
max-height: calc(100vh - var(--nav-height) - 44px);
|
||||
min-height: min(680px, calc(100vh - var(--nav-height) - 44px));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
padding: 14px;
|
||||
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
.modal-head {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
padding-bottom: 2px;
|
||||
border-bottom: 1px solid #eef2f8;
|
||||
}
|
||||
.modal-card .title,
|
||||
.modal-card .sub {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.modal-content {
|
||||
flex: 1 1 auto;
|
||||
min-height: 320px;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
}
|
||||
.modal-list {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 10px;
|
||||
background: #fcfdff;
|
||||
padding: 12px;
|
||||
}
|
||||
.modal-list-grid {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.modal-list-item {
|
||||
border: 1px solid #dbe4f2;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
padding: 12px 14px;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
.modal-list-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.modal-list-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #203554;
|
||||
}
|
||||
.modal-list-meta {
|
||||
font-size: 12px;
|
||||
color: #62728a;
|
||||
}
|
||||
.modal-list-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
.modal-list-empty {
|
||||
height: 100%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: var(--sub);
|
||||
font-size: 14px;
|
||||
}
|
||||
.modal-card textarea {
|
||||
width: 100%;
|
||||
min-height: 360px;
|
||||
min-height: 420px;
|
||||
height: auto;
|
||||
resize: none;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
background: #fcfdff;
|
||||
}
|
||||
.modal-card pre {
|
||||
min-height: 240px;
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
font-size: 12px;
|
||||
@@ -343,6 +523,15 @@
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid #eef2f8;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
@media (max-width: 1020px) {
|
||||
.manage-layout {
|
||||
@@ -353,6 +542,24 @@
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.brand-toolbar-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.brand-toolbar-btn,
|
||||
.brand-toolbar-btn.stat-btn,
|
||||
.brand-toolbar-btn.action-btn {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
.modal-backdrop {
|
||||
padding: calc(var(--nav-height) + 12px) 12px 12px;
|
||||
}
|
||||
.modal-card {
|
||||
max-height: calc(100vh - var(--nav-height) - 24px);
|
||||
min-height: calc(100vh - var(--nav-height) - 24px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -366,8 +573,9 @@
|
||||
</nav>
|
||||
|
||||
<div class="wrap">
|
||||
<section class="card">
|
||||
<section class="card page-card">
|
||||
<h1 class="title">数据管理</h1>
|
||||
<p class="sub page-sub">把常用操作留在前面,把低频说明收起来。</p>
|
||||
|
||||
<div class="manage-layout">
|
||||
<aside class="manage-tabs">
|
||||
@@ -379,19 +587,21 @@
|
||||
|
||||
<div class="manage-content">
|
||||
<section id="brandTabPanel" class="manage-panel">
|
||||
<div class="result-head">
|
||||
<button id="brandCountBtn" type="button" class="pill-btn">品牌数: -</button>
|
||||
<button id="manufacturerCountBtn" type="button" class="pill-btn">厂商数: -</button>
|
||||
<div class="panel-stack">
|
||||
<article class="card section-card">
|
||||
<div class="brand-toolbar">
|
||||
<div class="brand-toolbar-row">
|
||||
<button id="brandCountBtn" type="button" class="brand-toolbar-btn stat-btn">品牌数: -</button>
|
||||
<button id="manufacturerCountBtn" type="button" class="brand-toolbar-btn stat-btn">厂商数: -</button>
|
||||
<button id="editBrandListBtn" type="button" class="brand-toolbar-btn action-btn">编辑品牌列表</button>
|
||||
<button id="editBrandRelationsBtn" type="button" class="brand-toolbar-btn action-btn">编辑品牌-厂商关系</button>
|
||||
<button id="editBrandAliasesBtn" type="button" class="brand-toolbar-btn action-btn">编辑品牌同义词</button>
|
||||
</div>
|
||||
|
||||
<div class="btns">
|
||||
<button id="editBrandListBtn" type="button">编辑品牌列表</button>
|
||||
<button id="editBrandRelationsBtn" type="button">编辑品牌-厂商关系</button>
|
||||
<button id="editBrandAliasesBtn" type="button">编辑品牌同义词</button>
|
||||
</div>
|
||||
|
||||
<div id="brandStats" class="sub">索引未加载。</div>
|
||||
</article>
|
||||
|
||||
<article class="card section-card">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
@@ -406,29 +616,49 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="sourceTabPanel" class="manage-panel hidden">
|
||||
<h3 class="title">数据来源管理(权重排序)</h3>
|
||||
<p class="sub">拖拽调整优先级。越靠前权重越高。初始化规则:`_cn.md` 在前,非 `cn` 在后。</p>
|
||||
<div class="panel-stack">
|
||||
<article class="card section-card">
|
||||
<h3 class="title">数据来源排序</h3>
|
||||
<p class="sub">拖拽调整优先级,越靠前权重越高。</p>
|
||||
<div class="btns">
|
||||
<button id="saveSourceOrderBtn" type="button" class="primary">保存来源排序</button>
|
||||
<button id="resetSourceOrderBtn" type="button">重置来源排序</button>
|
||||
</div>
|
||||
<div id="sourceOrderStats" class="sub">来源列表未加载。</div>
|
||||
</article>
|
||||
|
||||
<article class="card section-card">
|
||||
<div class="source-order-wrap">
|
||||
<ul id="sourceOrderList" class="source-order-list">
|
||||
<li class="sub">暂无来源数据</li>
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="syncTabPanel" class="manage-panel hidden">
|
||||
<div class="panel-stack">
|
||||
<article class="card section-card">
|
||||
<h3 class="title">原始数据同步</h3>
|
||||
<p class="sub">从上游 `KHwang9883/MobileModels` 拉取原始 markdown 数据,并重建 `dist/device_index.json`。如已开启 MySQL 自动装载,也会同步刷新 MySQL。请先启动完整服务。</p>
|
||||
<ul class="compact-note-list">
|
||||
<li>从上游拉取原始 markdown,并重建 <code>dist/device_index.json</code>。</li>
|
||||
<li>如果已开启 MySQL 自动装载,同步时也会刷新 MySQL。</li>
|
||||
<li>开始前请确认完整服务已经启动。</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<details class="card collapse-card">
|
||||
<summary>同步配置</summary>
|
||||
<div class="collapse-body">
|
||||
<div class="sync-schedule-card">
|
||||
<h4 class="title">MySQL 自动装载</h4>
|
||||
<p class="sub">控制同步任务和容器后续启动时是否自动导入 schema 与 seed。保持关闭更安全;开启后,启动容器和“开始同步原始数据”都可能刷新 MySQL 数据。</p>
|
||||
<p class="sub">控制同步任务和容器后续启动时是否自动导入 schema 与 seed。</p>
|
||||
<div class="sync-schedule-grid">
|
||||
<label class="check-row">
|
||||
<input id="mysqlAutoLoadEnabled" type="checkbox" />
|
||||
@@ -440,16 +670,18 @@
|
||||
</div>
|
||||
<div id="mysqlSettingsStatus" class="sub">正在读取 MySQL 自动装载设置。</div>
|
||||
</div>
|
||||
|
||||
<div class="sync-schedule-card">
|
||||
<h4 class="title">外部 MySQL 初始化</h4>
|
||||
<p class="sub">面向关闭自动装载的外部 MySQL。点击后会执行 schema 与 seed 导入,自动创建数据库,并重建 `mobilemodels` 相关表与视图。请确认连接参数与账号权限无误后再执行。</p>
|
||||
<p class="sub">面向关闭自动装载的外部 MySQL,需要时再执行。</p>
|
||||
<div class="btns">
|
||||
<button id="initMysqlBtn" type="button">初始化外部 MySQL</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sync-schedule-card">
|
||||
<h4 class="title">每日自动同步</h4>
|
||||
<p class="sub">在项目容器内按固定时间自动拉取上游原始数据,并重建索引与 MySQL Seed。时间按容器时区执行,设置会持久化到运行期数据目录。</p>
|
||||
<p class="sub">按固定时间自动拉取上游并重建索引与 MySQL Seed。</p>
|
||||
<div class="sync-schedule-grid">
|
||||
<label class="check-row">
|
||||
<input id="scheduleEnabled" type="checkbox" />
|
||||
@@ -469,22 +701,43 @@
|
||||
</div>
|
||||
<div id="scheduleStatus" class="sub">正在读取自动同步设置。</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<article class="card section-card">
|
||||
<div class="btns">
|
||||
<button id="syncUpstreamBtn" type="button" class="primary">开始同步原始数据</button>
|
||||
<button id="refreshSyncStatusBtn" type="button">刷新同步状态</button>
|
||||
</div>
|
||||
<div id="syncStatus" class="sub">正在检测同步能力。</div>
|
||||
<pre id="syncLog" class="sync-log mono">暂无同步记录</pre>
|
||||
</article>
|
||||
|
||||
<details class="card collapse-card">
|
||||
<summary>任务日志</summary>
|
||||
<div class="collapse-body">
|
||||
<pre id="syncLog" class="sync-log compact mono">暂无同步记录</pre>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="indexTabPanel" class="manage-panel hidden">
|
||||
<div class="panel-stack">
|
||||
<article class="card section-card">
|
||||
<h3 class="title">索引数据</h3>
|
||||
<p class="sub">这里集中显示 `dist/device_index.json` 的加载状态与基础统计,并提供手动重新加载入口。</p>
|
||||
<p class="sub">查看当前索引加载状态,并在需要时手动刷新。</p>
|
||||
<div class="btns">
|
||||
<button id="reloadIndexBtn" type="button" class="primary">重新加载索引</button>
|
||||
</div>
|
||||
<div id="indexStatus" class="sub">索引尚未加载。</div>
|
||||
<pre id="indexSummary" class="sync-log mono">暂无索引信息</pre>
|
||||
</article>
|
||||
<details class="card collapse-card">
|
||||
<summary>索引详情</summary>
|
||||
<div class="collapse-body">
|
||||
<pre id="indexSummary" class="sync-log compact mono">暂无索引信息</pre>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@@ -493,11 +746,16 @@
|
||||
|
||||
<div id="brandModalBackdrop" class="modal-backdrop hidden">
|
||||
<div class="modal-card">
|
||||
<div class="modal-head">
|
||||
<h3 id="brandModalTitle" class="title">数据管理</h3>
|
||||
<p id="brandModalHint" class="sub"></p>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<textarea id="brandModalTextarea" class="hidden"></textarea>
|
||||
<pre id="brandModalPre" class="hidden"></pre>
|
||||
<div class="btns">
|
||||
<div id="brandModalList" class="modal-list hidden"></div>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button id="brandModalCancelBtn" type="button">关闭</button>
|
||||
<button id="brandModalSaveBtn" type="button" class="primary">保存</button>
|
||||
</div>
|
||||
@@ -526,6 +784,7 @@
|
||||
const brandModalHintEl = document.getElementById("brandModalHint");
|
||||
const brandModalTextareaEl = document.getElementById("brandModalTextarea");
|
||||
const brandModalPreEl = document.getElementById("brandModalPre");
|
||||
const brandModalListEl = document.getElementById("brandModalList");
|
||||
const brandModalSaveBtnEl = document.getElementById("brandModalSaveBtn");
|
||||
const brandModalCancelBtnEl = document.getElementById("brandModalCancelBtn");
|
||||
const sourceOrderStatsEl = document.getElementById("sourceOrderStats");
|
||||
@@ -734,7 +993,7 @@
|
||||
async function runMysqlInit() {
|
||||
if (syncRunning || mysqlInitRunning) return;
|
||||
const confirmed = window.confirm(
|
||||
"初始化外部 MySQL 会创建数据库,并重建 mobilemodels 相关表、视图和 seed 数据。是否继续?"
|
||||
"初始化外部 MySQL 会创建数据库,并重建 mobilemodels 主表、相关索引和 seed 数据,同时清理历史兼容对象。是否继续?"
|
||||
);
|
||||
if (!confirmed) return;
|
||||
|
||||
@@ -1183,7 +1442,38 @@
|
||||
renderSourceOrder();
|
||||
}
|
||||
|
||||
function openBrandModal({ title, hint, text, editable, onSave }) {
|
||||
function renderModalList(items = []) {
|
||||
if (!items.length) {
|
||||
brandModalListEl.innerHTML = `<div class="modal-list-empty">暂无数据</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
brandModalListEl.innerHTML = `
|
||||
<div class="modal-list-grid">
|
||||
${items.map((item) => `
|
||||
<section class="modal-list-item">
|
||||
<div class="modal-list-head">
|
||||
<div class="modal-list-title">${escapeHtml(item.title || "-")}</div>
|
||||
${item.meta ? `<div class="modal-list-meta">${escapeHtml(item.meta)}</div>` : ""}
|
||||
</div>
|
||||
${item.tags && item.tags.length ? `
|
||||
<div class="modal-list-tags">
|
||||
${item.tags.map((tag) => `<span class="tag">${escapeHtml(tag)}</span>`).join("")}
|
||||
</div>
|
||||
` : ""}
|
||||
</section>
|
||||
`).join("")}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function resizeBrandModalTextarea() {
|
||||
if (brandModalTextareaEl.classList.contains("hidden")) return;
|
||||
brandModalTextareaEl.style.height = "auto";
|
||||
brandModalTextareaEl.style.height = `${Math.max(420, brandModalTextareaEl.scrollHeight + 24)}px`;
|
||||
}
|
||||
|
||||
function openBrandModal({ title, hint, text, editable, onSave, listItems }) {
|
||||
modalSaveHandler = onSave || null;
|
||||
brandModalTitleEl.textContent = title || "数据管理";
|
||||
brandModalHintEl.textContent = hint || "";
|
||||
@@ -1192,20 +1482,31 @@
|
||||
brandModalTextareaEl.classList.remove("hidden");
|
||||
brandModalTextareaEl.value = text || "";
|
||||
brandModalPreEl.classList.add("hidden");
|
||||
brandModalListEl.classList.add("hidden");
|
||||
brandModalSaveBtnEl.classList.remove("hidden");
|
||||
requestAnimationFrame(resizeBrandModalTextarea);
|
||||
} else if (listItems) {
|
||||
brandModalListEl.classList.remove("hidden");
|
||||
renderModalList(listItems);
|
||||
brandModalPreEl.classList.add("hidden");
|
||||
brandModalTextareaEl.classList.add("hidden");
|
||||
brandModalSaveBtnEl.classList.add("hidden");
|
||||
} else {
|
||||
brandModalPreEl.classList.remove("hidden");
|
||||
brandModalPreEl.textContent = text || "";
|
||||
brandModalTextareaEl.classList.add("hidden");
|
||||
brandModalListEl.classList.add("hidden");
|
||||
brandModalSaveBtnEl.classList.add("hidden");
|
||||
}
|
||||
|
||||
brandModalBackdropEl.classList.remove("hidden");
|
||||
document.body.classList.add("modal-open");
|
||||
}
|
||||
|
||||
function closeBrandModal() {
|
||||
modalSaveHandler = null;
|
||||
brandModalBackdropEl.classList.add("hidden");
|
||||
document.body.classList.remove("modal-open");
|
||||
}
|
||||
|
||||
function applyManagedBrandConfigUpdate(newConfig) {
|
||||
@@ -1218,8 +1519,14 @@
|
||||
function openBrandListModal() {
|
||||
openBrandModal({
|
||||
title: "品牌列表",
|
||||
hint: "当前独立维护的品牌列表(含同义词与父级厂商)。",
|
||||
text: JSON.stringify(managedBrandConfig.brands, null, 2),
|
||||
hint: "当前独立维护的品牌列表,按品牌查看同义词与所属厂商。",
|
||||
listItems: [...managedBrandConfig.brands]
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((brand) => ({
|
||||
title: brand.name,
|
||||
meta: `所属厂商:${managedBrandToManufacturer.get(brand.name) || brand.manufacturer || "-"}`,
|
||||
tags: normalizeAliasList(brand.name, brand.aliases || []),
|
||||
})),
|
||||
editable: false,
|
||||
});
|
||||
}
|
||||
@@ -1231,8 +1538,14 @@
|
||||
}));
|
||||
openBrandModal({
|
||||
title: "厂商列表",
|
||||
hint: "当前独立维护的厂商列表(含所属品牌)。",
|
||||
text: JSON.stringify(rows, null, 2),
|
||||
hint: "当前独立维护的厂商列表,按厂商查看归属品牌。",
|
||||
listItems: rows
|
||||
.sort((a, b) => a.manufacturer.localeCompare(b.manufacturer))
|
||||
.map((row) => ({
|
||||
title: row.manufacturer,
|
||||
meta: `品牌数:${row.brands.length}`,
|
||||
tags: row.brands,
|
||||
})),
|
||||
editable: false,
|
||||
});
|
||||
}
|
||||
@@ -1469,6 +1782,7 @@
|
||||
brandStatsEl.textContent = `保存失败: ${err.message}`;
|
||||
}
|
||||
});
|
||||
brandModalTextareaEl.addEventListener("input", resizeBrandModalTextarea);
|
||||
|
||||
function switchManageTab(tab) {
|
||||
const isBrand = tab === "brand";
|
||||
|
||||
Reference in New Issue
Block a user