标签字段可编辑,Init 参数改从 cardsoon.config.json 读取

- 模板 type 3/4/5 文本可编辑,type 1 支持选图
- cardsoon.config.json 增加 sharedDir、logLevel、cleanTaskFile(默认 0)
- cleanTaskFile 默认改为 false,启动时同步 sharedDir 到运行时配置

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
24kycj
2026-06-03 09:43:10 +08:00
parent a6fc966d8b
commit 3ec168350c
10 changed files with 165 additions and 23 deletions
+2 -1
View File
@@ -15,7 +15,7 @@ if (!gotSingleInstanceLock) {
}
import { suppressKnownDllStderr } from './utils/suppress-dll-stderr'
import { loadAppFileConfig } from './services/app-config'
import { loadAppFileConfig, applyFileConfigToStore } from './services/app-config'
import { migrateTraceConfig, setTraceWebContents } from './utils/trace-bridge'
suppressKnownDllStderr()
@@ -133,6 +133,7 @@ app.whenReady().then(async () => {
try {
migrateTraceConfig()
loadAppFileConfig()
applyFileConfigToStore()
log.info('app startup', { packaged: app.isPackaged, execPath: process.execPath })
registerIpcHandlers()
try {
+54 -3
View File
@@ -2,20 +2,59 @@ import { app } from 'electron'
import fs from 'fs'
import path from 'path'
import log from 'electron-log'
import { LOG_FATAL_FLAG } from '../constants'
import { configStore } from './config-store'
import { getProcessExecDir } from './native-path'
export const APP_CONFIG_FILENAME = 'cardsoon.config.json'
const DEFAULT_SHARED_DIR = path.join('C:', 'PrintTasks')
export interface AppFileConfig {
designAppPath: string
sharedDir: string
logLevel: number
cleanTaskFile: boolean
}
const defaults: AppFileConfig = {
designAppPath: ''
designAppPath: '',
sharedDir: DEFAULT_SHARED_DIR,
logLevel: LOG_FATAL_FLAG,
cleanTaskFile: false
}
let cached: AppFileConfig | null = null
function pickRaw(raw: Record<string, unknown>, ...keys: string[]): unknown {
for (const key of keys) {
const hit = Object.entries(raw).find(([name]) => name.toLowerCase() === key.toLowerCase())
if (hit && hit[1] != null && String(hit[1]).trim() !== '') return hit[1]
}
return undefined
}
function parseCleanTaskFile(raw: unknown): boolean {
if (raw === undefined || raw === null) return defaults.cleanTaskFile
if (typeof raw === 'boolean') return raw
if (typeof raw === 'number') return raw !== 0
const s = String(raw).trim().toLowerCase()
if (s === '0' || s === 'false') return false
if (s === '1' || s === 'true') return true
return defaults.cleanTaskFile
}
function parseLogLevel(raw: unknown): number {
if (raw === undefined || raw === null) return defaults.logLevel
const n = Number(raw)
return Number.isFinite(n) ? Math.round(n) : defaults.logLevel
}
function parseSharedDir(raw: unknown): string {
const s = String(raw ?? '').trim()
return s || DEFAULT_SHARED_DIR
}
function bundledConfigPath(): string {
if (app.isPackaged) {
return path.join(process.resourcesPath, APP_CONFIG_FILENAME)
@@ -33,7 +72,10 @@ function configSearchPaths(): string[] {
function parseConfigFile(filePath: string): AppFileConfig {
const raw = JSON.parse(fs.readFileSync(filePath, 'utf8')) as Record<string, unknown>
return {
designAppPath: String(raw.designAppPath ?? '').trim()
designAppPath: String(pickRaw(raw, 'designAppPath') ?? '').trim(),
sharedDir: parseSharedDir(pickRaw(raw, 'sharedDir', 'shareddir')),
logLevel: parseLogLevel(pickRaw(raw, 'logLevel', 'loglevel')),
cleanTaskFile: parseCleanTaskFile(pickRaw(raw, 'cleanTaskFile', 'cleantaskfile'))
}
}
@@ -44,7 +86,11 @@ export function loadAppFileConfig(): AppFileConfig {
if (!fs.existsSync(filePath)) continue
try {
cached = parseConfigFile(filePath)
log.info(`Loaded ${APP_CONFIG_FILENAME} from ${filePath}`)
log.info(`Loaded ${APP_CONFIG_FILENAME} from ${filePath}`, {
sharedDir: cached.sharedDir,
logLevel: cached.logLevel,
cleanTaskFile: cached.cleanTaskFile
})
return cached
} catch (e) {
log.warn(`Skip invalid ${APP_CONFIG_FILENAME}: ${filePath}`, e)
@@ -58,6 +104,11 @@ export function loadAppFileConfig(): AppFileConfig {
return cached
}
export function applyFileConfigToStore(): void {
const cfg = loadAppFileConfig()
configStore.set('sharedDir', cfg.sharedDir)
}
export function getDesignAppPath(): string {
return loadAppFileConfig().designAppPath
}
+5 -3
View File
@@ -3,6 +3,7 @@ import log from 'electron-log'
import { CS_OK } from '../constants'
import { mainAppState } from './app-state'
import { configStore } from './config-store'
import { loadAppFileConfig } from './app-config'
import { loadDllModule } from './dll-loader'
import type { InitParams } from './work-dll.service'
@@ -18,8 +19,9 @@ export async function ensureDllInitialized(
if (dllInitAttempted) {
return { code: CS_OK }
}
const fileCfg = loadAppFileConfig()
const sharedDir =
params?.sharedDir || (configStore.get('sharedDir') as string) || 'C:\\PrintTasks'
params?.sharedDir?.trim() || fileCfg.sharedDir || (configStore.get('sharedDir') as string)
try {
const dll = await loadDllModule()
fs.mkdirSync(sharedDir, { recursive: true })
@@ -27,10 +29,10 @@ export async function ensureDllInitialized(
sharedDir,
keepCombinedImage: params?.keepCombinedImage,
stopOnFailure: params?.stopOnFailure,
cleanTaskFile: params?.cleanTaskFile,
cleanTaskFile: params?.cleanTaskFile ?? fileCfg.cleanTaskFile,
autoRetryTimes: params?.autoRetryTimes,
rejectConfig: params?.rejectConfig,
logLevel: params?.logLevel,
logLevel: params?.logLevel ?? fileCfg.logLevel,
outBack: params?.outBack
})
dllInitAttempted = true
+2 -2
View File
@@ -239,7 +239,7 @@ export function dllInit(params: InitParams): number {
sharedDir: params.sharedDir,
keepCombinedImage: params.keepCombinedImage ?? true,
stopOnFailure: params.stopOnFailure ?? false,
cleanTaskFile: params.cleanTaskFile ?? true,
cleanTaskFile: params.cleanTaskFile ?? false,
autoRetryTimes: params.autoRetryTimes ?? 0,
rejectConfig: params.rejectConfig ?? false,
logLevel: params.logLevel ?? LOG_FATAL_FLAG,
@@ -251,7 +251,7 @@ export function dllInit(params: InitParams): number {
params.sharedDir,
params.keepCombinedImage ?? true,
params.stopOnFailure ?? false,
params.cleanTaskFile ?? true,
params.cleanTaskFile ?? false,
params.autoRetryTimes ?? 0,
params.rejectConfig ?? false,
params.logLevel ?? LOG_FATAL_FLAG,
+5 -3
View File
@@ -5,6 +5,8 @@ export interface TemplateFieldRow {
label: string
value: string
originName: string
/** soon frontData/backData 的 type1=图片,3/4/5=文本可编辑 */
fieldType: number
}
export interface ParsedSoonTemplate {
@@ -80,7 +82,7 @@ function parseSoonWorkerDisk(soonPath: string, raw: Record<string, unknown>): Pa
const name = String(o.name ?? '').trim()
if (!name) continue
const value = o.DefaultText == null ? '' : String(o.DefaultText)
fields.push({ label: `${name}[${side}]`, value, originName: name })
fields.push({ label: `${name}[${side}]`, value, originName: name, fieldType: type })
}
}
@@ -115,7 +117,7 @@ function parseSoonLegacy(soonPath: string, raw: Record<string, unknown>): Parsed
if (side === 'front') {
if (!frontImageUrl) frontImageUrl = url
const name = pickStr(item, ['name', 'field', 'key']) || 'IMAGE'
fields.push({ label: toFieldLabel(name, 'front'), value: fileRef, originName: name })
fields.push({ label: toFieldLabel(name, 'front'), value: fileRef, originName: name, fieldType: 1 })
} else if (!backImageUrl) {
backImageUrl = url
}
@@ -127,7 +129,7 @@ function parseSoonLegacy(soonPath: string, raw: Record<string, unknown>): Parsed
const value = pickStr(item, ['value', 'text', 'default', 'content', 'data'])
let side = sideOf(item)
if (!side) side = /image|img|front/i.test(name) ? 'front' : 'back'
fields.push({ label: toFieldLabel(name, side), value, originName: name })
fields.push({ label: toFieldLabel(name, side), value, originName: name, fieldType: 5 })
})
return { frontImageUrl, backImageUrl, fields, printFlag: readSoonPrintFlag(raw) }