HEL-351: 修复夜间下拉/日期弹层、一次性初始密码领取与缩放滚动条
夜间控件改走现有 token 与 color-scheme,日期弹层锚定触发器并可翻转; 创建公司账号改为一次性可复制口令窗口,列表与后续接口不再回明文。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
9d06445d81
commit
acc3c29c49
+33
-5
@@ -11,7 +11,7 @@
|
||||
document.documentElement.classList.add("v-fusion");
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="design-system.css?v=12" />
|
||||
<link rel="stylesheet" href="design-system.css?v=13" />
|
||||
</head>
|
||||
<body class="is-app" data-portal="admin">
|
||||
<a class="skip-link" href="#main-content">跳到主要内容</a>
|
||||
@@ -512,7 +512,7 @@
|
||||
</div>
|
||||
<div class="table-foot" style="border: 1px solid var(--border); border-radius: 0 0 var(--radius-lg) var(--radius-lg);">
|
||||
<span id="companyFoot">共 0 家公司</span>
|
||||
<span class="meta">登录账号初始密码由管理员统一发放</span>
|
||||
<span class="meta">创建账号时生成一次性随机密码,仅当时显示一次</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -876,7 +876,7 @@
|
||||
<span class="modal-title">新增成员公司</span>
|
||||
<button type="button" class="modal-close" data-close="companyDialog" aria-label="关闭">×</button>
|
||||
</div>
|
||||
<p class="modal-sub">登记后系统将生成公司端登录账号,初始密码由管理员统一发放。</p>
|
||||
<p class="modal-sub">登记后可同时生成公司端登录账号。系统会发一次性随机初始密码,仅创建成功时显示一次,请当场复制后交给出纳;首次登录必须改密。没有全公司通用默认密码。</p>
|
||||
<form id="companyForm" novalidate>
|
||||
<div class="field" style="margin-bottom: 12px;">
|
||||
<label for="f-name">公司全称</label>
|
||||
@@ -899,7 +899,7 @@
|
||||
<input class="input" id="f-cashier" name="cashier" type="text" required />
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint" style="margin-top: 10px;">创建后生成随机初始密码,仅显示一次,首次登录必须修改。</p>
|
||||
<p class="hint" style="margin-top: 10px;">创建成功后弹出一次性口令窗口,可复制账号和密码。关闭后无法再次查看明文,列表与日志也不会保存密码。</p>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn" data-close="companyDialog">取消</button>
|
||||
<button type="submit" class="btn btn-primary">创建公司与账号</button>
|
||||
@@ -908,6 +908,33 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 一次性初始密码领取(关闭后从页面清除明文) -->
|
||||
<div class="modal-backdrop" id="credentialDialog">
|
||||
<div class="modal" role="dialog" aria-labelledby="cred-title" aria-modal="true">
|
||||
<div class="modal-head">
|
||||
<span class="modal-title" id="cred-title">一次性初始密码</span>
|
||||
<button type="button" class="modal-close" data-close="credentialDialog" aria-label="关闭">×</button>
|
||||
</div>
|
||||
<p class="modal-sub" id="cred-sub">请立即复制并交给出纳。关闭后无法再次查看明文。</p>
|
||||
<div class="cred-box">
|
||||
<div class="cred-row">
|
||||
<span class="cred-label">登录账号</span>
|
||||
<span class="cred-value" id="cred-user"></span>
|
||||
<button type="button" class="btn btn-sm" id="cred-copy-user">复制</button>
|
||||
</div>
|
||||
<div class="cred-row">
|
||||
<span class="cred-label">初始密码</span>
|
||||
<span class="cred-value" id="cred-pass"></span>
|
||||
<button type="button" class="btn btn-sm btn-primary" id="cred-copy-pass">复制密码</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="cred-warn">对方首次登录必须修改密码。重置密码同样只展示这一次。</p>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn btn-primary" data-close="credentialDialog">我已抄录</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 公司详情弹窗 -->
|
||||
<div class="modal-backdrop" id="modal-detail">
|
||||
<div class="modal wide">
|
||||
@@ -1228,6 +1255,7 @@
|
||||
</div>
|
||||
<div class="toast-region" id="toastRegion" aria-live="polite"></div>
|
||||
<script src="theme.js?v=1"></script>
|
||||
<script src="app.js?v=17"></script>
|
||||
<script src="datepicker.js?v=1"></script>
|
||||
<script src="app.js?v=18"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+81
-10
@@ -129,6 +129,50 @@ function showToast(title, detail = "", kind = "info") {
|
||||
});
|
||||
}
|
||||
|
||||
function copyText(value, button) {
|
||||
const text = String(value || "");
|
||||
const done = () => {
|
||||
if (!button) return;
|
||||
const previous = button.textContent;
|
||||
button.textContent = "已复制";
|
||||
window.setTimeout(() => { button.textContent = previous; }, 1400);
|
||||
};
|
||||
const fallback = () => {
|
||||
const area = document.createElement("textarea");
|
||||
area.value = text;
|
||||
area.setAttribute("readonly", "");
|
||||
area.style.position = "fixed";
|
||||
area.style.left = "-9999px";
|
||||
document.body.append(area);
|
||||
area.select();
|
||||
try { document.execCommand("copy"); } catch (err) { /* ignore */ }
|
||||
area.remove();
|
||||
done();
|
||||
};
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text).then(done).catch(fallback);
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
|
||||
function wipeCredentials() {
|
||||
const user = $("#cred-user");
|
||||
const pass = $("#cred-pass");
|
||||
if (user) user.textContent = "";
|
||||
if (pass) pass.textContent = "";
|
||||
}
|
||||
|
||||
function showOnceCredentials({ title, subtitle, username, password }) {
|
||||
const titleEl = $("#cred-title");
|
||||
const subEl = $("#cred-sub");
|
||||
if (titleEl && title) titleEl.textContent = title;
|
||||
if (subEl && subtitle) subEl.textContent = subtitle;
|
||||
if ($("#cred-user")) $("#cred-user").textContent = username || "";
|
||||
if ($("#cred-pass")) $("#cred-pass").textContent = password || "";
|
||||
openModal("credentialDialog");
|
||||
}
|
||||
|
||||
function toastIfLocked(result) {
|
||||
const msg = String(result?.message || "");
|
||||
if (!(result?.year_month || /已结账锁定/.test(msg))) return false;
|
||||
@@ -2675,9 +2719,24 @@ function initAdmin() {
|
||||
}));
|
||||
$("#auditCompany")?.addEventListener("change", filterAuditRows);
|
||||
|
||||
document.querySelectorAll("[data-close]").forEach((el) => el.addEventListener("click", () => closeModal(el.dataset.close)));
|
||||
document.querySelectorAll(".modal-backdrop").forEach((bd) => bd.addEventListener("click", (e) => { if (e.target === bd) bd.classList.remove("open"); }));
|
||||
document.addEventListener("keydown", (e) => { if (e.key === "Escape") document.querySelectorAll(".modal-backdrop.open").forEach((m) => m.classList.remove("open")); });
|
||||
document.querySelectorAll("[data-close]").forEach((el) => el.addEventListener("click", () => {
|
||||
closeModal(el.dataset.close);
|
||||
if (el.dataset.close === "credentialDialog") wipeCredentials();
|
||||
}));
|
||||
$("#cred-copy-user")?.addEventListener("click", () => copyText($("#cred-user")?.textContent, $("#cred-copy-user")));
|
||||
$("#cred-copy-pass")?.addEventListener("click", () => copyText($("#cred-pass")?.textContent, $("#cred-copy-pass")));
|
||||
document.querySelectorAll(".modal-backdrop").forEach((bd) => bd.addEventListener("click", (e) => {
|
||||
if (e.target !== bd) return;
|
||||
bd.classList.remove("open");
|
||||
if (bd.id === "credentialDialog") wipeCredentials();
|
||||
}));
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key !== "Escape") return;
|
||||
document.querySelectorAll(".modal-backdrop.open").forEach((m) => {
|
||||
m.classList.remove("open");
|
||||
if (m.id === "credentialDialog") wipeCredentials();
|
||||
});
|
||||
});
|
||||
|
||||
async function submitAuditResult(row) {
|
||||
const decision = state.auditDecision;
|
||||
@@ -2889,11 +2948,17 @@ function initAdmin() {
|
||||
closeModal("companyDialog");
|
||||
form.reset();
|
||||
await loadAdminCompanies();
|
||||
showToast(
|
||||
accountCreated ? "公司与账号已创建" : "公司已创建",
|
||||
accountCreated ? `账号 ${result.username} 的初始密码已生成(仅此一次显示):${result.initial_password},首次登录必须修改` : "可稍后在账号管理中创建公司账号",
|
||||
"success",
|
||||
);
|
||||
if (accountCreated) {
|
||||
showOnceCredentials({
|
||||
title: "一次性初始密码",
|
||||
subtitle: "请立即复制并交给出纳。关闭后无法再次查看明文。",
|
||||
username: result.username,
|
||||
password: result.initial_password,
|
||||
});
|
||||
showToast("公司与账号已创建", "请在一次性口令窗口复制初始密码,关闭后无法再看", "success");
|
||||
} else {
|
||||
showToast("公司已创建", "可稍后在账号管理中创建公司账号", "success");
|
||||
}
|
||||
});
|
||||
|
||||
function openCompanyDetail(companyId) {
|
||||
@@ -2933,8 +2998,14 @@ function initAdmin() {
|
||||
if (!response || !response.ok) { showToast("重置失败", result?.message || "请稍后重试", "danger"); return; }
|
||||
$("#btn-reset-pwd").disabled = true;
|
||||
$("#btn-reset-pwd").textContent = "已重置";
|
||||
$("#d-login-meta").textContent = `临时密码已生成(仅此一次):${result.initial_password},首次登录必须修改`;
|
||||
showToast("密码已重置", "临时密码仅本次显示,首次登录必须修改", "success");
|
||||
$("#d-login-meta").textContent = "临时密码已在一次性窗口展示,关闭后无法再查看。";
|
||||
showOnceCredentials({
|
||||
title: "临时密码已生成",
|
||||
subtitle: "仅此一次显示。旧会话已失效,首次登录必须修改。",
|
||||
username: user.username,
|
||||
password: result.initial_password,
|
||||
});
|
||||
showToast("密码已重置", "请在一次性口令窗口复制后交给出纳", "success");
|
||||
});
|
||||
|
||||
const remind = $("#cs-remind");
|
||||
|
||||
+3
-2
@@ -11,7 +11,7 @@
|
||||
document.documentElement.classList.add("v-fusion");
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="design-system.css?v=12" />
|
||||
<link rel="stylesheet" href="design-system.css?v=13" />
|
||||
</head>
|
||||
<body class="is-app" data-portal="company">
|
||||
<a class="skip-link" href="#main-content">跳到主要内容</a>
|
||||
@@ -1044,6 +1044,7 @@
|
||||
|
||||
<div class="toast-region" id="toastRegion" aria-live="polite"></div>
|
||||
<script src="theme.js?v=1"></script>
|
||||
<script src="app.js?v=17"></script>
|
||||
<script src="datepicker.js?v=1"></script>
|
||||
<script src="app.js?v=18"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
/* 锚定在日期输入旁的页内日历。不引入第三方库;滚动/缩放后跟随触发器。 */
|
||||
(function (global) {
|
||||
var WEEK = ["日", "一", "二", "三", "四", "五", "六"];
|
||||
var panel = null;
|
||||
var grid = null;
|
||||
var titleEl = null;
|
||||
var activeInput = null;
|
||||
var viewYear = 0;
|
||||
var viewMonth = 0;
|
||||
var bound = false;
|
||||
|
||||
function pad(n) {
|
||||
return n < 10 ? "0" + n : String(n);
|
||||
}
|
||||
|
||||
function toISO(year, month, day) {
|
||||
return year + "-" + pad(month + 1) + "-" + pad(day);
|
||||
}
|
||||
|
||||
function parseISO(value) {
|
||||
var match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(value || "").trim());
|
||||
if (!match) return null;
|
||||
var year = Number(match[1]);
|
||||
var month = Number(match[2]) - 1;
|
||||
var day = Number(match[3]);
|
||||
var date = new Date(year, month, day);
|
||||
if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
|
||||
return null;
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
function todayISO() {
|
||||
var now = new Date();
|
||||
return toISO(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
}
|
||||
|
||||
function ensurePanel() {
|
||||
if (panel) return;
|
||||
panel = document.createElement("div");
|
||||
panel.className = "ds-datepicker";
|
||||
panel.id = "ds-datepicker";
|
||||
panel.hidden = true;
|
||||
panel.setAttribute("role", "dialog");
|
||||
panel.setAttribute("aria-label", "选择日期");
|
||||
panel.innerHTML =
|
||||
'<div class="ds-dp-head">' +
|
||||
'<button type="button" class="ds-dp-nav" data-dp-nav="-1" aria-label="上一月">‹</button>' +
|
||||
'<div class="ds-dp-title"></div>' +
|
||||
'<button type="button" class="ds-dp-nav" data-dp-nav="1" aria-label="下一月">›</button>' +
|
||||
"</div>" +
|
||||
'<div class="ds-dp-week">' + WEEK.map(function (d) { return "<span>" + d + "</span>"; }).join("") + "</div>" +
|
||||
'<div class="ds-dp-grid" role="grid"></div>' +
|
||||
'<div class="ds-dp-foot">' +
|
||||
'<button type="button" data-dp-today>今天</button>' +
|
||||
'<button type="button" data-dp-clear>清除</button>' +
|
||||
"</div>";
|
||||
document.body.appendChild(panel);
|
||||
titleEl = panel.querySelector(".ds-dp-title");
|
||||
grid = panel.querySelector(".ds-dp-grid");
|
||||
panel.addEventListener("mousedown", function (event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
panel.addEventListener("click", function (event) {
|
||||
var nav = event.target.closest("[data-dp-nav]");
|
||||
if (nav) {
|
||||
shiftMonth(Number(nav.getAttribute("data-dp-nav")));
|
||||
return;
|
||||
}
|
||||
if (event.target.closest("[data-dp-today]")) {
|
||||
commit(todayISO());
|
||||
return;
|
||||
}
|
||||
if (event.target.closest("[data-dp-clear]")) {
|
||||
commit("");
|
||||
return;
|
||||
}
|
||||
var dayBtn = event.target.closest("[data-iso]");
|
||||
if (dayBtn && !dayBtn.disabled) commit(dayBtn.getAttribute("data-iso"));
|
||||
});
|
||||
}
|
||||
|
||||
function enhance(input) {
|
||||
if (!input || input.dataset.dsDate === "1") return;
|
||||
input.dataset.dsDate = "1";
|
||||
if (input.type === "date") {
|
||||
var value = input.value;
|
||||
try {
|
||||
input.type = "text";
|
||||
} catch (err) {
|
||||
/* keep native type if the engine refuses */
|
||||
}
|
||||
if (value) input.value = value;
|
||||
}
|
||||
input.setAttribute("inputmode", "numeric");
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("spellcheck", "false");
|
||||
input.setAttribute("placeholder", input.getAttribute("placeholder") || "YYYY-MM-DD");
|
||||
if (!input.getAttribute("pattern")) input.setAttribute("pattern", "\\d{4}-\\d{2}-\\d{2}");
|
||||
if (!input.classList.contains("num-input")) input.classList.add("num-input");
|
||||
input.setAttribute("aria-haspopup", "dialog");
|
||||
}
|
||||
|
||||
function scan() {
|
||||
document.querySelectorAll('input[type="date"]').forEach(enhance);
|
||||
}
|
||||
|
||||
function render() {
|
||||
if (!grid || !titleEl) return;
|
||||
titleEl.textContent = viewYear + "年" + pad(viewMonth + 1) + "月";
|
||||
var first = new Date(viewYear, viewMonth, 1);
|
||||
var start = first.getDay();
|
||||
var selected = activeInput ? parseISO(activeInput.value) : null;
|
||||
var selectedISO = selected ? toISO(selected.getFullYear(), selected.getMonth(), selected.getDate()) : "";
|
||||
var today = todayISO();
|
||||
var min = activeInput && activeInput.min ? activeInput.min : "";
|
||||
var max = activeInput && activeInput.max ? activeInput.max : "";
|
||||
var html = "";
|
||||
var cursor = new Date(viewYear, viewMonth, 1 - start);
|
||||
for (var i = 0; i < 42; i += 1) {
|
||||
var iso = toISO(cursor.getFullYear(), cursor.getMonth(), cursor.getDate());
|
||||
var other = cursor.getMonth() !== viewMonth;
|
||||
var disabled = (min && iso < min) || (max && iso > max);
|
||||
var cls = "ds-dp-day";
|
||||
if (other) cls += " is-other";
|
||||
if (iso === today) cls += " is-today";
|
||||
if (iso === selectedISO) cls += " is-selected";
|
||||
html +=
|
||||
'<button type="button" class="' + cls + '" data-iso="' + iso + '"' +
|
||||
(disabled ? " disabled" : "") +
|
||||
' aria-label="' + iso + '"' +
|
||||
(iso === selectedISO ? ' aria-current="date"' : "") +
|
||||
">" + cursor.getDate() + "</button>";
|
||||
cursor.setDate(cursor.getDate() + 1);
|
||||
}
|
||||
grid.innerHTML = html;
|
||||
position();
|
||||
}
|
||||
|
||||
function viewportBox() {
|
||||
var vv = global.visualViewport;
|
||||
if (vv) {
|
||||
return {
|
||||
left: vv.offsetLeft,
|
||||
top: vv.offsetTop,
|
||||
width: vv.width,
|
||||
height: vv.height,
|
||||
};
|
||||
}
|
||||
return { left: 0, top: 0, width: global.innerWidth, height: global.innerHeight };
|
||||
}
|
||||
|
||||
function position() {
|
||||
if (!panel || panel.hidden || !activeInput) return;
|
||||
var rect = activeInput.getBoundingClientRect();
|
||||
var view = viewportBox();
|
||||
var gap = 6;
|
||||
var width = panel.offsetWidth || 280;
|
||||
var height = panel.offsetHeight || 320;
|
||||
var left = rect.left;
|
||||
var top = rect.bottom + gap;
|
||||
if (top + height > view.top + view.height - 8 && rect.top - gap - height >= view.top + 8) {
|
||||
top = rect.top - gap - height;
|
||||
}
|
||||
if (left + width > view.left + view.width - 8) {
|
||||
left = Math.max(view.left + 8, rect.right - width);
|
||||
}
|
||||
if (left < view.left + 8) left = view.left + 8;
|
||||
if (top < view.top + 8) top = view.top + 8;
|
||||
panel.style.left = Math.round(left) + "px";
|
||||
panel.style.top = Math.round(top) + "px";
|
||||
}
|
||||
|
||||
function openPicker(input) {
|
||||
if (!input || input.disabled) return;
|
||||
enhance(input);
|
||||
ensurePanel();
|
||||
activeInput = input;
|
||||
var parsed = parseISO(input.value) || new Date();
|
||||
viewYear = parsed.getFullYear();
|
||||
viewMonth = parsed.getMonth();
|
||||
panel.hidden = false;
|
||||
render();
|
||||
input.setAttribute("aria-expanded", "true");
|
||||
}
|
||||
|
||||
function closePicker() {
|
||||
if (!panel || panel.hidden) return;
|
||||
panel.hidden = true;
|
||||
if (activeInput) activeInput.setAttribute("aria-expanded", "false");
|
||||
activeInput = null;
|
||||
}
|
||||
|
||||
function shiftMonth(delta) {
|
||||
viewMonth += delta;
|
||||
while (viewMonth < 0) {
|
||||
viewMonth += 12;
|
||||
viewYear -= 1;
|
||||
}
|
||||
while (viewMonth > 11) {
|
||||
viewMonth -= 12;
|
||||
viewYear += 1;
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
function commit(iso) {
|
||||
if (!activeInput) return;
|
||||
var input = activeInput;
|
||||
input.value = iso;
|
||||
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
input.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
closePicker();
|
||||
input.focus({ preventScroll: true });
|
||||
}
|
||||
|
||||
function onPointer(event) {
|
||||
var input = event.target.closest && event.target.closest('input[type="date"], input[data-ds-date="1"]');
|
||||
if (input) {
|
||||
if (event.type === "mousedown" && event.button === 0) event.preventDefault();
|
||||
openPicker(input);
|
||||
return;
|
||||
}
|
||||
if (panel && !panel.hidden && !panel.contains(event.target)) closePicker();
|
||||
}
|
||||
|
||||
function onKey(event) {
|
||||
if (event.key === "Escape") {
|
||||
closePicker();
|
||||
return;
|
||||
}
|
||||
var input = event.target.closest && event.target.closest('input[data-ds-date="1"]');
|
||||
if (!input) return;
|
||||
if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
|
||||
event.preventDefault();
|
||||
openPicker(input);
|
||||
}
|
||||
}
|
||||
|
||||
function bind() {
|
||||
if (bound) return;
|
||||
bound = true;
|
||||
document.addEventListener("mousedown", onPointer, true);
|
||||
document.addEventListener("focusin", function (event) {
|
||||
var input = event.target.closest && event.target.closest('input[type="date"]');
|
||||
if (input) openPicker(input);
|
||||
});
|
||||
document.addEventListener("keydown", onKey);
|
||||
document.addEventListener("scroll", position, true);
|
||||
global.addEventListener("resize", position);
|
||||
if (global.visualViewport) {
|
||||
global.visualViewport.addEventListener("resize", position);
|
||||
global.visualViewport.addEventListener("scroll", position);
|
||||
}
|
||||
if (typeof MutationObserver === "function") {
|
||||
new MutationObserver(scan).observe(document.documentElement, { childList: true, subtree: true });
|
||||
}
|
||||
scan();
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", bind);
|
||||
} else {
|
||||
bind();
|
||||
}
|
||||
|
||||
global.JinniuDatePicker = { open: openPicker, close: closePicker, scan: scan };
|
||||
})(window);
|
||||
+246
-9
@@ -61,6 +61,7 @@
|
||||
--lp-brand: linear-gradient(158deg, #c13423, #a02415 52%, #7e170b);
|
||||
--lp-brand-glow: radial-gradient(120% 80% at 18% 12%, rgba(255, 255, 255, 0.22), transparent 58%);
|
||||
--lp-tick: var(--gold-bright);
|
||||
color-scheme: light;
|
||||
|
||||
--fg: var(--text);
|
||||
--muted: var(--text-2);
|
||||
@@ -134,6 +135,9 @@ html[data-theme="night"],
|
||||
--lp-brand: linear-gradient(158deg, #201b14, #161310 52%, #0f0d0a);
|
||||
--lp-brand-glow: radial-gradient(120% 80% at 18% 12%, rgba(217, 180, 92, 0.18), transparent 58%);
|
||||
--lp-tick: var(--gold);
|
||||
color-scheme: dark;
|
||||
scrollbar-color: var(--gold) var(--surface-2);
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
/* ─── reset ─────────────────────────────────────────────────────── */
|
||||
@@ -225,7 +229,7 @@ html[data-theme="night"] .brand-logo-night { display: block; }
|
||||
}
|
||||
.side-role.company { background: var(--info-soft); color: var(--info); }
|
||||
|
||||
.side-nav { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 12px 10px; }
|
||||
.side-nav { flex: 1; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; padding: 12px 10px; }
|
||||
.side-nav .nav-group { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.08em; color: var(--muted); padding: 14px 10px 6px; }
|
||||
.side-nav a {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
@@ -316,6 +320,7 @@ html[data-theme="night"] .brand-logo-night { display: block; }
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
display: flex;
|
||||
@@ -478,7 +483,8 @@ html[data-theme="night"] .brand-logo-night { display: block; }
|
||||
.dash-company-list {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.dash-company-loading,
|
||||
.dash-company-empty {
|
||||
@@ -606,7 +612,8 @@ html[data-theme="night"] .brand-logo-night { display: block; }
|
||||
border-radius: 0;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.dash-detail-empty {
|
||||
flex: 1;
|
||||
@@ -713,7 +720,16 @@ html[data-theme="night"] .brand-logo-night { display: block; }
|
||||
}
|
||||
|
||||
/* ─── 数据表 ────────────────────────────────────────────────────── */
|
||||
.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); }
|
||||
.table-wrap {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
background: var(--surface);
|
||||
}
|
||||
.table-wrap.dash-master-scroll {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ds-table { width: 100%; border-collapse: collapse; font-size: 13px; min-width: 640px; }
|
||||
.ds-table th, .ds-table td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--border); white-space: nowrap; }
|
||||
.ds-table th {
|
||||
@@ -784,19 +800,71 @@ html[data-theme="night"] .brand-logo-night { display: block; }
|
||||
font: inherit;
|
||||
font-size: 13.5px;
|
||||
min-height: 34px;
|
||||
color-scheme: inherit;
|
||||
}
|
||||
.input:focus, .select:focus, .textarea:focus, .field .input:focus, .field .select:focus, .field .textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--focus-ring);
|
||||
box-shadow: 0 0 0 3px var(--focus-glow);
|
||||
}
|
||||
.input:disabled, .select:disabled, .textarea:disabled,
|
||||
.field .input:disabled, .field .select:disabled, .field .textarea:disabled {
|
||||
color: var(--text-3);
|
||||
background: var(--fill);
|
||||
opacity: 0.72;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.select option,
|
||||
.select optgroup {
|
||||
background-color: var(--surface);
|
||||
color: var(--text);
|
||||
}
|
||||
.select option:checked,
|
||||
.select option:hover {
|
||||
background-color: var(--accent-soft);
|
||||
color: var(--text);
|
||||
}
|
||||
.select option:disabled {
|
||||
color: var(--text-3);
|
||||
}
|
||||
.input.num-input { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
|
||||
.textarea { min-height: 84px; resize: vertical; line-height: 1.55; }
|
||||
.field .hint { font-size: 11.5px; color: var(--muted); }
|
||||
.field .hint.error { color: var(--danger); }
|
||||
|
||||
/* 关闭原生日历层,改由 ds-datepicker 锚定在触发器旁 */
|
||||
input[type="date"]::-webkit-calendar-picker-indicator {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
}
|
||||
.input[type="date"],
|
||||
.field .input[type="date"],
|
||||
.input[data-ds-date="1"],
|
||||
.field .input[data-ds-date="1"] {
|
||||
padding-right: 34px;
|
||||
background-color: var(--fill);
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%236e6e73' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.5' y='3.5' width='11' height='10' rx='1.5'/%3E%3Cpath d='M2.5 6.5h11M6 2v3M10 2v3'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 10px center;
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
html[data-theme="night"] .input[type="date"],
|
||||
html[data-theme="night"] .field .input[type="date"],
|
||||
html[data-theme="night"] .input[data-ds-date="1"],
|
||||
html[data-theme="night"] .field .input[data-ds-date="1"] {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%23d9b45c' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.5' y='3.5' width='11' height='10' rx='1.5'/%3E%3Cpath d='M2.5 6.5h11M6 2v3M10 2v3'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ─── Tabs ──────────────────────────────────────────────────────── */
|
||||
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 16px; overflow-x: auto; }
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: 16px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
.tabs button {
|
||||
background: none; border: 0;
|
||||
padding: 9px 14px;
|
||||
@@ -867,7 +935,7 @@ html[data-theme="night"] .brand-logo-night { display: block; }
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--gold-line);
|
||||
width: 100%; max-width: min(520px, 94vw);
|
||||
max-height: 86vh; overflow-y: auto;
|
||||
max-height: 86vh; overflow-x: hidden; overflow-y: auto;
|
||||
padding: 22px 24px;
|
||||
box-shadow: var(--shadow-modal);
|
||||
}
|
||||
@@ -1190,6 +1258,7 @@ a.flow-step.doing:hover { background: color-mix(in oklch, var(--warn) 16%, trans
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
padding: 8px 4px;
|
||||
flex: 1;
|
||||
}
|
||||
@@ -1207,7 +1276,7 @@ a.flow-step.doing:hover { background: color-mix(in oklch, var(--warn) 16%, trans
|
||||
.theme-seg { margin-bottom: 0; width: 92px; grid-template-columns: 1fr 1fr; }
|
||||
.account-box { padding: 4px; background: transparent; }
|
||||
.side-foot .user-name, .side-foot .user-meta, .side-foot .avatar { display: none; }
|
||||
.main { min-height: 0; height: 100%; overflow-y: auto; }
|
||||
.main { min-height: 0; height: 100%; overflow-x: hidden; overflow-y: auto; }
|
||||
}
|
||||
|
||||
/* ─── Toast 轻量提示 ───────────────────────────────────────────── */
|
||||
@@ -1265,7 +1334,7 @@ a.flow-step.doing:hover { background: color-mix(in oklch, var(--warn) 16%, trans
|
||||
}
|
||||
.drawer-head .d-title { font-size: 16px; font-weight: 700; margin-top: 6px; }
|
||||
.drawer-head .d-desc { color: var(--muted); font-size: 12.5px; margin-top: 4px; }
|
||||
.drawer-body { flex: 1; overflow-y: auto; padding: 16px 20px; }
|
||||
.drawer-body { flex: 1; overflow-x: hidden; overflow-y: auto; padding: 16px 20px; }
|
||||
.drawer-body .kv { grid-template-columns: auto 1fr; gap: 8px 18px; }
|
||||
.drawer-body .kv dt { color: var(--muted); white-space: nowrap; }
|
||||
.drawer-body .kv dd { text-align: right; word-break: break-all; }
|
||||
@@ -1429,7 +1498,7 @@ body[data-portal="company"] .side-nav a[data-view="transfers"].active svg {
|
||||
background-position: center;
|
||||
}
|
||||
.batch-bar { display: flex; align-items: center; gap: 10px; padding: 8px 0; }
|
||||
.login-page { min-height: 100vh; overflow: auto; }
|
||||
.login-page { min-height: 100vh; overflow-x: hidden; overflow-y: auto; }
|
||||
.toast .t-detail { color: rgba(255,255,255,.72); }
|
||||
.toast .t-dot { margin-top: 0; }
|
||||
|
||||
@@ -1675,3 +1744,171 @@ body[data-portal="company"] .side-nav a[data-view="transfers"].active svg {
|
||||
.audit-event-cards { display: grid; gap: 10px; }
|
||||
}
|
||||
|
||||
/* ─── 夜间滚动条:金色滑块 + 深色轨道(HEL-351) ─────────────── */
|
||||
html[data-theme="night"] ::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
html[data-theme="night"] ::-webkit-scrollbar-track {
|
||||
background: var(--surface-2);
|
||||
}
|
||||
html[data-theme="night"] ::-webkit-scrollbar-thumb {
|
||||
background-color: var(--gold);
|
||||
border: 2px solid var(--surface-2);
|
||||
border-radius: 999px;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
html[data-theme="night"] ::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--gold-bright);
|
||||
}
|
||||
html[data-theme="night"] ::-webkit-scrollbar-corner {
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
/* ─── 日期选择弹层:锚定触发器,夜间黑金点缀 ───────────────── */
|
||||
.ds-datepicker {
|
||||
position: fixed;
|
||||
z-index: var(--z-tooltip);
|
||||
width: 280px;
|
||||
padding: 12px;
|
||||
background: var(--material-drawer);
|
||||
backdrop-filter: blur(28px);
|
||||
-webkit-backdrop-filter: blur(28px);
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: 16px;
|
||||
box-shadow: var(--shadow-modal);
|
||||
color: var(--text);
|
||||
}
|
||||
html[data-theme="night"] .ds-datepicker {
|
||||
background: var(--surface);
|
||||
border-color: var(--gold-line);
|
||||
}
|
||||
.ds-datepicker[hidden] { display: none !important; }
|
||||
.ds-dp-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ds-dp-title {
|
||||
font-size: 13.5px;
|
||||
font-weight: 650;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
html[data-theme="night"] .ds-dp-title { color: var(--gold); }
|
||||
.ds-dp-nav {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 0;
|
||||
border-radius: 7px;
|
||||
background: var(--fill);
|
||||
color: var(--text-2);
|
||||
}
|
||||
.ds-dp-nav:hover { background: var(--hover); color: var(--text); }
|
||||
.ds-dp-week,
|
||||
.ds-dp-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 2px;
|
||||
}
|
||||
.ds-dp-week span {
|
||||
height: 28px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--text-3);
|
||||
}
|
||||
.ds-dp-day {
|
||||
height: 32px;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
font-size: 13px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.ds-dp-day:hover { background: var(--hover); }
|
||||
.ds-dp-day.is-other { color: var(--text-3); }
|
||||
.ds-dp-day.is-today {
|
||||
box-shadow: inset 0 0 0 1px var(--accent);
|
||||
color: var(--accent);
|
||||
}
|
||||
.ds-dp-day.is-selected {
|
||||
background: var(--accent-soft);
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
}
|
||||
.ds-dp-day.is-selected.is-today { box-shadow: none; }
|
||||
html[data-theme="night"] .ds-dp-day.is-today {
|
||||
box-shadow: inset 0 0 0 1px var(--gold-line);
|
||||
color: var(--gold);
|
||||
}
|
||||
html[data-theme="night"] .ds-dp-day.is-selected {
|
||||
background: var(--gold-soft);
|
||||
color: var(--gold);
|
||||
}
|
||||
.ds-dp-day:disabled {
|
||||
color: var(--text-3);
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.ds-dp-foot {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
.ds-dp-foot button {
|
||||
height: 26px;
|
||||
padding: 0 10px;
|
||||
border: 0;
|
||||
border-radius: 7px;
|
||||
background: transparent;
|
||||
color: var(--text-2);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.ds-dp-foot button:hover { background: var(--hover); color: var(--text); }
|
||||
|
||||
/* ─── 一次性初始密码领取 ─────────────────────────────────────── */
|
||||
.cred-box {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid var(--gold-line);
|
||||
border-radius: 14px;
|
||||
background: var(--gold-soft);
|
||||
}
|
||||
.cred-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
.cred-row .cred-label {
|
||||
flex: none;
|
||||
width: 72px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--text-2);
|
||||
}
|
||||
.cred-row .cred-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-family: var(--font-mono);
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-size: 14px;
|
||||
font-weight: 650;
|
||||
color: var(--text);
|
||||
word-break: break-all;
|
||||
}
|
||||
.cred-warn {
|
||||
margin-top: 12px;
|
||||
font-size: 12.5px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
document.documentElement.classList.add("v-fusion");
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="design-system.css?v=12" />
|
||||
<link rel="stylesheet" href="design-system.css?v=13" />
|
||||
</head>
|
||||
<body class="login-page">
|
||||
<div class="portal-wrap">
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
document.documentElement.classList.add("v-fusion");
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="design-system.css?v=12" />
|
||||
<link rel="stylesheet" href="design-system.css?v=13" />
|
||||
</head>
|
||||
<body class="login-page" data-role="admin">
|
||||
<div class="login-wrap">
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
document.documentElement.classList.add("v-fusion");
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="design-system.css?v=12" />
|
||||
<link rel="stylesheet" href="design-system.css?v=13" />
|
||||
</head>
|
||||
<body class="login-page" data-role="company">
|
||||
<div class="login-wrap">
|
||||
|
||||
Reference in New Issue
Block a user