fix: 落实测试反馈五项并优化分发配置

- 色带余量 RibbonAmount 独立展示与轮询刷新
- 加密狗授权码 UI、校验、auth_code 提交及 secrets 持久化
- 打印机状态全局轮询(前台 1.5s / 后台 8s)
- 网络路径:列表仅映射目录,凭据单独配置,net_info 对齐 SoonWoker
- 标签打印面:点击正/反面选择,print_flag 1/2/3 与模板校验

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
24kycj
2026-06-11 10:34:05 +08:00
parent f1b73ee3d3
commit 931fcf90a4
24 changed files with 840 additions and 126 deletions
+21 -26
View File
@@ -1,16 +1,16 @@
// 临时自测:验证 networkPath.ts 的纯函数行为
// 跑法:node scripts/network-path-selftest.mjs
import {
isNetworkPath,
extractHostName,
extractDriveLetter,
buildNetworkUrl,
collectHostsForCopyPaths,
buildNetInfo
} from '../src/renderer/src/utils/networkPath.ts'
} from '../src/shared/network-host.ts'
const cases = []
function eq(name, actual, expected) {
// 两边按字面字符串比较(不走 JSON.stringify 避免转义歧义)
const ok = actual === expected
const ok =
Array.isArray(expected) ? JSON.stringify(actual) === JSON.stringify(expected) : actual === expected
cases.push({ name, ok, actual, expected })
if (!ok) {
console.error(`FAIL ${name}`)
@@ -19,38 +19,36 @@ function eq(name, actual, expected) {
}
}
// isNetworkPath
eq('isNetworkPath \\host', isNetworkPath('\\\\192.168.1.100\\share'), true)
eq('isNetworkPath //host', isNetworkPath('//nas/share'), true)
eq('isNetworkPath D:\\a (应 false)', isNetworkPath('D:\\data'), false)
eq('isNetworkPath D:\\a', isNetworkPath('D:\\data'), false)
eq('isNetworkPath empty', isNetworkPath(''), false)
// extractHostName
const unc = '\\\\192.168.1.100\\share\\a.pdf' // 实际 \\192.168.1.100\share\a.pdf
const unc = '\\\\192.168.1.100\\share\\a.pdf'
eq('extractHostName \\host\\share', extractHostName(unc), '192.168.1.100')
eq('extractHostName //host/share', extractHostName('//nas/share'), 'nas')
eq('extractHostName local', extractHostName('D:\\data'), '')
eq('extractHostName empty', extractHostName(''), '')
// buildNetworkUrl
// 期望: \\192.168.1.100 -> 字面 '\\\\192.168.1.100'
eq('extractDriveLetter Z', extractDriveLetter('Z:\\folder'), 'Z')
eq('extractDriveLetter C', extractDriveLetter('C:\\data\\sub'), 'C')
eq('extractDriveLetter unc', extractDriveLetter(unc), '')
eq('buildNetworkUrl bare', buildNetworkUrl('192.168.1.100', ''), '\\\\192.168.1.100')
// 期望: \\192.168.1.100\share -> 字面 '\\\\192.168.1.100\\share'
eq('buildNetworkUrl share', buildNetworkUrl('192.168.1.100', 'share'), '\\\\192.168.1.100\\share')
eq('buildNetworkUrl slashes stripped', buildNetworkUrl('host', '/share/'), '\\\\host\\share')
// buildNetInfo
// 期望产物是 JSON 文本(JSON 字符串里 \\ 表示 1 个 \ 字符)
// 反序列化后 host_name 值是 2 个 \ 字符 -> JSON 文本中需要 4 个 \ 字符
// 4 个 \ 字符 = JS 字面 '\\\\\\\\' (8 个 \)
const stored = new Map([['192.168.1.100', { userName: 'admin', password: 'pass', lastUsed: '' }]])
const r1 = buildNetInfo(
[{ hostName: '192.168.1.100' }],
(h) => stored.get(h) || null
eq(
'collectHosts unc + drive',
collectHostsForCopyPaths(['\\\\192.168.1.100\\share\\a', 'Z:\\data'], { Z: '192.168.1.200' }),
['192.168.1.100', '192.168.1.200']
)
eq('buildNetInfo single', r1, '[{"host_name":"\\\\\\\\192.168.1.100","user_name":"admin","password":"pass"}]')
eq('collectHosts local only', collectHostsForCopyPaths(['E:\\data'], {}), [])
const stored = new Map([['192.168.1.100', { userName: 'admin', password: 'pass', lastUsed: '' }]])
const r1 = buildNetInfo([{ hostName: '192.168.1.100' }], (h) => stored.get(h) || null)
eq('buildNetInfo single', r1, '[{"host_name":"192.168.1.100","user_name":"admin","password":"pass"}]')
// dedup
const r2 = buildNetInfo(
[
{ hostName: '192.168.1.100', userName: 'a', password: 'p' },
@@ -58,9 +56,8 @@ const r2 = buildNetInfo(
],
() => null
)
eq('buildNetInfo dedup', r2, '[{"host_name":"\\\\\\\\192.168.1.100","user_name":"a","password":"p"}]')
eq('buildNetInfo dedup', r2, '[{"host_name":"192.168.1.100","user_name":"a","password":"p"}]')
// missing throws
let threw = false
try {
buildNetInfo([{ hostName: 'h1' }], () => null)
@@ -68,8 +65,6 @@ try {
threw = e.message.includes('缺少网络凭据')
}
eq('buildNetInfo missing throws', threw, true)
// empty -> empty string
eq('buildNetInfo empty list', buildNetInfo([], () => null), '')
const pass = cases.filter((c) => c.ok).length