From 6ed8a24745c8320a6a98cf8dd8e748782a25f65e Mon Sep 17 00:00:00 2001 From: dev_ljl <2590379346@qq.com> Date: Mon, 18 Aug 2025 17:22:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E9=A2=84=E8=A7=88=E9=97=AE=E5=8D=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../questionnaire/question/model.d.ts | 27 +- .../questionnaire/questionnaire/model.d.ts | 10 +- .../customerService/questionnaire/data.ts | 178 ++++------ .../customerService/questionnaire/index.vue | 192 ++++------ .../questionnaire/questionnaire-detail.vue | 206 +++++++---- .../questionnaire/questionnaire-modal.vue | 333 ++++++++++-------- 6 files changed, 504 insertions(+), 442 deletions(-) diff --git a/apps/web-antd/src/api/property/customerService/questionnaire/question/model.d.ts b/apps/web-antd/src/api/property/customerService/questionnaire/question/model.d.ts index 11d169dd..3fc2d1b4 100644 --- a/apps/web-antd/src/api/property/customerService/questionnaire/question/model.d.ts +++ b/apps/web-antd/src/api/property/customerService/questionnaire/question/model.d.ts @@ -1,6 +1,6 @@ -import type { PageQuery, BaseEntity } from '#/api/common'; +import type {PageQuery, BaseEntity} from '#/api/common'; import type { - QuestionItemForm + QuestionItemForm, QuestionItemVO } from "#/api/property/customerService/questionnaire/questionItem/model"; export interface QuestionVO { @@ -39,6 +39,19 @@ export interface QuestionVO { */ sort: number; + /** + * 问题选项 + */ + questionnaireQuestionItemVos: QuestionItemVO[]; + + options: string[]; + + answer: string; + + rate:number; + + checked:string[]; + } export interface QuestionForm extends BaseEntity { @@ -80,17 +93,17 @@ export interface QuestionForm extends BaseEntity { /** * 选项 */ - questionnaireQuestionItems:QuestionItemForm[] + questionnaireQuestionItems: QuestionItemForm[] /** * 评分预览 */ - rate?:number; + rate?: number; /** * 时间预览 */ - dateTime?:string; + dateTime?: string; } @@ -126,7 +139,7 @@ export interface QuestionQuery extends PageQuery { sort?: number; /** - * 日期范围参数 - */ + * 日期范围参数 + */ params?: any; } diff --git a/apps/web-antd/src/api/property/customerService/questionnaire/questionnaire/model.d.ts b/apps/web-antd/src/api/property/customerService/questionnaire/questionnaire/model.d.ts index 97390bf9..73a564e0 100644 --- a/apps/web-antd/src/api/property/customerService/questionnaire/questionnaire/model.d.ts +++ b/apps/web-antd/src/api/property/customerService/questionnaire/questionnaire/model.d.ts @@ -1,5 +1,8 @@ import type { PageQuery, BaseEntity } from '#/api/common'; -import type {QuestionForm} from "#/api/property/customerService/questionnaire/question/model"; +import type { + QuestionForm, + QuestionVO +} from "#/api/property/customerService/questionnaire/question/model"; export interface QuestionnaireVO { /** @@ -36,7 +39,10 @@ export interface QuestionnaireVO { * 状态(1草稿2已发布3未发布) */ status: string; - + /** + * 问题 + */ + questionnaireQuestionVos:QuestionVO[]; } export interface QuestionnaireForm extends BaseEntity { diff --git a/apps/web-antd/src/views/property/customerService/questionnaire/data.ts b/apps/web-antd/src/views/property/customerService/questionnaire/data.ts index 933e55ec..2e77cf25 100644 --- a/apps/web-antd/src/views/property/customerService/questionnaire/data.ts +++ b/apps/web-antd/src/views/property/customerService/questionnaire/data.ts @@ -1,104 +1,84 @@ import type { FormSchemaGetter } from '#/adapter/form'; import type { VxeGridProps } from '#/adapter/vxe-table'; -import { getDictOptions } from '#/utils/dict'; -import { renderDict } from '#/utils/render'; -import {h} from "vue"; -import {Rate} from "ant-design-vue"; +import {renderDict} from "#/utils/render"; +import {getDictOptions} from "#/utils/dict"; + export const querySchema: FormSchemaGetter = () => [ { - component: 'Select', - componentProps: { - options: getDictOptions('type_contingency_plan'), - }, - fieldName: 'contingenPlanType', - label: '预案类型', - }, - { - component: 'ApiSelect', - fieldName: 'dutyPersion', - label: '责任人', + component: 'Input', + fieldName: 'head', + label: '问卷标题', }, + { component: 'Select', componentProps: { - options: getDictOptions('pro_exercise_status'), + options:getDictOptions('wy_dcwjzt') }, fieldName: 'status', - label: '演练状态', + label: '问卷状态',//(1草稿2已发布3未发布) }, ]; +// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新 +// export const columns: () => VxeGridProps['columns'] = () => [ export const columns: VxeGridProps['columns'] = [ { type: 'checkbox', width: 60 }, { - title: '序号', - field: 'id', - slots: { - default: ({rowIndex}) => { - return (rowIndex + 1).toString(); - } - } + title: '问卷标题', + field: 'head', + width:200, }, { - title: '预案名称', - field: 'contingenPlanName', + title: '问卷描述', + field: 'depict', + minWidth:180, }, { - title: '预案类型', - field: 'contingenPlanType', - slots: { - default: ({ row }) => { - return renderDict(row.contingenPlanType, 'type_contingency_plan'); - }, - }, + title: '匿名收集', + field: 'isAnonyCollec', + slots:{ + default: ({row})=>{ + return row.isAnonyCollec=='1'?'是':'否' + }}, + width:100, }, { - title: '风险等级', - field: 'grade', - slots: { - default: ({ row }) => { - return h(Rate, { - value: row.grade || 0, - disabled: true, - }); - }, - }, - minWidth: '150' + title: '多次提交', + field: 'isCommit', + slots:{ + default: ({row})=>{ + return row.isCommit=='1'?'是':'否' + }}, + width:100, }, { - title: '发起人', - field: 'initiatName', + title: '截止日期', + field: 'deadline', + width:150, }, { - title: '演练状态', + title: '问卷状态',//(1草稿2已发布3未发布) field: 'status', - slots: { - default: ({ row }) => { - return renderDict(row.status, 'pro_exercise_status'); - }, - }, - }, - { - title: '责任人', - field: 'dutyPersionName', - }, - { - title: '完成时间', - field: 'compleTimes', + slots:{ + default: ({row})=>{ + return renderDict(row.status,'wy_dcwjzt') + }}, + width:100, }, { field: 'action', fixed: 'right', slots: { default: 'action' }, title: '操作', - width: 240, + width: 280, }, ]; export const modalSchema: FormSchemaGetter = () => [ { - label: '主键', + label: 'id', fieldName: 'id', component: 'Input', dependencies: { @@ -107,52 +87,46 @@ export const modalSchema: FormSchemaGetter = () => [ }, }, { - label: '预案名称', - fieldName: 'contingenPlanName', + label: '问卷标题', + fieldName: 'head', + component: 'Textarea', + rules: 'required', + }, + { + label: '问卷描述', + fieldName: 'depict', + component: 'Textarea', + }, + { + label: '是否匿名收集', + fieldName: 'isAnonyCollec', component: 'Input', rules: 'required', }, { - label: '预案类型', - fieldName: 'contingenPlanType', - component: 'Select', + label: '是否多次提交', + fieldName: 'isCommit', + component: 'Input', + rules: 'required', + }, + { + label: '截止日期', + fieldName: 'deadline', + component: 'DatePicker', componentProps: { - options: getDictOptions('type_contingency_plan'), - }, - rules: 'selectRequired', - }, - { - label: '发起人', - fieldName: 'initiat', - component: 'ApiSelect', - rules: 'selectRequired', - }, - { - label: '责任人', - fieldName: 'dutyPersion', - component: 'ApiSelect', - rules: 'selectRequired', - }, - { - label: '预案内容', - fieldName: 'contingenPlanContent', - component: 'RichTextarea', - componentProps: { - // disabled: false, // 是否只读 - // height: 400 // 高度 默认400 - }, - formItemClass: 'col-span-2' - }, - { - label: '风险等级', - fieldName: 'grade', - component: 'Rate', - componentProps: { - allowHalf: false, - count: 5, - tooltips: ['1星', '2星', '3星', '4星', '5星'], - defaultValue: 0 + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'YYYY-MM-DD HH:mm:ss', }, rules: 'required', }, + { + label: '状态(1草稿2已发布3未发布)', + fieldName: 'status', + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + optionType: 'button', + }, + }, ]; diff --git a/apps/web-antd/src/views/property/customerService/questionnaire/index.vue b/apps/web-antd/src/views/property/customerService/questionnaire/index.vue index 149a5bd4..14ff6195 100644 --- a/apps/web-antd/src/views/property/customerService/questionnaire/index.vue +++ b/apps/web-antd/src/views/property/customerService/questionnaire/index.vue @@ -1,28 +1,26 @@ - + - - {{ $t('pages.common.export') }} - - - {{ $t('pages.common.delete') }} - + + + + + + + + + + + + + + {{ $t('pages.common.add') }} @@ -183,28 +146,16 @@ onMounted(async () => { - - - {{ '审核' }} - - - {{ $t('pages.common.info') }} + 预览问卷 {{ $t('pages.common.edit') }} @@ -216,16 +167,23 @@ onMounted(async () => { > {{ $t('pages.common.delete') }} + + 统计分析 + - - + + diff --git a/apps/web-antd/src/views/property/customerService/questionnaire/questionnaire-detail.vue b/apps/web-antd/src/views/property/customerService/questionnaire/questionnaire-detail.vue index bf8fb9a2..ee62df03 100644 --- a/apps/web-antd/src/views/property/customerService/questionnaire/questionnaire-detail.vue +++ b/apps/web-antd/src/views/property/customerService/questionnaire/questionnaire-detail.vue @@ -1,96 +1,158 @@ - - - - {{ contingenPlanIDetail.contingenPlanName }} - - - {{ contingenPlanIDetail.createTime }} - - - - - - {{ contingenPlanIDetail.updateTime }} - - - - - - {{ contingenPlanIDetail.compleTimes }} - - - {{ contingenPlanIDetail.initiatName }} - - - {{ contingenPlanIDetail.dutyPersionName}} - - - - - - - - - - - 处理记录 - - - - 类型: - - 时间:{{item.createTime}} - 处理人:{{item.handlerName}} - - + + + {{ questionnaireDetail.head }} + {{ questionnaireDetail.depict }} + + + {{ index + 1 }} + {{ item.head }} + + + {{ item.depict }} + + + + * + + + + + + + + + + + {{ option.itemContent }} + + + + + + + + + 请评分 + + + + + + + + + diff --git a/apps/web-antd/src/views/property/customerService/questionnaire/questionnaire-modal.vue b/apps/web-antd/src/views/property/customerService/questionnaire/questionnaire-modal.vue index f49a0444..2af25332 100644 --- a/apps/web-antd/src/views/property/customerService/questionnaire/questionnaire-modal.vue +++ b/apps/web-antd/src/views/property/customerService/questionnaire/questionnaire-modal.vue @@ -14,11 +14,24 @@ import { Divider, Rate, Popover, - Badge + Badge, + message } from 'ant-design-vue' import {PlusOutlined, DeleteOutlined} from '@ant-design/icons-vue'; import {renderDictValue} from "#/utils/render"; import {getDictOptions} from "#/utils/dict"; +import type { + QuestionnaireForm +} from "#/api/property/customerService/questionnaire/questionnaire/model"; +import type {QuestionForm} from "#/api/property/customerService/questionnaire/question/model"; +import { + questionnaireAdd, + questionnaireUpdate, + questionnaireInfo +} from '#/api/property/customerService/questionnaire/questionnaire'; +import type { + QuestionItemForm +} from "#/api/property/customerService/questionnaire/questionItem/model"; const emit = defineEmits<{ reload: [] }>(); const isUpdate = ref(false); @@ -26,35 +39,10 @@ const title = computed(() => { return isUpdate.value ? '编辑问卷' : '新增问卷'; }); -// const [BasicForm, formApi] = useVbenForm({ -// commonConfig: { -// // 默认占满两列 -// formItemClass: 'col-span-1', -// // 默认label宽度 px -// labelWidth: 80, -// // 通用配置项 会影响到所有表单项 -// componentProps: { -// class: 'w-full', -// } -// }, -// schema: modalSchema(), -// showDefaultActions: false, -// wrapperClass: 'grid-cols-2', -// }); - -// const {onBeforeClose, markInitialized, resetInitialized} = useBeforeCloseDiff( -// { -// initializedGetter: defaultFormValueGetter(formApi), -// currentGetter: defaultFormValueGetter(formApi), -// }, -// ); - const [BasicModal, modalApi] = useVbenModal({ - // 在这里更改宽度 fullscreenButton: false, fullscreen: true, onClosed: handleClosed, - onConfirm: handleConfirm, onOpenChange: async (isOpen) => { if (!isOpen) { return null; @@ -64,44 +52,53 @@ const [BasicModal, modalApi] = useVbenModal({ isUpdate.value = !!id; if (isUpdate.value && id) { - // const record = await contingenPlanInfo(id); - // await formApi.setValues(record); + const record = await questionnaireInfo(id); + Object.assign(counts, badgeCounts); + let questions: QuestionForm[] = []; + if (record.questionnaireQuestionVos) { + questions = record.questionnaireQuestionVos.map(item => { + const question: QuestionForm = { ...item }; + switch (item.type) { + case '1': counts.inputCount++; break; + case '2': counts.textareaCount++; break; + case '3': counts.radioCount++; break; + case '4': counts.checkboxCount++; break; + case '5': counts.rateCount++; break; + case '6': counts.datePickerCount++; break; + } + if (item.questionnaireQuestionItemVos) { + question.questionnaireQuestionItems = item.questionnaireQuestionItemVos.map( + option => ({ ...option } as QuestionItemForm) + ); + } + return question; + }); + } + Object.assign(formState, { + ...record, + questionnaireQuestions: questions, + }); } - // await markInitialized(); - modalApi.modalLoading(false); }, }); -async function handleConfirm() { - try { - modalApi.lock(true); - // const {valid} = await formApi.validate(); - // if (!valid) { - // return; - // } - // getValues获取为一个readonly的对象 需要修改必须先深拷贝一次 - // const data = cloneDeep(await formApi.getValues()); - // await (isUpdate.value ? contingenPlanUpdate(data) : contingenPlanAdd(data)); - // resetInitialized(); - emit('reload'); - modalApi.close(); - } catch (error) { - console.error(error); - } finally { - modalApi.lock(false); - } -} - async function handleClosed() { - // await formApi.resetForm(); - // resetInitialized(); + formRef.value.clearValidate(); + Object.assign(formState, initialState); + Object.assign(counts, badgeCounts); + formState.questionnaireQuestions = []; } -const initialState = { - username: '', - password: '', - questionList: [], +const initialState: QuestionnaireForm = { + id: '', + head: '', + depict: '', + isAnonyCollec: '1', + isCommit: '1', + deadline: '', + status: '', + questionnaireQuestions: [] as Array }; const badgeCounts = { @@ -114,74 +111,110 @@ const badgeCounts = { } const counts = reactive({...badgeCounts}) -const formState = reactive({ - ...initialState -}); +const formState = reactive({...initialState}); async function handleCancel() { + formRef.value.clearValidate(); Object.assign(formState, initialState); Object.assign(counts, badgeCounts); - formState.questionList = []; + formState.questionnaireQuestions = []; await modalApi.close(); } -function handleAddQuestion(type: number) { - formState.questionList.push({type}) +function handleAddQuestion(type: string) { + let question: QuestionForm = { + type, + questionnaireQuestionItems: [], + isRequired: '1', + } switch (type) { - case 1: + case '1': counts.inputCount++; break; - case 2: + case '2': counts.textareaCount++; break; - case 3: + case '3': counts.radioCount++; + question.questionnaireQuestionItems.push({itemContent: ''}); break; - case 4: + case '4': counts.checkboxCount++; + question.questionnaireQuestionItems.push({itemContent: ''}); break; - case 5: + case '5': counts.rateCount++; break; - case 6: + case '6': counts.datePickerCount++; break; } + formState.questionnaireQuestions.push(question); } -function addOptions(val: any) { - +function addOptions(index: number) { + formState.questionnaireQuestions[index]?.questionnaireQuestionItems.push({itemContent: ''}) } -function deleteOptions(index: number, type: number) { - formState.questionList.splice(index, 1) +function deleteOptions(index: number, type: string) { + formState.questionnaireQuestions.splice(index, 1) switch (type) { - case 1: + case '1': counts.inputCount--; break; - case 2: + case '2': counts.textareaCount--; break; - case 3: + case '3': counts.radioCount--; break; - case 4: + case '4': counts.checkboxCount--; break; - case 5: + case '5': counts.rateCount--; break; - case 6: + case '6': counts.datePickerCount--; break; } } -async function handleSave(type: number) { - await modalApi.close(); +const formRef = ref(); + +async function handleSave(type: string) { + try { + await formRef.value.validate(); + const data = cloneDeep(formState); + if (!data.questionnaireQuestions.length) { + message.error('还没有问题,请添加'); + return; + } + modalApi.lock(true); + data.status = type; + data.questionnaireQuestions.forEach((item, index) => { + item.sort = index + 1; + item.questionnaireQuestionItems.forEach((option, i) => { + option.sort = i + 1; + }); + }); + await (isUpdate.value ? questionnaireUpdate(data) : questionnaireAdd(data)); + emit('reload'); + await modalApi.close(); + } catch (error: any) { + if (error.errorFields) { + const firstErrorField = error.errorFields[0].name[0]; + formRef.value.scrollToField(firstErrorField); + message.error('请完善问卷'); + } else { + console.error(error); + } + } finally { + modalApi.lock(false); + } } -onMounted(()=>{ +onMounted(() => { getDictOptions('wy_wtsjlx') }) @@ -194,22 +227,22 @@ onMounted(()=>{ 问题设计 - 单行文本 + 单行文本 - 多行文本 + 多行文本 - 单选题 + 单选题 - 多选题 + 多选题 - 评分题 + 评分题 - 日期选择 + 日期选择 @@ -219,24 +252,25 @@ onMounted(()=>{ - + - + - - + + 问题{{ (index + 1) + '\xa0' + renderDictValue(item.type, 'wy_wtsjlx') }} @@ -252,32 +286,42 @@ onMounted(()=>{ - + - + - + - - + + + + + + - + @@ -286,25 +330,25 @@ onMounted(()=>{ - + - + - + @@ -316,28 +360,30 @@ onMounted(()=>{ - + - + - + @@ -346,8 +392,8 @@ onMounted(()=>{ @@ -382,6 +428,25 @@ onMounted(()=>{ max-height: 87vh; overflow-x: hidden; overflow-y: auto; + + .option-item { + :deep(.ant-form-item .ant-form-item-control-input) { + margin-bottom: -20px; + } + } + + .empty-box { + height: 50vh; + border: 2px dashed #dce0e6; + border-radius: 10px; + + :deep(.ant-empty .ant-empty-image) { + margin-top: 15vh; + } + + margin-bottom: 10px; + margin-left: 80px; + } } .footer-button { @@ -399,23 +464,7 @@ onMounted(()=>{ background-color: #ffffff; } - .empty-box { - height: 50vh; - border: 2px dashed #dce0e6; - border-radius: 10px; - :deep(.ant-empty .ant-empty-image) { - margin-top: 15vh; - } - - margin-bottom: 10px; - margin-left: 80px; - } - - .question-item-title { - font-weight: bold; - margin: 5px 12px; - } }
类型: -
时间:{{item.createTime}}
处理人:{{item.handlerName}}
{{ item.depict }}