mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 10:24:22 +08:00
更新builder的css
This commit is contained in:
@@ -16,8 +16,7 @@ export {
|
||||
ThemeServiceToken,
|
||||
WindowManagerToken,
|
||||
TrayServiceToken,
|
||||
AutoScoreServiceToken,
|
||||
FileSystemServiceToken
|
||||
AutoScoreServiceToken
|
||||
} from './tokens'
|
||||
export type {
|
||||
appRuntimeContext,
|
||||
|
||||
@@ -15,4 +15,3 @@ export const ThemeServiceToken = Symbol.for('secscore.themeService')
|
||||
export const WindowManagerToken = Symbol.for('secscore.windowManager')
|
||||
export const TrayServiceToken = Symbol.for('secscore.trayService')
|
||||
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 { TrayService } from './services/TrayService'
|
||||
import { AutoScoreService } from './services/AutoScoreService'
|
||||
import { FileSystemService } from './services/FileSystemService'
|
||||
import { DbConnectionService } from './services/DbConnectionService'
|
||||
import { StudentRepository } from './repos/StudentRepository'
|
||||
import { ReasonRepository } from './repos/ReasonRepository'
|
||||
@@ -39,8 +38,7 @@ import {
|
||||
ThemeServiceToken,
|
||||
WindowManagerToken,
|
||||
TrayServiceToken,
|
||||
AutoScoreServiceToken,
|
||||
FileSystemServiceToken
|
||||
AutoScoreServiceToken
|
||||
} from './hosting'
|
||||
|
||||
type mainAppConfig = {
|
||||
@@ -267,10 +265,6 @@ app.whenReady().then(async () => {
|
||||
TrayServiceToken,
|
||||
(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(DbConnectionService, (p) => new DbConnectionService(p.get(MainContext)))
|
||||
})
|
||||
@@ -320,7 +314,6 @@ app.whenReady().then(async () => {
|
||||
const tray = services.get(TrayServiceToken) as TrayService
|
||||
tray.initialize()
|
||||
}
|
||||
services.get(FileSystemServiceToken)
|
||||
const autoScore = services.get(AutoScoreServiceToken) as AutoScoreService
|
||||
await autoScore.initialize?.()
|
||||
services.get(DbConnectionService)
|
||||
|
||||
@@ -11,30 +11,12 @@ interface AutoScoreRule extends RuleConfig {
|
||||
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' {
|
||||
interface Context {
|
||||
autoScore: AutoScoreService
|
||||
}
|
||||
}
|
||||
|
||||
const RULES_FILE_NAME = 'auto-score-rules.json'
|
||||
|
||||
export class AutoScoreService extends Service {
|
||||
private rules: AutoScoreRule[] = []
|
||||
private timers: Map<number, NodeJS.Timeout> = new Map()
|
||||
@@ -130,83 +112,7 @@ export class AutoScoreService extends Service {
|
||||
}
|
||||
|
||||
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()
|
||||
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
|
||||
await this.loadRulesFromSettings()
|
||||
}
|
||||
|
||||
private async loadRulesFromSettings() {
|
||||
@@ -226,32 +132,7 @@ export class AutoScoreService extends Service {
|
||||
}
|
||||
|
||||
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()
|
||||
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()
|
||||
}
|
||||
await this.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 {
|
||||
--querybuilder-font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans',
|
||||
sans-serif;
|
||||
--querybuilder-background: var(--ss-card-bg, #ffffff);
|
||||
--querybuilder-color: var(--ss-text-main, rgba(0, 0, 0, 0.85));
|
||||
--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);
|
||||
.ruleGroup {
|
||||
background-color: var(--ss-card-bg, #fff);
|
||||
border: 1px solid var(--ss-border-color, #d9d9d9);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.queryBuilder .ruleGroup {
|
||||
background: var(--querybuilder-background);
|
||||
border: 1px solid var(--querybuilder-border-color);
|
||||
border-radius: var(--querybuilder-border-radius);
|
||||
padding: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.queryBuilder .ruleGroup-header {
|
||||
.ruleGroup-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
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;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
background-color: var(--ss-item-bg, rgba(0, 0, 0, 0.02));
|
||||
border-radius: 6px;
|
||||
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;
|
||||
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 {
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
border-color: var(--querybuilder-border-color);
|
||||
.rule:hover {
|
||||
background-color: var(--ss-item-hover, rgba(0, 0, 0, 0.04));
|
||||
}
|
||||
|
||||
.queryBuilder select,
|
||||
.queryBuilder .ruleGroup-combinators select {
|
||||
appearance: none;
|
||||
background: var(--querybuilder-background);
|
||||
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;
|
||||
.rule-fields,
|
||||
.rule-operators,
|
||||
.rule-value,
|
||||
.rule-subfields {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.queryBuilder input[type='text']:hover,
|
||||
.queryBuilder input[type='number']:hover,
|
||||
.queryBuilder input:not([type]):hover {
|
||||
border-color: #40a9ff;
|
||||
.rule-fields .ant-select-selector,
|
||||
.rule-operators .ant-select-selector,
|
||||
.rule-subfields .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;
|
||||
}
|
||||
|
||||
.queryBuilder input[type='text']:focus,
|
||||
.queryBuilder input[type='number']:focus,
|
||||
.queryBuilder input:not([type]):focus {
|
||||
outline: none;
|
||||
border-color: #40a9ff;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
.rule-fields .ant-select-selector:hover,
|
||||
.rule-operators .ant-select-selector:hover,
|
||||
.rule-subfields .ant-select-selector:hover {
|
||||
border-color: var(--ss-primary-color, #1677ff) !important;
|
||||
}
|
||||
|
||||
.queryBuilder input::placeholder {
|
||||
color: #bfbfbf;
|
||||
.rule-fields .ant-select-focused .ant-select-selector,
|
||||
.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 {
|
||||
display: inline-flex;
|
||||
.rule-value .ant-input,
|
||||
.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;
|
||||
justify-content: center;
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
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;
|
||||
margin: 8px 0;
|
||||
color: var(--ss-text-secondary, rgba(0, 0, 0, 0.45));
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.queryBuilder .rule-with-error {
|
||||
border-color: #ff4d4f !important;
|
||||
.betweenRules::before,
|
||||
.betweenRules::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background-color: var(--ss-border-color, #d9d9d9);
|
||||
}
|
||||
|
||||
.queryBuilder .rule-with-error input,
|
||||
.queryBuilder .rule-with-error select {
|
||||
border-color: #ff4d4f !important;
|
||||
.betweenRules::before {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.queryBuilder .rule-with-error input:focus,
|
||||
.queryBuilder .rule-with-error select:focus {
|
||||
box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.2) !important;
|
||||
.betweenRules::after {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.queryBuilder {
|
||||
--querybuilder-border-color: #434343;
|
||||
--querybuilder-disabled-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.queryBuilder .rule {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.queryBuilder .rule:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.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 input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
.dragHandle {
|
||||
cursor: grab;
|
||||
color: var(--ss-text-secondary, rgba(0, 0, 0, 0.45));
|
||||
padding: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.dragHandle:hover {
|
||||
color: var(--ss-text-main, rgba(0, 0, 0, 0.88));
|
||||
}
|
||||
|
||||
.queryBuilder .ant-select-dropdown {
|
||||
background-color: var(--ss-card-bg, #fff);
|
||||
}
|
||||
|
||||
.queryBuilder .ant-select-item {
|
||||
color: var(--ss-text-main, rgba(0, 0, 0, 0.88));
|
||||
}
|
||||
|
||||
.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/preload/*.d.ts",
|
||||
"src/preload/types.ts",
|
||||
"src/shared/**/*",
|
||||
"src/main/services/HttpService.ts"
|
||||
"src/shared/**/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
|
||||
Reference in New Issue
Block a user