feat: 更新蓝牙体重秤小程序原型页面及后台管理页面
This commit is contained in:
@@ -122,11 +122,25 @@ if (title) {
|
||||
if (filePath) {
|
||||
document.getElementById('doc-path').textContent = filePath;
|
||||
/* 用绝对路径(/ 开头),避免 <base href="../"> 在不同浏览器中解析不一致 */
|
||||
fetch('/' + filePath)
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error('文件未找到 (' + res.status + ')');
|
||||
var fetchUrl = '/' + filePath;
|
||||
|
||||
function tryFetch(url, fallbackUrl) {
|
||||
return fetch(url).then(function(res) {
|
||||
if (!res.ok) throw new Error('HTTP ' + res.status);
|
||||
return res.text();
|
||||
})
|
||||
}).catch(function(err) {
|
||||
/* 主路径失败 → 尝试去掉开头的 / 重新请求 */
|
||||
if (fallbackUrl && url !== fallbackUrl) {
|
||||
return fetch(fallbackUrl).then(function(res) {
|
||||
if (!res.ok) throw new Error('HTTP ' + res.status + ' (fallback)');
|
||||
return res.text();
|
||||
});
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
tryFetch(fetchUrl, filePath)
|
||||
.then(md => {
|
||||
/* 检测是否为自带 HTML 结构的文件(如共性需求说明.md) */
|
||||
const isRawHtml = /^\s*<(?:style|div|!doctype|html)/i.test(md);
|
||||
@@ -227,9 +241,9 @@ if (filePath) {
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
document.getElementById('content').innerHTML = '<div class="loading error">加载失败:' + err.message + '<br><small style="color:var(--text-placeholder);margin-top:8px;display:block">请确认文件路径:' + filePath + '</small></div>';
|
||||
});
|
||||
.catch(err => {
|
||||
document.getElementById('content').innerHTML = '<div class="loading error">❌ 加载失败:' + err.message + '<br><small style="color:var(--text-placeholder);margin-top:8px;display:block">📁 文件路径:' + filePath + '<br>🔗 请求地址:' + fetchUrl + '<br>💡 请确认服务器已启动(双击 启动服务器.bat),且文件路径正确</small></div>';
|
||||
});
|
||||
} else {
|
||||
document.getElementById('content').innerHTML = '<div class="loading">未指定文件路径</div>';
|
||||
}
|
||||
|
||||
+111
-76
@@ -169,19 +169,22 @@ body { background: var(--background); font-family: -apple-system, BlinkMacSystem
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 本地服务器启动遮罩 -->
|
||||
<div class="launch-overlay" id="launch-overlay">
|
||||
<div class="launch-card">
|
||||
<div class="launch-card__icon" id="launch-icon">🚀</div>
|
||||
<div class="launch-card__title" id="launch-title">需要启动本地服务器</div>
|
||||
<div class="launch-card__sub" id="launch-sub">PRD文档预览等功能需要本地HTTP服务器</div>
|
||||
<div class="launch-card__actions" id="launch-actions">
|
||||
<a class="launch-card__btn" href="启动服务器.bat">⚡ 一键启动</a>
|
||||
<a class="launch-card__btn launch-card__btn--outline" href="http://localhost:8080/index.html">📖 直接打开原型</a>
|
||||
</div>
|
||||
<div class="launch-card__status" id="launch-status"><span class="launch-card__spinner"></span>等待服务器就绪...</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 本地服务器启动遮罩 -->
|
||||
<div class="launch-overlay" id="launch-overlay">
|
||||
<div class="launch-card">
|
||||
<div class="launch-card__icon" id="launch-icon">🚀</div>
|
||||
<div class="launch-card__title" id="launch-title">需要启动本地服务器</div>
|
||||
<div class="launch-card__sub" id="launch-sub">PRD文档预览、Markdown查看器等功能需要本地HTTP服务器。<br>请双击项目根目录下的 <code style="background:#f0f2f8;padding:2px 8px;border-radius:4px;">启动服务器.bat</code></div>
|
||||
<div class="launch-card__actions" id="launch-actions">
|
||||
<a class="launch-card__btn" href="启动服务器.bat" download>⚡ 下载启动脚本</a>
|
||||
<a class="launch-card__btn launch-card__btn--outline" href="http://localhost:8080/index.html">📖 直接打开原型</a>
|
||||
</div>
|
||||
<div style="margin-top:12px;text-align:center;">
|
||||
<a href="javascript:void(0)" id="launch-dismiss-btn" style="font-size:13px;color:var(--text-placeholder);text-decoration:none;border-bottom:1px dashed var(--text-placeholder);padding-bottom:1px;">跳过,直接浏览导航页</a>
|
||||
</div>
|
||||
<div class="launch-card__status" id="launch-status"><span class="launch-card__spinner"></span>等待服务器就绪(检测中...)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header">
|
||||
<div class="header__inner">
|
||||
<div class="header__title">健康CQ升级项目 · 高保真交互原型</div>
|
||||
@@ -196,75 +199,99 @@ body { background: var(--background); font-family: -apple-system, BlinkMacSystem
|
||||
/* ===== file:// 协议检测 & 服务器自动启动 ===== */
|
||||
(function() {
|
||||
if (location.protocol !== 'file:') return; /* 非 file:// 协议,无需处理 */
|
||||
|
||||
|
||||
var overlay = document.getElementById('launch-overlay');
|
||||
var launchIcon = document.getElementById('launch-icon');
|
||||
var launchTitle = document.getElementById('launch-title');
|
||||
var launchSub = document.getElementById('launch-sub');
|
||||
var launchStatus = document.getElementById('launch-status');
|
||||
var launchActions = document.getElementById('launch-actions');
|
||||
|
||||
/* 步骤1: 先静默检测服务器是否已在运行(不显示遮罩) */
|
||||
var dismissed = false; /* 用户是否已点击"跳过" */
|
||||
|
||||
/* 服务器检测:只用 fetch(比 iframe 更可靠,不会因跨域判断误报) */
|
||||
function checkServer(callback) {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = 'http://localhost:8080/index.html';
|
||||
var done = false;
|
||||
var t = setTimeout(function() {
|
||||
if (!done) { done = true; callback(false); }
|
||||
}, 2000);
|
||||
iframe.onload = function() {
|
||||
if (!done) { done = true; clearTimeout(t); callback(true); }
|
||||
};
|
||||
iframe.onerror = function() {
|
||||
if (!done) { done = true; clearTimeout(t); callback(false); }
|
||||
};
|
||||
document.body.appendChild(iframe);
|
||||
try {
|
||||
var controller = new AbortController();
|
||||
var timeout = setTimeout(function() {
|
||||
controller.abort();
|
||||
callback(false);
|
||||
}, 3000);
|
||||
|
||||
fetch('http://localhost:8080/index.html?' + Date.now(), {
|
||||
mode: 'no-cors',
|
||||
cache: 'no-store',
|
||||
signal: controller.signal
|
||||
}).then(function() {
|
||||
/* fetch 成功 → 服务器可达(响应了 HTTP,即使是 404 也算可达) */
|
||||
clearTimeout(timeout);
|
||||
callback(true);
|
||||
}).catch(function(err) {
|
||||
/* fetch 失败 → 服务器不可达(连接拒绝/DNS 失败等) */
|
||||
clearTimeout(timeout);
|
||||
callback(false);
|
||||
});
|
||||
} catch (e) {
|
||||
/* AbortController / fetch 不可用(极旧浏览器)→ 保守:认为服务器不在线 */
|
||||
callback(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* 步骤2: 服务器已运行 → 无感跳转 */
|
||||
|
||||
/* 服务器已运行 → 无感跳转 */
|
||||
function redirectToServer() {
|
||||
location.replace('http://localhost:8080/index.html');
|
||||
}
|
||||
|
||||
/* 步骤3: 显示遮罩 + 轮询 */
|
||||
|
||||
/* 显示遮罩 + 轮询 */
|
||||
function showOverlay() {
|
||||
overlay.classList.add('launch-overlay--show');
|
||||
launchIcon.textContent = '🚀';
|
||||
launchIcon.className = 'launch-card__icon';
|
||||
launchTitle.textContent = '需要启动本地服务器';
|
||||
launchSub.textContent = 'PRD文档预览等功能需要本地HTTP服务器,请双击项目根目录下的 启动服务器.bat';
|
||||
launchStatus.innerHTML = '<span class="launch-card__spinner"></span>等待服务器就绪...';
|
||||
launchActions.innerHTML = '<a class="launch-card__btn" href="启动服务器.bat">⚡ 一键启动</a> <a class="launch-card__btn launch-card__btn--outline" href="http://localhost:8080/index.html">📖 直接打开原型</a>';
|
||||
|
||||
/* 轮询:每2秒检测一次,最多30次(60秒) */
|
||||
|
||||
/* 轮询:每2秒检测一次,最多60次(120秒) */
|
||||
var attempts = 0;
|
||||
var maxAttempts = 60;
|
||||
var pollTimer = setInterval(function() {
|
||||
if (dismissed) { clearInterval(pollTimer); return; }
|
||||
attempts++;
|
||||
checkServer(function(ok) {
|
||||
if (dismissed) return;
|
||||
if (ok) {
|
||||
clearInterval(pollTimer);
|
||||
launchIcon.textContent = '✅';
|
||||
launchIcon.className = 'launch-card__icon launch-card__icon--ok';
|
||||
launchTitle.textContent = '服务器已就绪';
|
||||
launchSub.textContent = '即将自动跳转...';
|
||||
launchStatus.innerHTML = '<a href="http://localhost:8080/index.html" style="color:var(--primary)">立即跳转</a>';
|
||||
launchStatus.innerHTML = '<a href="http://localhost:8080/index.html" style="color:var(--primary);font-weight:600;">立即跳转 →</a>';
|
||||
launchActions.innerHTML = '<a class="launch-card__btn launch-card__btn--outline" href="http://localhost:8080/index.html">📖 打开原型导航</a>';
|
||||
setTimeout(redirectToServer, 500);
|
||||
setTimeout(redirectToServer, 600);
|
||||
}
|
||||
if (attempts >= maxAttempts) {
|
||||
clearInterval(pollTimer);
|
||||
launchTitle.textContent = '未检测到服务器';
|
||||
launchSub.textContent = '请确认已双击运行 启动服务器.bat,然后刷新页面或点击下方按钮';
|
||||
launchStatus.innerHTML = '⏱ 等待超时(120秒)';
|
||||
launchActions.innerHTML = '<button class="launch-card__btn" onclick="location.reload()">🔄 刷新重试</button> <a class="launch-card__btn launch-card__btn--outline" href="http://localhost:8080/index.html">📖 直接打开原型</a>';
|
||||
}
|
||||
});
|
||||
if (attempts >= 30) {
|
||||
clearInterval(pollTimer);
|
||||
launchTitle.textContent = '未检测到服务器';
|
||||
launchSub.textContent = '请确认 启动服务器.bat 已运行,然后刷新页面';
|
||||
launchStatus.innerHTML = '⏱ 等待超时(60秒)';
|
||||
launchActions.innerHTML = '<button class="launch-card__btn" onclick="location.reload()">🔄 刷新重试</button>';
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
|
||||
/* 绑定"跳过"按钮 */
|
||||
function bindDismissBtn() {
|
||||
var btn = document.getElementById('launch-dismiss-btn');
|
||||
if (btn) {
|
||||
btn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
dismissed = true;
|
||||
overlay.classList.remove('launch-overlay--show');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* 初始绑定 */
|
||||
bindDismissBtn();
|
||||
|
||||
/* === 主流程:先静默检测 === */
|
||||
checkServer(function(running) {
|
||||
if (dismissed) return;
|
||||
if (running) {
|
||||
/* 服务器已在运行 → 直接跳转,用户全程无感 */
|
||||
redirectToServer();
|
||||
@@ -1076,7 +1103,7 @@ function cardHTML(mod, uid) {
|
||||
const icon = p.dialog ? ICONS.dialog : ICONS.page;
|
||||
return `<a class="page-popover__item${dcls}" href="${mod.dir}/${p.file}" target="_blank">${icon}${p.label}</a>`;
|
||||
}).join('');
|
||||
pageCountHtml = `<span class="module-card__page-count" data-pop="${uid}" onclick="event.stopPropagation();togglePop('${uid}')">${mod.pages.length} 页</span><div class="page-popover" id="pop-${uid}" onclick="event.stopPropagation()"><div class="page-popover__title">页面列表</div>${popItems}</div>`;
|
||||
pageCountHtml = `<span class="module-card__page-count" data-pop="${uid}" onclick="togglePop('${uid}',event)">${mod.pages.length} 页</span><div class="page-popover" id="pop-${uid}"><div class="page-popover__title">页面列表</div>${popItems}</div>`;
|
||||
}
|
||||
|
||||
/* 底部行占位:无内容时保持高度一致 */
|
||||
@@ -1084,29 +1111,37 @@ function cardHTML(mod, uid) {
|
||||
return `<div class="${cls}" ${onclick}>${watermarkHtml}<div class="module-card__header"><span class="module-card__name">${mod.name}</span>${statusHtml}</div><div class="module-card__bottom">${prdLink}${pageCountHtml}${bottomPlaceholder}</div></div>`;
|
||||
}
|
||||
|
||||
function togglePop(uid) {
|
||||
const el = document.getElementById('pop-' + uid);
|
||||
if (openPopover && openPopover !== el) {
|
||||
openPopover.classList.remove('page-popover--visible');
|
||||
openPopover.closest('.module-card').classList.remove('module-card--pop-open');
|
||||
}
|
||||
el.classList.toggle('page-popover--visible');
|
||||
const card = el.closest('.module-card');
|
||||
if (el.classList.contains('page-popover--visible')) {
|
||||
card.classList.add('module-card--pop-open');
|
||||
openPopover = el;
|
||||
} else {
|
||||
card.classList.remove('module-card--pop-open');
|
||||
openPopover = null;
|
||||
}
|
||||
}
|
||||
document.addEventListener('click', () => {
|
||||
if (openPopover) {
|
||||
openPopover.closest('.module-card').classList.remove('module-card--pop-open');
|
||||
openPopover.classList.remove('page-popover--visible');
|
||||
openPopover = null;
|
||||
}
|
||||
});
|
||||
function togglePop(uid, e) {
|
||||
if (e) { e.stopPropagation(); e.preventDefault(); }
|
||||
const el = document.getElementById('pop-' + uid);
|
||||
if (!el) return;
|
||||
if (openPopover && openPopover !== el) {
|
||||
openPopover.classList.remove('page-popover--visible');
|
||||
var prevCard = openPopover.closest('.module-card');
|
||||
if (prevCard) prevCard.classList.remove('module-card--pop-open');
|
||||
}
|
||||
el.classList.toggle('page-popover--visible');
|
||||
const card = el.closest('.module-card');
|
||||
if (el.classList.contains('page-popover--visible')) {
|
||||
if (card) card.classList.add('module-card--pop-open');
|
||||
openPopover = el;
|
||||
} else {
|
||||
if (card) card.classList.remove('module-card--pop-open');
|
||||
openPopover = null;
|
||||
}
|
||||
}
|
||||
/* 点击页面任意空白处关闭 popover(不依赖 stopPropagation) */
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!openPopover) return;
|
||||
/* 点击在 popover 内部 或 页数徽章上 → 不关闭 */
|
||||
if (e.target.closest('.page-popover')) return;
|
||||
if (e.target.closest('.module-card__page-count')) return;
|
||||
/* 其他情况 → 关闭 */
|
||||
var card = openPopover.closest('.module-card');
|
||||
if (card) card.classList.remove('module-card--pop-open');
|
||||
openPopover.classList.remove('page-popover--visible');
|
||||
openPopover = null;
|
||||
});
|
||||
|
||||
/* 生成 Tab 内容 */
|
||||
let uidCounter = 0;
|
||||
|
||||
@@ -137,6 +137,23 @@
|
||||
}
|
||||
.add-member-card:hover { border-color: var(--primary); color: var(--primary); }
|
||||
.add-member-card .plus { font-size: 20px; font-weight: 300; }
|
||||
.add-member-card--self {
|
||||
background: var(--primary-light); border-color: var(--primary); color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
.add-member-card--self:hover { background: #d6e8ff; }
|
||||
|
||||
/* 空状态 */
|
||||
.member-empty {
|
||||
text-align: center; padding: 50px 20px 30px;
|
||||
}
|
||||
.member-empty__icon { font-size: 52px; margin-bottom: 12px; opacity: 0.35; }
|
||||
.member-empty__text { font-size: 14px; color: var(--text-placeholder); }
|
||||
.member-empty__sub { font-size: 12px; color: #bbb; margin-top: 4px; }
|
||||
|
||||
/* 底部双按钮 */
|
||||
.bottom-actions { display: flex; gap: 10px; margin-top: 4px; }
|
||||
.bottom-actions .add-member-card { flex: 1; }
|
||||
|
||||
/* Toast */
|
||||
.toast {
|
||||
@@ -295,29 +312,28 @@
|
||||
<div id="membersB2" style="display:none;">
|
||||
<div class="section-header">
|
||||
<span class="section-title" id="sectionTitleB2">已录入成员</span>
|
||||
<button class="sync-btn" id="syncBtnB2" onclick="syncMembers()">同步成员至设备</button>
|
||||
<button class="sync-btn sync-btn--disabled" id="syncBtnB2" onclick="syncMembers()">同步成员至设备</button>
|
||||
</div>
|
||||
<div class="sync-hint" id="syncHintB2">当前成员信息未同步至设备,请在连接设备的状态下,点击同步按钮完成信息同步</div>
|
||||
<div class="member-list">
|
||||
<div class="member-card">
|
||||
<div class="member-avatar member-avatar--male">张</div>
|
||||
<div class="member-info">
|
||||
<div class="member-top">
|
||||
<span class="member-name">张建国</span>
|
||||
<span class="member-tag--self">本人</span>
|
||||
<span class="member-sync member-sync--pending">未同步</span>
|
||||
</div>
|
||||
<div class="member-detail">男 · 45岁 · 175cm</div>
|
||||
<div class="member-actions">
|
||||
<button class="action-btn action-btn--edit" onclick="showToast('编辑功能开发中')">编辑</button>
|
||||
<button class="action-btn action-btn--delete" onclick="showToast('删除功能开发中')">删除</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加按钮 -->
|
||||
<div class="add-member-card" onclick="location.href='add-member.html'">
|
||||
<span class="plus">+</span> 添加新成员
|
||||
<!-- 空状态 -->
|
||||
<div class="member-empty" id="memberEmptyB2">
|
||||
<div class="member-empty__icon">👥</div>
|
||||
<div class="member-empty__text">暂无成员</div>
|
||||
<div class="member-empty__sub">请添加成员以使用设备测量</div>
|
||||
</div>
|
||||
|
||||
<div class="member-list" id="memberListB2">
|
||||
<!-- 成员卡片动态填充 -->
|
||||
|
||||
<!-- 底部按钮区 -->
|
||||
<div class="bottom-actions" id="bottomActionsB2">
|
||||
<div class="add-member-card add-member-card--self" id="addSelfBtnB2" onclick="addSelfMember()">
|
||||
👤 一键添加本人
|
||||
</div>
|
||||
<div class="add-member-card" onclick="location.href='add-member.html'">
|
||||
<span class="plus">+</span> 添加新成员
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /membersB2 -->
|
||||
@@ -359,7 +375,7 @@
|
||||
status: 'offline',
|
||||
statusText: '未连接',
|
||||
membersDiv: 'membersB2',
|
||||
memberCount: 1,
|
||||
memberCount: 0,
|
||||
sectionTitleId: 'sectionTitleB2',
|
||||
syncBtnId: 'syncBtnB2'
|
||||
}
|
||||
@@ -388,10 +404,70 @@
|
||||
syncBtn.disabled = true;
|
||||
}
|
||||
|
||||
// 成员数为 0 时的特殊处理
|
||||
if (data.memberCount === 0) {
|
||||
// 隐藏同步按钮和提示
|
||||
syncBtn.style.display = 'none';
|
||||
var hintId = 'syncHint' + device.toUpperCase();
|
||||
var hintEl = document.getElementById(hintId);
|
||||
if (hintEl) hintEl.style.display = 'none';
|
||||
}
|
||||
|
||||
// 更新返回按钮,带上设备参数
|
||||
document.querySelector('.nav-bar .back').href = 'main.html#tab-device-paired';
|
||||
})();
|
||||
|
||||
/* 一键添加本人 */
|
||||
function addSelfMember() {
|
||||
var params = new URLSearchParams(location.search);
|
||||
var device = params.get('device') || 'b2';
|
||||
var devUpper = device.toUpperCase();
|
||||
|
||||
// 隐藏空状态
|
||||
var emptyEl = document.getElementById('memberEmpty' + devUpper);
|
||||
if (emptyEl) emptyEl.style.display = 'none';
|
||||
|
||||
// 构建本人成员卡片并插入 memberList 最前面
|
||||
var memberCard = document.createElement('div');
|
||||
memberCard.className = 'member-card';
|
||||
memberCard.innerHTML = ''
|
||||
+ '<div class="member-avatar member-avatar--male">张</div>'
|
||||
+ '<div class="member-info">'
|
||||
+ '<div class="member-top">'
|
||||
+ '<span class="member-name">张建国</span>'
|
||||
+ '<span class="member-tag--self">本人</span>'
|
||||
+ '<span class="member-sync member-sync--pending">未同步</span>'
|
||||
+ '</div>'
|
||||
+ '<div class="member-detail">男 · 45岁 · 175cm</div>'
|
||||
+ '<div class="member-actions">'
|
||||
+ '<button class="action-btn action-btn--edit" onclick="showToast(\'编辑功能开发中\')">编辑</button>'
|
||||
+ '<button class="action-btn action-btn--delete" onclick="showToast(\'删除功能开发中\')">删除</button>'
|
||||
+ '</div>'
|
||||
+ '</div>';
|
||||
|
||||
var memberList = document.getElementById('memberList' + devUpper);
|
||||
var bottomActions = document.getElementById('bottomActions' + devUpper);
|
||||
if (memberList && bottomActions) {
|
||||
memberList.insertBefore(memberCard, bottomActions);
|
||||
}
|
||||
|
||||
// 隐藏"一键添加本人"按钮
|
||||
var selfBtn = document.getElementById('addSelfBtn' + devUpper);
|
||||
if (selfBtn) selfBtn.style.display = 'none';
|
||||
|
||||
// 显示同步按钮和提示
|
||||
var syncBtn = document.getElementById('syncBtn' + devUpper);
|
||||
var hintEl = document.getElementById('syncHint' + devUpper);
|
||||
if (syncBtn) syncBtn.style.display = '';
|
||||
if (hintEl) hintEl.style.display = '';
|
||||
|
||||
// 更新标题
|
||||
var sectionTitle = document.getElementById('sectionTitle' + devUpper);
|
||||
if (sectionTitle) sectionTitle.textContent = '已录入成员(1/10)';
|
||||
|
||||
showToast('已添加本人');
|
||||
}
|
||||
|
||||
/* 同步成员至设备 */
|
||||
function syncMembers() {
|
||||
var overlay = document.getElementById('syncOverlay');
|
||||
|
||||
@@ -43,12 +43,27 @@
|
||||
.page-content { flex: 1; overflow-y: auto; background: var(--bg); padding: 16px; }
|
||||
.page-content::-webkit-scrollbar { display: none; }
|
||||
|
||||
/* 分类标签 */
|
||||
.category-tabs {
|
||||
display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 16px;
|
||||
}
|
||||
.category-tab {
|
||||
padding: 7px 16px; font-size: 13px; font-weight: 500;
|
||||
color: var(--text-secondary); cursor: pointer;
|
||||
border-radius: 20px; transition: all 0.2s; white-space: nowrap;
|
||||
background: #fff; border: 1px solid #e8e8e8;
|
||||
}
|
||||
.category-tab--active {
|
||||
background: var(--primary); color: #fff; border-color: var(--primary);
|
||||
}
|
||||
|
||||
.faq-list { display: flex; flex-direction: column; gap: 0; }
|
||||
.faq-item {
|
||||
background: #fff; padding: 14px 16px; border-bottom: 1px solid #f5f5f5;
|
||||
display: flex; align-items: center; gap: 12px; cursor: pointer;
|
||||
text-decoration: none; color: inherit;
|
||||
text-decoration: none; color: inherit; transition: all 0.2s;
|
||||
}
|
||||
.faq-item--hidden { display: none; }
|
||||
.faq-item:first-child { border-radius: 14px 14px 0 0; }
|
||||
.faq-item:last-child { border-radius: 0 0 14px 14px; border-bottom: none; }
|
||||
.faq-item:only-child { border-radius: 14px; }
|
||||
@@ -84,43 +99,51 @@
|
||||
</div>
|
||||
|
||||
<div class="page-content">
|
||||
<div class="faq-list">
|
||||
<a class="faq-item" href="help-detail.html?id=1">
|
||||
<!-- 分类标签 -->
|
||||
<div class="category-tabs" id="categoryTabs">
|
||||
<div class="category-tab category-tab--active" data-category="all" onclick="switchCategory(this)">全部</div>
|
||||
<div class="category-tab" data-category="connect" onclick="switchCategory(this)">连接配对</div>
|
||||
<div class="category-tab" data-category="measure" onclick="switchCategory(this)">使用测量</div>
|
||||
<div class="category-tab" data-category="data" onclick="switchCategory(this)">数据管理</div>
|
||||
</div>
|
||||
|
||||
<div class="faq-list" id="faqList">
|
||||
<a class="faq-item" href="help-detail.html?id=1" data-category="connect">
|
||||
<div class="faq-item__icon">📡</div>
|
||||
<div class="faq-item__info">
|
||||
<div class="faq-item__title">如何连接蓝牙设备?</div>
|
||||
</div>
|
||||
<span class="faq-item__arrow">›</span>
|
||||
</a>
|
||||
<a class="faq-item" href="help-detail.html?id=2">
|
||||
<a class="faq-item" href="help-detail.html?id=2" data-category="connect">
|
||||
<div class="faq-item__icon">📶</div>
|
||||
<div class="faq-item__info">
|
||||
<div class="faq-item__title">设备无法连接WiFi怎么办?</div>
|
||||
</div>
|
||||
<span class="faq-item__arrow">›</span>
|
||||
</a>
|
||||
<a class="faq-item" href="help-detail.html?id=3">
|
||||
<a class="faq-item" href="help-detail.html?id=3" data-category="measure">
|
||||
<div class="faq-item__icon">👥</div>
|
||||
<div class="faq-item__info">
|
||||
<div class="faq-item__title">如何添加和管理成员?</div>
|
||||
</div>
|
||||
<span class="faq-item__arrow">›</span>
|
||||
</a>
|
||||
<a class="faq-item" href="help-detail.html?id=4">
|
||||
<a class="faq-item" href="help-detail.html?id=4" data-category="measure">
|
||||
<div class="faq-item__icon">📊</div>
|
||||
<div class="faq-item__info">
|
||||
<div class="faq-item__title">测量数据不准确如何校准?</div>
|
||||
</div>
|
||||
<span class="faq-item__arrow">›</span>
|
||||
</a>
|
||||
<a class="faq-item" href="help-detail.html?id=5">
|
||||
<a class="faq-item" href="help-detail.html?id=5" data-category="data">
|
||||
<div class="faq-item__icon">🔄</div>
|
||||
<div class="faq-item__info">
|
||||
<div class="faq-item__title">数据补传功能如何使用?</div>
|
||||
</div>
|
||||
<span class="faq-item__arrow">›</span>
|
||||
</a>
|
||||
<a class="faq-item" href="help-detail.html?id=6">
|
||||
<a class="faq-item" href="help-detail.html?id=6" data-category="measure">
|
||||
<div class="faq-item__icon">🔋</div>
|
||||
<div class="faq-item__info">
|
||||
<div class="faq-item__title">设备电量消耗过快?</div>
|
||||
@@ -132,5 +155,45 @@
|
||||
|
||||
<div class="home-indicator"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* 分类标签切换 */
|
||||
function switchCategory(el) {
|
||||
var tabs = document.getElementById('categoryTabs');
|
||||
tabs.querySelectorAll('.category-tab').forEach(function(t) { t.classList.remove('category-tab--active'); });
|
||||
el.classList.add('category-tab--active');
|
||||
|
||||
var cat = el.dataset.category;
|
||||
var items = document.getElementById('faqList').querySelectorAll('.faq-item');
|
||||
var anyVisible = false;
|
||||
|
||||
items.forEach(function(item, i) {
|
||||
if (cat === 'all' || item.dataset.category === cat) {
|
||||
item.classList.remove('faq-item--hidden');
|
||||
anyVisible = true;
|
||||
} else {
|
||||
item.classList.add('faq-item--hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// 动态修正圆角:给第一个可见项和最后一个可见项加圆角
|
||||
items.forEach(function(item) { item.style.borderRadius = ''; });
|
||||
var visible = document.getElementById('faqList').querySelectorAll('.faq-item:not(.faq-item--hidden)');
|
||||
if (visible.length > 0) {
|
||||
if (visible.length === 1) {
|
||||
visible[0].style.borderRadius = '14px';
|
||||
} else {
|
||||
visible[0].style.borderRadius = '14px 14px 0 0';
|
||||
visible[visible.length - 1].style.borderRadius = '0 0 14px 14px';
|
||||
}
|
||||
// 隐藏最后一项的底边框
|
||||
visible[visible.length - 1].style.borderBottom = 'none';
|
||||
// 恢复非最后一项的底边框
|
||||
for (var j = 0; j < visible.length - 1; j++) {
|
||||
visible[j].style.borderBottom = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -76,9 +76,26 @@
|
||||
width: 100px; height: 100px; border-radius: 50%;
|
||||
background: rgba(255,255,255,0.08);
|
||||
}
|
||||
.home-header__title { font-size: 20px; font-weight: 700; color: #fff; position: relative; z-index: 1; }
|
||||
.home-header__title { font-size: 20px; font-weight: 700; color: #fff; position: relative; z-index: 1; cursor: default; }
|
||||
.home-header__sub { font-size: 13px; color: rgba(255,255,255,0.85); position: relative; z-index: 1; }
|
||||
|
||||
/* 运维模式标记 */
|
||||
.ops-badge {
|
||||
position: absolute; top: 12px; right: 12px; z-index: 10;
|
||||
display: none; align-items: center; gap: 6px;
|
||||
}
|
||||
.ops-badge--show { display: flex; }
|
||||
.ops-badge__label {
|
||||
font-size: 11px; font-weight: 700; color: #fff;
|
||||
background: rgba(0,0,0,0.25); padding: 4px 8px; border-radius: 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ops-badge__close {
|
||||
font-size: 10px; padding: 3px 8px; border-radius: 5px; border: 1px solid rgba(255,255,255,0.5);
|
||||
background: transparent; color: #fff; font-weight: 600; cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.device-card {
|
||||
background: #fff; border-radius: 14px; padding: 16px;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
@@ -155,6 +172,7 @@
|
||||
.device-section-title {
|
||||
font-size: 15px; font-weight: 600; color: #1a1a1a;
|
||||
margin-bottom: 12px; padding: 0 2px;
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
}
|
||||
.device-add-btn {
|
||||
width: 100%; height: 44px; border: 1px dashed #d9d9d9; border-radius: 12px;
|
||||
@@ -168,7 +186,14 @@
|
||||
.device-item {
|
||||
background: #fff; border-radius: 14px; padding: 16px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.04);
|
||||
position: relative;
|
||||
}
|
||||
.device-item__unpair {
|
||||
position: absolute; top: 10px; right: 12px;
|
||||
font-size: 12px; color: #999; border: none;
|
||||
background: none; cursor: pointer; padding: 2px 4px;
|
||||
}
|
||||
.device-item__unpair:active { color: #ff4d4f; }
|
||||
.device-item__header { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
|
||||
.device-item__icon {
|
||||
width: 44px; height: 44px; border-radius: 12px;
|
||||
@@ -471,9 +496,21 @@
|
||||
</div>
|
||||
|
||||
<!-- ===== 设备 Tab ===== -->
|
||||
<div class="tab-panel" id="panel-device">
|
||||
<div class="tab-panel" id="panel-device" style="padding:0;">
|
||||
<!-- 绿色头部 -->
|
||||
<div class="home-header" style="position:relative;">
|
||||
<div class="home-header__title" id="deviceHeaderTitle" onclick="onDeviceHeaderClick()">智能体重秤 👋</div>
|
||||
<div class="home-header__sub">设备智联 · 掌握健康</div>
|
||||
<!-- 运维模式标记 -->
|
||||
<div class="ops-badge" id="opsBadge">
|
||||
<span class="ops-badge__label">运维模式</span>
|
||||
<button class="ops-badge__close" onclick="event.stopPropagation();exitDeviceOpsMode()">关闭运维模式</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding:16px;">
|
||||
<!-- 空状态:未绑定设备 -->
|
||||
<div id="deviceEmpty" style="display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:500px;text-align:center;">
|
||||
<div id="deviceEmpty" style="display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:420px;text-align:center;">
|
||||
<div style="width:80px;height:80px;border-radius:50%;background:#f5f5f5;display:flex;align-items:center;justify-content:center;font-size:40px;margin-bottom:20px;">⚖️</div>
|
||||
<div style="font-size:15px;color:#999;margin-bottom:24px;">请绑定设备</div>
|
||||
<!-- [交互] 点击跳转至搜索设备页 -->
|
||||
@@ -482,9 +519,10 @@
|
||||
</div>
|
||||
<!-- 已配对设备列表 -->
|
||||
<div id="devicePaired" style="display:none;">
|
||||
<div class="device-section-title">已配对设备</div>
|
||||
<div class="device-section-title"><span>已配对设备</span><button class="btn btn--outline" id="btnUnpairAll" style="display:none;flex:none;height:28px;font-size:11px;padding:0 10px;color:#ff4d4f;border-color:#ff4d4f;" onclick="unpairAllDevices()">取消全部配对</button></div>
|
||||
<div class="device-list">
|
||||
<div class="device-item">
|
||||
<button class="device-item__unpair" onclick="unpairDevice('s1')">取消配对</button>
|
||||
<div class="device-item__header">
|
||||
<div class="device-item__icon">⚖️</div>
|
||||
<div class="device-item__info">
|
||||
@@ -500,6 +538,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="device-item">
|
||||
<button class="device-item__unpair" onclick="unpairDevice('b2')">取消配对</button>
|
||||
<div class="device-item__header">
|
||||
<div class="device-item__icon">⚖️</div>
|
||||
<div class="device-item__info">
|
||||
@@ -510,12 +549,13 @@
|
||||
</div>
|
||||
<div class="device-item__actions">
|
||||
<button class="btn btn--primary" onclick="connectDevice()">📡 连接设备</button>
|
||||
<button class="btn btn--outline" onclick="location.href='device-members.html?device=b2'">👥 成员管理(1)</button>
|
||||
<button class="btn btn--outline" onclick="location.href='device-members.html?device=b2'">👥 成员管理(0)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="device-add-btn" onclick="location.href='search-device.html'">+ 添加设备</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== 数据 Tab ===== -->
|
||||
@@ -908,10 +948,56 @@
|
||||
overlay.classList.remove('connect-overlay--show');
|
||||
_dataUploaded = true;
|
||||
showToast('数据已成功上传');
|
||||
}, 4000);
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
/* ===== 成员选择弹窗 ===== */
|
||||
/* ===== 设备Tab运维模式 ===== */
|
||||
var _deviceOpsClicks = 0;
|
||||
var _deviceOpsTimer = null;
|
||||
var _deviceOpsMode = false;
|
||||
|
||||
function onDeviceHeaderClick() {
|
||||
if (_deviceOpsMode) return;
|
||||
_deviceOpsClicks++;
|
||||
if (_deviceOpsClicks === 1) {
|
||||
_deviceOpsTimer = setTimeout(function() { _deviceOpsClicks = 0; }, 3000);
|
||||
}
|
||||
if (_deviceOpsClicks >= 5) {
|
||||
clearTimeout(_deviceOpsTimer);
|
||||
_deviceOpsClicks = 0;
|
||||
enterDeviceOpsMode();
|
||||
}
|
||||
}
|
||||
|
||||
function enterDeviceOpsMode() {
|
||||
_deviceOpsMode = true;
|
||||
document.getElementById('opsBadge').classList.add('ops-badge--show');
|
||||
var unpairBtn = document.getElementById('btnUnpairAll');
|
||||
if (unpairBtn) unpairBtn.style.display = '';
|
||||
showToast('已进入运维模式');
|
||||
}
|
||||
|
||||
function exitDeviceOpsMode() {
|
||||
_deviceOpsMode = false;
|
||||
document.getElementById('opsBadge').classList.remove('ops-badge--show');
|
||||
var unpairBtn = document.getElementById('btnUnpairAll');
|
||||
if (unpairBtn) unpairBtn.style.display = 'none';
|
||||
showToast('已退出运维模式');
|
||||
}
|
||||
|
||||
function unpairAllDevices() {
|
||||
if (!confirm('确定要取消全部设备配对吗?')) return;
|
||||
sessionStorage.removeItem('devicePaired');
|
||||
updateDeviceTabView();
|
||||
showToast('已取消全部配对');
|
||||
}
|
||||
|
||||
function unpairDevice(deviceId) {
|
||||
// 单个设备取消配对(模拟:提示后不做实际移除,保持列表展示)
|
||||
showToast('设备「' + (deviceId === 's1' ? '家庭蓝牙体脂秤 S1' : '办公蓝牙体重秤 B2') + '」已取消配对');
|
||||
}
|
||||
|
||||
/* ===== 成员选择弹窗 ===== */
|
||||
function openMemberModal() {
|
||||
document.getElementById('memberModal').classList.add('member-modal--show');
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}.link:hover{color:#40a9ff;}
|
||||
.link-danger{color:var(--danger);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}.link-danger:hover{color:#ff7875;}
|
||||
/* 成员查看弹窗 */
|
||||
/* 成员管理弹窗 */
|
||||
.modal-mask{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.45);z-index:1000;display:none;align-items:center;justify-content:center;}
|
||||
.modal-mask.active{display:flex;}
|
||||
.modal{background:#fff;border-radius:var(--radius-lg);width:90vw;max-width:1100px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 8px 40px rgba(0,0,0,0.15);}
|
||||
@@ -92,6 +92,30 @@
|
||||
.member-table th:nth-child(7){width:90px;}
|
||||
.member-table tbody tr:last-child td{border-bottom:none;}
|
||||
.tag-blue{background:var(--primary-light);color:var(--primary);display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
|
||||
/* 添加成员按钮 */
|
||||
.member-toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;}
|
||||
/* 添加成员弹窗 */
|
||||
.add-member-modal{width:560px;}
|
||||
.search-wrap{position:relative;margin-bottom:16px;}
|
||||
.search-wrap input{width:100%;height:40px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;}
|
||||
.search-wrap input:focus{border-color:var(--primary);}
|
||||
.search-dropdown{position:absolute;top:42px;left:0;right:0;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:0 4px 12px rgba(0,0,0,0.12);max-height:200px;overflow-y:auto;display:none;z-index:10;}
|
||||
.search-dropdown--show{display:block;}
|
||||
.search-dropdown__item{padding:10px 14px;cursor:pointer;font-size:var(--font-sm);border-bottom:1px solid #f5f5f5;display:flex;justify-content:space-between;align-items:center;}
|
||||
.search-dropdown__item:hover,.search-dropdown__item.active{background:var(--primary-light);}
|
||||
.search-dropdown__item:last-child{border-bottom:none;}
|
||||
.search-dropdown__name{font-weight:500;color:var(--text-primary);}
|
||||
.search-dropdown__no{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.employee-card{background:#F5F7FA;border-radius:var(--radius-md);padding:16px;margin-top:16px;display:none;}
|
||||
.employee-card--show{display:block;}
|
||||
.employee-card__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:12px;display:flex;align-items:center;gap:6px;}
|
||||
.employee-card__grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;}
|
||||
.employee-card__item{display:flex;flex-direction:column;gap:2px;}
|
||||
.employee-card__label{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.employee-card__value{font-size:var(--font-sm);color:var(--text-primary);font-weight:500;}
|
||||
.modal__footer{display:flex;justify-content:flex-end;gap:12px;padding:16px 24px;border-top:1px solid var(--border);}
|
||||
.btn-cancel{height:36px;padding:0 24px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;background:#fff;color:var(--text-primary);border:1px solid var(--border);transition:all .2s;}
|
||||
.btn-cancel:hover{color:var(--primary);border-color:var(--primary);}
|
||||
/* 分页 */
|
||||
.pagination{display:flex;align-items:center;justify-content:flex-end;padding:16px 24px;gap:8px;border-top:1px solid var(--border);}
|
||||
.pagination span{font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
@@ -169,6 +193,7 @@
|
||||
<button class="btn btn-default">导出列表数据</button>
|
||||
<button class="btn btn-default">查看导出任务</button>
|
||||
<button class="btn btn-success">批量入库导入</button>
|
||||
<button class="btn btn-success">批量添加成员</button>
|
||||
<button class="btn btn-default">查看导入任务</button>
|
||||
</div>
|
||||
<!-- 数据列表 -->
|
||||
@@ -177,11 +202,11 @@
|
||||
<table>
|
||||
<thead><tr><th>设备编码</th><th>设备名称</th><th>设备厂商</th><th>设备型号</th><th>所属单位</th><th>布放位置</th><th>入库日期</th><th>设备状态</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>BT-2024-001</td><td>蓝牙称A1</td><td>华为</td><td>HW-S100</td><td>采油一厂</td><td>职工活动中心1F</td><td>2024-03-15</td><td><span class="tag tag-green">正常</span></td><td><a class="link">详情</a> <a class="link" onclick="openMemberModal(0)">成员查看</a> <a class="link">编辑</a> <a class="link-danger">删除</a></td></tr>
|
||||
<tr><td>BT-2024-002</td><td>蓝牙称A2</td><td>小米</td><td>MI-Scale2</td><td>采油一厂</td><td>办公楼2F健身房</td><td>2024-03-18</td><td><span class="tag tag-green">正常</span></td><td><a class="link">详情</a> <a class="link" onclick="openMemberModal(1)">成员查看</a> <a class="link">编辑</a> <a class="link-danger">删除</a></td></tr>
|
||||
<tr><td>BT-2024-003</td><td>蓝牙称B1</td><td>乐心</td><td>LX-Pro</td><td>采油二厂</td><td>员工食堂入口</td><td>2024-04-02</td><td><span class="tag tag-orange">维护中</span></td><td><a class="link">详情</a> <a class="link" onclick="openMemberModal(2)">成员查看</a> <a class="link">编辑</a> <a class="link-danger">删除</a></td></tr>
|
||||
<tr><td>BT-2024-004</td><td>蓝牙称B2</td><td>华为</td><td>HW-S100</td><td>采油二厂</td><td>宿舍楼大厅</td><td>2024-04-10</td><td><span class="tag tag-green">正常</span></td><td><a class="link">详情</a> <a class="link" onclick="openMemberModal(3)">成员查看</a> <a class="link">编辑</a> <a class="link-danger">删除</a></td></tr>
|
||||
<tr><td>BT-2024-005</td><td>蓝牙称C1</td><td>小米</td><td>MI-Scale2</td><td>采气厂</td><td>综合办公楼1F</td><td>2024-05-08</td><td><span class="tag tag-red">已停用</span></td><td><a class="link">详情</a> <a class="link" onclick="openMemberModal(4)">成员查看</a> <a class="link">编辑</a> <a class="link-danger">删除</a></td></tr>
|
||||
<tr><td>BT-2024-001</td><td>蓝牙称A1</td><td>华为</td><td>HW-S100</td><td>采油一厂</td><td>职工活动中心1F</td><td>2024-03-15</td><td><span class="tag tag-green">正常</span></td><td><a class="link">详情</a> <a class="link" onclick="openMemberModal(0)">成员管理</a> <a class="link">编辑</a> <a class="link-danger">删除</a></td></tr>
|
||||
<tr><td>BT-2024-002</td><td>蓝牙称A2</td><td>小米</td><td>MI-Scale2</td><td>采油一厂</td><td>办公楼2F健身房</td><td>2024-03-18</td><td><span class="tag tag-green">正常</span></td><td><a class="link">详情</a> <a class="link" onclick="openMemberModal(1)">成员管理</a> <a class="link">编辑</a> <a class="link-danger">删除</a></td></tr>
|
||||
<tr><td>BT-2024-003</td><td>蓝牙称B1</td><td>乐心</td><td>LX-Pro</td><td>采油二厂</td><td>员工食堂入口</td><td>2024-04-02</td><td><span class="tag tag-orange">维护中</span></td><td><a class="link">详情</a> <a class="link" onclick="openMemberModal(2)">成员管理</a> <a class="link">编辑</a> <a class="link-danger">删除</a></td></tr>
|
||||
<tr><td>BT-2024-004</td><td>蓝牙称B2</td><td>华为</td><td>HW-S100</td><td>采油二厂</td><td>宿舍楼大厅</td><td>2024-04-10</td><td><span class="tag tag-green">正常</span></td><td><a class="link">详情</a> <a class="link" onclick="openMemberModal(3)">成员管理</a> <a class="link">编辑</a> <a class="link-danger">删除</a></td></tr>
|
||||
<tr><td>BT-2024-005</td><td>蓝牙称C1</td><td>小米</td><td>MI-Scale2</td><td>采气厂</td><td>综合办公楼1F</td><td>2024-05-08</td><td><span class="tag tag-red">已停用</span></td><td><a class="link">详情</a> <a class="link" onclick="openMemberModal(4)">成员管理</a> <a class="link">编辑</a> <a class="link-danger">删除</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -198,77 +223,207 @@
|
||||
|
||||
<script>function toggleSubgroup(h){var p=h.parentElement;var sidebar=p.closest("aside");if(sidebar)sidebar.querySelectorAll(".sidebar-subgroup.open").forEach(function(g){if(g!==p)g.classList.remove("open");});p.classList.toggle("open");}(function(){var a=document.querySelector('.sidebar-item.active');if(!a)return;var sidebar=document.querySelector('aside');if(sidebar)sidebar.querySelectorAll('.sidebar-subgroup.open').forEach(function(g){g.classList.remove('open');});var s=a.closest('.sidebar-subgroup');if(s)s.classList.add('open');})();
|
||||
|
||||
// 成员查看弹窗数据
|
||||
var deviceData = [
|
||||
{ code:'BT-2024-001', name:'蓝牙称A1', dept:'采油一厂' },
|
||||
{ code:'BT-2024-002', name:'蓝牙称A2', dept:'采油一厂' },
|
||||
{ code:'BT-2024-003', name:'蓝牙称B1', dept:'采油二厂' },
|
||||
{ code:'BT-2024-004', name:'蓝牙称B2', dept:'采油二厂' },
|
||||
{ code:'BT-2024-005', name:'蓝牙称C1', dept:'采气厂' }
|
||||
];
|
||||
var memberData = [
|
||||
[
|
||||
{ name:'张伟', idCard:'610102199001125517', gender:'男', age:36, height:175, isCQ:true, empNo:'CQ10023' },
|
||||
{ name:'李娜', idCard:'610103198805083024', gender:'女', age:38, height:162, isCQ:true, empNo:'CQ10045' },
|
||||
{ name:'王强', idCard:'610104199203176831', gender:'男', age:34, height:180, isCQ:false, empNo:'' }
|
||||
],
|
||||
[
|
||||
{ name:'赵敏', idCard:'610105199106224519', gender:'女', age:35, height:165, isCQ:true, empNo:'CQ10078' },
|
||||
{ name:'刘洋', idCard:'610106198912081237', gender:'男', age:37, height:172, isCQ:true, empNo:'CQ10091' }
|
||||
],
|
||||
[
|
||||
{ name:'陈磊', idCard:'610107199305142618', gender:'男', age:33, height:178, isCQ:true, empNo:'CQ20015' },
|
||||
{ name:'周芳', idCard:'610108199008273945', gender:'女', age:36, height:160, isCQ:false, empNo:'' },
|
||||
{ name:'吴刚', idCard:'610109198711054372', gender:'男', age:39, height:176, isCQ:true, empNo:'CQ20032' },
|
||||
{ name:'孙丽', idCard:'610110199204186523', gender:'女', age:34, height:163, isCQ:true, empNo:'CQ20041' }
|
||||
],
|
||||
[
|
||||
{ name:'马超', idCard:'610111199107093841', gender:'男', age:35, height:182, isCQ:true, empNo:'CQ20056' }
|
||||
],
|
||||
[
|
||||
{ name:'黄蓉', idCard:'610112199309157264', gender:'女', age:33, height:158, isCQ:true, empNo:'CQ30012' },
|
||||
{ name:'郭靖', idCard:'610113198806241058', gender:'男', age:38, height:179, isCQ:false, empNo:'' }
|
||||
]
|
||||
];
|
||||
// 成员管理弹窗数据
|
||||
var deviceData = [
|
||||
{ code:'BT-2024-001', name:'蓝牙称A1', dept:'采油一厂', model:'HW-S100' },
|
||||
{ code:'BT-2024-002', name:'蓝牙称A2', dept:'采油一厂', model:'MI-Scale2' },
|
||||
{ code:'BT-2024-003', name:'蓝牙称B1', dept:'采油二厂', model:'LX-Pro' },
|
||||
{ code:'BT-2024-004', name:'蓝牙称B2', dept:'采油二厂', model:'HW-S100' },
|
||||
{ code:'BT-2024-005', name:'蓝牙称C1', dept:'采气厂', model:'MI-Scale2' }
|
||||
];
|
||||
var memberData = [
|
||||
[
|
||||
{ name:'张伟', idCard:'610102199001125517', gender:'男', age:36, height:175, isCQ:true, empNo:'CQ10023', synced:true },
|
||||
{ name:'李娜', idCard:'610103198805083024', gender:'女', age:38, height:162, isCQ:true, empNo:'CQ10045', synced:true },
|
||||
{ name:'王强', idCard:'610104199203176831', gender:'男', age:34, height:180, isCQ:false, empNo:'', synced:false }
|
||||
],
|
||||
[
|
||||
{ name:'赵敏', idCard:'610105199106224519', gender:'女', age:35, height:165, isCQ:true, empNo:'CQ10078', synced:true },
|
||||
{ name:'刘洋', idCard:'610106198912081237', gender:'男', age:37, height:172, isCQ:true, empNo:'CQ10091', synced:true }
|
||||
],
|
||||
[
|
||||
{ name:'陈磊', idCard:'610107199305142618', gender:'男', age:33, height:178, isCQ:true, empNo:'CQ20015', synced:true },
|
||||
{ name:'周芳', idCard:'610108199008273945', gender:'女', age:36, height:160, isCQ:false, empNo:'', synced:false },
|
||||
{ name:'吴刚', idCard:'610109198711054372', gender:'男', age:39, height:176, isCQ:true, empNo:'CQ20032', synced:true },
|
||||
{ name:'孙丽', idCard:'610110199204186523', gender:'女', age:34, height:163, isCQ:true, empNo:'CQ20041', synced:false }
|
||||
],
|
||||
[
|
||||
{ name:'马超', idCard:'610111199107093841', gender:'男', age:35, height:182, isCQ:true, empNo:'CQ20056', synced:true }
|
||||
],
|
||||
[
|
||||
{ name:'黄蓉', idCard:'610112199309157264', gender:'女', age:33, height:158, isCQ:true, empNo:'CQ30012', synced:true },
|
||||
{ name:'郭靖', idCard:'610113198806241058', gender:'男', age:38, height:179, isCQ:false, empNo:'', synced:true }
|
||||
]
|
||||
];
|
||||
|
||||
function openMemberModal(idx) {
|
||||
var dev = deviceData[idx];
|
||||
var members = memberData[idx];
|
||||
document.getElementById('modalDevName').textContent = dev.name;
|
||||
document.getElementById('modalDevCode').textContent = dev.code;
|
||||
document.getElementById('modalDevDept').textContent = dev.dept;
|
||||
var tbody = document.getElementById('memberTbody');
|
||||
tbody.innerHTML = members.map(function(m) {
|
||||
var tagHtml = m.isCQ ? '<span class="tag-blue">是</span>' : '<span style="color:#999">否</span>';
|
||||
var empHtml = m.isCQ ? m.empNo : '-';
|
||||
return '<tr><td>'+m.name+'</td><td>'+m.idCard+'</td><td>'+m.gender+'</td><td>'+m.age+'</td><td>'+m.height+' cm</td><td>'+tagHtml+'</td><td>'+empHtml+'</td></tr>';
|
||||
}).join('');
|
||||
document.getElementById('memberModal').classList.add('active');
|
||||
}
|
||||
function closeMemberModal() {
|
||||
document.getElementById('memberModal').classList.remove('active');
|
||||
}
|
||||
</script>
|
||||
function openMemberModal(idx) {
|
||||
var dev = deviceData[idx];
|
||||
var members = memberData[idx];
|
||||
document.getElementById('modalDevName').textContent = dev.name;
|
||||
document.getElementById('modalDevCode').textContent = dev.code;
|
||||
document.getElementById('modalDevDept').textContent = dev.dept;
|
||||
document.getElementById('modalDevModel').textContent = dev.model;
|
||||
// 存储当前设备索引,供添加成员时使用
|
||||
document.getElementById('memberModal').setAttribute('data-device-idx', idx);
|
||||
var tbody = document.getElementById('memberTbody');
|
||||
tbody.innerHTML = members.map(function(m) {
|
||||
var tagHtml = m.isCQ ? '<span class="tag-blue">是</span>' : '<span style="color:#999">否</span>';
|
||||
var empHtml = m.isCQ ? m.empNo : '-';
|
||||
var syncHtml = m.synced ? '<span class="tag tag-green">是</span>' : '<span class="tag tag-orange">否</span>';
|
||||
return '<tr><td>'+m.name+'</td><td>'+m.idCard+'</td><td>'+m.gender+'</td><td>'+m.age+'</td><td>'+m.height+' cm</td><td>'+tagHtml+'</td><td>'+empHtml+'</td><td>'+syncHtml+'</td></tr>';
|
||||
}).join('');
|
||||
document.getElementById('memberModal').classList.add('active');
|
||||
}
|
||||
function closeMemberModal() {
|
||||
document.getElementById('memberModal').classList.remove('active');
|
||||
}
|
||||
|
||||
<!-- 成员查看弹窗 -->
|
||||
<div class="modal-mask" id="memberModal" onclick="if(event.target===this)closeMemberModal()">
|
||||
<div class="modal">
|
||||
<div class="modal__header">
|
||||
<h3>成员查看</h3>
|
||||
<button class="modal__close" onclick="closeMemberModal()">×</button>
|
||||
</div>
|
||||
<div class="modal__body">
|
||||
<div class="device-info">
|
||||
<div class="device-info__item"><span class="device-info__label">设备名称</span><span class="device-info__value" id="modalDevName">-</span></div>
|
||||
<div class="device-info__item"><span class="device-info__label">设备编码</span><span class="device-info__value" id="modalDevCode">-</span></div>
|
||||
<div class="device-info__item"><span class="device-info__label">所属单位</span><span class="device-info__value" id="modalDevDept">-</span></div>
|
||||
</div>
|
||||
<table class="member-table">
|
||||
<thead><tr><th>姓名</th><th>身份证</th><th>性别</th><th>年龄</th><th>身高</th><th>是否为CQ员工</th><th>员工编号</th></tr></thead>
|
||||
<tbody id="memberTbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
/* ===== 添加成员 ===== */
|
||||
var allEmployees = [
|
||||
{ name:'赵建国', empNo:'CQ10001', gender:'男', age:42, unit:'采油一厂', dept:'技术部-研发组' },
|
||||
{ name:'钱晓燕', empNo:'CQ10002', gender:'女', age:35, unit:'采油一厂', dept:'综合办公室' },
|
||||
{ name:'孙志强', empNo:'CQ10003', gender:'男', age:38, unit:'采油一厂', dept:'生产运行部' },
|
||||
{ name:'李红梅', empNo:'CQ20004', gender:'女', age:31, unit:'采油二厂', dept:'人力资源部' },
|
||||
{ name:'周文博', empNo:'CQ20005', gender:'男', age:45, unit:'采油二厂', dept:'安全环保部' },
|
||||
{ name:'吴晓峰', empNo:'CQ30006', gender:'男', age:33, unit:'采气厂', dept:'工程技术部' },
|
||||
{ name:'郑丽华', empNo:'CQ30007', gender:'女', age:40, unit:'采气厂', dept:'质量监督站' },
|
||||
{ name:'王大军', empNo:'CQ10008', gender:'男', age:37, unit:'采油一厂', dept:'设备管理部' }
|
||||
];
|
||||
var _selectedEmployee = null;
|
||||
|
||||
// 点击外部关闭搜索下拉
|
||||
document.addEventListener('click', function(e) {
|
||||
var wrap = document.querySelector('.search-wrap');
|
||||
if (wrap && !wrap.contains(e.target)) {
|
||||
document.getElementById('searchDropdown').classList.remove('search-dropdown--show');
|
||||
}
|
||||
});
|
||||
|
||||
function openAddMemberModal() {
|
||||
document.getElementById('addMemberModal').classList.add('active');
|
||||
document.getElementById('employeeSearch').value = '';
|
||||
document.getElementById('employeeCard').classList.remove('employee-card--show');
|
||||
document.getElementById('searchDropdown').classList.remove('search-dropdown--show');
|
||||
document.getElementById('btnConfirmAdd').disabled = true;
|
||||
_selectedEmployee = null;
|
||||
}
|
||||
|
||||
function closeAddMemberModal() {
|
||||
document.getElementById('addMemberModal').classList.remove('active');
|
||||
}
|
||||
|
||||
function searchEmployee() {
|
||||
var kw = document.getElementById('employeeSearch').value.trim().toLowerCase();
|
||||
var dropdown = document.getElementById('searchDropdown');
|
||||
if (!kw) {
|
||||
dropdown.classList.remove('search-dropdown--show');
|
||||
return;
|
||||
}
|
||||
var results = allEmployees.filter(function(e) {
|
||||
return e.name.toLowerCase().indexOf(kw) !== -1 || e.empNo.toLowerCase().indexOf(kw) !== -1;
|
||||
});
|
||||
if (results.length === 0) {
|
||||
dropdown.innerHTML = '<div class="search-dropdown__item" style="color:#999;cursor:default;">未找到匹配的员工</div>';
|
||||
} else {
|
||||
dropdown.innerHTML = results.map(function(e) {
|
||||
return '<div class="search-dropdown__item" onclick="selectEmployee(\''+e.empNo+'\')"><span class="search-dropdown__name">'+e.name+'</span><span class="search-dropdown__no">'+e.empNo+'</span></div>';
|
||||
}).join('');
|
||||
}
|
||||
dropdown.classList.add('search-dropdown--show');
|
||||
}
|
||||
|
||||
function selectEmployee(empNo) {
|
||||
var emp = allEmployees.find(function(e) { return e.empNo === empNo; });
|
||||
if (!emp) return;
|
||||
_selectedEmployee = emp;
|
||||
document.getElementById('employeeSearch').value = emp.name + ' (' + emp.empNo + ')';
|
||||
document.getElementById('searchDropdown').classList.remove('search-dropdown--show');
|
||||
// 填充员工信息卡片
|
||||
document.getElementById('empName').textContent = emp.name;
|
||||
document.getElementById('empNo').textContent = emp.empNo;
|
||||
document.getElementById('empGender').textContent = emp.gender;
|
||||
document.getElementById('empAge').textContent = emp.age;
|
||||
document.getElementById('empUnit').textContent = emp.unit;
|
||||
document.getElementById('empDept').textContent = emp.dept;
|
||||
document.getElementById('employeeCard').classList.add('employee-card--show');
|
||||
document.getElementById('btnConfirmAdd').disabled = false;
|
||||
}
|
||||
|
||||
function confirmAddMember() {
|
||||
if (!_selectedEmployee) return;
|
||||
var idx = parseInt(document.getElementById('memberModal').getAttribute('data-device-idx')) || 0;
|
||||
var emp = _selectedEmployee;
|
||||
// 检查是否已存在
|
||||
var exists = memberData[idx].some(function(m) { return m.empNo === emp.empNo; });
|
||||
if (exists) {
|
||||
alert('该员工已在该设备成员列表中');
|
||||
return;
|
||||
}
|
||||
memberData[idx].push({
|
||||
name: emp.name, idCard: '', gender: emp.gender, age: emp.age,
|
||||
height: 170, isCQ: true, empNo: emp.empNo, synced: false
|
||||
});
|
||||
// 刷新成员列表
|
||||
openMemberModal(idx);
|
||||
closeAddMemberModal();
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 成员管理弹窗 -->
|
||||
<div class="modal-mask" id="memberModal" onclick="if(event.target===this)closeMemberModal()">
|
||||
<div class="modal">
|
||||
<div class="modal__header">
|
||||
<h3>成员管理</h3>
|
||||
<button class="modal__close" onclick="closeMemberModal()">×</button>
|
||||
</div>
|
||||
<div class="modal__body">
|
||||
<div class="device-info">
|
||||
<div class="device-info__item"><span class="device-info__label">设备名称</span><span class="device-info__value" id="modalDevName">-</span></div>
|
||||
<div class="device-info__item"><span class="device-info__label">设备编码</span><span class="device-info__value" id="modalDevCode">-</span></div>
|
||||
<div class="device-info__item"><span class="device-info__label">设备型号</span><span class="device-info__value" id="modalDevModel">-</span></div>
|
||||
<div class="device-info__item"><span class="device-info__label">所属单位</span><span class="device-info__value" id="modalDevDept">-</span></div>
|
||||
</div>
|
||||
<div class="member-toolbar">
|
||||
<span style="font-size:var(--font-sm);font-weight:600;color:var(--text-primary);">成员列表</span>
|
||||
<button class="btn btn-primary" onclick="openAddMemberModal()">+ 添加成员</button>
|
||||
</div>
|
||||
<table class="member-table">
|
||||
<thead><tr><th>姓名</th><th>身份证</th><th>性别</th><th>年龄</th><th>身高</th><th>是否为CQ员工</th><th>员工编号</th><th>同步设备</th></tr></thead>
|
||||
<tbody id="memberTbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加成员弹窗 -->
|
||||
<div class="modal-mask" id="addMemberModal" onclick="if(event.target===this)closeAddMemberModal()">
|
||||
<div class="modal add-member-modal">
|
||||
<div class="modal__header">
|
||||
<h3>添加成员</h3>
|
||||
<button class="modal__close" onclick="closeAddMemberModal()">×</button>
|
||||
</div>
|
||||
<div class="modal__body">
|
||||
<div class="search-wrap">
|
||||
<input type="text" id="employeeSearch" placeholder="请输入员工姓名或编号搜索" oninput="searchEmployee()" onfocus="searchEmployee()" autocomplete="off">
|
||||
<div class="search-dropdown" id="searchDropdown"></div>
|
||||
</div>
|
||||
<div class="employee-card" id="employeeCard">
|
||||
<div class="employee-card__title">📋 员工信息</div>
|
||||
<div class="employee-card__grid">
|
||||
<div class="employee-card__item"><span class="employee-card__label">姓名</span><span class="employee-card__value" id="empName">-</span></div>
|
||||
<div class="employee-card__item"><span class="employee-card__label">员工编号</span><span class="employee-card__value" id="empNo">-</span></div>
|
||||
<div class="employee-card__item"><span class="employee-card__label">性别</span><span class="employee-card__value" id="empGender">-</span></div>
|
||||
<div class="employee-card__item"><span class="employee-card__label">年龄</span><span class="employee-card__value" id="empAge">-</span></div>
|
||||
<div class="employee-card__item"><span class="employee-card__label">单位</span><span class="employee-card__value" id="empUnit">-</span></div>
|
||||
<div class="employee-card__item"><span class="employee-card__label">部门</span><span class="employee-card__value" id="empDept">-</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal__footer">
|
||||
<button class="btn-cancel" onclick="closeAddMemberModal()">取消</button>
|
||||
<button class="btn btn-primary" id="btnConfirmAdd" onclick="confirmAddMember()" disabled>确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7,56 +7,66 @@ echo ============================================
|
||||
echo 健康CQ升级项目 · 高保真交互原型
|
||||
echo ============================================
|
||||
echo.
|
||||
echo [检测] 检查端口 8080 ...
|
||||
|
||||
:: ── 检查端口 8080 是否已被占用 ──
|
||||
netstat -ano 2>nul | findstr ":8080" | findstr "LISTENING" >nul
|
||||
if %errorlevel% equ 0 (
|
||||
echo [跳过] 端口 8080 已被占用,服务器已在运行。
|
||||
goto open_browser
|
||||
)
|
||||
|
||||
:: ── 优先 npx serve(配置了 serve.json),备选 Python ──
|
||||
where npx >nul 2>nul
|
||||
if %errorlevel% equ 0 (
|
||||
echo [启动] 使用 npx serve 启动服务器...
|
||||
start "健康CQ原型-服务器" /min cmd /c "npx --yes serve . -p 8080 --no-clipboard"
|
||||
goto wait_server
|
||||
echo [警告] 端口 8080 已被占用!
|
||||
echo.
|
||||
echo 请先关闭占用 8080 端口的程序,或者:
|
||||
echo 1. 任务管理器关闭相关进程
|
||||
echo 2. 运行: netstat -ano ^| findstr :8080 查看 PID
|
||||
echo 3. 运行: taskkill /PID ^<PID^> /F 强制结束
|
||||
|
||||
:: 尝试直接打开已有的服务
|
||||
echo.
|
||||
echo [尝试] 直接打开浏览器访问 http://localhost:8080/ ...
|
||||
start http://localhost:8080/
|
||||
pause
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
:: ── 优先 Python(最稳定,无需联网)──
|
||||
where python >nul 2>nul
|
||||
if %errorlevel% equ 0 (
|
||||
echo [启动] 使用 Python 启动服务器...
|
||||
start "健康CQ原型-服务器" /min cmd /c "python -m http.server 8080"
|
||||
goto wait_server
|
||||
echo [启动] 使用 Python 启动 HTTP 服务器(端口 8080)...
|
||||
echo.
|
||||
echo ★ 服务器运行中,请勿关闭本窗口 ★
|
||||
echo 访问地址:http://localhost:8080/
|
||||
echo 按 Ctrl+C 停止服务器
|
||||
echo ============================================
|
||||
echo.
|
||||
python -m http.server 8080
|
||||
goto end
|
||||
)
|
||||
|
||||
echo [错误] 未找到 Node.js (npx) 或 Python!
|
||||
echo 请安装 Node.js: https://nodejs.org/
|
||||
:: ── 备选 npx serve(需联网下载)──
|
||||
where npx >nul 2>nul
|
||||
if %errorlevel% equ 0 (
|
||||
echo [启动] 使用 npx serve 启动服务器(端口 8080)...
|
||||
echo 首次运行可能需要下载依赖,请稍候...
|
||||
echo.
|
||||
echo ★ 服务器运行中,请勿关闭本窗口 ★
|
||||
echo 访问地址:http://localhost:8080/
|
||||
echo 按 Ctrl+C 停止服务器
|
||||
echo ============================================
|
||||
echo.
|
||||
npx --yes serve . -p 8080 --no-clipboard
|
||||
goto end
|
||||
)
|
||||
|
||||
:: ── 都不可用 ──
|
||||
echo [错误] 未找到 Python 或 Node.js!
|
||||
echo.
|
||||
echo 请安装以下任一工具:
|
||||
echo · Python 3: https://www.python.org/downloads/
|
||||
echo · Node.js: https://nodejs.org/
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
|
||||
:wait_server
|
||||
echo [等待] 服务器启动中...
|
||||
set /a COUNT=0
|
||||
:wait_loop
|
||||
set /a COUNT+=1
|
||||
powershell -NoProfile -Command "try {$r=Invoke-WebRequest -Uri 'http://localhost:8080/' -TimeoutSec 2 -UseBasicParsing; exit 0} catch {exit 1}" >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo [就绪] 启动成功!(%COUNT%秒)
|
||||
goto open_browser
|
||||
)
|
||||
if %COUNT% geq 30 (
|
||||
echo [超时] 30秒未就绪,请检查是否被防火墙拦截
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
timeout /t 1 /nobreak >nul
|
||||
goto wait_loop
|
||||
|
||||
:open_browser
|
||||
echo [打开] 打开浏览器...
|
||||
start http://localhost:8080/
|
||||
:end
|
||||
echo.
|
||||
echo ✅ 完成!以后双击本文件即可一键启动。
|
||||
timeout /t 2 /nobreak >nul
|
||||
exit
|
||||
echo 服务器已停止。
|
||||
pause
|
||||
|
||||
Reference in New Issue
Block a user