feat: 高级设置组件迁移

This commit is contained in:
sudoooooo 2024-08-13 23:44:19 +08:00
parent 9e07e8330a
commit 013f9ac811
11 changed files with 349 additions and 381 deletions

View File

@ -28,7 +28,11 @@
:isSelected="currentEditOne === element.qIndex"
:readonly="true"
@change="handleChange"
></QuestionContainerB>
>
<template #advancedEdit>
<slot name="advancedEdit" :moduleConfig="element"></slot>
</template>
</QuestionContainerB>
</QuestionWrapper>
</template>
</draggable>
@ -65,7 +69,7 @@ export default defineComponent({
const editStore = useEditStore()
const renderData = computed({
get() {
return props.questionDataList; //filterQuestionPreviewData(props.questionDataList)
return props.questionDataList //filterQuestionPreviewData(props.questionDataList)
},
set(value) {
editStore.moveQuestionDataList(value)

View File

@ -20,7 +20,11 @@
@change="handleChange"
@changeSeq="onQuestionOperation"
ref="materialGroup"
/>
>
<template #advancedEdit="{ moduleConfig }">
<AdvancedComponent :moduleConfig="moduleConfig" @handleChange="handleChange" />
</template>
</MaterialGroup>
<SubmitButton
:submit-conf="submitConf"
:readonly="false"
@ -36,10 +40,14 @@
<script setup>
import { ref, watch, toRefs } from 'vue'
import { storeToRefs } from 'pinia'
import communalLoader from '@materials/communals/communalLoader.js'
import PageWrapper from '@/management/pages/edit/components/Pagination/PaginationWrapper.vue'
import MaterialGroup from '@/management/pages/edit/components/MaterialGroup.vue'
import { storeToRefs } from 'pinia'
import AdvancedComponent from './components/AdvancedConfig/index.vue'
import { useEditStore } from '@/management/stores/edit'
const MainTitle = communalLoader.loadComponent('MainTitle')
@ -95,6 +103,7 @@ const onQuestionOperation = (data) => {
break
}
}
watch(
skinConf,
(newVal) => {

View File

@ -1,23 +1,26 @@
<template>
<div>
<span class="primary-color" @click="openOptionConfig"> 高级设置 > </span>
<el-dialog
title="选项高级设置"
class="option-config-wrapper"
v-model="configVisible"
:append-to-body="true"
:width="dialogWidth"
width="60%"
size="large"
>
<div class="option-handwrite">
<div class="option-header">
<div class="header-item flex-1" v-if="showText">选项内容</div>
<div class="header-item w285" v-if="showOthers">选项后增添输入框</div>
<div class="header-item flex-1">选项内容</div>
<div class="header-item w285">选项后增添输入框</div>
</div>
<div>
<draggable :list="curOptions" handle=".drag-handle" itemKey="hash">
<template #item="{ element, index }">
<div class="option-item">
<span class="drag-handle qicon qicon-tuodong"></span>
<div class="flex-1 oitem" v-if="showText">
<div class="flex-1 oitem">
<div
contenteditable="true"
class="render-html"
@ -25,7 +28,7 @@
@blur="onBlur($event, index)"
></div>
</div>
<div class="oitem moreInfo lh36" v-if="showOthers">
<div class="oitem moreInfo lh36">
<el-switch
:modelValue="element.others"
@change="(val) => changeOptionOthers(val, element)"
@ -36,12 +39,8 @@
</div>
</div>
<div class="operate-area" v-if="showOperateOption">
<i-ep-circlePlus
v-if="showOperateOption"
class="area-btn-icon"
@click="addOption('选项', false, index)"
/>
<div class="operate-area">
<i-ep-circlePlus class="area-btn-icon" @click="addOption('选项', false, index)" />
<i-ep-remove
v-show="curOptions.length"
class="area-btn-icon"
@ -53,15 +52,12 @@
</draggable>
</div>
<div class="add-btn-row">
<div class="add-option" v-if="showOperateOption" @click="addOption()">
<div class="add-option" @click="addOption()">
<span class="add-option-item"> <i-ep-circlePlus class="icon" /> 添加新选项 </span>
</div>
<div v-if="showOperateOption && showOthers" class="add-option" @click="addOtherOption">
<span>
<extra-icon type="add-square"></extra-icon>
其他____
</span>
<div class="add-option" @click="addOtherOption">
<span class="add-option-item"> <i-ep-circlePlus class="icon" /> 其他____ </span>
</div>
</div>
</div>
@ -72,6 +68,7 @@
</span>
</template>
</el-dialog>
</div>
</template>
<script>
@ -81,54 +78,26 @@ import { forEach as _forEach, cloneDeep as _cloneDeep } from 'lodash-es'
import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss'
import { useEditStore } from '@/management/stores/edit'
import { cleanRichText } from '@/common/xss'
import ExtraIcon from '../ExtraIcon/index.vue'
export default {
name: 'OptionConfig',
inject: ['moduleConfig'],
props: {
fieldId: {
type: String,
default: ''
}
},
data() {
return {
curOptions: _cloneDeep(this.options),
popoverVisible: false
}
},
props: {
options: {
type: Array,
default() {
return []
}
},
showOptionDialog: {
type: Boolean,
default: false
},
dialogWidth: {
type: String,
default: '60%'
},
showOperateOption: {
type: Boolean,
default: true
},
showText: {
type: Boolean,
default: true
},
showOthers: {
type: Boolean,
default: true
configVisible: false
}
},
computed: {
configVisible: {
get() {
return this.showOptionDialog
},
set(newVal) {
this.$emit('update:modelValue', newVal)
}
curOptions() {
const editStore = useEditStore()
return _cloneDeep(editStore.moduleConfig.options)
},
hashMap() {
const mapData = {}
@ -144,8 +113,7 @@ export default {
}
},
components: {
draggable,
ExtraIcon
draggable
},
watch: {
options(val) {
@ -154,10 +122,12 @@ export default {
},
methods: {
addOtherOption() {
const { field } = this.moduleConfig
this.addOption('其他', true, -1, field)
this.addOption('其他', true, -1, this.fieldId)
},
addOption(text = '选项', others = false, index = -1, field) {
openOptionConfig() {
this.configVisible = true
},
addOption(text = '选项', others = false, index = -1, fieldId) {
let addOne
if (this.curOptions[0]) {
addOne = _cloneDeep(this.curOptions[0])
@ -176,7 +146,7 @@ export default {
for (const i in addOne) {
if (i === 'others') {
addOne[i] = others
if (others) addOne.othersKey = `${field}_${addOne.hash}`
if (others) addOne.othersKey = `${fieldId}_${addOne.hash}`
} else if (i === 'mustOthers') {
addOne[i] = false
} else if (i === 'text') {
@ -201,7 +171,6 @@ export default {
if (typeof newOptions !== 'undefined' && newOptions.length > 0) {
this.curOptions = newOptions
this.importKey = 'single'
this.popoverVisible = false
} else {
ElMessage.warning('最少保留一项')
}
@ -217,10 +186,9 @@ export default {
return Math.random().toString().slice(-6)
},
changeOptionOthers(val, option) {
const { field } = this.moduleConfig
option.others = val
if (val) {
option.othersKey = `${field}_${option.hash}`
option.othersKey = `${this.fieldId}_${option.hash}`
} else {
option.othersKey = ''
}
@ -238,7 +206,7 @@ export default {
ElMessage.warning('已存在相同的标签内容,请重新输入')
return
}
this.$emit('optionChange', this.curOptions)
this.$emit('handleChange', { key: 'options', value: this.curOptions })
this.configVisible = false
},
onBlur(e, index) {
@ -250,6 +218,10 @@ export default {
</script>
<style lang="scss" scoped>
.primary-color {
color: $primary-color;
}
.option-config-wrapper {
.option-handwrite {
.option-header {

View File

@ -0,0 +1,172 @@
<template>
<div>
<span class="primary-color" @click="openOptionConfig"> 评分高级设置 > </span>
<el-dialog
title="评分高级设置"
custom-class="option-config-wrapper"
v-model="configVisible"
:append-to-body="true"
width="800px"
>
<div class="head">
<div class="row">
<div class="score">评分数值</div>
<div class="explain" v-if="isStar">评分释义</div>
<div class="other">评分后增添输入框</div>
</div>
</div>
<div class="body">
<div class="row" v-for="item in range" :key="item.index">
<div class="score">{{ item.index }}</div>
<div class="explain" v-if="isStar">
<el-input class="text" v-model="item.explain" maxlength="200" placeholder="最多200字" />
</div>
<div class="other">
<el-switch class="is-show" v-model="item.isShowInput"></el-switch>
<el-input
class="text"
v-show="item.isShowInput"
v-model="item.text"
placeholder="提示文案"
/>
<el-checkbox class="required" v-show="item.isShowInput" v-model="item.required"
>必填</el-checkbox
>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="configVisible = false">取消</el-button>
<el-button type="primary" @click="onConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
import { get as _get } from 'lodash-es'
import { useEditStore } from '@/management/stores/edit'
import { QUESTION_TYPE } from '@/common/typeEnum'
const editStore = useEditStore()
export default {
data() {
return {
range: [],
configVisible: false
}
},
mounted() {
this.initRange()
},
computed: {
isStar() {
return editStore.moduleConfig.type === QUESTION_TYPE.RADIO_STAR
},
isNps() {
return editStore.moduleConfig.type === QUESTION_TYPE.RADIO_NPS
},
min() {
const { min = 1, starMin = 1 } = editStore.moduleConfig
return this.isNps ? min : starMin
},
max() {
const { max = 5, starMax = 5 } = editStore.moduleConfig
return this.isNps ? max : starMax
}
},
watch: {
min(newValue, oldValue) {
if (newValue !== oldValue) {
this.initRange()
}
},
max(newValue, oldValue) {
if (newValue !== oldValue) {
this.initRange()
}
}
},
methods: {
openOptionConfig() {
this.configVisible = true
},
initRange() {
if (this.min >= this.max) {
return
}
const res = []
const rangeConfig = editStore.moduleConfig.rangeConfig
console.log(234234, editStore.moduleConfig.rangeConfig)
for (let i = this.min; i <= this.max; i++) {
res.push({
index: i,
isShowInput: _get(rangeConfig, `${i}.isShowInput`) || false,
text: _get(rangeConfig, `${i}.text`) || '',
required: _get(rangeConfig, `${i}.required`) || false,
explain: _get(rangeConfig, `${i}.explain`) || ''
})
}
this.range = res
},
onConfirm() {
const res = {}
for (const item of this.range) {
res[item.index] = {
isShowInput: item.isShowInput,
text: item.text,
required: item.required,
explain: item.explain
}
}
this.$emit('handleChange', {
key: `rangeConfig`,
value: res
})
this.configVisible = false
}
}
}
</script>
<style lang="scss" scoped>
.primary-color {
color: $primary-color;
}
.row {
display: flex;
height: 60px;
align-items: center;
border-bottom: 1px solid #eee;
.score {
flex-basis: 110px;
text-align: center;
}
.other {
flex: 1;
padding-left: 10px;
display: flex;
align-items: center;
padding-left: 10px;
.is-show {
margin-right: 10px;
}
.text {
width: 240px;
margin-right: 10px;
}
}
.explain {
width: 216px;
}
}
.head .row {
border: 1px solid #edeffc;
background-color: #f9fafc;
}
</style>

View File

@ -0,0 +1,47 @@
<template>
<Component
:is="advancedComponent"
:fieldId="editStore.moduleConfig.field"
@handleChange="handleChange"
/>
</template>
<script setup>
import { shallowRef, defineAsyncComponent } from 'vue'
import { QUESTION_TYPE } from '@/common/typeEnum'
import { useEditStore } from '@/management/stores/edit'
const editStore = useEditStore()
const emit = defineEmits(['handleChange'])
const props = defineProps({
moduleConfig: {
type: Object,
default: () => {
return {}
}
}
})
const advancedComponent = shallowRef(null)
const handleChange = (data) => {
emit('handleChange', data)
}
//
switch (props.moduleConfig.type) {
case QUESTION_TYPE.RADIO:
case QUESTION_TYPE.CHECKBOX:
case QUESTION_TYPE.VOTE:
advancedComponent.value = defineAsyncComponent(() => import('./OptionConfig.vue'))
break
case QUESTION_TYPE.RADIO_STAR:
case QUESTION_TYPE.RADIO_NPS:
advancedComponent.value = defineAsyncComponent(() => import('./RateConfig.vue'))
break
default:
break
}
</script>
<style lang="scss" scoped></style>

View File

@ -1,4 +1,4 @@
import { computed, defineComponent, onMounted, shallowRef, ref } from 'vue'
import { computed, defineComponent, onMounted, shallowRef, ref, provide } from 'vue'
import questionLoader from '@/materials/questions/questionLoader.js'
@ -46,7 +46,7 @@ export default defineComponent({
}
},
emits: ['blur', 'focus', 'change', 'select'],
setup(props, { emit }) {
setup(props, { slots, emit }) {
const BlockComponent = shallowRef(null)
const questionMeta = ref({})
@ -76,6 +76,11 @@ export default defineComponent({
emit('change', data)
}
//
provide('slots', {
advancedEdit: slots.advancedEdit
})
return {
props,
BlockComponent,

View File

@ -1,160 +0,0 @@
<template>
<el-dialog
title="评分高级设置"
custom-class="option-config-wrapper"
v-model="innerVisible"
:append-to-body="true"
:width="dialogWidth"
>
<div class="head">
<div class="row">
<div class="score">评分数值</div>
<div class="explain" v-if="explain">评分释义</div>
<div class="other">评分后增添输入框</div>
</div>
</div>
<div class="body">
<div class="row" v-for="item in range" :key="item.index">
<div class="score">{{ item.index }}</div>
<div class="explain" v-if="explain">
<el-input class="text" v-model="item.explain" maxlength="200" placeholder="最多200字" />
</div>
<div class="other">
<el-switch class="is-show" v-model="item.isShowInput"></el-switch>
<el-input
class="text"
v-show="item.isShowInput"
v-model="item.text"
placeholder="提示文案"
/>
<el-checkbox class="required" v-show="item.isShowInput" v-model="item.required"
>必填</el-checkbox
>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="innerVisible = false">取消</el-button>
<el-button type="primary" @click="onConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script>
import { get as _get } from 'lodash-es'
export default {
props: {
min: {
type: Number,
default: 1
},
max: {
type: Number,
default: 5
},
visible: {
type: Boolean,
default: false
},
dialogWidth: {
type: [Number, String],
default: '600px'
},
rangeConfig: {
type: Object,
default: () => ({})
},
explain: {
type: Boolean,
default: false
}
},
data() {
return {
range: []
}
},
created() {
this.initRange()
},
computed: {
innerVisible: {
get() {
return this.visible
},
set(newVal) {
this.$emit('update:modelValue', newVal)
}
}
},
methods: {
initRange() {
if (this.min >= this.max) {
return
}
const res = []
for (let i = this.min; i <= this.max; i++) {
res.push({
index: i,
isShowInput: _get(this.rangeConfig, `${i}.isShowInput`) || false,
text: _get(this.rangeConfig, `${i}.text`) || '',
required: _get(this.rangeConfig, `${i}.required`) || false,
explain: _get(this.rangeConfig, `${i}.explain`) || ''
})
}
this.range = res
},
onConfirm() {
const res = {}
for (const item of this.range) {
res[item.index] = {
isShowInput: item.isShowInput,
text: item.text,
required: item.required,
explain: item.explain
}
}
const payload = {
key: `rangeConfig`,
value: res
}
this.$emit('confirm', payload)
this.innerVisible = false
}
}
}
</script>
<style lang="scss" scoped>
.row {
display: flex;
height: 60px;
align-items: center;
border-bottom: 1px solid #eee;
.score {
flex-basis: 110px;
text-align: center;
}
.other {
flex: 1;
padding-left: 10px;
display: flex;
align-items: center;
padding-left: 10px;
.is-show {
margin-right: 10px;
}
.text {
width: 240px;
margin-right: 10px;
}
}
.explain {
width: 216px;
}
}
.head .row {
border: 1px solid #edeffc;
background-color: #f9fafc;
}
</style>

View File

@ -1,60 +1,22 @@
<template>
<!-- const { showOthers, hasAdvancedConfig, hasAdvancedRateConfig, min, max, explain, isNps } = this -->
<div class="option-edit-bar-wrap">
<div class="option-edit-bar">
<div v-if="showOthers" class="add-option primary-color" @click="addOther">
<extra-icon type="add-square"></extra-icon>
其他____
</div>
<span
v-if="hasAdvancedConfig"
class="option-advanced-config primary-color"
@click="openOptionConfig"
>
高级设置>
</span>
<span
v-if="hasAdvancedRateConfig"
class="option-advanced-config primary-color"
@click="openRateConfig"
>
高级评分设置>
<!-- 预留作为左侧内容的扩展 -->
<!-- <span class="extra-config"><component :is="slots.extraEdit" /></span> -->
<span v-if="showAdvancedConfig && !!slots.advancedEdit" class="advanced-config">
<component :is="slots.advancedEdit" />
</span>
</div>
<OptionConfig
:options="optionList"
:showOptionDialog="optionConfigVisible"
:showOthers="showOthers"
:showLimit="false"
v-model="optionConfigVisible"
@addOther="addOther"
@optionChange="handleOptionChange"
@change="handleChange"
/>
<RateConfig
:class="isNps ? 'nps-rate-config' : ''"
dialogWidth="800px"
:min="min"
:max="max"
:rangeConfig="moduleConfig.rangeConfig"
v-model="rateConfigVisible"
:explain="explain"
@confirm="handleChange"
@visibleChange="onVisibleChange"
/>
</div>
</template>
<script setup>
import { ref, computed, inject } from 'vue'
import OptionConfig from '../AdvancedConfig/OptionConfig.vue'
import RateConfig from '../AdvancedConfig/RateConfig.vue'
import { ref, inject } from 'vue'
import ExtraIcon from '../ExtraIcon/index.vue'
import { QUESTION_TYPE } from '@/common/typeEnum'
defineProps({
optionList: {
@ -65,18 +27,16 @@ defineProps({
type: Boolean,
default: true
},
hasAdvancedConfig: {
showAdvancedConfig: {
type: Boolean,
default: true
},
hasAdvancedRateConfig: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['addOther', 'optionChange', 'change'])
const moduleConfig = inject('moduleConfig')
const slots = inject('slots')
const optionConfigVisible = ref(false)
const openOptionConfig = () => {
optionConfigVisible.value = true
@ -85,41 +45,6 @@ const openOptionConfig = () => {
const addOther = () => {
emit('addOther')
}
const handleOptionChange = (value) => {
emit('optionChange', value)
}
const handleChange = (data) => {
emit('change', data)
}
const rateConfigVisible = ref(false)
const openRateConfig = () => {
rateConfigVisible.value = true
}
const onVisibleChange = (val) => {
rateConfigVisible.value = val
}
const isNps = computed(() => {
return moduleConfig.value.type === QUESTION_TYPE.RADIO_NPS
})
const min = computed(() => {
const { min, starMin } = moduleConfig.value
return isNps.value ? min : starMin
})
const max = computed(() => {
const { max, starMax } = moduleConfig.value
return isNps.value ? max : starMax
})
const explain = computed(() => {
const { type } = moduleConfig.value
if (type == 'radio-start') return true
if (isNps.value) return false
return true
})
</script>
<style lang="scss" scoped>
@ -133,16 +58,14 @@ const explain = computed(() => {
.add-option {
display: inline-block;
margin-right: 10px;
cursor: pointer;
margin-right: 10px;
font-size: 12px;
}
.option-advanced-config {
color: #0f8a82;
.advanced-config {
float: right;
cursor: pointer;
font-size: 12px;
}
.primary-color {

View File

@ -46,9 +46,8 @@ export default defineComponent({
emit('change', { key, value })
}
const hasAdvancedConfig = ref(false)
const hasAdvancedRateConfig = ref(false)
const showOthers = ref(false)
const showAdvancedConfig = ref(false)
const showOptionEdit = ref(true)
const showOptionEditBar = ref(true)
onMounted(() => {
@ -56,17 +55,15 @@ export default defineComponent({
showOptionEdit.value = optionEdit.show
showOptionEditBar.value = optionEditBar.show
showOthers.value = optionEditBar.configure.showOthers
hasAdvancedConfig.value = Boolean(optionEditBar.configure.showAdvancedConfig)
hasAdvancedRateConfig.value = Boolean(optionEditBar.configure.showAdvancedRateConfig)
showAdvancedConfig.value = optionEditBar.configure.showAdvancedConfig
})
return {
slots,
getOptions,
hasAdvancedConfig,
hasAdvancedRateConfig,
showOptionEdit,
showOptionEditBar,
showOthers,
showAdvancedConfig,
handleAddOption,
handleAddOtherOption,
handleOptionChange,
@ -92,8 +89,7 @@ export default defineComponent({
ref="optionEditBar"
option-list={this.getOptions}
showOthers={this.showOthers}
hasAdvancedConfig={this.hasAdvancedConfig}
hasAdvancedRateConfig={this.hasAdvancedRateConfig}
showAdvancedConfig={this.showAdvancedConfig}
onAddOption={this.handleAddOption}
onAddOther={this.handleAddOtherOption}
onOptionChange={this.handleOptionChange}

View File

@ -149,7 +149,7 @@ const meta = {
show: true,
configure: {
showOthers: false,
showAdvancedRateConfig: true
showAdvancedConfig: true
}
}
}

View File

@ -107,7 +107,7 @@ const meta = {
show: true,
configure: {
showOthers: false,
showAdvancedRateConfig: true
showAdvancedConfig: true
}
}
}