fix: 大屏背景图被兜底渐变盖住 + 配置/规则也搬到用户数据目录
1) 大屏背景图不生效:App.vue 里 .bg 与 .bg-fallback 都是 absolute inset:0, 而兜底渐变是不透明的且在图片之后绘制 → 配了背景图也只看得到渐变。 改成 v-if/v-else 互斥(用无头 Chromium 真渲染对照验证:修复后取到图片像素, 回退版本只剩渐变)。新增 background-layer.test.ts 锁住层次关系。 2) 规则/config 仍在安装目录,覆盖安装/卸载会连配置一起删:现在三样都落在 %LOCALAPPDATA%\ScoreboardConsole\(与数据目录同一基座): - console\config.json(Console 读写都用这份) - config.yaml(Console 用绝对路径 -config 传给服务端与 passwd) - rules\(首次从安装目录复制过去,之后只认用户目录那份) 首次运行复制迁移、已存在不覆盖、不可写则回落安装目录并记日志(不影响启动); 开发态行为不变。备份脚本与文档同步。 注意 resolveExecutable 必须显式传部署包根目录,否则会去用户目录找 scoreboard.exe。 自检:服务端 11 包全绿;console 语法 16 文件 + node:test 20/20; 前端 vue-tsc + build、node --test 69/69。
This commit is contained in:
1 parent
2d207fd654
commit
407abec3b0
15 files changed
+759
-81
No files matched your search
@@ -0,0 +1,169 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const { LOCAL_BASE } = require('./datadir')
|
||||
|
||||
// 这三样以前随安装包落在安装目录里,但都属于「用户改出来的东西」,必须跟数据目录一样
|
||||
// 挪到安装目录之外:卸载器结尾是 `RMDir /r $INSTDIR`,覆盖安装又会先静默跑一遍旧卸载器,
|
||||
// 放在安装目录里等于用户改一次丢一次。
|
||||
// console\config.json → <用户根>\console\config.json
|
||||
// config.yaml → <用户根>\config.yaml
|
||||
// rules\ → <用户根>\rules\
|
||||
// 用户根与数据目录同一基座:%LOCALAPPDATA%\ScoreboardConsole\
|
||||
const CONSOLE_SUBDIR = 'console'
|
||||
const CONSOLE_CONFIG_FILE = 'config.json'
|
||||
const SERVER_CONFIG_FILE = 'config.yaml'
|
||||
const RULES_SUBDIR = 'rules'
|
||||
|
||||
const ITEM_NAMES = ['consoleConfig', 'serverConfig', 'rulesDir']
|
||||
|
||||
const ITEM_LABEL = {
|
||||
consoleConfig: 'Console 配置文件',
|
||||
serverConfig: '服务端配置文件',
|
||||
rulesDir: '规则目录'
|
||||
}
|
||||
|
||||
// 用户根目录;拿不到 %LOCALAPPDATA% 时返回 null,由调用方回落到安装目录
|
||||
function resolveUserRoot(env) {
|
||||
const localAppData = env ? env.LOCALAPPDATA : ''
|
||||
const value = typeof localAppData === 'string' ? localAppData.trim() : ''
|
||||
return value ? path.join(value, LOCAL_BASE) : null
|
||||
}
|
||||
|
||||
// 解析落点,纯函数不碰磁盘。mode='user' 才接管到用户目录;
|
||||
// 其余情况(开发态、拿不到 %LOCALAPPDATA%)三项一律用安装目录里随包的那份,
|
||||
// 行为与以前完全一致 —— 开发态不能动仓库里的文件。
|
||||
function planUserPaths({ installDir, userRoot, isPackaged }) {
|
||||
const install = path.resolve(installDir || '.')
|
||||
const templates = {
|
||||
consoleConfig: path.join(install, CONSOLE_CONFIG_FILE),
|
||||
serverConfig: path.join(install, SERVER_CONFIG_FILE),
|
||||
rulesDir: path.join(install, RULES_SUBDIR)
|
||||
}
|
||||
const items = {}
|
||||
const targets = {
|
||||
consoleConfig: path.join(install, CONSOLE_CONFIG_FILE),
|
||||
serverConfig: path.join(install, SERVER_CONFIG_FILE),
|
||||
rulesDir: path.join(install, RULES_SUBDIR)
|
||||
}
|
||||
|
||||
const reason = !isPackaged ? 'dev' : userRoot ? null : 'no-localappdata'
|
||||
if (!reason) {
|
||||
targets.consoleConfig = path.join(userRoot, CONSOLE_SUBDIR, CONSOLE_CONFIG_FILE)
|
||||
targets.serverConfig = path.join(userRoot, SERVER_CONFIG_FILE)
|
||||
targets.rulesDir = path.join(userRoot, RULES_SUBDIR)
|
||||
}
|
||||
|
||||
for (const name of ITEM_NAMES) {
|
||||
items[name] = { target: path.resolve(targets[name]), template: path.resolve(templates[name]) }
|
||||
}
|
||||
return {
|
||||
mode: reason ? 'install' : 'user',
|
||||
reason,
|
||||
root: userRoot ? path.resolve(userRoot) : null,
|
||||
installDir: install,
|
||||
items
|
||||
}
|
||||
}
|
||||
|
||||
// 目标目录必须真的能写:mkdir 只保证目录建出来,权限不足或路径被占要在这一步就暴露,
|
||||
// 好让调用方回落到安装目录,而不是等到写配置文件时才失败
|
||||
function ensureDir(dir) {
|
||||
fs.mkdirSync(dir, { recursive: true })
|
||||
fs.accessSync(dir, fs.constants.W_OK)
|
||||
}
|
||||
|
||||
function tryRemove(dir) {
|
||||
try {
|
||||
fs.rmSync(dir, { recursive: true, force: true })
|
||||
} catch {
|
||||
// 清不掉就留着:只是本次拷贝留下的临时目录,下次会原地重建
|
||||
}
|
||||
}
|
||||
|
||||
// 规则目录先拷到临时目录再整体改名到位:中途失败不会留下半个 rules\,
|
||||
// 否则用户目录里一旦存在同名目录,下次启动就按「已存在」跳过、再也不补拷
|
||||
function copyDirOnce(template, target) {
|
||||
const tmp = `${target}.tmp-${process.pid}`
|
||||
try {
|
||||
tryRemove(tmp)
|
||||
fs.cpSync(template, tmp, { recursive: true })
|
||||
fs.renameSync(tmp, target)
|
||||
} catch (err) {
|
||||
tryRemove(tmp)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// 按 plan 落盘,返回最终生效的路径。判定只有两条:
|
||||
// 目标不存在 + 安装目录里有 → 复制过去(安装目录那份留作模板,反正卸载器会删);
|
||||
// 目标已存在 → 一律用用户目录那份,绝不覆盖用户改过的东西。
|
||||
// 任一目录建不出来 / 拷不动都只记日志,该项继续用安装目录里的那份,绝不删用户文件、
|
||||
// 也绝不因此启动失败。
|
||||
function prepareUserPaths({ plan, log }) {
|
||||
const notify = typeof log === 'function' ? log : () => {}
|
||||
const result = {
|
||||
mode: plan.mode,
|
||||
reason: plan.reason,
|
||||
root: plan.mode === 'user' ? plan.root : null,
|
||||
consoleConfig: plan.items.consoleConfig.template,
|
||||
rulesDir: plan.items.rulesDir.template,
|
||||
// null = 不接管:拉起服务端与 passwd 时沿用 config.json 里 -config 的原值
|
||||
serverConfig: null
|
||||
}
|
||||
|
||||
if (plan.mode !== 'user') {
|
||||
if (plan.reason === 'no-localappdata') {
|
||||
notify(
|
||||
'警告:拿不到 %LOCALAPPDATA%,config.json / config.yaml / rules 继续用安装目录里的那份,' +
|
||||
'覆盖安装会连同它们一起丢失'
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
for (const name of ITEM_NAMES) {
|
||||
const { target, template } = plan.items[name]
|
||||
const label = ITEM_LABEL[name]
|
||||
if (path.resolve(target) === path.resolve(template)) continue
|
||||
|
||||
try {
|
||||
ensureDir(path.dirname(target))
|
||||
if (!fs.existsSync(target)) {
|
||||
if (!fs.existsSync(template)) {
|
||||
notify(`警告:${label}在用户目录与安装目录里都不存在(${template}),本次跳过复制`)
|
||||
} else {
|
||||
if (name === 'rulesDir') copyDirOnce(template, target)
|
||||
else fs.copyFileSync(template, target)
|
||||
notify(`已把 ${label} 复制到 ${target}(安装目录里那份留作模板)`)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
notify(
|
||||
`警告:准备 ${label} 失败(${err.message}),继续用安装目录里的 ${template};` +
|
||||
'覆盖安装会丢失对它的改动'
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if (name === 'serverConfig') result.serverConfig = target
|
||||
else result[name] = target
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 服务端读哪份 config.yaml 由 Console 定:config.json 里的 server.args 只声明「有个 -config 参数」,
|
||||
// 它的值在拉起时换成上面解析出的绝对路径(不传 configPath 时原样返回,开发态就走这条路)
|
||||
function withConfigArg(args, configPath) {
|
||||
const list = Array.isArray(args) ? [...args] : []
|
||||
if (typeof configPath !== 'string' || configPath === '') return list
|
||||
const index = list.indexOf('-config')
|
||||
if (index !== -1 && typeof list[index + 1] === 'string') list[index + 1] = configPath
|
||||
else list.push('-config', configPath)
|
||||
return list
|
||||
}
|
||||
|
||||
module.exports = { resolveUserRoot, planUserPaths, prepareUserPaths, withConfigArg }
|
||||
Reference in new issue
Block a user