mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 21:14:21 +08:00
Merge branch 'main' of https://github.com/SECTL/SecScore
This commit is contained in:
@@ -16,8 +16,7 @@ export {
|
|||||||
ThemeServiceToken,
|
ThemeServiceToken,
|
||||||
WindowManagerToken,
|
WindowManagerToken,
|
||||||
TrayServiceToken,
|
TrayServiceToken,
|
||||||
AutoScoreServiceToken,
|
AutoScoreServiceToken
|
||||||
FileSystemServiceToken
|
|
||||||
} from './tokens'
|
} from './tokens'
|
||||||
export type {
|
export type {
|
||||||
appRuntimeContext,
|
appRuntimeContext,
|
||||||
|
|||||||
@@ -15,4 +15,3 @@ export const ThemeServiceToken = Symbol.for('secscore.themeService')
|
|||||||
export const WindowManagerToken = Symbol.for('secscore.windowManager')
|
export const WindowManagerToken = Symbol.for('secscore.windowManager')
|
||||||
export const TrayServiceToken = Symbol.for('secscore.trayService')
|
export const TrayServiceToken = Symbol.for('secscore.trayService')
|
||||||
export const AutoScoreServiceToken = Symbol.for('secscore.autoScoreService')
|
export const AutoScoreServiceToken = Symbol.for('secscore.autoScoreService')
|
||||||
export const FileSystemServiceToken = Symbol.for('secscore.fileSystemService')
|
|
||||||
|
|||||||
+1
-8
@@ -16,7 +16,6 @@ import { ThemeService } from './services/ThemeService'
|
|||||||
import { WindowManager, type windowManagerOptions } from './services/WindowManager'
|
import { WindowManager, type windowManagerOptions } from './services/WindowManager'
|
||||||
import { TrayService } from './services/TrayService'
|
import { TrayService } from './services/TrayService'
|
||||||
import { AutoScoreService } from './services/AutoScoreService'
|
import { AutoScoreService } from './services/AutoScoreService'
|
||||||
import { FileSystemService } from './services/FileSystemService'
|
|
||||||
import { DbConnectionService } from './services/DbConnectionService'
|
import { DbConnectionService } from './services/DbConnectionService'
|
||||||
import { StudentRepository } from './repos/StudentRepository'
|
import { StudentRepository } from './repos/StudentRepository'
|
||||||
import { ReasonRepository } from './repos/ReasonRepository'
|
import { ReasonRepository } from './repos/ReasonRepository'
|
||||||
@@ -39,8 +38,7 @@ import {
|
|||||||
ThemeServiceToken,
|
ThemeServiceToken,
|
||||||
WindowManagerToken,
|
WindowManagerToken,
|
||||||
TrayServiceToken,
|
TrayServiceToken,
|
||||||
AutoScoreServiceToken,
|
AutoScoreServiceToken
|
||||||
FileSystemServiceToken
|
|
||||||
} from './hosting'
|
} from './hosting'
|
||||||
|
|
||||||
type mainAppConfig = {
|
type mainAppConfig = {
|
||||||
@@ -267,10 +265,6 @@ app.whenReady().then(async () => {
|
|||||||
TrayServiceToken,
|
TrayServiceToken,
|
||||||
(p) => new TrayService(p.get(MainContext), config.window)
|
(p) => new TrayService(p.get(MainContext), config.window)
|
||||||
)
|
)
|
||||||
services.addSingleton(
|
|
||||||
FileSystemServiceToken,
|
|
||||||
(p) => new FileSystemService(p.get(MainContext), config.configDir)
|
|
||||||
)
|
|
||||||
services.addSingleton(AutoScoreServiceToken, (p) => new AutoScoreService(p.get(MainContext)))
|
services.addSingleton(AutoScoreServiceToken, (p) => new AutoScoreService(p.get(MainContext)))
|
||||||
services.addSingleton(DbConnectionService, (p) => new DbConnectionService(p.get(MainContext)))
|
services.addSingleton(DbConnectionService, (p) => new DbConnectionService(p.get(MainContext)))
|
||||||
})
|
})
|
||||||
@@ -320,7 +314,6 @@ app.whenReady().then(async () => {
|
|||||||
const tray = services.get(TrayServiceToken) as TrayService
|
const tray = services.get(TrayServiceToken) as TrayService
|
||||||
tray.initialize()
|
tray.initialize()
|
||||||
}
|
}
|
||||||
services.get(FileSystemServiceToken)
|
|
||||||
const autoScore = services.get(AutoScoreServiceToken) as AutoScoreService
|
const autoScore = services.get(AutoScoreServiceToken) as AutoScoreService
|
||||||
await autoScore.initialize?.()
|
await autoScore.initialize?.()
|
||||||
services.get(DbConnectionService)
|
services.get(DbConnectionService)
|
||||||
|
|||||||
@@ -11,30 +11,12 @@ interface AutoScoreRule extends RuleConfig {
|
|||||||
lastExecuted?: Date
|
lastExecuted?: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AutoScoreRuleFileData {
|
|
||||||
id: number
|
|
||||||
enabled: boolean
|
|
||||||
name: string
|
|
||||||
studentNames: string[]
|
|
||||||
lastExecuted?: string
|
|
||||||
triggers?: Array<{ event: string; value?: string; relation?: 'AND' | 'OR' }>
|
|
||||||
actions?: Array<{ event: string; value?: string; reason?: string }>
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AutoScoreRulesFile {
|
|
||||||
version: number
|
|
||||||
rules: AutoScoreRuleFileData[]
|
|
||||||
updatedAt?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '../../shared/kernel' {
|
declare module '../../shared/kernel' {
|
||||||
interface Context {
|
interface Context {
|
||||||
autoScore: AutoScoreService
|
autoScore: AutoScoreService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const RULES_FILE_NAME = 'auto-score-rules.json'
|
|
||||||
|
|
||||||
export class AutoScoreService extends Service {
|
export class AutoScoreService extends Service {
|
||||||
private rules: AutoScoreRule[] = []
|
private rules: AutoScoreRule[] = []
|
||||||
private timers: Map<number, NodeJS.Timeout> = new Map()
|
private timers: Map<number, NodeJS.Timeout> = new Map()
|
||||||
@@ -130,83 +112,7 @@ export class AutoScoreService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async loadRulesFromFile(): Promise<void> {
|
private async loadRulesFromFile(): Promise<void> {
|
||||||
try {
|
|
||||||
const fs = this.mainCtx.fileSystem
|
|
||||||
if (!fs) {
|
|
||||||
this.logger.warn('FileSystemService not available, falling back to settings')
|
|
||||||
await this.loadRulesFromSettings()
|
await this.loadRulesFromSettings()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await fs.readJsonFile<AutoScoreRulesFile>(RULES_FILE_NAME, 'automatic')
|
|
||||||
if (data && data.rules) {
|
|
||||||
this.rules = data.rules.map((rule: any) => {
|
|
||||||
const migratedRule = this.migrateRule(rule)
|
|
||||||
return {
|
|
||||||
...migratedRule,
|
|
||||||
lastExecuted: migratedRule.lastExecuted
|
|
||||||
? new Date(migratedRule.lastExecuted)
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (
|
|
||||||
data.rules.some(
|
|
||||||
(rule: any) => rule.intervalMinutes !== undefined || rule.scoreValue !== undefined
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
await this.saveRulesToFile()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await this.loadRulesFromSettings()
|
|
||||||
await this.saveRulesToFile()
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.warn('Failed to load auto score rules from file, falling back to settings', {
|
|
||||||
error
|
|
||||||
})
|
|
||||||
await this.loadRulesFromSettings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private migrateRule(rule: any): AutoScoreRule {
|
|
||||||
if (!rule.intervalMinutes && !rule.scoreValue) {
|
|
||||||
return rule
|
|
||||||
}
|
|
||||||
|
|
||||||
const migratedRule: AutoScoreRule = {
|
|
||||||
id: rule.id,
|
|
||||||
enabled: rule.enabled,
|
|
||||||
name: rule.name,
|
|
||||||
studentNames: rule.studentNames || [],
|
|
||||||
lastExecuted: rule.lastExecuted,
|
|
||||||
triggers: rule.triggers || [],
|
|
||||||
actions: rule.actions || []
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
rule.intervalMinutes &&
|
|
||||||
!migratedRule.triggers?.find((t) => t.event === 'interval_time_passed')
|
|
||||||
) {
|
|
||||||
migratedRule.triggers = migratedRule.triggers || []
|
|
||||||
migratedRule.triggers.push({
|
|
||||||
event: 'interval_time_passed',
|
|
||||||
value: String(rule.intervalMinutes)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
rule.scoreValue !== undefined &&
|
|
||||||
!migratedRule.actions?.find((a) => a.event === 'add_score')
|
|
||||||
) {
|
|
||||||
migratedRule.actions = migratedRule.actions || []
|
|
||||||
migratedRule.actions.push({
|
|
||||||
event: 'add_score',
|
|
||||||
value: String(rule.scoreValue),
|
|
||||||
reason: rule.reason
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return migratedRule
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadRulesFromSettings() {
|
private async loadRulesFromSettings() {
|
||||||
@@ -226,32 +132,7 @@ export class AutoScoreService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async saveRulesToFile(): Promise<void> {
|
private async saveRulesToFile(): Promise<void> {
|
||||||
try {
|
|
||||||
const fs = this.mainCtx.fileSystem
|
|
||||||
if (!fs) {
|
|
||||||
this.logger.warn('FileSystemService not available, falling back to settings')
|
|
||||||
await this.saveRulesToSettings()
|
await this.saveRulesToSettings()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const data: AutoScoreRulesFile = {
|
|
||||||
version: 1,
|
|
||||||
rules: this.rules.map(({ lastExecuted, ...rule }) => ({
|
|
||||||
...rule,
|
|
||||||
lastExecuted: lastExecuted?.toISOString()
|
|
||||||
})),
|
|
||||||
updatedAt: new Date().toISOString()
|
|
||||||
}
|
|
||||||
|
|
||||||
const success = await fs.writeJsonFile(RULES_FILE_NAME, data, 'automatic')
|
|
||||||
if (!success) {
|
|
||||||
this.logger.warn('Failed to save rules to file, falling back to settings')
|
|
||||||
await this.saveRulesToSettings()
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error('Failed to save auto score rules to file:', { error })
|
|
||||||
await this.saveRulesToSettings()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async saveRulesToSettings() {
|
private async saveRulesToSettings() {
|
||||||
|
|||||||
@@ -1,281 +0,0 @@
|
|||||||
import { Service } from '../../shared/kernel'
|
|
||||||
import { MainContext } from '../context'
|
|
||||||
import { join } from 'path'
|
|
||||||
import fs from 'fs/promises'
|
|
||||||
|
|
||||||
declare module '../../shared/kernel' {
|
|
||||||
interface Context {
|
|
||||||
fileSystem: FileSystemService
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ConfigFileInfo {
|
|
||||||
name: string
|
|
||||||
path: string
|
|
||||||
size: number
|
|
||||||
modified: Date
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ConfigFolderStructure {
|
|
||||||
configRoot: string
|
|
||||||
automatic: string
|
|
||||||
script: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FileSystemService extends Service {
|
|
||||||
private configRoot: string
|
|
||||||
private automaticDir: string
|
|
||||||
private scriptDir: string
|
|
||||||
private initPromise: Promise<void> | null = null
|
|
||||||
|
|
||||||
constructor(ctx: MainContext, ConfigRoot: string) {
|
|
||||||
super(ctx, 'fileSystem')
|
|
||||||
this.configRoot = ConfigRoot
|
|
||||||
this.automaticDir = join(this.configRoot, 'automatic')
|
|
||||||
this.scriptDir = join(this.configRoot, 'script')
|
|
||||||
this.initPromise = this.initialize()
|
|
||||||
this.registerIpc()
|
|
||||||
}
|
|
||||||
|
|
||||||
private async initialize(): Promise<void> {
|
|
||||||
await this.ensureDirectories()
|
|
||||||
}
|
|
||||||
|
|
||||||
private async ensureDirectories(): Promise<void> {
|
|
||||||
try {
|
|
||||||
await fs.mkdir(this.configRoot, { recursive: true })
|
|
||||||
await fs.mkdir(this.automaticDir, { recursive: true })
|
|
||||||
await fs.mkdir(this.scriptDir, { recursive: true })
|
|
||||||
} catch (error) {
|
|
||||||
this.ctx.logger.warn('FileSystemService: Failed to create config directories', { error })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getConfigStructure(): ConfigFolderStructure {
|
|
||||||
return {
|
|
||||||
configRoot: this.configRoot,
|
|
||||||
automatic: this.automaticDir,
|
|
||||||
script: this.scriptDir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async readJsonFile<T = any>(
|
|
||||||
relativePath: string,
|
|
||||||
folder: 'automatic' | 'script' = 'automatic'
|
|
||||||
): Promise<T | null> {
|
|
||||||
await this.initPromise
|
|
||||||
const baseDir = folder === 'automatic' ? this.automaticDir : this.scriptDir
|
|
||||||
const filePath = join(baseDir, relativePath)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const content = await fs.readFile(filePath, 'utf-8')
|
|
||||||
return JSON.parse(content) as T
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof Error && 'code' in error && (error as any).code === 'ENOENT') {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
this.ctx.logger.warn('FileSystemService: Failed to read JSON file', { path: filePath, error })
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async writeJsonFile(
|
|
||||||
relativePath: string,
|
|
||||||
data: any,
|
|
||||||
folder: 'automatic' | 'script' = 'automatic'
|
|
||||||
): Promise<boolean> {
|
|
||||||
await this.initPromise
|
|
||||||
const baseDir = folder === 'automatic' ? this.automaticDir : this.scriptDir
|
|
||||||
const filePath = join(baseDir, relativePath)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const content = JSON.stringify(data, null, 2)
|
|
||||||
await fs.writeFile(filePath, content, 'utf-8')
|
|
||||||
return true
|
|
||||||
} catch (error) {
|
|
||||||
this.ctx.logger.warn('FileSystemService: Failed to write JSON file', {
|
|
||||||
path: filePath,
|
|
||||||
error
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async readTextFile(
|
|
||||||
relativePath: string,
|
|
||||||
folder: 'automatic' | 'script' = 'automatic'
|
|
||||||
): Promise<string | null> {
|
|
||||||
await this.initPromise
|
|
||||||
const baseDir = folder === 'automatic' ? this.automaticDir : this.scriptDir
|
|
||||||
const filePath = join(baseDir, relativePath)
|
|
||||||
|
|
||||||
try {
|
|
||||||
return await fs.readFile(filePath, 'utf-8')
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof Error && 'code' in error && (error as any).code === 'ENOENT') {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
this.ctx.logger.warn('FileSystemService: Failed to read text file', { path: filePath, error })
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async writeTextFile(
|
|
||||||
content: string,
|
|
||||||
relativePath: string,
|
|
||||||
folder: 'automatic' | 'script' = 'automatic'
|
|
||||||
): Promise<boolean> {
|
|
||||||
await this.initPromise
|
|
||||||
const baseDir = folder === 'automatic' ? this.automaticDir : this.scriptDir
|
|
||||||
const filePath = join(baseDir, relativePath)
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fs.writeFile(filePath, content, 'utf-8')
|
|
||||||
return true
|
|
||||||
} catch (error) {
|
|
||||||
this.ctx.logger.warn('FileSystemService: Failed to write text file', {
|
|
||||||
path: filePath,
|
|
||||||
error
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteFile(
|
|
||||||
relativePath: string,
|
|
||||||
folder: 'automatic' | 'script' = 'automatic'
|
|
||||||
): Promise<boolean> {
|
|
||||||
await this.initPromise
|
|
||||||
const baseDir = folder === 'automatic' ? this.automaticDir : this.scriptDir
|
|
||||||
const filePath = join(baseDir, relativePath)
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fs.unlink(filePath)
|
|
||||||
return true
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof Error && 'code' in error && (error as any).code === 'ENOENT') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
this.ctx.logger.warn('FileSystemService: Failed to delete file', { path: filePath, error })
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async listFiles(folder: 'automatic' | 'script' = 'automatic'): Promise<ConfigFileInfo[]> {
|
|
||||||
await this.initPromise
|
|
||||||
const baseDir = folder === 'automatic' ? this.automaticDir : this.scriptDir
|
|
||||||
|
|
||||||
try {
|
|
||||||
const entries = await fs.readdir(baseDir, { withFileTypes: true })
|
|
||||||
const files: ConfigFileInfo[] = []
|
|
||||||
|
|
||||||
for (const entry of entries) {
|
|
||||||
if (entry.isFile()) {
|
|
||||||
const filePath = join(baseDir, entry.name)
|
|
||||||
const stats = await fs.stat(filePath)
|
|
||||||
files.push({
|
|
||||||
name: entry.name,
|
|
||||||
path: filePath,
|
|
||||||
size: stats.size,
|
|
||||||
modified: stats.mtime
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return files
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof Error && 'code' in error && (error as any).code === 'ENOENT') {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
this.ctx.logger.warn('FileSystemService: Failed to list files', { path: baseDir, error })
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fileExists(
|
|
||||||
relativePath: string,
|
|
||||||
folder: 'automatic' | 'script' = 'automatic'
|
|
||||||
): Promise<boolean> {
|
|
||||||
await this.initPromise
|
|
||||||
const baseDir = folder === 'automatic' ? this.automaticDir : this.scriptDir
|
|
||||||
const filePath = join(baseDir, relativePath)
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fs.access(filePath)
|
|
||||||
return true
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private get mainCtx() {
|
|
||||||
return this.ctx as MainContext
|
|
||||||
}
|
|
||||||
|
|
||||||
private registerIpc(): void {
|
|
||||||
this.mainCtx.handle('fs:getConfigStructure', async () => {
|
|
||||||
await this.initPromise
|
|
||||||
return { success: true, data: this.getConfigStructure() }
|
|
||||||
})
|
|
||||||
|
|
||||||
this.mainCtx.handle(
|
|
||||||
'fs:readJson',
|
|
||||||
async (_event, relativePath: string, folder: 'automatic' | 'script') => {
|
|
||||||
await this.initPromise
|
|
||||||
const data = await this.readJsonFile(relativePath, folder)
|
|
||||||
return { success: true, data }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
this.mainCtx.handle(
|
|
||||||
'fs:writeJson',
|
|
||||||
async (_event, relativePath: string, data: any, folder: 'automatic' | 'script') => {
|
|
||||||
await this.initPromise
|
|
||||||
const success = await this.writeJsonFile(relativePath, data, folder)
|
|
||||||
return { success }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
this.mainCtx.handle(
|
|
||||||
'fs:readText',
|
|
||||||
async (_event, relativePath: string, folder: 'automatic' | 'script') => {
|
|
||||||
await this.initPromise
|
|
||||||
const content = await this.readTextFile(relativePath, folder)
|
|
||||||
return { success: true, data: content }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
this.mainCtx.handle(
|
|
||||||
'fs:writeText',
|
|
||||||
async (_event, content: string, relativePath: string, folder: 'automatic' | 'script') => {
|
|
||||||
await this.initPromise
|
|
||||||
const success = await this.writeTextFile(content, relativePath, folder)
|
|
||||||
return { success }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
this.mainCtx.handle(
|
|
||||||
'fs:deleteFile',
|
|
||||||
async (_event, relativePath: string, folder: 'automatic' | 'script') => {
|
|
||||||
await this.initPromise
|
|
||||||
const success = await this.deleteFile(relativePath, folder)
|
|
||||||
return { success }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
this.mainCtx.handle('fs:listFiles', async (_event, folder: 'automatic' | 'script') => {
|
|
||||||
await this.initPromise
|
|
||||||
const files = await this.listFiles(folder)
|
|
||||||
return { success: true, data: files }
|
|
||||||
})
|
|
||||||
|
|
||||||
this.mainCtx.handle(
|
|
||||||
'fs:fileExists',
|
|
||||||
async (_event, relativePath: string, folder: 'automatic' | 'script') => {
|
|
||||||
await this.initPromise
|
|
||||||
const exists = await this.fileExists(relativePath, folder)
|
|
||||||
return { success: true, data: exists }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,309 +1,251 @@
|
|||||||
.queryBuilder {
|
.ruleGroup {
|
||||||
--querybuilder-font-family:
|
background-color: var(--ss-card-bg, #fff);
|
||||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans',
|
border: 1px solid var(--ss-border-color, #d9d9d9);
|
||||||
sans-serif;
|
border-radius: 8px;
|
||||||
--querybuilder-background: var(--ss-card-bg, #ffffff);
|
padding: 16px;
|
||||||
--querybuilder-color: var(--ss-text-main, rgba(0, 0, 0, 0.85));
|
margin-bottom: 12px;
|
||||||
--querybuilder-border-radius: 6px;
|
|
||||||
--querybuilder-border-color: #d9d9d9;
|
|
||||||
--querybuilder-disabled-color: rgba(0, 0, 0, 0.25);
|
|
||||||
font-family: var(--querybuilder-font-family);
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.5715;
|
|
||||||
color: var(--querybuilder-color);
|
|
||||||
background: var(--querybuilder-background);
|
|
||||||
padding: 0;
|
|
||||||
border-radius: var(--querybuilder-border-radius);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryBuilder .ruleGroup {
|
.ruleGroup-header {
|
||||||
background: var(--querybuilder-background);
|
|
||||||
border: 1px solid var(--querybuilder-border-color);
|
|
||||||
border-radius: var(--querybuilder-border-radius);
|
|
||||||
padding: 8px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .ruleGroup-header {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruleGroup-combinators,
|
||||||
|
.ruleGroup-addRule,
|
||||||
|
.ruleGroup-addGroup {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruleGroup-body {
|
||||||
|
padding-left: 12px;
|
||||||
|
border-left: 2px solid var(--ss-primary-color, #1677ff);
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule {
|
||||||
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: var(--ss-item-bg, rgba(0, 0, 0, 0.02));
|
||||||
|
border-radius: 6px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .ruleGroup-body {
|
|
||||||
padding-left: 20px;
|
|
||||||
border-left: 2px solid var(--querybuilder-border-color);
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .rule {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 4px 8px;
|
|
||||||
margin: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.02);
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryBuilder .rule:hover {
|
.rule:hover {
|
||||||
background: rgba(0, 0, 0, 0.04);
|
background-color: var(--ss-item-hover, rgba(0, 0, 0, 0.04));
|
||||||
border-color: var(--querybuilder-border-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryBuilder select,
|
.rule-fields,
|
||||||
.queryBuilder .ruleGroup-combinators select {
|
.rule-operators,
|
||||||
appearance: none;
|
.rule-value,
|
||||||
background: var(--querybuilder-background);
|
.rule-subfields {
|
||||||
border: 1px solid var(--querybuilder-border-color);
|
|
||||||
border-radius: var(--querybuilder-border-radius);
|
|
||||||
padding: 4px 28px 4px 11px;
|
|
||||||
height: 32px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--querybuilder-color);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='rgba(0,0,0,0.25)' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: right 8px center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder select:hover,
|
|
||||||
.queryBuilder .ruleGroup-combinators select:hover {
|
|
||||||
border-color: #40a9ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder select:focus,
|
|
||||||
.queryBuilder .ruleGroup-combinators select:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #40a9ff;
|
|
||||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder input[type='text'],
|
|
||||||
.queryBuilder input[type='number'],
|
|
||||||
.queryBuilder input:not([type]) {
|
|
||||||
background: var(--querybuilder-background);
|
|
||||||
border: 1px solid var(--querybuilder-border-color);
|
|
||||||
border-radius: var(--querybuilder-border-radius);
|
|
||||||
padding: 4px 11px;
|
|
||||||
height: 32px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--querybuilder-color);
|
|
||||||
transition: all 0.3s;
|
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryBuilder input[type='text']:hover,
|
.rule-fields .ant-select-selector,
|
||||||
.queryBuilder input[type='number']:hover,
|
.rule-operators .ant-select-selector,
|
||||||
.queryBuilder input:not([type]):hover {
|
.rule-subfields .ant-select-selector {
|
||||||
border-color: #40a9ff;
|
background-color: var(--ss-input-bg, #fff) !important;
|
||||||
|
border-color: var(--ss-border-color, #d9d9d9) !important;
|
||||||
|
color: var(--ss-text-main, rgba(0, 0, 0, 0.88)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryBuilder input[type='text']:focus,
|
.rule-fields .ant-select-selector:hover,
|
||||||
.queryBuilder input[type='number']:focus,
|
.rule-operators .ant-select-selector:hover,
|
||||||
.queryBuilder input:not([type]):focus {
|
.rule-subfields .ant-select-selector:hover {
|
||||||
outline: none;
|
border-color: var(--ss-primary-color, #1677ff) !important;
|
||||||
border-color: #40a9ff;
|
|
||||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryBuilder input::placeholder {
|
.rule-fields .ant-select-focused .ant-select-selector,
|
||||||
color: #bfbfbf;
|
.rule-operators .ant-select-focused .ant-select-selector,
|
||||||
|
.rule-subfields .ant-select-focused .ant-select-selector {
|
||||||
|
border-color: var(--ss-primary-color, #1677ff) !important;
|
||||||
|
box-shadow: 0 0 0 2px rgba(var(--ss-primary-rgb, 22, 119, 255), 0.1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryBuilder button {
|
.rule-value .ant-input,
|
||||||
display: inline-flex;
|
.rule-value .ant-input-number,
|
||||||
|
.rule-value .ant-select-selector {
|
||||||
|
background-color: var(--ss-input-bg, #fff) !important;
|
||||||
|
border-color: var(--ss-border-color, #d9d9d9) !important;
|
||||||
|
color: var(--ss-text-main, rgba(0, 0, 0, 0.88)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-value .ant-input:hover,
|
||||||
|
.rule-value .ant-input-number:hover,
|
||||||
|
.rule-value .ant-select-selector:hover {
|
||||||
|
border-color: var(--ss-primary-color, #1677ff) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-value .ant-input:focus,
|
||||||
|
.rule-value .ant-input-number-focused,
|
||||||
|
.rule-value .ant-select-focused .ant-select-selector {
|
||||||
|
border-color: var(--ss-primary-color, #1677ff) !important;
|
||||||
|
box-shadow: 0 0 0 2px rgba(var(--ss-primary-rgb, 22, 119, 255), 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-value .ant-input-number-input {
|
||||||
|
color: var(--ss-text-main, rgba(0, 0, 0, 0.88)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-remove,
|
||||||
|
.ruleGroup-remove {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: var(--ant-color-error, #ff4d4f) !important;
|
||||||
|
border-color: var(--ant-color-error, #ff4d4f) !important;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-remove:hover,
|
||||||
|
.ruleGroup-remove:hover {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: #ff7875 !important;
|
||||||
|
border-color: #ff7875 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruleGroup-addRule,
|
||||||
|
.ruleGroup-addGroup {
|
||||||
|
color: var(--ss-primary-color, #1677ff) !important;
|
||||||
|
border-color: var(--ss-primary-color, #1677ff) !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruleGroup-addRule:hover,
|
||||||
|
.ruleGroup-addGroup:hover {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: var(--ss-primary-color, #1677ff) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.betweenRules {
|
||||||
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-weight: 400;
|
margin: 8px 0;
|
||||||
white-space: nowrap;
|
color: var(--ss-text-secondary, rgba(0, 0, 0, 0.45));
|
||||||
text-align: center;
|
|
||||||
background-image: none;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
height: 32px;
|
|
||||||
padding: 4px 15px;
|
|
||||||
font-size: 14px;
|
|
||||||
border-radius: var(--querybuilder-border-radius);
|
|
||||||
color: var(--querybuilder-color);
|
|
||||||
background: var(--querybuilder-background);
|
|
||||||
border-color: var(--querybuilder-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder button:hover {
|
|
||||||
color: #40a9ff;
|
|
||||||
border-color: #40a9ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder button:focus {
|
|
||||||
outline: none;
|
|
||||||
color: #40a9ff;
|
|
||||||
border-color: #40a9ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder button.ruleGroup-addRule,
|
|
||||||
.queryBuilder button.ruleGroup-addGroup {
|
|
||||||
color: #fff;
|
|
||||||
background: var(--ant-color-primary, #1677ff);
|
|
||||||
border-color: var(--ant-color-primary, #1677ff);
|
|
||||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
|
|
||||||
box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder button.ruleGroup-addRule:hover,
|
|
||||||
.queryBuilder button.ruleGroup-addGroup:hover {
|
|
||||||
color: #fff;
|
|
||||||
background: var(--ant-color-primary-hover, #4096ff);
|
|
||||||
border-color: var(--ant-color-primary-hover, #4096ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder button.ruleGroup-addRule:focus,
|
|
||||||
.queryBuilder button.ruleGroup-addGroup:focus {
|
|
||||||
color: #fff;
|
|
||||||
background: var(--ant-color-primary-hover, #4096ff);
|
|
||||||
border-color: var(--ant-color-primary-hover, #4096ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder button.rule-remove,
|
|
||||||
.queryBuilder button.ruleGroup-remove {
|
|
||||||
color: #ff4d4f;
|
|
||||||
border-color: #ff4d4f;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder button.rule-remove:hover,
|
|
||||||
.queryBuilder button.ruleGroup-remove:hover {
|
|
||||||
color: #ff7875;
|
|
||||||
border-color: #ff7875;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder button.rule-remove:focus,
|
|
||||||
.queryBuilder button.ruleGroup-remove:focus {
|
|
||||||
color: #ff7875;
|
|
||||||
border-color: #ff7875;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .ruleGroup-combinators {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .betweenRules {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .betweenRules .operator-joiner {
|
|
||||||
color: var(--querybuilder-color);
|
|
||||||
padding: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .ruleGroup-notToggle {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
margin-left: 8px;
|
|
||||||
color: var(--querybuilder-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .ruleGroup-notToggle input[type='checkbox'] {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
accent-color: #1890ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .dragHandle {
|
|
||||||
cursor: move;
|
|
||||||
color: var(--querybuilder-disabled-color);
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .dragHandle:hover {
|
|
||||||
color: var(--querybuilder-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .shiftActions {
|
|
||||||
display: inline-flex;
|
|
||||||
gap: 4px;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .shiftActions button {
|
|
||||||
padding: 4px 8px;
|
|
||||||
min-width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .valueEditor-caseSensitive,
|
|
||||||
.queryBuilder .valueEditor-caseSensitive input[type='checkbox'] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .valueEditor-caseSensitive input[type='checkbox'] {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
accent-color: #1890ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .valueSource-dropdown {
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder:disabled,
|
|
||||||
.queryBuilder .disabled {
|
|
||||||
opacity: 0.65;
|
|
||||||
cursor: not-allowed;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .validation-error {
|
|
||||||
color: #ff4d4f;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryBuilder .rule-with-error {
|
.betweenRules::before,
|
||||||
border-color: #ff4d4f !important;
|
.betweenRules::after {
|
||||||
|
content: '';
|
||||||
|
flex: 1;
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--ss-border-color, #d9d9d9);
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryBuilder .rule-with-error input,
|
.betweenRules::before {
|
||||||
.queryBuilder .rule-with-error select {
|
margin-right: 12px;
|
||||||
border-color: #ff4d4f !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryBuilder .rule-with-error input:focus,
|
.betweenRules::after {
|
||||||
.queryBuilder .rule-with-error select:focus {
|
margin-left: 12px;
|
||||||
box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.2) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
.dragHandle {
|
||||||
.queryBuilder {
|
cursor: grab;
|
||||||
--querybuilder-border-color: #434343;
|
color: var(--ss-text-secondary, rgba(0, 0, 0, 0.45));
|
||||||
--querybuilder-disabled-color: rgba(255, 255, 255, 0.3);
|
padding: 4px;
|
||||||
}
|
margin-right: 4px;
|
||||||
|
}
|
||||||
.queryBuilder .rule {
|
|
||||||
background: rgba(255, 255, 255, 0.04);
|
.dragHandle:hover {
|
||||||
}
|
color: var(--ss-text-main, rgba(0, 0, 0, 0.88));
|
||||||
|
}
|
||||||
.queryBuilder .rule:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.08);
|
.queryBuilder .ant-select-dropdown {
|
||||||
}
|
background-color: var(--ss-card-bg, #fff);
|
||||||
|
}
|
||||||
.queryBuilder select {
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='rgba(255,255,255,0.3)' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
|
.queryBuilder .ant-select-item {
|
||||||
}
|
color: var(--ss-text-main, rgba(0, 0, 0, 0.88));
|
||||||
|
}
|
||||||
.queryBuilder input::placeholder {
|
|
||||||
color: rgba(255, 255, 255, 0.3);
|
.queryBuilder .ant-select-item-option-active {
|
||||||
}
|
background-color: var(--ss-item-hover, rgba(0, 0, 0, 0.04));
|
||||||
|
}
|
||||||
|
|
||||||
|
.queryBuilder .ant-select-item-option-selected {
|
||||||
|
background-color: rgba(var(--ss-primary-rgb, 22, 119, 255), 0.1);
|
||||||
|
color: var(--ss-primary-color, #1677ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.queryBuilder .ant-btn {
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queryBuilder .ant-select-selector,
|
||||||
|
.queryBuilder .ant-input,
|
||||||
|
.queryBuilder .ant-input-number {
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .ruleGroup {
|
||||||
|
background-color: var(--ss-card-bg, #1f1f1f);
|
||||||
|
border-color: var(--ss-border-color, #424242);
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .rule {
|
||||||
|
background-color: var(--ss-item-bg, rgba(255, 255, 255, 0.04));
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .rule:hover {
|
||||||
|
background-color: var(--ss-item-hover, rgba(255, 255, 255, 0.08));
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .rule-fields .ant-select-selector,
|
||||||
|
[theme-mode='dark'] .rule-operators .ant-select-selector,
|
||||||
|
[theme-mode='dark'] .rule-subfields .ant-select-selector,
|
||||||
|
[theme-mode='dark'] .rule-value .ant-input,
|
||||||
|
[theme-mode='dark'] .rule-value .ant-input-number,
|
||||||
|
[theme-mode='dark'] .rule-value .ant-select-selector {
|
||||||
|
background-color: var(--ss-input-bg, #141414) !important;
|
||||||
|
border-color: var(--ss-border-color, #424242) !important;
|
||||||
|
color: var(--ss-text-main, rgba(255, 255, 255, 0.85)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .rule-value .ant-input-number-input {
|
||||||
|
color: var(--ss-text-main, rgba(255, 255, 255, 0.85)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .betweenRules {
|
||||||
|
color: var(--ss-text-secondary, rgba(255, 255, 255, 0.45));
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .betweenRules::before,
|
||||||
|
[theme-mode='dark'] .betweenRules::after {
|
||||||
|
background-color: var(--ss-border-color, #424242);
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .dragHandle {
|
||||||
|
color: var(--ss-text-secondary, rgba(255, 255, 255, 0.45));
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .dragHandle:hover {
|
||||||
|
color: var(--ss-text-main, rgba(255, 255, 255, 0.85));
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .queryBuilder .ant-select-dropdown {
|
||||||
|
background-color: var(--ss-card-bg, #1f1f1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .queryBuilder .ant-select-item {
|
||||||
|
color: var(--ss-text-main, rgba(255, 255, 255, 0.85));
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .queryBuilder .ant-select-item-option-active {
|
||||||
|
background-color: var(--ss-item-hover, rgba(255, 255, 255, 0.08));
|
||||||
|
}
|
||||||
|
|
||||||
|
[theme-mode='dark'] .queryBuilder .ant-select-item-option-selected {
|
||||||
|
background-color: rgba(var(--ss-primary-rgb, 22, 119, 255), 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruleGroup-header .ant-select-selector {
|
||||||
|
min-width: 80px;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -6,8 +6,7 @@
|
|||||||
"src/renderer/src/**/*.tsx",
|
"src/renderer/src/**/*.tsx",
|
||||||
"src/preload/*.d.ts",
|
"src/preload/*.d.ts",
|
||||||
"src/preload/types.ts",
|
"src/preload/types.ts",
|
||||||
"src/shared/**/*",
|
"src/shared/**/*"
|
||||||
"src/main/services/HttpService.ts"
|
|
||||||
],
|
],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true,
|
"composite": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user