fix: 移除有问题的功能

This commit is contained in:
sudoooooo 2024-09-14 13:50:51 +08:00
parent b749cfa6f6
commit 43b20b1be6
18 changed files with 104 additions and 551 deletions

View File

@ -1,6 +1,6 @@
import { MongoMemoryServer } from 'mongodb-memory-server'; import { MongoMemoryServer } from 'mongodb-memory-server';
import { spawn } from 'child_process'; import { spawn } from 'child_process';
import { RedisMemoryServer } from 'redis-memory-server'; // import { RedisMemoryServer } from 'redis-memory-server';
async function startServerAndRunScript() { async function startServerAndRunScript() {
// 启动 MongoDB 内存服务器 // 启动 MongoDB 内存服务器
@ -9,17 +9,17 @@ async function startServerAndRunScript() {
console.log('MongoDB Memory Server started:', mongoUri); console.log('MongoDB Memory Server started:', mongoUri);
const redisServer = new RedisMemoryServer(); // const redisServer = new RedisMemoryServer();
const redisHost = await redisServer.getHost(); // const redisHost = await redisServer.getHost();
const redisPort = await redisServer.getPort(); // const redisPort = await redisServer.getPort();
// 通过 spawn 运行另一个脚本,并传递 MongoDB 连接 URL 作为环境变量 // 通过 spawn 运行另一个脚本,并传递 MongoDB 连接 URL 作为环境变量
const tsnode = spawn( const tsnode = spawn(
'cross-env', 'cross-env',
[ [
`XIAOJU_SURVEY_MONGO_URL=${mongoUri}`, `XIAOJU_SURVEY_MONGO_URL=${mongoUri}`,
`XIAOJU_SURVEY_REDIS_HOST=${redisHost}`, // `XIAOJU_SURVEY_REDIS_HOST=${redisHost}`,
`XIAOJU_SURVEY_REDIS_PORT=${redisPort}`, // `XIAOJU_SURVEY_REDIS_PORT=${redisPort}`,
'NODE_ENV=development', 'NODE_ENV=development',
'SERVER_ENV=local', 'SERVER_ENV=local',
'npm', 'npm',
@ -42,7 +42,7 @@ async function startServerAndRunScript() {
tsnode.on('close', async (code) => { tsnode.on('close', async (code) => {
console.log(`Nodemon process exited with code ${code}`); console.log(`Nodemon process exited with code ${code}`);
await mongod.stop(); // 停止 MongoDB 内存服务器 await mongod.stop(); // 停止 MongoDB 内存服务器
await redisServer.stop(); // await redisServer.stop();
}); });
} }

View File

@ -60,7 +60,6 @@ export interface DataItem {
rangeConfig?: any; rangeConfig?: any;
starStyle?: string; starStyle?: string;
innerType?: string; innerType?: string;
quotaNoDisplay?: boolean;
} }
export interface Option { export interface Option {
@ -70,7 +69,6 @@ export interface Option {
othersKey?: string; othersKey?: string;
placeholderDesc: string; placeholderDesc: string;
hash: string; hash: string;
quota?: number;
} }
export interface DataConf { export interface DataConf {

View File

@ -48,8 +48,7 @@
"mustOthers": false, "mustOthers": false,
"othersKey": "", "othersKey": "",
"placeholderDesc": "", "placeholderDesc": "",
"hash": "115019", "hash": "115019"
"quota": "0"
}, },
{ {
"text": "选项2", "text": "选项2",
@ -58,11 +57,9 @@
"mustOthers": false, "mustOthers": false,
"othersKey": "", "othersKey": "",
"placeholderDesc": "", "placeholderDesc": "",
"hash": "115020", "hash": "115020"
"quota": "0"
} }
], ]
"quotaNoDisplay": false
} }
] ]
} }

View File

@ -10,7 +10,7 @@ import { ResponseSchemaService } from '../services/responseScheme.service';
import { SurveyResponseService } from '../services/surveyResponse.service'; import { SurveyResponseService } from '../services/surveyResponse.service';
import { ClientEncryptService } from '../services/clientEncrypt.service'; import { ClientEncryptService } from '../services/clientEncrypt.service';
import { MessagePushingTaskService } from '../../message/services/messagePushingTask.service'; import { MessagePushingTaskService } from '../../message/services/messagePushingTask.service';
import { RedisService } from 'src/modules/redis/redis.service'; // import { RedisService } from 'src/modules/redis/redis.service';
import moment from 'moment'; import moment from 'moment';
import * as Joi from 'joi'; import * as Joi from 'joi';
@ -41,7 +41,7 @@ export class SurveyResponseController {
private readonly messagePushingTaskService: MessagePushingTaskService, private readonly messagePushingTaskService: MessagePushingTaskService,
private readonly counterService: CounterService, private readonly counterService: CounterService,
private readonly logger: XiaojuSurveyLogger, private readonly logger: XiaojuSurveyLogger,
private readonly redisService: RedisService, // private readonly redisService: RedisService,
private readonly userService: UserService, private readonly userService: UserService,
private readonly workspaceMemberService: WorkspaceMemberService, private readonly workspaceMemberService: WorkspaceMemberService,
) {} ) {}
@ -224,17 +224,15 @@ export class SurveyResponseController {
const arr = cur.options.map((optionItem) => ({ const arr = cur.options.map((optionItem) => ({
hash: optionItem.hash, hash: optionItem.hash,
text: optionItem.text, text: optionItem.text,
quota: optionItem.quota,
})); }));
pre[cur.field] = arr; pre[cur.field] = arr;
return pre; return pre;
}, {}); }, {});
// 使用redis作为锁校验选项配额
const surveyId = responseSchema.pageId; const surveyId = responseSchema.pageId;
const lockKey = `locks:optionSelectedCount:${surveyId}`; // const lockKey = `locks:optionSelectedCount:${surveyId}`;
const lock = await this.redisService.lockResource(lockKey, 1000); // const lock = await this.redisService.lockResource(lockKey, 1000);
this.logger.info(`lockKey: ${lockKey}`); // this.logger.info(`lockKey: ${lockKey}`);
try { try {
const successParams = []; const successParams = [];
for (const field in decryptedData) { for (const field in decryptedData) {
@ -250,23 +248,6 @@ export class SurveyResponseController {
//遍历选项hash值 //遍历选项hash值
for (const val of values) { for (const val of values) {
const option = optionTextAndId[field].find(
(opt) => opt['hash'] === val,
);
const quota = parseInt(option['quota']);
if (
quota &&
optionCountData?.[val] &&
quota <= optionCountData[val]
) {
return {
code: EXCEPTION_CODE.RESPONSE_OVER_LIMIT,
data: {
field,
optionHash: option.hash,
},
};
}
if (!optionCountData[val]) { if (!optionCountData[val]) {
optionCountData[val] = 0; optionCountData[val] = 0;
} }
@ -292,10 +273,11 @@ export class SurveyResponseController {
} catch (error) { } catch (error) {
this.logger.error(error.message); this.logger.error(error.message);
throw error; throw error;
} finally {
await this.redisService.unlockResource(lock);
this.logger.info(`unlockResource: ${lockKey}`);
} }
// finally {
// await this.redisService.unlockResource(lock);
// this.logger.info(`unlockResource: ${lockKey}`);
// }
// 入库 // 入库
const surveyResponse = const surveyResponse =

View File

@ -1,12 +1,12 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { MessageModule } from '../message/message.module'; import { MessageModule } from '../message/message.module';
import { RedisModule } from '../redis/redis.module'; // import { RedisModule } from '../redis/redis.module';
import { ResponseSchemaService } from './services/responseScheme.service'; import { ResponseSchemaService } from './services/responseScheme.service';
import { SurveyResponseService } from './services/surveyResponse.service'; import { SurveyResponseService } from './services/surveyResponse.service';
import { CounterService } from './services/counter.service'; import { CounterService } from './services/counter.service';
import { ClientEncryptService } from './services/clientEncrypt.service'; import { ClientEncryptService } from './services/clientEncrypt.service';
import { RedisService } from '../redis/redis.service'; // import { RedisService } from '../redis/redis.service';
import { ResponseSchema } from 'src/models/responseSchema.entity'; import { ResponseSchema } from 'src/models/responseSchema.entity';
import { Counter } from 'src/models/counter.entity'; import { Counter } from 'src/models/counter.entity';
@ -35,7 +35,7 @@ import { ConfigModule } from '@nestjs/config';
]), ]),
ConfigModule, ConfigModule,
MessageModule, MessageModule,
RedisModule, // RedisModule,
AuthModule, AuthModule,
WorkspaceModule, WorkspaceModule,
], ],
@ -52,7 +52,7 @@ import { ConfigModule } from '@nestjs/config';
CounterService, CounterService,
ClientEncryptService, ClientEncryptService,
LoggerProvider, LoggerProvider,
RedisService, // RedisService,
], ],
exports: [ exports: [
ResponseSchemaService, ResponseSchemaService,

View File

@ -11,7 +11,7 @@
"build-only": "vite build", "build-only": "vite build",
"type-check": "vue-tsc --build --force", "type-check": "vue-tsc --build --force",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/materials/setters/widgets/QuotaConfig.vue" "format": "prettier --write src/"
}, },
"dependencies": { "dependencies": {
"@logicflow/core": "2.0.0", "@logicflow/core": "2.0.0",

View File

@ -41,15 +41,13 @@ export const defaultQuestionConfig = {
text: '选项1', text: '选项1',
others: false, others: false,
othersKey: '', othersKey: '',
placeholderDesc: '', placeholderDesc: ''
quota: '0'
}, },
{ {
text: '选项2', text: '选项2',
others: false, others: false,
othersKey: '', othersKey: '',
placeholderDesc: '', placeholderDesc: ''
quota: '0'
} }
], ],
star: 5, star: 5,
@ -76,6 +74,5 @@ export const defaultQuestionConfig = {
placeholder: '500', placeholder: '500',
value: 500 value: 500
} }
}, }
quotaNoDisplay: false
} }

View File

@ -47,10 +47,6 @@ export default defineComponent({
voteTotal: { voteTotal: {
type: Number, type: Number,
default: 10 default: 10
},
quotaNoDisplay:{
type: Boolean,
default: true
} }
}, },
emits: ['change'], emits: ['change'],
@ -148,20 +144,6 @@ export default defineComponent({
style="display: block; height: auto; padding-top: 9px" style="display: block; height: auto; padding-top: 9px"
></span> ></span>
)} )}
{
//
!this.readonly && (item.quota && item.quota !== "0") && !this.quotaNoDisplay && (
<span
class="remaining-text"
style={{
display: 'block',
fontSize: 'smaller',
color: item.release === 0 ? '#EB505C' : '#92949D'
}}
>
剩余{item.release}
</span>
)}
{slots.vote?.({ {slots.vote?.({
option: item, option: item,
voteTotal: this.voteTotal voteTotal: this.voteTotal

View File

@ -41,15 +41,10 @@ export default defineComponent({
maxNum: { maxNum: {
type: [Number, String], type: [Number, String],
default: 1 default: 1
},
quotaNoDisplay:{
type: Boolean,
default: false
} }
}, },
emits: ['change'], emits: ['change'],
setup(props, { emit }) { setup(props, { emit }) {
const disableState = computed(() => { const disableState = computed(() => {
if (!props.maxNum) { if (!props.maxNum) {
return false return false
@ -65,24 +60,10 @@ export default defineComponent({
return options.map((item) => { return options.map((item) => {
return { return {
...item, ...item,
disabled: (item.release === 0) || isDisabled(item) disabled: isDisabled(item)
} }
}) })
}) })
// 0
watch(() => props.value, (value) => {
const disabledHash = myOptions.value.filter(i => i.disabled).map(i => i.hash)
if (value && disabledHash.length) {
disabledHash.forEach(hash => {
const index = value.indexOf(hash)
if( index> -1) {
const newValue = [...value]
newValue.splice(index, 1)
onChange(newValue)
}
})
}
})
const onChange = (value) => { const onChange = (value) => {
const key = props.field const key = props.field
emit('change', { emit('change', {
@ -111,13 +92,12 @@ export default defineComponent({
return { return {
onChange, onChange,
handleSelectMoreChange, handleSelectMoreChange,
disableState,
myOptions, myOptions,
selectMoreView selectMoreView
} }
}, },
render() { render() {
const { readonly, field, myOptions, onChange, maxNum, value, quotaNoDisplay, selectMoreView } = this const { readonly, field, myOptions, onChange, maxNum, value, selectMoreView } = this
return ( return (
<BaseChoice <BaseChoice
uiTarget="checkbox" uiTarget="checkbox"
@ -128,7 +108,6 @@ export default defineComponent({
onChange={onChange} onChange={onChange}
value={value} value={value}
layout={this.layout} layout={this.layout}
quotaNoDisplay={quotaNoDisplay}
> >
{{ {{
selectMore: (scoped) => { selectMore: (scoped) => {

View File

@ -110,7 +110,7 @@ const meta = {
{ {
label: '横排', label: '横排',
value: 'horizontal' value: 'horizontal'
}, }
] ]
}, },
{ {
@ -119,7 +119,9 @@ const meta = {
key: 'minNum', key: 'minNum',
value: 0, value: 0,
min: 0, min: 0,
max: moduleConfig => { return moduleConfig?.maxNum || 0 }, max: (moduleConfig) => {
return moduleConfig?.maxNum || 0
},
contentClass: 'input-number-config' contentClass: 'input-number-config'
}, },
{ {
@ -127,30 +129,15 @@ const meta = {
type: 'InputNumber', type: 'InputNumber',
key: 'maxNum', key: 'maxNum',
value: 0, value: 0,
min: moduleConfig => { return moduleConfig?.minNum || 0 }, min: (moduleConfig) => {
max: moduleConfig => { return moduleConfig?.options?.length }, return moduleConfig?.minNum || 0
},
max: (moduleConfig) => {
return moduleConfig?.options?.length
},
contentClass: 'input-number-config' contentClass: 'input-number-config'
}, }
] ]
},
{
name: 'optionQuota',
label: '选项配额',
labelStyle: {
'font-weight': 'bold'
},
type: 'QuotaConfig',
// 输出转换
valueSetter({ options, quotaNoDisplay}) {
return [{
key: 'options',
value: options
},
{
key: 'quotaNoDisplay',
value: quotaNoDisplay
}]
}
} }
], ],
editConfigure: { editConfigure: {

View File

@ -31,28 +31,10 @@ export default defineComponent({
readonly: { readonly: {
type: Boolean, type: Boolean,
default: false default: false
},
quotaNoDisplay:{
type: Boolean,
default: false
} }
}, },
emits: ['change'], emits: ['change'],
setup(props, { emit }) { setup(props, { emit }) {
// 0
watch(() => props.value, (value) => {
const disabledHash = props.options.filter(i => i.disabled).map(i => i.hash)
if (value && disabledHash.length) {
disabledHash.forEach(hash => {
const index = value.indexOf(hash)
if( index> -1) {
const newValue = [...value]
newValue.splice(index, 1)
onChange(newValue)
}
})
}
})
const onChange = (value) => { const onChange = (value) => {
const key = props.field const key = props.field
emit('change', { emit('change', {
@ -99,7 +81,6 @@ export default defineComponent({
field={this.field} field={this.field}
layout={this.layout} layout={this.layout}
onChange={this.onChange} onChange={this.onChange}
quotaNoDisplay={this.quotaNoDisplay}
> >
{{ {{
selectMore: (scoped) => { selectMore: (scoped) => {

View File

@ -53,22 +53,22 @@ const meta = {
description: '这是用于描述选项', description: '这是用于描述选项',
defaultValue: [ defaultValue: [
{ {
"text": "选项1", text: '选项1',
"imageUrl": "", imageUrl: '',
"others": false, others: false,
"mustOthers": false, mustOthers: false,
"othersKey": "", othersKey: '',
"placeholderDesc": "", placeholderDesc: '',
"hash": "115019" hash: '115019'
}, },
{ {
"text": "选项2", text: '选项2',
"imageUrl": "", imageUrl: '',
"others": false, others: false,
"mustOthers": false, mustOthers: false,
"othersKey": "", othersKey: '',
"placeholderDesc": "", placeholderDesc: '',
"hash": "115020" hash: '115020'
} }
] ]
}, },
@ -77,55 +77,34 @@ const meta = {
propType: String, propType: String,
description: '排列方式', description: '排列方式',
defaultValue: 'vertical' defaultValue: 'vertical'
},
{
name: 'quotaNoDisplay',
propType: Boolean,
description: '不展示配额剩余数量',
defaultValue: false
} }
], ],
formConfig: [basicConfig, { formConfig: [
name: 'optionConfig', basicConfig,
title: '选项配置', {
type: 'Customed', name: 'optionConfig',
content: [ title: '选项配置',
{ type: 'Customed',
label: '排列方式', content: [
type: 'RadioGroup', {
key: 'layout', label: '排列方式',
value: 'vertical', type: 'RadioGroup',
options: [ key: 'layout',
{ value: 'vertical',
label: '竖排', options: [
value: 'vertical' {
}, label: '竖排',
{ value: 'vertical'
label: '横排', },
value: 'horizontal' {
}, label: '横排',
] value: 'horizontal'
}, }
] ]
},{ }
name: 'optionQuota', ]
label: '选项配额',
labelStyle: {
'font-weight': 'bold'
},
type: 'QuotaConfig',
// 输出转换
valueSetter({ options, quotaNoDisplay}) {
return [{
key: 'options',
value: options
},
{
key: 'quotaNoDisplay',
value: quotaNoDisplay
}]
} }
}], ],
editConfigure: { editConfigure: {
optionEdit: { optionEdit: {
show: true show: true

View File

@ -1,180 +0,0 @@
<template>
<div class="quota-wrapper">
<span class="quota-config" @click="openQuotaConfig"> 设置> </span>
<el-dialog v-model="dialogVisible" @closed="cleanTempQuota" class="dialog">
<template #header>
<div class="dialog-title">选项配额</div>
</template>
<el-table
:header-cell-style="{ background: '#F6F7F9', color: '#6E707C' }"
:data="optionData"
border
style="width: 100%"
@cell-click="handleCellClick"
>
<el-table-column property="text" label="选项" style="width: 50%">
<template v-slot="scope">
<div v-html="cleanRichTextWithMediaTag(scope.row.text)"></div>
</template>
</el-table-column>
<el-table-column property="quota" style="width: 50%">
<template #header>
<div style="display: flex; align-items: center">
<span>配额设置</span>
<el-tooltip
class="tooltip"
effect="dark"
placement="right"
content="类似商品库存表示最多可以被选择多少次0为无限制已发布问卷上限修改时数量不可减小。"
>
<i-ep-questionFilled class="icon-tip" />
</el-tooltip>
</div>
</template>
<template v-slot="scope">
<el-input
v-if="scope.row.isEditing"
:id="`${scope.row.hash}editInput`"
v-model="scope.row.tempQuota"
type="number"
@blur="handleInput(scope.row)"
placeholder="请输入"
>
</el-input>
<div v-else class="item__txt">
<span v-if="scope.row.tempQuota !== '0'">{{ scope.row.tempQuota }}</span>
<span v-else style="color: #c8c9cd">请输入</span>
</div>
</template>
</el-table-column>
</el-table>
<div class="quota-no-display">
<el-checkbox v-model="quotaNoDisplayValue" label="不展示配额剩余数量"> </el-checkbox>
<el-tooltip
class="tooltip"
effect="dark"
placement="right"
content="勾选后,将不对用户展示剩余配额数量。"
>
<i-ep-questionFilled class="icon-tip" />
</el-tooltip>
</div>
<template #footer>
<div class="diaglog-footer">
<el-button @click="cancel">取消</el-button>
<el-button @click="confirm" type="primary">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, watch, nextTick } from 'vue'
import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant'
import { ElMessageBox } from 'element-plus'
import { cleanRichTextWithMediaTag } from '@/common/xss'
const props = defineProps(['formConfig', 'moduleConfig'])
const emit = defineEmits(['form-change'])
const dialogVisible = ref(false)
const moduleConfig = ref(props.moduleConfig)
const optionData = ref(props.moduleConfig.options)
const quotaNoDisplayValue = ref(moduleConfig.value.quotaNoDisplay)
const openQuotaConfig = () => {
optionData.value.forEach((item) => {
item.tempQuota = item.quota
})
dialogVisible.value = true
}
const cancel = () => {
dialogVisible.value = false
}
const confirm = () => {
dialogVisible.value = false
//
handleQuotaChange()
emit(FORM_CHANGE_EVENT_KEY, {
options: optionData.value,
quotaNoDisplay: quotaNoDisplayValue.value
})
}
const handleCellClick = (row, column) => {
if (column.property === 'quota') {
optionData.value.forEach((r) => {
if (r !== row) r.isEditing = false
})
row.tempQuota = row.tempQuota === '0' ? row.quota : row.tempQuota
row.isEditing = true
nextTick(() => {
const input = document.getElementById(`${row.hash}editInput`)
input.focus()
})
}
}
const handleInput = (row) => {
if (row.tempQuota !== '0' && +row.tempQuota < +row.quota) {
ElMessageBox.alert('配额数不可减少!', '警告', {
confirmButtonText: '确定'
})
row.tempQuota = row.quota
}
row.isEditing = false
}
const handleQuotaChange = () => {
optionData.value.forEach((item) => {
item.quota = item.tempQuota
delete item.tempQuota
})
}
const cleanTempQuota = () => {
optionData.value.forEach((item) => {
delete item.tempQuota
})
}
watch(
() => props.moduleConfig,
(val) => {
moduleConfig.value = val
optionData.value = val.options
quotaNoDisplayValue.value = val.quotaNoDisplay
},
{ immediate: true, deep: true }
)
</script>
<style lang="scss" scoped>
.quota-wrapper {
width: 90%;
display: flex;
justify-content: flex-end;
:deep(.cell) {
line-height: 35px;
}
.quota-no-display {
padding-top: 8px;
}
}
.quota-title {
font-size: 14px;
color: #606266;
margin-bottom: 20px;
font-weight: bold;
align-items: center;
}
.quota-config {
color: #ffa600;
cursor: pointer;
font-size: 14px;
}
.dialog {
width: 41vw;
.dialog-title {
color: #292a36;
font-size: 20px;
}
}
</style>

View File

@ -15,7 +15,6 @@ import QuestionRuleContainer from '../../materials/questions/QuestionRuleContain
import { useVoteMap } from '@/render/hooks/useVoteMap' import { useVoteMap } from '@/render/hooks/useVoteMap'
import { useShowOthers } from '@/render/hooks/useShowOthers' import { useShowOthers } from '@/render/hooks/useShowOthers'
import { useShowInput } from '@/render/hooks/useShowInput' import { useShowInput } from '@/render/hooks/useShowInput'
import { useOptionsQuota } from '@/render/hooks/useOptionsQuota'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { useQuestionStore } from '../stores/question' import { useQuestionStore } from '../stores/question'
import { useSurveyStore } from '../stores/survey' import { useSurveyStore } from '../stores/survey'
@ -57,17 +56,7 @@ const questionConfig = computed(() => {
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) && if (NORMAL_CHOICES.includes(type) && options.some((option) => option.others)) {
options.some(option => option.quota > 0)) {
//
let { options: optionWithQuota } = useOptionsQuota(field)
alloptions = alloptions.map((obj, index) => Object.assign(obj, optionWithQuota[index]))
}
if (
NORMAL_CHOICES.includes(type) &&
options.some(option => option.others)
) {
// //
let { options, othersValue } = useShowOthers(field) let { options, othersValue } = useShowOthers(field)
const othersOptions = unref(options) const othersOptions = unref(options)
@ -137,10 +126,6 @@ const handleChange = (data) => {
if (props.moduleConfig.type === QUESTION_TYPE.VOTE) { if (props.moduleConfig.type === QUESTION_TYPE.VOTE) {
questionStore.updateVoteData(data) questionStore.updateVoteData(data)
} }
//
if (props.moduleConfig.type === NORMAL_CHOICES) {
questionStore.updateQuotaData(data)
}
// //
localStorageBack() localStorageBack()
processJumpSkip() processJumpSkip()
@ -191,11 +176,11 @@ const processJumpSkip = () => {
questionStore.addNeedHideFields(skipKey) questionStore.addNeedHideFields(skipKey)
} }
const localStorageBack = () => { const localStorageBack = () => {
var formData = Object.assign({}, surveyStore.formValues); var formData = Object.assign({}, surveyStore.formValues)
// //
localStorage.removeItem(surveyStore.surveyPath + "_questionData") localStorage.removeItem(surveyStore.surveyPath + '_questionData')
localStorage.setItem(surveyStore.surveyPath + "_questionData", JSON.stringify(formData)) localStorage.setItem(surveyStore.surveyPath + '_questionData', JSON.stringify(formData))
localStorage.setItem('isSubmit', JSON.stringify(false)) localStorage.setItem('isSubmit', JSON.stringify(false))
} }
</script> </script>

View File

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

View File

@ -72,7 +72,6 @@ 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 route = useRoute()
onMounted(() => { onMounted(() => {
const surveyId = route.params.surveyId const surveyId = route.params.surveyId
@ -153,12 +152,12 @@ const normalizationRequestBody = () => {
} }
// //
localStorage.removeItem(surveyPath.value + "_questionData") localStorage.removeItem(surveyPath.value + '_questionData')
localStorage.removeItem("isSubmit") localStorage.removeItem('isSubmit')
// //
var formData : Record<string, any> = Object.assign({}, surveyStore.formValues) var formData: Record<string, any> = Object.assign({}, surveyStore.formValues)
localStorage.setItem(surveyPath.value + "_questionData", JSON.stringify(formData)) localStorage.setItem(surveyPath.value + '_questionData', JSON.stringify(formData))
localStorage.setItem('isSubmit', JSON.stringify(true)) localStorage.setItem('isSubmit', JSON.stringify(true))
if (encryptInfo?.encryptType) { if (encryptInfo?.encryptType) {
@ -187,15 +186,6 @@ const submitSurver = async () => {
const res: any = await submitForm(params) const res: any = await submitForm(params)
if (res.code === 200) { if (res.code === 200) {
router.replace({ name: 'successPage' }) router.replace({ name: 'successPage' })
} else if(res.code === 9003) {
//
questionStore.initQuotaMap()
const titile = useQuestionInfo(res.data.field).questionTitle
const optionText = useQuestionInfo(res.data.field).getOptionTitle(res.data.optionHash)
const message = `${titile}】的【${optionText}】配额已满,请重新选择`
alert({
title: message
})
} else { } else {
alert({ alert({
title: res.errmsg || '提交失败' title: res.errmsg || '提交失败'

View File

@ -6,13 +6,12 @@ import { queryVote } from '@/render/api/survey'
import { QUESTION_TYPE, NORMAL_CHOICES } from '@/common/typeEnum' import { QUESTION_TYPE, NORMAL_CHOICES } from '@/common/typeEnum'
const VOTE_INFO_KEY = 'voteinfo' const VOTE_INFO_KEY = 'voteinfo'
const QUOTA_INFO_KEY = 'limitinfo'
// 投票进度逻辑聚合 // 投票进度逻辑聚合
const usevVoteMap = (questionData) => { const usevVoteMap = (questionData) => {
const voteMap = ref({}) const voteMap = ref({})
//初始化投票题的数据 //初始化投票题的数据
const initVoteData = async () => { const initVoteData = async () => {
const surveyStore = useSurveyStore() const surveyStore = useSurveyStore()
const surveyPath = surveyStore.surveyPath const surveyPath = surveyStore.surveyPath
@ -104,93 +103,6 @@ const usevVoteMap = (questionData) => {
} }
} }
// 选项配额逻辑聚合
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', () => { export const useQuestionStore = defineStore('question', () => {
const questionData = ref(null) const questionData = ref(null)
const questionSeq = ref([]) // 题目的顺序,因为可能会有分页的情况,所以是一个二维数组[[qid1, qid2], [qid3,qid4]] const questionSeq = ref([]) // 题目的顺序,因为可能会有分页的情况,所以是一个二维数组[[qid1, qid2], [qid3,qid4]]
@ -267,7 +179,6 @@ export const useQuestionStore = defineStore('question', () => {
questionData.value = data questionData.value = data
} }
const { voteMap, setVoteMap, initVoteData, updateVoteData } = usevVoteMap(questionData) const { voteMap, setVoteMap, initVoteData, updateVoteData } = usevVoteMap(questionData)
const { quotaMap, initQuotaMap, updateQuotaData } = useQuotaMap(questionData)
const changeSelectMoreData = (data) => { const changeSelectMoreData = (data) => {
const { key, value, field } = data const { key, value, field } = data
@ -278,7 +189,6 @@ export const useQuestionStore = defineStore('question', () => {
questionSeq.value = data questionSeq.value = data
} }
const setChangeField = (field) => { const setChangeField = (field) => {
changeField.value = field changeField.value = field
} }
@ -296,7 +206,6 @@ export const useQuestionStore = defineStore('question', () => {
needHideFields.value = needHideFields.value.filter((field) => !fields.includes(field)) needHideFields.value = needHideFields.value.filter((field) => !fields.includes(field))
} }
return { return {
questionData, questionData,
questionSeq, questionSeq,
renderData, renderData,
@ -316,9 +225,6 @@ export const useQuestionStore = defineStore('question', () => {
needHideFields, needHideFields,
addNeedHideFields, addNeedHideFields,
removeNeedHideFields, removeNeedHideFields,
getQuestionIndexByField, getQuestionIndexByField
quotaMap,
initQuotaMap,
updateQuotaData
} }
}) })

View File

@ -13,7 +13,6 @@ import moment from 'moment'
import 'moment/locale/zh-cn' import 'moment/locale/zh-cn'
// 设置中文 // 设置中文
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 useCommandComponent from '../hooks/useCommandComponent'
@ -31,7 +30,6 @@ const CODE_MAP = {
NO_AUTH: 403 NO_AUTH: 403
} }
export const useSurveyStore = defineStore('survey', () => { export const useSurveyStore = defineStore('survey', () => {
const surveyPath = ref('') const surveyPath = ref('')
const isMobile = ref(isInMobile()) const isMobile = ref(isInMobile())
@ -116,8 +114,6 @@ export const useSurveyStore = defineStore('survey', () => {
return isSuccess return isSuccess
} }
// 加载空白页面 // 加载空白页面
function clearFormData(option) { function clearFormData(option) {
// 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段 // 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段
@ -156,18 +152,15 @@ export const useSurveyStore = defineStore('survey', () => {
// 获取已投票数据 // 获取已投票数据
questionStore.initVoteData() questionStore.initVoteData()
questionStore.initQuotaMap()
} }
function fillFormData(formData) { function fillFormData(formData) {
const _formValues = cloneDeep(formValues.value) const _formValues = cloneDeep(formValues.value)
for(const key in formData){ for (const key in formData) {
_formValues[key] = formData[key] _formValues[key] = formData[key]
} }
formValues.value = _formValues formValues.value = _formValues
} }
const initSurvey = (option) => { const initSurvey = (option) => {
setEnterTime() setEnterTime()
if (!canFillQuestionnaire(option.baseConf, option.submitConf)) { if (!canFillQuestionnaire(option.baseConf, option.submitConf)) {
return return
@ -176,15 +169,15 @@ export const useSurveyStore = defineStore('survey', () => {
clearFormData(option) clearFormData(option)
const { breakAnswer, backAnswer } = option.baseConf const { breakAnswer, backAnswer } = option.baseConf
const localData = JSON.parse(localStorage.getItem(surveyPath.value + "_questionData")) const localData = JSON.parse(localStorage.getItem(surveyPath.value + '_questionData'))
const isSubmit = JSON.parse(localStorage.getItem('isSubmit')) const isSubmit = JSON.parse(localStorage.getItem('isSubmit'))
if(localData) { if (localData) {
// 断点续答 // 断点续答
if(breakAnswer) { if (breakAnswer) {
confirm({ confirm({
title: "是否继续上次填写的内容?", title: '是否继续上次填写的内容?',
onConfirm: async () => { onConfirm: async () => {
try { try {
// 回填答题内容 // 回填答题内容
@ -195,14 +188,14 @@ export const useSurveyStore = defineStore('survey', () => {
confirm.close() confirm.close()
} }
}, },
onCancel: async() => { onCancel: async () => {
confirm.close() confirm.close()
} }
}) })
} else if (backAnswer) { } else if (backAnswer) {
if(isSubmit){ if (isSubmit) {
confirm({ confirm({
title: "是否继续上次提交的内容?", title: '是否继续上次提交的内容?',
onConfirm: async () => { onConfirm: async () => {
try { try {
// 回填答题内容 // 回填答题内容
@ -213,7 +206,7 @@ export const useSurveyStore = defineStore('survey', () => {
confirm.close() confirm.close()
} }
}, },
onCancel: async() => { onCancel: async () => {
confirm.close() confirm.close()
} }
}) })