Merge branch 'develop' into feature/jumpLogic
This commit is contained in:
commit
965dc89c54
3
.github/workflows/server-lint.yml
vendored
3
.github/workflows/server-lint.yml
vendored
@ -37,6 +37,3 @@ jobs:
|
|||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: cd server && npm run lint
|
run: cd server && npm run lint
|
||||||
|
|
||||||
- name: Format
|
|
||||||
run: cd server && npm run format
|
|
||||||
|
3
.github/workflows/web-lint.yml
vendored
3
.github/workflows/web-lint.yml
vendored
@ -40,6 +40,3 @@ jobs:
|
|||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: cd web && npm run lint
|
run: cd web && npm run lint
|
||||||
|
|
||||||
- name: Format
|
|
||||||
run: cd web && npm run format
|
|
||||||
|
16
README.md
16
README.md
@ -29,12 +29,10 @@
|
|||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
  **XIAOJUSURVEY**是一套轻量、安全的**问卷系统基座**,提供面向个人和企业的一站式产品级解决方案,快速满足各类线上调研场景。
|
  **XIAOJUSURVEY**是一套轻量、安全的问卷系统,提供面向个人和企业的一站式产品级解决方案,用于构建各类问卷、考试、测评和复杂表单,快速满足各类线上调研场景。
|
||||||
|
|
||||||
  内部系统已沉淀 40+种题型,累积精选模板 100+,适用于市场调研、客户满意度调研、在线考试、投票、报道、测评等众多场景。数据能力上,经过上亿量级打磨,沉淀了分题统计、交叉分析、多渠道分析等在线报表能力,快速满足专业化分析。
|
  内部系统已沉淀 40+种题型,累积精选模板 100+,适用于市场调研、客户满意度调研、在线考试、投票、报道、测评等众多场景。数据能力上,经过上亿量级打磨,沉淀了分题统计、交叉分析、多渠道分析等在线报表能力,快速满足专业化分析。
|
||||||
|
|
||||||
  开源项目以打造**调研基座**为核心,围绕**平台能力**、**工程架构**、**研发体系**进行建设,大家可以「快速」打造「专属」问卷系统:[快速了解生态发展理念](https://xiaojusurvey.didi.cn/docs/next/community/%E7%94%9F%E6%80%81%E5%BB%BA%E8%AE%BE)
|
|
||||||
|
|
||||||
# 功能简介
|
# 功能简介
|
||||||
|
|
||||||
- 问卷管理:创、编、投、收、数据分析
|
- 问卷管理:创、编、投、收、数据分析
|
||||||
@ -45,7 +43,7 @@
|
|||||||
|
|
||||||
- 数据安全:传输加密、脱敏等
|
- 数据安全:传输加密、脱敏等
|
||||||
|
|
||||||
> 更全的建设请查阅 [官方 Feature](https://github.com/didi/xiaoju-survey/issues/45)
|
> 更全的建设请查阅 [功能介绍](https://xiaojusurvey.didi.cn/docs/next/document/%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8C/%E5%8A%9F%E8%83%BD%E4%BB%8B%E7%BB%8D/%E5%9F%BA%E7%A1%80%E6%B5%81%E7%A8%8B)
|
||||||
|
|
||||||
<img src="https://github.com/didi/xiaoju-survey/assets/16012672/dd427471-368d-49d9-bc44-13c34d84e3be" width="700" />
|
<img src="https://github.com/didi/xiaoju-survey/assets/16012672/dd427471-368d-49d9-bc44-13c34d84e3be" width="700" />
|
||||||
|
|
||||||
@ -220,16 +218,8 @@ npm run serve
|
|||||||
|
|
||||||
## Future Tasks
|
## Future Tasks
|
||||||
|
|
||||||
1、[官方 Feature](https://github.com/didi/xiaoju-survey/issues/45)
|
[欢迎共建](https://github.com/didi/xiaoju-survey/issues/85)
|
||||||
|
|
||||||
2、[WIP](https://github.com/didi/xiaoju-survey/labels/WIP)
|
|
||||||
|
|
||||||
## CHANGELOG
|
## CHANGELOG
|
||||||
|
|
||||||
关注重大变更:[MAJOR CHANGELOG](https://github.com/didi/xiaoju-survey/issues/48)
|
关注重大变更:[MAJOR CHANGELOG](https://github.com/didi/xiaoju-survey/issues/48)
|
||||||
|
|
||||||
## 文章分享
|
|
||||||
|
|
||||||
1、[掘金](https://juejin.cn/user/3705833332160473/posts)、2、[CSDN](https://blog.csdn.net/XIAOJUSURVEY)、3、[InfoQ](https://www.infoq.cn/profile/7E08AC616A07B2/publish)、4、[知乎](https://www.zhihu.com/people/xiaojusurvey)
|
|
||||||
|
|
||||||
[欢迎共建](https://github.com/didi/xiaoju-survey/issues/85)
|
|
||||||
|
12
README_EN.md
12
README_EN.md
@ -29,12 +29,10 @@
|
|||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
  XIAOJUSURVEY is a lightweight, secure questionnaire system foundation that provides one-stop product-level solutions for individuals and enterprises, quickly meeting various online survey scenarios.
|
  XIAOJUSURVEY is an open-source form builder and analytics platform to create questionnaires, exams, polls, quizzes, and analyze data online.
|
||||||
|
|
||||||
  The internal system has accumulated over 40 question types and more than 100 selected templates, suitable for market research, customer satisfaction surveys, online exams, voting, reporting, evaluations, and many other scenarios. In terms of data capabilities, it has been honed through hundreds of millions of iterations, resulting in the ability to provide online reports with per-question statistics, cross-analysis, and multi-channel analysis, quickly meeting professional analysis needs.
|
  The internal system has accumulated over 40 question types and more than 100 selected templates, suitable for market research, customer satisfaction surveys, online exams, voting, reporting, evaluations, and many other scenarios. In terms of data capabilities, it has been honed through hundreds of millions of iterations, resulting in the ability to provide online reports with per-question statistics, cross-analysis, and multi-channel analysis, quickly meeting professional analysis needs.
|
||||||
|
|
||||||
  The open-source project focuses on building a survey foundation, constructing around platform capabilities, engineering structure, and development systems, allowing everyone to 「quickly」 create their own 「exclusive」 questionnaire system: [quickly understanding the ecological development philosophy](https://xiaojusurvey.didi.cn/docs/next/community/%E7%94%9F%E6%80%81%E5%BB%BA%E8%AE%BE).
|
|
||||||
|
|
||||||
# Function Overview
|
# Function Overview
|
||||||
|
|
||||||
- Questionnaire Management: Create, edit, distribute, collect, data analysis.
|
- Questionnaire Management: Create, edit, distribute, collect, data analysis.
|
||||||
@ -45,7 +43,7 @@
|
|||||||
|
|
||||||
- Data Security: Encrypted transmission, data masking, etc.
|
- Data Security: Encrypted transmission, data masking, etc.
|
||||||
|
|
||||||
> For more comprehensive features, please refer to the official Feature documentation.
|
> For more comprehensive features, please refer to the [documentation](https://xiaojusurvey.didi.cn/docs/next/document/%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8C/%E5%8A%9F%E8%83%BD%E4%BB%8B%E7%BB%8D/%E5%9F%BA%E7%A1%80%E6%B5%81%E7%A8%8B).
|
||||||
|
|
||||||
<img src="https://github.com/didi/xiaoju-survey/assets/16012672/508ce30f-0ae8-4f5f-84a7-e96de8238a7f" width="700" />
|
<img src="https://github.com/didi/xiaoju-survey/assets/16012672/508ce30f-0ae8-4f5f-84a7-e96de8238a7f" width="700" />
|
||||||
|
|
||||||
@ -225,9 +223,3 @@ If you want to become a contributor or expand your technical stack, please check
|
|||||||
## CHANGELOG
|
## CHANGELOG
|
||||||
|
|
||||||
Follow major changes: [MAJOR CHANGELOG](https://github.com/didi/xiaoju-survey/issues/48)
|
Follow major changes: [MAJOR CHANGELOG](https://github.com/didi/xiaoju-survey/issues/48)
|
||||||
|
|
||||||
## Article Sharing
|
|
||||||
|
|
||||||
1、[x.com](https://x.com/t_sudoooooo)
|
|
||||||
|
|
||||||
[Welcome to contribute.](https://github.com/didi/xiaoju-survey/issues/85)
|
|
||||||
|
@ -141,15 +141,8 @@ const onMoveDown = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const onDelete = async () => {
|
const onDelete = async () => {
|
||||||
if (unref(hasShowLogic)) {
|
if (unref(hasShowLogic) || getShowLogicText.value) {
|
||||||
ElMessageBox.alert('该题目被显示逻辑关联,请先清除逻辑依赖', '提示', {
|
ElMessageBox.alert('该问题被逻辑依赖,请先删除逻辑依赖', '提示', {
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'warning'
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (unref(hasJumpLogic)) {
|
|
||||||
ElMessageBox.alert('该题目被跳转逻辑关联,请先清除逻辑依赖', '提示', {
|
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
})
|
})
|
||||||
|
@ -1,37 +1,69 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="setter-wrapper">
|
<div class="setter-wrapper">
|
||||||
<div class="no-select-question" v-if="currentEditOne === null">
|
<el-tabs v-model="confType" type="border-card" stretch>
|
||||||
<img src="/imgs/icons/unselected.webp" />
|
<el-tab-pane name="baseConf" label="单题设置">
|
||||||
<h4 class="tipFont">选中题型可以编辑</h4>
|
<div v-if="currentEditMeta?.title" class="setter-title">
|
||||||
<span class="tip">来!试试看~</span>
|
{{ currentEditMeta?.title || '' }}
|
||||||
</div>
|
</div>
|
||||||
<template v-else>
|
|
||||||
<div class="setter-title">{{ currentEditMeta?.title || '' }}</div>
|
<div
|
||||||
<SetterField
|
class="no-select-question"
|
||||||
class="question-config-form"
|
v-if="editStore.currentEditOne === 'mainTitle' || editStore.currentEditOne === null"
|
||||||
:form-config-list="formConfigList"
|
>
|
||||||
:module-config="moduleConfig"
|
<img src="/imgs/icons/unselected.webp" />
|
||||||
@form-change="handleFormChange"
|
<h4 class="tipFont">选中题型可以编辑</h4>
|
||||||
/>
|
<span class="tip">来!试试看~</span>
|
||||||
</template>
|
</div>
|
||||||
|
|
||||||
|
<SetterField
|
||||||
|
v-else
|
||||||
|
:form-config-list="formConfigList"
|
||||||
|
:module-config="moduleConfig"
|
||||||
|
@form-change="handleFormChange"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane name="globalBaseConf" label="整卷设置">
|
||||||
|
<SetterField
|
||||||
|
:form-config-list="[basicConfig]"
|
||||||
|
:module-config="editStore.editGlobalBaseConf.globalBaseConfig"
|
||||||
|
@form-change="editStore.editGlobalBaseConf.updateGlobalConfOption"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
import { useEditStore } from '@/management/stores/edit'
|
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
|
import { useEditStore } from '@/management/stores/edit'
|
||||||
|
import basicConfig from '@/materials/questions/common/config/basicConfig'
|
||||||
import SetterField from '@/management/pages/edit/components/SetterField.vue'
|
import SetterField from '@/management/pages/edit/components/SetterField.vue'
|
||||||
|
|
||||||
|
const confType = ref('baseConf')
|
||||||
const editStore = useEditStore()
|
const editStore = useEditStore()
|
||||||
const { currentEditOne, currentEditKey, currentEditMeta, formConfigList, moduleConfig } =
|
|
||||||
storeToRefs(editStore)
|
const { currentEditKey, currentEditMeta, formConfigList, moduleConfig } = storeToRefs(editStore)
|
||||||
const { changeSchema } = editStore
|
const { changeSchema } = editStore
|
||||||
const handleFormChange = (data: any) => {
|
const handleFormChange = (data: any) => {
|
||||||
const { key, value } = data
|
const { key, value } = data
|
||||||
const resultKey = `${currentEditKey.value}.${key}`
|
const resultKey = `${currentEditKey.value}.${key}`
|
||||||
changeSchema({ key: resultKey, value })
|
changeSchema({ key: resultKey, value })
|
||||||
|
if (key in editStore.editGlobalBaseConf.globalBaseConfig)
|
||||||
|
editStore.editGlobalBaseConf.updateCounts('MODIFY', { key, value })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => editStore.currentEditOne,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal === 0 || (!!newVal && newVal !== 'mainTitle')) {
|
||||||
|
confType.value = 'baseConf'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.setter-wrapper {
|
.setter-wrapper {
|
||||||
width: 360px;
|
width: 360px;
|
||||||
@ -39,36 +71,53 @@ const handleFormChange = (data: any) => {
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
.no-select-question {
|
||||||
|
padding-top: 125px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.setter-title {
|
img {
|
||||||
height: 40px;
|
width: 160px;
|
||||||
line-height: 40px;
|
padding: 25px;
|
||||||
font-size: 14px;
|
}
|
||||||
color: $primary-color;
|
|
||||||
padding-left: 20px;
|
|
||||||
border-bottom: 1px solid #edeffc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-select-question {
|
.tip {
|
||||||
padding-top: 125px;
|
font-size: 14px;
|
||||||
display: flex;
|
color: $normal-color;
|
||||||
flex-direction: column;
|
letter-spacing: 0;
|
||||||
align-items: center;
|
}
|
||||||
|
|
||||||
img {
|
|
||||||
width: 160px;
|
|
||||||
padding: 25px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tip {
|
.setter-title {
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: $normal-color;
|
color: $primary-color;
|
||||||
letter-spacing: 0;
|
padding-left: 20px;
|
||||||
|
border-bottom: 1px solid #edeffc;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.question-config-form {
|
:deep(.el-tabs) {
|
||||||
padding: 30px 20px 50px 20px;
|
width: 360px;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.el-tabs__nav {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.config-form {
|
||||||
|
padding: 30px 20px 50px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -19,11 +19,14 @@
|
|||||||
class="qtopic-item"
|
class="qtopic-item"
|
||||||
:id="'qtopic' + element.type"
|
:id="'qtopic' + element.type"
|
||||||
@click="onQuestionType({ type: 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>
|
<i
|
||||||
|
class="iconfont"
|
||||||
|
:class="['icon-' + element.icon]"
|
||||||
|
@mouseenter="showPreview(element, 'qtopic' + element.type)"
|
||||||
|
@mouseleave="isShowPreviewImage = false"
|
||||||
|
@mousedown="isShowPreviewImage = false"
|
||||||
|
></i>
|
||||||
<p class="text">{{ element.title }}</p>
|
<p class="text">{{ element.title }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
import { reactive, type Ref } from 'vue'
|
||||||
|
|
||||||
|
export type TypeMethod = 'INIT' | 'MODIFY' | 'REMOVE' | 'ADD'
|
||||||
|
|
||||||
|
export default function useEditGlobalBaseConf(
|
||||||
|
questionDetailList: Ref<any[]>,
|
||||||
|
updateTime: () => void
|
||||||
|
) {
|
||||||
|
// 整卷配置数据
|
||||||
|
const globalBaseConfig = reactive({
|
||||||
|
isRequired: true,
|
||||||
|
showIndex: true,
|
||||||
|
showType: true,
|
||||||
|
showSpliter: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 整卷配置各项选项选中个数统计
|
||||||
|
const optionCheckedCounts = {
|
||||||
|
isRequiredCount: 0,
|
||||||
|
showIndexCount: 0,
|
||||||
|
showTypeCount: 0,
|
||||||
|
showSpliterCount: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetCount = () => {
|
||||||
|
optionCheckedCounts.isRequiredCount = 0
|
||||||
|
optionCheckedCounts.showIndexCount = 0
|
||||||
|
optionCheckedCounts.showTypeCount = 0
|
||||||
|
optionCheckedCounts.showSpliterCount = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化统计
|
||||||
|
function initCounts() {
|
||||||
|
resetCount()
|
||||||
|
questionDetailList.value.forEach((question: any) => {
|
||||||
|
calculateCountsForQuestion('INIT', { question })
|
||||||
|
})
|
||||||
|
updateGlobalBaseConf()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新统计
|
||||||
|
function updateCounts(type: TypeMethod, data: any) {
|
||||||
|
if (type === 'MODIFY') {
|
||||||
|
calculateOptionCounts(type, data)
|
||||||
|
} else {
|
||||||
|
calculateCountsForQuestion(type, data)
|
||||||
|
}
|
||||||
|
updateGlobalBaseConf()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算整卷配置各项选项选中个数
|
||||||
|
function calculateCountsForQuestion(type: TypeMethod, { question }: any) {
|
||||||
|
Object.keys(globalBaseConfig).forEach((key) => {
|
||||||
|
calculateOptionCounts(type, { key, value: question[key] })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算单个选项选中个数
|
||||||
|
function calculateOptionCounts(type: TypeMethod, { key, value }: any) {
|
||||||
|
const _key = `${key}Count` as keyof typeof optionCheckedCounts
|
||||||
|
if (value) {
|
||||||
|
if (type === 'REMOVE') optionCheckedCounts[_key]--
|
||||||
|
else optionCheckedCounts[_key]++
|
||||||
|
} else {
|
||||||
|
if (type === 'MODIFY') optionCheckedCounts[_key]--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新整卷配置状态
|
||||||
|
function updateGlobalBaseConf() {
|
||||||
|
const len = questionDetailList.value.length
|
||||||
|
const { isRequiredCount, showIndexCount, showSpliterCount, showTypeCount } = optionCheckedCounts
|
||||||
|
Object.assign(globalBaseConfig, {
|
||||||
|
isRequired: isRequiredCount === len,
|
||||||
|
showIndex: showIndexCount === len,
|
||||||
|
showSpliter: showSpliterCount === len,
|
||||||
|
showType: showTypeCount === len
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 整卷配置修改
|
||||||
|
function updateGlobalConfOption({ key, value }: { key: string; value: boolean }) {
|
||||||
|
const len = questionDetailList.value.length
|
||||||
|
const _key = `${key}Count` as keyof typeof optionCheckedCounts
|
||||||
|
if (value) optionCheckedCounts[_key] = len
|
||||||
|
else optionCheckedCounts[_key] = 0
|
||||||
|
questionDetailList.value.forEach((question: any) => {
|
||||||
|
question[key] = value
|
||||||
|
})
|
||||||
|
updateTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
globalBaseConfig,
|
||||||
|
initCounts,
|
||||||
|
updateCounts,
|
||||||
|
updateGlobalConfOption
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ import questionLoader from '@/materials/questions/questionLoader'
|
|||||||
import { SurveyPermissions } from '@/management/utils/types/workSpace'
|
import { SurveyPermissions } from '@/management/utils/types/workSpace'
|
||||||
import { getBannerData } from '@/management/api/skin.js'
|
import { getBannerData } from '@/management/api/skin.js'
|
||||||
import { getCollaboratorPermissions } from '@/management/api/space'
|
import { getCollaboratorPermissions } from '@/management/api/space'
|
||||||
|
import useEditGlobalBaseConf, { type TypeMethod } from './composables/useEditGlobalBaseConf'
|
||||||
import { CODE_MAP } from '../api/base'
|
import { CODE_MAP } from '../api/base'
|
||||||
import { RuleBuild } from '@/common/logicEngine/RuleBuild'
|
import { RuleBuild } from '@/common/logicEngine/RuleBuild'
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ const innerMetaConfig = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function useInitializeSchema(surveyId: Ref<string>) {
|
function useInitializeSchema(surveyId: Ref<string>, initializeSchemaCallBack: () => void) {
|
||||||
const schema = reactive({
|
const schema = reactive({
|
||||||
metaData: null,
|
metaData: null,
|
||||||
bannerConf: {
|
bannerConf: {
|
||||||
@ -133,6 +134,7 @@ function useInitializeSchema(surveyId: Ref<string>) {
|
|||||||
logicConf
|
logicConf
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
initializeSchemaCallBack()
|
||||||
|
|
||||||
initShowLogicEngine()
|
initShowLogicEngine()
|
||||||
initJumpLogicEngine()
|
initJumpLogicEngine()
|
||||||
@ -150,11 +152,17 @@ function useInitializeSchema(surveyId: Ref<string>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function useQuestionDataListOperations(
|
function useQuestionDataListOperations({
|
||||||
questionDataList: Ref<any[]>,
|
questionDataList,
|
||||||
updateTime: () => void,
|
updateTime,
|
||||||
|
pageOperations,
|
||||||
|
updateCounts
|
||||||
|
}: {
|
||||||
|
questionDataList: Ref<any[]>
|
||||||
|
updateTime: () => void
|
||||||
pageOperations: (type: string) => void
|
pageOperations: (type: string) => void
|
||||||
) {
|
updateCounts: (type: TypeMethod, data: any) => void
|
||||||
|
}) {
|
||||||
function copyQuestion({ index }: { index: number }) {
|
function copyQuestion({ index }: { index: number }) {
|
||||||
const newQuestion = _cloneDeep(questionDataList.value[index])
|
const newQuestion = _cloneDeep(questionDataList.value[index])
|
||||||
newQuestion.field = getNewField(questionDataList.value.map((item) => item.field))
|
newQuestion.field = getNewField(questionDataList.value.map((item) => item.field))
|
||||||
@ -165,12 +173,14 @@ function useQuestionDataListOperations(
|
|||||||
questionDataList.value.splice(index, 0, question)
|
questionDataList.value.splice(index, 0, question)
|
||||||
pageOperations('add')
|
pageOperations('add')
|
||||||
updateTime()
|
updateTime()
|
||||||
|
updateCounts('ADD', { question })
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteQuestion({ index }: { index: number }) {
|
function deleteQuestion({ index }: { index: number }) {
|
||||||
pageOperations('remove')
|
pageOperations('remove')
|
||||||
questionDataList.value.splice(index, 1)
|
const [question] = questionDataList.value.splice(index, 1)
|
||||||
updateTime()
|
updateTime()
|
||||||
|
updateCounts('REMOVE', { question })
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveQuestion({ index, range }: { index: number; range: number }) {
|
function moveQuestion({ index, range }: { index: number; range: number }) {
|
||||||
@ -459,8 +469,13 @@ export const useEditStore = defineStore('edit', () => {
|
|||||||
const cooperPermissions = ref(Object.values(SurveyPermissions))
|
const cooperPermissions = ref(Object.values(SurveyPermissions))
|
||||||
const schemaUpdateTime = ref(Date.now())
|
const schemaUpdateTime = ref(Date.now())
|
||||||
const { schema, initSchema, getSchemaFromRemote, showLogicEngine, jumpLogicEngine } =
|
const { schema, initSchema, getSchemaFromRemote, showLogicEngine, jumpLogicEngine } =
|
||||||
useInitializeSchema(surveyId)
|
useInitializeSchema(surveyId, () => {
|
||||||
|
editGlobalBaseConf.initCounts()
|
||||||
|
})
|
||||||
const questionDataList = toRef(schema, 'questionDataList')
|
const questionDataList = toRef(schema, 'questionDataList')
|
||||||
|
|
||||||
|
const editGlobalBaseConf = useEditGlobalBaseConf(questionDataList, updateTime)
|
||||||
|
|
||||||
function setQuestionDataList(data: any) {
|
function setQuestionDataList(data: any) {
|
||||||
schema.questionDataList = data
|
schema.questionDataList = data
|
||||||
}
|
}
|
||||||
@ -524,9 +539,12 @@ export const useEditStore = defineStore('edit', () => {
|
|||||||
} = usePageEdit({ schema, questionDataList }, updateTime)
|
} = usePageEdit({ schema, questionDataList }, updateTime)
|
||||||
|
|
||||||
const { copyQuestion, addQuestion, deleteQuestion, moveQuestion } = useQuestionDataListOperations(
|
const { copyQuestion, addQuestion, deleteQuestion, moveQuestion } = useQuestionDataListOperations(
|
||||||
questionDataList,
|
{
|
||||||
updateTime,
|
questionDataList,
|
||||||
pageOperations
|
updateTime,
|
||||||
|
pageOperations,
|
||||||
|
updateCounts: editGlobalBaseConf.updateCounts
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
function moveQuestionDataList(data: any) {
|
function moveQuestionDataList(data: any) {
|
||||||
@ -602,6 +620,7 @@ export const useEditStore = defineStore('edit', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
editGlobalBaseConf,
|
||||||
surveyId,
|
surveyId,
|
||||||
setSurveyId,
|
setSurveyId,
|
||||||
bannerList,
|
bannerList,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted } from 'vue'
|
import { onMounted, watch } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
import { getPublishedSurveyInfo, getPreviewSchema } from '../api/survey'
|
import { getPublishedSurveyInfo, getPreviewSchema } from '../api/survey'
|
||||||
@ -57,6 +57,13 @@ onMounted(() => {
|
|||||||
getDetail(surveyId as string)
|
getDetail(surveyId as string)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.query.t,
|
||||||
|
() => {
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const getDetail = async (surveyPath: string) => {
|
const getDetail = async (surveyPath: string) => {
|
||||||
const alert = useCommandComponent(AlertDialog)
|
const alert = useCommandComponent(AlertDialog)
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
></SubmitButton>
|
></SubmitButton>
|
||||||
</div>
|
</div>
|
||||||
<LogoIcon :logo-conf="logoConf" :readonly="true" />
|
<LogoIcon :logo-conf="logoConf" :readonly="true" />
|
||||||
<VerifyWhiteDialog/>
|
<VerifyWhiteDialog />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -114,7 +114,7 @@ const submitSurver = async () => {
|
|||||||
console.log(params)
|
console.log(params)
|
||||||
const res: any = await submitForm(params)
|
const res: any = await submitForm(params)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
router.push({ name: 'successPage' })
|
router.replace({ name: 'successPage' })
|
||||||
} else {
|
} else {
|
||||||
alert({
|
alert({
|
||||||
title: res.errmsg || '提交失败'
|
title: res.errmsg || '提交失败'
|
||||||
|
@ -4,6 +4,18 @@
|
|||||||
<div class="result-content">
|
<div class="result-content">
|
||||||
<img src="/imgs/icons/success.webp" />
|
<img src="/imgs/icons/success.webp" />
|
||||||
<div class="msg" v-html="successMsg"></div>
|
<div class="msg" v-html="successMsg"></div>
|
||||||
|
<router-link
|
||||||
|
:to="{
|
||||||
|
name: 'renderPage',
|
||||||
|
query: {
|
||||||
|
t: new Date().getTime()
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
replace
|
||||||
|
class="reset-link"
|
||||||
|
>
|
||||||
|
重新填写
|
||||||
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<LogoIcon :logo-conf="logoConf" :readonly="true" />
|
<LogoIcon :logo-conf="logoConf" :readonly="true" />
|
||||||
</div>
|
</div>
|
||||||
@ -65,5 +77,13 @@ const successMsg = computed(() => {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-top: 0.15rem;
|
margin-top: 0.15rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reset-link {
|
||||||
|
margin-top: 0.24rem;
|
||||||
|
font-size: 0.27rem;
|
||||||
|
color: #5094f0;
|
||||||
|
text-decoration: underline;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user