标签字段可编辑,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:
@@ -1,3 +1,6 @@
|
|||||||
{
|
{
|
||||||
"designAppPath": "D:\\SoonProject\\SoonDesign\\build\\win-unpacked\\SoonDesign.exe"
|
"designAppPath": "D:\\SoonProject\\SoonDesign\\build\\win-unpacked\\SoonDesign.exe",
|
||||||
|
"sharedDir": "C:\\PrintTasks",
|
||||||
|
"logLevel": 3,
|
||||||
|
"cleanTaskFile": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ if (!gotSingleInstanceLock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
import { suppressKnownDllStderr } from './utils/suppress-dll-stderr'
|
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'
|
import { migrateTraceConfig, setTraceWebContents } from './utils/trace-bridge'
|
||||||
|
|
||||||
suppressKnownDllStderr()
|
suppressKnownDllStderr()
|
||||||
@@ -133,6 +133,7 @@ app.whenReady().then(async () => {
|
|||||||
try {
|
try {
|
||||||
migrateTraceConfig()
|
migrateTraceConfig()
|
||||||
loadAppFileConfig()
|
loadAppFileConfig()
|
||||||
|
applyFileConfigToStore()
|
||||||
log.info('app startup', { packaged: app.isPackaged, execPath: process.execPath })
|
log.info('app startup', { packaged: app.isPackaged, execPath: process.execPath })
|
||||||
registerIpcHandlers()
|
registerIpcHandlers()
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,20 +2,59 @@ import { app } from 'electron'
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import log from 'electron-log'
|
import log from 'electron-log'
|
||||||
|
import { LOG_FATAL_FLAG } from '../constants'
|
||||||
|
import { configStore } from './config-store'
|
||||||
import { getProcessExecDir } from './native-path'
|
import { getProcessExecDir } from './native-path'
|
||||||
|
|
||||||
export const APP_CONFIG_FILENAME = 'cardsoon.config.json'
|
export const APP_CONFIG_FILENAME = 'cardsoon.config.json'
|
||||||
|
|
||||||
|
const DEFAULT_SHARED_DIR = path.join('C:', 'PrintTasks')
|
||||||
|
|
||||||
export interface AppFileConfig {
|
export interface AppFileConfig {
|
||||||
designAppPath: string
|
designAppPath: string
|
||||||
|
sharedDir: string
|
||||||
|
logLevel: number
|
||||||
|
cleanTaskFile: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaults: AppFileConfig = {
|
const defaults: AppFileConfig = {
|
||||||
designAppPath: ''
|
designAppPath: '',
|
||||||
|
sharedDir: DEFAULT_SHARED_DIR,
|
||||||
|
logLevel: LOG_FATAL_FLAG,
|
||||||
|
cleanTaskFile: false
|
||||||
}
|
}
|
||||||
|
|
||||||
let cached: AppFileConfig | null = null
|
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 {
|
function bundledConfigPath(): string {
|
||||||
if (app.isPackaged) {
|
if (app.isPackaged) {
|
||||||
return path.join(process.resourcesPath, APP_CONFIG_FILENAME)
|
return path.join(process.resourcesPath, APP_CONFIG_FILENAME)
|
||||||
@@ -33,7 +72,10 @@ function configSearchPaths(): string[] {
|
|||||||
function parseConfigFile(filePath: string): AppFileConfig {
|
function parseConfigFile(filePath: string): AppFileConfig {
|
||||||
const raw = JSON.parse(fs.readFileSync(filePath, 'utf8')) as Record<string, unknown>
|
const raw = JSON.parse(fs.readFileSync(filePath, 'utf8')) as Record<string, unknown>
|
||||||
return {
|
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
|
if (!fs.existsSync(filePath)) continue
|
||||||
try {
|
try {
|
||||||
cached = parseConfigFile(filePath)
|
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
|
return cached
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.warn(`Skip invalid ${APP_CONFIG_FILENAME}: ${filePath}`, e)
|
log.warn(`Skip invalid ${APP_CONFIG_FILENAME}: ${filePath}`, e)
|
||||||
@@ -58,6 +104,11 @@ export function loadAppFileConfig(): AppFileConfig {
|
|||||||
return cached
|
return cached
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyFileConfigToStore(): void {
|
||||||
|
const cfg = loadAppFileConfig()
|
||||||
|
configStore.set('sharedDir', cfg.sharedDir)
|
||||||
|
}
|
||||||
|
|
||||||
export function getDesignAppPath(): string {
|
export function getDesignAppPath(): string {
|
||||||
return loadAppFileConfig().designAppPath
|
return loadAppFileConfig().designAppPath
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import log from 'electron-log'
|
|||||||
import { CS_OK } from '../constants'
|
import { CS_OK } from '../constants'
|
||||||
import { mainAppState } from './app-state'
|
import { mainAppState } from './app-state'
|
||||||
import { configStore } from './config-store'
|
import { configStore } from './config-store'
|
||||||
|
import { loadAppFileConfig } from './app-config'
|
||||||
import { loadDllModule } from './dll-loader'
|
import { loadDllModule } from './dll-loader'
|
||||||
import type { InitParams } from './work-dll.service'
|
import type { InitParams } from './work-dll.service'
|
||||||
|
|
||||||
@@ -18,8 +19,9 @@ export async function ensureDllInitialized(
|
|||||||
if (dllInitAttempted) {
|
if (dllInitAttempted) {
|
||||||
return { code: CS_OK }
|
return { code: CS_OK }
|
||||||
}
|
}
|
||||||
|
const fileCfg = loadAppFileConfig()
|
||||||
const sharedDir =
|
const sharedDir =
|
||||||
params?.sharedDir || (configStore.get('sharedDir') as string) || 'C:\\PrintTasks'
|
params?.sharedDir?.trim() || fileCfg.sharedDir || (configStore.get('sharedDir') as string)
|
||||||
try {
|
try {
|
||||||
const dll = await loadDllModule()
|
const dll = await loadDllModule()
|
||||||
fs.mkdirSync(sharedDir, { recursive: true })
|
fs.mkdirSync(sharedDir, { recursive: true })
|
||||||
@@ -27,10 +29,10 @@ export async function ensureDllInitialized(
|
|||||||
sharedDir,
|
sharedDir,
|
||||||
keepCombinedImage: params?.keepCombinedImage,
|
keepCombinedImage: params?.keepCombinedImage,
|
||||||
stopOnFailure: params?.stopOnFailure,
|
stopOnFailure: params?.stopOnFailure,
|
||||||
cleanTaskFile: params?.cleanTaskFile,
|
cleanTaskFile: params?.cleanTaskFile ?? fileCfg.cleanTaskFile,
|
||||||
autoRetryTimes: params?.autoRetryTimes,
|
autoRetryTimes: params?.autoRetryTimes,
|
||||||
rejectConfig: params?.rejectConfig,
|
rejectConfig: params?.rejectConfig,
|
||||||
logLevel: params?.logLevel,
|
logLevel: params?.logLevel ?? fileCfg.logLevel,
|
||||||
outBack: params?.outBack
|
outBack: params?.outBack
|
||||||
})
|
})
|
||||||
dllInitAttempted = true
|
dllInitAttempted = true
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ export function dllInit(params: InitParams): number {
|
|||||||
sharedDir: params.sharedDir,
|
sharedDir: params.sharedDir,
|
||||||
keepCombinedImage: params.keepCombinedImage ?? true,
|
keepCombinedImage: params.keepCombinedImage ?? true,
|
||||||
stopOnFailure: params.stopOnFailure ?? false,
|
stopOnFailure: params.stopOnFailure ?? false,
|
||||||
cleanTaskFile: params.cleanTaskFile ?? true,
|
cleanTaskFile: params.cleanTaskFile ?? false,
|
||||||
autoRetryTimes: params.autoRetryTimes ?? 0,
|
autoRetryTimes: params.autoRetryTimes ?? 0,
|
||||||
rejectConfig: params.rejectConfig ?? false,
|
rejectConfig: params.rejectConfig ?? false,
|
||||||
logLevel: params.logLevel ?? LOG_FATAL_FLAG,
|
logLevel: params.logLevel ?? LOG_FATAL_FLAG,
|
||||||
@@ -251,7 +251,7 @@ export function dllInit(params: InitParams): number {
|
|||||||
params.sharedDir,
|
params.sharedDir,
|
||||||
params.keepCombinedImage ?? true,
|
params.keepCombinedImage ?? true,
|
||||||
params.stopOnFailure ?? false,
|
params.stopOnFailure ?? false,
|
||||||
params.cleanTaskFile ?? true,
|
params.cleanTaskFile ?? false,
|
||||||
params.autoRetryTimes ?? 0,
|
params.autoRetryTimes ?? 0,
|
||||||
params.rejectConfig ?? false,
|
params.rejectConfig ?? false,
|
||||||
params.logLevel ?? LOG_FATAL_FLAG,
|
params.logLevel ?? LOG_FATAL_FLAG,
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ export interface TemplateFieldRow {
|
|||||||
label: string
|
label: string
|
||||||
value: string
|
value: string
|
||||||
originName: string
|
originName: string
|
||||||
|
/** soon frontData/backData 的 type:1=图片,3/4/5=文本可编辑 */
|
||||||
|
fieldType: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ParsedSoonTemplate {
|
export interface ParsedSoonTemplate {
|
||||||
@@ -80,7 +82,7 @@ function parseSoonWorkerDisk(soonPath: string, raw: Record<string, unknown>): Pa
|
|||||||
const name = String(o.name ?? '').trim()
|
const name = String(o.name ?? '').trim()
|
||||||
if (!name) continue
|
if (!name) continue
|
||||||
const value = o.DefaultText == null ? '' : String(o.DefaultText)
|
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 (side === 'front') {
|
||||||
if (!frontImageUrl) frontImageUrl = url
|
if (!frontImageUrl) frontImageUrl = url
|
||||||
const name = pickStr(item, ['name', 'field', 'key']) || 'IMAGE'
|
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) {
|
} else if (!backImageUrl) {
|
||||||
backImageUrl = url
|
backImageUrl = url
|
||||||
}
|
}
|
||||||
@@ -127,7 +129,7 @@ function parseSoonLegacy(soonPath: string, raw: Record<string, unknown>): Parsed
|
|||||||
const value = pickStr(item, ['value', 'text', 'default', 'content', 'data'])
|
const value = pickStr(item, ['value', 'text', 'default', 'content', 'data'])
|
||||||
let side = sideOf(item)
|
let side = sideOf(item)
|
||||||
if (!side) side = /image|img|front/i.test(name) ? 'front' : 'back'
|
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) }
|
return { frontImageUrl, backImageUrl, fields, printFlag: readSoonPrintFlag(raw) }
|
||||||
|
|||||||
@@ -103,6 +103,12 @@ export async function dialogOpenSoon(): Promise<IpcResult<{ path: string }>> {
|
|||||||
>
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function dialogOpenImage(): Promise<IpcResult<{ path: string }>> {
|
||||||
|
return api().invoke('dialog:open-file', [
|
||||||
|
{ name: 'Image', extensions: ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'webp'] }
|
||||||
|
]) as Promise<IpcResult<{ path: string }>>
|
||||||
|
}
|
||||||
|
|
||||||
export async function fsPathExists(paths: string[]): Promise<IpcResult<{ missing: string[] }>> {
|
export async function fsPathExists(paths: string[]): Promise<IpcResult<{ missing: string[] }>> {
|
||||||
return api().invoke('fs:path-exists', paths) as Promise<IpcResult<{ missing: string[] }>>
|
return api().invoke('fs:path-exists', paths) as Promise<IpcResult<{ missing: string[] }>>
|
||||||
}
|
}
|
||||||
@@ -126,7 +132,7 @@ export async function fsParseSoon(filePath: string): Promise<
|
|||||||
IpcResult<{
|
IpcResult<{
|
||||||
frontImageUrl: string
|
frontImageUrl: string
|
||||||
backImageUrl: string
|
backImageUrl: string
|
||||||
fields: { label: string; value: string; originName: string }[]
|
fields: { label: string; value: string; originName: string; fieldType: number }[]
|
||||||
printFlag: number
|
printFlag: number
|
||||||
}>
|
}>
|
||||||
> {
|
> {
|
||||||
@@ -134,7 +140,7 @@ export async function fsParseSoon(filePath: string): Promise<
|
|||||||
IpcResult<{
|
IpcResult<{
|
||||||
frontImageUrl: string
|
frontImageUrl: string
|
||||||
backImageUrl: string
|
backImageUrl: string
|
||||||
fields: { label: string; value: string; originName: string }[]
|
fields: { label: string; value: string; originName: string; fieldType: number }[]
|
||||||
printFlag: number
|
printFlag: number
|
||||||
}>
|
}>
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export interface TemplateFieldRow {
|
|||||||
label: string
|
label: string
|
||||||
value: string
|
value: string
|
||||||
originName: string
|
originName: string
|
||||||
|
fieldType: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TemplatePreview {
|
export interface TemplatePreview {
|
||||||
|
|||||||
@@ -566,6 +566,43 @@
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c-data-table-mini .c-field-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 16px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
color: inherit;
|
||||||
|
outline: none;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-field-pick {
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: #fff;
|
||||||
|
font-size: 8px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #495057;
|
||||||
|
padding: 0 6px;
|
||||||
|
height: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-field-pick:hover {
|
||||||
|
background: #f1f3f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-field-value {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.c-path-cell__text {
|
.c-path-cell__text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|||||||
@@ -117,7 +117,21 @@
|
|||||||
<table class="c-data-table-mini">
|
<table class="c-data-table-mini">
|
||||||
<tr v-for="(row, idx) in formStore.templatePreview!.fields" :key="idx">
|
<tr v-for="(row, idx) in formStore.templatePreview!.fields" :key="idx">
|
||||||
<td>{{ row.label }}</td>
|
<td>{{ row.label }}</td>
|
||||||
<td>
|
<td v-if="isImageField(row)" class="c-path-cell">
|
||||||
|
<span class="c-path-cell__text" :title="row.value">{{
|
||||||
|
imageFieldLabel(row.value)
|
||||||
|
}}</span>
|
||||||
|
<button type="button" class="c-field-pick" @click="pickFieldImage(idx)">选择</button>
|
||||||
|
</td>
|
||||||
|
<td v-else-if="isTextField(row)">
|
||||||
|
<input
|
||||||
|
v-model="row.value"
|
||||||
|
type="text"
|
||||||
|
class="c-field-input"
|
||||||
|
:placeholder="row.label"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td v-else>
|
||||||
<span class="c-field-value" :title="row.value">{{ row.value || '—' }}</span>
|
<span class="c-field-value" :title="row.value">{{ row.value || '—' }}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -148,8 +162,10 @@ import { useAppStore } from '@/stores/app'
|
|||||||
import { validateJobPreflight } from '@/utils/validateJobPreflight'
|
import { validateJobPreflight } from '@/utils/validateJobPreflight'
|
||||||
import { createDistributeJob } from '@/utils/createDistributeJob'
|
import { createDistributeJob } from '@/utils/createDistributeJob'
|
||||||
import { formatBytesAsGb, formatBytesCompact } from '@/utils/formatBytes'
|
import { formatBytesAsGb, formatBytesCompact } from '@/utils/formatBytes'
|
||||||
|
import type { TemplateFieldRow } from '@/stores/distributeForm'
|
||||||
import {
|
import {
|
||||||
dialogOpenDirectory,
|
dialogOpenDirectory,
|
||||||
|
dialogOpenImage,
|
||||||
dialogOpenSoon,
|
dialogOpenSoon,
|
||||||
dllJobCancel,
|
dllJobCancel,
|
||||||
fsDirSize,
|
fsDirSize,
|
||||||
@@ -185,6 +201,36 @@ const loadProgressText = computed(() => {
|
|||||||
return `已加载: ${loadedGb} GB / ${CARD_CAPACITY_GB} GB (${loadPercent.value}%)`
|
return `已加载: ${loadedGb} GB / ${CARD_CAPACITY_GB} GB (${loadPercent.value}%)`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function isTextField(row: TemplateFieldRow): boolean {
|
||||||
|
return row.fieldType === 3 || row.fieldType === 4 || row.fieldType === 5
|
||||||
|
}
|
||||||
|
|
||||||
|
function isImageField(row: TemplateFieldRow): boolean {
|
||||||
|
return row.fieldType === 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function imageFieldLabel(value: string): string {
|
||||||
|
const v = value.trim()
|
||||||
|
if (!v) return '未选择图片'
|
||||||
|
const parts = v.replace(/\\/g, '/').split('/')
|
||||||
|
return parts[parts.length - 1] || v
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pickFieldImage(idx: number): Promise<void> {
|
||||||
|
const preview = formStore.templatePreview
|
||||||
|
if (!preview) return
|
||||||
|
const row = preview.fields[idx]
|
||||||
|
if (!row || !isImageField(row)) return
|
||||||
|
const r = await dialogOpenImage()
|
||||||
|
if (!r.ok) {
|
||||||
|
notify.error(r.message || '打开图片选择失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const picked = r.data?.path?.trim()
|
||||||
|
if (!picked) return
|
||||||
|
row.value = picked
|
||||||
|
}
|
||||||
|
|
||||||
function onClear(): void {
|
function onClear(): void {
|
||||||
formStore.reset()
|
formStore.reset()
|
||||||
notify.info('已清空,已恢复初始状态')
|
notify.info('已清空,已恢复初始状态')
|
||||||
@@ -341,11 +387,4 @@ async function onSubmit(): Promise<void> {
|
|||||||
.c-card-small--back {
|
.c-card-small--back {
|
||||||
background: #f8f9fa;
|
background: #f8f9fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-field-value {
|
|
||||||
display: block;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user