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 { WorkspaceGuard } from 'src/guards/workspace.guard';
|
||||||
import { PERMISSION as WORKSPACE_PERMISSION } from 'src/enums/workspace';
|
import { PERMISSION as WORKSPACE_PERMISSION } from 'src/enums/workspace';
|
||||||
import { SessionService } from '../services/session.service';
|
import { SessionService } from '../services/session.service';
|
||||||
import { MemberType, WhitelistType } from 'src/interfaces/survey';
|
|
||||||
import { UserService } from 'src/modules/auth/services/user.service';
|
import { UserService } from 'src/modules/auth/services/user.service';
|
||||||
|
|
||||||
@ApiTags('survey')
|
@ApiTags('survey')
|
||||||
@ -247,16 +246,6 @@ export class SurveyController {
|
|||||||
surveyMeta.isCollaborated = false;
|
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 {
|
return {
|
||||||
code: 200,
|
code: 200,
|
||||||
data: {
|
data: {
|
||||||
|
@ -34,7 +34,14 @@
|
|||||||
"tLimit": 0,
|
"tLimit": 0,
|
||||||
"language": "chinese",
|
"language": "chinese",
|
||||||
"answerBegTime": "00:00:00",
|
"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": {
|
"skinConf": {
|
||||||
"skinColor": "#4a4c5b",
|
"skinColor": "#4a4c5b",
|
||||||
|
@ -97,14 +97,17 @@ export class ResponseSchemaController {
|
|||||||
// 密码校验
|
// 密码校验
|
||||||
if (passwordSwitch) {
|
if (passwordSwitch) {
|
||||||
if (settingPassword !== password) {
|
if (settingPassword !== password) {
|
||||||
throw new HttpException('验证失败', EXCEPTION_CODE.WHITELIST_ERROR);
|
throw new HttpException('密码验证失败', EXCEPTION_CODE.WHITELIST_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 名单校验(手机号/邮箱)
|
// 名单校验(手机号/邮箱)
|
||||||
if (whitelistType === WhitelistType.CUSTOM) {
|
if (whitelistType === WhitelistType.CUSTOM) {
|
||||||
if (!whitelist.includes(whitelistValue)) {
|
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) {
|
if (whitelistType === WhitelistType.MEMBER) {
|
||||||
const user = await this.userService.getUserByUsername(whitelistValue);
|
const user = await this.userService.getUserByUsername(whitelistValue);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new HttpException('验证失败', EXCEPTION_CODE.WHITELIST_ERROR);
|
throw new HttpException('名单验证失败', EXCEPTION_CODE.WHITELIST_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
const workspaceMember = await this.workspaceMemberService.findAllByUserId(
|
const workspaceMember = await this.workspaceMemberService.findAllByUserId(
|
||||||
|
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { MongoRepository } from 'typeorm';
|
import { MongoRepository } from 'typeorm';
|
||||||
import { SurveyResponse } from 'src/models/surveyResponse.entity';
|
import { SurveyResponse } from 'src/models/surveyResponse.entity';
|
||||||
|
import { RECORD_STATUS } from 'src/enums';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SurveyResponseService {
|
export class SurveyResponseService {
|
||||||
constructor(
|
constructor(
|
||||||
@ -35,14 +36,14 @@ export class SurveyResponseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getSurveyResponseTotalByPath(surveyPath: string) {
|
async getSurveyResponseTotalByPath(surveyPath: string) {
|
||||||
const count = await this.surveyResponseRepository.count({
|
const data = await this.surveyResponseRepository.find({
|
||||||
where: {
|
where: {
|
||||||
surveyPath,
|
surveyPath,
|
||||||
'curStatus.status': {
|
'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
|
let timer: any
|
||||||
|
|
||||||
const showConfirmBox = () => {
|
const showConfirmBox = () => {
|
||||||
ElMessageBox.alert('登录状态已失效,请重新登陆。', '提示', {
|
ElMessageBox.alert('登录状态已失效,请重新登录。', '提示', {
|
||||||
confirmButtonText: '确认',
|
confirmButtonText: '确认',
|
||||||
showClose: false,
|
showClose: false,
|
||||||
callback: (action: Action) => {
|
callback: (action: Action) => {
|
||||||
@ -57,9 +57,9 @@ const checkAuth = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => userStore.hasLogined,
|
() => userStore.hasLogin,
|
||||||
(hasLogined) => {
|
(hasLogin) => {
|
||||||
if (hasLogined) {
|
if (hasLogin) {
|
||||||
timer = setTimeout(
|
timer = setTimeout(
|
||||||
() => {
|
() => {
|
||||||
checkAuth()
|
checkAuth()
|
||||||
|
@ -37,9 +37,9 @@ instance.interceptors.response.use(
|
|||||||
|
|
||||||
instance.interceptors.request.use((config) => {
|
instance.interceptors.request.use((config) => {
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const hasLogined = _get(userStore, 'hasLogined')
|
const hasLogin = _get(userStore, 'hasLogin')
|
||||||
const token = _get(userStore, 'userInfo.token')
|
const token = _get(userStore, 'userInfo.token')
|
||||||
if (hasLogined && token) {
|
if (hasLogin && token) {
|
||||||
if (!config.headers) {
|
if (!config.headers) {
|
||||||
config.headers = {}
|
config.headers = {}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// 引入防抖函数
|
// 引入防抖函数
|
||||||
import { debounce as _debounce } from 'lodash-es'
|
import { debounce } from 'lodash-es'
|
||||||
/**
|
/**
|
||||||
* @description: 监听元素尺寸变化
|
* @description: 监听元素尺寸变化
|
||||||
* @param {*} el 元素dom
|
* @param {*} el 元素dom
|
||||||
@ -8,7 +8,7 @@ import { debounce as _debounce } from 'lodash-es'
|
|||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
export default (el, cb, wait = 200) => {
|
export default (el, cb, wait = 200) => {
|
||||||
const resizeObserver = new ResizeObserver(_debounce(cb, wait))
|
const resizeObserver = new ResizeObserver(debounce(cb, wait))
|
||||||
|
|
||||||
resizeObserver.observe(el)
|
resizeObserver.observe(el)
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
<div><slot v-if="moduleConfig.type !== 'section'"></slot></div>
|
<div><slot v-if="moduleConfig.type !== 'section'"></slot></div>
|
||||||
|
|
||||||
<div :class="[showHover ? 'visibily' : 'hidden', 'hoverItem']">
|
<div :class="[showHover ? 'visibility' : 'hidden', 'hoverItem']">
|
||||||
<div
|
<div
|
||||||
class="item el-icon-rank"
|
class="item el-icon-rank"
|
||||||
@click.stop.prevent
|
@click.stop.prevent
|
||||||
|
@ -29,8 +29,6 @@ import { ref, watch } from 'vue'
|
|||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useEditStore } from '@/management/stores/edit'
|
import { useEditStore } from '@/management/stores/edit'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import 'moment/locale/zh-cn'
|
|
||||||
moment.locale('zh-cn')
|
|
||||||
|
|
||||||
import { getSurveyHistory } from '@/management/api/survey'
|
import { getSurveyHistory } from '@/management/api/survey'
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ const getSpaceMenus = async () => {
|
|||||||
id: v.ownerId,
|
id: v.ownerId,
|
||||||
label: v.name,
|
label: v.name,
|
||||||
children: members?.map((v) => ({
|
children: members?.map((v) => ({
|
||||||
id: v.userId,
|
id: `${v.workspaceId}_${v.userId}`,
|
||||||
label: v.username
|
label: v.username
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
@ -26,15 +26,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref, onMounted } from 'vue'
|
||||||
import { useEditStore } from '@/management/stores/edit'
|
import { useEditStore } from '@/management/stores/edit'
|
||||||
|
import { getBannerData } from '@/management/api/skin.js'
|
||||||
import skinPresets from '@/management/config/skinPresets.js'
|
import skinPresets from '@/management/config/skinPresets.js'
|
||||||
|
|
||||||
const editStore = useEditStore()
|
const editStore = useEditStore()
|
||||||
const { changeThemePreset } = editStore
|
const { changeThemePreset } = editStore
|
||||||
const groupName = ref<string>('temp')
|
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(() =>
|
const groupList = computed(() =>
|
||||||
Object.keys(bannerList.value).map((key) => ({
|
Object.keys(bannerList.value).map((key) => ({
|
||||||
label: bannerList.value[key].name,
|
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 acc.concat(curr)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return allbanner.filter((item: any) => {
|
return allBanner.filter((item: any) => {
|
||||||
if (groupName.value === 'temp') {
|
if (groupName.value === 'temp') {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
|
@ -12,19 +12,19 @@
|
|||||||
</CommonTemplate>
|
</CommonTemplate>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted } from 'vue'
|
// import { onMounted } from 'vue'
|
||||||
import { useEditStore } from '@/management/stores/edit'
|
// import { useEditStore } from '@/management/stores/edit'
|
||||||
|
|
||||||
import CommonTemplate from '../../components/CommonTemplate.vue'
|
import CommonTemplate from '../../components/CommonTemplate.vue'
|
||||||
import CatalogPanel from '../../modules/skinModule/CatalogPanel.vue'
|
import CatalogPanel from '../../modules/skinModule/CatalogPanel.vue'
|
||||||
import PreviewPanel from '../../modules/skinModule/PreviewPanel.vue'
|
import PreviewPanel from '../../modules/skinModule/PreviewPanel.vue'
|
||||||
import SetterPanel from '../../modules/skinModule/SetterPanel.vue'
|
import SetterPanel from '../../modules/skinModule/SetterPanel.vue'
|
||||||
|
|
||||||
const editStore = useEditStore()
|
// const editStore = useEditStore()
|
||||||
|
|
||||||
onMounted(() => {
|
// onMounted(() => {
|
||||||
editStore.fetchBannerData()
|
// editStore.fetchBannerData()
|
||||||
})
|
// })
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.navbar {
|
.navbar {
|
||||||
|
@ -27,16 +27,14 @@ export default {
|
|||||||
label: '允许断点续答',
|
label: '允许断点续答',
|
||||||
tip: '回填前一次作答中的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)',
|
tip: '回填前一次作答中的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)',
|
||||||
placement: 'top',
|
placement: 'top',
|
||||||
type: 'CustomedSwitch',
|
type: 'CustomedSwitch'
|
||||||
value: false
|
|
||||||
},
|
},
|
||||||
limit_fillSubmitAnswer: {
|
limit_fillSubmitAnswer: {
|
||||||
key: 'fillSubmitAnswer',
|
key: 'fillSubmitAnswer',
|
||||||
label: '自动填充上次提交内容',
|
label: '自动填充上次提交内容',
|
||||||
tip: '回填前一次提交的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)',
|
tip: '回填前一次提交的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)',
|
||||||
placement: 'top',
|
placement: 'top',
|
||||||
type: 'CustomedSwitch',
|
type: 'CustomedSwitch'
|
||||||
value: false
|
|
||||||
},
|
},
|
||||||
interview_pwd_switch: {
|
interview_pwd_switch: {
|
||||||
key: 'passwordSwitch',
|
key: 'passwordSwitch',
|
||||||
@ -81,6 +79,10 @@ export default {
|
|||||||
{
|
{
|
||||||
key: 'whitelist', // 切换tab清空名单列表
|
key: 'whitelist', // 切换tab清空名单列表
|
||||||
value: []
|
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.scss'
|
||||||
import 'element-plus/theme-chalk/src/message-box.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 EmptyIndex from '@/management/components/EmptyIndex.vue'
|
||||||
import CooperModify from '@/management/components/CooperModify/ModifyDialog.vue'
|
import CooperModify from '@/management/components/CooperModify/ModifyDialog.vue'
|
||||||
import { CODE_MAP } from '@/management/api/base'
|
import { CODE_MAP } from '@/management/api/base'
|
||||||
|
@ -158,15 +158,11 @@ const handleModify = async (id: string) => {
|
|||||||
showSpaceModify.value = true
|
showSpaceModify.value = true
|
||||||
}
|
}
|
||||||
const handleDelete = (id: string) => {
|
const handleDelete = (id: string) => {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm('删除后团队内的问卷将同步被删除,是否确认本次删除?', '提示', {
|
||||||
'删除团队后,团队内的问卷将同步被删除,请谨慎考虑!是否确认本次删除?',
|
confirmButtonText: '确定',
|
||||||
'提示',
|
cancelButtonText: '取消',
|
||||||
{
|
type: 'warning'
|
||||||
confirmButtonText: '确定',
|
})
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await workSpaceStore.deleteSpace(id)
|
await workSpaceStore.deleteSpace(id)
|
||||||
await workSpaceStore.getSpaceList()
|
await workSpaceStore.getSpaceList()
|
||||||
|
@ -44,20 +44,22 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item class="button-group">
|
<el-form-item class="button-group">
|
||||||
|
<el-button
|
||||||
|
:loading="pending.register"
|
||||||
|
class="button register-button"
|
||||||
|
@click="submitForm('register')"
|
||||||
|
>
|
||||||
|
注册
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
:loading="pending.login"
|
:loading="pending.login"
|
||||||
size="small"
|
size="small"
|
||||||
type="primary"
|
type="primary"
|
||||||
class="button"
|
class="button"
|
||||||
@click="submitForm('login')"
|
@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-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -71,7 +73,7 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import 'element-plus/theme-chalk/src/message.scss'
|
import 'element-plus/theme-chalk/src/message.scss'
|
||||||
|
|
||||||
import { debounce as _debounce } from 'lodash-es'
|
import { debounce } from 'lodash-es'
|
||||||
|
|
||||||
import { getPasswordStrength, login, register } from '@/management/api/auth'
|
import { getPasswordStrength, login, register } from '@/management/api/auth'
|
||||||
import { refreshCaptcha as refreshCaptchaApi } from '@/management/api/captcha'
|
import { refreshCaptcha as refreshCaptchaApi } from '@/management/api/captcha'
|
||||||
@ -161,7 +163,7 @@ const rules = {
|
|||||||
],
|
],
|
||||||
password: [
|
password: [
|
||||||
{
|
{
|
||||||
validator: _debounce(passwordValidator, 500),
|
validator: debounce(passwordValidator, 500),
|
||||||
trigger: 'change'
|
trigger: 'change'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -285,7 +287,7 @@ const refreshCaptcha = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
width: 200px;
|
width: 204px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
@ -293,7 +295,6 @@ const refreshCaptcha = async () => {
|
|||||||
.register-button {
|
.register-button {
|
||||||
border-color: #faa600;
|
border-color: #faa600;
|
||||||
color: #faa600;
|
color: #faa600;
|
||||||
margin-left: 20px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ async function handleLoginGuard(
|
|||||||
next: NavigationGuardNext
|
next: NavigationGuardNext
|
||||||
) {
|
) {
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
if (userStore?.hasLogined) {
|
if (userStore?.hasLogin) {
|
||||||
await handlePermissionsGuard(to, from, next)
|
await handlePermissionsGuard(to, from, next)
|
||||||
} else {
|
} else {
|
||||||
next({
|
next({
|
||||||
@ -221,8 +221,8 @@ async function handlePermissionsGuard(
|
|||||||
} else {
|
} else {
|
||||||
// 如果跳转编辑页面,且跳转页面和上一页的surveyId不同,判断是否有对应页面权限
|
// 如果跳转编辑页面,且跳转页面和上一页的surveyId不同,判断是否有对应页面权限
|
||||||
if (currSurveyId !== prevSurveyId) {
|
if (currSurveyId !== prevSurveyId) {
|
||||||
await editStore.fetchCooperPermissions(currSurveyId as string)
|
const cooperPermissions = await editStore.fetchCooperPermissions(currSurveyId as string)
|
||||||
if (hasRequiredPermissions(to.meta.permissions as string[], editStore.cooperPermissions)) {
|
if (hasRequiredPermissions(to.meta.permissions as string[], cooperPermissions)) {
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
ElMessage.warning('您没有该问卷的相关协作权限')
|
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'
|
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 { defineStore } from 'pinia'
|
||||||
import {
|
import { set as _set, isNumber as _isNumber } from 'lodash-es'
|
||||||
merge as _merge,
|
|
||||||
cloneDeep as _cloneDeep,
|
|
||||||
set as _set,
|
|
||||||
isNumber as _isNumber
|
|
||||||
} from 'lodash-es'
|
|
||||||
import { ElMessageBox } from 'element-plus'
|
|
||||||
import { QUESTION_TYPE } from '@/common/typeEnum'
|
import { QUESTION_TYPE } from '@/common/typeEnum'
|
||||||
import { getQuestionByType } from '@/management/utils/index'
|
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 { SurveyPermissions } from '@/management/utils/types/workSpace'
|
||||||
import { getBannerData } from '@/management/api/skin.js'
|
|
||||||
import { getCollaboratorPermissions } from '@/management/api/space'
|
import { getCollaboratorPermissions } from '@/management/api/space'
|
||||||
import useEditGlobalBaseConf, { type TypeMethod } from './composables/useEditGlobalBaseConf'
|
|
||||||
|
import 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 { 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', () => {
|
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('')
|
const surveyId = ref('')
|
||||||
function setSurveyId(id: string) {
|
function setSurveyId(id: string) {
|
||||||
surveyId.value = id
|
surveyId.value = id
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化schem相关
|
// 初始化问卷内容
|
||||||
const {
|
const {
|
||||||
schema,
|
schema,
|
||||||
sessionId,
|
sessionId,
|
||||||
@ -539,45 +32,45 @@ export const useEditStore = defineStore('edit', () => {
|
|||||||
} = useInitializeSchema(surveyId, () => {
|
} = useInitializeSchema(surveyId, () => {
|
||||||
editGlobalBaseConf.initCounts()
|
editGlobalBaseConf.initCounts()
|
||||||
})
|
})
|
||||||
|
async function init() {
|
||||||
function changeSchema({ key, value }: { key: string; value: any }) {
|
const { metaData } = schema
|
||||||
_set(schema, key, value)
|
if (!metaData || (metaData as any)?._id !== surveyId.value) {
|
||||||
updateTime()
|
await Promise.all([getSchemaFromRemote(), initSessionId()])
|
||||||
|
}
|
||||||
|
currentEditOne.value = null
|
||||||
|
currentEditStatus.value = 'Success'
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeThemePreset(presets: any) {
|
// 问卷协作权限
|
||||||
Object.keys(presets).forEach((key) => {
|
const cooperPermissions = ref(Object.values(SurveyPermissions))
|
||||||
_set(schema, key, presets[key])
|
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 questionDataList = toRef(schema, 'questionDataList')
|
||||||
|
|
||||||
const editGlobalBaseConf = useEditGlobalBaseConf(questionDataList, updateTime)
|
|
||||||
function setQuestionDataList(data: any) {
|
function setQuestionDataList(data: any) {
|
||||||
schema.questionDataList = data
|
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 compareQuestionSeq = (val: Array<any>) => {
|
||||||
const newSeq: Array<string> = []
|
const newSeq: Array<string> = []
|
||||||
const oldSeq: Array<string> = []
|
const oldSeq: Array<string> = []
|
||||||
let status = false
|
let status = false
|
||||||
val.map((v) => {
|
for (const v of val) {
|
||||||
newSeq.push(v.field)
|
newSeq.push(v.field)
|
||||||
})
|
}
|
||||||
;(questionDataList.value as Array<any>).map((v) => {
|
for (const v of questionDataList.value as Array<any>) {
|
||||||
oldSeq.push(v.field)
|
oldSeq.push(v.field)
|
||||||
})
|
}
|
||||||
for (let index = 0; index < newSeq.length; index++) {
|
for (let index = 0; index < newSeq.length; index++) {
|
||||||
if (newSeq[index] !== oldSeq[index]) {
|
if (newSeq[index] !== oldSeq[index]) {
|
||||||
status = true
|
status = true
|
||||||
@ -589,40 +82,38 @@ export const useEditStore = defineStore('edit', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const newQuestionIndex = computed(() => {
|
// 画布区域移动题目顺序
|
||||||
if (_isNumber(currentEditOne.value)) {
|
function moveQuestionDataList(data: any) {
|
||||||
return currentEditOne.value + 1
|
const { startIndex, endIndex } = getSorter()
|
||||||
} else {
|
const newData = [
|
||||||
const pageConf = schema.pageConf
|
...questionDataList.value.slice(0, startIndex),
|
||||||
const questCount = pageConf[schema.pageEditOne - 1]
|
...data,
|
||||||
const { startIndex, endIndex } = getSorter()
|
...questionDataList.value.slice(endIndex)
|
||||||
if (!questCount) {
|
]
|
||||||
return startIndex
|
const countTotal: number = (schema.pageConf as Array<number>).reduce(
|
||||||
}
|
(v: number, i: number) => v + i
|
||||||
return endIndex
|
)
|
||||||
|
if (countTotal != newData.length) {
|
||||||
|
schema.pageConf[pageEditOne.value - 1] = (schema.pageConf[pageEditOne.value - 1] + 1) as never
|
||||||
}
|
}
|
||||||
})
|
setQuestionDataList(newData)
|
||||||
|
}
|
||||||
|
|
||||||
// 当前编辑题目
|
// 问卷schema更新
|
||||||
const {
|
const schemaUpdateTime = ref(Date.now())
|
||||||
currentEditOne,
|
function updateTime() {
|
||||||
currentEditKey,
|
schemaUpdateTime.value = Date.now()
|
||||||
currentEditStatus,
|
}
|
||||||
moduleConfig,
|
|
||||||
formConfigList,
|
|
||||||
currentEditMeta,
|
|
||||||
setCurrentEditOne,
|
|
||||||
changeCurrentEditStatus
|
|
||||||
} = useCurrentEdit({ schema, questionDataList })
|
|
||||||
|
|
||||||
// 初始化问卷
|
function changeSchema({ key, value }: { key: string; value: any }) {
|
||||||
async function init() {
|
_set(schema, key, value)
|
||||||
const { metaData } = schema
|
updateTime()
|
||||||
if (!metaData || (metaData as any)?._id !== surveyId.value) {
|
}
|
||||||
await Promise.all([getSchemaFromRemote(), initSessionId()])
|
|
||||||
}
|
function changeThemePreset(presets: any) {
|
||||||
currentEditOne.value = null
|
Object.keys(presets).forEach((key) => {
|
||||||
currentEditStatus.value = 'Success'
|
_set(schema, key, presets[key])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页相关
|
// 分页相关
|
||||||
@ -642,42 +133,60 @@ export const useEditStore = defineStore('edit', () => {
|
|||||||
swapArrayRanges,
|
swapArrayRanges,
|
||||||
setPage
|
setPage
|
||||||
} = usePageEdit({ schema, questionDataList }, updateTime)
|
} = usePageEdit({ schema, questionDataList }, updateTime)
|
||||||
|
// 增加新分页时新增题目
|
||||||
// 问卷列表相关操作
|
const createNewQuestion = ({ type }: { type: QUESTION_TYPE }) => {
|
||||||
const { copyQuestion, addQuestion, deleteQuestion, moveQuestion } = useQuestionDataListOperations(
|
const fields = questionDataList.value.map((item: any) => item.field)
|
||||||
{
|
const newQuestion = getQuestionByType(type, fields)
|
||||||
questionDataList,
|
newQuestion.title = newQuestion.title = `标题${newQuestionIndex.value + 1}`
|
||||||
updateTime,
|
if (type === QUESTION_TYPE.VOTE) {
|
||||||
pageOperations,
|
newQuestion.innerType = QUESTION_TYPE.RADIO
|
||||||
updateCounts: editGlobalBaseConf.updateCounts
|
|
||||||
}
|
}
|
||||||
)
|
return newQuestion
|
||||||
|
|
||||||
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 })
|
||||||
|
|
||||||
|
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 {
|
return {
|
||||||
editGlobalBaseConf,
|
editGlobalBaseConf,
|
||||||
surveyId,
|
surveyId,
|
||||||
sessionId,
|
sessionId,
|
||||||
setSurveyId,
|
setSurveyId,
|
||||||
bannerList,
|
|
||||||
fetchBannerData,
|
|
||||||
cooperPermissions,
|
cooperPermissions,
|
||||||
fetchCooperPermissions,
|
fetchCooperPermissions,
|
||||||
|
|
||||||
currentEditOne,
|
currentEditOne,
|
||||||
moduleConfig,
|
moduleConfig,
|
||||||
formConfigList,
|
formConfigList,
|
||||||
@ -687,6 +196,7 @@ export const useEditStore = defineStore('edit', () => {
|
|||||||
newQuestionIndex,
|
newQuestionIndex,
|
||||||
setCurrentEditOne,
|
setCurrentEditOne,
|
||||||
changeCurrentEditStatus,
|
changeCurrentEditStatus,
|
||||||
|
|
||||||
pageEditOne,
|
pageEditOne,
|
||||||
pageConf,
|
pageConf,
|
||||||
isFinallyPage,
|
isFinallyPage,
|
||||||
@ -700,6 +210,7 @@ export const useEditStore = defineStore('edit', () => {
|
|||||||
copyPage,
|
copyPage,
|
||||||
swapArrayRanges,
|
swapArrayRanges,
|
||||||
setPage,
|
setPage,
|
||||||
|
|
||||||
schemaUpdateTime,
|
schemaUpdateTime,
|
||||||
schema,
|
schema,
|
||||||
questionDataList,
|
questionDataList,
|
||||||
@ -715,6 +226,7 @@ export const useEditStore = defineStore('edit', () => {
|
|||||||
changeSchema,
|
changeSchema,
|
||||||
changeThemePreset,
|
changeThemePreset,
|
||||||
compareQuestionSeq,
|
compareQuestionSeq,
|
||||||
|
|
||||||
showLogicEngine,
|
showLogicEngine,
|
||||||
jumpLogicEngine
|
jumpLogicEngine
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,29 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import localstorage from '@/common/localstorage'
|
import { getUserInfo, setUserInfo, clearUserInfo } from '@/management/utils/storage'
|
||||||
|
|
||||||
type IUserInfo = {
|
type IUserInfo = {
|
||||||
username: string
|
username: string
|
||||||
token: string
|
token: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const USER_INFO_KEY = 'surveyUserInfo'
|
|
||||||
export const useUserStore = defineStore('user', () => {
|
export const useUserStore = defineStore('user', () => {
|
||||||
const userInfo = ref<IUserInfo | null>({
|
const userInfo = ref<IUserInfo | null>({
|
||||||
username: '',
|
username: '',
|
||||||
token: ''
|
token: ''
|
||||||
})
|
})
|
||||||
const hasLogined = ref(false)
|
const hasLogin = ref(false)
|
||||||
const loginTime = ref<number | null>(null)
|
const loginTime = ref<number | null>(null)
|
||||||
const initialized = ref(false)
|
const initialized = ref(false)
|
||||||
|
|
||||||
const init = () => {
|
const init = () => {
|
||||||
const localData = localstorage.getItem(USER_INFO_KEY)
|
const localData = getUserInfo()
|
||||||
if (localData) {
|
if (localData) {
|
||||||
try {
|
try {
|
||||||
const { userInfo: info, loginTime: time } = localData as any
|
const { userInfo: info, loginTime: time } = localData as any
|
||||||
if (Date.now() - time > 7 * 3600000) {
|
if (Date.now() - time > 7 * 3600000) {
|
||||||
localstorage.removeItem(USER_INFO_KEY)
|
clearUserInfo()
|
||||||
} else {
|
} else {
|
||||||
login(info)
|
login(info)
|
||||||
}
|
}
|
||||||
@ -36,18 +35,18 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
}
|
}
|
||||||
const login = (data: IUserInfo) => {
|
const login = (data: IUserInfo) => {
|
||||||
userInfo.value = data
|
userInfo.value = data
|
||||||
hasLogined.value = true
|
hasLogin.value = true
|
||||||
loginTime.value = Date.now()
|
loginTime.value = Date.now()
|
||||||
localstorage.setItem(USER_INFO_KEY, {
|
setUserInfo({
|
||||||
userInfo: data,
|
userInfo: data,
|
||||||
loginTime: loginTime
|
loginTime: loginTime
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
userInfo.value = null
|
userInfo.value = null
|
||||||
hasLogined.value = false
|
hasLogin.value = false
|
||||||
localstorage.removeItem(USER_INFO_KEY)
|
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: {
|
props: {
|
||||||
uiTarget: {
|
uiTarget: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'radio'
|
default: 'input'
|
||||||
},
|
},
|
||||||
customClass: {
|
customClass: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="mask" v-if="visible">
|
<div class="mask" v-if="visible">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="title">{{ title }}</div>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -34,18 +34,8 @@ const handleConfirm = () => {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import url('../styles/dialog.scss');
|
@import url('../styles/dialog.scss');
|
||||||
|
|
||||||
.btn {
|
.btn-base {
|
||||||
display: inline-block;
|
// display: inline-block;
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 42px;
|
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
line-height: 42px;
|
|
||||||
border-radius: 2px;
|
|
||||||
font-size: 16px;
|
|
||||||
background-color: #4a4c5b;
|
|
||||||
border: 1px solid #4a4c5b;
|
|
||||||
color: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,74 +1,115 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mask" v-show="visible">
|
<div class="mask" v-show="isVisible">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="title">{{ title }}</div>
|
<div class="head-wrapper">
|
||||||
<div class="btn-box">
|
<div class="title">{{ title }}</div>
|
||||||
<div class="btn cancel" @click="handleCancel">{{ cancelBtnText }}</div>
|
<div v-if="tips" class="tips">{{ tips }}</div>
|
||||||
<div class="btn confirm" @click="handleConfirm">{{ confirmBtnText }}</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
type FormItem = {
|
||||||
|
key: string
|
||||||
|
value: string
|
||||||
|
placeholder?: string
|
||||||
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
visible?: boolean
|
visible?: boolean
|
||||||
|
cancel?: boolean // 是否展示取消按钮
|
||||||
cancelBtnText?: string
|
cancelBtnText?: string
|
||||||
confirmBtnText?: string
|
confirmBtnText?: string
|
||||||
title?: string
|
title?: string
|
||||||
|
tips?: string
|
||||||
|
bodyContent?: FormItem[]
|
||||||
|
autoClose?: boolean // 点击确认时是否关闭
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Emit {
|
interface Emit {
|
||||||
(ev: 'confirm', callback: () => void): void
|
(ev: 'confirm', data, callback: () => void): void
|
||||||
(ev: 'close'): void
|
(ev: 'close'): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<Emit>()
|
const emit = defineEmits<Emit>()
|
||||||
|
|
||||||
withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
visible: false,
|
visible: false,
|
||||||
|
cancel: true,
|
||||||
cancelBtnText: '取消',
|
cancelBtnText: '取消',
|
||||||
confirmBtnText: '确定',
|
confirmBtnText: '确定',
|
||||||
title: ''
|
title: '',
|
||||||
|
tips: '',
|
||||||
|
autoClose: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const isVisible = ref(props.visible)
|
||||||
|
const formValues = reactive(props.bodyContent || [])
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
emit('confirm', () => {
|
const data = {}
|
||||||
emit('close')
|
formValues.forEach((item) => {
|
||||||
|
data[item.key] = item.value
|
||||||
})
|
})
|
||||||
|
|
||||||
|
emit('confirm', data, handleCancel)
|
||||||
|
|
||||||
|
props.autoClose && handleCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
|
isVisible.value = false
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import url('../styles/dialog.scss');
|
@import url('../styles/dialog.scss');
|
||||||
|
|
||||||
.btn-box {
|
.tips {
|
||||||
padding: 20px 0 0;
|
text-align: center;
|
||||||
|
font-size: 0.24rem;
|
||||||
|
color: #92949d;
|
||||||
|
margin-top: 0.15rem;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-wrapper {
|
||||||
|
margin-top: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
.btn {
|
.btn-base {
|
||||||
width: 48%;
|
width: 100%;
|
||||||
font-size: 0.28rem;
|
margin-right: 0.2rem;
|
||||||
border-radius: 0.04rem;
|
|
||||||
text-align: center;
|
|
||||||
padding: 0.16rem 0;
|
|
||||||
line-height: 0.4rem;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&.cancel {
|
&:last-child {
|
||||||
background: #fff;
|
margin-right: 0;
|
||||||
color: #92949d;
|
|
||||||
border: 1px solid #e3e4e8;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.confirm {
|
|
||||||
background-color: #4a4c5b;
|
|
||||||
border: 1px solid #4a4c5b;
|
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ const { percent } = useProgressBar()
|
|||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.progress-outer {
|
.progress-outer {
|
||||||
z-index: 10000;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.progress-inner-wrapper-pc {
|
.progress-inner-wrapper-pc {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<QuestionRuleContainer
|
<QuestionRuleContainer
|
||||||
v-if="visibily"
|
v-if="visibility"
|
||||||
:moduleConfig="questionConfig"
|
:moduleConfig="questionConfig"
|
||||||
:indexNumber="indexNumber"
|
:indexNumber="indexNumber"
|
||||||
:showTitle="true"
|
:showTitle="true"
|
||||||
@ -9,18 +9,21 @@
|
|||||||
></QuestionRuleContainer>
|
></QuestionRuleContainer>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { unref, computed, watch, ref } from 'vue'
|
import { unref, computed, watch } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
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 { 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 { 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({
|
const props = defineProps({
|
||||||
indexNumber: {
|
indexNumber: {
|
||||||
@ -41,27 +44,28 @@ const surveyStore = useSurveyStore()
|
|||||||
const formValues = computed(() => {
|
const formValues = computed(() => {
|
||||||
return surveyStore.formValues
|
return surveyStore.formValues
|
||||||
})
|
})
|
||||||
const { showLogicEngine } = storeToRefs(surveyStore)
|
const { showLogicEngine, baseConf } = storeToRefs(surveyStore)
|
||||||
const { changeField, changeIndex, needHideFields } = storeToRefs(questionStore)
|
const { changeField, changeIndex, needHideFields } = storeToRefs(questionStore)
|
||||||
|
|
||||||
// 题型配置转换
|
// 题型配置转换
|
||||||
const questionConfig = computed(() => {
|
const questionConfig = computed(() => {
|
||||||
let moduleConfig = props.moduleConfig
|
let moduleConfig = props.moduleConfig
|
||||||
const { type, field, options = [], ...rest } = cloneDeep(moduleConfig)
|
const { type, field, options = [], ...rest } = cloneDeep(moduleConfig)
|
||||||
|
|
||||||
let alloptions = options
|
let allOptions = options
|
||||||
|
|
||||||
if (type === QUESTION_TYPE.VOTE) {
|
if (type === QUESTION_TYPE.VOTE) {
|
||||||
// 处理投票进度
|
// 处理投票进度
|
||||||
const { options, voteTotal } = useVoteMap(field)
|
const { options, voteTotal } = useVoteMap(field)
|
||||||
const voteOptions = unref(options)
|
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)
|
moduleConfig.voteTotal = unref(voteTotal)
|
||||||
}
|
}
|
||||||
if (NORMAL_CHOICES.includes(type) && options.some((option) => option.others)) {
|
if (NORMAL_CHOICES.includes(type) && options.some((option) => option.others)) {
|
||||||
// 处理普通选择题的填写更多
|
// 处理普通选择题的填写更多
|
||||||
let { options, othersValue } = useShowOthers(field)
|
let { options, othersValue } = useOthersData(field)
|
||||||
const othersOptions = unref(options)
|
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)
|
moduleConfig.othersValue = unref(othersValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,34 +76,79 @@ const questionConfig = computed(() => {
|
|||||||
0
|
0
|
||||||
) {
|
) {
|
||||||
// 处理评分题的的选项后输入框
|
// 处理评分题的的选项后输入框
|
||||||
let { rangeConfig, othersValue } = useShowInput(field)
|
let { rangeConfig, othersValue } = useInputData(field)
|
||||||
moduleConfig.rangeConfig = unref(rangeConfig)
|
moduleConfig.rangeConfig = unref(rangeConfig)
|
||||||
moduleConfig.othersValue = unref(othersValue)
|
moduleConfig.othersValue = unref(othersValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...moduleConfig,
|
...moduleConfig,
|
||||||
options: alloptions,
|
options: allOptions,
|
||||||
value: formValues.value[props.moduleConfig.field]
|
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有变化的时候触发重新计算
|
// computed有计算缓存,当match有变化的时候触发重新计算
|
||||||
const result = showLogicEngine.value.match(props.moduleConfig.field, 'question', formValues.value)
|
const result = showLogicEngine.value.match(props.moduleConfig.field, 'question', formValues.value)
|
||||||
return result === undefined ? true : result
|
return result === undefined ? true : result
|
||||||
})
|
})
|
||||||
|
// 跳转逻辑:题目是否需要跳过(隐藏)
|
||||||
const logicskip = computed(() => {
|
const logicSkip = computed(() => {
|
||||||
return needHideFields.value.includes(props.moduleConfig.field)
|
return needHideFields.value.includes(props.moduleConfig.field)
|
||||||
})
|
})
|
||||||
const visibily = computed(() => {
|
const visibility = computed(() => {
|
||||||
return logicshow.value && !logicskip.value
|
return logicShow.value && !logicSkip.value
|
||||||
})
|
})
|
||||||
|
|
||||||
// 当题目被隐藏时,清空题目的选中项,实现a显示关联b,b显示关联c场景下,b隐藏不影响题目c的展示
|
// 当题目被隐藏时,清空题目的选中项,实现a显示关联b,b显示关联c场景下,b隐藏不影响题目c的展示
|
||||||
watch(
|
watch(
|
||||||
() => visibily.value,
|
() => visibility.value,
|
||||||
(newVal, oldVal) => {
|
(newVal, oldVal) => {
|
||||||
const { field, type, innerType } = props.moduleConfig
|
const { field, type, innerType } = props.moduleConfig
|
||||||
if (!newVal && oldVal) {
|
if (!newVal && oldVal) {
|
||||||
@ -110,46 +159,20 @@ watch(
|
|||||||
if (type === QUESTION_TYPE.CHECKBOX || innerType === QUESTION_TYPE.CHECKBOX) {
|
if (type === QUESTION_TYPE.CHECKBOX || innerType === QUESTION_TYPE.CHECKBOX) {
|
||||||
value = value ? [value] : []
|
value = value ? [value] : []
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
key: field,
|
key: field,
|
||||||
value: value
|
value: value
|
||||||
}
|
}
|
||||||
surveyStore.changeData(data)
|
surveyStore.changeData(data)
|
||||||
|
|
||||||
processJumpSkip()
|
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 processJumpSkip = () => {
|
||||||
const targetResult = surveyStore.jumpLogicEngine
|
const targetResult = surveyStore.jumpLogicEngine
|
||||||
.getResultsByField(changeField.value, surveyStore.formValues)
|
.getResultsByField(changeField.value, surveyStore.formValues)
|
||||||
@ -190,9 +213,5 @@ const processJumpSkip = () => {
|
|||||||
.map((item) => item.field)
|
.map((item) => item.field)
|
||||||
questionStore.addNeedHideFields(skipKey)
|
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>
|
</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 { useQuestionStore } from '../stores/question'
|
||||||
import { useSurveyStore } from '../stores/survey'
|
import { useSurveyStore } from '../stores/survey'
|
||||||
|
|
||||||
export const useShowInput = (questionKey) => {
|
export const useInputData = (questionKey) => {
|
||||||
const questionStore = useQuestionStore()
|
const questionStore = useQuestionStore()
|
||||||
const surveyStore = useSurveyStore()
|
const surveyStore = useSurveyStore()
|
||||||
const formValues = surveyStore.formValues
|
const formValues = surveyStore.formValues
|
@ -1,7 +1,7 @@
|
|||||||
import { useQuestionStore } from '../stores/question'
|
import { useQuestionStore } from '../stores/question'
|
||||||
import { useSurveyStore } from '../stores/survey'
|
import { useSurveyStore } from '../stores/survey'
|
||||||
|
|
||||||
export const useShowOthers = (questionKey) => {
|
export const useOthersData = (questionKey) => {
|
||||||
const questionStore = useQuestionStore()
|
const questionStore = useQuestionStore()
|
||||||
const surveyStore = useSurveyStore()
|
const surveyStore = useSurveyStore()
|
||||||
const formValues = surveyStore.formValues
|
const formValues = surveyStore.formValues
|
@ -38,7 +38,7 @@ export const useProgressBar = () => {
|
|||||||
|
|
||||||
const percent = computed(() => {
|
const percent = computed(() => {
|
||||||
const { fillCount, topicCount } = surveySchedule.value
|
const { fillCount, topicCount } = surveySchedule.value
|
||||||
return Math.floor((100 / topicCount) * fillCount) + '%'
|
return Math.floor((100 / topicCount) * fillCount) || 0 + '%'
|
||||||
})
|
})
|
||||||
|
|
||||||
return { surveySchedule, percent }
|
return { surveySchedule, percent }
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<img src="/imgs/nodata.webp" alt="" class="ui-404" />
|
<img src="/imgs/nodata.webp" alt="" class="ui-404" />
|
||||||
<div class="ui-title">
|
<div class="ui-title">问卷找不到啦~</div>
|
||||||
<div class="ui-maintitle">问卷找不到啦~</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -2,10 +2,15 @@
|
|||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { watch } from 'vue'
|
import { watch, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
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 route = useRoute()
|
||||||
|
const surveyStore = useSurveyStore()
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.query.t,
|
() => route.query.t,
|
||||||
@ -13,4 +18,65 @@ watch(
|
|||||||
location.reload()
|
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>
|
</script>
|
||||||
|
@ -16,31 +16,36 @@
|
|||||||
></SubmitButton>
|
></SubmitButton>
|
||||||
</div>
|
</div>
|
||||||
<LogoIcon :logo-conf="logoConf" :readonly="true" />
|
<LogoIcon :logo-conf="logoConf" :readonly="true" />
|
||||||
<VerifyWhiteDialog />
|
<VerifyDialog />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, onMounted } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import communalLoader from '@materials/communals/communalLoader.js'
|
import communalLoader from '@materials/communals/communalLoader.js'
|
||||||
|
|
||||||
|
import useCommandComponent from '../hooks/useCommandComponent'
|
||||||
import MainRenderer from '../components/MainRenderer.vue'
|
import MainRenderer from '../components/MainRenderer.vue'
|
||||||
import AlertDialog from '../components/AlertDialog.vue'
|
import AlertDialog from '../components/AlertDialog.vue'
|
||||||
import VerifyWhiteDialog from '../components/VerifyWhiteDialog.vue'
|
|
||||||
import ConfirmDialog from '../components/ConfirmDialog.vue'
|
import ConfirmDialog from '../components/ConfirmDialog.vue'
|
||||||
|
import VerifyDialog from '../components/VerifyDialog/index.vue'
|
||||||
|
|
||||||
import ProgressBar from '../components/ProgressBar.vue'
|
import ProgressBar from '../components/ProgressBar.vue'
|
||||||
|
|
||||||
import { useSurveyStore } from '../stores/survey'
|
import { useSurveyStore } from '../stores/survey'
|
||||||
import { useQuestionStore } from '../stores/question'
|
import { useQuestionStore } from '../stores/question'
|
||||||
import { submitForm } from '../api/survey'
|
import { submitForm } from '../api/survey'
|
||||||
import encrypt from '../utils/encrypt'
|
import encrypt from '../utils/encrypt'
|
||||||
|
import {
|
||||||
import useCommandComponent from '../hooks/useCommandComponent'
|
clearSurveyData,
|
||||||
import { getPublishedSurveyInfo, getPreviewSchema } from '../api/survey'
|
setSurveyData,
|
||||||
import { FORMDATA_SUFFIX, SUBMIT_FLAG } from '@/render/utils/constant'
|
clearSurveySubmit,
|
||||||
import localstorage from '@/common/localstorage'
|
setSurveySubmit
|
||||||
|
} from '../utils/storage'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
questionInfo?: any
|
questionInfo?: any
|
||||||
@ -73,76 +78,16 @@ const pageIndex = computed(() => questionStore.pageIndex)
|
|||||||
const { bannerConf, submitConf, bottomConf: logoConf, whiteData } = storeToRefs(surveyStore)
|
const { bannerConf, submitConf, bottomConf: logoConf, whiteData } = storeToRefs(surveyStore)
|
||||||
const surveyPath = computed(() => surveyStore.surveyPath || '')
|
const surveyPath = computed(() => surveyStore.surveyPath || '')
|
||||||
|
|
||||||
const route = useRoute()
|
const validate = (callback: (v: boolean) => void) => {
|
||||||
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 index = 0
|
const index = 0
|
||||||
mainRef.value.$refs.formGroup[index].validate(cbk)
|
mainRef.value.$refs.formGroup[index].validate(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizationRequestBody = () => {
|
const normalizationRequestBody = () => {
|
||||||
const enterTime = surveyStore.enterTime
|
const enterTime = surveyStore.enterTime
|
||||||
const encryptInfo = surveyStore.encryptInfo as any
|
const encryptInfo = surveyStore.encryptInfo as any
|
||||||
const formValues = surveyStore.formValues
|
const formValues = surveyStore.formValues
|
||||||
|
const baseConf = surveyStore.baseConf
|
||||||
|
|
||||||
const result: any = {
|
const result: any = {
|
||||||
surveyPath: surveyPath.value,
|
surveyPath: surveyPath.value,
|
||||||
@ -152,21 +97,24 @@ const normalizationRequestBody = () => {
|
|||||||
...whiteData.value
|
...whiteData.value
|
||||||
}
|
}
|
||||||
|
|
||||||
//浏览器缓存数据
|
// 自动回填开启时,记录数据
|
||||||
localstorage.removeItem(surveyPath.value + FORMDATA_SUFFIX)
|
if (baseConf.fillSubmitAnswer) {
|
||||||
localstorage.removeItem(SUBMIT_FLAG)
|
clearSurveyData(surveyPath.value)
|
||||||
//数据加密
|
clearSurveySubmit(surveyPath.value)
|
||||||
let formData: Record<string, any> = Object.assign({}, surveyStore.formValues)
|
|
||||||
|
|
||||||
localstorage.setItem(surveyPath.value + FORMDATA_SUFFIX, formData)
|
setSurveyData(surveyPath.value, formValues)
|
||||||
localstorage.setItem(SUBMIT_FLAG, true)
|
setSurveySubmit(surveyPath.value, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据加密
|
||||||
if (encryptInfo?.encryptType) {
|
if (encryptInfo?.encryptType) {
|
||||||
result.encryptType = encryptInfo.encryptType
|
result.encryptType = encryptInfo.encryptType
|
||||||
|
|
||||||
result.data = encrypt[result.encryptType as 'rsa']({
|
result.data = encrypt[result.encryptType as 'rsa']({
|
||||||
data: result.data,
|
data: result.data,
|
||||||
secretKey: encryptInfo?.data?.secretKey
|
secretKey: encryptInfo?.data?.secretKey
|
||||||
})
|
})
|
||||||
|
|
||||||
if (encryptInfo?.data?.sessionId) {
|
if (encryptInfo?.data?.sessionId) {
|
||||||
result.sessionId = encryptInfo.data.sessionId
|
result.sessionId = encryptInfo.data.sessionId
|
||||||
}
|
}
|
||||||
@ -177,7 +125,7 @@ const normalizationRequestBody = () => {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitSurver = async () => {
|
const submitSurvey = async () => {
|
||||||
if (surveyPath.value.length > 8) {
|
if (surveyPath.value.length > 8) {
|
||||||
router.push({ name: 'successPage' })
|
router.push({ name: 'successPage' })
|
||||||
return
|
return
|
||||||
@ -209,7 +157,7 @@ const handleSubmit = () => {
|
|||||||
title: again_text,
|
title: again_text,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
try {
|
try {
|
||||||
submitSurver()
|
submitSurvey()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
} finally {
|
} finally {
|
||||||
@ -218,7 +166,7 @@ const handleSubmit = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
submitSurver()
|
submitSurvey()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -30,7 +30,9 @@ import communalLoader from '@materials/communals/communalLoader.js'
|
|||||||
const LogoIcon = communalLoader.loadComponent('LogoIcon')
|
const LogoIcon = communalLoader.loadComponent('LogoIcon')
|
||||||
const surveyStore = useSurveyStore()
|
const surveyStore = useSurveyStore()
|
||||||
|
|
||||||
const logoConf = computed(() => surveyStore?.bottomConf || {})
|
const logoConf = computed(() => {
|
||||||
|
return surveyStore?.bottomConf || {}
|
||||||
|
})
|
||||||
const successMsg = computed(() => {
|
const successMsg = computed(() => {
|
||||||
const msgContent = (surveyStore?.submitConf as any)?.msgContent || {}
|
const msgContent = (surveyStore?.submitConf as any)?.msgContent || {}
|
||||||
return msgContent?.msg_200 || '提交成功'
|
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 { useSurveyStore } from '@/render/stores/survey'
|
||||||
import { queryVote } from '@/render/api/survey'
|
import { queryVote } from '@/render/api/survey'
|
||||||
import { QUESTION_TYPE } from '@/common/typeEnum'
|
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 voteMap = ref({})
|
||||||
//初始化投票题的数据
|
//初始化投票题的数据
|
||||||
const initVoteData = async () => {
|
const initVoteData = async () => {
|
||||||
@ -28,16 +28,14 @@ const usevVoteMap = (questionData) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
localstorage.removeItem(VOTE_INFO_KEY)
|
clearVoteData()
|
||||||
const voteRes = await queryVote({
|
const voteRes = await queryVote({
|
||||||
surveyPath,
|
surveyPath,
|
||||||
fieldList: fieldList.join(',')
|
fieldList: fieldList.join(',')
|
||||||
})
|
})
|
||||||
|
|
||||||
if (voteRes.code === 200) {
|
if (voteRes.code === 200) {
|
||||||
localstorage.setItem(VOTE_INFO_KEY, {
|
setVoteData(voteRes.data)
|
||||||
...voteRes.data
|
|
||||||
})
|
|
||||||
setVoteMap(voteRes.data)
|
setVoteMap(voteRes.data)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -58,25 +56,25 @@ const usevVoteMap = (questionData) => {
|
|||||||
const updateVoteData = (data) => {
|
const updateVoteData = (data) => {
|
||||||
const { key: questionKey, value: questionVal } = data
|
const { key: questionKey, value: questionVal } = data
|
||||||
// 更新前获取接口缓存在localstorage中的数据
|
// 更新前获取接口缓存在localstorage中的数据
|
||||||
const voteinfo = localstorage.getItem(VOTE_INFO_KEY)
|
const voteInfo = getVoteData()
|
||||||
const currentQuestion = questionData.value[questionKey]
|
const currentQuestion = questionData.value[questionKey]
|
||||||
const options = currentQuestion.options
|
const options = currentQuestion.options
|
||||||
const voteTotal = voteinfo?.[questionKey]?.total || 0
|
const voteTotal = voteInfo?.[questionKey]?.total || 0
|
||||||
let totalPayload = {
|
let totalPayload = {
|
||||||
questionKey,
|
questionKey,
|
||||||
voteKey: 'total',
|
voteKey: 'total',
|
||||||
voteValue: voteTotal
|
voteValue: voteTotal
|
||||||
}
|
}
|
||||||
options.forEach((option) => {
|
options.forEach((option) => {
|
||||||
const optionhash = option.hash
|
const optionHash = option.hash
|
||||||
const voteCount = voteinfo?.[questionKey]?.[optionhash] || 0
|
const voteCount = voteInfo?.[questionKey]?.[optionHash] || 0
|
||||||
// 如果选中值包含该选项,对应voteCount 和 voteTotal + 1
|
// 如果选中值包含该选项,对应voteCount 和 voteTotal + 1
|
||||||
if (
|
if (
|
||||||
Array.isArray(questionVal) ? questionVal.includes(optionhash) : questionVal === optionhash
|
Array.isArray(questionVal) ? questionVal.includes(optionHash) : questionVal === optionHash
|
||||||
) {
|
) {
|
||||||
const countPayload = {
|
const countPayload = {
|
||||||
questionKey,
|
questionKey,
|
||||||
voteKey: optionhash,
|
voteKey: optionHash,
|
||||||
voteValue: voteCount + 1
|
voteValue: voteCount + 1
|
||||||
}
|
}
|
||||||
totalPayload.voteValue += 1
|
totalPayload.voteValue += 1
|
||||||
@ -84,7 +82,7 @@ const usevVoteMap = (questionData) => {
|
|||||||
} else {
|
} else {
|
||||||
const countPayload = {
|
const countPayload = {
|
||||||
questionKey,
|
questionKey,
|
||||||
voteKey: optionhash,
|
voteKey: optionHash,
|
||||||
voteValue: voteCount
|
voteValue: voteCount
|
||||||
}
|
}
|
||||||
updateVoteMapByKey(countPayload)
|
updateVoteMapByKey(countPayload)
|
||||||
@ -174,7 +172,7 @@ export const useQuestionStore = defineStore('question', () => {
|
|||||||
const setQuestionData = (data) => {
|
const setQuestionData = (data) => {
|
||||||
questionData.value = data
|
questionData.value = data
|
||||||
}
|
}
|
||||||
const { voteMap, setVoteMap, initVoteData, updateVoteData } = usevVoteMap(questionData)
|
const { voteMap, setVoteMap, initVoteData, updateVoteData } = useVoteMap(questionData)
|
||||||
|
|
||||||
const changeSelectMoreData = (data) => {
|
const changeSelectMoreData = (data) => {
|
||||||
const { key, value, field } = data
|
const { key, value, field } = data
|
||||||
|
@ -1,25 +1,20 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { cloneDeep, pick } from 'lodash-es'
|
import { pick } from 'lodash-es'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
// 引入中文
|
// 引入中文
|
||||||
import 'moment/locale/zh-cn'
|
import 'moment/locale/zh-cn'
|
||||||
// 设置中文
|
// 设置中文
|
||||||
|
|
||||||
import { isMobile as isInMobile } from '@/render/utils/index'
|
import { isMobile as isInMobile } from '@/render/utils/index'
|
||||||
|
|
||||||
import { getEncryptInfo as getEncryptInfoApi } from '@/render/api/survey'
|
import { getEncryptInfo as getEncryptInfoApi } from '@/render/api/survey'
|
||||||
import { useQuestionStore } from '@/render/stores/question'
|
import { useQuestionStore } from '@/render/stores/question'
|
||||||
import { useErrorInfo } from '@/render/stores/errorInfo'
|
import { useErrorInfo } from '@/render/stores/errorInfo'
|
||||||
import { FORMDATA_SUFFIX, SUBMIT_FLAG } from '@/render/utils/constant'
|
|
||||||
|
|
||||||
import adapter from '../adapter'
|
import adapter from '../adapter'
|
||||||
import { RuleMatch } from '@/common/logicEngine/RulesMatch'
|
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')
|
moment.locale('zh-cn')
|
||||||
/**
|
/**
|
||||||
@ -63,6 +58,10 @@ export const useSurveyStore = defineStore('survey', () => {
|
|||||||
enterTime.value = Date.now()
|
enterTime.value = Date.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setFormValues = (data) => {
|
||||||
|
formValues.value = data
|
||||||
|
}
|
||||||
|
|
||||||
const getEncryptInfo = async () => {
|
const getEncryptInfo = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await getEncryptInfoApi()
|
const res = await getEncryptInfoApi()
|
||||||
@ -154,13 +153,7 @@ export const useSurveyStore = defineStore('survey', () => {
|
|||||||
// 获取已投票数据
|
// 获取已投票数据
|
||||||
questionStore.initVoteData()
|
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) => {
|
const initSurvey = (option) => {
|
||||||
setEnterTime()
|
setEnterTime()
|
||||||
if (!canFillQuestionnaire(option.baseConf, option.submitConf)) {
|
if (!canFillQuestionnaire(option.baseConf, option.submitConf)) {
|
||||||
@ -168,31 +161,6 @@ export const useSurveyStore = defineStore('survey', () => {
|
|||||||
}
|
}
|
||||||
// 加载空白问卷
|
// 加载空白问卷
|
||||||
clearFormData(option)
|
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)
|
questionStore.setChangeField(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化逻辑引擎
|
||||||
const showLogicEngine = ref()
|
const showLogicEngine = ref()
|
||||||
const initShowLogicEngine = (showLogicConf) => {
|
const initShowLogicEngine = (showLogicConf) => {
|
||||||
showLogicEngine.value = new RuleMatch().fromJson(showLogicConf || [])
|
showLogicEngine.value = new RuleMatch().fromJson(showLogicConf || [])
|
||||||
@ -231,6 +200,7 @@ export const useSurveyStore = defineStore('survey', () => {
|
|||||||
initSurvey,
|
initSurvey,
|
||||||
changeData,
|
changeData,
|
||||||
setWhiteData,
|
setWhiteData,
|
||||||
|
setFormValues,
|
||||||
setSurveyPath,
|
setSurveyPath,
|
||||||
setEnterTime,
|
setEnterTime,
|
||||||
getEncryptInfo,
|
getEncryptInfo,
|
||||||
|
@ -8,19 +8,70 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: #292a36f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box {
|
.box {
|
||||||
width: 6rem;
|
width: 6.3rem;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 0.56rem 0.44rem 0.32rem;
|
padding: 0.56rem 0.44rem 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-wrapper {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 0.3rem;
|
font-size: 0.33rem;
|
||||||
color: #4a4c5b;
|
color: #4a4c5b;
|
||||||
letter-spacing: 0;
|
letter-spacing: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 600;
|
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