fix: pinia改写

This commit is contained in:
dayou 2024-08-23 17:43:38 +08:00
parent 20ef020a19
commit 2162f3cffd
12 changed files with 383 additions and 166 deletions

View File

@ -18,8 +18,6 @@ import { SURVEY_PERMISSION } from 'src/enums/surveyPermission';
import { Logger } from 'src/logger';
import { HttpException } from 'src/exceptions/httpException';
import { EXCEPTION_CODE } from 'src/enums/exceptionCode';
import { val } from 'cheerio/lib/api/attributes';
@ApiTags('survey')
@Controller('/api/surveyHisotry')
export class SurveyHistoryController {

View File

@ -189,7 +189,7 @@ export class SurveyDownloadService implements OnModuleInit {
}
return {
...data,
difTime: (submitedData.difTime / 1000).toFixed(2),
diffTime: (submitedData.diffTime / 1000).toFixed(2),
createDate: moment(submitedData.createDate).format(
'YYYY-MM-DD HH:mm:ss',
),

View File

@ -6,6 +6,7 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useEditStore } from '@/management/stores/edit'
import { useUserStore } from '@/management/stores/user'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox, type Action } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss'
@ -24,9 +25,10 @@ const props = defineProps<Props>()
const isPublishing = ref<boolean>(false)
const editStore = useEditStore()
const { schema, getSchemaFromRemote } = editStore
const userStore = useUserStore()
const router = useRouter()
const saveData = computed(() => {
return buildData(store.state.edit.schema, sessionStorage.getItem('sessionUUID'))
return buildData(schema, sessionStorage.getItem('sessionUUID'))
})
const validate = () => {
@ -74,12 +76,12 @@ const onSave = async () => {
//
const [isconflict, conflictName] = await checkConflict(saveData.value.surveyId)
if(isconflict) {
if (conflictName == store.state.user.userInfo.username) {
if (conflictName == userStore.userInfo.username) {
ElMessageBox.alert('当前问卷已在其它页面开启编辑,刷新以获取最新内容。', '提示', {
confirmButtonText: '确认',
callback: (action: Action) => {
if (action === 'confirm') {
store.dispatch('edit/getSchemaFromRemote')
getSchemaFromRemote()
}
}
});
@ -88,58 +90,7 @@ const onSave = async () => {
confirmButtonText: '确认',
callback: (action: Action) => {
if (action === 'confirm') {
store.dispatch('edit/getSchemaFromRemote')
}
}
});
}
return null
} else {
//
res = await saveSurvey(saveData.value)
}
return res
}
const checkConflict = async (surveyid:string) => {
try {
const dailyHis = await getConflictHistory({surveyId: surveyid, historyType: 'dailyHis', sessionId: sessionStorage.getItem('sessionUUID')})
if (dailyHis.data.length > 0) {
const lastHis = dailyHis.data.at(0)
if (Date.now() - lastHis.createDate > 2 * 60 * 1000) {
return [false, '']
}
return [true, lastHis.operator.username]
}
} catch (error) {
console.log(error)
}
return [false, '']
}
const onSave = async () => {
let res
if (!saveData.value.surveyId) {
ElMessage.error('未获取到问卷id')
return null
}
//
const [isconflict, conflictName] = await checkConflict(saveData.value.surveyId)
if(isconflict) {
if (conflictName == store.state.user.userInfo.username) {
ElMessageBox.alert('当前问卷已在其它页面开启编辑,刷新以获取最新内容。', '提示', {
confirmButtonText: '确认',
callback: (action: Action) => {
if (action === 'confirm') {
store.dispatch('edit/getSchemaFromRemote')
}
}
});
} else {
ElMessageBox.alert(`当前问卷2分钟内由${conflictName}编辑,刷新以获取最新内容。`, '提示', {
confirmButtonText: '确认',
callback: (action: Action) => {
if (action === 'confirm') {
store.dispatch('edit/getSchemaFromRemote')
getSchemaFromRemote()
}
}
});

View File

@ -200,7 +200,7 @@ const handleSave = async () => {
isSaving.value = false
}
}
const schemaUpdateTime = computed(() => _get(store.state, 'edit.schemaUpdateTime'))
watch(schemaUpdateTime, () => {
triggerAutoSave()
})

View File

@ -7,7 +7,7 @@ export default [
{
title: '提交限制',
key: 'limitConfig',
formList: ['limit_tLimit']
formList: ['limit_tLimit', 'limit_breakAnswer', 'limit_backAnswer']
},
{
title: '作答限制',

View File

@ -22,6 +22,20 @@ export default {
type: 'QuestionTimeHour',
placement: 'top'
},
limit_breakAnswer: {
key: 'baseConf.breakAnswer',
label: '允许断点续答',
tip: '回填前一次作答中的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)',
type: 'ELSwitch',
value: false
},
limit_backAnswer: {
key: 'baseConf.backAnswer',
label: '自动填充上次填写内容',
tip: '回填前一次提交的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)',
type: 'ELSwitch',
value: false
},
interview_pwd_switch: {
key: 'passwordSwitch',
label: '访问密码',
@ -96,5 +110,5 @@ export default {
relyFunc: (data) => {
return data.whitelistType == 'MEMBER'
}
}
},
}

View File

@ -143,6 +143,12 @@ const handleChange = (data) => {
if (props.moduleConfig.type === QUESTION_TYPE.VOTE) {
questionStore.updateVoteData(data)
}
//
if (props.moduleConfig.type === NORMAL_CHOICES) {
store.dispatch('changeQuota', data)
}
//
localStorageBack()
processJumpSkip()
}
@ -180,4 +186,15 @@ const processJumpSkip = () => {
questionStore.addNeedHideFields(skipKey)
}
const localStorageBack = () => {
var formData = Object.assign({}, surveyStore.formValues);
for(const key in formData){
formData[key] = encodeURIComponent(formData[key])
}
//
localStorage.removeItem(surveyStore.surveyPath + "_questionData")
localStorage.setItem(surveyStore.surveyPath + "_questionData", JSON.stringify(formData))
localStorage.setItem('isSubmit', JSON.stringify(false))
}
</script>

View File

@ -1,9 +1,10 @@
import store from '../store/index'
import { useQuestionStore } from '../stores/question'
export const useOptionsQuota = (questionKey) => {
const options = store.state.questionData[questionKey].options.map((option) => {
const questionStore = useQuestionStore()
const options = questionStore.questionData[questionKey].options.map((option) => {
if(option.quota){
const optionHash = option.hash
const selectCount = store.state.quotaMap?.[questionKey]?.[optionHash] || 0
const selectCount = questionStore.quotaMap?.[questionKey]?.[optionHash] || 0
const release = Number(option.quota) - Number(selectCount)
return {
...option,

View File

@ -92,7 +92,7 @@ const normalizationRequestBody = () => {
localStorage.removeItem(surveyPath.value + "_questionData")
localStorage.removeItem("isSubmit")
//
var formData = Object.assign({}, store.state.formValues)
var formData = Object.assign({}, surveyStore.formValues)
for(const key in formData){
formData[key] = encodeURIComponent(formData[key])
}

View File

@ -360,5 +360,5 @@ export default {
})
// 获取已投票数据
dispatch('initVoteData')
dispatch('initQuotaMap')
// dispatch('initQuotaMap')
}

View File

@ -3,11 +3,201 @@ import { defineStore } from 'pinia'
import { set } from 'lodash-es'
import { useSurveyStore } from '@/render/stores/survey'
import { queryVote } from '@/render/api/survey'
import { QUESTION_TYPE, NORMAL_CHOICES } from '@/common/typeEnum'
const VOTE_INFO_KEY = 'voteinfo'
const QUOTA_INFO_KEY = 'limitinfo'
import useCommandComponent from '../hooks/useCommandComponent'
import BackAnswerDialog from '../components/BackAnswerDialog.vue'
const confirm = useCommandComponent(BackAnswerDialog)
// 投票进度逻辑聚合
const usevVoteMap = (questionData) => {
const voteMap = ref({})
//初始化投票题的数据
const initVoteData = async () => {
const surveyStore = useSurveyStore()
const surveyPath = surveyStore.surveyPath
const fieldList = []
for (const field in questionData.value) {
const { type } = questionData.value[field]
if (type.includes(QUESTION_TYPE.VOTE)) {
fieldList.push(field)
}
}
if (fieldList.length <= 0) {
return
}
try {
localStorage.removeItem(VOTE_INFO_KEY)
const voteRes = await queryVote({
surveyPath,
fieldList: fieldList.join(',')
})
if (voteRes.code === 200) {
localStorage.setItem(
VOTE_INFO_KEY,
JSON.stringify({
...voteRes.data
})
)
setVoteMap(voteRes.data)
}
} catch (error) {
console.log(error)
}
}
const updateVoteMapByKey = (data) => {
const { questionKey, voteKey, voteValue } = data
// 兼容为空的情况
if (!voteMap.value[questionKey]) {
voteMap.value[questionKey] = {}
}
voteMap.value[questionKey][voteKey] = voteValue
}
const setVoteMap = (data) => {
voteMap.value = data
}
const updateVoteData = (data) => {
const { key: questionKey, value: questionVal } = data
// 更新前获取接口缓存在localStorage中的数据
const localData = localStorage.getItem(VOTE_INFO_KEY)
const voteinfo = JSON.parse(localData)
const currentQuestion = questionData.value[questionKey]
const options = currentQuestion.options
const voteTotal = voteinfo?.[questionKey]?.total || 0
let totalPayload = {
questionKey,
voteKey: 'total',
voteValue: voteTotal
}
options.forEach((option) => {
const optionhash = option.hash
const voteCount = voteinfo?.[questionKey]?.[optionhash] || 0
// 如果选中值包含该选项对应voteCount 和 voteTotal + 1
if (
Array.isArray(questionVal) ? questionVal.includes(optionhash) : questionVal === optionhash
) {
const countPayload = {
questionKey,
voteKey: optionhash,
voteValue: voteCount + 1
}
totalPayload.voteValue += 1
updateVoteMapByKey(countPayload)
} else {
const countPayload = {
questionKey,
voteKey: optionhash,
voteValue: voteCount
}
updateVoteMapByKey(countPayload)
}
updateVoteMapByKey(totalPayload)
})
}
return {
voteMap,
initVoteData,
updateVoteData
}
}
// 选项配额逻辑聚合
const useQuotaMap = (questionData) => {
const quotaMap = ref({})
const updateQuotaMapByKey = ({ questionKey, optionKey, data }) =>{
// 兼容为空的情况
if (!quotaMap.value[questionKey]) {
quotaMap.value[questionKey] = {}
}
quotaMap.value[questionKey][optionKey] = data
}
const initQuotaMap = async () => {
const surveyStore = useSurveyStore()
const surveyPath = surveyStore.surveyPath
const fieldList = Object.keys(questionData.value).filter(field => {
if (NORMAL_CHOICES.includes(questionData.value[field].type)) {
return questionData.value[field].options.some(option => option.quota > 0)
}
})
// 如果不存在则不请求选项上限接口
if (fieldList.length <= 0) {
return
}
try {
localStorage.removeItem(QUOTA_INFO_KEY)
const quotaRes = await queryVote({
surveyPath,
fieldList: fieldList.join(',')
})
if (quotaRes.code === 200) {
localStorage.setItem(
QUOTA_INFO_KEY,
JSON.stringify({
...quotaRes.data
})
)
Object.keys(quotaRes.data).forEach(field => {
Object.keys(quotaRes.data[field]).forEach((optionHash) => {
updateQuotaMapByKey({ questionKey: field, optionKey: optionHash, data: quotaRes.data[field][optionHash] })
})
})
}
} catch (error) {
console.log(error)
}
}
const updateQuotaData = (data) => {
const { key: questionKey, value: questionVal } = data
// 更新前获取接口缓存在localStorage中的数据
const localData = localStorage.getItem(QUOTA_INFO_KEY)
const quotaMap = JSON.parse(localData)
// const quotaMap = state.quotaMap
const currentQuestion = questionData.value[questionKey]
const options = currentQuestion.options
options.forEach((option) => {
const optionhash = option.hash
const selectCount = quotaMap?.[questionKey]?.[optionhash].selectCount || 0
// 如果选中值包含该选项,对应 voteCount 和 voteTotal + 1
if (
Array.isArray(questionVal) ? questionVal.includes(optionhash) : questionVal === optionhash
) {
const countPayload = {
questionKey,
optionKey: optionhash,
selectCount: selectCount + 1
}
updateQuotaMapByKey(countPayload)
} else {
const countPayload = {
questionKey,
optionKey: optionhash,
selectCount: selectCount
}
updateQuotaMapByKey(countPayload)
}
})
}
return {
quotaMap,
initQuotaMap,
updateQuotaMapByKey,
updateQuotaData
}
}
export const useQuestionStore = defineStore('question', () => {
const voteMap = ref({})
const questionData = ref(null)
const questionSeq = ref([]) // 题目的顺序,因为可能会有分页的情况,所以是一个二维数组[[qid1, qid2], [qid3,qid4]]
const pageIndex = ref(1) // 当前分页的索引
@ -82,6 +272,8 @@ export const useQuestionStore = defineStore('question', () => {
const setQuestionData = (data) => {
questionData.value = data
}
const { voteMap, setVoteMap, initVoteData, updateVoteData } = usevVoteMap(questionData)
const { quotaMap, initQuotaMap, updateQuotaData } = useQuotaMap(questionData)
const changeSelectMoreData = (data) => {
const { key, value, field } = data
@ -92,95 +284,6 @@ export const useQuestionStore = defineStore('question', () => {
questionSeq.value = data
}
const setVoteMap = (data) => {
voteMap.value = data
}
const updateVoteMapByKey = (data) => {
const { questionKey, voteKey, voteValue } = data
// 兼容为空的情况
if (!voteMap.value[questionKey]) {
voteMap.value[questionKey] = {}
}
voteMap.value[questionKey][voteKey] = voteValue
}
//初始化投票题的数据
const initVoteData = async () => {
const surveyStore = useSurveyStore()
const surveyPath = surveyStore.surveyPath
const fieldList = []
for (const field in questionData.value) {
const { type } = questionData.value[field]
if (/vote/.test(type)) {
fieldList.push(field)
}
}
if (fieldList.length <= 0) {
return
}
try {
localStorage.removeItem(VOTE_INFO_KEY)
const voteRes = await queryVote({
surveyPath,
fieldList: fieldList.join(',')
})
if (voteRes.code === 200) {
localStorage.setItem(
VOTE_INFO_KEY,
JSON.stringify({
...voteRes.data
})
)
setVoteMap(voteRes.data)
}
} catch (error) {
console.log(error)
}
}
const updateVoteData = (data) => {
const { key: questionKey, value: questionVal } = data
// 更新前获取接口缓存在localStorage中的数据
const localData = localStorage.getItem(VOTE_INFO_KEY)
const voteinfo = JSON.parse(localData)
const currentQuestion = questionData.value[questionKey]
const options = currentQuestion.options
const voteTotal = voteinfo?.[questionKey]?.total || 0
let totalPayload = {
questionKey,
voteKey: 'total',
voteValue: voteTotal
}
options.forEach((option) => {
const optionhash = option.hash
const voteCount = voteinfo?.[questionKey]?.[optionhash] || 0
// 如果选中值包含该选项对应voteCount 和 voteTotal + 1
if (
Array.isArray(questionVal) ? questionVal.includes(optionhash) : questionVal === optionhash
) {
const countPayload = {
questionKey,
voteKey: optionhash,
voteValue: voteCount + 1
}
totalPayload.voteValue += 1
updateVoteMapByKey(countPayload)
} else {
const countPayload = {
questionKey,
voteKey: optionhash,
voteValue: voteCount
}
updateVoteMapByKey(countPayload)
}
updateVoteMapByKey(totalPayload)
})
}
const setChangeField = (field) => {
changeField.value = field
@ -199,7 +302,7 @@ export const useQuestionStore = defineStore('question', () => {
needHideFields.value = needHideFields.value.filter(field => !fields.includes(field))
}
return {
voteMap,
questionData,
questionSeq,
renderData,
@ -209,8 +312,8 @@ export const useQuestionStore = defineStore('question', () => {
setQuestionData,
changeSelectMoreData,
setQuestionSeq,
voteMap,
setVoteMap,
updateVoteMapByKey,
initVoteData,
updateVoteData,
changeField,
@ -219,6 +322,9 @@ export const useQuestionStore = defineStore('question', () => {
needHideFields,
addNeedHideFields,
removeNeedHideFields,
getQuestionIndexByField
getQuestionIndexByField,
quotaMap,
initQuotaMap,
updateQuotaData
}
})

View File

@ -26,6 +26,8 @@ const CODE_MAP = {
ERROR: 500,
NO_AUTH: 403
}
export const useSurveyStore = defineStore('survey', () => {
const surveyPath = ref('')
const isMobile = ref(isInMobile())
@ -110,15 +112,11 @@ export const useSurveyStore = defineStore('survey', () => {
return isSuccess
}
const initSurvey = (option) => {
setEnterTime()
if (!canFillQuestionnaire(option.baseConf, option.submitConf)) {
return
}
// 加载空白页面
function clearFormData(option) {
// 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段
const {
questionData,
@ -137,6 +135,7 @@ export const useSurveyStore = defineStore('survey', () => {
'pageConf'
])
)
// todo: 建议通过questionStore提供setqueationdata方法修改属性否则不好跟踪变化
questionStore.questionData = questionData
questionStore.questionSeq = questionSeq
@ -151,8 +150,139 @@ export const useSurveyStore = defineStore('survey', () => {
formValues.value = _formValues
whiteData.value = option.whiteData
pageConf.value = option.pageConf
// 获取已投票数据
questionStore.initVoteData()
questionStore.initQuotaMap()
}
// 加载上次填写过的数据到问卷页
function loadFormData({bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }, formData) {
// 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段
const { questionData, questionSeq, rules, formValues } = adapter.generateData({
bannerConf,
baseConf,
bottomConf,
dataConf,
skinConf,
submitConf
})
for(const key in formData){
formValues[key] = formData[key]
}
// 将数据设置到state上
commit('assignState', {
questionData,
questionSeq,
rules,
bannerConf,
baseConf,
bottomConf,
dataConf,
skinConf,
submitConf,
formValues
})
// 获取已投票数据
dispatch('initVoteData')
// 获取选项上线选中数据
dispatch('initQuotaMap')
// todo: 建议通过questionStore提供setqueationdata方法修改属性否则不好跟踪变化
questionStore.questionData = questionData
questionStore.questionSeq = questionSeq
// 将数据设置到state上
rules.value = rules
bannerConf.value = option.bannerConf
baseConf.value = option.baseConf
bottomConf.value = option.bottomConf
dataConf.value = option.dataConf
skinConf.value = option.skinConf
submitConf.value = option.submitConf
formValues.value = _formValues
whiteData.value = option.whiteData
pageConf.value = option.pageConf
// 获取已投票数据
questionStore.initVoteData()
questionStore.initQuotaMap()
}
const initSurvey = (option) => {
setEnterTime()
if (!canFillQuestionnaire(option.baseConf, option.submitConf)) {
return
}
const localData = JSON.parse(localStorage.getItem(surveyPath.value + "_questionData"))
for(const key in localData){
localData[key] = decodeURIComponent(localData[key])
}
const isSubmit = JSON.parse(localStorage.getItem('isSubmit'))
if(localData) {
if(isSubmit){
if(!option.baseConf.backAnswer) {
clearFormData(option)
} else {
confirm({
title: "您之前已提交过问卷,是否要回填?",
onConfirm: async () => {
try {
loadFormData(option, localData)
} catch (error) {
console.log(error)
} finally {
confirm.close()
}
},
onCancel: async() => {
try {
clearFormData({ commit, dispatch }, { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf })
} catch (error) {
console.log(error)
} finally {
confirm.close()
}
}
})
}
} else {
if(!option.baseConf.breakAnswer) {
clearFormData(option)
} else {
confirm({
title: "您之前已填写部分内容, 是否要继续填写?",
onConfirm: async () => {
try {
loadFormData(option, localData)
} catch (error) {
console.log(error)
} finally {
confirm.close()
}
},
onCancel: async() => {
try {
clearFormData(option)
} catch (error) {
console.log(error)
} finally {
confirm.close()
}
}
})
}
}
} else {
clearFormData(option)
}
}
// 用户输入或者选择后,更新表单数据