diff --git a/web/device_query.html b/web/device_query.html
index 7971e1c..2104c99 100644
--- a/web/device_query.html
+++ b/web/device_query.html
@@ -14,23 +14,41 @@
--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.docs-mode {
+ padding-top: var(--nav-height);
+ }
.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);
+ }
+ body.docs-mode .top-nav {
+ position: fixed !important;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 1000;
}
.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,9 +84,48 @@
margin: 24px auto;
padding: 0 16px 32px;
display: grid;
- grid-template-columns: 360px 1fr;
+ grid-template-columns: 320px 1fr;
gap: 16px;
}
+ .query-wrap {
+ align-items: start;
+ }
+ .query-sidebar {
+ position: sticky;
+ top: calc(var(--nav-height) + 24px);
+ display: grid;
+ gap: 12px;
+ }
+ .query-main {
+ min-width: 0;
+ }
+ .sidebar-card {
+ padding: 16px;
+ border-color: #d4def1;
+ box-shadow:
+ 0 16px 32px rgba(26, 42, 78, 0.07),
+ inset 0 1px 0 rgba(255, 255, 255, 0.8);
+ }
+ .sidebar-card .title {
+ font-size: 15px;
+ color: #173053;
+ }
+ .sidebar-card .sub {
+ margin-bottom: 0;
+ }
+ .sidebar-card .helper-box {
+ margin-top: 0;
+ }
+ .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;
+ }
.page-tabs-wrap {
max-width: 1200px;
margin: 18px auto 0;
@@ -105,6 +162,156 @@
padding: 14px;
box-shadow: 0 6px 18px rgba(36, 56, 89, 0.06);
}
+ .search-card {
+ position: relative;
+ overflow: hidden;
+ padding: 16px;
+ border-color: #cfdaf0;
+ background:
+ linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(247, 250, 255, 0.98)),
+ #fff;
+ box-shadow:
+ 0 20px 40px rgba(26, 42, 78, 0.08),
+ inset 0 1px 0 rgba(255, 255, 255, 0.75);
+ }
+ .search-card .title {
+ font-size: 18px;
+ color: #16263f;
+ }
+ .search-card .sub {
+ margin-bottom: 4px;
+ color: #53627a;
+ }
+ .search-layout {
+ display: grid;
+ grid-template-columns: minmax(420px, 1.08fr) minmax(0, 0.92fr);
+ gap: 8px 18px;
+ align-items: stretch;
+ }
+ .search-layout-single {
+ display: grid;
+ gap: 8px;
+ }
+ .search-layout-single .search-copy {
+ grid-column: auto;
+ }
+ .search-copy {
+ grid-column: 1 / -1;
+ }
+ .search-copy .sub:last-child {
+ margin-bottom: 0;
+ }
+ .search-header-row {
+ grid-column: 1 / -1;
+ display: grid;
+ grid-template-columns: inherit;
+ gap: 18px;
+ align-items: end;
+ }
+ .search-field-label {
+ margin: 0;
+ font-size: 13px;
+ color: #324056;
+ font-weight: 600;
+ line-height: 1.2;
+ }
+ .search-main-fields,
+ .search-side-fields {
+ min-width: 0;
+ display: grid;
+ gap: 6px;
+ align-content: stretch;
+ align-self: start;
+ }
+ .search-inline-row {
+ display: grid;
+ grid-template-columns: minmax(220px, 1fr) 76px;
+ gap: 10px;
+ align-items: end;
+ justify-content: stretch;
+ min-height: 44px;
+ }
+ .search-layout-single .search-inline-row {
+ grid-template-columns: minmax(0, 1fr) 76px;
+ }
+ .search-input-stack {
+ display: grid;
+ gap: 4px;
+ }
+ .search-input-stack label,
+ .search-input-stack .search-field-label {
+ margin: 0;
+ }
+ .search-inline-row .btns {
+ justify-content: flex-end;
+ }
+ .search-card .helper-box {
+ background: linear-gradient(180deg, #fbfdff, #f4f8ff);
+ border-color: #d9e4f8;
+ }
+ .search-card input {
+ min-height: 44px;
+ padding: 10px 12px;
+ border-radius: 14px;
+ border-color: #cbd8ee;
+ background: rgba(255, 255, 255, 0.96);
+ box-shadow: inset 0 1px 2px rgba(25, 36, 61, 0.04);
+ }
+ .search-card input:focus {
+ outline: none;
+ border-color: #0f6fff;
+ box-shadow: 0 0 0 4px rgba(15, 111, 255, 0.12);
+ }
+ .search-card .platform-picker {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 8px;
+ margin-top: 0;
+ padding: 3px;
+ height: 44px;
+ border: 1px solid #dbe5f5;
+ border-radius: 12px;
+ background: #fdfefe;
+ align-items: stretch;
+ }
+ .search-card .platform-card {
+ min-height: 34px;
+ height: 34px;
+ border-radius: 9px;
+ border-color: transparent;
+ background: transparent;
+ box-shadow: none;
+ gap: 8px;
+ padding: 0 9px;
+ flex-direction: row;
+ justify-content: center;
+ }
+ .search-card .platform-card.active {
+ border-color: #c6d8fb;
+ background: #edf4ff;
+ box-shadow:
+ 0 4px 10px rgba(15, 111, 255, 0.1),
+ inset 0 0 0 1px rgba(15, 111, 255, 0.1);
+ }
+ .search-card .btns {
+ margin-top: 0;
+ display: flex;
+ align-items: stretch;
+ height: 44px;
+ }
+ .search-card .btns .primary {
+ min-height: 44px;
+ height: 44px;
+ width: 76px;
+ min-width: 76px;
+ padding: 0 12px;
+ border-radius: 14px;
+ font-size: 14px;
+ line-height: 44px;
+ letter-spacing: 0.01em;
+ box-shadow: 0 10px 20px rgba(15, 111, 255, 0.18);
+ white-space: nowrap;
+ }
.title {
margin: 0 0 8px;
font-size: 16px;
@@ -208,6 +415,12 @@
}
@media (max-width: 640px) {
.credential-grid { grid-template-columns: 1fr; }
+ .search-card {
+ padding: 16px;
+ }
+ .search-card .platform-picker {
+ grid-template-columns: 1fr;
+ }
}
code {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
@@ -255,20 +468,22 @@
color: #15437d;
}
.platform-icon {
- width: 58px;
- height: 58px;
+ width: 22px;
+ height: 22px;
display: flex;
align-items: center;
justify-content: center;
+ flex: 0 0 auto;
}
.platform-icon svg {
- width: 34px;
- height: 34px;
+ width: 20px;
+ height: 20px;
display: block;
}
.platform-name {
- font-size: 13px;
+ font-size: 12px;
font-weight: 700;
+ white-space: nowrap;
}
.pill {
display: inline-block;
@@ -330,6 +545,64 @@
}
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
details summary { cursor: pointer; color: #294f88; }
+ .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;
+ }
+ .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;
+ display: grid;
+ gap: 14px;
+ }
+ .collapse-body .credential-grid {
+ margin-top: 0;
+ }
+ .collapse-section {
+ display: grid;
+ gap: 8px;
+ }
+ .section-label {
+ margin: 0;
+ color: #203554;
+ font-size: 13px;
+ font-weight: 700;
+ line-height: 1.4;
+ }
+ .collapse-section .title {
+ margin-bottom: 0;
+ font-size: 14px;
+ }
.tag {
display: inline-block;
margin: 2px 6px 2px 0;
@@ -424,8 +697,21 @@
}
@media (max-width: 1020px) {
.wrap { grid-template-columns: 1fr; }
+ .query-sidebar {
+ position: static;
+ }
+ .search-layout {
+ grid-template-columns: 1fr;
+ }
+ .search-header-row {
+ grid-template-columns: 1fr;
+ gap: 8px;
+ }
}
@media (max-width: 640px) {
+ .search-inline-row {
+ grid-template-columns: 1fr;
+ }
.platform-picker { grid-template-columns: 1fr; }
}
@@ -442,93 +728,104 @@
-
-
+
+
+
-
-
- 设备识别
- 当前模式基于 `dist/device_index.json` 内存索引查询,适合页面快速识别与联调。
-
-
-
- - Android / iOS:通常只需要
platform 和 model_raw。
- - HarmonyOS:通常只需要
platform 和 model_raw。
- - 请直接使用客户端原始上报值,不要手动改写。
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
标识索引查询
+
输入设备标识或常见设备名称即可检索索引。
+
+
+
+
+
- 识别结果
- 输入设备字段后开始识别。
+ 索引结果
+ 输入设备标识或设备名称后开始检索索引。
@@ -553,104 +850,171 @@
-
- 调试数据 JSON
- {}
-
+
+ 调试数据
+
+
-
-
- SQL 查询
- 当前模式直接查询 MySQL 主表 `mobilemodels.mm_device_catalog`,结果与线上 SQL 接入保持一致。
+
+
-
-
-
+
- SQL 查询结果
- 输入设备标识后开始查询 MySQL。
+ 设备标识结果
+ 输入设备标识 model_raw 后开始查询。
@@ -674,15 +1038,103 @@
+
+ 调试信息
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- 执行 SQL
- -- 暂无 SQL
+ 查询结果
+ 输入设备名称后开始查询。
- 返回 JSON
- {}
+ 返回结果
+
+
+
+
+ | # |
+ 命中标识 |
+ 设备 |
+ 品牌 / 厂商 |
+ 类型 |
+ 来源 |
+
+
+
+ | 暂无结果 |
+
+
+
+
+
+ 调试信息
+
+
@@ -691,7 +1143,7 @@
相关文档
- 这里统一整理页面调试、MySQL 接入和兼容查询相关说明,直接在当前页面查看,不再跳转到单独页面。
+ 这里保留完整文档阅读;查询约定已经前移到各个查询页的使用提示里。
@@ -705,71 +1157,6 @@
加载中...
-
-
-
主推查询表
-
mobilemodels.mm_device_catalog
-
-
-
兼容视图
-
mobilemodels.mm_device_lookup
-mobilemodels.mm_device_record
-mobilemodels.models
-python_services_test.models
-
-
-
归一化规则
-
全部转小写
-只保留 [0-9a-z中文]
-去掉空格、横线、下划线和其他标点
-
-SM-G9980 -> smg9980
-iPhone14,2 -> iphone142
-NOH-AL00 -> nohal00
-
-
-
-
-
-
主推 SQL
-
SELECT
- model,
- record_id,
- alias_norm,
- device_name,
- brand,
- manufacturer_brand,
- parent_brand,
- market_brand,
- device_type,
- source_file,
- section,
- source_rank,
- source_weight,
- code,
- code_alias,
- ver_name
-FROM mobilemodels.mm_device_catalog
-WHERE alias_norm = ?
-ORDER BY source_rank ASC, record_id ASC
-LIMIT 20;
-
-
-
兼容旧结构 SQL
-
SELECT
- model,
- dtype,
- brand,
- brand_title,
- code,
- code_alias,
- model_name,
- ver_name
-FROM python_services_test.models
-WHERE model = ?
-LIMIT 20;
-
-
@@ -804,11 +1191,17 @@ LIMIT 20;
const sqlStatementEl = document.getElementById("sqlStatement");
const sqlJsonOutputEl = document.getElementById("sqlJsonOutput");
const sqlReadonlyInfoEl = document.getElementById("sqlReadonlyInfo");
+ const deviceNameSqlSummaryEl = document.getElementById("deviceNameSqlSummary");
+ const deviceNameSqlResultBodyEl = document.getElementById("deviceNameSqlResultBody");
+ const deviceNameSqlStatementEl = document.getElementById("deviceNameSqlStatement");
+ const deviceNameSqlJsonOutputEl = document.getElementById("deviceNameSqlJsonOutput");
+ const deviceNameSqlReadonlyInfoEl = document.getElementById("deviceNameSqlReadonlyInfo");
const docPanelTitleEl = document.getElementById("docPanelTitle");
const docPanelPathEl = document.getElementById("docPanelPath");
const docPanelContentEl = document.getElementById("docPanelContent");
const indexQueryPanelEl = document.getElementById("indexQueryPanel");
const sqlQueryPanelEl = document.getElementById("sqlQueryPanel");
+ const deviceNameSqlQueryPanelEl = document.getElementById("deviceNameSqlQueryPanel");
const docsPanelEl = document.getElementById("docsPanel");
const topNavQueryEl = document.getElementById("topNavQuery");
const topNavDocsEl = document.getElementById("topNavDocs");
@@ -844,6 +1237,102 @@ LIMIT 20;
return out;
}
+ function parseAppleSeriesGeneration(name) {
+ const text = String(name || "").trim().replace(/\s+/g, " ");
+ if (!text) {
+ return null;
+ }
+
+ const ordinalMatch = text.match(/^(.*?)(?:\s*\((\d+)(?:st|nd|rd|th)\s+generation\)|\s+(\d+))$/i);
+ if (ordinalMatch) {
+ const baseLabel = String(ordinalMatch[1] || "").trim().replace(/\s+/g, " ");
+ const generation = Number(ordinalMatch[2] || ordinalMatch[3] || 0);
+ if (baseLabel && generation > 0) {
+ return {
+ baseLabel,
+ baseNorm: normalizeText(baseLabel),
+ generation,
+ chipLike: false,
+ };
+ }
+ }
+
+ const chipLike = /\((?:[^)]*\b(?:a\d{1,2}|m\d{1,2})\b[^)]*)\)$/i.test(text);
+ const baseLabel = text.replace(/\s*\([^)]*\)\s*$/g, "").trim().replace(/\s+/g, " ");
+ if (!baseLabel) {
+ return null;
+ }
+
+ return {
+ baseLabel,
+ baseNorm: normalizeText(baseLabel),
+ generation: null,
+ chipLike,
+ };
+ }
+
+ function buildSyntheticAppleGenerationAliases(records) {
+ const groups = new Map();
+ const aliasesByRecordId = new Map();
+
+ for (const record of records || []) {
+ const brand = record.market_brand || record.manufacturer_brand || record.brand || "";
+ if (brand !== "Apple") {
+ continue;
+ }
+
+ const parsed = parseAppleSeriesGeneration(record.device_name);
+ if (!parsed || !parsed.baseNorm) {
+ continue;
+ }
+
+ if (!groups.has(parsed.baseNorm)) {
+ groups.set(parsed.baseNorm, {
+ baseLabel: parsed.baseLabel,
+ items: [],
+ });
+ }
+
+ groups.get(parsed.baseNorm).items.push({
+ id: record.id,
+ deviceName: record.device_name,
+ baseLabel: parsed.baseLabel,
+ generation: parsed.generation,
+ chipLike: parsed.chipLike,
+ });
+ }
+
+ for (const group of groups.values()) {
+ const explicitGenerations = [...new Set(
+ group.items
+ .map((item) => item.generation)
+ .filter((value) => Number.isInteger(value) && value > 0)
+ )].sort((a, b) => a - b);
+ const maxExplicitGeneration = explicitGenerations.length
+ ? explicitGenerations[explicitGenerations.length - 1]
+ : 0;
+
+ for (const item of group.items) {
+ let generation = item.generation;
+ if (!generation && item.deviceName === group.baseLabel && maxExplicitGeneration >= 2) {
+ generation = 1;
+ } else if (!generation && item.chipLike && maxExplicitGeneration >= 1) {
+ generation = maxExplicitGeneration + 1;
+ }
+
+ if (!generation || generation <= 0) {
+ continue;
+ }
+
+ const syntheticAliases = aliasesByRecordId.get(item.id) || [];
+ syntheticAliases.push(`${group.baseLabel} ${generation}`);
+ aliasesByRecordId.set(item.id, syntheticAliases);
+ }
+ }
+
+ return aliasesByRecordId;
+ }
+
function sanitizeManagedBrandConfig(rawConfig) {
const manufacturersMap = new Map();
const brandsMap = new Map();
@@ -1139,7 +1628,23 @@ LIMIT 20;
function applyManagedBrandConfigToRecords() {
recordById = new Map();
+ if (!indexData.lookup || typeof indexData.lookup !== "object") {
+ indexData.lookup = {};
+ }
+ const syntheticAliasesByRecordId = buildSyntheticAppleGenerationAliases(indexData.records || []);
for (const r of indexData.records || []) {
+ const extraAliases = syntheticAliasesByRecordId.get(r.id) || [];
+ r.aliases = normalizeAliasList(r.device_name, [...(Array.isArray(r.aliases) ? r.aliases : []), ...extraAliases]);
+ for (const alias of extraAliases) {
+ const key = normalizeText(alias);
+ if (!key) continue;
+ const ids = Array.isArray(indexData.lookup[key]) ? indexData.lookup[key] : [];
+ if (!ids.includes(r.id)) {
+ ids.push(r.id);
+ }
+ indexData.lookup[key] = ids;
+ }
+
const mappedBrand = r.market_brand || r.manufacturer_brand || r.brand;
const mappedManufacturer =
managedBrandToManufacturer.get(mappedBrand)
@@ -1523,6 +2028,7 @@ LIMIT 20;
});
indexQueryPanelEl.classList.toggle("hidden", tabName !== "index-query");
sqlQueryPanelEl.classList.toggle("hidden", tabName !== "sql-query");
+ deviceNameSqlQueryPanelEl.classList.toggle("hidden", tabName !== "device-name-sql-query");
try {
localStorage.setItem(QUERY_PAGE_TAB_STORAGE_KEY, tabName);
} catch {
@@ -1534,11 +2040,13 @@ LIMIT 20;
const params = new URLSearchParams(window.location.search);
const view = params.get("view") || "";
const docsMode = view === "docs";
+ document.body.classList.toggle("docs-mode", docsMode);
docsPanelEl.classList.toggle("hidden", !docsMode);
document.querySelector(".page-tabs-wrap").classList.toggle("hidden", docsMode);
if (docsMode) {
indexQueryPanelEl.classList.add("hidden");
sqlQueryPanelEl.classList.add("hidden");
+ deviceNameSqlQueryPanelEl.classList.add("hidden");
topNavQueryEl.classList.remove("active");
topNavDocsEl.classList.add("active");
return;
@@ -1551,6 +2059,7 @@ LIMIT 20;
} catch {
topNavQueryEl.classList.add("active");
topNavDocsEl.classList.remove("active");
+ document.body.classList.remove("docs-mode");
docsPanelEl.classList.add("hidden");
activateTopTab("sql-query");
}
@@ -1618,12 +2127,14 @@ LIMIT 20;
["User", data.mysql_reader_user || "-"],
["Password", data.mysql_reader_password || "-"],
];
- sqlReadonlyInfoEl.innerHTML = items.map(([label, value]) => `
+ const html = items.map(([label, value]) => `
${escapeHtml(label)}
${escapeHtml(value)}
`).join("");
+ sqlReadonlyInfoEl.innerHTML = html;
+ deviceNameSqlReadonlyInfoEl.innerHTML = html;
}
async function loadReadonlyInfo() {
@@ -1631,12 +2142,14 @@ LIMIT 20;
const data = await fetchJson("/api/status", { cache: "no-store" });
renderReadonlyInfo(data);
} catch (err) {
- sqlReadonlyInfoEl.innerHTML = `
+ const html = `
`;
+ sqlReadonlyInfoEl.innerHTML = html;
+ deviceNameSqlReadonlyInfoEl.innerHTML = html;
}
}
@@ -1675,6 +2188,7 @@ LIMIT 20;
reportModelRaw: document.getElementById("reportModelRaw").value,
sqlPlatform: document.getElementById("sqlPlatform").value,
sqlModelRaw: document.getElementById("sqlModelRaw").value,
+ deviceNameSqlInput: document.getElementById("deviceNameSqlInput").value,
};
}
@@ -1689,6 +2203,7 @@ LIMIT 20;
setValue("reportModelRaw", state.reportModelRaw);
setValue("sqlPlatform", state.sqlPlatform);
setValue("sqlModelRaw", state.sqlModelRaw);
+ setValue("deviceNameSqlInput", state.deviceNameSqlInput);
}
function saveQueryFormState() {
@@ -1714,6 +2229,7 @@ LIMIT 20;
"reportModelRaw",
"sqlPlatform",
"sqlModelRaw",
+ "deviceNameSqlInput",
];
for (const id of ids) {
const el = document.getElementById(id);
@@ -1762,30 +2278,50 @@ LIMIT 20;
e.preventDefault();
document.getElementById("sqlQueryBtn").click();
});
+ document.getElementById("deviceNameSqlInput").addEventListener("keydown", (e) => {
+ if (e.key !== "Enter") return;
+ e.preventDefault();
+ document.getElementById("deviceNameSqlQueryBtn").click();
+ });
+
+ function renderSqlResult(output, options = {}) {
+ const summaryEl = options.summaryEl || sqlSummaryEl;
+ const statementEl = options.statementEl || sqlStatementEl;
+ const resultBodyEl = options.resultBodyEl || sqlResultBodyEl;
+ const jsonOutputEl = options.jsonOutputEl || sqlJsonOutputEl;
+ const strategyLabel = options.defaultStrategyLabel || "alias_norm 精确匹配";
+ const description = options.summaryDescription || "当前模式直接查询 MySQL 主表 mm_device_catalog。";
- function renderSqlResult(output) {
if (output.error) {
- sqlSummaryEl.textContent = output.error;
- sqlStatementEl.textContent = "-- SQL 执行失败";
- sqlResultBodyEl.innerHTML = `| ${escapeHtml(output.error)} |
`;
- sqlJsonOutputEl.textContent = JSON.stringify(output, null, 2);
+ summaryEl.textContent = output.error;
+ statementEl.textContent = "-- SQL 执行失败";
+ resultBodyEl.innerHTML = `| ${escapeHtml(output.error)} |
`;
+ jsonOutputEl.textContent = JSON.stringify(output, null, 2);
return;
}
- sqlSummaryEl.innerHTML = `
+ summaryEl.innerHTML = `
查询键: ${escapeHtml(output.alias_norm || "-")}
命中: ${Number(output.row_count || 0)}
limit: ${Number(output.limit || 0)}
+ 命中方式: ${escapeHtml(output.match_strategy_label || strategyLabel)}
- 当前模式直接查询 MySQL 主表 mm_device_catalog。
+ ${description}
+
+
登录设备管理字段建议
+
推荐取 device_name / market_brand(为空退回 brand)/ parent_brand(为空退回 manufacturer_brand)/ device_type。
+
+ ${Array.isArray(output.resolved_device_names) && output.resolved_device_names.length
+ ? `设备名别名映射: ${escapeHtml(output.resolved_device_names.join(" | "))}
`
+ : ""}
`;
- sqlStatementEl.textContent = output.sql || "-- 暂无 SQL";
+ statementEl.textContent = output.sql || "-- 暂无 SQL";
if (!Array.isArray(output.rows) || !output.rows.length) {
- sqlResultBodyEl.innerHTML = `| 没有查到结果。 |
`;
+ resultBodyEl.innerHTML = `| 没有查到结果。 |
`;
} else {
- sqlResultBodyEl.innerHTML = output.rows.map((row, idx) => `
+ resultBodyEl.innerHTML = output.rows.map((row, idx) => `
| ${idx + 1} |
@@ -1795,7 +2331,7 @@ LIMIT 20;
| ${escapeHtml(row.device_name || "-")} |
品牌: ${escapeHtml(row.market_brand || row.brand || "-")}
- 厂商: ${escapeHtml(row.manufacturer_brand || "-")}
+ 厂商: ${escapeHtml(row.parent_brand || row.manufacturer_brand || "-")}
|
${escapeHtml(row.device_type || "-")} |
@@ -1806,7 +2342,7 @@ LIMIT 20;
`).join("");
}
- sqlJsonOutputEl.textContent = JSON.stringify(output, null, 2);
+ jsonOutputEl.textContent = JSON.stringify(output, null, 2);
}
document.getElementById("queryBtn").addEventListener("click", () => {
@@ -1854,11 +2390,47 @@ LIMIT 20;
}
});
+ document.getElementById("deviceNameSqlQueryBtn").addEventListener("click", async () => {
+ const deviceName = document.getElementById("deviceNameSqlInput").value.trim();
+
+ deviceNameSqlSummaryEl.textContent = "正在查询 MySQL,请稍候。";
+ deviceNameSqlStatementEl.textContent = "-- 查询进行中...";
+ deviceNameSqlResultBodyEl.innerHTML = ` |
| 查询进行中... |
`;
+
+ try {
+ const output = await fetchJson("/api/query-sql-device-name", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ device_name: deviceName,
+ limit: 20,
+ }),
+ });
+ renderSqlResult(output, {
+ summaryEl: deviceNameSqlSummaryEl,
+ statementEl: deviceNameSqlStatementEl,
+ resultBodyEl: deviceNameSqlResultBodyEl,
+ jsonOutputEl: deviceNameSqlJsonOutputEl,
+ defaultStrategyLabel: "device_name 模糊匹配",
+ summaryDescription: "当前模式仅支持设备名称搜索,直接查询 MySQL 主表 mm_device_catalog。",
+ });
+ saveQueryFormState();
+ } catch (err) {
+ renderSqlResult({ error: err.message || String(err) }, {
+ summaryEl: deviceNameSqlSummaryEl,
+ statementEl: deviceNameSqlStatementEl,
+ resultBodyEl: deviceNameSqlResultBodyEl,
+ jsonOutputEl: deviceNameSqlJsonOutputEl,
+ });
+ }
+ });
+
document.querySelectorAll(".page-tab").forEach((el) => {
el.addEventListener("click", () => {
const url = new URL(window.location.href);
url.searchParams.delete("view");
window.history.replaceState({}, "", url.toString());
+ document.body.classList.remove("docs-mode");
docsPanelEl.classList.add("hidden");
document.querySelector(".page-tabs-wrap").classList.remove("hidden");
topNavQueryEl.classList.add("active");