Files
admin-vben5/apps/web-antd/src/views/property/customerService/questionnaire/questionnaire-modal.vue

422 lines
12 KiB
Vue
Raw Normal View History

2025-08-14 14:58:42 +08:00
<script setup lang="ts">
import {computed, onMounted, reactive, ref} from 'vue';
import {useVbenModal} from '@vben/common-ui';
import {cloneDeep} from '@vben/utils';
import {
Form,
FormItem,
Input,
Row,
Col,
Switch,
DatePicker,
Empty,
Divider,
Rate,
Popover,
Badge
} from 'ant-design-vue'
import {PlusOutlined, DeleteOutlined} from '@ant-design/icons-vue';
import {renderDictValue} from "#/utils/render";
import {getDictOptions} from "#/utils/dict";
const emit = defineEmits<{ reload: [] }>();
const isUpdate = ref(false);
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;
}
modalApi.modalLoading(true);
const {id} = modalApi.getData() as { id?: number | string };
isUpdate.value = !!id;
if (isUpdate.value && id) {
// const record = await contingenPlanInfo(id);
// await formApi.setValues(record);
}
// 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();
}
const initialState = {
username: '',
password: '',
questionList: [],
};
const badgeCounts = {
inputCount: 0,
textareaCount: 0,
radioCount: 0,
checkboxCount: 0,
rateCount: 0,
datePickerCount: 0,
}
const counts = reactive({...badgeCounts})
const formState = reactive({
...initialState
});
async function handleCancel() {
Object.assign(formState, initialState);
Object.assign(counts, badgeCounts);
formState.questionList = [];
await modalApi.close();
}
function handleAddQuestion(type: number) {
formState.questionList.push({type})
switch (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;
}
}
function addOptions(val: any) {
}
function deleteOptions(index: number, type: number) {
formState.questionList.splice(index, 1)
switch (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;
}
}
async function handleSave(type: number) {
await modalApi.close();
}
onMounted(()=>{
getDictOptions('wy_wtsjlx')
})
</script>
<template>
<BasicModal :title="title" :footer="false" class="modal-container">
<Row :gutter="24">
<Col class="gutter-row" :span="3">
<div class="left-options">
<div class="header-title">问题设计</div>
<Badge :count="counts.inputCount" :number-style="{ backgroundColor: '#3996f3' }">
<a-button size="large" @click="handleAddQuestion(1)">单行文本</a-button>
</Badge>
<Badge :count="counts.textareaCount" :number-style="{ backgroundColor: '#3996f3' }">
<a-button size="large" @click="handleAddQuestion(2)">多行文本</a-button>
</Badge>
<Badge :count="counts.radioCount" :number-style="{ backgroundColor: '#3996f3' }">
<a-button size="large" @click="handleAddQuestion(3)">单选题</a-button>
</Badge>
<Badge :count="counts.checkboxCount" :number-style="{ backgroundColor: '#3996f3' }">
<a-button size="large" @click="handleAddQuestion(4)">多选题</a-button>
</Badge>
<Badge :count="counts.rateCount" :number-style="{ backgroundColor: '#3996f3' }">
<a-button size="large" @click="handleAddQuestion(5)">评分题</a-button>
</Badge>
<Badge :count="counts.datePickerCount" :number-style="{ backgroundColor: '#3996f3' }">
<a-button size="large" @click="handleAddQuestion(6)">日期选择</a-button>
</Badge>
</div>
</Col>
<Col class="gutter-row" :span="21">
<div class="right-question">
<div class="header-title">基本信息</div>
<Form
:model="formState"
name="basic"
>
<FormItem
label="问卷标题"
name="username"
:rules="[{ required: true, message: '问卷标题不能为空' }]"
>
<Input v-model:value="formState.username"/>
</FormItem>
<FormItem
label="问卷描述"
name="password"
:rules="[{ required: true, message: 'Please input your password!' }]"
>
<Input v-model:value="formState.password"/>
</FormItem>
<div v-if="formState.questionList.length">
<div v-for="(item,index) in formState.questionList">
<Divider orientation="left">
问题{{ (index + 1) + '\xa0' + renderDictValue(item.type, 'wy_wtsjlx') }}
<Popover placement="top">
<template #content>
<p>删除当前问题</p>
</template>
<a-button type="link" @click="deleteOptions(index,item.type)">
<template #icon>
<DeleteOutlined style="color: red"/>
</template>
</a-button>
</Popover>
</Divider>
<FormItem
label="问题标题"
name="username"
:rules="[{ required: true }]"
>
<Input v-model:value="formState.username"/>
</FormItem>
<FormItem
label="问题描述"
name="password"
:rules="[{ required: true }]"
>
<Input v-model:value="formState.password"/>
</FormItem>
<Row :gutter="24" v-if="item.type==3||item.type==4">
<Col class="gutter-row" :span="12">
<FormItem
label="选项设置"
name="password"
:rules="[{ required: true }]"
>
<Input style="margin-bottom: 10px" v-model:value="formState.password"
placeholder="选项1"/>
<Input v-model:value="formState.password" placeholder="选项1"/>
</FormItem>
</Col>
<Col class="gutter-row" :span="12">
<a-button @click="addOptions(item)">
<template #icon>
<PlusOutlined/>
</template>
添加选项
</a-button>
</Col>
</Row>
<FormItem
v-if="item.type==5"
label="评分预览"
name="password"
>
<Rate v-model:value="formState.username"/>
</FormItem>
<FormItem
v-if="item.type==6"
label="日期选择预览"
name="password"
>
<DatePicker style="width: 180px" v-model:value="formState.username"/>
</FormItem>
<FormItem
label="是否必填"
name="password"
:rules="[{ required: true, message: 'Please input your password!' }]"
>
<Switch v-model:checked="formState.username"/>
</FormItem>
</div>
</div>
<div class="empty-box" v-else>
<Empty description="还没有添加问题,点击左侧按钮开始创建"/>
</div>
<div class="header-title">发布设计</div>
<Row :gutter="24">
<Col class="gutter-row" :span="6">
<FormItem
label="匿名收集"
name="username"
:rules="[{ required: true, message: '匿名收集不能为空' }]"
>
<Switch v-model:checked="formState.username"/>
</FormItem>
</Col>
<Col class="gutter-row" :span="6">
<FormItem
label="允许多次提交"
name="username"
:rules="[{ required: true, message: '多次提交不能为空' }]"
>
<Switch v-model:checked="formState.username"/>
</FormItem>
</Col>
<Col class="gutter-row" :span="12">
<FormItem
label="截至日期"
name="username"
:rules="[{ required: true, message: '截止日期不能为空' }]"
>
<DatePicker style="width: 180px" v-model:value="formState.username"/>
</FormItem>
</Col>
</Row>
</Form>
</div>
</Col>
</Row>
<div class="footer-button">
<a-button type="primary" @click="handleSave(1)">保存并发布</a-button>
<a-button @click="handleSave(2)">保存为草稿</a-button>
<a-button @click="handleCancel">取消</a-button>
</div>
</BasicModal>
</template>
<style lang="scss" scoped>
.modal-container {
position: relative;
.header-title {
margin-bottom: 10px;
font-size: 17px;
font-weight: bold;
}
.left-options {
display: flex;
flex-direction: column;
:deep(.ant-badge .ant-badge-count) {
position: absolute;
top: 40px;
}
.ant-btn {
width: 150px;
margin-top: 40px;
}
}
.right-question {
max-height: 87vh;
overflow-x: hidden;
overflow-y: auto;
}
.footer-button {
width: 100%;
.ant-btn {
float: right;
margin: 0 15px 10px 0;
}
position: absolute;
right: 0;
bottom: 0;
z-index: 100;
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;
}
}
</style>