fix: 修复写法问题、C端组件问题
This commit is contained in:
parent
56c37fce3c
commit
cdb8b6532a
@ -33,7 +33,6 @@ import { SURVEY_PERMISSION } from 'src/enums/surveyPermission';
|
||||
import { WorkspaceGuard } from 'src/guards/workspace.guard';
|
||||
import { PERMISSION as WORKSPACE_PERMISSION } from 'src/enums/workspace';
|
||||
import { SessionService } from '../services/session.service';
|
||||
import { MemberType, WhitelistType } from 'src/interfaces/survey';
|
||||
import { UserService } from 'src/modules/auth/services/user.service';
|
||||
|
||||
@ApiTags('survey')
|
||||
@ -247,16 +246,6 @@ export class SurveyController {
|
||||
surveyMeta.isCollaborated = false;
|
||||
}
|
||||
|
||||
// 白名单相关字段的默认值
|
||||
const baseConf = surveyConf.code?.baseConf;
|
||||
if (baseConf) {
|
||||
baseConf.passwordSwitch = baseConf.passwordSwitch ?? false;
|
||||
baseConf.password = baseConf.password ?? '';
|
||||
baseConf.whitelistType = baseConf.whitelistType ?? WhitelistType.ALL;
|
||||
baseConf.whitelist = baseConf.whitelist ?? [];
|
||||
baseConf.memberType = baseConf.memberType ?? MemberType.MOBILE;
|
||||
}
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
data: {
|
||||
|
@ -34,7 +34,14 @@
|
||||
"tLimit": 0,
|
||||
"language": "chinese",
|
||||
"answerBegTime": "00:00:00",
|
||||
"answerEndTime": "23:59:59"
|
||||
"answerEndTime": "23:59:59",
|
||||
"passwordSwitch": false,
|
||||
"password": "",
|
||||
"whitelistType": "ALL",
|
||||
"whitelist": [],
|
||||
"memberType": "MOBILE",
|
||||
"fillAnswer": false,
|
||||
"fillSubmitAnswer": false
|
||||
},
|
||||
"skinConf": {
|
||||
"skinColor": "#4a4c5b",
|
||||
|
@ -97,14 +97,17 @@ export class ResponseSchemaController {
|
||||
// 密码校验
|
||||
if (passwordSwitch) {
|
||||
if (settingPassword !== password) {
|
||||
throw new HttpException('验证失败', EXCEPTION_CODE.WHITELIST_ERROR);
|
||||
throw new HttpException('密码验证失败', EXCEPTION_CODE.WHITELIST_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
// 名单校验(手机号/邮箱)
|
||||
if (whitelistType === WhitelistType.CUSTOM) {
|
||||
if (!whitelist.includes(whitelistValue)) {
|
||||
throw new HttpException('验证失败', EXCEPTION_CODE.WHITELIST_ERROR);
|
||||
throw new HttpException(
|
||||
'白名单验证失败',
|
||||
EXCEPTION_CODE.WHITELIST_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +115,7 @@ export class ResponseSchemaController {
|
||||
if (whitelistType === WhitelistType.MEMBER) {
|
||||
const user = await this.userService.getUserByUsername(whitelistValue);
|
||||
if (!user) {
|
||||
throw new HttpException('验证失败', EXCEPTION_CODE.WHITELIST_ERROR);
|
||||
throw new HttpException('名单验证失败', EXCEPTION_CODE.WHITELIST_ERROR);
|
||||
}
|
||||
|
||||
const workspaceMember = await this.workspaceMemberService.findAllByUserId(
|
||||
|
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { MongoRepository } from 'typeorm';
|
||||
import { SurveyResponse } from 'src/models/surveyResponse.entity';
|
||||
import { RECORD_STATUS } from 'src/enums';
|
||||
@Injectable()
|
||||
export class SurveyResponseService {
|
||||
constructor(
|
||||
@ -35,14 +36,14 @@ export class SurveyResponseService {
|
||||
}
|
||||
|
||||
async getSurveyResponseTotalByPath(surveyPath: string) {
|
||||
const count = await this.surveyResponseRepository.count({
|
||||
const data = await this.surveyResponseRepository.find({
|
||||
where: {
|
||||
surveyPath,
|
||||
'curStatus.status': {
|
||||
$ne: 'removed',
|
||||
$ne: RECORD_STATUS.REMOVED,
|
||||
},
|
||||
},
|
||||
});
|
||||
return count;
|
||||
return (data || []).length;
|
||||
}
|
||||
}
|
||||
|
@ -1,58 +0,0 @@
|
||||
interface ExtendedItem {
|
||||
value: any
|
||||
expires?: number // 可选属性
|
||||
}
|
||||
|
||||
const localstorage = {
|
||||
// 检查是否支持localStorage
|
||||
isSupported(): boolean {
|
||||
return typeof window !== 'undefined' && 'localStorage' in window
|
||||
},
|
||||
|
||||
// 设置值
|
||||
setItem(key: string, value: any, expires?: number): void {
|
||||
if (!this.isSupported()) return
|
||||
|
||||
let item: ExtendedItem = { value }
|
||||
|
||||
if (expires !== undefined) {
|
||||
item = { ...item, expires: Date.now() + expires * 1000 }
|
||||
}
|
||||
|
||||
const serializedValue = JSON.stringify(item)
|
||||
|
||||
localStorage.setItem(key, serializedValue)
|
||||
},
|
||||
|
||||
// 获取值
|
||||
getItem<T>(key: string): T | null {
|
||||
if (!this.isSupported()) return null
|
||||
|
||||
const serializedValue = localStorage.getItem(key) as string
|
||||
if (!serializedValue) return null
|
||||
|
||||
let item: any
|
||||
try {
|
||||
item = JSON.parse(serializedValue)
|
||||
} catch (e) {
|
||||
console.error('Error parsing JSON from localStorage')
|
||||
return null
|
||||
}
|
||||
|
||||
if (item.expires && item.expires < Date.now()) {
|
||||
this.removeItem(key)
|
||||
return null
|
||||
}
|
||||
|
||||
return item.value as T
|
||||
},
|
||||
|
||||
// 移除值
|
||||
removeItem(key: string): void {
|
||||
if (!this.isSupported()) return
|
||||
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
}
|
||||
|
||||
export default localstorage
|
@ -18,7 +18,7 @@ const router = useRouter()
|
||||
let timer: any
|
||||
|
||||
const showConfirmBox = () => {
|
||||
ElMessageBox.alert('登录状态已失效,请重新登陆。', '提示', {
|
||||
ElMessageBox.alert('登录状态已失效,请重新登录。', '提示', {
|
||||
confirmButtonText: '确认',
|
||||
showClose: false,
|
||||
callback: (action: Action) => {
|
||||
@ -57,9 +57,9 @@ const checkAuth = async () => {
|
||||
}
|
||||
|
||||
watch(
|
||||
() => userStore.hasLogined,
|
||||
(hasLogined) => {
|
||||
if (hasLogined) {
|
||||
() => userStore.hasLogin,
|
||||
(hasLogin) => {
|
||||
if (hasLogin) {
|
||||
timer = setTimeout(
|
||||
() => {
|
||||
checkAuth()
|
||||
|
@ -37,9 +37,9 @@ instance.interceptors.response.use(
|
||||
|
||||
instance.interceptors.request.use((config) => {
|
||||
const userStore = useUserStore()
|
||||
const hasLogined = _get(userStore, 'hasLogined')
|
||||
const hasLogin = _get(userStore, 'hasLogin')
|
||||
const token = _get(userStore, 'userInfo.token')
|
||||
if (hasLogined && token) {
|
||||
if (hasLogin && token) {
|
||||
if (!config.headers) {
|
||||
config.headers = {}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// 引入防抖函数
|
||||
import { debounce as _debounce } from 'lodash-es'
|
||||
import { debounce } from 'lodash-es'
|
||||
/**
|
||||
* @description: 监听元素尺寸变化
|
||||
* @param {*} el 元素dom
|
||||
@ -8,7 +8,7 @@ import { debounce as _debounce } from 'lodash-es'
|
||||
* @return {*}
|
||||
*/
|
||||
export default (el, cb, wait = 200) => {
|
||||
const resizeObserver = new ResizeObserver(_debounce(cb, wait))
|
||||
const resizeObserver = new ResizeObserver(debounce(cb, wait))
|
||||
|
||||
resizeObserver.observe(el)
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
>
|
||||
<div><slot v-if="moduleConfig.type !== 'section'"></slot></div>
|
||||
|
||||
<div :class="[showHover ? 'visibily' : 'hidden', 'hoverItem']">
|
||||
<div :class="[showHover ? 'visibility' : 'hidden', 'hoverItem']">
|
||||
<div
|
||||
class="item el-icon-rank"
|
||||
@click.stop.prevent
|
||||
|
@ -29,8 +29,6 @@ import { ref, watch } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useEditStore } from '@/management/stores/edit'
|
||||
import moment from 'moment'
|
||||
import 'moment/locale/zh-cn'
|
||||
moment.locale('zh-cn')
|
||||
|
||||
import { getSurveyHistory } from '@/management/api/survey'
|
||||
|
||||
|
@ -65,7 +65,7 @@ const getSpaceMenus = async () => {
|
||||
id: v.ownerId,
|
||||
label: v.name,
|
||||
children: members?.map((v) => ({
|
||||
id: v.userId,
|
||||
id: `${v.workspaceId}_${v.userId}`,
|
||||
label: v.username
|
||||
}))
|
||||
})
|
||||
|
@ -26,15 +26,21 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, onMounted } from 'vue'
|
||||
import { useEditStore } from '@/management/stores/edit'
|
||||
|
||||
import { getBannerData } from '@/management/api/skin.js'
|
||||
import skinPresets from '@/management/config/skinPresets.js'
|
||||
|
||||
const editStore = useEditStore()
|
||||
const { changeThemePreset } = editStore
|
||||
const groupName = ref<string>('temp')
|
||||
const bannerList = computed(() => editStore.bannerList || [])
|
||||
let bannerList = ref([])
|
||||
|
||||
onMounted(async () => {
|
||||
const res = await getBannerData()
|
||||
bannerList.value = res.data
|
||||
})
|
||||
|
||||
const groupList = computed(() =>
|
||||
Object.keys(bannerList.value).map((key) => ({
|
||||
label: bannerList.value[key].name,
|
||||
@ -53,11 +59,11 @@ const currentBannerList = computed(() => {
|
||||
})
|
||||
})
|
||||
|
||||
const allbanner = arr.reduce((acc, curr) => {
|
||||
const allBanner = arr.reduce((acc, curr) => {
|
||||
return acc.concat(curr)
|
||||
}, [])
|
||||
|
||||
return allbanner.filter((item: any) => {
|
||||
return allBanner.filter((item: any) => {
|
||||
if (groupName.value === 'temp') {
|
||||
return true
|
||||
} else {
|
||||
|
@ -12,19 +12,19 @@
|
||||
</CommonTemplate>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
import { useEditStore } from '@/management/stores/edit'
|
||||
// import { onMounted } from 'vue'
|
||||
// import { useEditStore } from '@/management/stores/edit'
|
||||
|
||||
import CommonTemplate from '../../components/CommonTemplate.vue'
|
||||
import CatalogPanel from '../../modules/skinModule/CatalogPanel.vue'
|
||||
import PreviewPanel from '../../modules/skinModule/PreviewPanel.vue'
|
||||
import SetterPanel from '../../modules/skinModule/SetterPanel.vue'
|
||||
|
||||
const editStore = useEditStore()
|
||||
// const editStore = useEditStore()
|
||||
|
||||
onMounted(() => {
|
||||
editStore.fetchBannerData()
|
||||
})
|
||||
// onMounted(() => {
|
||||
// editStore.fetchBannerData()
|
||||
// })
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.navbar {
|
||||
|
@ -27,16 +27,14 @@ export default {
|
||||
label: '允许断点续答',
|
||||
tip: '回填前一次作答中的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)',
|
||||
placement: 'top',
|
||||
type: 'CustomedSwitch',
|
||||
value: false
|
||||
type: 'CustomedSwitch'
|
||||
},
|
||||
limit_fillSubmitAnswer: {
|
||||
key: 'fillSubmitAnswer',
|
||||
label: '自动填充上次提交内容',
|
||||
tip: '回填前一次提交的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)',
|
||||
placement: 'top',
|
||||
type: 'CustomedSwitch',
|
||||
value: false
|
||||
type: 'CustomedSwitch'
|
||||
},
|
||||
interview_pwd_switch: {
|
||||
key: 'passwordSwitch',
|
||||
@ -81,6 +79,10 @@ export default {
|
||||
{
|
||||
key: 'whitelist', // 切换tab清空名单列表
|
||||
value: []
|
||||
},
|
||||
{
|
||||
key: 'memberType',
|
||||
value: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -113,12 +113,6 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/src/message.scss'
|
||||
import 'element-plus/theme-chalk/src/message-box.scss'
|
||||
|
||||
import moment from 'moment'
|
||||
// 引入中文
|
||||
import 'moment/locale/zh-cn'
|
||||
// 设置中文
|
||||
moment.locale('zh-cn')
|
||||
|
||||
import EmptyIndex from '@/management/components/EmptyIndex.vue'
|
||||
import CooperModify from '@/management/components/CooperModify/ModifyDialog.vue'
|
||||
import { CODE_MAP } from '@/management/api/base'
|
||||
|
@ -158,15 +158,11 @@ const handleModify = async (id: string) => {
|
||||
showSpaceModify.value = true
|
||||
}
|
||||
const handleDelete = (id: string) => {
|
||||
ElMessageBox.confirm(
|
||||
'删除团队后,团队内的问卷将同步被删除,请谨慎考虑!是否确认本次删除?',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
ElMessageBox.confirm('删除后团队内的问卷将同步被删除,是否确认本次删除?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
await workSpaceStore.deleteSpace(id)
|
||||
await workSpaceStore.getSpaceList()
|
||||
|
@ -44,20 +44,22 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item class="button-group">
|
||||
<el-button
|
||||
:loading="pending.register"
|
||||
class="button register-button"
|
||||
@click="submitForm('register')"
|
||||
>
|
||||
注册
|
||||
</el-button>
|
||||
<el-button
|
||||
:loading="pending.login"
|
||||
size="small"
|
||||
type="primary"
|
||||
class="button"
|
||||
@click="submitForm('login')"
|
||||
>登录</el-button
|
||||
>
|
||||
<el-button
|
||||
:loading="pending.register"
|
||||
class="button register-button"
|
||||
@click="submitForm('register')"
|
||||
>注册</el-button
|
||||
>
|
||||
登录
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
@ -71,7 +73,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/src/message.scss'
|
||||
|
||||
import { debounce as _debounce } from 'lodash-es'
|
||||
import { debounce } from 'lodash-es'
|
||||
|
||||
import { getPasswordStrength, login, register } from '@/management/api/auth'
|
||||
import { refreshCaptcha as refreshCaptchaApi } from '@/management/api/captcha'
|
||||
@ -161,7 +163,7 @@ const rules = {
|
||||
],
|
||||
password: [
|
||||
{
|
||||
validator: _debounce(passwordValidator, 500),
|
||||
validator: debounce(passwordValidator, 500),
|
||||
trigger: 'change'
|
||||
}
|
||||
],
|
||||
@ -285,7 +287,7 @@ const refreshCaptcha = async () => {
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 200px;
|
||||
width: 204px;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
@ -293,7 +295,6 @@ const refreshCaptcha = async () => {
|
||||
.register-button {
|
||||
border-color: #faa600;
|
||||
color: #faa600;
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,7 +197,7 @@ async function handleLoginGuard(
|
||||
next: NavigationGuardNext
|
||||
) {
|
||||
const userStore = useUserStore()
|
||||
if (userStore?.hasLogined) {
|
||||
if (userStore?.hasLogin) {
|
||||
await handlePermissionsGuard(to, from, next)
|
||||
} else {
|
||||
next({
|
||||
@ -221,8 +221,8 @@ async function handlePermissionsGuard(
|
||||
} else {
|
||||
// 如果跳转编辑页面,且跳转页面和上一页的surveyId不同,判断是否有对应页面权限
|
||||
if (currSurveyId !== prevSurveyId) {
|
||||
await editStore.fetchCooperPermissions(currSurveyId as string)
|
||||
if (hasRequiredPermissions(to.meta.permissions as string[], editStore.cooperPermissions)) {
|
||||
const cooperPermissions = await editStore.fetchCooperPermissions(currSurveyId as string)
|
||||
if (hasRequiredPermissions(to.meta.permissions as string[], cooperPermissions)) {
|
||||
next()
|
||||
} else {
|
||||
ElMessage.warning('您没有该问卷的相关协作权限')
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { reactive, type Ref } from 'vue'
|
||||
import { type Ref, reactive } from 'vue'
|
||||
|
||||
export type TypeMethod = 'INIT' | 'MODIFY' | 'REMOVE' | 'ADD'
|
||||
|
100
web/src/management/stores/composables/useCurrentEdit.ts
Normal file
100
web/src/management/stores/composables/useCurrentEdit.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import { type Ref, ref, computed } from 'vue'
|
||||
|
||||
import submitFormConfig from '@/management/pages/edit/setterConfig/submitConfig'
|
||||
import questionLoader from '@/materials/questions/questionLoader'
|
||||
|
||||
const innerMetaConfig = {
|
||||
submit: {
|
||||
title: '提交配置',
|
||||
formConfig: submitFormConfig
|
||||
}
|
||||
}
|
||||
|
||||
export default function useCurrentEdit({
|
||||
schema,
|
||||
questionDataList
|
||||
}: {
|
||||
schema: any
|
||||
questionDataList: Ref<any[]>
|
||||
}) {
|
||||
const currentEditOne = ref()
|
||||
const currentEditStatus = ref('Success')
|
||||
|
||||
const currentEditKey = computed(() => {
|
||||
if (currentEditOne.value === null) {
|
||||
return null
|
||||
}
|
||||
let key = ''
|
||||
switch (currentEditOne.value) {
|
||||
case 'banner':
|
||||
case 'mainTitle':
|
||||
key = 'bannerConf'
|
||||
break
|
||||
case 'submit':
|
||||
key = 'submitConf'
|
||||
break
|
||||
case 'logo':
|
||||
key = 'bottomConf'
|
||||
break
|
||||
default:
|
||||
key = `questionDataList.${currentEditOne.value}`
|
||||
}
|
||||
return key
|
||||
})
|
||||
|
||||
const currentEditMeta = computed(() => {
|
||||
if (currentEditOne.value === null) {
|
||||
return null
|
||||
} else if (innerMetaConfig[currentEditOne.value as keyof typeof innerMetaConfig]) {
|
||||
return innerMetaConfig[currentEditOne.value as keyof typeof innerMetaConfig]
|
||||
} else {
|
||||
const questionType = questionDataList.value?.[currentEditOne.value]?.type
|
||||
return questionLoader.getMeta(questionType)
|
||||
}
|
||||
})
|
||||
|
||||
const moduleConfig = computed(() => {
|
||||
if (currentEditOne.value === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (currentEditOne.value === 'banner' || currentEditOne.value === 'mainTitle') {
|
||||
return schema?.bannerConf
|
||||
} else if (currentEditOne.value === 'submit') {
|
||||
return schema?.submitConf
|
||||
} else if (currentEditOne.value === 'logo') {
|
||||
return schema?.bottomConf
|
||||
} else if (!Number.isNaN(currentEditOne.value)) {
|
||||
return questionDataList.value?.[currentEditOne.value]
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
const formConfigList = computed(() => {
|
||||
if (currentEditOne.value === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return currentEditMeta.value?.formConfig || []
|
||||
})
|
||||
|
||||
function setCurrentEditOne(data: any) {
|
||||
currentEditOne.value = data
|
||||
}
|
||||
|
||||
function changeCurrentEditStatus(status: string) {
|
||||
currentEditStatus.value = status
|
||||
}
|
||||
|
||||
return {
|
||||
currentEditOne,
|
||||
currentEditKey,
|
||||
currentEditStatus,
|
||||
moduleConfig,
|
||||
formConfigList,
|
||||
currentEditMeta,
|
||||
setCurrentEditOne,
|
||||
changeCurrentEditStatus
|
||||
}
|
||||
}
|
146
web/src/management/stores/composables/useInitializeSchema.ts
Normal file
146
web/src/management/stores/composables/useInitializeSchema.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import { type Ref, ref, reactive } from 'vue'
|
||||
import { merge } from 'lodash-es'
|
||||
|
||||
import { getSurveyById, getSessionId } from '@/management/api/survey'
|
||||
import useLogicEngine from './useLogicEngine'
|
||||
|
||||
export default function useInitializeSchema(
|
||||
surveyId: Ref<string>,
|
||||
initializeSchemaCallBack: () => void
|
||||
) {
|
||||
const schema = reactive({
|
||||
metaData: null,
|
||||
bannerConf: {
|
||||
titleConfig: {
|
||||
mainTitle: '<h3 style="text-align: center">欢迎填写问卷</h3>',
|
||||
subTitle: `<p>为了给您提供更好的服务,希望您能抽出几分钟时间,将您的感受和建议告诉我们,<span style="color: rgb(204, 0, 0)">期待您的参与!</span></p>`,
|
||||
applyTitle: ''
|
||||
},
|
||||
bannerConfig: {
|
||||
bgImage: '',
|
||||
bgImageAllowJump: false,
|
||||
bgImageJumpLink: '',
|
||||
videoLink: '',
|
||||
postImg: ''
|
||||
}
|
||||
},
|
||||
bottomConf: {
|
||||
logoImage: '',
|
||||
logoImageWidth: '28%'
|
||||
},
|
||||
skinConf: {
|
||||
backgroundConf: {
|
||||
color: '#fff'
|
||||
},
|
||||
themeConf: {
|
||||
color: '#ffa600'
|
||||
},
|
||||
contentConf: {
|
||||
opacity: 100
|
||||
}
|
||||
},
|
||||
baseConf: {
|
||||
begTime: '',
|
||||
endTime: '',
|
||||
language: 'chinese',
|
||||
tLimit: 0,
|
||||
answerBegTime: '',
|
||||
answerEndTime: '',
|
||||
answerLimitTime: 0
|
||||
},
|
||||
submitConf: {
|
||||
submitTitle: '',
|
||||
msgContent: {},
|
||||
confirmAgain: {
|
||||
is_again: true
|
||||
},
|
||||
link: ''
|
||||
},
|
||||
questionDataList: [],
|
||||
pageEditOne: 1,
|
||||
pageConf: [], // 分页逻辑
|
||||
logicConf: {
|
||||
showLogicConf: [],
|
||||
jumpLogicConf: []
|
||||
}
|
||||
})
|
||||
const { showLogicEngine, initShowLogicEngine, jumpLogicEngine, initJumpLogicEngine } =
|
||||
useLogicEngine(schema)
|
||||
|
||||
function initSchema({ metaData, codeData }: { metaData: any; codeData: any }) {
|
||||
schema.metaData = metaData
|
||||
schema.bannerConf = merge({}, schema.bannerConf, codeData.bannerConf)
|
||||
schema.bottomConf = merge({}, schema.bottomConf, codeData.bottomConf)
|
||||
schema.skinConf = merge({}, schema.skinConf, codeData.skinConf)
|
||||
schema.baseConf = merge({}, schema.baseConf, codeData.baseConf)
|
||||
schema.submitConf = merge({}, schema.submitConf, codeData.submitConf)
|
||||
schema.questionDataList = codeData.questionDataList || []
|
||||
schema.logicConf = codeData.logicConf
|
||||
schema.pageEditOne = 1
|
||||
schema.pageConf = codeData.pageConf
|
||||
}
|
||||
|
||||
const sessionId = ref('')
|
||||
async function initSessionId() {
|
||||
const sessionIdKey = `${surveyId.value}_sessionId`
|
||||
const localSessionId = sessionStorage.getItem(sessionIdKey)
|
||||
if (localSessionId) {
|
||||
sessionId.value = localSessionId
|
||||
} else {
|
||||
const res: Record<string, any> = await getSessionId({ surveyId: surveyId.value })
|
||||
if (res.code === 200) {
|
||||
sessionId.value = res.data.sessionId
|
||||
sessionStorage.setItem(sessionIdKey, sessionId.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getSchemaFromRemote() {
|
||||
const res: any = await getSurveyById(surveyId.value)
|
||||
if (res.code === 200) {
|
||||
const metaData = res.data.surveyMetaRes
|
||||
document.title = metaData.title
|
||||
const data = res.data.surveyConfRes.code
|
||||
const {
|
||||
bannerConf,
|
||||
bottomConf,
|
||||
skinConf,
|
||||
baseConf,
|
||||
submitConf,
|
||||
dataConf,
|
||||
logicConf = {}
|
||||
} = data
|
||||
if (!data.pageConf || data.pageConf.length === 0) {
|
||||
data.pageConf = [dataConf.dataList.length]
|
||||
}
|
||||
initSchema({
|
||||
metaData,
|
||||
codeData: {
|
||||
bannerConf,
|
||||
bottomConf,
|
||||
skinConf,
|
||||
baseConf,
|
||||
submitConf,
|
||||
questionDataList: dataConf.dataList,
|
||||
pageConf: data.pageConf,
|
||||
logicConf
|
||||
}
|
||||
})
|
||||
initializeSchemaCallBack()
|
||||
|
||||
initShowLogicEngine()
|
||||
initJumpLogicEngine()
|
||||
} else {
|
||||
throw new Error(res.errmsg || '问卷不存在')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
schema,
|
||||
getSchemaFromRemote,
|
||||
showLogicEngine,
|
||||
jumpLogicEngine,
|
||||
sessionId,
|
||||
initSessionId
|
||||
}
|
||||
}
|
20
web/src/management/stores/composables/useLogicEngine.ts
Normal file
20
web/src/management/stores/composables/useLogicEngine.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { ref, toRef } from 'vue'
|
||||
import { RuleBuild } from '@/common/logicEngine/RuleBuild'
|
||||
|
||||
export default function useLogicEngine(schema: any) {
|
||||
const logicConf = toRef(schema, 'logicConf')
|
||||
const showLogicEngine = ref()
|
||||
const jumpLogicEngine = ref()
|
||||
function initShowLogicEngine() {
|
||||
showLogicEngine.value = new RuleBuild().fromJson(logicConf.value?.showLogicConf)
|
||||
}
|
||||
function initJumpLogicEngine() {
|
||||
jumpLogicEngine.value = new RuleBuild().fromJson(logicConf.value?.jumpLogicConf)
|
||||
}
|
||||
return {
|
||||
showLogicEngine,
|
||||
jumpLogicEngine,
|
||||
initShowLogicEngine,
|
||||
initJumpLogicEngine
|
||||
}
|
||||
}
|
156
web/src/management/stores/composables/usePageEdit.ts
Normal file
156
web/src/management/stores/composables/usePageEdit.ts
Normal file
@ -0,0 +1,156 @@
|
||||
import { type Ref, computed } from 'vue'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
import { filterQuestionPreviewData, getNewField } from '@/management/utils/index'
|
||||
import { useShowLogicInfo } from '@/management/hooks/useShowLogicInfo'
|
||||
import { useJumpLogicInfo } from '@/management/hooks/useJumpLogicInfo'
|
||||
|
||||
export default function usePageEdit(
|
||||
{
|
||||
schema,
|
||||
questionDataList
|
||||
}: {
|
||||
schema: any
|
||||
questionDataList: Ref<any[]>
|
||||
},
|
||||
updateTime: () => void
|
||||
) {
|
||||
const pageConf = computed(() => schema.pageConf)
|
||||
const pageEditOne = computed(() => schema.pageEditOne)
|
||||
const isFinallyPage = computed(() => {
|
||||
return pageEditOne.value === pageConf.value.length
|
||||
})
|
||||
const pageCount = computed(() => pageConf.value.length || 0)
|
||||
|
||||
const pageQuestionData = computed(() => {
|
||||
return getPageQuestionData(pageEditOne.value)
|
||||
})
|
||||
|
||||
const getPageQuestionData = (index: number) => {
|
||||
const { startIndex, endIndex } = getSorter(index)
|
||||
return filterQuestionPreviewData(questionDataList.value).slice(startIndex, endIndex)
|
||||
}
|
||||
|
||||
const getSorter = (index?: number) => {
|
||||
let startIndex = 0
|
||||
const newPageEditOne = index || pageEditOne.value
|
||||
const endIndex = pageConf.value[newPageEditOne - 1]
|
||||
|
||||
for (let index = 0; index < pageConf.value.length; index++) {
|
||||
const item = pageConf.value[index]
|
||||
if (newPageEditOne - 1 == index) {
|
||||
break
|
||||
}
|
||||
startIndex += item
|
||||
}
|
||||
return {
|
||||
startIndex,
|
||||
endIndex: startIndex + endIndex
|
||||
}
|
||||
}
|
||||
|
||||
const addPage = () => {
|
||||
schema.pageConf.push(1)
|
||||
}
|
||||
|
||||
const updatePageEditOne = (index: number) => {
|
||||
schema.pageEditOne = index
|
||||
}
|
||||
|
||||
const deletePage = (index: number) => {
|
||||
if (pageConf.value.length <= 1) return
|
||||
const { startIndex, endIndex } = getSorter(index)
|
||||
const newQuestionList = cloneDeep(questionDataList.value)
|
||||
const deleteFields = newQuestionList
|
||||
.slice(startIndex, endIndex - startIndex)
|
||||
.map((i) => i.field)
|
||||
|
||||
// 删除分页判断题目是否存在逻辑关联
|
||||
const hasLogic = deleteFields.filter((field) => {
|
||||
const { hasShowLogic } = useShowLogicInfo(field)
|
||||
const { hasJumpLogic } = useJumpLogicInfo(field)
|
||||
return hasShowLogic || hasJumpLogic
|
||||
})
|
||||
if (hasLogic.length) {
|
||||
ElMessageBox.alert('该分页下有题目被显示或跳转逻辑关联,请先清除', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'warning'
|
||||
})
|
||||
return
|
||||
}
|
||||
updatePageEditOne(1)
|
||||
newQuestionList.splice(startIndex, endIndex - startIndex)
|
||||
schema.pageConf.splice(index - 1, 1)
|
||||
questionDataList.value = newQuestionList
|
||||
updateTime()
|
||||
}
|
||||
|
||||
const swapArrayRanges = (index: number, range: number) => {
|
||||
const { startIndex: start1, endIndex: end1 } = getSorter(index)
|
||||
const { startIndex: start2, endIndex: end2 } = getSorter(range)
|
||||
const newQuestion = cloneDeep(questionDataList.value)
|
||||
const range1 = newQuestion.slice(start1, end1)
|
||||
const range2 = newQuestion.slice(start2, end2)
|
||||
newQuestion.splice(start1, range1.length, ...range2)
|
||||
newQuestion.splice(start2, range2.length, ...range1)
|
||||
questionDataList.value = newQuestion
|
||||
const rangeCount = schema.pageConf[range - 1]
|
||||
schema.pageConf[range - 1] = schema.pageConf[index - 1]
|
||||
schema.pageConf[index - 1] = rangeCount
|
||||
updateTime()
|
||||
}
|
||||
|
||||
const copyPage = (index: number) => {
|
||||
const newQuestionList = cloneDeep(getPageQuestionData(index))
|
||||
newQuestionList.forEach((item) => {
|
||||
item.field = getNewField(questionDataList.value.map((item) => item.field))
|
||||
})
|
||||
schema.pageConf.splice(index, 0, newQuestionList.length)
|
||||
const { endIndex } = getSorter(index)
|
||||
questionDataList.value.splice(endIndex, 0, ...newQuestionList)
|
||||
updateTime()
|
||||
}
|
||||
|
||||
const pageOperations = (type: string) => {
|
||||
const count = pageConf.value[pageEditOne.value - 1]
|
||||
if (type == 'add') {
|
||||
if (count != undefined) {
|
||||
schema.pageConf[pageEditOne.value - 1] = count + 1
|
||||
}
|
||||
return
|
||||
}
|
||||
if (type == 'remove') {
|
||||
if (count) {
|
||||
schema.pageConf[pageEditOne.value - 1] = count - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const setPage = (data: Array<number>) => {
|
||||
for (let index = 0; index < pageConf.value.length; index++) {
|
||||
const newIndex = data[index]
|
||||
const oldIndex = pageConf.value[index]
|
||||
if (newIndex != oldIndex) {
|
||||
schema.pageConf[index] = newIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
pageEditOne,
|
||||
pageConf,
|
||||
isFinallyPage,
|
||||
pageCount,
|
||||
pageQuestionData,
|
||||
getSorter,
|
||||
updatePageEditOne,
|
||||
deletePage,
|
||||
addPage,
|
||||
copyPage,
|
||||
getPageQuestionData,
|
||||
pageOperations,
|
||||
swapArrayRanges,
|
||||
setPage
|
||||
}
|
||||
}
|
72
web/src/management/stores/composables/useQuestionData.ts
Normal file
72
web/src/management/stores/composables/useQuestionData.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { type Ref } from 'vue'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
import { getNewField } from '@/management/utils'
|
||||
import { type TypeMethod } from '../composables/useBaseConfig'
|
||||
|
||||
export default function useQuestionData({
|
||||
questionDataList,
|
||||
updateTime,
|
||||
pageOperations,
|
||||
updateCounts
|
||||
}: {
|
||||
questionDataList: Ref<any[]>
|
||||
updateTime: () => void
|
||||
pageOperations: (type: string) => void
|
||||
updateCounts: (type: TypeMethod, data: any) => void
|
||||
}) {
|
||||
function copyQuestion({ index }: { index: number }) {
|
||||
const newQuestion = cloneDeep(questionDataList.value[index])
|
||||
newQuestion.field = getNewField(questionDataList.value.map((item) => item.field))
|
||||
addQuestion({ question: newQuestion, index })
|
||||
}
|
||||
|
||||
function addQuestion({ question, index }: { question: any; index: number }) {
|
||||
questionDataList.value.splice(index, 0, question)
|
||||
pageOperations('add')
|
||||
updateTime()
|
||||
updateCounts('ADD', { question })
|
||||
}
|
||||
|
||||
function deleteQuestion({ index }: { index: number }) {
|
||||
pageOperations('remove')
|
||||
const [question] = questionDataList.value.splice(index, 1)
|
||||
updateTime()
|
||||
updateCounts('REMOVE', { question })
|
||||
}
|
||||
|
||||
function moveQuestion({ index, range }: { index: number; range: number }) {
|
||||
console.log('move')
|
||||
let start, end
|
||||
if (range < 0) {
|
||||
// 向上移动
|
||||
start = index + range
|
||||
end = index
|
||||
} else if (range > 0) {
|
||||
// 向下移动
|
||||
start = index + 1
|
||||
end = index + range + 1
|
||||
} else {
|
||||
// 无变化
|
||||
return
|
||||
}
|
||||
const currentData = questionDataList.value[index]
|
||||
// 新位置和老位置之间所有的题目
|
||||
const comparedList = questionDataList.value.slice(start, end)
|
||||
if (range < 0) {
|
||||
// 向上移动
|
||||
questionDataList.value.splice(index + range, 1 - range, currentData, ...comparedList)
|
||||
} else if (range > 0) {
|
||||
// 向下移动
|
||||
questionDataList.value.splice(index, range + 1, ...comparedList, currentData)
|
||||
}
|
||||
updateTime()
|
||||
}
|
||||
|
||||
return {
|
||||
copyQuestion,
|
||||
addQuestion,
|
||||
deleteQuestion,
|
||||
moveQuestion
|
||||
}
|
||||
}
|
@ -1,534 +1,27 @@
|
||||
import { type Ref, ref, reactive, toRef, computed } from 'vue'
|
||||
import { type Ref, ref, toRef, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import {
|
||||
merge as _merge,
|
||||
cloneDeep as _cloneDeep,
|
||||
set as _set,
|
||||
isNumber as _isNumber
|
||||
} from 'lodash-es'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { set as _set, isNumber as _isNumber } from 'lodash-es'
|
||||
import { QUESTION_TYPE } from '@/common/typeEnum'
|
||||
import { getQuestionByType } from '@/management/utils/index'
|
||||
import { filterQuestionPreviewData } from '@/management/utils/index'
|
||||
|
||||
import { getSurveyById, getSessionId } from '@/management/api/survey'
|
||||
import { getNewField } from '@/management/utils'
|
||||
|
||||
import submitFormConfig from '@/management/pages/edit/setterConfig/submitConfig'
|
||||
|
||||
import questionLoader from '@/materials/questions/questionLoader'
|
||||
import { SurveyPermissions } from '@/management/utils/types/workSpace'
|
||||
import { getBannerData } from '@/management/api/skin.js'
|
||||
import { getCollaboratorPermissions } from '@/management/api/space'
|
||||
import useEditGlobalBaseConf, { type TypeMethod } from './composables/useEditGlobalBaseConf'
|
||||
|
||||
import useInitializeSchema from './composables/useInitializeSchema'
|
||||
import useBaseConfig from './composables/useBaseConfig'
|
||||
import useQuestionData from './composables/useQuestionData'
|
||||
import useCurrentEdit from './composables/useCurrentEdit'
|
||||
import usePageEdit from './composables/usePageEdit'
|
||||
|
||||
import { CODE_MAP } from '../api/base'
|
||||
import { RuleBuild } from '@/common/logicEngine/RuleBuild'
|
||||
import { useShowLogicInfo } from '@/management/hooks/useShowLogicInfo'
|
||||
import { useJumpLogicInfo } from '@/management/hooks/useJumpLogicInfo'
|
||||
|
||||
const innerMetaConfig = {
|
||||
submit: {
|
||||
title: '提交配置',
|
||||
formConfig: submitFormConfig
|
||||
}
|
||||
}
|
||||
|
||||
function useInitializeSchema(surveyId: Ref<string>, initializeSchemaCallBack: () => void) {
|
||||
const schema = reactive({
|
||||
metaData: null,
|
||||
bannerConf: {
|
||||
titleConfig: {
|
||||
mainTitle: '<h3 style="text-align: center">欢迎填写问卷</h3>',
|
||||
subTitle: `<p>为了给您提供更好的服务,希望您能抽出几分钟时间,将您的感受和建议告诉我们,<span style="color: rgb(204, 0, 0)">期待您的参与!</span></p>`,
|
||||
applyTitle: ''
|
||||
},
|
||||
bannerConfig: {
|
||||
bgImage: '',
|
||||
bgImageAllowJump: false,
|
||||
bgImageJumpLink: '',
|
||||
videoLink: '',
|
||||
postImg: ''
|
||||
}
|
||||
},
|
||||
bottomConf: {
|
||||
logoImage: '',
|
||||
logoImageWidth: '28%'
|
||||
},
|
||||
skinConf: {
|
||||
backgroundConf: {
|
||||
color: '#fff'
|
||||
},
|
||||
themeConf: {
|
||||
color: '#ffa600'
|
||||
},
|
||||
contentConf: {
|
||||
opacity: 100
|
||||
}
|
||||
},
|
||||
baseConf: {
|
||||
begTime: '',
|
||||
endTime: '',
|
||||
language: 'chinese',
|
||||
tLimit: 0,
|
||||
answerBegTime: '',
|
||||
answerEndTime: '',
|
||||
answerLimitTime: 0
|
||||
},
|
||||
submitConf: {
|
||||
submitTitle: '',
|
||||
msgContent: {},
|
||||
confirmAgain: {
|
||||
is_again: true
|
||||
},
|
||||
link: ''
|
||||
},
|
||||
questionDataList: [],
|
||||
pageEditOne: 1,
|
||||
pageConf: [], // 分页逻辑
|
||||
logicConf: {
|
||||
showLogicConf: [],
|
||||
jumpLogicConf: []
|
||||
}
|
||||
})
|
||||
const { showLogicEngine, initShowLogicEngine, jumpLogicEngine, initJumpLogicEngine } =
|
||||
useLogicEngine(schema)
|
||||
|
||||
function initSchema({ metaData, codeData }: { metaData: any; codeData: any }) {
|
||||
schema.metaData = metaData
|
||||
schema.bannerConf = _merge({}, schema.bannerConf, codeData.bannerConf)
|
||||
schema.bottomConf = _merge({}, schema.bottomConf, codeData.bottomConf)
|
||||
schema.skinConf = _merge({}, schema.skinConf, codeData.skinConf)
|
||||
schema.baseConf = _merge({}, schema.baseConf, codeData.baseConf)
|
||||
schema.submitConf = _merge({}, schema.submitConf, codeData.submitConf)
|
||||
schema.questionDataList = codeData.questionDataList || []
|
||||
schema.logicConf = codeData.logicConf
|
||||
schema.pageEditOne = 1
|
||||
schema.pageConf = codeData.pageConf
|
||||
}
|
||||
|
||||
const sessionId = ref('')
|
||||
async function initSessionId() {
|
||||
const sessionIdKey = `${surveyId.value}_sessionId`
|
||||
const localSessionId = sessionStorage.getItem(sessionIdKey)
|
||||
if (localSessionId) {
|
||||
sessionId.value = localSessionId
|
||||
} else {
|
||||
const res: Record<string, any> = await getSessionId({ surveyId: surveyId.value })
|
||||
if (res.code === 200) {
|
||||
sessionId.value = res.data.sessionId
|
||||
sessionStorage.setItem(sessionIdKey, sessionId.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getSchemaFromRemote() {
|
||||
const res: any = await getSurveyById(surveyId.value)
|
||||
if (res.code === 200) {
|
||||
const metaData = res.data.surveyMetaRes
|
||||
document.title = metaData.title
|
||||
const data = res.data.surveyConfRes.code
|
||||
const {
|
||||
bannerConf,
|
||||
bottomConf,
|
||||
skinConf,
|
||||
baseConf,
|
||||
submitConf,
|
||||
dataConf,
|
||||
logicConf = {}
|
||||
} = data
|
||||
if (!data.pageConf || data.pageConf.length === 0) {
|
||||
data.pageConf = [dataConf.dataList.length]
|
||||
}
|
||||
initSchema({
|
||||
metaData,
|
||||
codeData: {
|
||||
bannerConf,
|
||||
bottomConf,
|
||||
skinConf,
|
||||
baseConf,
|
||||
submitConf,
|
||||
questionDataList: dataConf.dataList,
|
||||
pageConf: data.pageConf,
|
||||
logicConf
|
||||
}
|
||||
})
|
||||
initializeSchemaCallBack()
|
||||
|
||||
initShowLogicEngine()
|
||||
initJumpLogicEngine()
|
||||
} else {
|
||||
throw new Error(res.errmsg || '问卷不存在')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
schema,
|
||||
getSchemaFromRemote,
|
||||
showLogicEngine,
|
||||
jumpLogicEngine,
|
||||
sessionId,
|
||||
initSessionId
|
||||
}
|
||||
}
|
||||
|
||||
function useQuestionDataListOperations({
|
||||
questionDataList,
|
||||
updateTime,
|
||||
pageOperations,
|
||||
updateCounts
|
||||
}: {
|
||||
questionDataList: Ref<any[]>
|
||||
updateTime: () => void
|
||||
pageOperations: (type: string) => void
|
||||
updateCounts: (type: TypeMethod, data: any) => void
|
||||
}) {
|
||||
function copyQuestion({ index }: { index: number }) {
|
||||
const newQuestion = _cloneDeep(questionDataList.value[index])
|
||||
newQuestion.field = getNewField(questionDataList.value.map((item) => item.field))
|
||||
addQuestion({ question: newQuestion, index })
|
||||
}
|
||||
|
||||
function addQuestion({ question, index }: { question: any; index: number }) {
|
||||
questionDataList.value.splice(index, 0, question)
|
||||
pageOperations('add')
|
||||
updateTime()
|
||||
updateCounts('ADD', { question })
|
||||
}
|
||||
|
||||
function deleteQuestion({ index }: { index: number }) {
|
||||
pageOperations('remove')
|
||||
const [question] = questionDataList.value.splice(index, 1)
|
||||
updateTime()
|
||||
updateCounts('REMOVE', { question })
|
||||
}
|
||||
|
||||
function moveQuestion({ index, range }: { index: number; range: number }) {
|
||||
console.log('move')
|
||||
let start, end
|
||||
if (range < 0) {
|
||||
// 向上移动
|
||||
start = index + range
|
||||
end = index
|
||||
} else if (range > 0) {
|
||||
// 向下移动
|
||||
start = index + 1
|
||||
end = index + range + 1
|
||||
} else {
|
||||
// 无变化
|
||||
return
|
||||
}
|
||||
const currentData = questionDataList.value[index]
|
||||
// 新位置和老位置之间所有的题目
|
||||
const comparedList = questionDataList.value.slice(start, end)
|
||||
if (range < 0) {
|
||||
// 向上移动
|
||||
questionDataList.value.splice(index + range, 1 - range, currentData, ...comparedList)
|
||||
} else if (range > 0) {
|
||||
// 向下移动
|
||||
questionDataList.value.splice(index, range + 1, ...comparedList, currentData)
|
||||
}
|
||||
updateTime()
|
||||
}
|
||||
|
||||
return {
|
||||
copyQuestion,
|
||||
addQuestion,
|
||||
deleteQuestion,
|
||||
moveQuestion
|
||||
}
|
||||
}
|
||||
|
||||
function useCurrentEdit({
|
||||
schema,
|
||||
questionDataList
|
||||
}: {
|
||||
schema: any
|
||||
questionDataList: Ref<any[]>
|
||||
}) {
|
||||
const currentEditOne = ref()
|
||||
const currentEditStatus = ref('Success')
|
||||
|
||||
const currentEditKey = computed(() => {
|
||||
if (currentEditOne.value === null) {
|
||||
return null
|
||||
}
|
||||
let key = ''
|
||||
switch (currentEditOne.value) {
|
||||
case 'banner':
|
||||
case 'mainTitle':
|
||||
key = 'bannerConf'
|
||||
break
|
||||
case 'submit':
|
||||
key = 'submitConf'
|
||||
break
|
||||
case 'logo':
|
||||
key = 'bottomConf'
|
||||
break
|
||||
default:
|
||||
key = `questionDataList.${currentEditOne.value}`
|
||||
}
|
||||
return key
|
||||
})
|
||||
|
||||
const currentEditMeta = computed(() => {
|
||||
if (currentEditOne.value === null) {
|
||||
return null
|
||||
} else if (innerMetaConfig[currentEditOne.value as keyof typeof innerMetaConfig]) {
|
||||
return innerMetaConfig[currentEditOne.value as keyof typeof innerMetaConfig]
|
||||
} else {
|
||||
const questionType = questionDataList.value?.[currentEditOne.value]?.type
|
||||
return questionLoader.getMeta(questionType)
|
||||
}
|
||||
})
|
||||
|
||||
const moduleConfig = computed(() => {
|
||||
if (currentEditOne.value === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (currentEditOne.value === 'banner' || currentEditOne.value === 'mainTitle') {
|
||||
return schema?.bannerConf
|
||||
} else if (currentEditOne.value === 'submit') {
|
||||
return schema?.submitConf
|
||||
} else if (currentEditOne.value === 'logo') {
|
||||
return schema?.bottomConf
|
||||
} else if (!Number.isNaN(currentEditOne.value)) {
|
||||
return questionDataList.value?.[currentEditOne.value]
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
const formConfigList = computed(() => {
|
||||
if (currentEditOne.value === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return currentEditMeta.value?.formConfig || []
|
||||
})
|
||||
|
||||
function setCurrentEditOne(data: any) {
|
||||
currentEditOne.value = data
|
||||
}
|
||||
|
||||
function changeCurrentEditStatus(status: string) {
|
||||
currentEditStatus.value = status
|
||||
}
|
||||
|
||||
return {
|
||||
currentEditOne,
|
||||
currentEditKey,
|
||||
currentEditStatus,
|
||||
moduleConfig,
|
||||
formConfigList,
|
||||
currentEditMeta,
|
||||
setCurrentEditOne,
|
||||
changeCurrentEditStatus
|
||||
}
|
||||
}
|
||||
|
||||
function usePageEdit(
|
||||
{
|
||||
schema,
|
||||
questionDataList
|
||||
}: {
|
||||
schema: any
|
||||
questionDataList: Ref<any[]>
|
||||
},
|
||||
updateTime: () => void
|
||||
) {
|
||||
const pageConf = computed(() => schema.pageConf)
|
||||
const pageEditOne = computed(() => schema.pageEditOne)
|
||||
const isFinallyPage = computed(() => {
|
||||
return pageEditOne.value === pageConf.value.length
|
||||
})
|
||||
const pageCount = computed(() => pageConf.value.length || 0)
|
||||
|
||||
const pageQuestionData = computed(() => {
|
||||
return getPageQuestionData(pageEditOne.value)
|
||||
})
|
||||
|
||||
const getPageQuestionData = (index: number) => {
|
||||
const { startIndex, endIndex } = getSorter(index)
|
||||
return filterQuestionPreviewData(questionDataList.value).slice(startIndex, endIndex)
|
||||
}
|
||||
|
||||
const getSorter = (index?: number) => {
|
||||
let startIndex = 0
|
||||
const newPageEditOne = index || pageEditOne.value
|
||||
const endIndex = pageConf.value[newPageEditOne - 1]
|
||||
|
||||
for (let index = 0; index < pageConf.value.length; index++) {
|
||||
const item = pageConf.value[index]
|
||||
if (newPageEditOne - 1 == index) {
|
||||
break
|
||||
}
|
||||
startIndex += item
|
||||
}
|
||||
return {
|
||||
startIndex,
|
||||
endIndex: startIndex + endIndex
|
||||
}
|
||||
}
|
||||
|
||||
const addPage = () => {
|
||||
schema.pageConf.push(1)
|
||||
}
|
||||
|
||||
const updatePageEditOne = (index: number) => {
|
||||
schema.pageEditOne = index
|
||||
}
|
||||
|
||||
const deletePage = (index: number) => {
|
||||
if (pageConf.value.length <= 1) return
|
||||
const { startIndex, endIndex } = getSorter(index)
|
||||
const newQuestionList = _cloneDeep(questionDataList.value)
|
||||
const deleteFields = newQuestionList
|
||||
.slice(startIndex, endIndex - startIndex)
|
||||
.map((i) => i.field)
|
||||
|
||||
// 删除分页判断题目是否存在逻辑关联
|
||||
const hasLogic = deleteFields.filter((field) => {
|
||||
const { hasShowLogic } = useShowLogicInfo(field)
|
||||
const { hasJumpLogic } = useJumpLogicInfo(field)
|
||||
return hasShowLogic || hasJumpLogic
|
||||
})
|
||||
if (hasLogic.length) {
|
||||
ElMessageBox.alert('该分页下有题目被显示或跳转逻辑关联,请先清除', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'warning'
|
||||
})
|
||||
return
|
||||
}
|
||||
updatePageEditOne(1)
|
||||
newQuestionList.splice(startIndex, endIndex - startIndex)
|
||||
schema.pageConf.splice(index - 1, 1)
|
||||
questionDataList.value = newQuestionList
|
||||
updateTime()
|
||||
}
|
||||
|
||||
const swapArrayRanges = (index: number, range: number) => {
|
||||
const { startIndex: start1, endIndex: end1 } = getSorter(index)
|
||||
const { startIndex: start2, endIndex: end2 } = getSorter(range)
|
||||
const newQuestion = _cloneDeep(questionDataList.value)
|
||||
const range1 = newQuestion.slice(start1, end1)
|
||||
const range2 = newQuestion.slice(start2, end2)
|
||||
newQuestion.splice(start1, range1.length, ...range2)
|
||||
newQuestion.splice(start2, range2.length, ...range1)
|
||||
questionDataList.value = newQuestion
|
||||
const rangeCount = schema.pageConf[range - 1]
|
||||
schema.pageConf[range - 1] = schema.pageConf[index - 1]
|
||||
schema.pageConf[index - 1] = rangeCount
|
||||
updateTime()
|
||||
}
|
||||
|
||||
const copyPage = (index: number) => {
|
||||
const newQuestionList = _cloneDeep(getPageQuestionData(index))
|
||||
newQuestionList.forEach((item) => {
|
||||
item.field = getNewField(questionDataList.value.map((item) => item.field))
|
||||
})
|
||||
schema.pageConf.splice(index, 0, newQuestionList.length)
|
||||
const { endIndex } = getSorter(index)
|
||||
questionDataList.value.splice(endIndex, 0, ...newQuestionList)
|
||||
updateTime()
|
||||
}
|
||||
|
||||
const pageOperations = (type: string) => {
|
||||
const count = pageConf.value[pageEditOne.value - 1]
|
||||
if (type == 'add') {
|
||||
if (count != undefined) {
|
||||
schema.pageConf[pageEditOne.value - 1] = count + 1
|
||||
}
|
||||
return
|
||||
}
|
||||
if (type == 'remove') {
|
||||
if (count) {
|
||||
schema.pageConf[pageEditOne.value - 1] = count - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const setPage = (data: Array<number>) => {
|
||||
for (let index = 0; index < pageConf.value.length; index++) {
|
||||
const newIndex = data[index]
|
||||
const oldIndex = pageConf.value[index]
|
||||
if (newIndex != oldIndex) {
|
||||
schema.pageConf[index] = newIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
pageEditOne,
|
||||
pageConf,
|
||||
isFinallyPage,
|
||||
pageCount,
|
||||
pageQuestionData,
|
||||
getSorter,
|
||||
updatePageEditOne,
|
||||
deletePage,
|
||||
addPage,
|
||||
copyPage,
|
||||
getPageQuestionData,
|
||||
pageOperations,
|
||||
swapArrayRanges,
|
||||
setPage
|
||||
}
|
||||
}
|
||||
|
||||
function useLogicEngine(schema: any) {
|
||||
const logicConf = toRef(schema, 'logicConf')
|
||||
const showLogicEngine = ref()
|
||||
const jumpLogicEngine = ref()
|
||||
function initShowLogicEngine() {
|
||||
showLogicEngine.value = new RuleBuild().fromJson(logicConf.value?.showLogicConf)
|
||||
}
|
||||
function initJumpLogicEngine() {
|
||||
jumpLogicEngine.value = new RuleBuild().fromJson(logicConf.value?.jumpLogicConf)
|
||||
}
|
||||
return {
|
||||
showLogicEngine,
|
||||
jumpLogicEngine,
|
||||
initShowLogicEngine,
|
||||
initJumpLogicEngine
|
||||
}
|
||||
}
|
||||
|
||||
type IBannerItem = {
|
||||
name: string
|
||||
key: string
|
||||
list: Array<Object>
|
||||
}
|
||||
type IBannerList = Record<string, IBannerItem>
|
||||
|
||||
export const useEditStore = defineStore('edit', () => {
|
||||
const bannerList: Ref<IBannerList> = ref({})
|
||||
const fetchBannerData = async () => {
|
||||
const res: any = await getBannerData()
|
||||
if (res.code === CODE_MAP.SUCCESS) {
|
||||
bannerList.value = res.data
|
||||
}
|
||||
}
|
||||
|
||||
const cooperPermissions = ref(Object.values(SurveyPermissions))
|
||||
const fetchCooperPermissions = async (id: string) => {
|
||||
const res: any = await getCollaboratorPermissions(id)
|
||||
if (res.code === CODE_MAP.SUCCESS) {
|
||||
cooperPermissions.value = res.data.permissions
|
||||
}
|
||||
}
|
||||
|
||||
const schemaUpdateTime = ref(Date.now())
|
||||
function updateTime() {
|
||||
schemaUpdateTime.value = Date.now()
|
||||
}
|
||||
|
||||
const surveyId = ref('')
|
||||
function setSurveyId(id: string) {
|
||||
surveyId.value = id
|
||||
}
|
||||
|
||||
// 初始化schem相关
|
||||
// 初始化问卷内容
|
||||
const {
|
||||
schema,
|
||||
sessionId,
|
||||
@ -539,45 +32,45 @@ export const useEditStore = defineStore('edit', () => {
|
||||
} = useInitializeSchema(surveyId, () => {
|
||||
editGlobalBaseConf.initCounts()
|
||||
})
|
||||
|
||||
function changeSchema({ key, value }: { key: string; value: any }) {
|
||||
_set(schema, key, value)
|
||||
updateTime()
|
||||
async function init() {
|
||||
const { metaData } = schema
|
||||
if (!metaData || (metaData as any)?._id !== surveyId.value) {
|
||||
await Promise.all([getSchemaFromRemote(), initSessionId()])
|
||||
}
|
||||
currentEditOne.value = null
|
||||
currentEditStatus.value = 'Success'
|
||||
}
|
||||
|
||||
function changeThemePreset(presets: any) {
|
||||
Object.keys(presets).forEach((key) => {
|
||||
_set(schema, key, presets[key])
|
||||
})
|
||||
// 问卷协作权限
|
||||
const cooperPermissions = ref(Object.values(SurveyPermissions))
|
||||
const fetchCooperPermissions = async (id: string) => {
|
||||
const res: any = await getCollaboratorPermissions(id)
|
||||
if (res.code === CODE_MAP.SUCCESS) {
|
||||
cooperPermissions.value = res.data.permissions
|
||||
}
|
||||
|
||||
return res.data.permissions
|
||||
}
|
||||
|
||||
// 问卷题目列表
|
||||
const questionDataList = toRef(schema, 'questionDataList')
|
||||
|
||||
const editGlobalBaseConf = useEditGlobalBaseConf(questionDataList, updateTime)
|
||||
function setQuestionDataList(data: any) {
|
||||
schema.questionDataList = data
|
||||
}
|
||||
// 整卷配置
|
||||
const editGlobalBaseConf = useBaseConfig(questionDataList, updateTime)
|
||||
|
||||
const createNewQuestion = ({ type }: { type: QUESTION_TYPE }) => {
|
||||
const fields = questionDataList.value.map((item: any) => item.field)
|
||||
const newQuestion = getQuestionByType(type, fields)
|
||||
newQuestion.title = newQuestion.title = `标题${newQuestionIndex.value + 1}`
|
||||
if (type === QUESTION_TYPE.VOTE) {
|
||||
newQuestion.innerType = QUESTION_TYPE.RADIO
|
||||
}
|
||||
return newQuestion
|
||||
}
|
||||
|
||||
// 题目大纲移动题目顺序
|
||||
const compareQuestionSeq = (val: Array<any>) => {
|
||||
const newSeq: Array<string> = []
|
||||
const oldSeq: Array<string> = []
|
||||
let status = false
|
||||
val.map((v) => {
|
||||
for (const v of val) {
|
||||
newSeq.push(v.field)
|
||||
})
|
||||
;(questionDataList.value as Array<any>).map((v) => {
|
||||
}
|
||||
for (const v of questionDataList.value as Array<any>) {
|
||||
oldSeq.push(v.field)
|
||||
})
|
||||
}
|
||||
for (let index = 0; index < newSeq.length; index++) {
|
||||
if (newSeq[index] !== oldSeq[index]) {
|
||||
status = true
|
||||
@ -589,40 +82,38 @@ export const useEditStore = defineStore('edit', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const newQuestionIndex = computed(() => {
|
||||
if (_isNumber(currentEditOne.value)) {
|
||||
return currentEditOne.value + 1
|
||||
} else {
|
||||
const pageConf = schema.pageConf
|
||||
const questCount = pageConf[schema.pageEditOne - 1]
|
||||
const { startIndex, endIndex } = getSorter()
|
||||
if (!questCount) {
|
||||
return startIndex
|
||||
}
|
||||
return endIndex
|
||||
// 画布区域移动题目顺序
|
||||
function moveQuestionDataList(data: any) {
|
||||
const { startIndex, endIndex } = getSorter()
|
||||
const newData = [
|
||||
...questionDataList.value.slice(0, startIndex),
|
||||
...data,
|
||||
...questionDataList.value.slice(endIndex)
|
||||
]
|
||||
const countTotal: number = (schema.pageConf as Array<number>).reduce(
|
||||
(v: number, i: number) => v + i
|
||||
)
|
||||
if (countTotal != newData.length) {
|
||||
schema.pageConf[pageEditOne.value - 1] = (schema.pageConf[pageEditOne.value - 1] + 1) as never
|
||||
}
|
||||
})
|
||||
setQuestionDataList(newData)
|
||||
}
|
||||
|
||||
// 当前编辑题目
|
||||
const {
|
||||
currentEditOne,
|
||||
currentEditKey,
|
||||
currentEditStatus,
|
||||
moduleConfig,
|
||||
formConfigList,
|
||||
currentEditMeta,
|
||||
setCurrentEditOne,
|
||||
changeCurrentEditStatus
|
||||
} = useCurrentEdit({ schema, questionDataList })
|
||||
// 问卷schema更新
|
||||
const schemaUpdateTime = ref(Date.now())
|
||||
function updateTime() {
|
||||
schemaUpdateTime.value = Date.now()
|
||||
}
|
||||
|
||||
// 初始化问卷
|
||||
async function init() {
|
||||
const { metaData } = schema
|
||||
if (!metaData || (metaData as any)?._id !== surveyId.value) {
|
||||
await Promise.all([getSchemaFromRemote(), initSessionId()])
|
||||
}
|
||||
currentEditOne.value = null
|
||||
currentEditStatus.value = 'Success'
|
||||
function changeSchema({ key, value }: { key: string; value: any }) {
|
||||
_set(schema, key, value)
|
||||
updateTime()
|
||||
}
|
||||
|
||||
function changeThemePreset(presets: any) {
|
||||
Object.keys(presets).forEach((key) => {
|
||||
_set(schema, key, presets[key])
|
||||
})
|
||||
}
|
||||
|
||||
// 分页相关
|
||||
@ -642,42 +133,60 @@ export const useEditStore = defineStore('edit', () => {
|
||||
swapArrayRanges,
|
||||
setPage
|
||||
} = usePageEdit({ schema, questionDataList }, updateTime)
|
||||
|
||||
// 问卷列表相关操作
|
||||
const { copyQuestion, addQuestion, deleteQuestion, moveQuestion } = useQuestionDataListOperations(
|
||||
{
|
||||
questionDataList,
|
||||
updateTime,
|
||||
pageOperations,
|
||||
updateCounts: editGlobalBaseConf.updateCounts
|
||||
// 增加新分页时新增题目
|
||||
const createNewQuestion = ({ type }: { type: QUESTION_TYPE }) => {
|
||||
const fields = questionDataList.value.map((item: any) => item.field)
|
||||
const newQuestion = getQuestionByType(type, fields)
|
||||
newQuestion.title = newQuestion.title = `标题${newQuestionIndex.value + 1}`
|
||||
if (type === QUESTION_TYPE.VOTE) {
|
||||
newQuestion.innerType = QUESTION_TYPE.RADIO
|
||||
}
|
||||
)
|
||||
|
||||
function moveQuestionDataList(data: any) {
|
||||
const { startIndex, endIndex } = getSorter()
|
||||
const newData = [
|
||||
...questionDataList.value.slice(0, startIndex),
|
||||
...data,
|
||||
...questionDataList.value.slice(endIndex)
|
||||
]
|
||||
const countTotal: number = (schema.pageConf as Array<number>).reduce(
|
||||
(v: number, i: number) => v + i
|
||||
)
|
||||
if (countTotal != newData.length) {
|
||||
schema.pageConf[pageEditOne.value - 1] = (schema.pageConf[pageEditOne.value - 1] + 1) as never
|
||||
}
|
||||
setQuestionDataList(newData)
|
||||
return newQuestion
|
||||
}
|
||||
|
||||
// 当前编辑的题目信息
|
||||
const {
|
||||
currentEditOne,
|
||||
currentEditKey,
|
||||
currentEditStatus,
|
||||
moduleConfig,
|
||||
formConfigList,
|
||||
currentEditMeta,
|
||||
setCurrentEditOne,
|
||||
changeCurrentEditStatus
|
||||
} = useCurrentEdit({ schema, questionDataList })
|
||||
|
||||
const newQuestionIndex = computed(() => {
|
||||
if (_isNumber(currentEditOne.value)) {
|
||||
return currentEditOne.value + 1
|
||||
} else {
|
||||
const pageConf = schema.pageConf
|
||||
const questCount = pageConf[schema.pageEditOne - 1]
|
||||
const { startIndex, endIndex } = getSorter()
|
||||
if (!questCount) {
|
||||
return startIndex
|
||||
}
|
||||
return endIndex
|
||||
}
|
||||
})
|
||||
|
||||
// 题目操作:增删改
|
||||
const { copyQuestion, addQuestion, deleteQuestion, moveQuestion } = useQuestionData({
|
||||
questionDataList,
|
||||
updateTime,
|
||||
pageOperations,
|
||||
updateCounts: editGlobalBaseConf.updateCounts
|
||||
})
|
||||
|
||||
return {
|
||||
editGlobalBaseConf,
|
||||
surveyId,
|
||||
sessionId,
|
||||
setSurveyId,
|
||||
bannerList,
|
||||
fetchBannerData,
|
||||
|
||||
cooperPermissions,
|
||||
fetchCooperPermissions,
|
||||
|
||||
currentEditOne,
|
||||
moduleConfig,
|
||||
formConfigList,
|
||||
@ -687,6 +196,7 @@ export const useEditStore = defineStore('edit', () => {
|
||||
newQuestionIndex,
|
||||
setCurrentEditOne,
|
||||
changeCurrentEditStatus,
|
||||
|
||||
pageEditOne,
|
||||
pageConf,
|
||||
isFinallyPage,
|
||||
@ -700,6 +210,7 @@ export const useEditStore = defineStore('edit', () => {
|
||||
copyPage,
|
||||
swapArrayRanges,
|
||||
setPage,
|
||||
|
||||
schemaUpdateTime,
|
||||
schema,
|
||||
questionDataList,
|
||||
@ -715,6 +226,7 @@ export const useEditStore = defineStore('edit', () => {
|
||||
changeSchema,
|
||||
changeThemePreset,
|
||||
compareQuestionSeq,
|
||||
|
||||
showLogicEngine,
|
||||
jumpLogicEngine
|
||||
}
|
||||
|
@ -1,30 +1,29 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import localstorage from '@/common/localstorage'
|
||||
import { getUserInfo, setUserInfo, clearUserInfo } from '@/management/utils/storage'
|
||||
|
||||
type IUserInfo = {
|
||||
username: string
|
||||
token: string
|
||||
}
|
||||
|
||||
const USER_INFO_KEY = 'surveyUserInfo'
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
const userInfo = ref<IUserInfo | null>({
|
||||
username: '',
|
||||
token: ''
|
||||
})
|
||||
const hasLogined = ref(false)
|
||||
const hasLogin = ref(false)
|
||||
const loginTime = ref<number | null>(null)
|
||||
const initialized = ref(false)
|
||||
|
||||
const init = () => {
|
||||
const localData = localstorage.getItem(USER_INFO_KEY)
|
||||
const localData = getUserInfo()
|
||||
if (localData) {
|
||||
try {
|
||||
const { userInfo: info, loginTime: time } = localData as any
|
||||
if (Date.now() - time > 7 * 3600000) {
|
||||
localstorage.removeItem(USER_INFO_KEY)
|
||||
clearUserInfo()
|
||||
} else {
|
||||
login(info)
|
||||
}
|
||||
@ -36,18 +35,18 @@ export const useUserStore = defineStore('user', () => {
|
||||
}
|
||||
const login = (data: IUserInfo) => {
|
||||
userInfo.value = data
|
||||
hasLogined.value = true
|
||||
hasLogin.value = true
|
||||
loginTime.value = Date.now()
|
||||
localstorage.setItem(USER_INFO_KEY, {
|
||||
setUserInfo({
|
||||
userInfo: data,
|
||||
loginTime: loginTime
|
||||
})
|
||||
}
|
||||
const logout = () => {
|
||||
userInfo.value = null
|
||||
hasLogined.value = false
|
||||
localstorage.removeItem(USER_INFO_KEY)
|
||||
hasLogin.value = false
|
||||
clearUserInfo()
|
||||
}
|
||||
|
||||
return { userInfo, hasLogined, loginTime, initialized, init, login, logout }
|
||||
return { userInfo, hasLogin, loginTime, initialized, init, login, logout }
|
||||
})
|
||||
|
22
web/src/management/utils/storage.ts
Normal file
22
web/src/management/utils/storage.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export const getUserInfo = (): any => {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem('surveyUserInfo') as string) || {}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
export const setUserInfo = (params: { userInfo: any; loginTime: any }) => {
|
||||
const { userInfo, loginTime } = params
|
||||
localStorage.setItem(
|
||||
'surveyUserInfo',
|
||||
JSON.stringify({
|
||||
userInfo,
|
||||
loginTime
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export const clearUserInfo = () => localStorage.removeItem('surveyUserInfo')
|
@ -6,7 +6,7 @@ export default defineComponent({
|
||||
props: {
|
||||
uiTarget: {
|
||||
type: String,
|
||||
default: 'radio'
|
||||
default: 'input'
|
||||
},
|
||||
customClass: {
|
||||
type: String,
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="mask" v-if="visible">
|
||||
<div class="box">
|
||||
<div class="title">{{ title }}</div>
|
||||
<div class="btn" @click="handleConfirm">{{ btnText }}</div>
|
||||
<div class="btn btn-primary btn-base" @click="handleConfirm">{{ btnText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -34,18 +34,8 @@ const handleConfirm = () => {
|
||||
<style lang="scss" scoped>
|
||||
@import url('../styles/dialog.scss');
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
.btn-base {
|
||||
// display: inline-block;
|
||||
margin-top: 20px;
|
||||
line-height: 42px;
|
||||
border-radius: 2px;
|
||||
font-size: 16px;
|
||||
background-color: #4a4c5b;
|
||||
border: 1px solid #4a4c5b;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,74 +1,115 @@
|
||||
<template>
|
||||
<div class="mask" v-show="visible">
|
||||
<div class="mask" v-show="isVisible">
|
||||
<div class="box">
|
||||
<div class="title">{{ title }}</div>
|
||||
<div class="btn-box">
|
||||
<div class="btn cancel" @click="handleCancel">{{ cancelBtnText }}</div>
|
||||
<div class="btn confirm" @click="handleConfirm">{{ confirmBtnText }}</div>
|
||||
<div class="head-wrapper">
|
||||
<div class="title">{{ title }}</div>
|
||||
<div v-if="tips" class="tips">{{ tips }}</div>
|
||||
</div>
|
||||
<div class="body-wrapper">
|
||||
<div v-if="formValues && formValues.length" class="body-content">
|
||||
<div class="form-item" v-for="item in formValues" :key="item.key">
|
||||
<div class="input-wrapper">
|
||||
<input
|
||||
class="input-inner"
|
||||
:name="item.key"
|
||||
v-model="item.value"
|
||||
:placeholder="item.placeholder"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<div class="btn btn-shallow btn-base" v-if="cancel" @click="handleCancel">
|
||||
{{ cancelBtnText }}
|
||||
</div>
|
||||
<div class="btn btn-primary btn-base" @click="handleConfirm">{{ confirmBtnText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
|
||||
type FormItem = {
|
||||
key: string
|
||||
value: string
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
visible?: boolean
|
||||
cancel?: boolean // 是否展示取消按钮
|
||||
cancelBtnText?: string
|
||||
confirmBtnText?: string
|
||||
title?: string
|
||||
tips?: string
|
||||
bodyContent?: FormItem[]
|
||||
autoClose?: boolean // 点击确认时是否关闭
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(ev: 'confirm', callback: () => void): void
|
||||
(ev: 'confirm', data, callback: () => void): void
|
||||
(ev: 'close'): void
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
visible: false,
|
||||
cancel: true,
|
||||
cancelBtnText: '取消',
|
||||
confirmBtnText: '确定',
|
||||
title: ''
|
||||
title: '',
|
||||
tips: '',
|
||||
autoClose: true
|
||||
})
|
||||
|
||||
const isVisible = ref(props.visible)
|
||||
const formValues = reactive(props.bodyContent || [])
|
||||
|
||||
const handleConfirm = () => {
|
||||
emit('confirm', () => {
|
||||
emit('close')
|
||||
const data = {}
|
||||
formValues.forEach((item) => {
|
||||
data[item.key] = item.value
|
||||
})
|
||||
|
||||
emit('confirm', data, handleCancel)
|
||||
|
||||
props.autoClose && handleCancel()
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
isVisible.value = false
|
||||
emit('close')
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import url('../styles/dialog.scss');
|
||||
|
||||
.btn-box {
|
||||
padding: 20px 0 0;
|
||||
.tips {
|
||||
text-align: center;
|
||||
font-size: 0.24rem;
|
||||
color: #92949d;
|
||||
margin-top: 0.15rem;
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.btn-wrapper {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
|
||||
.btn {
|
||||
width: 48%;
|
||||
font-size: 0.28rem;
|
||||
border-radius: 0.04rem;
|
||||
text-align: center;
|
||||
padding: 0.16rem 0;
|
||||
line-height: 0.4rem;
|
||||
cursor: pointer;
|
||||
.btn-base {
|
||||
width: 100%;
|
||||
margin-right: 0.2rem;
|
||||
|
||||
&.cancel {
|
||||
background: #fff;
|
||||
color: #92949d;
|
||||
border: 1px solid #e3e4e8;
|
||||
}
|
||||
|
||||
&.confirm {
|
||||
background-color: #4a4c5b;
|
||||
border: 1px solid #4a4c5b;
|
||||
color: #fff;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ const { percent } = useProgressBar()
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.progress-outer {
|
||||
z-index: 10000;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
.progress-inner-wrapper-pc {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<QuestionRuleContainer
|
||||
v-if="visibily"
|
||||
v-if="visibility"
|
||||
:moduleConfig="questionConfig"
|
||||
:indexNumber="indexNumber"
|
||||
:showTitle="true"
|
||||
@ -9,18 +9,21 @@
|
||||
></QuestionRuleContainer>
|
||||
</template>
|
||||
<script setup>
|
||||
import { unref, computed, watch, ref } from 'vue'
|
||||
import { unref, computed, watch } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import QuestionRuleContainer from '../../materials/questions/QuestionRuleContainer'
|
||||
import { useVoteMap } from '@/render/hooks/useVoteMap'
|
||||
import { useShowOthers } from '@/render/hooks/useShowOthers'
|
||||
import { useShowInput } from '@/render/hooks/useShowInput'
|
||||
import { debounce, cloneDeep } from 'lodash-es'
|
||||
import { useQuestionStore } from '../stores/question'
|
||||
import { useSurveyStore } from '../stores/survey'
|
||||
import { FORMDATA_SUFFIX, SUBMIT_FLAG } from '../utils/constant'
|
||||
|
||||
import { NORMAL_CHOICES, RATES, QUESTION_TYPE } from '@/common/typeEnum.ts'
|
||||
import localstorage from '@/common/localstorage'
|
||||
import QuestionRuleContainer from '@/materials/questions/QuestionRuleContainer'
|
||||
|
||||
import { useVoteMap } from '@/render/hooks/useVoteMap'
|
||||
import { useOthersData } from '@/render/hooks/useOthersData'
|
||||
import { useInputData } from '@/render/hooks/useInputData'
|
||||
|
||||
import { useQuestionStore } from '@/render/stores/question'
|
||||
import { useSurveyStore } from '@/render/stores/survey'
|
||||
|
||||
import { setSurveyData, clearSurveyData, setSurveySubmit } from '@/render/utils/storage'
|
||||
|
||||
const props = defineProps({
|
||||
indexNumber: {
|
||||
@ -41,27 +44,28 @@ const surveyStore = useSurveyStore()
|
||||
const formValues = computed(() => {
|
||||
return surveyStore.formValues
|
||||
})
|
||||
const { showLogicEngine } = storeToRefs(surveyStore)
|
||||
const { showLogicEngine, baseConf } = storeToRefs(surveyStore)
|
||||
const { changeField, changeIndex, needHideFields } = storeToRefs(questionStore)
|
||||
|
||||
// 题型配置转换
|
||||
const questionConfig = computed(() => {
|
||||
let moduleConfig = props.moduleConfig
|
||||
const { type, field, options = [], ...rest } = cloneDeep(moduleConfig)
|
||||
|
||||
let alloptions = options
|
||||
let allOptions = options
|
||||
|
||||
if (type === QUESTION_TYPE.VOTE) {
|
||||
// 处理投票进度
|
||||
const { options, voteTotal } = useVoteMap(field)
|
||||
const voteOptions = unref(options)
|
||||
alloptions = alloptions.map((obj, index) => Object.assign(obj, voteOptions[index]))
|
||||
allOptions = allOptions.map((obj, index) => Object.assign(obj, voteOptions[index]))
|
||||
moduleConfig.voteTotal = unref(voteTotal)
|
||||
}
|
||||
if (NORMAL_CHOICES.includes(type) && options.some((option) => option.others)) {
|
||||
// 处理普通选择题的填写更多
|
||||
let { options, othersValue } = useShowOthers(field)
|
||||
let { options, othersValue } = useOthersData(field)
|
||||
const othersOptions = unref(options)
|
||||
alloptions = alloptions.map((obj, index) => Object.assign(obj, othersOptions[index]))
|
||||
allOptions = allOptions.map((obj, index) => Object.assign(obj, othersOptions[index]))
|
||||
moduleConfig.othersValue = unref(othersValue)
|
||||
}
|
||||
|
||||
@ -72,34 +76,79 @@ const questionConfig = computed(() => {
|
||||
0
|
||||
) {
|
||||
// 处理评分题的的选项后输入框
|
||||
let { rangeConfig, othersValue } = useShowInput(field)
|
||||
let { rangeConfig, othersValue } = useInputData(field)
|
||||
moduleConfig.rangeConfig = unref(rangeConfig)
|
||||
moduleConfig.othersValue = unref(othersValue)
|
||||
}
|
||||
|
||||
return {
|
||||
...moduleConfig,
|
||||
options: alloptions,
|
||||
options: allOptions,
|
||||
value: formValues.value[props.moduleConfig.field]
|
||||
}
|
||||
})
|
||||
|
||||
const logicshow = computed(() => {
|
||||
const updateFormData = (value) => {
|
||||
const key = props.moduleConfig.field
|
||||
const formData = cloneDeep(formValues.value)
|
||||
if (key in formData) {
|
||||
formData[key] = value
|
||||
}
|
||||
|
||||
return formData
|
||||
}
|
||||
|
||||
const handleChange = (data) => {
|
||||
emit('change', data)
|
||||
// 处理投票题questionConfig
|
||||
if (props.moduleConfig.type === QUESTION_TYPE.VOTE) {
|
||||
questionStore.updateVoteData(data)
|
||||
}
|
||||
|
||||
processJumpSkip()
|
||||
|
||||
// 开启了断点续答:记录内容
|
||||
if (baseConf.value.fillAnswer) {
|
||||
const formData = updateFormData(data.value)
|
||||
storageAnswer(formData)
|
||||
}
|
||||
}
|
||||
|
||||
const handleInput = debounce((e) => {
|
||||
// 开启了断点续答:记录内容
|
||||
if (baseConf.value.fillAnswer) {
|
||||
const formData = updateFormData(e.target.value)
|
||||
storageAnswer(formData)
|
||||
}
|
||||
}, 500)
|
||||
|
||||
// 数据回填处理
|
||||
const storageAnswer = (formData) => {
|
||||
const id = surveyStore.surveyPath
|
||||
|
||||
clearSurveyData(id)
|
||||
setSurveyData(id, formData)
|
||||
setSurveySubmit(id, 0)
|
||||
}
|
||||
|
||||
/** 问卷逻辑处理 */
|
||||
// 显示逻辑:题目是否需要显示
|
||||
const logicShow = computed(() => {
|
||||
// computed有计算缓存,当match有变化的时候触发重新计算
|
||||
const result = showLogicEngine.value.match(props.moduleConfig.field, 'question', formValues.value)
|
||||
return result === undefined ? true : result
|
||||
})
|
||||
|
||||
const logicskip = computed(() => {
|
||||
// 跳转逻辑:题目是否需要跳过(隐藏)
|
||||
const logicSkip = computed(() => {
|
||||
return needHideFields.value.includes(props.moduleConfig.field)
|
||||
})
|
||||
const visibily = computed(() => {
|
||||
return logicshow.value && !logicskip.value
|
||||
const visibility = computed(() => {
|
||||
return logicShow.value && !logicSkip.value
|
||||
})
|
||||
|
||||
// 当题目被隐藏时,清空题目的选中项,实现a显示关联b,b显示关联c场景下,b隐藏不影响题目c的展示
|
||||
watch(
|
||||
() => visibily.value,
|
||||
() => visibility.value,
|
||||
(newVal, oldVal) => {
|
||||
const { field, type, innerType } = props.moduleConfig
|
||||
if (!newVal && oldVal) {
|
||||
@ -110,46 +159,20 @@ watch(
|
||||
if (type === QUESTION_TYPE.CHECKBOX || innerType === QUESTION_TYPE.CHECKBOX) {
|
||||
value = value ? [value] : []
|
||||
}
|
||||
|
||||
const data = {
|
||||
key: field,
|
||||
value: value
|
||||
}
|
||||
surveyStore.changeData(data)
|
||||
|
||||
processJumpSkip()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const handleChange = (data) => {
|
||||
emit('change', data)
|
||||
// 处理投票题
|
||||
if (props.moduleConfig.type === QUESTION_TYPE.VOTE) {
|
||||
questionStore.updateVoteData(data)
|
||||
}
|
||||
processJumpSkip()
|
||||
valueTemp.value = data.value
|
||||
debounceStorageSave()
|
||||
}
|
||||
const valueTemp = ref()
|
||||
const handleInput = (e) => {
|
||||
valueTemp.value = e.target.value
|
||||
debounceStorageSave()
|
||||
}
|
||||
|
||||
const debounceStorageSave = debounce(() => {
|
||||
let data = {
|
||||
key: props.moduleConfig.field,
|
||||
value: valueTemp.value
|
||||
}
|
||||
const formData = cloneDeep(formValues.value)
|
||||
let { key, value } = data
|
||||
if (key in formData) {
|
||||
formData[key] = value
|
||||
}
|
||||
localStorageSave(formData)
|
||||
}, 500)
|
||||
|
||||
// 解析跳转逻辑
|
||||
const processJumpSkip = () => {
|
||||
const targetResult = surveyStore.jumpLogicEngine
|
||||
.getResultsByField(changeField.value, surveyStore.formValues)
|
||||
@ -190,9 +213,5 @@ const processJumpSkip = () => {
|
||||
.map((item) => item.field)
|
||||
questionStore.addNeedHideFields(skipKey)
|
||||
}
|
||||
const localStorageSave = (formData) => {
|
||||
localstorage.removeItem(surveyStore.surveyPath + FORMDATA_SUFFIX)
|
||||
localstorage.setItem(surveyStore.surveyPath + FORMDATA_SUFFIX, formData)
|
||||
localstorage.setItem(SUBMIT_FLAG, false)
|
||||
}
|
||||
/** 问卷逻辑处理 */
|
||||
</script>
|
||||
|
23
web/src/render/components/VerifyDialog/FillDataDialog.vue
Normal file
23
web/src/render/components/VerifyDialog/FillDataDialog.vue
Normal file
@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<ConfirmDialog
|
||||
:title="fillAnswer ? '是否继续上次填写的内容?' : '是否继续上次提交的内容?'"
|
||||
@confirm="handleSubmit"
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
import { useSurveyStore } from '../../stores/survey'
|
||||
|
||||
import { getSurveyData, getSurveySubmit } from '../../utils/storage'
|
||||
|
||||
import ConfirmDialog from '../ConfirmDialog.vue'
|
||||
|
||||
const surveyStore = useSurveyStore()
|
||||
const { fillAnswer } = surveyStore.baseConf || {}
|
||||
|
||||
const handleSubmit = () => {
|
||||
const surveyPath = surveyStore.surveyPath
|
||||
const data = getSurveyData(surveyPath)
|
||||
surveyStore.setFormValues(data)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
80
web/src/render/components/VerifyDialog/WhiteListDialog.vue
Normal file
80
web/src/render/components/VerifyDialog/WhiteListDialog.vue
Normal file
@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<ConfirmDialog
|
||||
title="验证"
|
||||
:tips="whitelistTip"
|
||||
:bodyContent="bodyContent"
|
||||
:autoClose="false"
|
||||
:cancel="false"
|
||||
@confirm="handleSubmit"
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { validate } from '../../api/survey'
|
||||
import { useSurveyStore } from '../../stores/survey'
|
||||
|
||||
import ConfirmDialog from '../ConfirmDialog.vue'
|
||||
|
||||
interface Emit {
|
||||
(ev: 'confirm'): void
|
||||
}
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const surveyStore = useSurveyStore()
|
||||
const { passwordSwitch, whitelistType, memberType, whitelistTip } = surveyStore.baseConf || {}
|
||||
|
||||
const bodyContent = computed(() => {
|
||||
const content = []
|
||||
if (passwordSwitch) {
|
||||
content.push({
|
||||
key: 'password',
|
||||
value: '',
|
||||
placeholder: '请输入访问密码'
|
||||
})
|
||||
}
|
||||
if (whitelistType && whitelistType != 'ALL') {
|
||||
let placeholder = ''
|
||||
if (whitelistType == 'MEMBER') {
|
||||
placeholder = '请输入用户名'
|
||||
}
|
||||
if (memberType == 'MOBILE') {
|
||||
placeholder = '请输入手机号'
|
||||
}
|
||||
if (memberType == 'EMAIL') {
|
||||
placeholder = '请输入邮箱'
|
||||
}
|
||||
|
||||
content.push({
|
||||
key: 'whitelist',
|
||||
value: '',
|
||||
placeholder
|
||||
})
|
||||
}
|
||||
|
||||
return content
|
||||
})
|
||||
|
||||
const handleSubmit = async (data, close) => {
|
||||
const params = {
|
||||
surveyPath: surveyStore.surveyPath
|
||||
}
|
||||
if (data.whitelist) {
|
||||
params.whitelist = data.whitelist
|
||||
}
|
||||
if (data.password) {
|
||||
params.password = data.password
|
||||
}
|
||||
const res = await validate(params)
|
||||
|
||||
if (res.code != 200) {
|
||||
ElMessage.error(res.errmsg || '验证失败')
|
||||
return
|
||||
}
|
||||
|
||||
close()
|
||||
emit('confirm')
|
||||
surveyStore.setWhiteData(params)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
73
web/src/render/components/VerifyDialog/index.vue
Normal file
73
web/src/render/components/VerifyDialog/index.vue
Normal file
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div>
|
||||
<WhiteListDialog
|
||||
v-if="showWhiteList"
|
||||
:visible="showWhiteList"
|
||||
ref="whiteListRef"
|
||||
@confirm="whiteListConfirm"
|
||||
/>
|
||||
<FillDataDialog v-if="showFillData" :visible="showFillData" ref="fillDataRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch, computed, ref } from 'vue'
|
||||
import { useSurveyStore } from '../../stores/survey'
|
||||
import { getSurveyData, getSurveySubmit } from '../../utils/storage'
|
||||
|
||||
import WhiteListDialog from './WhiteListDialog.vue'
|
||||
import FillDataDialog from './FillDataDialog.vue'
|
||||
|
||||
const surveyStore = useSurveyStore()
|
||||
const baseConf = computed(() => surveyStore?.baseConf || {})
|
||||
|
||||
const showWhiteList = ref(false)
|
||||
const showFillData = ref(false)
|
||||
|
||||
watch(
|
||||
() => baseConf.value,
|
||||
() => {
|
||||
const { passwordSwitch, whitelistType } = baseConf.value || {}
|
||||
|
||||
// 密码 or 白名单
|
||||
if ((whitelistType && whitelistType != 'ALL') || passwordSwitch) {
|
||||
showWhiteList.value = true
|
||||
return
|
||||
}
|
||||
|
||||
whiteListConfirm()
|
||||
}
|
||||
)
|
||||
|
||||
const whiteListConfirm = () => {
|
||||
const { surveyPath } = surveyStore || {}
|
||||
const { fillAnswer, fillSubmitAnswer } = baseConf.value || {}
|
||||
// 断点续答 or 自动填充
|
||||
const localData = getSurveyData(surveyPath)
|
||||
const isSubmit = getSurveySubmit(surveyPath)
|
||||
if ((fillAnswer || (fillSubmitAnswer && isSubmit)) && localData) {
|
||||
showFillData.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// export default {
|
||||
// components: { DialogComponent },
|
||||
// data() {
|
||||
// return {
|
||||
// showWhiteListDialog: false,
|
||||
// showFillDataDialog: false,
|
||||
// };
|
||||
// },
|
||||
// methods: {
|
||||
// startValidation() {
|
||||
// this.showDialog1 = true
|
||||
// await this.$refs.dialog1.validate() // 校验第一个弹窗
|
||||
// this.showDialog1 = false
|
||||
|
||||
// this.showDialog2 = true
|
||||
// await this.$refs.dialog2.validate() // 校验第二个弹窗
|
||||
// this.showDialog2 = false
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
</script>
|
@ -1,147 +0,0 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="whiteVisible"
|
||||
title="验证"
|
||||
:show-close="false"
|
||||
class="verify-white-wrap"
|
||||
width="315"
|
||||
:close-on-press-escape="false"
|
||||
:close-on-click-modal="false"
|
||||
align-center
|
||||
>
|
||||
<template #header>
|
||||
<div class="verify-white-head">
|
||||
<div class="verify-white-title">验证</div>
|
||||
<div v-if="whitelistTip" class="verify-white-tips">{{ whitelistTip }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="verify-white-body">
|
||||
<el-input
|
||||
v-if="isPwd"
|
||||
v-model="state.password"
|
||||
class="wd255 mb16"
|
||||
placeholder="请输入6位字符串类型访问密码"
|
||||
/>
|
||||
<el-input
|
||||
v-if="isValue"
|
||||
v-model="state.value"
|
||||
class="wd255 mb16"
|
||||
:placeholder="placeholder"
|
||||
/>
|
||||
<div class="submit-btn" @click="handleSubmit">验证并开始答题</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/src/message.scss'
|
||||
|
||||
import { validate } from '../api/survey'
|
||||
import { useSurveyStore } from '../stores/survey'
|
||||
|
||||
const whiteVisible = ref(false)
|
||||
|
||||
const surveyStore = useSurveyStore()
|
||||
const state = reactive({
|
||||
password: '',
|
||||
value: '',
|
||||
is_submit: false
|
||||
})
|
||||
|
||||
const baseConf = computed(() => surveyStore?.baseConf || {})
|
||||
const isPwd = computed(() => baseConf.value.passwordSwitch)
|
||||
const whitelistType = computed(() => baseConf.value.whitelistType)
|
||||
const memberType = computed(() => baseConf.value.memberType)
|
||||
const whitelistTip = computed(() => baseConf.value.whitelistTip)
|
||||
const surveyPath = computed(() => surveyStore?.surveyPath || '')
|
||||
|
||||
const isValue = computed(() => {
|
||||
if (!whitelistType.value) return false
|
||||
return whitelistType.value != 'ALL'
|
||||
})
|
||||
|
||||
const placeholder = computed(() => {
|
||||
if (whitelistType.value == 'MEMBER') {
|
||||
return '请输入用户名'
|
||||
}
|
||||
if (memberType.value == 'MOBILE') {
|
||||
return '请输入手机号'
|
||||
}
|
||||
if (memberType.value == 'EMAIL') {
|
||||
return '请输入邮箱'
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (state.is_submit) return
|
||||
const params = {
|
||||
surveyPath: surveyPath.value
|
||||
}
|
||||
if (isValue.value) {
|
||||
params.whitelist = state.value
|
||||
}
|
||||
if (isPwd.value) {
|
||||
params.password = state.password
|
||||
}
|
||||
const res = await validate(params)
|
||||
if (res.code != 200) {
|
||||
ElMessage.error(res.errmsg || '验证失败')
|
||||
return
|
||||
}
|
||||
whiteVisible.value = false
|
||||
surveyStore.setWhiteData(params)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => baseConf.value,
|
||||
() => {
|
||||
if (whiteVisible.value) return
|
||||
if (isValue.value || isPwd.value) {
|
||||
whiteVisible.value = true
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.verify-white-wrap {
|
||||
.verify-white-body {
|
||||
padding: 0 14px;
|
||||
}
|
||||
.verify-white-head {
|
||||
padding: 0 14px;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.mb16 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.verify-white-tips {
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
font-size: 14px;
|
||||
color: #92949d;
|
||||
}
|
||||
.verify-white-title {
|
||||
font-size: 16px;
|
||||
color: #292a36;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.submit-btn {
|
||||
background: #faa600;
|
||||
border-radius: 2px;
|
||||
width: 255px;
|
||||
height: 32px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,7 +1,7 @@
|
||||
import { useQuestionStore } from '../stores/question'
|
||||
import { useSurveyStore } from '../stores/survey'
|
||||
|
||||
export const useShowInput = (questionKey) => {
|
||||
export const useInputData = (questionKey) => {
|
||||
const questionStore = useQuestionStore()
|
||||
const surveyStore = useSurveyStore()
|
||||
const formValues = surveyStore.formValues
|
@ -1,7 +1,7 @@
|
||||
import { useQuestionStore } from '../stores/question'
|
||||
import { useSurveyStore } from '../stores/survey'
|
||||
|
||||
export const useShowOthers = (questionKey) => {
|
||||
export const useOthersData = (questionKey) => {
|
||||
const questionStore = useQuestionStore()
|
||||
const surveyStore = useSurveyStore()
|
||||
const formValues = surveyStore.formValues
|
@ -38,7 +38,7 @@ export const useProgressBar = () => {
|
||||
|
||||
const percent = computed(() => {
|
||||
const { fillCount, topicCount } = surveySchedule.value
|
||||
return Math.floor((100 / topicCount) * fillCount) + '%'
|
||||
return Math.floor((100 / topicCount) * fillCount) || 0 + '%'
|
||||
})
|
||||
|
||||
return { surveySchedule, percent }
|
||||
|
@ -1,9 +1,7 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<img src="/imgs/nodata.webp" alt="" class="ui-404" />
|
||||
<div class="ui-title">
|
||||
<div class="ui-maintitle">问卷找不到啦~</div>
|
||||
</div>
|
||||
<div class="ui-title">问卷找不到啦~</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -2,10 +2,15 @@
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue'
|
||||
import { watch, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { getPublishedSurveyInfo, getPreviewSchema } from '../api/survey'
|
||||
import AlertDialog from '../components/AlertDialog.vue'
|
||||
import { useSurveyStore } from '../stores/survey'
|
||||
import useCommandComponent from '../hooks/useCommandComponent'
|
||||
|
||||
const route = useRoute()
|
||||
const surveyStore = useSurveyStore()
|
||||
|
||||
watch(
|
||||
() => route.query.t,
|
||||
@ -13,4 +18,65 @@ watch(
|
||||
location.reload()
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
const surveyId = route.params.surveyId
|
||||
console.log({ surveyId })
|
||||
surveyStore.setSurveyPath(surveyId)
|
||||
getDetail(surveyId as string)
|
||||
})
|
||||
const loadData = (res: any, surveyPath: string) => {
|
||||
if (res.code === 200) {
|
||||
const data = res.data
|
||||
const {
|
||||
bannerConf,
|
||||
baseConf,
|
||||
bottomConf,
|
||||
dataConf,
|
||||
skinConf,
|
||||
submitConf,
|
||||
logicConf,
|
||||
pageConf
|
||||
} = data.code
|
||||
const questionData = {
|
||||
bannerConf,
|
||||
baseConf,
|
||||
bottomConf,
|
||||
dataConf,
|
||||
skinConf,
|
||||
submitConf,
|
||||
pageConf
|
||||
}
|
||||
|
||||
if (!pageConf || pageConf?.length == 0) {
|
||||
questionData.pageConf = [dataConf.dataList.length]
|
||||
}
|
||||
|
||||
document.title = data.title
|
||||
|
||||
surveyStore.setSurveyPath(surveyPath)
|
||||
surveyStore.initSurvey(questionData)
|
||||
surveyStore.initShowLogicEngine(logicConf?.showLogicConf)
|
||||
surveyStore.initJumpLogicEngine(logicConf?.jumpLogicConf)
|
||||
} else {
|
||||
throw new Error(res.errmsg)
|
||||
}
|
||||
}
|
||||
const getDetail = async (surveyPath: string) => {
|
||||
const alert = useCommandComponent(AlertDialog)
|
||||
|
||||
try {
|
||||
if (surveyPath.length > 8) {
|
||||
const res: any = await getPreviewSchema({ surveyPath })
|
||||
loadData(res, surveyPath)
|
||||
} else {
|
||||
const res: any = await getPublishedSurveyInfo({ surveyPath })
|
||||
loadData(res, surveyPath)
|
||||
surveyStore.getEncryptInfo()
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log(error)
|
||||
alert({ title: error.message || '获取问卷失败' })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -16,31 +16,36 @@
|
||||
></SubmitButton>
|
||||
</div>
|
||||
<LogoIcon :logo-conf="logoConf" :readonly="true" />
|
||||
<VerifyWhiteDialog />
|
||||
<VerifyDialog />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, onMounted } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useRouter } from 'vue-router'
|
||||
// @ts-ignore
|
||||
import communalLoader from '@materials/communals/communalLoader.js'
|
||||
|
||||
import useCommandComponent from '../hooks/useCommandComponent'
|
||||
import MainRenderer from '../components/MainRenderer.vue'
|
||||
import AlertDialog from '../components/AlertDialog.vue'
|
||||
import VerifyWhiteDialog from '../components/VerifyWhiteDialog.vue'
|
||||
|
||||
import ConfirmDialog from '../components/ConfirmDialog.vue'
|
||||
import VerifyDialog from '../components/VerifyDialog/index.vue'
|
||||
|
||||
import ProgressBar from '../components/ProgressBar.vue'
|
||||
|
||||
import { useSurveyStore } from '../stores/survey'
|
||||
import { useQuestionStore } from '../stores/question'
|
||||
import { submitForm } from '../api/survey'
|
||||
import encrypt from '../utils/encrypt'
|
||||
|
||||
import useCommandComponent from '../hooks/useCommandComponent'
|
||||
import { getPublishedSurveyInfo, getPreviewSchema } from '../api/survey'
|
||||
import { FORMDATA_SUFFIX, SUBMIT_FLAG } from '@/render/utils/constant'
|
||||
import localstorage from '@/common/localstorage'
|
||||
import {
|
||||
clearSurveyData,
|
||||
setSurveyData,
|
||||
clearSurveySubmit,
|
||||
setSurveySubmit
|
||||
} from '../utils/storage'
|
||||
|
||||
interface Props {
|
||||
questionInfo?: any
|
||||
@ -73,76 +78,16 @@ const pageIndex = computed(() => questionStore.pageIndex)
|
||||
const { bannerConf, submitConf, bottomConf: logoConf, whiteData } = storeToRefs(surveyStore)
|
||||
const surveyPath = computed(() => surveyStore.surveyPath || '')
|
||||
|
||||
const route = useRoute()
|
||||
onMounted(() => {
|
||||
const surveyId = route.params.surveyId
|
||||
console.log({ surveyId })
|
||||
surveyStore.setSurveyPath(surveyId)
|
||||
getDetail(surveyId as string)
|
||||
})
|
||||
const loadData = (res: any, surveyPath: string) => {
|
||||
if (res.code === 200) {
|
||||
const data = res.data
|
||||
const {
|
||||
bannerConf,
|
||||
baseConf,
|
||||
bottomConf,
|
||||
dataConf,
|
||||
skinConf,
|
||||
submitConf,
|
||||
logicConf,
|
||||
pageConf
|
||||
} = data.code
|
||||
const questionData = {
|
||||
bannerConf,
|
||||
baseConf,
|
||||
bottomConf,
|
||||
dataConf,
|
||||
skinConf,
|
||||
submitConf,
|
||||
pageConf
|
||||
}
|
||||
|
||||
if (!pageConf || pageConf?.length == 0) {
|
||||
questionData.pageConf = [dataConf.dataList.length]
|
||||
}
|
||||
|
||||
document.title = data.title
|
||||
|
||||
surveyStore.setSurveyPath(surveyPath)
|
||||
surveyStore.initSurvey(questionData)
|
||||
surveyStore.initShowLogicEngine(logicConf?.showLogicConf)
|
||||
surveyStore.initJumpLogicEngine(logicConf?.jumpLogicConf)
|
||||
} else {
|
||||
throw new Error(res.errmsg)
|
||||
}
|
||||
}
|
||||
const getDetail = async (surveyPath: string) => {
|
||||
const alert = useCommandComponent(AlertDialog)
|
||||
|
||||
try {
|
||||
if (surveyPath.length > 8) {
|
||||
const res: any = await getPreviewSchema({ surveyPath })
|
||||
loadData(res, surveyPath)
|
||||
} else {
|
||||
const res: any = await getPublishedSurveyInfo({ surveyPath })
|
||||
loadData(res, surveyPath)
|
||||
surveyStore.getEncryptInfo()
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log(error)
|
||||
alert({ title: error.message || '获取问卷失败' })
|
||||
}
|
||||
}
|
||||
const validate = (cbk: (v: boolean) => void) => {
|
||||
const validate = (callback: (v: boolean) => void) => {
|
||||
const index = 0
|
||||
mainRef.value.$refs.formGroup[index].validate(cbk)
|
||||
mainRef.value.$refs.formGroup[index].validate(callback)
|
||||
}
|
||||
|
||||
const normalizationRequestBody = () => {
|
||||
const enterTime = surveyStore.enterTime
|
||||
const encryptInfo = surveyStore.encryptInfo as any
|
||||
const formValues = surveyStore.formValues
|
||||
const baseConf = surveyStore.baseConf
|
||||
|
||||
const result: any = {
|
||||
surveyPath: surveyPath.value,
|
||||
@ -152,21 +97,24 @@ const normalizationRequestBody = () => {
|
||||
...whiteData.value
|
||||
}
|
||||
|
||||
//浏览器缓存数据
|
||||
localstorage.removeItem(surveyPath.value + FORMDATA_SUFFIX)
|
||||
localstorage.removeItem(SUBMIT_FLAG)
|
||||
//数据加密
|
||||
let formData: Record<string, any> = Object.assign({}, surveyStore.formValues)
|
||||
// 自动回填开启时,记录数据
|
||||
if (baseConf.fillSubmitAnswer) {
|
||||
clearSurveyData(surveyPath.value)
|
||||
clearSurveySubmit(surveyPath.value)
|
||||
|
||||
localstorage.setItem(surveyPath.value + FORMDATA_SUFFIX, formData)
|
||||
localstorage.setItem(SUBMIT_FLAG, true)
|
||||
setSurveyData(surveyPath.value, formValues)
|
||||
setSurveySubmit(surveyPath.value, 1)
|
||||
}
|
||||
|
||||
// 数据加密
|
||||
if (encryptInfo?.encryptType) {
|
||||
result.encryptType = encryptInfo.encryptType
|
||||
|
||||
result.data = encrypt[result.encryptType as 'rsa']({
|
||||
data: result.data,
|
||||
secretKey: encryptInfo?.data?.secretKey
|
||||
})
|
||||
|
||||
if (encryptInfo?.data?.sessionId) {
|
||||
result.sessionId = encryptInfo.data.sessionId
|
||||
}
|
||||
@ -177,7 +125,7 @@ const normalizationRequestBody = () => {
|
||||
return result
|
||||
}
|
||||
|
||||
const submitSurver = async () => {
|
||||
const submitSurvey = async () => {
|
||||
if (surveyPath.value.length > 8) {
|
||||
router.push({ name: 'successPage' })
|
||||
return
|
||||
@ -209,7 +157,7 @@ const handleSubmit = () => {
|
||||
title: again_text,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
submitSurver()
|
||||
submitSurvey()
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
} finally {
|
||||
@ -218,7 +166,7 @@ const handleSubmit = () => {
|
||||
}
|
||||
})
|
||||
} else {
|
||||
submitSurver()
|
||||
submitSurvey()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -30,7 +30,9 @@ import communalLoader from '@materials/communals/communalLoader.js'
|
||||
const LogoIcon = communalLoader.loadComponent('LogoIcon')
|
||||
const surveyStore = useSurveyStore()
|
||||
|
||||
const logoConf = computed(() => surveyStore?.bottomConf || {})
|
||||
const logoConf = computed(() => {
|
||||
return surveyStore?.bottomConf || {}
|
||||
})
|
||||
const successMsg = computed(() => {
|
||||
const msgContent = (surveyStore?.submitConf as any)?.msgContent || {}
|
||||
return msgContent?.msg_200 || '提交成功'
|
||||
|
@ -1,56 +0,0 @@
|
||||
import ConfirmDialog from '../../components/ConfirmDialog.vue'
|
||||
import AlertDialog from '../../components/AlertDialog.vue'
|
||||
|
||||
import { isFunction as _isFunction } from 'lodash-es'
|
||||
|
||||
export default {
|
||||
install(Vue) {
|
||||
Vue.prototype.$dialog = {
|
||||
confirm(options) {
|
||||
const MyComponent = Vue.extend(ConfirmDialog)
|
||||
const instance = new MyComponent({
|
||||
propsData: options
|
||||
})
|
||||
const closeConfirm = () => {
|
||||
if (instance && instance.$el) {
|
||||
instance.$el.remove()
|
||||
}
|
||||
}
|
||||
instance.$on('cancel', () => {
|
||||
if (options?.onCancel && _isFunction(options.onCancel)) {
|
||||
options.onCancel(closeConfirm)
|
||||
} else {
|
||||
closeConfirm()
|
||||
}
|
||||
})
|
||||
instance.$on('confirm', () => {
|
||||
if (options?.onConfirm && _isFunction(options.onConfirm)) {
|
||||
options.onConfirm(closeConfirm)
|
||||
}
|
||||
})
|
||||
instance.$mount()
|
||||
document.body.append(instance.$el)
|
||||
},
|
||||
alert(options) {
|
||||
const MyComponent = Vue.extend(AlertDialog)
|
||||
const instance = new MyComponent({
|
||||
propsData: options
|
||||
})
|
||||
const closeConfirm = () => {
|
||||
if (instance && instance.$el) {
|
||||
instance.$el.remove()
|
||||
}
|
||||
}
|
||||
instance.$on('confirm', () => {
|
||||
if (options?.onConfirm && _isFunction(options.onConfirm)) {
|
||||
options.onConfirm(closeConfirm)
|
||||
} else {
|
||||
closeConfirm()
|
||||
}
|
||||
})
|
||||
instance.$mount()
|
||||
document.body.append(instance.$el)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,11 +4,11 @@ import { set } from 'lodash-es'
|
||||
import { useSurveyStore } from '@/render/stores/survey'
|
||||
import { queryVote } from '@/render/api/survey'
|
||||
import { QUESTION_TYPE } from '@/common/typeEnum'
|
||||
import { VOTE_INFO_KEY } from '@/render/utils/constant'
|
||||
import localstorage from '@/common/localstorage'
|
||||
|
||||
import { getVoteData, setVoteData, clearVoteData } from '@/render/utils/storage'
|
||||
|
||||
// 投票进度逻辑聚合
|
||||
const usevVoteMap = (questionData) => {
|
||||
const useVoteMap = (questionData) => {
|
||||
const voteMap = ref({})
|
||||
//初始化投票题的数据
|
||||
const initVoteData = async () => {
|
||||
@ -28,16 +28,14 @@ const usevVoteMap = (questionData) => {
|
||||
return
|
||||
}
|
||||
try {
|
||||
localstorage.removeItem(VOTE_INFO_KEY)
|
||||
clearVoteData()
|
||||
const voteRes = await queryVote({
|
||||
surveyPath,
|
||||
fieldList: fieldList.join(',')
|
||||
})
|
||||
|
||||
if (voteRes.code === 200) {
|
||||
localstorage.setItem(VOTE_INFO_KEY, {
|
||||
...voteRes.data
|
||||
})
|
||||
setVoteData(voteRes.data)
|
||||
setVoteMap(voteRes.data)
|
||||
}
|
||||
} catch (error) {
|
||||
@ -58,25 +56,25 @@ const usevVoteMap = (questionData) => {
|
||||
const updateVoteData = (data) => {
|
||||
const { key: questionKey, value: questionVal } = data
|
||||
// 更新前获取接口缓存在localstorage中的数据
|
||||
const voteinfo = localstorage.getItem(VOTE_INFO_KEY)
|
||||
const voteInfo = getVoteData()
|
||||
const currentQuestion = questionData.value[questionKey]
|
||||
const options = currentQuestion.options
|
||||
const voteTotal = voteinfo?.[questionKey]?.total || 0
|
||||
const voteTotal = voteInfo?.[questionKey]?.total || 0
|
||||
let totalPayload = {
|
||||
questionKey,
|
||||
voteKey: 'total',
|
||||
voteValue: voteTotal
|
||||
}
|
||||
options.forEach((option) => {
|
||||
const optionhash = option.hash
|
||||
const voteCount = voteinfo?.[questionKey]?.[optionhash] || 0
|
||||
const optionHash = option.hash
|
||||
const voteCount = voteInfo?.[questionKey]?.[optionHash] || 0
|
||||
// 如果选中值包含该选项,对应voteCount 和 voteTotal + 1
|
||||
if (
|
||||
Array.isArray(questionVal) ? questionVal.includes(optionhash) : questionVal === optionhash
|
||||
Array.isArray(questionVal) ? questionVal.includes(optionHash) : questionVal === optionHash
|
||||
) {
|
||||
const countPayload = {
|
||||
questionKey,
|
||||
voteKey: optionhash,
|
||||
voteKey: optionHash,
|
||||
voteValue: voteCount + 1
|
||||
}
|
||||
totalPayload.voteValue += 1
|
||||
@ -84,7 +82,7 @@ const usevVoteMap = (questionData) => {
|
||||
} else {
|
||||
const countPayload = {
|
||||
questionKey,
|
||||
voteKey: optionhash,
|
||||
voteKey: optionHash,
|
||||
voteValue: voteCount
|
||||
}
|
||||
updateVoteMapByKey(countPayload)
|
||||
@ -174,7 +172,7 @@ export const useQuestionStore = defineStore('question', () => {
|
||||
const setQuestionData = (data) => {
|
||||
questionData.value = data
|
||||
}
|
||||
const { voteMap, setVoteMap, initVoteData, updateVoteData } = usevVoteMap(questionData)
|
||||
const { voteMap, setVoteMap, initVoteData, updateVoteData } = useVoteMap(questionData)
|
||||
|
||||
const changeSelectMoreData = (data) => {
|
||||
const { key, value, field } = data
|
||||
|
@ -1,25 +1,20 @@
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { defineStore } from 'pinia'
|
||||
import { cloneDeep, pick } from 'lodash-es'
|
||||
import { pick } from 'lodash-es'
|
||||
import moment from 'moment'
|
||||
// 引入中文
|
||||
import 'moment/locale/zh-cn'
|
||||
// 设置中文
|
||||
|
||||
import { isMobile as isInMobile } from '@/render/utils/index'
|
||||
|
||||
import { getEncryptInfo as getEncryptInfoApi } from '@/render/api/survey'
|
||||
import { useQuestionStore } from '@/render/stores/question'
|
||||
import { useErrorInfo } from '@/render/stores/errorInfo'
|
||||
import { FORMDATA_SUFFIX, SUBMIT_FLAG } from '@/render/utils/constant'
|
||||
|
||||
import adapter from '../adapter'
|
||||
import { RuleMatch } from '@/common/logicEngine/RulesMatch'
|
||||
import useCommandComponent from '../hooks/useCommandComponent'
|
||||
import ConfirmDialog from '../components/ConfirmDialog.vue'
|
||||
import localstorage from '@/common/localstorage'
|
||||
|
||||
const confirm = useCommandComponent(ConfirmDialog)
|
||||
|
||||
moment.locale('zh-cn')
|
||||
/**
|
||||
@ -63,6 +58,10 @@ export const useSurveyStore = defineStore('survey', () => {
|
||||
enterTime.value = Date.now()
|
||||
}
|
||||
|
||||
const setFormValues = (data) => {
|
||||
formValues.value = data
|
||||
}
|
||||
|
||||
const getEncryptInfo = async () => {
|
||||
try {
|
||||
const res = await getEncryptInfoApi()
|
||||
@ -154,13 +153,7 @@ export const useSurveyStore = defineStore('survey', () => {
|
||||
// 获取已投票数据
|
||||
questionStore.initVoteData()
|
||||
}
|
||||
function fillFormData(formData) {
|
||||
const _formValues = cloneDeep(formValues.value)
|
||||
for (const key in formData) {
|
||||
_formValues[key] = formData[key]
|
||||
}
|
||||
formValues.value = _formValues
|
||||
}
|
||||
|
||||
const initSurvey = (option) => {
|
||||
setEnterTime()
|
||||
if (!canFillQuestionnaire(option.baseConf, option.submitConf)) {
|
||||
@ -168,31 +161,6 @@ export const useSurveyStore = defineStore('survey', () => {
|
||||
}
|
||||
// 加载空白问卷
|
||||
clearFormData(option)
|
||||
|
||||
const { fillAnswer, fillSubmitAnswer } = option.baseConf
|
||||
const localData = localstorage.getItem(surveyPath.value + FORMDATA_SUFFIX)
|
||||
|
||||
const isSubmit = localstorage.getItem(SUBMIT_FLAG)
|
||||
// 开启了断点续答 or 回填上一次提交内容
|
||||
if ((fillAnswer || (fillSubmitAnswer && isSubmit)) && localData) {
|
||||
const title = fillAnswer ? '是否继续上次填写的内容?' : '是否继续上次提交的内容?'
|
||||
confirm({
|
||||
title: title,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
// 回填答题内容
|
||||
fillFormData(localData)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
confirm.close()
|
||||
}
|
||||
},
|
||||
onClose: async () => {
|
||||
confirm.close()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 用户输入或者选择后,更新表单数据
|
||||
@ -204,6 +172,7 @@ export const useSurveyStore = defineStore('survey', () => {
|
||||
questionStore.setChangeField(key)
|
||||
}
|
||||
|
||||
// 初始化逻辑引擎
|
||||
const showLogicEngine = ref()
|
||||
const initShowLogicEngine = (showLogicConf) => {
|
||||
showLogicEngine.value = new RuleMatch().fromJson(showLogicConf || [])
|
||||
@ -231,6 +200,7 @@ export const useSurveyStore = defineStore('survey', () => {
|
||||
initSurvey,
|
||||
changeData,
|
||||
setWhiteData,
|
||||
setFormValues,
|
||||
setSurveyPath,
|
||||
setEnterTime,
|
||||
getEncryptInfo,
|
||||
|
@ -8,19 +8,70 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
background-color: #292a36f2;
|
||||
}
|
||||
|
||||
.box {
|
||||
width: 6rem;
|
||||
width: 6.3rem;
|
||||
background: #fff;
|
||||
padding: 0.56rem 0.44rem 0.32rem;
|
||||
padding: 0.56rem 0.44rem 0.4rem;
|
||||
}
|
||||
|
||||
.head-wrapper {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 0.3rem;
|
||||
font-size: 0.33rem;
|
||||
color: #4a4c5b;
|
||||
letter-spacing: 0;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
padding: 1px 11px;
|
||||
background-color: #fff;
|
||||
border-radius: 0.1rem;
|
||||
box-shadow: 0 0 0 1px #dcdfe6 inset;
|
||||
}
|
||||
.input-wrapper:hover {
|
||||
box-shadow: 0 0 0 1px var(--primary-color) inset;
|
||||
}
|
||||
|
||||
.input-inner {
|
||||
width: 100%;
|
||||
color: #606266;
|
||||
font-size: 0.27rem;
|
||||
height: 0.6rem;
|
||||
line-height: 0.6rem;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-size: 0.26rem;
|
||||
border-radius: 0.04rem;
|
||||
text-align: center;
|
||||
padding: 0.1rem 0;
|
||||
line-height: 0.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
&.btn-shallow {
|
||||
background: #fff;
|
||||
color: #92949d;
|
||||
border: 1px solid #e3e4e8;
|
||||
}
|
||||
|
||||
&.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
export const VOTE_INFO_KEY = 'voteinfo'
|
||||
export const QUOTA_INFO_KEY = 'limitinfo'
|
||||
|
||||
export const SUBMIT_FLAG = 'isSubmit'
|
||||
export const FORMDATA_SUFFIX = '_questionData'
|
44
web/src/render/utils/storage.ts
Normal file
44
web/src/render/utils/storage.ts
Normal file
@ -0,0 +1,44 @@
|
||||
// 用于记录“问卷断点续答”的数据
|
||||
export const getSurveyData = (id: string): any => {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem(`${id}_questionData`) as string) || null
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
export const setSurveyData = (id: string, formData: any = {}) => {
|
||||
localStorage.setItem(`${id}_questionData`, JSON.stringify(formData))
|
||||
}
|
||||
export const clearSurveyData = (id: string) => localStorage.removeItem(`${id}_questionData`)
|
||||
|
||||
// 问卷是否提交过,用于“自动填充上次填写内容”
|
||||
export const getSurveySubmit = (id: string): number => {
|
||||
try {
|
||||
return Number(JSON.parse(localStorage.getItem(`${id}_submit`) as string)) || 0
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
export const setSurveySubmit = (id: string, value: number) => {
|
||||
localStorage.setItem(`${id}_submit`, JSON.stringify(value))
|
||||
}
|
||||
export const clearSurveySubmit = (id: string) => localStorage.removeItem(`${id}_submit`)
|
||||
|
||||
// 投票记录
|
||||
export const getVoteData = (): any => {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem('voteData') as string) || null
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
export const setVoteData = (params: any) => {
|
||||
localStorage.setItem('voteData', JSON.stringify(params))
|
||||
}
|
||||
export const clearVoteData = () => localStorage.removeItem('voteData')
|
Loading…
Reference in New Issue
Block a user