perl:代码优化 (#139)
This commit is contained in:
parent
990126f976
commit
c714f13905
7
web/components.d.ts
vendored
7
web/components.d.ts
vendored
@ -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']
|
||||
}
|
||||
}
|
||||
|
@ -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[];
|
||||
|
||||
// 定义事实对象类型
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
// 添加条件规则到规则引擎中
|
||||
|
@ -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;
|
||||
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
|
||||
}
|
||||
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<F extends string, O extends BasicOperator> {
|
||||
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<F extends string, O extends BasicOperator> {
|
||||
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<F extends string, O extends BasicOperator> {
|
||||
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<F extends string, O extends BasicOperator> {
|
||||
}
|
||||
|
||||
export class RuleNode {
|
||||
conditions: Map<string, ConditionNode<string, BasicOperator>>; // 使用哈希表存储条件规则对象
|
||||
conditions: Map<string, ConditionNode<string, Operator>>; // 使用哈希表存储条件规则对象
|
||||
public result: boolean = false;
|
||||
constructor(public target: string, public scope: string) {
|
||||
this.conditions = new Map();
|
||||
}
|
||||
// 添加条件规则到规则引擎中
|
||||
addCondition(condition: ConditionNode<string, BasicOperator>) {
|
||||
addCondition(condition: ConditionNode<string, Operator>) {
|
||||
const hash = condition.calculateHash();
|
||||
this.conditions.set(hash, condition);
|
||||
}
|
||||
@ -118,8 +118,16 @@ export class RuleNode {
|
||||
|
||||
export class RuleMatch {
|
||||
rules: Map<string, RuleNode>;
|
||||
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<string, BasicOperator>) => {
|
||||
existRule.conditions.forEach((item: ConditionNode<string, Operator>) => {
|
||||
rule.addCondition(item)
|
||||
})
|
||||
}
|
||||
|
23
web/src/management/hooks/useQuestionInfo.js
Normal file
23
web/src/management/hooks/useQuestionInfo.js
Normal 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 }
|
||||
}
|
@ -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 }
|
||||
}
|
7
web/src/management/hooks/useShowLogicEngine.js
Normal file
7
web/src/management/hooks/useShowLogicEngine.js
Normal 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)
|
||||
}
|
@ -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 `<span>【 ${cleanRichText(getQuestionTitle.value())}】 选择了 【${getOptionTitle.value(unref(item.value)).join('、')}】</span> <br/>`
|
||||
})
|
@ -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: '确定',
|
||||
|
@ -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)
|
||||
// 自动跳转回列表页
|
||||
|
@ -5,14 +5,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { get as _get } from 'lodash-es'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import { ElMessage } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/src/message.scss'
|
||||
|
||||
import { get as _get } from 'lodash-es'
|
||||
|
||||
import { publishSurvey, saveSurvey } from '@/management/api/survey'
|
||||
import { showLogicEngine } from '@/management/hooks/useShowLogicEngine'
|
||||
import buildData from './buildData'
|
||||
|
||||
export default {
|
||||
@ -68,15 +66,15 @@ export default {
|
||||
}
|
||||
},
|
||||
updateLogicConf() {
|
||||
if(this.$store.state.logic.showLogicEngine) {
|
||||
if(showLogicEngine.value) {
|
||||
try {
|
||||
this.$store.state.logic.showLogicEngine.validateSchema()
|
||||
showLogicEngine.value.validateSchema()
|
||||
|
||||
} catch (error) {
|
||||
throw error
|
||||
return
|
||||
}
|
||||
const showLogicConf = this.$store.state.logic.showLogicEngine.toJson()
|
||||
const showLogicConf = showLogicEngine.value.toJson()
|
||||
// 更新逻辑配置
|
||||
this.$store.dispatch('edit/changeSchema', { key: 'logicConf', value: { showLogicConf } })
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import 'element-plus/theme-chalk/src/message.scss'
|
||||
|
||||
import { saveSurvey } from '@/management/api/survey'
|
||||
import buildData from './buildData'
|
||||
import { showLogicEngine } from '@/management/hooks/useShowLogicEngine'
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
@ -89,14 +90,14 @@ export default {
|
||||
}
|
||||
},
|
||||
updateLogicConf() {
|
||||
if(this.$store.state.logic.showLogicEngine) {
|
||||
if(showLogicEngine.value) {
|
||||
try {
|
||||
this.$store.state.logic.showLogicEngine.validateSchema()
|
||||
showLogicEngine.value.validateSchema()
|
||||
} catch (error) {
|
||||
throw error
|
||||
return
|
||||
}
|
||||
const showLogicConf = this.$store.state.logic.showLogicEngine.toJson()
|
||||
const showLogicConf = showLogicEngine.value.toJson()
|
||||
// 更新逻辑配置
|
||||
this.$store.dispatch('edit/changeSchema', { key: 'logicConf', value: { showLogicConf } })
|
||||
}
|
||||
|
@ -20,25 +20,22 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
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 { RuleNode, ConditionNode } from '@/common/logicEngine/RuleBuild'
|
||||
|
||||
const store = useStore()
|
||||
|
||||
const list = computed(() => {
|
||||
return store.state.logic.showLogicEngine?.rules || []
|
||||
return showLogicEngine.value?.rules || []
|
||||
})
|
||||
|
||||
const handleAdd = () => {
|
||||
const condition = new ConditionNode()
|
||||
const ruleNode = new RuleNode()
|
||||
ruleNode.addCondition(condition)
|
||||
store.state.logic.showLogicEngine.addRule(ruleNode)
|
||||
showLogicEngine.value.addRule(ruleNode)
|
||||
}
|
||||
const handleDetele = (id: string) => {
|
||||
store.state.logic.showLogicEngine.removeRule(id)
|
||||
showLogicEngine.value.removeRule(id)
|
||||
}
|
||||
|
||||
const ruleWrappers = shallowRef([])
|
||||
|
@ -184,4 +184,4 @@ const handleDelete = (id: any) => {
|
||||
.select {
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
@ -7,14 +7,14 @@
|
||||
:inline="true"
|
||||
:model="ruleNode"
|
||||
>
|
||||
<conditionView
|
||||
<ConditionView
|
||||
v-for="(conditionNode, index) in ruleNode.conditions"
|
||||
:key="conditionNode.id"
|
||||
:index="index"
|
||||
:ruleNode="ruleNode"
|
||||
:conditionNode="conditionNode"
|
||||
@delete="handleDeleteCondition"
|
||||
></conditionView>
|
||||
></ConditionView>
|
||||
<div class="target-wrapper">
|
||||
<div class="line">
|
||||
<span class="desc">则显示</span>
|
||||
@ -46,18 +46,14 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, shallowRef, inject, type ComputedRef } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/src/message-box.scss'
|
||||
|
||||
import { RuleNode } from '@/common/logicEngine/RuleBuild'
|
||||
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 props = defineProps({
|
||||
@ -107,7 +103,7 @@ const targetQuestionList = computed(() => {
|
||||
return {
|
||||
label: `${item.showIndex ? item.indexNumber + '.' : ''} ${cleanRichText(item.title)}`,
|
||||
value: item.field,
|
||||
disabled: store.state.logic.showLogicEngine
|
||||
disabled: showLogicEngine.value
|
||||
.findTargetsByScope('question')
|
||||
.includes(item.field)
|
||||
}
|
||||
@ -146,4 +142,4 @@ defineExpose({
|
||||
.select {
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
@ -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>
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="logic-wrapper">
|
||||
<RuleListView></RuleListView>
|
||||
<RulePanel></RulePanel>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
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 { useStore } from 'vuex'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
@ -1,28 +1,28 @@
|
||||
<template>
|
||||
<commonTemplate>
|
||||
<CommonTemplate>
|
||||
<template #left>
|
||||
<catalogPanel />
|
||||
<CatalogPanel />
|
||||
</template>
|
||||
<template #center>
|
||||
<previewPanel />
|
||||
<PreviewPanel />
|
||||
</template>
|
||||
<template #right>
|
||||
<setterPanel />
|
||||
<SetterPanel />
|
||||
</template>
|
||||
</commonTemplate>
|
||||
</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';
|
||||
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: 'editInde1111x',
|
||||
name: 'editIndex',
|
||||
components: {
|
||||
commonTemplate,
|
||||
catalogPanel,
|
||||
previewPanel,
|
||||
setterPanel,
|
||||
CommonTemplate,
|
||||
CatalogPanel,
|
||||
PreviewPanel,
|
||||
SetterPanel,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SettingPanel from '../modules/settingModule/SettingPanel.vue'
|
||||
import SettingPanel from '../../modules/settingModule/SettingPanel'
|
||||
export default {
|
||||
name: 'SettingPage',
|
||||
components: {
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<commonTemplate>
|
||||
<CommonTemplate>
|
||||
<template #left>
|
||||
<CatalogPanel />
|
||||
</template>
|
||||
@ -9,10 +9,10 @@
|
||||
<template #right>
|
||||
<SetterPanel />
|
||||
</template>
|
||||
</commonTemplate>
|
||||
</CommonTemplate>
|
||||
</template>
|
||||
<script>
|
||||
import commonTemplate from '../../components/CommonTemplate.vue'
|
||||
import CommonTemplate from '../../components/CommonTemplate.vue'
|
||||
import CatalogPanel from '../../modules/settingModule/skin/CatalogPanel.vue'
|
||||
import PreviewPanel from '../../modules/settingModule/skin/PreviewPanel.vue'
|
||||
import SetterPanel from '../../modules/settingModule/skin/SetterPanel.vue'
|
||||
@ -20,7 +20,7 @@ import SetterPanel from '../../modules/settingModule/skin/SetterPanel.vue'
|
||||
export default {
|
||||
name: 'ContentPage',
|
||||
components: {
|
||||
commonTemplate,
|
||||
CommonTemplate,
|
||||
CatalogPanel,
|
||||
PreviewPanel,
|
||||
SetterPanel
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<commonTemplate>
|
||||
<CommonTemplate>
|
||||
<template #left>
|
||||
<ResultCatalog />
|
||||
</template>
|
||||
@ -9,11 +9,11 @@
|
||||
<template #right>
|
||||
<ResultSetter />
|
||||
</template>
|
||||
</commonTemplate>
|
||||
</CommonTemplate>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import commonTemplate from '../../components/CommonTemplate.vue'
|
||||
import CommonTemplate from '../../components/CommonTemplate.vue'
|
||||
import ResultCatalog from '../../modules/settingModule/result/CatalogPanel.vue'
|
||||
import ResultPreview from '../../modules/settingModule/result/PreviewPanel.vue'
|
||||
import ResultSetter from '../../modules/settingModule/result/SetterPanel.vue'
|
||||
@ -21,7 +21,7 @@ import ResultSetter from '../../modules/settingModule/result/SetterPanel.vue'
|
||||
export default {
|
||||
name: 'ResultPage',
|
||||
components: {
|
||||
commonTemplate,
|
||||
CommonTemplate,
|
||||
ResultCatalog,
|
||||
ResultPreview,
|
||||
ResultSetter
|
||||
|
@ -1,5 +1,5 @@
|
||||
export const type = {
|
||||
normal: '调查问卷',
|
||||
normal: '基础调查',
|
||||
vote: '投票评选',
|
||||
nps: 'NPS评分',
|
||||
register: '在线报名'
|
||||
|
@ -59,7 +59,7 @@ const routes: RouteRecordRaw[] = [
|
||||
meta: {
|
||||
needLogin: true
|
||||
},
|
||||
component: () => import('../pages/edit/pages/SettingPage.vue')
|
||||
component: () => import('../pages/edit/pages/setting/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'skin',
|
||||
@ -118,7 +118,7 @@ const routes: RouteRecordRaw[] = [
|
||||
name: 'login',
|
||||
component: () => import('../pages/login/LoginPage.vue'),
|
||||
meta: {
|
||||
title: '登陆'
|
||||
title: '登录'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { getBannerData } from '@/management/api/skin.js'
|
||||
import { RuleBuild } from '@/common/logicEngine/RuleBuild'
|
||||
|
||||
export default {
|
||||
async getBannerData({ state, commit }) {
|
||||
@ -10,9 +9,5 @@ export default {
|
||||
if (res.code === 200) {
|
||||
commit('setBannerList', res.data)
|
||||
}
|
||||
},
|
||||
initShowLogic({ commit }, ruleConf) {
|
||||
const showLogicEngine = new RuleBuild(ruleConf)
|
||||
commit('setShowLogicEngine', showLogicEngine)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { createStore } from 'vuex'
|
||||
import edit from './edit'
|
||||
import user from './user'
|
||||
import logic from './logic'
|
||||
|
||||
import actions from './actions'
|
||||
import mutations from './mutations'
|
||||
@ -14,7 +13,6 @@ export default createStore({
|
||||
actions,
|
||||
modules: {
|
||||
edit,
|
||||
user,
|
||||
logic
|
||||
user
|
||||
}
|
||||
})
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,4 @@ export default {
|
||||
setBannerList(state, data) {
|
||||
state.bannerList = data
|
||||
},
|
||||
setShowLogicEngine(state, logicEngine) {
|
||||
state.logicEngine = logicEngine
|
||||
},
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
export default {
|
||||
bannerList: [],
|
||||
logicEngine: null
|
||||
bannerList: []
|
||||
}
|
||||
|
@ -49,7 +49,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
import { ref, computed, inject } from 'vue'
|
||||
import OptionConfig from '../AdvancedConfig/OptionConfig.vue'
|
||||
import RateConfig from '../AdvancedConfig/RateConfig.vue'
|
||||
@ -75,7 +75,7 @@ defineProps({
|
||||
})
|
||||
const emit = defineEmits(['addOther', 'optionChange', 'change'])
|
||||
|
||||
const moduleConfig = inject('moduleConfig') as any
|
||||
const moduleConfig = inject('moduleConfig')
|
||||
const optionConfigVisible = ref(false)
|
||||
const openOptionConfig = () => {
|
||||
console.log('open')
|
||||
@ -85,10 +85,10 @@ const openOptionConfig = () => {
|
||||
const addOther = () => {
|
||||
emit('addOther')
|
||||
}
|
||||
const handleOptionChange = (value: any[]) => {
|
||||
const handleOptionChange = (value) => {
|
||||
emit('optionChange', value)
|
||||
}
|
||||
const handleChange = (data: any) => {
|
||||
const handleChange = (data) => {
|
||||
emit('change', data)
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ const rateConfigVisible = ref(false)
|
||||
const openRateConfig = () => {
|
||||
rateConfigVisible.value = true
|
||||
}
|
||||
const onVisibleChange = (val: boolean) => {
|
||||
const onVisibleChange = (val) => {
|
||||
rateConfigVisible.value = val
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ import AlertDialog from './components/AlertDialog.vue'
|
||||
|
||||
import LogoIcon from './components/LogoIcon.vue'
|
||||
import { get as _get } from 'lodash-es'
|
||||
import { ruleConf } from '@/common/logicEngine/ruleConf'
|
||||
import { initRuleEngine } from '@/render/hooks/useRuleEngine.js'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
@ -69,7 +69,7 @@ export default {
|
||||
this.setSkin(skinConf)
|
||||
this.$store.commit('setSurveyPath', surveyPath)
|
||||
this.$store.dispatch('init', questionData)
|
||||
this.$store.dispatch('initRuleEngine', logicConf?.showLogicConf);
|
||||
initRuleEngine(logicConf?.showLogicConf)
|
||||
this.$store.dispatch('getEncryptInfo')
|
||||
} else {
|
||||
throw new Error(res.errmsg)
|
||||
|
@ -6,7 +6,7 @@
|
||||
:indexNumber="indexNumber"
|
||||
:showTitle="true"
|
||||
@change="handleChange"
|
||||
></QuestionRuleContainer>
|
||||
></QuestionRuleContainer>
|
||||
</template>
|
||||
<script setup>
|
||||
import { unref, computed, watch } from 'vue'
|
||||
@ -16,6 +16,9 @@ import { useShowOthers } from '@/render/hooks/useShowOthers'
|
||||
import { useShowInput } from '@/render/hooks/useShowInput'
|
||||
import store from '@/render/store'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { ruleEngine } from '@/render/hooks/useRuleEngine.js'
|
||||
import { QUESTION_TYPE } from '@/render/constant/index'
|
||||
|
||||
const props = defineProps({
|
||||
indexNumber: {
|
||||
type: [Number, String],
|
||||
@ -38,21 +41,20 @@ const questionConfig = computed(() =>{
|
||||
const { type, field, options, ...rest } = cloneDeep(moduleConfig)
|
||||
// console.log(field,'这里依赖的formValue,所以change时会触发重新计算')
|
||||
let alloptions = options
|
||||
if(type === 'vote') {
|
||||
if(type === QUESTION_TYPE.VOTE) {
|
||||
const { options, voteTotal } = useVoteMap(field)
|
||||
const voteOptions = unref(options)
|
||||
alloptions = alloptions.map((obj, index) => Object.assign(obj, voteOptions[index]))
|
||||
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)
|
||||
const othersOptions = unref(options)
|
||||
alloptions = alloptions.map((obj, index) => Object.assign(obj, othersOptions[index]))
|
||||
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)
|
||||
// console.log({rangeConfig, othersValue})
|
||||
moduleConfig.rangeConfig = unref(rangeConfig)
|
||||
moduleConfig.othersValue = unref(othersValue)
|
||||
}
|
||||
@ -63,22 +65,21 @@ const questionConfig = computed(() =>{
|
||||
value: formValues.value[props.moduleConfig.field]
|
||||
}
|
||||
})
|
||||
|
||||
const { field } = props.moduleConfig
|
||||
|
||||
const visible = computed(() => {
|
||||
const { field } = props.moduleConfig
|
||||
const matchRule = store.state.ruleEngine.rules.get(field+'question')
|
||||
if(matchRule) {
|
||||
return matchRule.result
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
// computed有计算缓存,当match有变化的时候触发重新计算
|
||||
return ruleEngine.match(field, 'question', formValues.value)
|
||||
})
|
||||
|
||||
watch(() => visible.value, (newVal, oldVal) => {
|
||||
// 题目从显示到隐藏,需要清空值
|
||||
const { field, type, innerType } = props.moduleConfig
|
||||
if(!newVal && oldVal) {
|
||||
let value = ''
|
||||
// 题型是多选,或者子题型是多选(innerType是用于投票)
|
||||
if (/checkbox/.test(type) || innerType === 'checkbox') {
|
||||
if (type === QUESTION_TYPE.CHECKBOX || innerType === QUESTION_TYPE.CHECKBOX) {
|
||||
value = value ? [value] : []
|
||||
}
|
||||
const data = {
|
||||
@ -86,29 +87,14 @@ watch(() => visible.value, (newVal, oldVal) => {
|
||||
value: value
|
||||
}
|
||||
store.commit('changeFormData', data)
|
||||
notifyMatch(field)
|
||||
}
|
||||
})
|
||||
|
||||
const handleChange = (data) => {
|
||||
const { key } = data
|
||||
// console.log(key, '触发change事件')
|
||||
emit('change', data)
|
||||
// 处理投票题
|
||||
if(props.moduleConfig.type === 'vote') {
|
||||
if(props.moduleConfig.type === QUESTION_TYPE.VOTE) {
|
||||
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>
|
||||
|
||||
|
12
web/src/render/constant/index.js
Normal file
12
web/src/render/constant/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
export const QUESTION_TYPE = {
|
||||
VOTE: 'vote',
|
||||
CHECKBOX: 'checkbox',
|
||||
CHOICES: [ // 选择类题型分类
|
||||
'radio',
|
||||
'checkbox',
|
||||
],
|
||||
RATES: [ // 评分题题型分类
|
||||
'radio-star',
|
||||
'radio-nps'
|
||||
]
|
||||
}
|
6
web/src/render/hooks/useRuleEngine.js
Normal file
6
web/src/render/hooks/useRuleEngine.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { RuleMatch } from '@/common/logicEngine/RulesMatch'
|
||||
|
||||
export const ruleEngine = new RuleMatch()
|
||||
export const initRuleEngine = (ruleConf) => {
|
||||
ruleEngine.fromJson(ruleConf)
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="index">
|
||||
<progressBar />
|
||||
<ProgressBar />
|
||||
<div class="wrapper" ref="box">
|
||||
<HeaderSetter></HeaderSetter>
|
||||
<div class="content">
|
||||
<MainTitle></MainTitle>
|
||||
<MainRenderer ref="main"></MainRenderer>
|
||||
<submit :validate="validate" :renderData="renderData" @submit="onSubmit"></submit>
|
||||
<Submit :validate="validate" :renderData="renderData" @submit="onSubmit"></Submit>
|
||||
<LogoIcon />
|
||||
</div>
|
||||
</div>
|
||||
@ -16,11 +16,11 @@
|
||||
<script>
|
||||
import HeaderSetter from '../components/HeaderSetter.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 AlertDialog from '../components/AlertDialog.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 { submitForm } from '../api/survey'
|
||||
@ -44,9 +44,9 @@ export default {
|
||||
components: {
|
||||
HeaderSetter,
|
||||
MainTitle,
|
||||
submit,
|
||||
Submit,
|
||||
MainRenderer,
|
||||
progressBar,
|
||||
ProgressBar,
|
||||
LogoIcon
|
||||
},
|
||||
computed: {
|
||||
@ -93,22 +93,10 @@ export default {
|
||||
},
|
||||
getSubmitData() {
|
||||
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 = {
|
||||
surveyPath: this.surveyPath,
|
||||
data: JSON.stringify(formModel),
|
||||
data: JSON.stringify(formValues),
|
||||
difTime: Date.now() - this.$store.state.enterTime,
|
||||
clientTime: Date.now()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user