diff --git a/web/components.d.ts b/web/components.d.ts index a6b11b23..8a826169 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -11,26 +11,30 @@ declare module 'vue' { ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] + ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] + ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] + ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElForm: typeof import('element-plus/es')['ElForm'] ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElInput: typeof import('element-plus/es')['ElInput'] ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] - ElMenu: typeof import('element-plus/es')['ElMenu'] - ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElOption: typeof import('element-plus/es')['ElOption'] ElPagination: typeof import('element-plus/es')['ElPagination'] ElPopover: typeof import('element-plus/es')['ElPopover'] ElRadio: typeof import('element-plus/es')['ElRadio'] ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] + ElRow: typeof import('element-plus/es')['ElRow'] ElSelect: typeof import('element-plus/es')['ElSelect'] - ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] + ElSlider: typeof import('element-plus/es')['ElSlider'] ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabs: typeof import('element-plus/es')['ElTabs'] + ElTag: typeof import('element-plus/es')['ElTag'] + ElTimePicker: typeof import('element-plus/es')['ElTimePicker'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] IEpBottom: typeof import('~icons/ep/bottom')['default'] IEpCheck: typeof import('~icons/ep/check')['default'] diff --git a/web/src/common/logicEngine/BasicType.ts b/web/src/common/logicEngine/BasicType.ts index 810e1f78..c0b62fac 100644 --- a/web/src/common/logicEngine/BasicType.ts +++ b/web/src/common/logicEngine/BasicType.ts @@ -8,25 +8,22 @@ export enum Operator { Include = 'in', Equal = 'eq', NotEqual = 'neq', - NotInclude = 'nin', + NotInclude = 'nin' } - export enum PrefixID { Rule = 'r', Condition = 'c' } - + export enum Scope { Question = 'question', Option = 'option' } - -export type FieldTypes = string | string[]; +export type FieldTypes = string | string[] // 定义事实对象类型 export type Fact = { - [key: string]: any; -}; - + [key: string]: any +} diff --git a/web/src/common/logicEngine/RuleBuild.ts b/web/src/common/logicEngine/RuleBuild.ts index b358e7b7..d9b99ac0 100644 --- a/web/src/common/logicEngine/RuleBuild.ts +++ b/web/src/common/logicEngine/RuleBuild.ts @@ -1,4 +1,4 @@ -import { nanoid } from 'nanoid'; +import { nanoid } from 'nanoid' import * as yup from 'yup' import { type FieldTypes, PrefixID, Operator, Scope } from './BasicType' @@ -7,33 +7,33 @@ export function generateID(prefix = PrefixID.Rule) { } // 定义条件规则类 export class ConditionNode { - id: string = ''; - public field: string = ''; - public operator: Operator = Operator.Include; + id: string = '' + public field: string = '' + public operator: Operator = Operator.Include public value: FieldTypes = [] constructor(field: string = '', operator: Operator = Operator.Include, value: FieldTypes = []) { - this.field = field; - this.operator = operator; - this.value = value; + this.field = field + this.operator = operator + this.value = value this.id = generateID(PrefixID.Condition) } setField(field: string) { - this.field = field; + this.field = field } setOperator(operator: Operator) { - this.operator = operator; + this.operator = operator } setValue(value: FieldTypes) { - this.value = value; + this.value = value } } export class RuleNode { - id: string = ''; + id: string = '' conditions: ConditionNode[] = [] scope: string = Scope.Question target: string = '' - constructor(scope:string = Scope.Question, target: string = '') { + constructor(scope: string = Scope.Question, target: string = '') { this.id = generateID(PrefixID.Rule) this.scope = scope this.target = target @@ -42,44 +42,44 @@ export class RuleNode { this.target = value } addCondition(condition: ConditionNode) { - this.conditions.push(condition); + this.conditions.push(condition) } removeCondition(id: string) { - this.conditions = this.conditions.filter(v => v.id !== id); + this.conditions = this.conditions.filter((v) => v.id !== id) } findCondition(conditionId: string) { - return this.conditions.find(condition => condition.id === conditionId); + return this.conditions.find((condition) => condition.id === conditionId) } } export class RuleBuild { - rules: RuleNode[] = []; - static instance: RuleBuild; + rules: RuleNode[] = [] + static instance: RuleBuild constructor() { - this.rules = []; + this.rules = [] if (!RuleBuild.instance) { - RuleBuild.instance = this; + RuleBuild.instance = this } - - return RuleBuild.instance; + + return RuleBuild.instance } // 添加条件规则到规则引擎中 addRule(rule: RuleNode) { - this.rules.push(rule); + this.rules.push(rule) } removeRule(ruleId: string) { - this.rules = this.rules.filter(rule => rule.id !== ruleId); + this.rules = this.rules.filter((rule) => rule.id !== ruleId) } findRule(ruleId: string) { - return this.rules.find(rule => rule.id === ruleId); + return this.rules.find((rule) => rule.id === ruleId) } toJson() { - return this.rules.map(rule => { + return this.rules.map((rule) => { return { target: rule.target, scope: rule.scope, - conditions: rule.conditions.map(condition => { + conditions: rule.conditions.map((condition) => { return { field: condition.field, operator: condition.operator, @@ -91,13 +91,13 @@ export class RuleBuild { } fromJson(ruleConf: any) { this.rules = [] - if(ruleConf instanceof Array) { + if (ruleConf instanceof Array) { ruleConf.forEach((rule: any) => { const { scope, target } = rule - const ruleNode = new RuleNode(scope, target); + const ruleNode = new RuleNode(scope, target) rule.conditions.forEach((condition: any) => { const { field, operator, value } = condition - const conditionNode = new ConditionNode(field, operator, value); + const conditionNode = new ConditionNode(field, operator, value) ruleNode.addCondition(conditionNode) }) this.addRule(ruleNode) @@ -109,16 +109,16 @@ export class RuleBuild { return ruleSchema.validateSync(this.toJson()) } // 实现目标选择了下拉框置灰效果 - findTargetsByScope(scope: string){ - return this.rules.filter(rule => rule.scope === scope).map(rule => rule.target) + findTargetsByScope(scope: string) { + return this.rules.filter((rule) => rule.scope === scope).map((rule) => rule.target) } // 实现前置题删除校验 findTargetsByFields(field: string) { const nodes = this.rules.filter((rule: RuleNode) => { - const conditions = rule.conditions.filter((item: any) => { + const conditions = rule.conditions.filter((item: any) => { return item.field === field }) - return conditions.length > 0 + return conditions.length > 0 }) return nodes.map((item: any) => { return item.target @@ -126,11 +126,10 @@ export class RuleBuild { } // 根据目标题获取显示逻辑 findConditionByTarget(target: string) { - return this.rules.filter(rule=> rule.target === target).map(item => item.conditions) + return this.rules.filter((rule) => rule.target === target).map((item) => item.conditions) } } - export const ruleSchema = yup.array().of( yup.object({ target: yup.string().required(), @@ -143,4 +142,4 @@ export const ruleSchema = yup.array().of( }) ) }) -) \ No newline at end of file +) diff --git a/web/src/common/logicEngine/RulesMatch.ts b/web/src/common/logicEngine/RulesMatch.ts index 3b1ce657..fece3f90 100644 --- a/web/src/common/logicEngine/RulesMatch.ts +++ b/web/src/common/logicEngine/RulesMatch.ts @@ -1,64 +1,65 @@ - -import { Operator, type FieldTypes, type Fact } from "./BasicType"; +import { Operator, type FieldTypes, type Fact } from './BasicType' // 定义条件规则类 export class ConditionNode { // 默认显示 - public result: boolean = false; - constructor(public field: F, public operator: O, public value: FieldTypes) { - } + public result: boolean = false + constructor( + public field: F, + public operator: O, + public value: FieldTypes + ) {} // 计算条件规则的哈希值 calculateHash(): string { // 假设哈希值计算方法为简单的字符串拼接或其他哈希算法 - return this.field + this.operator + this.value; + return this.field + this.operator + this.value } - + match(facts: Fact): boolean { // console.log(this.calculateHash()) // 如果该特征在事实对象中不存在,则直接返回false - if(!facts[this.field]) { + if (!facts[this.field]) { this.result = false return this.result } switch (this.operator) { case Operator.Equal: - if(this.value instanceof Array) { - this.result = this.value.every(v => facts[this.field].includes(v)) + if (this.value instanceof Array) { + this.result = this.value.every((v) => facts[this.field].includes(v)) return this.result } else { - this.result = facts[this.field].includes(this.value); + this.result = facts[this.field].includes(this.value) return this.result } case Operator.Include: - if(this.value instanceof Array) { - this.result = this.value.some(v => facts[this.field].includes(v)) + if (this.value instanceof Array) { + this.result = this.value.some((v) => facts[this.field].includes(v)) return this.result } else { - this.result = facts[this.field].includes(this.value); + this.result = facts[this.field].includes(this.value) return this.result } case Operator.NotInclude: - if(this.value instanceof Array) { - this.result = this.value.some(v => !facts[this.field].includes(v)) + if (this.value instanceof Array) { + this.result = this.value.some((v) => !facts[this.field].includes(v)) return this.result } else { - this.result = facts[this.field].includes(this.value); + this.result = facts[this.field].includes(this.value) return this.result } case Operator.NotEqual: - if(this.value instanceof Array) { - this.result = this.value.every(v => !facts[this.field].includes(v)) + if (this.value instanceof Array) { + this.result = this.value.every((v) => !facts[this.field].includes(v)) return this.result } else { - this.result = facts[this.field].includes(this.value); + this.result = facts[this.field].includes(this.value) return this.result } // 其他比较操作符的判断逻辑 default: return this.result } - } getResult() { @@ -67,27 +68,30 @@ export class ConditionNode { } export class RuleNode { - conditions: Map>; // 使用哈希表存储条件规则对象 - public result: boolean = false; - constructor(public target: string, public scope: string) { - this.conditions = new Map(); + conditions: Map> // 使用哈希表存储条件规则对象 + public result: boolean = false + constructor( + public target: string, + public scope: string + ) { + this.conditions = new Map() } // 添加条件规则到规则引擎中 addCondition(condition: ConditionNode) { - const hash = condition.calculateHash(); - this.conditions.set(hash, condition); + const hash = condition.calculateHash() + this.conditions.set(hash, condition) } // 匹配条件规则 match(fact: Fact) { - const res = Array.from(this.conditions.entries()).every(([, value]) => { + const res = Array.from(this.conditions.entries()).every(([, value]) => { const res = value.match(fact) if (res) { - return true; + return true } else { return false } - }); + }) this.result = res return res } @@ -102,7 +106,7 @@ export class RuleNode { // 计算条件规则的哈希值 calculateHash(): string { // 假设哈希值计算方法为简单的字符串拼接或其他哈希算法 - return this.target + this.scope; + return this.target + this.scope } toJson() { return { @@ -111,30 +115,33 @@ export class RuleNode { conditions: Object.fromEntries( Array.from(this.conditions, ([key, value]) => [key, value.getResult()]) ) - }; + } } - } export class RuleMatch { - rules: Map; - static instance: any; + rules: Map + static instance: any constructor() { - this.rules = new Map(); + this.rules = new Map() if (!RuleMatch.instance) { - RuleMatch.instance = this; + RuleMatch.instance = this } - - return RuleMatch.instance; + + return RuleMatch.instance } - fromJson(ruleConf:any) { - if(ruleConf instanceof Array) { + fromJson(ruleConf: any) { + if (ruleConf instanceof Array) { ruleConf.forEach((rule: any) => { - const ruleNode = new RuleNode(rule.target, rule.scope); + const ruleNode = new RuleNode(rule.target, rule.scope) rule.conditions.forEach((condition: any) => { - const conditionNode = new ConditionNode(condition.field, condition.operator, condition.value); - ruleNode.addCondition(conditionNode) - }); + const conditionNode = new ConditionNode( + condition.field, + condition.operator, + condition.value + ) + ruleNode.addCondition(conditionNode) + }) this.addRule(ruleNode) }) } @@ -142,23 +149,22 @@ export class RuleMatch { // 添加条件规则到规则引擎中 addRule(rule: RuleNode) { - const hash = rule.calculateHash(); + const hash = rule.calculateHash() if (this.rules.has(hash)) { - const existRule: any = this.rules.get(hash); + const existRule: any = this.rules.get(hash) existRule.conditions.forEach((item: ConditionNode) => { rule.addCondition(item) }) } - - this.rules.set(hash, rule); - } + this.rules.set(hash, rule) + } // 匹配条件规则 match(target: string, scope: string, fact: Fact) { - const hash = this.calculateHash(target, scope); + const hash = this.calculateHash(target, scope) - const rule = this.rules.get(hash); + const rule = this.rules.get(hash) if (rule) { const result = rule.match(fact) // this.matchCache.set(hash, result); @@ -168,10 +174,10 @@ export class RuleMatch { return true } } - + getResult(target: string, scope: string) { - const hash = this.calculateHash(target, scope); - const rule = this.rules.get(hash); + const hash = this.calculateHash(target, scope) + const rule = this.rules.get(hash) if (rule) { const result = rule.getResult() return result @@ -183,15 +189,17 @@ export class RuleMatch { // 计算哈希值的方法 calculateHash(target: string, scope: string): string { // 假设哈希值计算方法为简单的字符串拼接或其他哈希算法 - return target + scope; + return target + scope } findTargetsByField(field: string) { - const rules = new Map([...this.rules.entries()].filter(([, value]) => { - return [...value.conditions.entries()].filter(([, value]) => { - return value.field === field + const rules = new Map( + [...this.rules.entries()].filter(([, value]) => { + return [...value.conditions.entries()].filter(([, value]) => { + return value.field === field + }) }) - })) - return [...rules.values()].map(obj => obj.target); + ) + return [...rules.values()].map((obj) => obj.target) } toJson() { return Array.from(this.rules.entries()).map(([, value]) => { diff --git a/web/src/management/config/dnd.ts b/web/src/management/config/dnd.ts index 11039a41..e704e2ed 100644 --- a/web/src/management/config/dnd.ts +++ b/web/src/management/config/dnd.ts @@ -1 +1 @@ -export const DND_GROUP = 'question' \ No newline at end of file +export const DND_GROUP = 'question' diff --git a/web/src/management/hooks/useQuestionInfo.js b/web/src/management/hooks/useQuestionInfo.js index 1612493a..f8f66e3f 100644 --- a/web/src/management/hooks/useQuestionInfo.js +++ b/web/src/management/hooks/useQuestionInfo.js @@ -1,4 +1,4 @@ -import { computed } from 'vue'; +import { computed } from 'vue' import store from '@/management/store' import { cleanRichText } from '@/common/xss' export const useQuestionInfo = (field) => { @@ -12,9 +12,11 @@ export const useQuestionInfo = (field) => { const questionDataList = store.state.edit.schema.questionDataList return (value) => { const options = questionDataList.find((item) => item.field === field)?.options || [] - if(value instanceof Array) { - return options.filter((item) => value.includes(item.hash)).map((item) => cleanRichText(item.text)) - } else { + if (value instanceof Array) { + return options + .filter((item) => value.includes(item.hash)) + .map((item) => cleanRichText(item.text)) + } else { return options.filter((item) => item.hash === value).map((item) => cleanRichText(item.text)) } } diff --git a/web/src/management/hooks/useShowLogicEngine.js b/web/src/management/hooks/useShowLogicEngine.js index 50c8d5d7..8a02934a 100644 --- a/web/src/management/hooks/useShowLogicEngine.js +++ b/web/src/management/hooks/useShowLogicEngine.js @@ -4,4 +4,4 @@ import { RuleBuild } from '@/common/logicEngine/RuleBuild' export const showLogicEngine = ref() export const initShowLogicEngine = (ruleConf) => { showLogicEngine.value = new RuleBuild().fromJson(ruleConf) -} \ No newline at end of file +} diff --git a/web/src/management/hooks/useShowLogicInfo.js b/web/src/management/hooks/useShowLogicInfo.js index 15b89bf4..a70bbded 100644 --- a/web/src/management/hooks/useShowLogicInfo.js +++ b/web/src/management/hooks/useShowLogicInfo.js @@ -1,4 +1,4 @@ -import { computed, unref } from 'vue'; +import { computed, unref } from 'vue' import { useQuestionInfo } from './useQuestionInfo' import { flatten } from 'lodash-es' import { cleanRichText } from '@/common/xss' @@ -16,14 +16,16 @@ export const useShowLogicInfo = (field) => { }) const getShowLogicText = computed(() => { const logicEngine = showLogicEngine.value - // 获取目标题的规则 - const rules = logicEngine?.findConditionByTarget(field) || [] - - const conditions = flatten(rules).map((item) => { - const { getQuestionTitle, getOptionTitle } = useQuestionInfo(item.field) + // 获取目标题的规则 + const rules = logicEngine?.findConditionByTarget(field) || [] + + const conditions = flatten(rules).map((item) => { + const { getQuestionTitle, getOptionTitle } = useQuestionInfo(item.field) return `【 ${cleanRichText(getQuestionTitle.value())}】 选择了 【${getOptionTitle.value(unref(item.value)).join('、')}】
` }) - return conditions.length ? conditions.join('') + '  满足以上全部,则显示本题' :'' + return conditions.length + ? conditions.join('') + '  满足以上全部,则显示本题' + : '' }) return { hasShowLogic, getShowLogicText } -} \ No newline at end of file +} diff --git a/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue b/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue index b5e6fe4f..2560b018 100644 --- a/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue +++ b/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue @@ -41,7 +41,7 @@ export default { if (this.isPublishing) { return } - + try { this.isPublishing = true const saveRes = await saveSurvey(saveData) @@ -66,7 +66,7 @@ export default { } }, updateLogicConf() { - if(showLogicEngine.value) { + if (showLogicEngine.value) { showLogicEngine.value.validateSchema() const showLogicConf = showLogicEngine.value.toJson() // 更新逻辑配置 diff --git a/web/src/management/pages/edit/modules/contentModule/SavePanel.vue b/web/src/management/pages/edit/modules/contentModule/SavePanel.vue index da2463b7..3ec009d0 100644 --- a/web/src/management/pages/edit/modules/contentModule/SavePanel.vue +++ b/web/src/management/pages/edit/modules/contentModule/SavePanel.vue @@ -90,7 +90,7 @@ export default { } }, updateLogicConf() { - if(showLogicEngine.value) { + if (showLogicEngine.value) { showLogicEngine.value.validateSchema() const showLogicConf = showLogicEngine.value.toJson() // 更新逻辑配置 @@ -118,7 +118,7 @@ export default { ElMessage.error('请检查逻辑配置是否有误') return } - + try { this.isSaving = true const res = await this.saveData() diff --git a/web/src/management/pages/edit/modules/logicModule/RulePanel.vue b/web/src/management/pages/edit/modules/logicModule/RulePanel.vue index 34b0031c..ddee0c26 100644 --- a/web/src/management/pages/edit/modules/logicModule/RulePanel.vue +++ b/web/src/management/pages/edit/modules/logicModule/RulePanel.vue @@ -1,7 +1,7 @@