format: 代码格式化 (#160)

This commit is contained in:
dayou 2024-05-23 21:52:57 +08:00 committed by sudoooooo
parent 38566f1f60
commit 4c85fcc47e
30 changed files with 291 additions and 274 deletions

10
web/components.d.ts vendored
View File

@ -11,26 +11,30 @@ declare module 'vue' {
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
IEpBottom: typeof import('~icons/ep/bottom')['default']
IEpCheck: typeof import('~icons/ep/check')['default']

View File

@ -8,25 +8,22 @@ export enum Operator {
Include = 'in',
Equal = 'eq',
NotEqual = 'neq',
NotInclude = 'nin',
NotInclude = 'nin'
}
export enum PrefixID {
Rule = 'r',
Condition = 'c'
}
export enum Scope {
Question = 'question',
Option = 'option'
}
export type FieldTypes = string | string[];
export type FieldTypes = string | string[]
// 定义事实对象类型
export type Fact = {
[key: string]: any;
};
[key: string]: any
}

View File

@ -1,4 +1,4 @@
import { nanoid } from 'nanoid';
import { nanoid } from 'nanoid'
import * as yup from 'yup'
import { type FieldTypes, PrefixID, Operator, Scope } from './BasicType'
@ -7,33 +7,33 @@ export function generateID(prefix = PrefixID.Rule) {
}
// 定义条件规则类
export class ConditionNode {
id: string = '';
public field: string = '';
public operator: Operator = Operator.Include;
id: string = ''
public field: string = ''
public operator: Operator = Operator.Include
public value: FieldTypes = []
constructor(field: string = '', operator: Operator = Operator.Include, value: FieldTypes = []) {
this.field = field;
this.operator = operator;
this.value = value;
this.field = field
this.operator = operator
this.value = value
this.id = generateID(PrefixID.Condition)
}
setField(field: string) {
this.field = field;
this.field = field
}
setOperator(operator: Operator) {
this.operator = operator;
this.operator = operator
}
setValue(value: FieldTypes) {
this.value = value;
this.value = value
}
}
export class RuleNode {
id: string = '';
id: string = ''
conditions: ConditionNode[] = []
scope: string = Scope.Question
target: string = ''
constructor(scope:string = Scope.Question, target: string = '') {
constructor(scope: string = Scope.Question, target: string = '') {
this.id = generateID(PrefixID.Rule)
this.scope = scope
this.target = target
@ -42,44 +42,44 @@ export class RuleNode {
this.target = value
}
addCondition(condition: ConditionNode) {
this.conditions.push(condition);
this.conditions.push(condition)
}
removeCondition(id: string) {
this.conditions = this.conditions.filter(v => v.id !== id);
this.conditions = this.conditions.filter((v) => v.id !== id)
}
findCondition(conditionId: string) {
return this.conditions.find(condition => condition.id === conditionId);
return this.conditions.find((condition) => condition.id === conditionId)
}
}
export class RuleBuild {
rules: RuleNode[] = [];
static instance: RuleBuild;
rules: RuleNode[] = []
static instance: RuleBuild
constructor() {
this.rules = [];
this.rules = []
if (!RuleBuild.instance) {
RuleBuild.instance = this;
RuleBuild.instance = this
}
return RuleBuild.instance;
return RuleBuild.instance
}
// 添加条件规则到规则引擎中
addRule(rule: RuleNode) {
this.rules.push(rule);
this.rules.push(rule)
}
removeRule(ruleId: string) {
this.rules = this.rules.filter(rule => rule.id !== ruleId);
this.rules = this.rules.filter((rule) => rule.id !== ruleId)
}
findRule(ruleId: string) {
return this.rules.find(rule => rule.id === ruleId);
return this.rules.find((rule) => rule.id === ruleId)
}
toJson() {
return this.rules.map(rule => {
return this.rules.map((rule) => {
return {
target: rule.target,
scope: rule.scope,
conditions: rule.conditions.map(condition => {
conditions: rule.conditions.map((condition) => {
return {
field: condition.field,
operator: condition.operator,
@ -91,13 +91,13 @@ export class RuleBuild {
}
fromJson(ruleConf: any) {
this.rules = []
if(ruleConf instanceof Array) {
if (ruleConf instanceof Array) {
ruleConf.forEach((rule: any) => {
const { scope, target } = rule
const ruleNode = new RuleNode(scope, target);
const ruleNode = new RuleNode(scope, target)
rule.conditions.forEach((condition: any) => {
const { field, operator, value } = condition
const conditionNode = new ConditionNode(field, operator, value);
const conditionNode = new ConditionNode(field, operator, value)
ruleNode.addCondition(conditionNode)
})
this.addRule(ruleNode)
@ -109,16 +109,16 @@ export class RuleBuild {
return ruleSchema.validateSync(this.toJson())
}
// 实现目标选择了下拉框置灰效果
findTargetsByScope(scope: string){
return this.rules.filter(rule => rule.scope === scope).map(rule => rule.target)
findTargetsByScope(scope: string) {
return this.rules.filter((rule) => rule.scope === scope).map((rule) => rule.target)
}
// 实现前置题删除校验
findTargetsByFields(field: string) {
const nodes = this.rules.filter((rule: RuleNode) => {
const conditions = rule.conditions.filter((item: any) => {
const conditions = rule.conditions.filter((item: any) => {
return item.field === field
})
return conditions.length > 0
return conditions.length > 0
})
return nodes.map((item: any) => {
return item.target
@ -126,11 +126,10 @@ export class RuleBuild {
}
// 根据目标题获取显示逻辑
findConditionByTarget(target: string) {
return this.rules.filter(rule=> rule.target === target).map(item => item.conditions)
return this.rules.filter((rule) => rule.target === target).map((item) => item.conditions)
}
}
export const ruleSchema = yup.array().of(
yup.object({
target: yup.string().required(),
@ -143,4 +142,4 @@ export const ruleSchema = yup.array().of(
})
)
})
)
)

View File

@ -1,64 +1,65 @@
import { Operator, type FieldTypes, type Fact } from "./BasicType";
import { Operator, type FieldTypes, type Fact } from './BasicType'
// 定义条件规则类
export class ConditionNode<F extends string, O extends Operator> {
// 默认显示
public result: boolean = false;
constructor(public field: F, public operator: O, public value: FieldTypes) {
}
public result: boolean = false
constructor(
public field: F,
public operator: O,
public value: FieldTypes
) {}
// 计算条件规则的哈希值
calculateHash(): string {
// 假设哈希值计算方法为简单的字符串拼接或其他哈希算法
return this.field + this.operator + this.value;
return this.field + this.operator + this.value
}
match(facts: Fact): boolean {
// console.log(this.calculateHash())
// 如果该特征在事实对象中不存在则直接返回false
if(!facts[this.field]) {
if (!facts[this.field]) {
this.result = false
return this.result
}
switch (this.operator) {
case Operator.Equal:
if(this.value instanceof Array) {
this.result = this.value.every(v => facts[this.field].includes(v))
if (this.value instanceof Array) {
this.result = this.value.every((v) => facts[this.field].includes(v))
return this.result
} else {
this.result = facts[this.field].includes(this.value);
this.result = facts[this.field].includes(this.value)
return this.result
}
case Operator.Include:
if(this.value instanceof Array) {
this.result = this.value.some(v => facts[this.field].includes(v))
if (this.value instanceof Array) {
this.result = this.value.some((v) => facts[this.field].includes(v))
return this.result
} else {
this.result = facts[this.field].includes(this.value);
this.result = facts[this.field].includes(this.value)
return this.result
}
case Operator.NotInclude:
if(this.value instanceof Array) {
this.result = this.value.some(v => !facts[this.field].includes(v))
if (this.value instanceof Array) {
this.result = this.value.some((v) => !facts[this.field].includes(v))
return this.result
} else {
this.result = facts[this.field].includes(this.value);
this.result = facts[this.field].includes(this.value)
return this.result
}
case Operator.NotEqual:
if(this.value instanceof Array) {
this.result = this.value.every(v => !facts[this.field].includes(v))
if (this.value instanceof Array) {
this.result = this.value.every((v) => !facts[this.field].includes(v))
return this.result
} else {
this.result = facts[this.field].includes(this.value);
this.result = facts[this.field].includes(this.value)
return this.result
}
// 其他比较操作符的判断逻辑
default:
return this.result
}
}
getResult() {
@ -67,27 +68,30 @@ export class ConditionNode<F extends string, O extends Operator> {
}
export class RuleNode {
conditions: Map<string, ConditionNode<string, Operator>>; // 使用哈希表存储条件规则对象
public result: boolean = false;
constructor(public target: string, public scope: string) {
this.conditions = new Map();
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, Operator>) {
const hash = condition.calculateHash();
this.conditions.set(hash, condition);
const hash = condition.calculateHash()
this.conditions.set(hash, condition)
}
// 匹配条件规则
match(fact: Fact) {
const res = Array.from(this.conditions.entries()).every(([, value]) => {
const res = Array.from(this.conditions.entries()).every(([, value]) => {
const res = value.match(fact)
if (res) {
return true;
return true
} else {
return false
}
});
})
this.result = res
return res
}
@ -102,7 +106,7 @@ export class RuleNode {
// 计算条件规则的哈希值
calculateHash(): string {
// 假设哈希值计算方法为简单的字符串拼接或其他哈希算法
return this.target + this.scope;
return this.target + this.scope
}
toJson() {
return {
@ -111,30 +115,33 @@ export class RuleNode {
conditions: Object.fromEntries(
Array.from(this.conditions, ([key, value]) => [key, value.getResult()])
)
};
}
}
}
export class RuleMatch {
rules: Map<string, RuleNode>;
static instance: any;
rules: Map<string, RuleNode>
static instance: any
constructor() {
this.rules = new Map();
this.rules = new Map()
if (!RuleMatch.instance) {
RuleMatch.instance = this;
RuleMatch.instance = this
}
return RuleMatch.instance;
return RuleMatch.instance
}
fromJson(ruleConf:any) {
if(ruleConf instanceof Array) {
fromJson(ruleConf: any) {
if (ruleConf instanceof Array) {
ruleConf.forEach((rule: any) => {
const ruleNode = new RuleNode(rule.target, rule.scope);
const ruleNode = new RuleNode(rule.target, rule.scope)
rule.conditions.forEach((condition: any) => {
const conditionNode = new ConditionNode(condition.field, condition.operator, condition.value);
ruleNode.addCondition(conditionNode)
});
const conditionNode = new ConditionNode(
condition.field,
condition.operator,
condition.value
)
ruleNode.addCondition(conditionNode)
})
this.addRule(ruleNode)
})
}
@ -142,23 +149,22 @@ export class RuleMatch {
// 添加条件规则到规则引擎中
addRule(rule: RuleNode) {
const hash = rule.calculateHash();
const hash = rule.calculateHash()
if (this.rules.has(hash)) {
const existRule: any = this.rules.get(hash);
const existRule: any = this.rules.get(hash)
existRule.conditions.forEach((item: ConditionNode<string, Operator>) => {
rule.addCondition(item)
})
}
this.rules.set(hash, rule);
}
this.rules.set(hash, rule)
}
// 匹配条件规则
match(target: string, scope: string, fact: Fact) {
const hash = this.calculateHash(target, scope);
const hash = this.calculateHash(target, scope)
const rule = this.rules.get(hash);
const rule = this.rules.get(hash)
if (rule) {
const result = rule.match(fact)
// this.matchCache.set(hash, result);
@ -168,10 +174,10 @@ export class RuleMatch {
return true
}
}
getResult(target: string, scope: string) {
const hash = this.calculateHash(target, scope);
const rule = this.rules.get(hash);
const hash = this.calculateHash(target, scope)
const rule = this.rules.get(hash)
if (rule) {
const result = rule.getResult()
return result
@ -183,15 +189,17 @@ export class RuleMatch {
// 计算哈希值的方法
calculateHash(target: string, scope: string): string {
// 假设哈希值计算方法为简单的字符串拼接或其他哈希算法
return target + scope;
return target + scope
}
findTargetsByField(field: string) {
const rules = new Map([...this.rules.entries()].filter(([, value]) => {
return [...value.conditions.entries()].filter(([, value]) => {
return value.field === field
const rules = new Map(
[...this.rules.entries()].filter(([, value]) => {
return [...value.conditions.entries()].filter(([, value]) => {
return value.field === field
})
})
}))
return [...rules.values()].map(obj => obj.target);
)
return [...rules.values()].map((obj) => obj.target)
}
toJson() {
return Array.from(this.rules.entries()).map(([, value]) => {

View File

@ -1 +1 @@
export const DND_GROUP = 'question'
export const DND_GROUP = 'question'

View File

@ -1,4 +1,4 @@
import { computed } from 'vue';
import { computed } from 'vue'
import store from '@/management/store'
import { cleanRichText } from '@/common/xss'
export const useQuestionInfo = (field) => {
@ -12,9 +12,11 @@ export const useQuestionInfo = (field) => {
const questionDataList = store.state.edit.schema.questionDataList
return (value) => {
const options = questionDataList.find((item) => item.field === field)?.options || []
if(value instanceof Array) {
return options.filter((item) => value.includes(item.hash)).map((item) => cleanRichText(item.text))
} else {
if (value instanceof Array) {
return options
.filter((item) => value.includes(item.hash))
.map((item) => cleanRichText(item.text))
} else {
return options.filter((item) => item.hash === value).map((item) => cleanRichText(item.text))
}
}

View File

@ -4,4 +4,4 @@ import { RuleBuild } from '@/common/logicEngine/RuleBuild'
export const showLogicEngine = ref()
export const initShowLogicEngine = (ruleConf) => {
showLogicEngine.value = new RuleBuild().fromJson(ruleConf)
}
}

View File

@ -1,4 +1,4 @@
import { computed, unref } from 'vue';
import { computed, unref } from 'vue'
import { useQuestionInfo } from './useQuestionInfo'
import { flatten } from 'lodash-es'
import { cleanRichText } from '@/common/xss'
@ -16,14 +16,16 @@ export const useShowLogicInfo = (field) => {
})
const getShowLogicText = computed(() => {
const logicEngine = showLogicEngine.value
// 获取目标题的规则
const rules = logicEngine?.findConditionByTarget(field) || []
const conditions = flatten(rules).map((item) => {
const { getQuestionTitle, getOptionTitle } = useQuestionInfo(item.field)
// 获取目标题的规则
const rules = logicEngine?.findConditionByTarget(field) || []
const conditions = flatten(rules).map((item) => {
const { getQuestionTitle, getOptionTitle } = useQuestionInfo(item.field)
return `<span>【 ${cleanRichText(getQuestionTitle.value())}】 选择了 【${getOptionTitle.value(unref(item.value)).join('、')}】</span> <br/>`
})
return conditions.length ? conditions.join('') + '<span> &nbsp;满足以上全部,则显示本题</span>' :''
return conditions.length
? conditions.join('') + '<span> &nbsp;满足以上全部,则显示本题</span>'
: ''
})
return { hasShowLogic, getShowLogicText }
}
}

View File

@ -41,7 +41,7 @@ export default {
if (this.isPublishing) {
return
}
try {
this.isPublishing = true
const saveRes = await saveSurvey(saveData)
@ -66,7 +66,7 @@ export default {
}
},
updateLogicConf() {
if(showLogicEngine.value) {
if (showLogicEngine.value) {
showLogicEngine.value.validateSchema()
const showLogicConf = showLogicEngine.value.toJson()
//

View File

@ -90,7 +90,7 @@ export default {
}
},
updateLogicConf() {
if(showLogicEngine.value) {
if (showLogicEngine.value) {
showLogicEngine.value.validateSchema()
const showLogicConf = showLogicEngine.value.toJson()
//
@ -118,7 +118,7 @@ export default {
ElMessage.error('请检查逻辑配置是否有误')
return
}
try {
this.isSaving = true
const res = await this.saveData()

View File

@ -1,7 +1,7 @@
<template>
<div class="rule-list">
<RuleNodeView
v-for="(item) in list"
v-for="item in list"
ref="ruleWrappers"
:key="item.id"
:ruleNode="item"

View File

@ -78,7 +78,8 @@ const props = defineProps({
})
const fieldList = computed(() => {
const currentIndex = renderData.value.findIndex((item) => item.field === props.ruleNode.target)
return renderData.value.slice(0, currentIndex)
return renderData.value
.slice(0, currentIndex)
.filter((question: any) => qAbleList.includes(question.type))
.map((item: any) => {
return {
@ -140,14 +141,14 @@ const handleDelete = (id: any) => {
position: relative;
display: flex;
padding: 24px 0;
&:not(:last-child)::before{
&:not(:last-child)::before {
content: attr(data-content-before);
bottom: 0px;
width: 20px;
height: 20px;
background: #FEF6E6;
background: #fef6e6;
border-radius: 2px;
color: #FAA600;
color: #faa600;
font-size: 12px;
display: flex;
justify-content: center;
@ -155,14 +156,14 @@ const handleDelete = (id: any) => {
position: absolute;
bottom: -8px;
}
&:not(:last-child)::after{
content: "";
display: block;
width: calc(100% - 32px);
border-top: 1px dashed #e3e4e8;
position: absolute;
left: 32px;
bottom: 0;
&:not(:last-child)::after {
content: '';
display: block;
width: calc(100% - 32px);
border-top: 1px dashed #e3e4e8;
position: absolute;
left: 32px;
bottom: 0;
}
.desc {
display: inline-block;
@ -192,4 +193,4 @@ const handleDelete = (id: any) => {
.select {
width: 200px;
}
</style>
</style>

View File

@ -32,7 +32,7 @@
v-for="{ label, value, disabled } in targetQuestionList"
:key="value"
:label="label"
:disabled="disabled && ruleNode.target !== value "
:disabled="disabled && ruleNode.target !== value"
:value="value"
>
</el-option>
@ -97,7 +97,9 @@ const submitForm = () => {
const targetQuestionList = computed(() => {
const currntIndexs: number[] = []
props.ruleNode.conditions.forEach((el) => {
currntIndexs.push(renderData.value.findIndex((item: { field: string }) => item.field === el.field))
currntIndexs.push(
renderData.value.findIndex((item: { field: string }) => item.field === el.field)
)
})
const currntIndex = Math.max(...currntIndexs)
let questionList = cloneDeep(renderData.value.slice(currntIndex + 1))
@ -105,9 +107,7 @@ const targetQuestionList = computed(() => {
return {
label: `${item.showIndex ? item.indexNumber + '.' : ''} ${cleanRichText(item.title)}`,
value: item.field,
disabled: showLogicEngine.value
.findTargetsByScope('question')
.includes(item.field)
disabled: showLogicEngine.value.findTargetsByScope('question').includes(item.field)
}
})
})
@ -144,4 +144,4 @@ defineExpose({
.select {
width: 200px;
}
</style>
</style>

View File

@ -6,34 +6,33 @@
:name="index"
:key="index"
>
<draggable
class="questiontype-list"
:list="item.questionList"
:group="{ name: DND_GROUP, pull: 'clone', put: false }"
:clone="getNewQuestion"
item-key="path"
>
<template #item="{ element }">
<div
:key="element.type"
class="qtopic-item"
:id="'qtopic' + element.type"
@click="onQuestionType({ type: element.type })"
@mouseenter="showPreview(element, 'qtopic' + element.type)"
@mouseleave="isShowPreviewImage = false"
@mousedown="isShowPreviewImage = false"
>
<i class="iconfont" :class="['icon-' + element.icon]"></i>
<p class="text">{{ element.title }}</p>
</div>
</template>
</draggable>
<draggable
class="questiontype-list"
:list="item.questionList"
:group="{ name: DND_GROUP, pull: 'clone', put: false }"
:clone="getNewQuestion"
item-key="path"
>
<template #item="{ element }">
<div
:key="element.type"
class="qtopic-item"
:id="'qtopic' + element.type"
@click="onQuestionType({ type: element.type })"
@mouseenter="showPreview(element, 'qtopic' + element.type)"
@mouseleave="isShowPreviewImage = false"
@mousedown="isShowPreviewImage = false"
>
<i class="iconfont" :class="['icon-' + element.icon]"></i>
<p class="text">{{ element.title }}</p>
</div>
</template>
</draggable>
</el-collapse-item>
<Teleport to="body">
<div class="preview-popover" v-show="isShowPreviewImage" :style="{ top: previewTop + 'px'}">
<img :src="previewImg" class="preview-image"/>
<span class="preview-arrow"></span>
<div class="preview-popover" v-show="isShowPreviewImage" :style="{ top: previewTop + 'px' }">
<img :src="previewImg" class="preview-image" />
<span class="preview-arrow"></span>
</div>
</Teleport>
</el-collapse>
@ -78,14 +77,14 @@ const getNewQuestion = ({ type }) => {
}
const onQuestionType = ({ type }) => {
const newQuestion = getNewQuestion({ type })
const newQuestion = getNewQuestion({ type })
store.dispatch('edit/addQuestion', { question: newQuestion, index: newQuestionIndex.value })
store.commit('edit/setCurrentEditOne', newQuestionIndex.value)
}
const showPreview = ({ snapshot }, id) => {
previewImg.value = snapshot
const dragEl = document.getElementById(id)
const { top, height } = dragEl.getBoundingClientRect()
previewTop.value = top + height / 2
@ -136,7 +135,7 @@ const showPreview = ({ snapshot }, id) => {
background-color: $primary-color-light;
border: 1px solid $primary-color;
}
.text {
font-size: 12px;
user-select: none;
@ -211,7 +210,7 @@ const showPreview = ({ snapshot }, id) => {
&::before {
position: absolute;
content: "";
content: '';
height: 10px;
width: 10px;
border: 1px solid var(--el-border-color-light);
@ -219,7 +218,6 @@ const showPreview = ({ snapshot }, id) => {
border-bottom-color: transparent;
border-right-color: transparent;
}
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<CommonTemplate>
<template #left>
<CatalogPanel />
<CatalogPanel />
</template>
<template #center>
<PreviewPanel />
@ -12,22 +12,22 @@
</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: 'editIndex',
components: {
CommonTemplate,
CatalogPanel,
PreviewPanel,
SetterPanel,
},
};
SetterPanel
}
}
</script>
<style lang="scss" scoped>
.navbar {
border-bottom: 1px solid #e7e9eb;
}
</style>
</style>

View File

@ -30,8 +30,7 @@ const routes: RouteRecordRaw[] = [
needLogin: true
},
name: 'QuestionEditPage',
component: () =>
import('../pages/edit/pages/edit/index.vue'),
component: () => import('../pages/edit/pages/edit/index.vue'),
children: [
{
path: '',
@ -39,8 +38,7 @@ const routes: RouteRecordRaw[] = [
meta: {
needLogin: true
},
component: () =>
import('../pages/edit/pages/edit/QuestionEditPage.vue')
component: () => import('../pages/edit/pages/edit/QuestionEditPage.vue')
},
{
path: 'logic',
@ -48,8 +46,7 @@ const routes: RouteRecordRaw[] = [
meta: {
needLogin: true
},
component: () =>
import('../pages/edit/pages/edit/LogicEditPage.vue')
component: () => import('../pages/edit/pages/edit/LogicEditPage.vue')
}
]
},

View File

@ -15,8 +15,15 @@ export default {
if (res.code === 200) {
const metaData = res.data.surveyMetaRes
document.title = metaData.title
const { bannerConf, bottomConf, skinConf, baseConf, submitConf, dataConf, logicConf = {} } =
res.data.surveyConfRes.code
const {
bannerConf,
bottomConf,
skinConf,
baseConf,
submitConf,
dataConf,
logicConf = {}
} = res.data.surveyConfRes.code
commit('initSchema', {
metaData,
codeData: {
@ -29,7 +36,6 @@ export default {
logicConf
}
})
} else {
throw new Error(res.errmsg || '问卷不存在')
}

View File

@ -1,5 +1,5 @@
export default {
setBannerList(state, data) {
state.bannerList = data
},
}
}

View File

@ -3,19 +3,14 @@ export const QOP_MAP = {
COPY: 'copy',
EDIT: 'edit'
}
export const qAbleList = [
'radio',
'checkbox',
'binary-choice',
'vote',
]
export const qAbleList = ['radio', 'checkbox', 'binary-choice', 'vote']
export const operatorOptions = [
{
label: '选择了',
value: 'in',
label: '选择了',
value: 'in'
},
{
label: '不选择',
value: 'nin',
},
]
label: '不选择',
value: 'nin'
}
]

View File

@ -32,7 +32,7 @@ interface Props {
}
interface Emit {
(ev: typeof FORM_CHANGE_EVENT_KEY, arg: { key: string; value: string }): void
(ev: typeof FORM_CHANGE_EVENT_KEY, arg: { key: string; value: string | null }): void
}
const emit = defineEmits<Emit>()
@ -58,8 +58,8 @@ const handleSelectChange = (value: string) => {
emit(FORM_CHANGE_EVENT_KEY, { key: formdataBackfillKey, value })
}
const handleSwitchChange = (value: string) => {
emit(FORM_CHANGE_EVENT_KEY, { key: formdataBackfillHourKey, value: value ? 24 : null })
const handleSwitchChange = (value: string | null) => {
emit(FORM_CHANGE_EVENT_KEY, { key: formdataBackfillHourKey, value: value ? '24' : null })
}
const watchValue = computed(() => props.formConfig.value)

View File

@ -77,15 +77,15 @@ onMounted(async () => {
if (res.code === 200) {
const data = res.data
const { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf, logicConf } = data.code
const { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf, logicConf } =
data.code
const questionData = {
bannerConf,
baseConf,
bottomConf,
dataConf,
skinConf,
submitConf,
submitConf
}
document.title = data.title
@ -126,4 +126,4 @@ html {
flex: 1;
background-color: #fff;
}
</style>
</style>

View File

@ -1,6 +1,6 @@
<template>
<form ref="ruleForm" :model="formValues" :rules="rules">
<div v-for="(item) in renderData" :key="item.field">
<div v-for="item in renderData" :key="item.field">
<QuestionWrapper
class="gap"
v-bind="$attrs"

View File

@ -29,36 +29,42 @@ const props = defineProps({
default: () => {
return {}
}
},
}
})
const emit = defineEmits(['change'])
const formValues = computed(() => {
return store.state.formValues
})
const questionConfig = computed(() =>{
const questionConfig = computed(() => {
let moduleConfig = props.moduleConfig
const { type, field, options, ...rest } = cloneDeep(moduleConfig)
// console.log(field,'formValuechange')
let alloptions = options
if(type === QUESTION_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(QUESTION_TYPE.CHOICES.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(QUESTION_TYPE.RATES.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)
moduleConfig.rangeConfig = unref(rangeConfig)
moduleConfig.othersValue = unref(othersValue)
}
return {
...moduleConfig,
options: alloptions,
@ -73,27 +79,30 @@ const visible = computed(() => {
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 (type === QUESTION_TYPE.CHECKBOX || innerType === QUESTION_TYPE.CHECKBOX) {
value = value ? [value] : []
watch(
() => visible.value,
(newVal, oldVal) => {
//
const { field, type, innerType } = props.moduleConfig
if (!newVal && oldVal) {
let value = ''
// innerType
if (type === QUESTION_TYPE.CHECKBOX || innerType === QUESTION_TYPE.CHECKBOX) {
value = value ? [value] : []
}
const data = {
key: field,
value: value
}
store.commit('changeFormData', data)
}
const data = {
key: field,
value: value
}
store.commit('changeFormData', data)
}
})
)
const handleChange = (data) => {
emit('change', data)
//
if(props.moduleConfig.type === QUESTION_TYPE.VOTE) {
if (props.moduleConfig.type === QUESTION_TYPE.VOTE) {
store.dispatch('updateVoteData', data)
}
}

View File

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

View File

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

View File

@ -5,15 +5,14 @@ export const useShowInput = (questionKey) => {
let rangeConfig = store.state.questionData[questionKey].rangeConfig
let othersValue = {}
if (rangeConfig && Object.keys(rangeConfig).length > 0) {
for(let key in rangeConfig) {
for (let key in rangeConfig) {
const curRange = rangeConfig[key]
if (curRange.isShowInput) {
const rangeKey = `${questionKey}_${key}`
othersValue[rangeKey] = formValues[rangeKey]
curRange.othersKey = rangeKey,
curRange.othersValue = formValues[rangeKey]
if(!questionVal.toString().includes(key) && formValues[rangeKey]) {
;(curRange.othersKey = rangeKey), (curRange.othersValue = formValues[rangeKey])
if (!questionVal.toString().includes(key) && formValues[rangeKey]) {
// 如果分值被未被选中且对应的填写更多有值,则清空填写更多
const data = {
key: rangeKey,
@ -24,7 +23,6 @@ export const useShowInput = (questionKey) => {
}
}
}
return { rangeConfig, othersValue }
}
return { rangeConfig, othersValue }
}

View File

@ -3,11 +3,11 @@ export const useShowOthers = (questionKey) => {
const formValues = store.state.formValues
const questionVal = formValues[questionKey]
let othersValue = {}
let options = store.state.questionData[questionKey].options.map(optionItem => {
let options = store.state.questionData[questionKey].options.map((optionItem) => {
if (optionItem.others) {
const opKey = `${questionKey}_${optionItem.hash}`
othersValue[opKey] = formValues[opKey]
if(!questionVal.includes(optionItem.hash) && formValues[opKey]) {
if (!questionVal.includes(optionItem.hash) && formValues[opKey]) {
// 如果选项被未被选中且对应的填写更多有值,则清空填写更多
const data = {
key: opKey,
@ -24,6 +24,6 @@ export const useShowOthers = (questionKey) => {
return optionItem
}
})
return { options, othersValue }
}
}

View File

@ -1,17 +1,16 @@
import store from '../store/index'
export const useVoteMap = (questionKey) => {
let voteTotal = store.state.voteMap?.[questionKey]?.total || 0
let voteTotal = store.state.voteMap?.[questionKey]?.total || 0
const options = store.state.questionData[questionKey].options.map((option) => {
const optionHash = option.hash
const voteCount = store.state.voteMap?.[questionKey]?.[optionHash] || 0
return {
...option,
voteCount
}
})
const options = store.state.questionData[questionKey].options.map(option => {
const optionHash = option.hash
const voteCount = store.state.voteMap?.[questionKey]?.[optionHash] || 0
return {
...option,
voteCount
}
})
return { options, voteTotal }
}
}

View File

@ -2,28 +2,28 @@ export default {
// 题目列表
renderData: (state) => {
const { questionSeq, questionData } = state
let index = 1
return (
questionSeq &&
questionSeq.reduce((pre, item) => {
const questionArr = []
item.forEach(questionKey => {
item.forEach((questionKey) => {
console.log('题目重新计算')
const question = { ...questionData[questionKey] }
// 开启显示序号
if (question.showIndex) {
question.indexNumber = index++
}
questionArr.push(question)
})
if (questionArr && questionArr.length) {
pre.push(questionArr)
}
return pre
}, [])
)

View File

@ -39,7 +39,7 @@ export default {
updateVoteMapByKey(state, data) {
const { questionKey, voteKey, voteValue } = data
// 兼容为空的情况
if(!state.voteMap[questionKey]){
if (!state.voteMap[questionKey]) {
state.voteMap[questionKey] = {}
}
state.voteMap[questionKey][voteKey] = voteValue
@ -52,5 +52,5 @@ export default {
},
setRuleEgine(state, ruleEngine) {
state.ruleEngine = ruleEngine
},
}
}