From c714f139055eb503e90c95b8283306af6c315699 Mon Sep 17 00:00:00 2001 From: dayou <853094838@qq.com> Date: Mon, 20 May 2024 17:40:30 +0800 Subject: [PATCH] =?UTF-8?q?perl:=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96=20(#1?= =?UTF-8?q?39)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/components.d.ts | 7 +-- web/src/common/logicEngine/BasicType.ts | 30 ++++++++++--- web/src/common/logicEngine/RuleBuild.ts | 24 ++++++---- web/src/common/logicEngine/RulesMatch.ts | 29 +++++++----- web/src/management/hooks/useQuestionInfo.js | 23 ++++++++++ web/src/management/hooks/useQuestionInfo.ts | 23 ---------- .../management/hooks/useShowLogicEngine.js | 7 +++ ...seShowLogicInfo.ts => useShowLogicInfo.js} | 10 ++--- .../pages/edit/components/QuestionWrapper.vue | 1 - web/src/management/pages/edit/index.vue | 5 ++- .../modules/contentModule/PublishPanel.vue | 12 +++-- .../edit/modules/contentModule/SavePanel.vue | 7 +-- .../edit/modules/logicModule/RulePanel.vue | 13 +++--- .../logicModule/components/ConditionView.vue | 2 +- .../logicModule/components/RuleNodeView.vue | 16 +++---- .../management/pages/edit/pages/EditPage.vue | 36 --------------- .../pages/edit/pages/edit/LogicEditPage.vue | 4 +- .../edit/pages/edit/QuestionEditPage.vue | 28 ++++++------ .../{SettingPage.vue => setting/index.vue} | 2 +- .../pages/edit/pages/skin/ContentPage.vue | 8 ++-- .../pages/edit/pages/skin/ResultPage.vue | 8 ++-- web/src/management/pages/list/config/index.js | 2 +- web/src/management/router/index.ts | 4 +- web/src/management/store/actions.js | 5 --- web/src/management/store/index.js | 4 +- web/src/management/store/logic/index.js | 19 -------- web/src/management/store/mutations.js | 3 -- web/src/management/store/state.js | 3 +- .../EditOptions/Options/OptionEditBar.vue | 10 ++--- web/src/render/App.vue | 4 +- web/src/render/components/QuestionWrapper.vue | 44 +++++++------------ web/src/render/constant/index.js | 12 +++++ web/src/render/hooks/useRuleEngine.js | 6 +++ web/src/render/pages/IndexPage.vue | 26 +++-------- 34 files changed, 195 insertions(+), 242 deletions(-) create mode 100644 web/src/management/hooks/useQuestionInfo.js delete mode 100644 web/src/management/hooks/useQuestionInfo.ts create mode 100644 web/src/management/hooks/useShowLogicEngine.js rename web/src/management/hooks/{useShowLogicInfo.ts => useShowLogicInfo.js} (80%) delete mode 100644 web/src/management/pages/edit/pages/EditPage.vue rename web/src/management/pages/edit/pages/{SettingPage.vue => setting/index.vue} (80%) delete mode 100644 web/src/management/store/logic/index.js create mode 100644 web/src/render/constant/index.js create mode 100644 web/src/render/hooks/useRuleEngine.js diff --git a/web/components.d.ts b/web/components.d.ts index a61892f4..bdb9edcc 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -12,12 +12,10 @@ declare module 'vue' { 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'] - ElIcon: typeof import('element-plus/es')['ElIcon'] ElIconCheck: typeof import('@element-plus/icons-vue')['Check'] ElIconLoading: typeof import('@element-plus/icons-vue')['Loading'] ElInput: typeof import('element-plus/es')['ElInput'] @@ -28,6 +26,7 @@ declare module 'vue' { 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'] ElSlider: typeof import('element-plus/es')['ElSlider'] ElSwitch: typeof import('element-plus/es')['ElSwitch'] @@ -38,9 +37,6 @@ declare module 'vue' { ElTag: typeof import('element-plus/es')['ElTag'] ElTimePicker: typeof import('element-plus/es')['ElTimePicker'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] - 'IEp-[]': typeof import('~icons/ep/[]')['default'] - 'IEp-[test]': typeof import('~icons/ep/[test]')['default'] - 'IEp-]': typeof import('~icons/ep/]')['default'] IEpBottom: typeof import('~icons/ep/bottom')['default'] IEpCheck: typeof import('~icons/ep/check')['default'] IEpCirclePlus: typeof import('~icons/ep/circle-plus')['default'] @@ -64,6 +60,5 @@ declare module 'vue' { } export interface ComponentCustomProperties { vLoading: typeof import('element-plus/es')['ElLoadingDirective'] - vPopover: typeof import('element-plus/es')['ElPopoverDirective'] } } diff --git a/web/src/common/logicEngine/BasicType.ts b/web/src/common/logicEngine/BasicType.ts index 61d83621..810e1f78 100644 --- a/web/src/common/logicEngine/BasicType.ts +++ b/web/src/common/logicEngine/BasicType.ts @@ -1,8 +1,28 @@ -export type BasicOperator = 'in' | 'eq' | 'neq' | 'nin' | 'gt'; -// in:包含, 选择了,任一 -// eq: 等于,选择了,全部 -// nin: 不包含,不选择,任一 -// neq:不等于,不选择,全部,可以实现“填写了” +/** + * in:包含, 选择了,任一 + * eq: 等于,选择了,全部 + * nin: 不包含,不选择,任一 + * neq:不等于,不选择,全部,可以实现“填写了” + */ +export enum Operator { + Include = 'in', + Equal = 'eq', + NotEqual = 'neq', + NotInclude = 'nin', +} + + +export enum PrefixID { + Rule = 'r', + Condition = 'c' +} + +export enum Scope { + Question = 'question', + Option = 'option' +} + + export type FieldTypes = string | string[]; // 定义事实对象类型 diff --git a/web/src/common/logicEngine/RuleBuild.ts b/web/src/common/logicEngine/RuleBuild.ts index 7b69d52c..744037e0 100644 --- a/web/src/common/logicEngine/RuleBuild.ts +++ b/web/src/common/logicEngine/RuleBuild.ts @@ -1,26 +1,26 @@ import { nanoid } from 'nanoid'; import * as yup from 'yup' -import { type BasicOperator, type FieldTypes } from './BasicType' +import { type FieldTypes, PrefixID, Operator, Scope } from './BasicType' -export function generateID(prefix = 'r') { +export function generateID(prefix = PrefixID.Rule) { return `${prefix}-${nanoid(5)}` } // 定义条件规则类 export class ConditionNode { id: string = ''; public field: string = ''; - public operator: BasicOperator = 'in'; + public operator: Operator = Operator.Include; public value: FieldTypes = [] - constructor(field: string = '', operator: BasicOperator = 'in', value: FieldTypes = []) { + constructor(field: string = '', operator: Operator = Operator.Include, value: FieldTypes = []) { this.field = field; this.operator = operator; this.value = value; - this.id = generateID('c') + this.id = generateID(PrefixID.Condition) } setField(field: string) { this.field = field; } - setOperator(operator: BasicOperator) { + setOperator(operator: Operator) { this.operator = operator; } setValue(value: FieldTypes) { @@ -31,10 +31,10 @@ export class ConditionNode { export class RuleNode { id: string = ''; conditions: ConditionNode[] = [] - scope: string = 'question' + scope: string = Scope.Question target: string = '' - constructor(scope:string = 'question', target: string = '') { - this.id = generateID('r') + constructor(scope:string = Scope.Question, target: string = '') { + this.id = generateID(PrefixID.Rule) this.scope = scope this.target = target } @@ -54,8 +54,14 @@ export class RuleNode { export class RuleBuild { rules: RuleNode[] = []; + static instance: RuleBuild; constructor() { this.rules = []; + if (!RuleBuild.instance) { + RuleBuild.instance = this; + } + + return RuleBuild.instance; } // 添加条件规则到规则引擎中 diff --git a/web/src/common/logicEngine/RulesMatch.ts b/web/src/common/logicEngine/RulesMatch.ts index d8c02343..e2b1592a 100644 --- a/web/src/common/logicEngine/RulesMatch.ts +++ b/web/src/common/logicEngine/RulesMatch.ts @@ -1,8 +1,8 @@ -import { type BasicOperator, type FieldTypes, type Fact } from "./BasicType"; +import { Operator, type FieldTypes, type Fact } from "./BasicType"; // 定义条件规则类 -export class ConditionNode { +export class ConditionNode { // 默认显示 public result: boolean = false; constructor(public field: F, public operator: O, public value: FieldTypes) { @@ -22,7 +22,7 @@ export class ConditionNode { return this.result } switch (this.operator) { - case 'eq': + case Operator.Equal: if(this.value instanceof Array) { this.result = this.value.every(v => facts[this.field].includes(v)) return this.result @@ -30,7 +30,7 @@ export class ConditionNode { this.result = facts[this.field].includes(this.value); return this.result } - case 'in': + case Operator.Include: if(this.value instanceof Array) { this.result = this.value.some(v => facts[this.field].includes(v)) return this.result @@ -38,7 +38,7 @@ export class ConditionNode { this.result = facts[this.field].includes(this.value); return this.result } - case 'nin': + case Operator.NotInclude: if(this.value instanceof Array) { this.result = this.value.some(v => !facts[this.field].includes(v)) return this.result @@ -46,7 +46,7 @@ export class ConditionNode { this.result = facts[this.field].includes(this.value); return this.result } - case 'neq': + case Operator.NotEqual: if(this.value instanceof Array) { this.result = this.value.every(v => !facts[this.field].includes(v)) return this.result @@ -67,13 +67,13 @@ export class ConditionNode { } export class RuleNode { - conditions: Map>; // 使用哈希表存储条件规则对象 + conditions: Map>; // 使用哈希表存储条件规则对象 public result: boolean = false; constructor(public target: string, public scope: string) { this.conditions = new Map(); } // 添加条件规则到规则引擎中 - addCondition(condition: ConditionNode) { + addCondition(condition: ConditionNode) { const hash = condition.calculateHash(); this.conditions.set(hash, condition); } @@ -118,8 +118,16 @@ export class RuleNode { export class RuleMatch { rules: Map; - constructor(ruleConf: any) { + static instance: any; + constructor() { this.rules = new Map(); + if (!RuleMatch.instance) { + RuleMatch.instance = this; + } + + return RuleMatch.instance; + } + fromJson(ruleConf:any) { if(ruleConf instanceof Array) { ruleConf.forEach((rule: any) => { const ruleNode = new RuleNode(rule.target, rule.scope); @@ -130,7 +138,6 @@ export class RuleMatch { this.addRule(ruleNode) }) } - } // 添加条件规则到规则引擎中 @@ -138,7 +145,7 @@ export class RuleMatch { const hash = rule.calculateHash(); if (this.rules.has(hash)) { const existRule: any = this.rules.get(hash); - existRule.conditions.forEach((item: ConditionNode) => { + existRule.conditions.forEach((item: ConditionNode) => { rule.addCondition(item) }) } diff --git a/web/src/management/hooks/useQuestionInfo.js b/web/src/management/hooks/useQuestionInfo.js new file mode 100644 index 00000000..1612493a --- /dev/null +++ b/web/src/management/hooks/useQuestionInfo.js @@ -0,0 +1,23 @@ +import { computed } from 'vue'; +import store from '@/management/store' +import { cleanRichText } from '@/common/xss' +export const useQuestionInfo = (field) => { + const getQuestionTitle = computed(() => { + const questionDataList = store.state.edit.schema.questionDataList + return () => { + return questionDataList.find((item) => item.field === field)?.title + } + }) + const getOptionTitle = computed(() => { + 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 { + return options.filter((item) => item.hash === value).map((item) => cleanRichText(item.text)) + } + } + }) + return { getQuestionTitle, getOptionTitle } +} diff --git a/web/src/management/hooks/useQuestionInfo.ts b/web/src/management/hooks/useQuestionInfo.ts deleted file mode 100644 index f8c722c8..00000000 --- a/web/src/management/hooks/useQuestionInfo.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { computed } from 'vue'; -import store from '@/management/store' -import { cleanRichText } from '@/common/xss' -export const useQuestionInfo = (field: string) => { - const getQuestionTitle = computed(() => { - const questionDataList = store.state.edit.schema.questionDataList - return () => { - return questionDataList.find((item: any) => item.field === field)?.title - } - }) - const getOptionTitle = computed(() => { - const questionDataList = store.state.edit.schema.questionDataList - return (value: string | Array) => { - const options = questionDataList.find((item: any) => item.field === field)?.options || [] - if(value instanceof Array) { - return options.filter((item: any) => value.includes(item.hash)).map((item: any) => cleanRichText(item.text)) - } else { - return options.filter((item: any) => item.hash === value).map((item: any) => cleanRichText(item.text)) - } - } - }) - return { getQuestionTitle, getOptionTitle } -} diff --git a/web/src/management/hooks/useShowLogicEngine.js b/web/src/management/hooks/useShowLogicEngine.js new file mode 100644 index 00000000..50c8d5d7 --- /dev/null +++ b/web/src/management/hooks/useShowLogicEngine.js @@ -0,0 +1,7 @@ +import { ref } from 'vue' +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.ts b/web/src/management/hooks/useShowLogicInfo.js similarity index 80% rename from web/src/management/hooks/useShowLogicInfo.ts rename to web/src/management/hooks/useShowLogicInfo.js index 134ce5f9..15b89bf4 100644 --- a/web/src/management/hooks/useShowLogicInfo.ts +++ b/web/src/management/hooks/useShowLogicInfo.js @@ -1,13 +1,13 @@ import { computed, unref } from 'vue'; import { useQuestionInfo } from './useQuestionInfo' import { flatten } from 'lodash-es' -import store from '@/management/store' import { cleanRichText } from '@/common/xss' +import { showLogicEngine } from '@/management/hooks/useShowLogicEngine' // 目标题的显示逻辑提示文案 -export const useShowLogicInfo = (field: string) => { +export const useShowLogicInfo = (field) => { const hasShowLogic = computed(() => { - const logicEngine = store.state.logic.showLogicEngine + const logicEngine = showLogicEngine.value // 判断该题是否作为了显示逻辑前置题 const isField = logicEngine?.findTargetsByFields(field)?.length > 0 // 判断该题是否作为了显示逻辑目标题 @@ -15,11 +15,11 @@ export const useShowLogicInfo = (field: string) => { return isField || isTarget }) const getShowLogicText = computed(() => { - const logicEngine = store.state.logic.showLogicEngine + const logicEngine = showLogicEngine.value // 获取目标题的规则 const rules = logicEngine?.findConditionByTarget(field) || [] - const conditions = flatten(rules).map((item:any) => { + const conditions = flatten(rules).map((item) => { const { getQuestionTitle, getOptionTitle } = useQuestionInfo(item.field) return `【 ${cleanRichText(getQuestionTitle.value())}】 选择了 【${getOptionTitle.value(unref(item.value)).join('、')}】
` }) diff --git a/web/src/management/pages/edit/components/QuestionWrapper.vue b/web/src/management/pages/edit/components/QuestionWrapper.vue index 71819add..11a42a7d 100644 --- a/web/src/management/pages/edit/components/QuestionWrapper.vue +++ b/web/src/management/pages/edit/components/QuestionWrapper.vue @@ -115,7 +115,6 @@ const onMoveDown = () => { isHover.value = false } const onDelete = async () => { - // const target = store.state.logic.showLogicEngine.findTargetsByFields(props.moduleConfig.field) if(unref(hasShowLogic)) { ElMessageBox.alert('该问题被逻辑依赖,请先删除逻辑依赖', '提示', { confirmButtonText: '确定', diff --git a/web/src/management/pages/edit/index.vue b/web/src/management/pages/edit/index.vue index 665ebee5..e1c4e83d 100644 --- a/web/src/management/pages/edit/index.vue +++ b/web/src/management/pages/edit/index.vue @@ -22,7 +22,7 @@ import LeftMenu from '@/management/components/LeftMenu.vue' import CommonTemplate from './components/CommonTemplate.vue' import Navbar from './components/ModuleNavbar.vue' - +import { initShowLogicEngine } from '@/management/hooks/useShowLogicEngine' export default { name: 'questionEditPage', components: { @@ -34,7 +34,8 @@ export default { this.$store.commit('edit/setSurveyId', this.$route.params.id) try { await this.$store.dispatch('edit/init') - await this.$store.dispatch('logic/initShowLogic', this.$store.state.edit.schema.logicConf.showLogicConf || {}) + // await this.$store.dispatch('logic/initShowLogic', this.$store.state.edit.schema.logicConf.showLogicConf || {}) + await initShowLogicEngine(this.$store.state.edit.schema.logicConf.showLogicConf || {}) } catch (error) { ElMessage.error(error.message) // 自动跳转回列表页 diff --git a/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue b/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue index dc22971b..19e5c934 100644 --- a/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue +++ b/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue @@ -5,14 +5,12 @@ - - diff --git a/web/src/management/pages/edit/pages/edit/LogicEditPage.vue b/web/src/management/pages/edit/pages/edit/LogicEditPage.vue index 9c2fc470..966ebc2c 100644 --- a/web/src/management/pages/edit/pages/edit/LogicEditPage.vue +++ b/web/src/management/pages/edit/pages/edit/LogicEditPage.vue @@ -1,11 +1,11 @@ diff --git a/web/src/management/pages/edit/pages/SettingPage.vue b/web/src/management/pages/edit/pages/setting/index.vue similarity index 80% rename from web/src/management/pages/edit/pages/SettingPage.vue rename to web/src/management/pages/edit/pages/setting/index.vue index eceba5e5..2c0fec56 100644 --- a/web/src/management/pages/edit/pages/SettingPage.vue +++ b/web/src/management/pages/edit/pages/setting/index.vue @@ -5,7 +5,7 @@ - diff --git a/web/src/render/constant/index.js b/web/src/render/constant/index.js new file mode 100644 index 00000000..f50b6d80 --- /dev/null +++ b/web/src/render/constant/index.js @@ -0,0 +1,12 @@ +export const QUESTION_TYPE = { + VOTE: 'vote', + CHECKBOX: 'checkbox', + CHOICES: [ // 选择类题型分类 + 'radio', + 'checkbox', + ], + RATES: [ // 评分题题型分类 + 'radio-star', + 'radio-nps' + ] +} \ No newline at end of file diff --git a/web/src/render/hooks/useRuleEngine.js b/web/src/render/hooks/useRuleEngine.js new file mode 100644 index 00000000..00dcee4f --- /dev/null +++ b/web/src/render/hooks/useRuleEngine.js @@ -0,0 +1,6 @@ +import { RuleMatch } from '@/common/logicEngine/RulesMatch' + +export const ruleEngine = new RuleMatch() +export const initRuleEngine = (ruleConf) => { + ruleEngine.fromJson(ruleConf) +} \ No newline at end of file diff --git a/web/src/render/pages/IndexPage.vue b/web/src/render/pages/IndexPage.vue index 19a15d9a..02450124 100644 --- a/web/src/render/pages/IndexPage.vue +++ b/web/src/render/pages/IndexPage.vue @@ -1,12 +1,12 @@