refactor: 重构 management/pages/edit 目录下组件, 使用 Vue3 组合式 API 写法 (#251)

This commit is contained in:
alwayrun 2024-06-04 21:01:26 +08:00 committed by sudoooooo
parent d9255db8a9
commit cffe037269
28 changed files with 806 additions and 1020 deletions

View File

@ -14,22 +14,21 @@
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import { useStore } from 'vuex'
import { get as _get } from 'lodash-es'
import BackPanel from '../modules/generalModule/BackPanel.vue' import BackPanel from '../modules/generalModule/BackPanel.vue'
import TitlePanel from '../modules/generalModule/TitlePanel.vue' import TitlePanel from '../modules/generalModule/TitlePanel.vue'
import NavPanel from '../modules/generalModule/NavPanel.vue' import NavPanel from '../modules/generalModule/NavPanel.vue'
import HistoryPanel from '../modules/contentModule/HistoryPanel.vue' import HistoryPanel from '../modules/contentModule/HistoryPanel.vue'
import SavePanel from '../modules/contentModule/SavePanel.vue' import SavePanel from '../modules/contentModule/SavePanel.vue'
import PublishPanel from '../modules/contentModule/PublishPanel.vue' import PublishPanel from '../modules/contentModule/PublishPanel.vue'
import { useStore } from 'vuex'
import { get as _get } from 'lodash-es'
import { computed } from 'vue'
const store = useStore() const store = useStore()
const title = computed(() => _get(store.state, 'edit.schema.metaData.title')) const title = computed(() => _get(store.state, 'edit.schema.metaData.title'))
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.nav { .nav {
width: 100%; width: 100%;

View File

@ -16,28 +16,28 @@
:form-config="content" :form-config="content"
> >
<Component <Component
:is="content.type" :is="components[content.type]"
:form-config="content" :form-config="content"
:module-config="moduleConfig" :module-config="moduleConfig"
@form-change="onFormChange($event, content)" @form-change="handleFormChange($event, content)"
:class="content.contentClass" :class="content.contentClass"
/> />
</FormItem> </FormItem>
</template> </template>
<FormItem v-else :form-config="item"> <FormItem v-else :form-config="item">
<Component <Component
:is="item.type" :is="components[item.type]"
:form-config="item" :form-config="item"
:module-config="moduleConfig" :module-config="moduleConfig"
@form-change="onFormChange($event, item)" @form-change="handleFormChange($event, item)"
:class="item.contentClass" :class="item.contentClass"
/> />
</FormItem> </FormItem>
</div> </div>
</el-form> </el-form>
</template> </template>
<script setup lang="ts">
<script> import { watch, ref, shallowRef } from 'vue'
import { get as _get, pick as _pick, isFunction as _isFunction } from 'lodash-es' import { get as _get, pick as _pick, isFunction as _isFunction } from 'lodash-es'
import FormItem from '@/materials/setters/widgets/FormItem.vue' import FormItem from '@/materials/setters/widgets/FormItem.vue'
@ -45,8 +45,20 @@ import setterLoader from '@/materials/setters/setterLoader'
import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant' import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant'
interface Props {
formConfigList: Array<any>
moduleConfig: any
}
interface Emit {
(ev: typeof FORM_CHANGE_EVENT_KEY, arg: { key: string; value: any }): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emit>()
// //
const formatValue = ({ item, moduleConfig }) => { const formatValue = ({ item, moduleConfig }: any) => {
if (_isFunction(item.valueAdapter)) { if (_isFunction(item.valueAdapter)) {
const value = item.valueAdapter({ moduleConfig }) const value = item.valueAdapter({ moduleConfig })
@ -65,141 +77,130 @@ const formatValue = ({ item, moduleConfig }) => {
} }
} }
export default { const formFieldData = ref<Array<any>>([])
name: 'SettersField', const init = ref<boolean>(true)
props: { const components = shallowRef<any>({})
formConfigList: Array, // meta.js
moduleConfig: Object
},
data() {
return {
register: {},
formFieldData: [],
init: true
}
},
components: {
FormItem
},
watch: {
formConfigList: {
deep: true,
immediate: true,
async handler(newVal) {
this.init = true
if (!newVal || !newVal.length) {
return
}
// const handleFormChange = (data: any, formConfig: any) => {
await this.handleComponentRegister(newVal) if (_isFunction(formConfig?.setterAdapter)) {
const resultData = formConfig.setterAdapter(data)
this.init = false if (Array.isArray(resultData)) {
this.formFieldData = this.setValues(this.formConfigList) resultData.forEach((item) => {
} emit(FORM_CHANGE_EVENT_KEY, item)
},
// schema
moduleConfig: {
deep: true,
async handler() {
// value
if (this.init) {
return
}
// TODO: schema
this.formFieldData = this.setValues(this.formConfigList)
}
}
},
methods: {
setValues(configList = []) {
return configList
.filter((item) => {
//
if (item.type === 'Customed') {
item.content = this.setValues(item.content)
return true
}
if (!item.type) {
return false
}
if (item.hidden) {
return false
}
//
if (_isFunction(item.relyFunc)) {
return item.relyFunc(this.moduleConfig)
}
return true
})
.map((item) => {
return {
...item,
value: formatValue({ item, moduleConfig: this.moduleConfig }) //
}
})
},
async handleComponentRegister(formFieldData) {
let innerSetters = []
const setters = formFieldData.map((item) => {
if (item.type === 'Customed') {
innerSetters.push(...(item.content || []).map((content) => content.type))
}
return item.type
}) })
} else {
const settersSet = new Set([...setters, ...innerSetters]) emit(FORM_CHANGE_EVENT_KEY, resultData)
const settersArr = Array.from(settersSet)
const allSetters = settersArr.map((item) => {
return {
type: item,
path: item
}
})
try {
const comps = await setterLoader.loadComponents(allSetters)
for (const comp of comps) {
if (!comp) {
continue
}
const { type, component, err } = comp
if (!err) {
const componentName = component.name
if (!this.$options.components) {
this.$options.components = {}
}
this.$options.components[componentName] = component
this.register[type] = componentName
}
}
} catch (err) {
console.error(err)
}
},
onFormChange(data, formConfig) {
if (_isFunction(formConfig?.setterAdapter)) {
const resultData = formConfig.setterAdapter(data)
if (Array.isArray(resultData)) {
resultData.forEach((item) => {
this.$emit(FORM_CHANGE_EVENT_KEY, item)
})
} else {
this.$emit(FORM_CHANGE_EVENT_KEY, resultData)
}
} else {
this.$emit(FORM_CHANGE_EVENT_KEY, data)
}
} }
} else {
emit(FORM_CHANGE_EVENT_KEY, data)
} }
} }
</script>
const normalizationValues = (configList: Array<any> = []) => {
return configList
.filter((item: any) => {
//
if (item.type === 'Customed') {
item.content = normalizationValues(item.content)
return true
}
if (!item.type) {
return false
}
if (item.hidden) {
return false
}
//
if (_isFunction(item.relyFunc)) {
return item.relyFunc(props.moduleConfig)
}
return true
})
.map((item: any) => {
return {
...item,
value: formatValue({ item, moduleConfig: props.moduleConfig }) //
}
})
}
const registerComponents = async (formFieldData: any) => {
let innerSetters: Array<any> = []
const setters = formFieldData.map((item: any) => {
if (item.type === 'Customed') {
innerSetters.push(...(item.content || []).map((content: any) => content.type))
}
return item.type
})
const settersSet = new Set([...setters, ...innerSetters])
const settersArr = Array.from(settersSet)
const allSetters = settersArr.map((item) => ({
type: item,
path: item
}))
try {
const comps = await setterLoader.loadComponents(allSetters)
for (const comp of comps) {
if (!comp) {
continue
}
const { type, component, err } = comp
if (!err) {
components.value[type] = component
}
}
} catch (err) {
console.error(err)
}
}
watch(
() => props.formConfigList,
async (newVal: Array<any>) => {
init.value = true
if (!newVal || !newVal.length) {
return
}
await registerComponents(newVal)
init.value = false
formFieldData.value = normalizationValues(newVal)
},
{
deep: true,
immediate: true
}
)
watch(
() => props.moduleConfig,
() => {
// value
if (init.value) {
return
}
// TODO: schema
formFieldData.value = normalizationValues(props.formConfigList)
},
{
deep: true
}
)
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
.config-form { .config-form {
padding: 15px 0; padding: 15px 0;

View File

@ -13,42 +13,38 @@
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts">
<script> import { onMounted } from 'vue'
import { useStore } from 'vuex'
import { useRouter, useRoute } from 'vue-router'
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 LeftMenu from '@/management/components/LeftMenu.vue' 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 {
name: 'questionEditPage',
components: {
CommonTemplate,
Navbar,
LeftMenu
},
async created() {
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 initShowLogicEngine(this.$store.state.edit.schema.logicConf.showLogicConf || {})
} catch (error) {
ElMessage.error(error.message)
//
setTimeout(() => {
this.$router.replace({
name: 'survey'
})
}, 1000)
}
}
}
</script>
import { initShowLogicEngine } from '@/management/hooks/useShowLogicEngine'
const store = useStore()
const router = useRouter()
const route = useRoute()
onMounted(async () => {
store.commit('edit/setSurveyId', route.params.id)
try {
await store.dispatch('edit/init')
await initShowLogicEngine(store.state.edit.schema.logicConf.showLogicConf || {})
} catch (err: any) {
ElMessage.error(err.message)
setTimeout(() => {
router.replace({ name: 'survey' })
}, 1000)
}
})
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
.edit-index { .edit-index {
height: 100%; height: 100%;

View File

@ -1,6 +1,6 @@
<template> <template>
<el-popover placement="top" trigger="click" @show="onShow" :width="320"> <el-popover placement="top" trigger="click" @show="handlePopoverShow" :width="320">
<el-tabs v-model="currentTab" class="custom-tab" v-if="visible" v-loading="paneLoading"> <el-tabs v-model="currentTab" class="custom-tab" v-if="visible" v-loading="loading">
<el-tab-pane label="修改历史" name="daily" class="custom-tab-pane"> <el-tab-pane label="修改历史" name="daily" class="custom-tab-pane">
<div class="line" v-for="(his, index) in dailyList" :key="index"> <div class="line" v-for="(his, index) in dailyList" :key="index">
<span class="operator">{{ his.operator }}</span> <span class="operator">{{ his.operator }}</span>
@ -24,100 +24,77 @@
</template> </template>
</el-popover> </el-popover>
</template> </template>
<script setup lang="ts">
<script> import { ref, computed, watch } from 'vue'
import { getSurveyHistory } from '@/management/api/survey' import { useStore } from 'vuex'
import { get as _get } from 'lodash-es'
import moment from 'moment' import moment from 'moment'
//
import 'moment/locale/zh-cn' import 'moment/locale/zh-cn'
//
moment.locale('zh-cn') moment.locale('zh-cn')
import { mapState } from 'vuex' import { getSurveyHistory } from '@/management/api/survey'
import { get as _get } from 'lodash-es'
const getItemData = (item) => ({ const getItemData = (item: any) => ({
operator: item?.operator?.username || '未知用户', operator: item?.operator?.username || '未知用户',
time: moment(item.createDate).format('YYYY-MM-DD HH:mm:ss') time: moment(item.createDate).format('YYYY-MM-DD HH:mm:ss')
}) })
export default { const dailyList = ref<Array<any>>([])
name: 'HistoryPanel', const publishList = ref<Array<any>>([])
computed: { const currentTab = ref<'daily' | 'publish'>('daily')
...mapState({ const visible = ref<boolean>(false)
surveyId: (state) => _get(state, 'edit.surveyId')
}),
dailyList() {
return this.dailyHis.map(getItemData)
},
publishList() {
return this.publishHis.map(getItemData)
}
},
data() {
return {
dailyHis: [],
publishHis: [],
currentTab: 'daily',
visible: false,
paneLoading: false
}
},
watch: {
visible: {
async handler(newVal) {
if (this.visible && newVal) {
this.fetchHis()
}
}
},
currentTab: {
immediate: true,
async handler(newVal) {
if (this.visible && newVal) {
this.fetchHis()
}
}
}
},
methods: {
onShow() {
this.visible = true
},
fetchHis() {
this.paneLoading = true
switch (this.currentTab) {
case 'daily':
getSurveyHistory({
surveyId: this.surveyId,
historyType: 'dailyHis'
})
.then((dailyHis) => {
this.dailyHis = dailyHis.data || []
})
.finally(() => {
this.paneLoading = false
})
break
case 'publish': const store = useStore()
getSurveyHistory({
surveyId: this.surveyId, const queryHistories = async () => {
historyType: 'publishHis' if (dirtyMonitor.value) {
}) loading.value = true
.then((publishHis) => { dirtyMonitor.value = false
this.publishHis = publishHis.data || []
}) const surveyId = _get(store.state, 'edit.surveyId')
.finally(() => { const [dHis, pHis] = await Promise.all([
this.paneLoading = false getSurveyHistory({
}) surveyId,
break historyType: 'dailyHis'
} }),
getSurveyHistory({
surveyId,
historyType: 'publishHis'
})
]).finally(() => {
loading.value = false
})
if ((dHis.data || []).length !== dailyList.value.length) {
dailyList.value = (dHis.data || []).map(getItemData)
}
if ((pHis.data || []).length !== publishList.value.length) {
publishList.value = (pHis.data || []).map(getItemData)
} }
} }
} }
</script>
const handlePopoverShow = async () => {
visible.value = true
queryHistories()
}
const loading = ref<boolean>(false)
const dirtyMonitor = ref<boolean>(true)
const schemaUpdateTime = computed(() => _get(store.state, 'edit.schemaUpdateTime'))
watch(
schemaUpdateTime,
() => {
if (!dirtyMonitor.value) {
dirtyMonitor.value = true
}
},
{
immediate: true
}
)
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import url('@/management/styles/edit-btn.scss'); @import url('@/management/styles/edit-btn.scss');

View File

@ -1,80 +1,81 @@
<template> <template>
<el-button type="primary" :loading="isPublishing" class="publish-btn" @click="onPublish"> <el-button type="primary" :loading="isPublishing" class="publish-btn" @click="handlePublish">
发布 发布
</el-button> </el-button>
</template> </template>
<script setup lang="ts">
<script> import { ref } from 'vue'
import { get as _get } from 'lodash-es' import { useStore } from 'vuex'
import { mapState } from 'vuex' import { useRouter } from 'vue-router'
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 { publishSurvey, saveSurvey } from '@/management/api/survey' import { publishSurvey, saveSurvey } from '@/management/api/survey'
import { showLogicEngine } from '@/management/hooks/useShowLogicEngine' import { showLogicEngine } from '@/management/hooks/useShowLogicEngine'
import buildData from './buildData' import buildData from './buildData'
export default { const isPublishing = ref<boolean>(false)
name: 'PublishPanel', const store = useStore()
data() { const router = useRouter()
return {
isPublishing: false
}
},
computed: {
...mapState({
surveyId: (state) => _get(state, 'edit.surveyId')
})
},
methods: {
async onPublish() {
try {
this.updateLogicConf()
} catch (error) {
ElMessage.error('请检查逻辑配置是否有误')
return
}
const saveData = buildData(this.$store.state.edit.schema)
if (!saveData.surveyId) {
ElMessage.error('未获取到问卷id')
return
}
if (this.isPublishing) {
return
}
try { const updateLogicConf = () => {
this.isPublishing = true if (
const saveRes = await saveSurvey(saveData) showLogicEngine.value &&
if (saveRes.code !== 200) { showLogicEngine.value.rules &&
ElMessage.error(saveRes.errmsg || '问卷保存失败') showLogicEngine.value.rules.length !== 0
return ) {
} showLogicEngine.value.validateSchema()
const publishRes = await publishSurvey({ surveyId: this.surveyId }) const showLogicConf = showLogicEngine.value.toJson()
if (publishRes.code === 200) { //
ElMessage.success('发布成功') store.dispatch('edit/changeSchema', { key: 'logicConf', value: { showLogicConf } })
this.$store.dispatch('edit/getSchemaFromRemote') }
this.$router.push({ name: 'publish' }) }
} else {
ElMessage.error(`发布失败 ${publishRes.errmsg}`) const handlePublish = async () => {
} if (isPublishing.value) {
} catch (error) { return
ElMessage.error(`发布失败`) }
} finally {
this.isPublishing = false isPublishing.value = true
}
}, try {
updateLogicConf() { updateLogicConf()
if (showLogicEngine.value) { } catch (err) {
showLogicEngine.value.validateSchema() isPublishing.value = false
const showLogicConf = showLogicEngine.value.toJson() ElMessage.error('请检查逻辑配置是否有误')
// return
this.$store.dispatch('edit/changeSchema', { key: 'logicConf', value: { showLogicConf } }) }
}
const saveData = buildData(store.state.edit.schema)
if (!saveData.surveyId) {
isPublishing.value = false
ElMessage.error('未获取到问卷id')
return
}
try {
const saveRes: any = await saveSurvey(saveData)
if (saveRes.code !== 200) {
isPublishing.value = false
ElMessage.error(saveRes.errmsg || '问卷保存失败')
return
} }
const publishRes: any = await publishSurvey({ surveyId: saveData.surveyId })
if (publishRes.code === 200) {
ElMessage.success('发布成功')
store.dispatch('edit/getSchemaFromRemote')
router.push({ name: 'publish' })
} else {
ElMessage.error(`发布失败 ${publishRes.errmsg}`)
}
} catch (err) {
ElMessage.error(`发布失败`)
} finally {
isPublishing.value = false
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.publish-btn { .publish-btn {
width: 100px; width: 100px;

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="btn" @click="onSave" v-loading="isSaving"> <div class="btn" @click="handleSave" v-loading="isSaving">
<i class="iconfont icon-baocun"></i> <i class="iconfont icon-baocun"></i>
<span class="btn-txt">保存</span> <span class="btn-txt">保存</span>
<transition name="fade"> <transition name="fade">
@ -13,130 +13,126 @@
</transition> </transition>
</div> </div>
</template> </template>
<script setup lang="ts">
<script> import { ref, computed, nextTick, watch } from 'vue'
import { mapState } from 'vuex' import { useStore } from 'vuex'
import { get as _get } from 'lodash-es' import { get as _get } from 'lodash-es'
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 { saveSurvey } from '@/management/api/survey' import { saveSurvey } from '@/management/api/survey'
import buildData from './buildData'
import { showLogicEngine } from '@/management/hooks/useShowLogicEngine' import { showLogicEngine } from '@/management/hooks/useShowLogicEngine'
import buildData from './buildData'
export default { const isSaving = ref<boolean>(false)
components: {}, const isShowAutoSave = ref<boolean>(false)
name: 'SavePanel', const autoSaveStatus = ref<'succeed' | 'saving' | 'failed'>('succeed')
data() { const saveText = computed(
return { () =>
isSaving: false, ({
isShowAutoSave: false, saving: '保存中',
autoSaveStatus: 'succeed' succeed: '保存成功',
} failed: '保存失败'
}, })[autoSaveStatus.value]
computed: { )
...mapState({
schemaUpdateTime: (state) => _get(state, 'edit.schemaUpdateTime')
}),
saveText() {
const statusMap = {
saving: '保存中',
succeed: '保存成功',
failed: '保存失败'
}
return statusMap[this.autoSaveStatus]
}
},
watch: {
schemaUpdateTime() {
this.triggerAutoSave()
}
},
methods: {
triggerAutoSave() {
if (this.autoSaveStatus === 'saving') {
//
setTimeout(() => {
this.triggerAutoSave()
}, 1000)
} else {
if (this.timer) {
clearTimeout(this.timer)
}
this.timer = setTimeout(() => {
this.autoSaveStatus = 'saving'
this.isShowAutoSave = true
this.$nextTick(() => {
this.saveData()
.then((res) => {
if (res.code === 200) {
this.autoSaveStatus = 'succeed'
} else {
this.autoSaveStatus = 'failed'
}
setTimeout(() => {
this.isShowAutoSave = false
this.timer = null
}, 300)
})
.catch(() => {
this.timer = null
this.autoSaveStatus = 'failed'
this.isShowAutoSave = true
})
})
}, 2000)
}
},
updateLogicConf() {
if (showLogicEngine.value) {
showLogicEngine.value.validateSchema()
const showLogicConf = showLogicEngine.value.toJson()
//
this.$store.dispatch('edit/changeSchema', { key: 'logicConf', value: { showLogicConf } })
}
},
async saveData() {
const saveData = buildData(this.$store.state.edit.schema)
if (!saveData.surveyId) {
ElMessage.error('未获取到问卷id')
return null
}
const res = await saveSurvey(saveData)
return res
},
async onSave() {
if (this.isSaving) {
return
}
this.isShowAutoSave = false
try {
this.updateLogicConf()
} catch (error) {
// console.error(error)
ElMessage.error('请检查逻辑配置是否有误')
return
}
try { const store = useStore()
this.isSaving = true
const res = await this.saveData() const saveData = async () => {
if (res.code === 200) { const saveData = buildData(store.state.edit.schema)
ElMessage.success('保存成功')
} else { if (!saveData.surveyId) {
ElMessage.error(res.errmsg) ElMessage.error('未获取到问卷id')
} return null
} catch (error) { }
ElMessage.error('保存问卷失败')
} finally { const res = await saveSurvey(saveData)
this.isSaving = false return res
} }
}
const updateLogicConf = () => {
if (
showLogicEngine.value &&
showLogicEngine.value.rules &&
showLogicEngine.value.rules.length !== 0
) {
showLogicEngine.value.validateSchema()
const showLogicConf = showLogicEngine.value.toJson()
//
store.dispatch('edit/changeSchema', { key: 'logicConf', value: { showLogicConf } })
} }
} }
</script>
const timerHandle = ref<NodeJS.Timeout | number | null>(null)
const triggerAutoSave = () => {
if (autoSaveStatus.value === 'saving') {
setTimeout(() => triggerAutoSave(), 1000)
} else {
if (timerHandle.value) {
clearTimeout(timerHandle.value)
timerHandle.value = null
}
timerHandle.value = setTimeout(() => {
autoSaveStatus.value = 'saving'
isShowAutoSave.value = true
nextTick(async () => {
try {
const res: any = await saveData()
if (res.code === 200) {
autoSaveStatus.value = 'succeed'
} else {
autoSaveStatus.value = 'failed'
}
setTimeout(() => {
isShowAutoSave.value = false
timerHandle.value = null
}, 300)
} catch (err) {
autoSaveStatus.value = 'failed'
isShowAutoSave.value = true
}
})
}, 2000)
}
}
const handleSave = async () => {
if (isSaving.value) {
return
}
isSaving.value = true
isShowAutoSave.value = false
try {
updateLogicConf()
} catch (error) {
isSaving.value = false
ElMessage.error('请检查逻辑配置是否有误')
return
}
try {
const res: any = await saveData()
if (res.code === 200) {
ElMessage.success('保存成功')
} else {
ElMessage.error(res.errmsg)
}
} catch (error) {
ElMessage.error('保存问卷失败')
} finally {
isSaving.value = false
}
}
const schemaUpdateTime = computed(() => _get(store.state, 'edit.schemaUpdateTime'))
watch(schemaUpdateTime, () => {
triggerAutoSave()
})
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import url('@/management/styles/edit-btn.scss'); @import url('@/management/styles/edit-btn.scss');

View File

@ -1,16 +1,12 @@
<template> <template>
<div class="back-btn" @click="onBack"> <div class="back-btn" @click="handleNavigateHome">
<i class="iconfont icon-fanhui"></i> <i class="iconfont icon-fanhui"></i>
<span>返回</span> <span>返回</span>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const onBack = () => { const handleNavigateHome = () => window.open('/survey', '_self')
window.open('/survey', '_self')
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.back-btn { .back-btn {
height: 100%; height: 100%;

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="content"> <div class="content">
<template v-for="btnItem in btnList" :key="btnItem.key"> <template v-for="route in routes" :key="route.key">
<router-link <router-link
:to="{ name: btnItem.router }" :to="{ name: route.router }"
replace replace
v-slot="{ href, navigate, isActive, isExactActive }" v-slot="{ href, navigate, isActive, isExactActive }"
custom custom
@ -10,25 +10,22 @@
<div <div
:class="[ :class="[
'navbar-btn', 'navbar-btn',
(isActive && ['skinsettings', 'edit'].includes(btnItem.key)) || isExactActive (isActive && ['skinsettings', 'edit'].includes(route.key)) || isExactActive
? 'router-link-exact-active' ? 'router-link-exact-active'
: '' : ''
]" ]"
> >
<i class="iconfont" :class="[btnItem.icon]"></i> <i class="iconfont" :class="[route.icon]"></i>
<a :href="href" @click="navigate" <a :href="href" @click="navigate"
><span>{{ btnItem.text }}</span></a ><span>{{ route.text }}</span></a
> >
</div> </div>
</router-link> </router-link>
</template> </template>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive } from 'vue' const routes = [
const btnList = reactive([
{ {
icon: 'icon-wenjuanbianji', icon: 'icon-wenjuanbianji',
text: '问卷编辑', text: '问卷编辑',
@ -50,9 +47,8 @@ const btnList = reactive([
key: 'skinsettings', key: 'skinsettings',
next: true next: true
} }
]) ]
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.content { .content {
display: flex; display: flex;

View File

@ -46,7 +46,6 @@ const hideFullTitle = () => {
tooltipVisible.value = false tooltipVisible.value = false
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.title-container { .title-container {
position: relative; position: relative;

View File

@ -8,26 +8,14 @@
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</template> </template>
<script setup lang="ts">
import { ref } from 'vue'
<script>
import TypeList from './components/TypeList.vue' import TypeList from './components/TypeList.vue'
import QuestionCatalog from './components/QuestionCatalog.vue' import QuestionCatalog from './components/QuestionCatalog.vue'
export default { const tabSelected = ref<string>('0')
name: 'CatalogPanel',
data() {
return {
tabSelected: '0'
}
},
components: {
TypeList,
QuestionCatalog
},
methods: {}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.tab-box { .tab-box {
width: 300px; width: 300px;

View File

@ -11,47 +11,31 @@
class="question-config-form" class="question-config-form"
:form-config-list="formConfigList" :form-config-list="formConfigList"
:module-config="moduleConfig" :module-config="moduleConfig"
@form-change="onFormChange" @form-change="handleFormChange"
/> />
</template> </template>
</div> </div>
</template> </template>
<script setup lang="ts">
import { computed } from 'vue'
import { useStore } from 'vuex'
<script>
import SetterField from '@/management/pages/edit/components/SetterField.vue' import SetterField from '@/management/pages/edit/components/SetterField.vue'
import { mapGetters } from 'vuex'
export default { const store = useStore()
name: 'SetterPanel',
data() { const currentEditOne = computed(() => store.state?.edit?.currentEditOne)
return { const formConfigList = computed(() => store.getters['edit/formConfigList'])
tabSelected: '0' const moduleConfig = computed(() => store.getters['edit/moduleConfig'])
} const currentEditKey = computed(() => store.getters['edit/currentEditKey'])
}, const currentEditMeta = computed(() => store.getters['edit/currentEditMeta'])
computed: {
currentEditOne() { const handleFormChange = (data: any) => {
return this.$store.state?.edit?.currentEditOne const { key, value } = data
}, const resultKey = `${currentEditKey.value}.${key}`
...mapGetters({ store.dispatch('edit/changeSchema', { key: resultKey, value })
formConfigList: 'edit/formConfigList',
moduleConfig: 'edit/moduleConfig',
currentEditKey: 'edit/currentEditKey',
currentEditMeta: 'edit/currentEditMeta'
})
},
components: {
SetterField
},
methods: {
onFormChange(data) {
const { key, value } = data
const resultKey = `${this.currentEditKey}.${key}`
this.$store.dispatch('edit/changeSchema', { key: resultKey, value })
}
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.setter-wrapper { .setter-wrapper {
width: 360px; width: 360px;

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="question-catalog-item" @click="onSelect()"> <div class="question-catalog-item" @click="handleSelect()">
<div class="iconfont icon-tuodong draggHandle"></div> <div class="iconfont icon-tuodong draggHandle"></div>
<div class="catalog-item-body"> <div class="catalog-item-body">
<div class="catalog-item-index" v-if="showIndex">{{ indexNumber }}.</div> <div class="catalog-item-index" v-if="showIndex">{{ indexNumber }}.</div>
@ -7,37 +7,29 @@
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts">
interface Props {
title: string
indexNumber: string | number
showIndex: boolean
}
<script> interface Emit {
export default { (ev: 'select'): void
name: 'CatalogItem', }
data() {
return {} withDefaults(defineProps<Props>(), {
}, title: '',
computed: {}, indexNumber: '',
props: { showIndex: false
title: { })
type: String,
default: '' const emit = defineEmits<Emit>()
},
indexNumber: { const handleSelect = () => {
type: [String, Number], emit('select')
default: ''
},
showIndex: {
type: Boolean,
default: false
}
},
components: {},
methods: {
onSelect() {
this.$emit('select')
}
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.question-catalog-item { .question-catalog-item {
position: relative; position: relative;

View File

@ -2,7 +2,7 @@
<div class="question-catalog-wrapper"> <div class="question-catalog-wrapper">
<draggable <draggable
:list="renderData" :list="renderData"
@end="onDragEnd" @end="handleDragEnd"
itemKey="field" itemKey="field"
handle=".draggHandle" handle=".draggHandle"
host-class="catalog-item-ghost" host-class="catalog-item-ghost"
@ -12,50 +12,43 @@
:title="element.title" :title="element.title"
:indexNumber="element.indexNumber" :indexNumber="element.indexNumber"
:showIndex="element.showIndex" :showIndex="element.showIndex"
@select="onSelect(index)" @select="handleSelect(index)"
/> />
</template> </template>
</draggable> </draggable>
</div> </div>
</template> </template>
<script setup lang="ts">
<script> import { computed } from 'vue'
import { useStore } from 'vuex'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import CatalogItem from './CatalogItem.vue' import CatalogItem from './CatalogItem.vue'
import { filterQuestionPreviewData } from '@/management/utils/index' import { filterQuestionPreviewData } from '@/management/utils/index'
export default { const store = useStore()
name: 'QuestionCatalog', const renderData = computed(() => {
data() { const questions = store.state.edit.schema.questionDataList
return {} return filterQuestionPreviewData(questions) || []
}, })
computed: {
questionDataList() { const handleDragEnd = ({ newIndex, oldIndex }: any) => {
return this.$store.state.edit.schema.questionDataList const currentActivityKey = store.state.edit.currentEditOne
},
renderData() { if (currentActivityKey === oldIndex) {
return filterQuestionPreviewData(this.questionDataList) || [] handleSelect(newIndex)
}
},
components: {
draggable,
CatalogItem
},
methods: {
onDragEnd(data) {
const { newIndex, oldIndex } = data
this.$store.dispatch('edit/moveQuestion', {
index: oldIndex,
range: newIndex - oldIndex
})
},
onSelect(index) {
this.$store.commit('edit/setCurrentEditOne', index)
}
} }
store.dispatch('edit/moveQuestion', {
index: oldIndex,
range: newIndex - oldIndex
})
}
const handleSelect = (idx: number) => {
store.commit('edit/setCurrentEditOne', idx)
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.question-catalog-wrapper { .question-catalog-wrapper {
padding-bottom: 400px; // padding-bottom: 400px; //

View File

@ -2,7 +2,7 @@
<div class="question-config"> <div class="question-config">
<div class="question-config-wrapper"> <div class="question-config-wrapper">
<div class="question-config-main"> <div class="question-config-main">
<div v-for="form of renderData" :key="form.key" class="config-item"> <div v-for="form of setterList" :key="form.key" class="config-item">
<div class="config-title"> <div class="config-title">
<span> <span>
{{ form.title }} {{ form.title }}
@ -16,17 +16,17 @@
> >
<template v-for="(item, index) in form.formList"> <template v-for="(item, index) in form.formList">
<FormItem <FormItem
v-if="item.type && !item.hidden && Boolean(register[item.type])" v-if="item.type && !item.hidden && Boolean(registerTypes[item.type])"
:key="index" :key="index"
:form-config="item" :form-config="item"
:style="item.style" :style="item.style"
> >
<Component <Component
v-if="Boolean(register[item.type])" v-if="Boolean(registerTypes[item.type])"
:is="item.type" :is="components[item.type]"
:module-config="form.dataConfig" :module-config="form.dataConfig"
:form-config="item" :form-config="item"
@form-change="onFormChange" @form-change="handleFormChange"
/> />
</FormItem> </FormItem>
</template> </template>
@ -36,92 +36,83 @@
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts">
import { computed, ref, onMounted, shallowRef } from 'vue'
import { useStore } from 'vuex'
import { cloneDeep as _cloneDeep, isArray as _isArray, get as _get } from 'lodash-es'
<script>
import baseConfig from './config/baseConfig' import baseConfig from './config/baseConfig'
import baseFormConfig from './config/baseFormConfig' import baseFormConfig from './config/baseFormConfig'
import FormItem from '@/materials/setters/widgets/FormItem.vue' import FormItem from '@/materials/setters/widgets/FormItem.vue'
import setterLoader from '@/materials/setters/setterLoader' import setterLoader from '@/materials/setters/setterLoader'
import { cloneDeep as _cloneDeep, isArray as _isArray, get as _get } from 'lodash-es'
export default { const formConfigList = ref<Array<any>>([])
name: 'SettingPanel', const components = shallowRef<any>({})
components: { const registerTypes = ref<any>({})
FormItem const setterList = computed(() => {
}, const list = _cloneDeep(formConfigList.value)
data() {
return {
formConfigList: [],
register: {}
}
},
methods: {
onFormChange(data) {
this.$store.dispatch('edit/changeSchema', {
key: data.key,
value: data.value
})
}
},
computed: {
allSetters() {
const formList = this.formConfigList.map((item) => item.formList).flat()
const typeList = formList.map((item) => ({
type: item.type,
path: item.path || item.type
}))
return typeList return list.map((form) => {
}, const dataConfig: any = {}
renderData() {
// todo: 1formConfigvalue2dataConfig
const formConfigList = _cloneDeep(this.formConfigList)
return formConfigList.map((form) => { for (const formItem of form.formList) {
const dataConfig = {} const formKey = formItem.key ? formItem.key : formItem.keys
for (const formItem of form.formList) { let formValue
const formKey = formItem.key ? formItem.key : formItem.keys if (_isArray(formKey)) {
let formValue formValue = []
if (_isArray(formKey)) { for (const key of formKey) {
formValue = [] const val = _get(store.state.edit.schema, key, formItem.value)
for (const key of formKey) { formValue.push(val)
const val = _get(this.$store.state.edit.schema, key, formItem.value) dataConfig[key] = val
formValue.push(val)
dataConfig[key] = val
}
} else {
formValue = _get(this.$store.state.edit.schema, formKey, formItem.value)
dataConfig[formKey] = formValue
}
formItem.value = formValue
} }
form.dataConfig = dataConfig } else {
return form formValue = _get(store.state.edit.schema, formKey, formItem.value)
}) dataConfig[formKey] = formValue
}
formItem.value = formValue
} }
},
async created() {
this.formConfigList = baseConfig.map((item) => {
return {
...item,
formList: item.formList.map((key) => baseFormConfig[key]).filter((config) => !!config)
}
})
const comps = await setterLoader.loadComponents(this.allSetters) form.dataConfig = dataConfig
for (const comp of comps) {
if (!comp) { return form
continue })
} })
const { type, component, err } = comp
if (!err) { const store = useStore()
const componentName = component.name const handleFormChange = (data: any) => {
this.$options.components[componentName] = component store.dispatch('edit/changeSchema', {
this.register[type] = componentName key: data.key,
} value: data.value
})
}
onMounted(async () => {
formConfigList.value = baseConfig.map((item) => ({
...item,
formList: item.formList.map((key) => (baseFormConfig as any)[key]).filter((config) => !!config)
}))
const formList = formConfigList.value.map((item) => item.formList).flat()
const typeList = formList.map((item) => ({
type: item.type,
path: item.path || item.type
}))
const comps = await setterLoader.loadComponents(typeList)
for (const comp of comps) {
if (!comp) {
continue
}
const { type, component, err } = comp
if (!err) {
const componentName = component.name
components.value[type] = component
registerTypes.value[type] = componentName
} }
} }
} })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -4,24 +4,15 @@
<p class="title-msg" v-safe-html="resultText"></p> <p class="title-msg" v-safe-html="resultText"></p>
</div> </div>
</template> </template>
<script setup lang="ts">
import { computed } from 'vue'
<script> interface Props {
export default { moduleConfig: any
name: 'OverTime',
props: {
moduleConfig: {
type: Object,
required: true
}
},
computed: {
resultText() {
return this.moduleConfig?.submitConf?.msgContent?.msg_9001 || '问卷已过期'
}
}
} }
const props = defineProps<Props>()
const resultText = computed(() => props.moduleConfig?.msgContent?.msg_9001 || '问卷已过期')
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.over-time { .over-time {
text-align: center; text-align: center;

View File

@ -9,24 +9,15 @@
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts">
import { computed } from 'vue'
<script> interface Props {
export default { moduleConfig: any
name: 'SuccessContent',
props: {
moduleConfig: {
type: Object,
required: true
}
},
computed: {
successText() {
return this.moduleConfig?.submitConf?.msgContent?.msg_200 || ''
}
}
} }
const props = defineProps<Props>()
const successText = computed(() => props.moduleConfig?.msgContent?.msg_200 || '')
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/*成功页面跳转全屏展示浮层*/ /*成功页面跳转全屏展示浮层*/
.suc-page { .suc-page {

View File

@ -6,7 +6,7 @@
v-for="(status, index) in statusList" v-for="(status, index) in statusList"
:key="index" :key="index"
class="status-item" class="status-item"
@click="filterDisabledStatus({ type: status.type })" @click="handleChangePreview({ type: status.type })"
> >
<span>{{ status.title }}</span> <span>{{ status.title }}</span>
<div class="preview-item"> <div class="preview-item">
@ -16,41 +16,32 @@
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts">
<script> import { useStore } from 'vuex'
import { mapMutations } from 'vuex'
import { EDIT_STATUS_MAP } from '../enum' import { EDIT_STATUS_MAP } from '../enum'
export default { const store = useStore()
name: 'CatalogPanel', const statusList = [
data() { {
return { type: EDIT_STATUS_MAP.SUCCESS,
statusList: [ title: '提交成功',
{ previewImg: '/imgs/icons/success.webp'
type: EDIT_STATUS_MAP.SUCCESS,
title: '提交成功',
previewImg: '/imgs/icons/success.webp'
},
{
type: EDIT_STATUS_MAP.OVERTIME,
title: '问卷过期',
previewImg: '/imgs/icons/overtime.webp'
}
]
}
}, },
computed: {}, {
methods: { type: EDIT_STATUS_MAP.OVERTIME,
...mapMutations({ title: '问卷过期',
changeStatusPreview: 'edit/changeStatusPreview' previewImg: '/imgs/icons/overtime.webp'
}), }
filterDisabledStatus(data) { ]
this.changeStatusPreview(data)
} const handleChangePreview = (data: any) => {
const currentStatus = store.state?.edit?.currentEditStatus
if (currentStatus && currentStatus !== data.type) {
store.commit('edit/changeStatusPreview', data)
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.tab-box { .tab-box {
width: 300px; width: 300px;

View File

@ -2,43 +2,35 @@
<div class="result-config-preview"> <div class="result-config-preview">
<div class="result-page-wrap"> <div class="result-page-wrap">
<div class="result-page"> <div class="result-page">
<component :is="currentEditStatus" :key="currentEditStatus" :module-config="moduleConfig" /> <component
:is="components[currentEditStatus]"
:key="currentEditStatus"
:module-config="moduleConfig"
/>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts">
import { computed } from 'vue'
import { useStore } from 'vuex'
import { get as _get } from 'lodash-es'
<script>
import { mapState } from 'vuex'
import SuccessContent from '../components/SuccessContent.vue' import SuccessContent from '../components/SuccessContent.vue'
import OverTime from '../components/OverTime.vue' import OverTime from '../components/OverTime.vue'
import { EDIT_STATUS_MAP } from '../enum' import { EDIT_STATUS_MAP } from '../enum'
import { get as _get } from 'lodash-es'
export default { const components = {
name: 'PreviewPanel', [EDIT_STATUS_MAP.SUCCESS]: SuccessContent,
props: {}, [EDIT_STATUS_MAP.OVERTIME]: OverTime
data() {
return {}
},
computed: {
...mapState({
currentEditStatus: (state) => state.edit.currentEditStatus,
submitConf: (state) => _get(state, 'edit.schema.submitConf')
}),
moduleConfig() {
return {
submitConf: this.submitConf
}
}
},
components: {
[EDIT_STATUS_MAP.SUCCESS]: SuccessContent,
[EDIT_STATUS_MAP.OVERTIME]: OverTime
}
} }
</script>
const store = useStore()
const currentEditStatus = computed(() => store.state?.edit?.currentEditStatus)
const moduleConfig = computed(() => {
return _get(store.state, 'edit.schema.submitConf')
})
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
.result-config-preview { .result-config-preview {
width: 100%; width: 100%;

View File

@ -4,134 +4,98 @@
{{ currentEditText }} {{ currentEditText }}
</div> </div>
<el-form class="question-config-form" label-position="top" @submit.prevent> <el-form class="question-config-form" label-position="top" @submit.prevent>
<template v-for="(item, index) in formFieldData" :key="index"> <template v-for="(item, index) in formFields" :key="index">
<FormItem <FormItem
v-if="item.type && !item.hidden && Boolean(register[item.type])" v-if="item.type && !item.hidden && Boolean(registerTypes[item.type])"
:form-config="item" :form-config="item"
:style="item.style" :style="item.style"
> >
<Component <Component
v-if="Boolean(register[item.type])" v-if="Boolean(registerTypes[item.type])"
:is="item.type" :is="components[item.type]"
:module-config="moduleConfig" :module-config="moduleConfig"
:form-config="item" :form-config="item"
@form-change="onFormChange" @form-change="handleFormChange"
/> />
</FormItem> </FormItem>
</template> </template>
</el-form> </el-form>
</div> </div>
</template> </template>
<script setup lang="ts">
import { computed, ref, shallowRef } from 'vue'
import { useStore } from 'vuex'
import { get as _get } from 'lodash-es'
<script>
import FormItem from '@/materials/setters/widgets/FormItem.vue' import FormItem from '@/materials/setters/widgets/FormItem.vue'
import setterLoader from '@/materials/setters/setterLoader' import setterLoader from '@/materials/setters/setterLoader'
import statusConfig from '../config/statusConfig' import statusConfig from '../config/statusConfig'
import { mapState } from 'vuex'
import { get as _get, pick as _pick } from 'lodash-es'
const textMap = { const textMap = {
Success: '提交成功页面配置', Success: '提交成功页面配置',
OverTime: '问卷过期页面配置' OverTime: '问卷过期页面配置'
} }
export default { const store = useStore()
name: 'SetterPanel',
components: { const components = shallowRef<any>({})
FormItem const registerTypes = ref<any>({})
}, const moduleConfig = computed(() => _get(store.state, 'edit.schema.submitConf'))
data() { const currentEditText = computed(() => (textMap as any)[store.state.edit.currentEditStatus])
const formFields = computed(() => {
const currentStatus = store.state.edit.currentEditStatus
const formList = (statusConfig as any)[currentStatus] || []
const list = formList.map((item: any) => {
const value = _get(moduleConfig.value, item.key, item.value)
return { ...item, value }
})
registerComponents(list)
return list
})
const handleFormChange = ({ key, value }: any) => {
store.dispatch('edit/changeSchema', {
key: `submitConf.${key}`,
value
})
}
const registerComponents = async (formFieldData: any) => {
const setters = formFieldData.map((item: any) => item.type)
const settersSet = new Set(setters)
const settersArr = Array.from(settersSet)
const allSetters = settersArr.map((item) => {
return { return {
register: {} type: item,
path: item
} }
}, })
computed: {
formFieldData() { try {
const formList = statusConfig[this.currentEditStatus] || [] const comps = await setterLoader.loadComponents(allSetters)
return formList.map((item) => {
const value = _get(this.moduleConfig, item.key, item.value) for (const comp of comps) {
return { if (!comp) {
...item, continue
value }
}
}) const { type, component, err } = comp
},
currentEditText() { if (!err) {
return textMap[this.currentEditStatus] || '' const componentName = component.name
},
...mapState({ components.value[type] = component
currentEditStatus: (state) => state.edit.currentEditStatus, registerTypes.value[type] = componentName
submitConf: (state) => _get(state, 'edit.schema.submitConf')
}),
moduleConfig() {
return this.submitConf
}
},
watch: {
formFieldData: {
immediate: true,
handler(newVal) {
if (Array.isArray(newVal)) {
this.handleComponentRegister(newVal)
}
} }
} }
}, } catch (err) {
methods: { console.error(err)
async handleComponentRegister(formFieldData) {
const setters = formFieldData.map((item) => item.type)
const settersSet = new Set(setters)
const settersArr = Array.from(settersSet)
const allSetters = settersArr.map((item) => {
return {
type: item,
path: item
}
})
try {
const comps = await setterLoader.loadComponents(allSetters)
for (const comp of comps) {
if (!comp) {
continue
}
const { type, component, err } = comp
if (!err) {
const componentName = component.name
this.$options.components[componentName] = component
this.register[type] = componentName
}
}
} catch (err) {
console.error(err)
}
},
getValueFromModuleConfig(item) {
const { key, keys } = item
const moduleConfig = this.moduleConfig
let result = item
if (key) {
result = {
...item,
value: _get(moduleConfig, key, item.value)
}
}
if (keys) {
result = {
...item,
value: _pick(moduleConfig, keys)
}
}
return result
},
onFormChange(data) {
const { key, value } = data
const resultKey = `submitConf.${key}`
this.$store.dispatch('edit/changeSchema', { key: resultKey, value })
}
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.question-edit-form { .question-edit-form {
width: 360px; width: 360px;

View File

@ -8,7 +8,7 @@
:class="[groupName === item.value ? 'current' : '', 'tag']" :class="[groupName === item.value ? 'current' : '', 'tag']"
type="info" type="info"
:key="item.value" :key="item.value"
@click="() => changeGroup(item.value)" @click="() => handleChangeGroup(item.value)"
> >
{{ item.label }} {{ item.label }}
</el-tag> </el-tag>
@ -25,75 +25,63 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { mapActions } from 'vuex' import { computed, ref } from 'vue'
import { useStore } from 'vuex'
import skinPresets from '@/management/config/skinPresets.js' import skinPresets from '@/management/config/skinPresets.js'
export default {
name: 'CatalogPanel',
data() {
return {
skinPresets: [],
groupName: 'temp'
}
},
computed: {
bannerList() {
return this.$store?.state?.bannerList || []
},
groupList() {
return Object.keys(this.bannerList).map((key) => {
return {
label: this.bannerList[key].name,
value: key
}
})
},
currentBannerList() {
const arr = Object.keys(this.bannerList)
.map((key) => {
return this.bannerList[key]
})
.map((data) => {
return data.list.map((item) => {
item.group = data.key
return item
})
})
const allbanner = arr.reduce((acc, curr) => {
return acc.concat(curr)
}, [])
return allbanner.filter((item) => {
if (this.groupName === 'temp') {
return true
} else {
return item.group === this.groupName
}
})
}
},
mounted() {},
methods: {
...mapActions({
changeThemePreset: 'edit/changeThemePreset'
}),
changeGroup(value) {
this.groupName = value
},
changePreset(banner) {
const name = banner.group + '-' + banner.title
let presets = {
'bannerConf.bannerConfig.bgImage': banner.src,
'skinConf.themeConf.color': '#FAA600',
'skinConf.backgroundConf.color': '#fff'
}
if (skinPresets[name]) {
presets = Object.assign(presets, skinPresets[name])
}
this.changeThemePreset(presets) const store = useStore()
const groupName = ref<string>('temp')
const bannerList = computed(() => store?.state?.bannerList || [])
const groupList = computed(() =>
Object.keys(bannerList.value).map((key) => ({
label: bannerList.value[key].name,
value: key
}))
)
const currentBannerList = computed(() => {
const arr = Object.keys(bannerList.value)
.map((key) => {
return bannerList.value[key]
})
.map((data) => {
return data.list.map((item: any) => {
item.group = data.key
return item
})
})
const allbanner = arr.reduce((acc, curr) => {
return acc.concat(curr)
}, [])
return allbanner.filter((item: any) => {
if (groupName.value === 'temp') {
return true
} else {
return item.group === groupName.value
} }
})
})
const handleChangeGroup = (value: string) => {
groupName.value = value
}
const changePreset = (banner: any) => {
const name = banner.group + '-' + banner.title
let presets = {
'bannerConf.bannerConfig.bgImage': banner.src,
'skinConf.themeConf.color': '#FAA600',
'skinConf.backgroundConf.color': '#fff'
} }
if ((skinPresets as any)[name]) {
presets = Object.assign(presets, (skinPresets as any)[name])
}
store.dispatch('edit/changeThemePreset', presets)
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -14,7 +14,7 @@
:module-config="_get(schema, collapse.key, {})" :module-config="_get(schema, collapse.key, {})"
@form-change=" @form-change="
(key) => { (key) => {
onFormChange(key, collapse.key) handleFormChange(key, collapse.key)
} }
" "
/> />
@ -23,37 +23,24 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { computed, ref } from 'vue'
import { useStore } from 'vuex'
import { get as _get } from 'lodash-es'
import skinConfig from '@/management/config/setterConfig/skinConfig' import skinConfig from '@/management/config/setterConfig/skinConfig'
import SetterField from '@/management/pages/edit/components/SetterField.vue' import SetterField from '@/management/pages/edit/components/SetterField.vue'
import { mapState } from 'vuex'
import { get as _get } from 'lodash-es' const store = useStore()
export default { const collapse = ref<string>('')
name: 'SetterPanel', const schema = computed(() => _get(store.state, 'edit.schema'))
components: {
SetterField const handleFormChange = (data: any, collapse: string) => {
}, const { key, value } = data
data() { const currentEditKey = `${collapse}`
return { const resultKey = `${currentEditKey}.${key}`
collapse: '',
skinConfig store.dispatch('edit/changeSchema', { key: resultKey, value })
}
},
computed: {
...mapState({
skinConf: (state) => _get(state, 'edit.schema.skinConf'),
schema: (state) => _get(state, 'edit.schema')
})
},
methods: {
_get,
onFormChange(data, collapse) {
const { key, value } = data
const currentEditKey = `${collapse}`
const resultKey = `${currentEditKey}.${key}`
this.$store.dispatch('edit/changeSchema', { key: resultKey, value })
}
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -5,15 +5,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, provide } from 'vue' import { computed, provide } from 'vue'
import RulePanel from '../../modules/logicModule/RulePanel.vue'
import { filterQuestionPreviewData } from '@/management/utils/index'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import RulePanel from '../../modules/logicModule/RulePanel.vue'
import { filterQuestionPreviewData } from '@/management/utils/index'
const store = useStore() const store = useStore()
const questionDataList = computed(() => { const questionDataList = computed(() => {
return store.state.edit.schema.questionDataList return store.state.edit.schema.questionDataList
}) })
const renderData = computed(() => { const renderData = computed(() => {
return filterQuestionPreviewData(cloneDeep(questionDataList.value)) return filterQuestionPreviewData(cloneDeep(questionDataList.value))
}) })

View File

@ -11,20 +11,11 @@
</template> </template>
</CommonTemplate> </CommonTemplate>
</template> </template>
<script> <script setup lang="ts">
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 {
name: 'editIndex',
components: {
CommonTemplate,
CatalogPanel,
PreviewPanel,
SetterPanel
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.navbar { .navbar {

View File

@ -3,45 +3,46 @@
<div class="navbar-tab"> <div class="navbar-tab">
<el-radio-group v-model="activeRouter"> <el-radio-group v-model="activeRouter">
<el-radio-button <el-radio-button
v-for="btnItem in btnList" v-for="item in routes"
:key="btnItem.router" :key="item.router"
:label="btnItem.text" :label="item.text"
:value="btnItem.router" :value="item.router"
/> />
</el-radio-group> </el-radio-group>
</div> </div>
<router-view></router-view> <router-view></router-view>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
export default { import { watch, ref } from 'vue'
name: 'QuestionPage', import { useRouter, useRoute } from 'vue-router'
props: {},
data() { const routes = [
return { {
activeRouter: this.$route.name, text: '内容设置',
btnList: [ router: 'QuestionEditIndex',
{ key: 'questionEdit'
text: '内容设置',
router: 'QuestionEditIndex',
key: 'questionEdit'
},
{
text: '逻辑设置',
router: 'LogicIndex',
key: 'logicEdit'
}
]
}
}, },
watch: { {
activeRouter: { text: '逻辑设置',
handler(val) { router: 'LogicIndex',
this.$router.push({ name: val }) key: 'logicEdit'
}
}
} }
} ]
const router = useRouter()
const route = useRoute()
const activeRouter = ref(route.name)
watch(
activeRouter,
(val: any) => {
router.push({ name: val })
},
{
immediate: true
}
)
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.question-content { .question-content {

View File

@ -3,17 +3,9 @@
<SettingPanel></SettingPanel> <SettingPanel></SettingPanel>
</div> </div>
</template> </template>
<script setup lang="ts">
<script>
import SettingPanel from '../../modules/settingModule/SettingPanel.vue' import SettingPanel from '../../modules/settingModule/SettingPanel.vue'
export default {
name: 'SettingPage',
components: {
SettingPanel
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.setting-page { .setting-page {
width: 100%; width: 100%;

View File

@ -11,24 +11,20 @@
</template> </template>
</CommonTemplate> </CommonTemplate>
</template> </template>
<script> <script setup lang="ts">
import { onMounted } from 'vue'
import { useStore } from 'vuex'
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'
export default { const store = useStore()
name: 'ContentPage',
components: { onMounted(() => {
CommonTemplate, store.dispatch('getBannerData')
CatalogPanel, })
PreviewPanel,
SetterPanel
},
created() {
this.$store.dispatch('getBannerData')
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.navbar { .navbar {

View File

@ -11,20 +11,9 @@
</template> </template>
</CommonTemplate> </CommonTemplate>
</template> </template>
<script setup lang="ts">
<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'
export default {
name: 'ResultPage',
components: {
CommonTemplate,
ResultCatalog,
ResultPreview,
ResultSetter
}
}
</script> </script>

View File

@ -3,46 +3,47 @@
<div class="navbar-tab"> <div class="navbar-tab">
<el-radio-group v-model="activeRouter"> <el-radio-group v-model="activeRouter">
<el-radio-button <el-radio-button
v-for="btnItem in btnList" v-for="item in routes"
:key="btnItem.router" :key="item.router"
:label="btnItem.text" :label="item.text"
:value="btnItem.router" :value="item.router"
/> />
</el-radio-group> </el-radio-group>
</div> </div>
<router-view></router-view> <router-view></router-view>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
export default { import { watch, ref } from 'vue'
name: 'skinPage', import { useRouter, useRoute } from 'vue-router'
props: {},
data() { const routes = [
return { {
activeRouter: this.$route.name, text: '内容页',
btnList: [ router: 'QuestionSkinSetting',
{ key: 'skinsettings',
text: '内容页', next: true
router: 'QuestionSkinSetting',
key: 'skinsettings',
next: true
},
{
text: '结果页',
router: 'QuestionEditResultConfig',
key: 'status'
}
]
}
}, },
watch: { {
activeRouter: { text: '结果页',
handler(val) { router: 'QuestionEditResultConfig',
this.$router.push({ name: val }) key: 'status'
}
}
} }
} ]
const router = useRouter()
const route = useRoute()
const activeRouter = ref(route.name)
watch(
activeRouter,
(val: any) => {
router.push({ name: val })
},
{
immediate: true
}
)
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.skin-content { .skin-content {