mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-21 18:19:03 +08:00
未完成:自动加分
This commit is contained in:
@@ -1,20 +1,16 @@
|
|||||||
import { Space, Input, InputNumber, Select, Button } from 'antd'
|
import { Space, Input, InputNumber, Select, Button } from 'antd'
|
||||||
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'
|
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import type { AutoScoreAction } from './ruleBuilderUtils'
|
import type { ActionItem } from './types'
|
||||||
|
import { ACTION_DEFINITIONS, SCORE_RANGE } from './constants'
|
||||||
|
|
||||||
interface ActionComponentProps {
|
interface ActionComponentProps {
|
||||||
actions: AutoScoreAction[]
|
actions: ActionItem[]
|
||||||
onAdd: () => void
|
onAdd: () => void
|
||||||
onRemove: (index: number) => void
|
onRemove: (id: number) => void
|
||||||
onChange: (index: number, field: keyof AutoScoreAction, value: any) => void
|
onChange: (id: number, field: keyof ActionItem, value: string | number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ACTION_DEFINITIONS = [
|
|
||||||
{ eventName: 'add_score', labelKey: 'autoScore.actionAddScore' },
|
|
||||||
{ eventName: 'add_tag', labelKey: 'autoScore.actionAddTag' }
|
|
||||||
]
|
|
||||||
|
|
||||||
export const ActionComponent: React.FC<ActionComponentProps> = ({
|
export const ActionComponent: React.FC<ActionComponentProps> = ({
|
||||||
actions,
|
actions,
|
||||||
onAdd,
|
onAdd,
|
||||||
@@ -23,16 +19,16 @@ export const ActionComponent: React.FC<ActionComponentProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const handleActionChange = (index: number, field: keyof AutoScoreAction, value: any) => {
|
const handleActionChange = (id: number, field: keyof ActionItem, value: string | number) => {
|
||||||
onChange(index, field, value)
|
onChange(id, field, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Space orientation="vertical" style={{ width: '100%' }}>
|
<Space direction="vertical" style={{ width: '100%' }}>
|
||||||
{actions.map((action, index) => (
|
{actions.map((action) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={action.id}
|
||||||
style={{
|
style={{
|
||||||
padding: '12px',
|
padding: '12px',
|
||||||
border: '1px solid #d9d9d9',
|
border: '1px solid #d9d9d9',
|
||||||
@@ -42,8 +38,8 @@ export const ActionComponent: React.FC<ActionComponentProps> = ({
|
|||||||
>
|
>
|
||||||
<Space style={{ width: '100%' }} wrap>
|
<Space style={{ width: '100%' }} wrap>
|
||||||
<Select
|
<Select
|
||||||
value={action.event}
|
value={action.eventName}
|
||||||
onChange={(value) => handleActionChange(index, 'event', value)}
|
onChange={(value) => handleActionChange(action.id, 'eventName', value)}
|
||||||
style={{ width: 150 }}
|
style={{ width: 150 }}
|
||||||
options={ACTION_DEFINITIONS.map((d) => ({
|
options={ACTION_DEFINITIONS.map((d) => ({
|
||||||
label: t(d.labelKey),
|
label: t(d.labelKey),
|
||||||
@@ -51,30 +47,30 @@ export const ActionComponent: React.FC<ActionComponentProps> = ({
|
|||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{action.event === 'add_score' && (
|
{action.eventName === 'add_score' && (
|
||||||
<InputNumber
|
<InputNumber
|
||||||
value={action.value ? parseInt(action.value) : 0}
|
value={action.value ? parseInt(action.value) : undefined}
|
||||||
onChange={(value) => handleActionChange(index, 'value', String(value || 0))}
|
onChange={(value) => handleActionChange(action.id, 'value', String(value || 0))}
|
||||||
placeholder={t('autoScore.scoreLabel')}
|
placeholder={t('autoScore.scorePlaceholder')}
|
||||||
min={-100}
|
min={SCORE_RANGE.MIN}
|
||||||
max={100}
|
max={SCORE_RANGE.MAX}
|
||||||
style={{ width: 120 }}
|
style={{ width: 120 }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{action.event === 'add_tag' && (
|
{action.eventName === 'add_tag' && (
|
||||||
<Input
|
<Input
|
||||||
value={action.value}
|
value={action.value}
|
||||||
onChange={(e) => handleActionChange(index, 'value', e.target.value)}
|
onChange={(e) => handleActionChange(action.id, 'value', e.target.value)}
|
||||||
placeholder={t('autoScore.tagNameLabel')}
|
placeholder={t('autoScore.tagNamePlaceholder')}
|
||||||
style={{ width: 200 }}
|
style={{ width: 200 }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
value={action.reason}
|
value={action.reason}
|
||||||
onChange={(e) => handleActionChange(index, 'reason', e.target.value)}
|
onChange={(e) => handleActionChange(action.id, 'reason', e.target.value)}
|
||||||
placeholder={t('autoScore.operationNoteLabel')}
|
placeholder={t('autoScore.reasonPlaceholder')}
|
||||||
style={{ flex: 1, minWidth: 200 }}
|
style={{ flex: 1, minWidth: 200 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -82,14 +78,14 @@ export const ActionComponent: React.FC<ActionComponentProps> = ({
|
|||||||
type="text"
|
type="text"
|
||||||
danger
|
danger
|
||||||
icon={<DeleteOutlined />}
|
icon={<DeleteOutlined />}
|
||||||
onClick={() => onRemove(index)}
|
onClick={() => onRemove(action.id)}
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<Button type="dashed" icon={<PlusOutlined />} onClick={onAdd} block>
|
<Button type="dashed" icon={<PlusOutlined />} onClick={onAdd} block>
|
||||||
{t('autoScore.addOperation')}
|
{t('autoScore.addAction')}
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import type { RuleGroupType, Field, Operator } from 'react-querybuilder'
|
import type { RuleGroupType, Field, RuleType } from 'react-querybuilder'
|
||||||
import {defaultOperators} from 'react-querybuilder'
|
import {defaultOperators} from 'react-querybuilder'
|
||||||
import { fetchAllTags } from '../TagEditorDialog'
|
import { fetchAllTags } from '../TagEditorDialog'
|
||||||
|
|
||||||
import type { FullField, RuleType } from 'react-querybuilder';
|
|
||||||
import { toFullOption } from 'react-querybuilder';
|
|
||||||
const tags = await fetchAllTags()
|
const tags = await fetchAllTags()
|
||||||
|
|
||||||
export interface AutoScoreTrigger {
|
export interface AutoScoreTrigger {
|
||||||
@@ -18,68 +16,35 @@ export interface AutoScoreAction {
|
|||||||
reason?: string
|
reason?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const musicalInstruments = fetchAllTags().then((tags) => tags.map((tag) => tag.name))
|
|
||||||
|
|
||||||
export interface AutoScoreRuleData {
|
export interface AutoScoreRuleData {
|
||||||
triggers: AutoScoreTrigger[]
|
triggers: AutoScoreTrigger[]
|
||||||
actions: AutoScoreAction[]
|
actions: AutoScoreAction[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to get fields with i18n support
|
export const validator = (r: RuleType) => !!r.value;
|
||||||
|
|
||||||
export const getFields = (t: (key: string) => string): Field[] => [
|
export const getFields = (t: (key: string) => string): Field[] => [
|
||||||
{
|
|
||||||
name: 'interval_time_passed',
|
|
||||||
label: t('autoScore.triggerIntervalTime'),
|
|
||||||
placeholder: t('autoScore.intervalMinutesPlaceholder'),
|
|
||||||
inputType: 'date',
|
|
||||||
datatype: 'timestamp with time zone',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'student_has_tag',
|
name: 'student_has_tag',
|
||||||
label: t('autoScore.triggerStudentTag'),
|
label: t('triggers.studentTag.label'),
|
||||||
placeholder: t('autoScore.tagNamesPlaceholder'),
|
placeholder: t('triggers.studentTag.placeholder'),
|
||||||
valueEditorType: 'multiselect',
|
valueEditorType: 'multiselect',
|
||||||
values: tags.map((tag) => tag.name),
|
values: tags.map((tag) => tag.name),
|
||||||
defaultValue: tags.length > 0 ? [tags[0].name] : [],
|
defaultValue: tags.length > 0 ? [tags[0].name] : [],
|
||||||
operators: defaultOperators.filter((op) => op.name === '='),
|
operators: defaultOperators.filter((op) => op.name === 'in'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'tourStops',
|
name: 'interval_time_passed',
|
||||||
label: 'Tour stops',
|
label: t('triggers.intervalTime.label'),
|
||||||
|
matchModes: ['all'],
|
||||||
matchModes: true,
|
|
||||||
subproperties: [
|
subproperties: [
|
||||||
{ name: 'date', label: 'Date', inputType: 'date', datatype: 'date' },
|
{ name: 'month', label: t('triggers.intervalTime.monthLabel'), inputType: 'number', datatype: 'month', operators: ['='] },
|
||||||
{ name: 'time', label: 'Time', inputType: 'time', datatype: 'time' },
|
/* { name: 'week', label: t('triggers.intervalTime.weekLabel'), inputType: 'week', datatype: 'week', operators: ['='] },
|
||||||
|
*/ { name: 'time', label: t('triggers.intervalTime.timeLabel'), inputType: 'time', datatype: 'time', operators: ['='] },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
/* export const operators: Operator[] = [
|
|
||||||
{ name: '=', label: '=' },
|
|
||||||
{ name: 'contains', label: 'contains' },
|
|
||||||
{ name: 'between', label: 'between' }
|
|
||||||
] */
|
|
||||||
|
|
||||||
export const fields: FullField[] = (
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'interval_time_passed',
|
|
||||||
label: ('autoScore.triggerIntervalTime'),
|
|
||||||
placeholder: ('autoScore.intervalMinutesPlaceholder')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'student_has_tag',
|
|
||||||
label: ('autoScore.triggerStudentTag'),
|
|
||||||
placeholder: ('autoScore.tagNamesPlaceholder'),
|
|
||||||
valueEditorType: 'multiselect',
|
|
||||||
values: tags.map((tag) => tag.name),
|
|
||||||
defaultValue: 'more_cowbell',
|
|
||||||
operators: defaultOperators.filter((op) => op.name === 'in'),
|
|
||||||
}
|
|
||||||
] satisfies Field[]
|
|
||||||
).map((o) => toFullOption(o));
|
|
||||||
|
|
||||||
export const defaultQuery: RuleGroupType = {
|
export const defaultQuery: RuleGroupType = {
|
||||||
combinator: 'and',
|
combinator: 'and',
|
||||||
rules: [{ field: 'interval_time_passed', operator: '=', value: '1440' }]
|
rules: [{ field: 'interval_time_passed', operator: '=', value: '1440' }]
|
||||||
@@ -88,7 +53,7 @@ export const defaultQuery: RuleGroupType = {
|
|||||||
export function queryToAutoScoreRule(query: RuleGroupType): AutoScoreRuleData {
|
export function queryToAutoScoreRule(query: RuleGroupType): AutoScoreRuleData {
|
||||||
const triggers: AutoScoreTrigger[] = []
|
const triggers: AutoScoreTrigger[] = []
|
||||||
|
|
||||||
const processRuleGroup = (group: RuleGroupType, relation: 'AND' | 'OR' = 'AND') => {
|
/* const processRuleGroup = (group: RuleGroupType, relation: 'AND' | 'OR' = 'AND') => {
|
||||||
group.rules.forEach((rule, index) => {
|
group.rules.forEach((rule, index) => {
|
||||||
if ('rules' in rule) {
|
if ('rules' in rule) {
|
||||||
processRuleGroup(rule, group.combinator === 'and' ? 'AND' : 'OR')
|
processRuleGroup(rule, group.combinator === 'and' ? 'AND' : 'OR')
|
||||||
@@ -105,9 +70,9 @@ export function queryToAutoScoreRule(query: RuleGroupType): AutoScoreRuleData {
|
|||||||
triggers.push(trigger)
|
triggers.push(trigger)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
} */
|
||||||
|
|
||||||
processRuleGroup(query)
|
/* processRuleGroup(query) */
|
||||||
|
|
||||||
return {
|
return {
|
||||||
triggers,
|
triggers,
|
||||||
@@ -138,27 +103,3 @@ export function autoScoreRuleToQuery(ruleData: AutoScoreRuleData): RuleGroupType
|
|||||||
rules: rules.length > 0 ? rules : defaultQuery.rules
|
rules: rules.length > 0 ? rules : defaultQuery.rules
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* import type { Field, FullField, RuleType } from 'react-querybuilder';
|
|
||||||
import { defaultOperators, toFullOption } from 'react-querybuilder';
|
|
||||||
import { fetchAllTags } from '../TagEditorDialog'
|
|
||||||
const tags = await fetchAllTags()
|
|
||||||
|
|
||||||
export const fields: FullField[] = (
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'interval_time_passed',
|
|
||||||
label: ('autoScore.triggerIntervalTime'),
|
|
||||||
placeholder: ('autoScore.intervalMinutesPlaceholder')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'student_has_tag',
|
|
||||||
label: ('autoScore.triggerStudentTag'),
|
|
||||||
placeholder: ('autoScore.tagNamesPlaceholder'),
|
|
||||||
valueEditorType: 'multiselect',
|
|
||||||
values: tags.map((tag) => tag.name),
|
|
||||||
defaultValue: 'more_cowbell',
|
|
||||||
operators: defaultOperators.filter((op) => op.name === 'in'),
|
|
||||||
}
|
|
||||||
] satisfies Field[]
|
|
||||||
).map((o) => toFullOption(o)); */
|
|
||||||
@@ -1,24 +1,21 @@
|
|||||||
import { formatQuery, QueryBuilder, RuleGroupType } from 'react-querybuilder'
|
import { formatQuery, QueryBuilder, RuleGroupType } from 'react-querybuilder'
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { QueryBuilderDnD } from '@react-querybuilder/dnd';
|
import { QueryBuilderDnD } from '@react-querybuilder/dnd'
|
||||||
import * as ReactDnD from 'react-dnd';
|
import * as ReactDnD from 'react-dnd'
|
||||||
import { QueryBuilderAntD } from '@react-querybuilder/antd';
|
import { QueryBuilderAntD } from '@react-querybuilder/antd'
|
||||||
import * as ReactDndHtml5Backend from 'react-dnd-html5-backend';
|
import * as ReactDndHtml5Backend from 'react-dnd-html5-backend'
|
||||||
import * as ReactDndTouchBackend from 'react-dnd-touch-backend';
|
import * as ReactDndTouchBackend from 'react-dnd-touch-backend'
|
||||||
import {
|
import {
|
||||||
getFields,
|
getFields,
|
||||||
/* operators, */
|
|
||||||
defaultQuery,
|
defaultQuery,
|
||||||
queryToAutoScoreRule,
|
|
||||||
autoScoreRuleToQuery,
|
autoScoreRuleToQuery,
|
||||||
type AutoScoreRuleData,
|
queryToAutoScoreRule,
|
||||||
AutoScoreAction
|
type AutoScoreRuleData
|
||||||
} from './ruleBuilderUtils'
|
} from './ruleBuilderUtils'
|
||||||
import { Card } from 'antd'
|
import { Card } from 'antd'
|
||||||
|
|
||||||
import './ruleBuilderOverride.css'
|
import './ruleBuilderOverride.css'
|
||||||
import { ActionComponent } from './actionComponent'
|
|
||||||
|
|
||||||
interface RuleComponentProps {
|
interface RuleComponentProps {
|
||||||
initialData?: AutoScoreRuleData
|
initialData?: AutoScoreRuleData
|
||||||
@@ -30,59 +27,36 @@ export const RuleComponent: React.FC<RuleComponentProps> = ({ initialData, onCha
|
|||||||
const [query, setQuery] = useState<RuleGroupType>(
|
const [query, setQuery] = useState<RuleGroupType>(
|
||||||
initialData ? autoScoreRuleToQuery(initialData) : defaultQuery
|
initialData ? autoScoreRuleToQuery(initialData) : defaultQuery
|
||||||
)
|
)
|
||||||
/* const [actions, setActions] = useState<AutoScoreRuleData['actions']>(initialData?.actions || [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (onChange) {
|
if (initialData) {
|
||||||
const ruleData = queryToAutoScoreRule(query)
|
setQuery(autoScoreRuleToQuery(initialData))
|
||||||
onChange({
|
|
||||||
...ruleData,
|
|
||||||
actions
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}, [query, actions])
|
}, [initialData])
|
||||||
|
|
||||||
const handleAddAction = () => {
|
|
||||||
setActions([...actions, { event: 'add_score', value: '5', reason: '' }])
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleRemoveAction = (index: number) => {
|
|
||||||
setActions(actions.filter((_, i) => i !== index))
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleActionChange = (index: number, field: keyof AutoScoreAction, value: any) => {
|
|
||||||
const newActions = [...actions]
|
|
||||||
newActions[index] = { ...newActions[index], [field]: value }
|
|
||||||
setActions(newActions)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const handleQueryChange = (newQuery: RuleGroupType) => {
|
const handleQueryChange = (newQuery: RuleGroupType) => {
|
||||||
setQuery(newQuery)
|
setQuery(newQuery)
|
||||||
|
if (onChange) {
|
||||||
|
const ruleData = queryToAutoScoreRule(newQuery)
|
||||||
|
onChange(ruleData)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Card title={t('autoScore.triggerCondition')} style={{ marginBottom: '24px', backgroundColor: 'var(--ss-card-bg)' }}>
|
<Card
|
||||||
|
title={t('autoScore.triggerCondition')}
|
||||||
|
style={{ marginBottom: '24px', backgroundColor: 'var(--ss-card-bg)' }}
|
||||||
|
>
|
||||||
<QueryBuilderDnD dnd={{ ...ReactDnD, ...ReactDndHtml5Backend, ...ReactDndTouchBackend }}>
|
<QueryBuilderDnD dnd={{ ...ReactDnD, ...ReactDndHtml5Backend, ...ReactDndTouchBackend }}>
|
||||||
<QueryBuilderAntD>
|
<QueryBuilderAntD>
|
||||||
<QueryBuilder
|
<QueryBuilder fields={getFields(t)} query={query} onQueryChange={handleQueryChange} />
|
||||||
fields={getFields(t)}
|
|
||||||
/* operators={operators} */
|
|
||||||
query={query}
|
|
||||||
/* onQueryChange={handleQueryChange} */
|
|
||||||
onQueryChange={handleQueryChange} />
|
|
||||||
</QueryBuilderAntD>
|
</QueryBuilderAntD>
|
||||||
</QueryBuilderDnD>
|
</QueryBuilderDnD>
|
||||||
<div style={{ marginTop: '8px' }}>
|
<div style={{ marginTop: '8px' }}>
|
||||||
<pre style={{ fontSize: '12px', color: '#999' }}>{ formatQuery(query, 'json') }</pre>
|
<pre style={{ fontSize: '12px', color: '#999' }}>{formatQuery(query, 'json')}</pre>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
{/* <Card title={t('autoScore.executeAction')} style={{ marginBottom: '24px', backgroundColor: 'var(--ss-card-bg)' }}>
|
|
||||||
<ActionComponent
|
|
||||||
actions={actions}
|
|
||||||
onAdd={handleAddAction}
|
|
||||||
onRemove={handleRemoveAction}
|
|
||||||
onChange={handleActionChange} />
|
|
||||||
</Card> */}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -513,7 +513,9 @@
|
|||||||
"tagsPlaceholder": "Enter tag name",
|
"tagsPlaceholder": "Enter tag name",
|
||||||
"scorePlaceholder": "Enter score",
|
"scorePlaceholder": "Enter score",
|
||||||
"tagNamePlaceholder": "Enter tag name",
|
"tagNamePlaceholder": "Enter tag name",
|
||||||
"reasonPlaceholder": "Enter reason"
|
"reasonPlaceholder": "Enter reason",
|
||||||
|
"relationAnd": "AND",
|
||||||
|
"relationOr": "OR"
|
||||||
},
|
},
|
||||||
"triggers": {
|
"triggers": {
|
||||||
"studentTag": {
|
"studentTag": {
|
||||||
|
|||||||
@@ -502,8 +502,6 @@
|
|||||||
"scoreLabel": "分数",
|
"scoreLabel": "分数",
|
||||||
"tagNameLabel": "标签名称",
|
"tagNameLabel": "标签名称",
|
||||||
"operationNoteLabel": "操作说明(可选)",
|
"operationNoteLabel": "操作说明(可选)",
|
||||||
"intervalMinutesPlaceholder": "例如:1440",
|
|
||||||
"tagNamesPlaceholder": "例如:优秀学生,班干部",
|
|
||||||
"triggerIntervalTime": "间隔时间",
|
"triggerIntervalTime": "间隔时间",
|
||||||
"triggerStudentTag": "学生标签",
|
"triggerStudentTag": "学生标签",
|
||||||
"actionAddScore": "加分",
|
"actionAddScore": "加分",
|
||||||
@@ -513,11 +511,13 @@
|
|||||||
"tagsPlaceholder": "请输入标签名称",
|
"tagsPlaceholder": "请输入标签名称",
|
||||||
"scorePlaceholder": "请输入分数",
|
"scorePlaceholder": "请输入分数",
|
||||||
"tagNamePlaceholder": "请输入标签名称",
|
"tagNamePlaceholder": "请输入标签名称",
|
||||||
"reasonPlaceholder": "请输入理由"
|
"reasonPlaceholder": "请输入理由",
|
||||||
|
"relationAnd": "并且",
|
||||||
|
"relationOr": "或者"
|
||||||
},
|
},
|
||||||
"triggers": {
|
"triggers": {
|
||||||
"studentTag": {
|
"studentTag": {
|
||||||
"label": "当学生匹配标签时触发",
|
"label": "学生标签",
|
||||||
"description": "当学生匹配特定标签时触发自动化",
|
"description": "当学生匹配特定标签时触发自动化",
|
||||||
"placeholder": "请选择标签",
|
"placeholder": "请选择标签",
|
||||||
"required": "请输入标签名称"
|
"required": "请输入标签名称"
|
||||||
@@ -525,21 +525,16 @@
|
|||||||
"randomTime": {
|
"randomTime": {
|
||||||
"label": "随机时间触发",
|
"label": "随机时间触发",
|
||||||
"description": "在指定时间范围内随机触发自动化",
|
"description": "在指定时间范围内随机触发自动化",
|
||||||
"from": "从",
|
|
||||||
"to": "到",
|
|
||||||
"hour": "时",
|
|
||||||
"minHourPlaceholder": "最小小时",
|
|
||||||
"maxHourPlaceholder": "最大小时",
|
|
||||||
"required": "请输入有效的时间范围(分钟)"
|
"required": "请输入有效的时间范围(分钟)"
|
||||||
},
|
},
|
||||||
"intervalTime": {
|
"intervalTime": {
|
||||||
"label": "根据间隔时间触发",
|
"label": "间隔时间",
|
||||||
"description": "当间隔时间到达时触发自动化",
|
"description": "当间隔时间到达时触发自动化",
|
||||||
"days": "天",
|
"placeholder": "请输入时间间隔",
|
||||||
"minutes": "分钟",
|
"required": "请输入有效的时间",
|
||||||
"placeholderMinutes": "请输入时间间隔(分钟)",
|
"monthLabel": "月",
|
||||||
"placeholderDays": "请输入时间间隔(天)",
|
"weekLabel": "周",
|
||||||
"required": "请输入有效的时间间隔(分钟)"
|
"timeLabel": "时间"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
|
|||||||
Reference in New Issue
Block a user