perl:代码优化 (#139)

This commit is contained in:
dayou 2024-05-20 17:40:30 +08:00 committed by GitHub
parent 990126f976
commit c714f13905
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 195 additions and 242 deletions

7
web/components.d.ts vendored
View File

@ -12,12 +12,10 @@ declare module 'vue' {
ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog'] ElDialog: typeof import('element-plus/es')['ElDialog']
ElForm: typeof import('element-plus/es')['ElForm'] ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElIconCheck: typeof import('@element-plus/icons-vue')['Check'] ElIconCheck: typeof import('@element-plus/icons-vue')['Check']
ElIconLoading: typeof import('@element-plus/icons-vue')['Loading'] ElIconLoading: typeof import('@element-plus/icons-vue')['Loading']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']
@ -28,6 +26,7 @@ declare module 'vue' {
ElRadio: typeof import('element-plus/es')['ElRadio'] ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect'] ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider'] ElSlider: typeof import('element-plus/es')['ElSlider']
ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElSwitch: typeof import('element-plus/es')['ElSwitch']
@ -38,9 +37,6 @@ declare module 'vue' {
ElTag: typeof import('element-plus/es')['ElTag'] ElTag: typeof import('element-plus/es')['ElTag']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker'] ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] 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'] IEpBottom: typeof import('~icons/ep/bottom')['default']
IEpCheck: typeof import('~icons/ep/check')['default'] IEpCheck: typeof import('~icons/ep/check')['default']
IEpCirclePlus: typeof import('~icons/ep/circle-plus')['default'] IEpCirclePlus: typeof import('~icons/ep/circle-plus')['default']
@ -64,6 +60,5 @@ declare module 'vue' {
} }
export interface ComponentCustomProperties { export interface ComponentCustomProperties {
vLoading: typeof import('element-plus/es')['ElLoadingDirective'] vLoading: typeof import('element-plus/es')['ElLoadingDirective']
vPopover: typeof import('element-plus/es')['ElPopoverDirective']
} }
} }

View File

@ -1,8 +1,28 @@
export type BasicOperator = 'in' | 'eq' | 'neq' | 'nin' | 'gt'; /**
// in包含, 选择了,任一 * in,
// eq: 等于,选择了,全部 * eq: 等于
// nin: 不包含,不选择,任一 * nin: 不包含
// neq不等于不选择全部可以实现“填写了” * 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[]; export type FieldTypes = string | string[];
// 定义事实对象类型 // 定义事实对象类型

View File

@ -1,26 +1,26 @@
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import * as yup from 'yup' 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)}` return `${prefix}-${nanoid(5)}`
} }
// 定义条件规则类 // 定义条件规则类
export class ConditionNode { export class ConditionNode {
id: string = ''; id: string = '';
public field: string = ''; public field: string = '';
public operator: BasicOperator = 'in'; public operator: Operator = Operator.Include;
public value: FieldTypes = [] public value: FieldTypes = []
constructor(field: string = '', operator: BasicOperator = 'in', value: FieldTypes = []) { constructor(field: string = '', operator: Operator = Operator.Include, value: FieldTypes = []) {
this.field = field; this.field = field;
this.operator = operator; this.operator = operator;
this.value = value; this.value = value;
this.id = generateID('c') this.id = generateID(PrefixID.Condition)
} }
setField(field: string) { setField(field: string) {
this.field = field; this.field = field;
} }
setOperator(operator: BasicOperator) { setOperator(operator: Operator) {
this.operator = operator; this.operator = operator;
} }
setValue(value: FieldTypes) { setValue(value: FieldTypes) {
@ -31,10 +31,10 @@ export class ConditionNode {
export class RuleNode { export class RuleNode {
id: string = ''; id: string = '';
conditions: ConditionNode[] = [] conditions: ConditionNode[] = []
scope: string = 'question' scope: string = Scope.Question
target: string = '' target: string = ''
constructor(scope:string = 'question', target: string = '') { constructor(scope:string = Scope.Question, target: string = '') {
this.id = generateID('r') this.id = generateID(PrefixID.Rule)
this.scope = scope this.scope = scope
this.target = target this.target = target
} }
@ -54,8 +54,14 @@ export class RuleNode {
export class RuleBuild { export class RuleBuild {
rules: RuleNode[] = []; rules: RuleNode[] = [];
static instance: RuleBuild;
constructor() { constructor() {
this.rules = []; this.rules = [];
if (!RuleBuild.instance) {
RuleBuild.instance = this;
}
return RuleBuild.instance;
} }
// 添加条件规则到规则引擎中 // 添加条件规则到规则引擎中

View File

@ -1,8 +1,8 @@
import { type BasicOperator, type FieldTypes, type Fact } from "./BasicType"; import { Operator, type FieldTypes, type Fact } from "./BasicType";
// 定义条件规则类 // 定义条件规则类
export class ConditionNode<F extends string, O extends BasicOperator> { export class ConditionNode<F extends string, O extends Operator> {
// 默认显示 // 默认显示
public result: boolean = false; public result: boolean = false;
constructor(public field: F, public operator: O, public value: FieldTypes) { constructor(public field: F, public operator: O, public value: FieldTypes) {
@ -22,7 +22,7 @@ export class ConditionNode<F extends string, O extends BasicOperator> {
return this.result return this.result
} }
switch (this.operator) { switch (this.operator) {
case 'eq': case Operator.Equal:
if(this.value instanceof Array) { if(this.value instanceof Array) {
this.result = this.value.every(v => facts[this.field].includes(v)) this.result = this.value.every(v => facts[this.field].includes(v))
return this.result return this.result
@ -30,7 +30,7 @@ export class ConditionNode<F extends string, O extends BasicOperator> {
this.result = facts[this.field].includes(this.value); this.result = facts[this.field].includes(this.value);
return this.result return this.result
} }
case 'in': case Operator.Include:
if(this.value instanceof Array) { if(this.value instanceof Array) {
this.result = this.value.some(v => facts[this.field].includes(v)) this.result = this.value.some(v => facts[this.field].includes(v))
return this.result return this.result
@ -38,7 +38,7 @@ export class ConditionNode<F extends string, O extends BasicOperator> {
this.result = facts[this.field].includes(this.value); this.result = facts[this.field].includes(this.value);
return this.result return this.result
} }
case 'nin': case Operator.NotInclude:
if(this.value instanceof Array) { if(this.value instanceof Array) {
this.result = this.value.some(v => !facts[this.field].includes(v)) this.result = this.value.some(v => !facts[this.field].includes(v))
return this.result return this.result
@ -46,7 +46,7 @@ export class ConditionNode<F extends string, O extends BasicOperator> {
this.result = facts[this.field].includes(this.value); this.result = facts[this.field].includes(this.value);
return this.result return this.result
} }
case 'neq': case Operator.NotEqual:
if(this.value instanceof Array) { if(this.value instanceof Array) {
this.result = this.value.every(v => !facts[this.field].includes(v)) this.result = this.value.every(v => !facts[this.field].includes(v))
return this.result return this.result
@ -67,13 +67,13 @@ export class ConditionNode<F extends string, O extends BasicOperator> {
} }
export class RuleNode { export class RuleNode {
conditions: Map<string, ConditionNode<string, BasicOperator>>; // 使用哈希表存储条件规则对象 conditions: Map<string, ConditionNode<string, Operator>>; // 使用哈希表存储条件规则对象
public result: boolean = false; public result: boolean = false;
constructor(public target: string, public scope: string) { constructor(public target: string, public scope: string) {
this.conditions = new Map(); this.conditions = new Map();
} }
// 添加条件规则到规则引擎中 // 添加条件规则到规则引擎中
addCondition(condition: ConditionNode<string, BasicOperator>) { addCondition(condition: ConditionNode<string, Operator>) {
const hash = condition.calculateHash(); const hash = condition.calculateHash();
this.conditions.set(hash, condition); this.conditions.set(hash, condition);
} }
@ -118,8 +118,16 @@ export class RuleNode {
export class RuleMatch { export class RuleMatch {
rules: Map<string, RuleNode>; rules: Map<string, RuleNode>;
constructor(ruleConf: any) { static instance: any;
constructor() {
this.rules = new Map(); this.rules = new Map();
if (!RuleMatch.instance) {
RuleMatch.instance = this;
}
return RuleMatch.instance;
}
fromJson(ruleConf:any) {
if(ruleConf instanceof Array) { if(ruleConf instanceof Array) {
ruleConf.forEach((rule: any) => { ruleConf.forEach((rule: any) => {
const ruleNode = new RuleNode(rule.target, rule.scope); const ruleNode = new RuleNode(rule.target, rule.scope);
@ -130,7 +138,6 @@ export class RuleMatch {
this.addRule(ruleNode) this.addRule(ruleNode)
}) })
} }
} }
// 添加条件规则到规则引擎中 // 添加条件规则到规则引擎中
@ -138,7 +145,7 @@ export class RuleMatch {
const hash = rule.calculateHash(); const hash = rule.calculateHash();
if (this.rules.has(hash)) { if (this.rules.has(hash)) {
const existRule: any = this.rules.get(hash); const existRule: any = this.rules.get(hash);
existRule.conditions.forEach((item: ConditionNode<string, BasicOperator>) => { existRule.conditions.forEach((item: ConditionNode<string, Operator>) => {
rule.addCondition(item) rule.addCondition(item)
}) })
} }

View File

@ -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 }
}

View File

@ -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<string>) => {
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 }
}

View File

@ -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)
}

View File

@ -1,13 +1,13 @@
import { computed, unref } from 'vue'; import { computed, unref } from 'vue';
import { useQuestionInfo } from './useQuestionInfo' import { useQuestionInfo } from './useQuestionInfo'
import { flatten } from 'lodash-es' import { flatten } from 'lodash-es'
import store from '@/management/store'
import { cleanRichText } from '@/common/xss' import { cleanRichText } from '@/common/xss'
import { showLogicEngine } from '@/management/hooks/useShowLogicEngine'
// 目标题的显示逻辑提示文案 // 目标题的显示逻辑提示文案
export const useShowLogicInfo = (field: string) => { export const useShowLogicInfo = (field) => {
const hasShowLogic = computed(() => { const hasShowLogic = computed(() => {
const logicEngine = store.state.logic.showLogicEngine const logicEngine = showLogicEngine.value
// 判断该题是否作为了显示逻辑前置题 // 判断该题是否作为了显示逻辑前置题
const isField = logicEngine?.findTargetsByFields(field)?.length > 0 const isField = logicEngine?.findTargetsByFields(field)?.length > 0
// 判断该题是否作为了显示逻辑目标题 // 判断该题是否作为了显示逻辑目标题
@ -15,11 +15,11 @@ export const useShowLogicInfo = (field: string) => {
return isField || isTarget return isField || isTarget
}) })
const getShowLogicText = computed(() => { const getShowLogicText = computed(() => {
const logicEngine = store.state.logic.showLogicEngine const logicEngine = showLogicEngine.value
// 获取目标题的规则 // 获取目标题的规则
const rules = logicEngine?.findConditionByTarget(field) || [] const rules = logicEngine?.findConditionByTarget(field) || []
const conditions = flatten(rules).map((item:any) => { const conditions = flatten(rules).map((item) => {
const { getQuestionTitle, getOptionTitle } = useQuestionInfo(item.field) const { getQuestionTitle, getOptionTitle } = useQuestionInfo(item.field)
return `<span>【 ${cleanRichText(getQuestionTitle.value())}】 选择了 【${getOptionTitle.value(unref(item.value)).join('、')}】</span> <br/>` return `<span>【 ${cleanRichText(getQuestionTitle.value())}】 选择了 【${getOptionTitle.value(unref(item.value)).join('、')}】</span> <br/>`
}) })

View File

@ -115,7 +115,6 @@ const onMoveDown = () => {
isHover.value = false isHover.value = false
} }
const onDelete = async () => { const onDelete = async () => {
// const target = store.state.logic.showLogicEngine.findTargetsByFields(props.moduleConfig.field)
if(unref(hasShowLogic)) { if(unref(hasShowLogic)) {
ElMessageBox.alert('该问题被逻辑依赖,请先删除逻辑依赖', '提示', { ElMessageBox.alert('该问题被逻辑依赖,请先删除逻辑依赖', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',

View File

@ -22,7 +22,7 @@ import LeftMenu from '@/management/components/LeftMenu.vue'
import CommonTemplate from './components/CommonTemplate.vue' import CommonTemplate from './components/CommonTemplate.vue'
import Navbar from './components/ModuleNavbar.vue' import Navbar from './components/ModuleNavbar.vue'
import { initShowLogicEngine } from '@/management/hooks/useShowLogicEngine'
export default { export default {
name: 'questionEditPage', name: 'questionEditPage',
components: { components: {
@ -34,7 +34,8 @@ export default {
this.$store.commit('edit/setSurveyId', this.$route.params.id) this.$store.commit('edit/setSurveyId', this.$route.params.id)
try { try {
await this.$store.dispatch('edit/init') 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) { } catch (error) {
ElMessage.error(error.message) ElMessage.error(error.message)
// //

View File

@ -5,14 +5,12 @@
</template> </template>
<script> <script>
import { get as _get } from 'lodash-es'
import { mapState } from 'vuex' import { mapState } from 'vuex'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss' import 'element-plus/theme-chalk/src/message.scss'
import { get as _get } from 'lodash-es'
import { publishSurvey, saveSurvey } from '@/management/api/survey' import { publishSurvey, saveSurvey } from '@/management/api/survey'
import { showLogicEngine } from '@/management/hooks/useShowLogicEngine'
import buildData from './buildData' import buildData from './buildData'
export default { export default {
@ -68,15 +66,15 @@ export default {
} }
}, },
updateLogicConf() { updateLogicConf() {
if(this.$store.state.logic.showLogicEngine) { if(showLogicEngine.value) {
try { try {
this.$store.state.logic.showLogicEngine.validateSchema() showLogicEngine.value.validateSchema()
} catch (error) { } catch (error) {
throw error throw error
return return
} }
const showLogicConf = this.$store.state.logic.showLogicEngine.toJson() const showLogicConf = showLogicEngine.value.toJson()
// //
this.$store.dispatch('edit/changeSchema', { key: 'logicConf', value: { showLogicConf } }) this.$store.dispatch('edit/changeSchema', { key: 'logicConf', value: { showLogicConf } })
} }

View File

@ -23,6 +23,7 @@ import 'element-plus/theme-chalk/src/message.scss'
import { saveSurvey } from '@/management/api/survey' import { saveSurvey } from '@/management/api/survey'
import buildData from './buildData' import buildData from './buildData'
import { showLogicEngine } from '@/management/hooks/useShowLogicEngine'
export default { export default {
components: {}, components: {},
@ -89,14 +90,14 @@ export default {
} }
}, },
updateLogicConf() { updateLogicConf() {
if(this.$store.state.logic.showLogicEngine) { if(showLogicEngine.value) {
try { try {
this.$store.state.logic.showLogicEngine.validateSchema() showLogicEngine.value.validateSchema()
} catch (error) { } catch (error) {
throw error throw error
return return
} }
const showLogicConf = this.$store.state.logic.showLogicEngine.toJson() const showLogicConf = showLogicEngine.value.toJson()
// //
this.$store.dispatch('edit/changeSchema', { key: 'logicConf', value: { showLogicConf } }) this.$store.dispatch('edit/changeSchema', { key: 'logicConf', value: { showLogicConf } })
} }

View File

@ -20,25 +20,22 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { shallowRef, computed } from 'vue' import { shallowRef, computed } from 'vue'
import { useStore } from 'vuex' import { RuleNode, ConditionNode } from '@/common/logicEngine/RuleBuild'
import { showLogicEngine } from '@/management/hooks/useShowLogicEngine'
import RuleNodeView from './components/RuleNodeView.vue' import RuleNodeView from './components/RuleNodeView.vue'
import { RuleNode, ConditionNode } from '@/common/logicEngine/RuleBuild'
const store = useStore()
const list = computed(() => { const list = computed(() => {
return store.state.logic.showLogicEngine?.rules || [] return showLogicEngine.value?.rules || []
}) })
const handleAdd = () => { const handleAdd = () => {
const condition = new ConditionNode() const condition = new ConditionNode()
const ruleNode = new RuleNode() const ruleNode = new RuleNode()
ruleNode.addCondition(condition) ruleNode.addCondition(condition)
store.state.logic.showLogicEngine.addRule(ruleNode) showLogicEngine.value.addRule(ruleNode)
} }
const handleDetele = (id: string) => { const handleDetele = (id: string) => {
store.state.logic.showLogicEngine.removeRule(id) showLogicEngine.value.removeRule(id)
} }
const ruleWrappers = shallowRef([]) const ruleWrappers = shallowRef([])

View File

@ -7,14 +7,14 @@
:inline="true" :inline="true"
:model="ruleNode" :model="ruleNode"
> >
<conditionView <ConditionView
v-for="(conditionNode, index) in ruleNode.conditions" v-for="(conditionNode, index) in ruleNode.conditions"
:key="conditionNode.id" :key="conditionNode.id"
:index="index" :index="index"
:ruleNode="ruleNode" :ruleNode="ruleNode"
:conditionNode="conditionNode" :conditionNode="conditionNode"
@delete="handleDeleteCondition" @delete="handleDeleteCondition"
></conditionView> ></ConditionView>
<div class="target-wrapper"> <div class="target-wrapper">
<div class="line"> <div class="line">
<span class="desc">则显示</span> <span class="desc">则显示</span>
@ -46,18 +46,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, shallowRef, inject, type ComputedRef } from 'vue' import { ref, computed, shallowRef, inject, type ComputedRef } from 'vue'
import { useStore } from 'vuex'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import 'element-plus/theme-chalk/src/message-box.scss' import 'element-plus/theme-chalk/src/message-box.scss'
import { RuleNode } from '@/common/logicEngine/RuleBuild' import { RuleNode } from '@/common/logicEngine/RuleBuild'
import { cleanRichText } from '@/common/xss' import { cleanRichText } from '@/common/xss'
import { showLogicEngine } from '@/management/hooks/useShowLogicEngine'
import ConditionView from './ConditionView.vue'
import conditionView from './ConditionView.vue'
const store = useStore()
const renderData = inject<ComputedRef<Array<any>>>('renderData') || ref([]) const renderData = inject<ComputedRef<Array<any>>>('renderData') || ref([])
const props = defineProps({ const props = defineProps({
@ -107,7 +103,7 @@ const targetQuestionList = computed(() => {
return { return {
label: `${item.showIndex ? item.indexNumber + '.' : ''} ${cleanRichText(item.title)}`, label: `${item.showIndex ? item.indexNumber + '.' : ''} ${cleanRichText(item.title)}`,
value: item.field, value: item.field,
disabled: store.state.logic.showLogicEngine disabled: showLogicEngine.value
.findTargetsByScope('question') .findTargetsByScope('question')
.includes(item.field) .includes(item.field)
} }

View File

@ -1,36 +0,0 @@
<template>
<commonTemplate>
<template #left>
<CatalogPanel></CatalogPanel>
</template>
<template #center>
<PreviewPanel></PreviewPanel>
</template>
<template #right>
<SetterPanel></SetterPanel>
</template>
</commonTemplate>
</template>
<script>
import commonTemplate from '../components/CommonTemplate.vue'
import CatalogPanel from '../modules/questionModule/CatalogPanel.vue'
import PreviewPanel from '../modules/questionModule/PreviewPanel.vue'
import SetterPanel from '../modules/questionModule/SetterPanel.vue'
export default {
name: 'EditPage',
components: {
commonTemplate,
CatalogPanel,
PreviewPanel,
SetterPanel
}
}
</script>
<style lang="scss" scoped>
.navbar {
border-bottom: 1px solid #e7e9eb;
}
</style>

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="logic-wrapper"> <div class="logic-wrapper">
<RuleListView></RuleListView> <RulePanel></RulePanel>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, provide } from 'vue' import { computed, provide } from 'vue'
import RuleListView from '../../modules/logicModule/RulePanel.vue' import RulePanel from '../../modules/logicModule/RulePanel.vue'
import { filterQuestionPreviewData } from '@/management/utils/index' import { filterQuestionPreviewData } from '@/management/utils/index'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'

View File

@ -1,28 +1,28 @@
<template> <template>
<commonTemplate> <CommonTemplate>
<template #left> <template #left>
<catalogPanel /> <CatalogPanel />
</template> </template>
<template #center> <template #center>
<previewPanel /> <PreviewPanel />
</template> </template>
<template #right> <template #right>
<setterPanel /> <SetterPanel />
</template> </template>
</commonTemplate> </CommonTemplate>
</template> </template>
<script> <script>
import commonTemplate from '../../components/CommonTemplate.vue'; import CommonTemplate from '../../components/CommonTemplate.vue';
import catalogPanel from '../../modules/questionModule/CatalogPanel.vue'; import CatalogPanel from '../../modules/questionModule/CatalogPanel.vue';
import previewPanel from '../../modules/questionModule/PreviewPanel.vue'; import PreviewPanel from '../../modules/questionModule/PreviewPanel.vue';
import setterPanel from '../../modules/questionModule/SetterPanel.vue'; import SetterPanel from '../../modules/questionModule/SetterPanel.vue';
export default { export default {
name: 'editInde1111x', name: 'editIndex',
components: { components: {
commonTemplate, CommonTemplate,
catalogPanel, CatalogPanel,
previewPanel, PreviewPanel,
setterPanel, SetterPanel,
}, },
}; };
</script> </script>

View File

@ -5,7 +5,7 @@
</template> </template>
<script> <script>
import SettingPanel from '../modules/settingModule/SettingPanel.vue' import SettingPanel from '../../modules/settingModule/SettingPanel'
export default { export default {
name: 'SettingPage', name: 'SettingPage',
components: { components: {

View File

@ -1,5 +1,5 @@
<template> <template>
<commonTemplate> <CommonTemplate>
<template #left> <template #left>
<CatalogPanel /> <CatalogPanel />
</template> </template>
@ -9,10 +9,10 @@
<template #right> <template #right>
<SetterPanel /> <SetterPanel />
</template> </template>
</commonTemplate> </CommonTemplate>
</template> </template>
<script> <script>
import commonTemplate from '../../components/CommonTemplate.vue' import CommonTemplate from '../../components/CommonTemplate.vue'
import CatalogPanel from '../../modules/settingModule/skin/CatalogPanel.vue' import CatalogPanel from '../../modules/settingModule/skin/CatalogPanel.vue'
import PreviewPanel from '../../modules/settingModule/skin/PreviewPanel.vue' import PreviewPanel from '../../modules/settingModule/skin/PreviewPanel.vue'
import SetterPanel from '../../modules/settingModule/skin/SetterPanel.vue' import SetterPanel from '../../modules/settingModule/skin/SetterPanel.vue'
@ -20,7 +20,7 @@ import SetterPanel from '../../modules/settingModule/skin/SetterPanel.vue'
export default { export default {
name: 'ContentPage', name: 'ContentPage',
components: { components: {
commonTemplate, CommonTemplate,
CatalogPanel, CatalogPanel,
PreviewPanel, PreviewPanel,
SetterPanel SetterPanel

View File

@ -1,5 +1,5 @@
<template> <template>
<commonTemplate> <CommonTemplate>
<template #left> <template #left>
<ResultCatalog /> <ResultCatalog />
</template> </template>
@ -9,11 +9,11 @@
<template #right> <template #right>
<ResultSetter /> <ResultSetter />
</template> </template>
</commonTemplate> </CommonTemplate>
</template> </template>
<script> <script>
import commonTemplate from '../../components/CommonTemplate.vue' import CommonTemplate from '../../components/CommonTemplate.vue'
import ResultCatalog from '../../modules/settingModule/result/CatalogPanel.vue' import ResultCatalog from '../../modules/settingModule/result/CatalogPanel.vue'
import ResultPreview from '../../modules/settingModule/result/PreviewPanel.vue' import ResultPreview from '../../modules/settingModule/result/PreviewPanel.vue'
import ResultSetter from '../../modules/settingModule/result/SetterPanel.vue' import ResultSetter from '../../modules/settingModule/result/SetterPanel.vue'
@ -21,7 +21,7 @@ import ResultSetter from '../../modules/settingModule/result/SetterPanel.vue'
export default { export default {
name: 'ResultPage', name: 'ResultPage',
components: { components: {
commonTemplate, CommonTemplate,
ResultCatalog, ResultCatalog,
ResultPreview, ResultPreview,
ResultSetter ResultSetter

View File

@ -1,5 +1,5 @@
export const type = { export const type = {
normal: '调查问卷', normal: '基础调查',
vote: '投票评选', vote: '投票评选',
nps: 'NPS评分', nps: 'NPS评分',
register: '在线报名' register: '在线报名'

View File

@ -59,7 +59,7 @@ const routes: RouteRecordRaw[] = [
meta: { meta: {
needLogin: true needLogin: true
}, },
component: () => import('../pages/edit/pages/SettingPage.vue') component: () => import('../pages/edit/pages/setting/index.vue')
}, },
{ {
path: 'skin', path: 'skin',
@ -118,7 +118,7 @@ const routes: RouteRecordRaw[] = [
name: 'login', name: 'login',
component: () => import('../pages/login/LoginPage.vue'), component: () => import('../pages/login/LoginPage.vue'),
meta: { meta: {
title: '登' title: '登'
} }
} }
] ]

View File

@ -1,5 +1,4 @@
import { getBannerData } from '@/management/api/skin.js' import { getBannerData } from '@/management/api/skin.js'
import { RuleBuild } from '@/common/logicEngine/RuleBuild'
export default { export default {
async getBannerData({ state, commit }) { async getBannerData({ state, commit }) {
@ -10,9 +9,5 @@ export default {
if (res.code === 200) { if (res.code === 200) {
commit('setBannerList', res.data) commit('setBannerList', res.data)
} }
},
initShowLogic({ commit }, ruleConf) {
const showLogicEngine = new RuleBuild(ruleConf)
commit('setShowLogicEngine', showLogicEngine)
} }
} }

View File

@ -1,7 +1,6 @@
import { createStore } from 'vuex' import { createStore } from 'vuex'
import edit from './edit' import edit from './edit'
import user from './user' import user from './user'
import logic from './logic'
import actions from './actions' import actions from './actions'
import mutations from './mutations' import mutations from './mutations'
@ -14,7 +13,6 @@ export default createStore({
actions, actions,
modules: { modules: {
edit, edit,
user, user
logic
} }
}) })

View File

@ -1,19 +0,0 @@
import { RuleBuild } from '@/common/logicEngine/RuleBuild'
export default {
namespaced: true,
state: {
showLogicEngine: null
},
mutations: {
setShowLogicEngine(state, logicEngine) {
state.showLogicEngine = logicEngine
}
},
actions: {
initShowLogic({ commit }, ruleConf) {
const showLogicEngine = new RuleBuild().fromJson(ruleConf)
commit('setShowLogicEngine', showLogicEngine)
}
}
}

View File

@ -2,7 +2,4 @@ export default {
setBannerList(state, data) { setBannerList(state, data) {
state.bannerList = data state.bannerList = data
}, },
setShowLogicEngine(state, logicEngine) {
state.logicEngine = logicEngine
},
} }

View File

@ -1,4 +1,3 @@
export default { export default {
bannerList: [], bannerList: []
logicEngine: null
} }

View File

@ -49,7 +49,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup>
import { ref, computed, inject } from 'vue' import { ref, computed, inject } from 'vue'
import OptionConfig from '../AdvancedConfig/OptionConfig.vue' import OptionConfig from '../AdvancedConfig/OptionConfig.vue'
import RateConfig from '../AdvancedConfig/RateConfig.vue' import RateConfig from '../AdvancedConfig/RateConfig.vue'
@ -75,7 +75,7 @@ defineProps({
}) })
const emit = defineEmits(['addOther', 'optionChange', 'change']) const emit = defineEmits(['addOther', 'optionChange', 'change'])
const moduleConfig = inject('moduleConfig') as any const moduleConfig = inject('moduleConfig')
const optionConfigVisible = ref(false) const optionConfigVisible = ref(false)
const openOptionConfig = () => { const openOptionConfig = () => {
console.log('open') console.log('open')
@ -85,10 +85,10 @@ const openOptionConfig = () => {
const addOther = () => { const addOther = () => {
emit('addOther') emit('addOther')
} }
const handleOptionChange = (value: any[]) => { const handleOptionChange = (value) => {
emit('optionChange', value) emit('optionChange', value)
} }
const handleChange = (data: any) => { const handleChange = (data) => {
emit('change', data) emit('change', data)
} }
@ -96,7 +96,7 @@ const rateConfigVisible = ref(false)
const openRateConfig = () => { const openRateConfig = () => {
rateConfigVisible.value = true rateConfigVisible.value = true
} }
const onVisibleChange = (val: boolean) => { const onVisibleChange = (val) => {
rateConfigVisible.value = val rateConfigVisible.value = val
} }

View File

@ -17,7 +17,7 @@ import AlertDialog from './components/AlertDialog.vue'
import LogoIcon from './components/LogoIcon.vue' import LogoIcon from './components/LogoIcon.vue'
import { get as _get } from 'lodash-es' import { get as _get } from 'lodash-es'
import { ruleConf } from '@/common/logicEngine/ruleConf' import { initRuleEngine } from '@/render/hooks/useRuleEngine.js'
export default { export default {
name: 'App', name: 'App',
@ -69,7 +69,7 @@ export default {
this.setSkin(skinConf) this.setSkin(skinConf)
this.$store.commit('setSurveyPath', surveyPath) this.$store.commit('setSurveyPath', surveyPath)
this.$store.dispatch('init', questionData) this.$store.dispatch('init', questionData)
this.$store.dispatch('initRuleEngine', logicConf?.showLogicConf); initRuleEngine(logicConf?.showLogicConf)
this.$store.dispatch('getEncryptInfo') this.$store.dispatch('getEncryptInfo')
} else { } else {
throw new Error(res.errmsg) throw new Error(res.errmsg)

View File

@ -16,6 +16,9 @@ import { useShowOthers } from '@/render/hooks/useShowOthers'
import { useShowInput } from '@/render/hooks/useShowInput' import { useShowInput } from '@/render/hooks/useShowInput'
import store from '@/render/store' import store from '@/render/store'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { ruleEngine } from '@/render/hooks/useRuleEngine.js'
import { QUESTION_TYPE } from '@/render/constant/index'
const props = defineProps({ const props = defineProps({
indexNumber: { indexNumber: {
type: [Number, String], type: [Number, String],
@ -38,21 +41,20 @@ const questionConfig = computed(() =>{
const { type, field, options, ...rest } = cloneDeep(moduleConfig) const { type, field, options, ...rest } = cloneDeep(moduleConfig)
// console.log(field,'formValuechange') // console.log(field,'formValuechange')
let alloptions = options let alloptions = options
if(type === 'vote') { if(type === QUESTION_TYPE.VOTE) {
const { options, voteTotal } = useVoteMap(field) const { options, voteTotal } = useVoteMap(field)
const voteOptions = unref(options) const voteOptions = unref(options)
alloptions = alloptions.map((obj, index) => Object.assign(obj, voteOptions[index])) alloptions = alloptions.map((obj, index) => Object.assign(obj, voteOptions[index]))
moduleConfig.voteTotal = unref(voteTotal) moduleConfig.voteTotal = unref(voteTotal)
} }
if(['radio','checkbox'].includes(type) && options.filter(optionItem => optionItem.others).length > 0) { if(QUESTION_TYPE.CHOICES.includes(type) && options.filter(optionItem => optionItem.others).length > 0) {
let { options, othersValue } = useShowOthers(field) let { options, othersValue } = useShowOthers(field)
const othersOptions = unref(options) const othersOptions = unref(options)
alloptions = alloptions.map((obj, index) => Object.assign(obj, othersOptions[index])) alloptions = alloptions.map((obj, index) => Object.assign(obj, othersOptions[index]))
moduleConfig.othersValue = unref(othersValue) moduleConfig.othersValue = unref(othersValue)
} }
if(['radio-star','radio-nps'].includes(type) && Object.keys(rest.rangeConfig).filter(index => rest.rangeConfig[index].isShowInput).length > 0) { if(QUESTION_TYPE.RATES.includes(type) && Object.keys(rest.rangeConfig).filter(index => rest.rangeConfig[index].isShowInput).length > 0) {
let { rangeConfig, othersValue } = useShowInput(field) let { rangeConfig, othersValue } = useShowInput(field)
// console.log({rangeConfig, othersValue})
moduleConfig.rangeConfig = unref(rangeConfig) moduleConfig.rangeConfig = unref(rangeConfig)
moduleConfig.othersValue = unref(othersValue) moduleConfig.othersValue = unref(othersValue)
} }
@ -63,22 +65,21 @@ const questionConfig = computed(() =>{
value: formValues.value[props.moduleConfig.field] value: formValues.value[props.moduleConfig.field]
} }
}) })
const { field } = props.moduleConfig
const visible = computed(() => { const visible = computed(() => {
const { field } = props.moduleConfig // computedmatch
const matchRule = store.state.ruleEngine.rules.get(field+'question') return ruleEngine.match(field, 'question', formValues.value)
if(matchRule) {
return matchRule.result
} else {
return true
}
}) })
watch(() => visible.value, (newVal, oldVal) => { watch(() => visible.value, (newVal, oldVal) => {
// //
const { field, type, innerType } = props.moduleConfig const { field, type, innerType } = props.moduleConfig
if(!newVal && oldVal) { if(!newVal && oldVal) {
let value = '' let value = ''
// innerType // innerType
if (/checkbox/.test(type) || innerType === 'checkbox') { if (type === QUESTION_TYPE.CHECKBOX || innerType === QUESTION_TYPE.CHECKBOX) {
value = value ? [value] : [] value = value ? [value] : []
} }
const data = { const data = {
@ -86,29 +87,14 @@ watch(() => visible.value, (newVal, oldVal) => {
value: value value: value
} }
store.commit('changeFormData', data) store.commit('changeFormData', data)
notifyMatch(field)
} }
}) })
const handleChange = (data) => { const handleChange = (data) => {
const { key } = data
// console.log(key, 'change')
emit('change', data) emit('change', data)
// //
if(props.moduleConfig.type === 'vote') { if(props.moduleConfig.type === QUESTION_TYPE.VOTE) {
store.dispatch('updateVoteData', data) store.dispatch('updateVoteData', data)
} }
//
notifyMatch(key)
}
const notifyMatch = (key) => {
let fact = unref(formValues)
const targets = store.state.ruleEngine.findTargetsByField(key) || []
//
targets.forEach((target) => {
store.state.ruleEngine.match(target, 'question', fact)
})
} }
</script> </script>

View File

@ -0,0 +1,12 @@
export const QUESTION_TYPE = {
VOTE: 'vote',
CHECKBOX: 'checkbox',
CHOICES: [ // 选择类题型分类
'radio',
'checkbox',
],
RATES: [ // 评分题题型分类
'radio-star',
'radio-nps'
]
}

View File

@ -0,0 +1,6 @@
import { RuleMatch } from '@/common/logicEngine/RulesMatch'
export const ruleEngine = new RuleMatch()
export const initRuleEngine = (ruleConf) => {
ruleEngine.fromJson(ruleConf)
}

View File

@ -1,12 +1,12 @@
<template> <template>
<div class="index"> <div class="index">
<progressBar /> <ProgressBar />
<div class="wrapper" ref="box"> <div class="wrapper" ref="box">
<HeaderSetter></HeaderSetter> <HeaderSetter></HeaderSetter>
<div class="content"> <div class="content">
<MainTitle></MainTitle> <MainTitle></MainTitle>
<MainRenderer ref="main"></MainRenderer> <MainRenderer ref="main"></MainRenderer>
<submit :validate="validate" :renderData="renderData" @submit="onSubmit"></submit> <Submit :validate="validate" :renderData="renderData" @submit="onSubmit"></Submit>
<LogoIcon /> <LogoIcon />
</div> </div>
</div> </div>
@ -16,11 +16,11 @@
<script> <script>
import HeaderSetter from '../components/HeaderSetter.vue' import HeaderSetter from '../components/HeaderSetter.vue'
import MainTitle from '../components/MainTitle.vue' import MainTitle from '../components/MainTitle.vue'
import submit from '../components/SubmitSetter.vue' import Submit from '../components/SubmitSetter.vue'
import MainRenderer from '../components/MainRenderer.vue' import MainRenderer from '../components/MainRenderer.vue'
import AlertDialog from '../components/AlertDialog.vue' import AlertDialog from '../components/AlertDialog.vue'
import ConfirmDialog from '../components/ConfirmDialog.vue' import ConfirmDialog from '../components/ConfirmDialog.vue'
import progressBar from '../components/ProgressBar.vue' import ProgressBar from '../components/ProgressBar.vue'
import LogoIcon from '../components/LogoIcon.vue' import LogoIcon from '../components/LogoIcon.vue'
import { submitForm } from '../api/survey' import { submitForm } from '../api/survey'
@ -44,9 +44,9 @@ export default {
components: { components: {
HeaderSetter, HeaderSetter,
MainTitle, MainTitle,
submit, Submit,
MainRenderer, MainRenderer,
progressBar, ProgressBar,
LogoIcon LogoIcon
}, },
computed: { computed: {
@ -93,22 +93,10 @@ export default {
}, },
getSubmitData() { getSubmitData() {
const formValues = cloneDeep(this.$store.state.formValues) const formValues = cloneDeep(this.$store.state.formValues)
// -
// -
const formModel = Object.keys(formValues)
.filter(key => this.$store.state.ruleEngine.getResult(key, 'question'))
.reduce((obj, key) => {
obj[key] = formValues[key];
return obj;
}, {});
const result = { const result = {
surveyPath: this.surveyPath, surveyPath: this.surveyPath,
data: JSON.stringify(formModel), data: JSON.stringify(formValues),
difTime: Date.now() - this.$store.state.enterTime, difTime: Date.now() - this.$store.state.enterTime,
clientTime: Date.now() clientTime: Date.now()
} }