tangweijie 7272342fe6 feat: 问卷任务管理前端增强
- 新建问卷页面(question/index.vue)
- 新增 Agent 填写对话框
- 优化任务详情对话框交互
- 新增 API 接口类型定义
2026-01-26 16:02:47 +08:00

337 lines
9.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-dialog
v-model="dialogVisible"
:title="isEdit ? '编辑任务' : '创建问卷任务'"
width="600px"
:close-on-click-modal="false"
>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
>
<!-- 基本信息 -->
<el-divider content-position="left">基本信息</el-divider>
<el-form-item label="任务名称" prop="taskName">
<el-input v-model="formData.taskName" placeholder="请输入任务名称" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item label="选择问卷" prop="questionnaireId">
<el-select
v-model="formData.questionnaireId"
placeholder="请选择问卷"
filterable
style="width: calc(100% - 90px)"
>
<el-option
v-for="item in questionnaireList"
:key="item.id"
:label="item.title"
:value="item.id"
/>
</el-select>
<el-button
type="primary"
link
:disabled="!formData.questionnaireId"
@click="handlePreviewQuestionnaire"
>
<Icon icon="ep:view" class="mr-3px" />预览
</el-button>
</el-form-item>
<!-- 目标范围 -->
<el-divider content-position="left">目标范围</el-divider>
<el-form-item label="目标类型" prop="targetType">
<el-radio-group v-model="formData.targetType">
<el-radio :value="1">指定犯人</el-radio>
<el-radio :value="2">指定监区</el-radio>
<el-radio :value="3">全部犯人</el-radio>
</el-radio-group>
</el-form-item>
<!-- 指定犯人 -->
<el-form-item v-if="formData.targetType === 1" label="选择犯人" prop="prisonerIds">
<el-button type="primary" plain @click="openPrisonerSelector">
<Icon icon="ep:user" class="mr-5px" />
选择犯人已选 {{ formData.prisonerIds.length }}
</el-button>
</el-form-item>
<!-- 指定监区 -->
<el-form-item v-if="formData.targetType === 2" label="选择监区" prop="areaId">
<el-select
v-model="formData.areaId"
placeholder="请选择监区"
filterable
style="width: 100%"
>
<el-option
v-for="item in areaList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<!-- 时间设置 -->
<el-divider content-position="left">时间设置</el-divider>
<el-form-item label="开始时间" prop="startTime">
<el-date-picker
v-model="formData.startTime"
type="datetime"
placeholder="选择开始时间"
style="width: 100%"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item label="截止时间" prop="deadline">
<el-date-picker
v-model="formData.deadline"
type="datetime"
placeholder="选择截止时间"
style="width: 100%"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<!-- 备注 -->
<el-divider content-position="left">备注</el-divider>
<el-form-item label="备注" prop="remark">
<el-input
v-model="formData.remark"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
<!-- 犯人选择器弹窗 -->
<PrisonerSelectorDialog
ref="prisonerSelectorRef"
:selected-ids="formData.prisonerIds"
@confirm="handlePrisonerSelect"
/>
<!-- 问卷预览弹窗 -->
<el-dialog
v-model="previewVisible"
title="问卷预览"
width="800px"
destroy-on-close
>
<QuestionnairePreview :id="previewQuestionnaireId" />
<template #footer>
<el-button @click="previewVisible = false">关闭</el-button>
</template>
</el-dialog>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">
确定
</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, watch } from 'vue'
import { ElMessage } from 'element-plus'
import PrisonerSelectorDialog from './PrisonerSelectorDialog.vue'
import QuestionnairePreview from '@/views/prison/questionnaire/components/QuestionnairePreview.vue'
import { QuestionnaireTaskApi } from '@/api/prison/questionnaire-task'
import { QuestionnaireApi } from '@/api/prison/questionnaire'
import { AreaApi } from '@/api/prison/area'
defineOptions({ name: 'CreateTaskDialog' })
const emit = defineEmits(['success'])
const dialogVisible = ref(false)
const submitLoading = ref(false)
const isEdit = ref(false)
const formRef = ref()
// 预览相关
const previewVisible = ref(false)
const previewQuestionnaireId = ref<number>()
// 表单数据
const formData = reactive({
id: undefined,
taskName: '',
questionnaireId: undefined as number | undefined,
targetType: 2, // 默认指定监区
prisonerIds: [] as number[],
areaId: undefined as number | undefined,
startTime: '',
deadline: '',
remark: ''
})
// 表单校验规则
const formRules = {
taskName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
questionnaireId: [{ required: true, message: '请选择问卷', trigger: 'change' }],
targetType: [{ required: true, message: '请选择目标类型', trigger: 'change' }],
deadline: [{ required: true, message: '请选择截止时间', trigger: 'change' }]
}
// 数据列表
const questionnaireList = ref<any[]>([])
const areaList = ref<any[]>([])
const prisonerSelectorRef = ref()
/** 打开弹窗 */
const open = async (row?: any) => {
dialogVisible.value = true
isEdit.value = !!row
if (row) {
// 编辑模式
formData.id = row.id
formData.taskName = row.taskName
formData.questionnaireId = row.questionnaireId
formData.targetType = row.targetType
formData.areaId = row.areaId
formData.startTime = row.startTime || ''
formData.deadline = row.deadline || ''
formData.remark = row.remark || ''
} else {
// 重置表单
formData.id = undefined
formData.taskName = ''
formData.questionnaireId = undefined
formData.targetType = 2
formData.prisonerIds = []
formData.areaId = undefined
formData.startTime = ''
formData.deadline = ''
formData.remark = ''
}
// 加载数据
await Promise.all([getQuestionnaires(), getAreas()])
}
/** 获取问卷列表 */
const getQuestionnaires = async () => {
try {
const data = await QuestionnaireApi.getQuestionnairePage({
pageNo: 1,
pageSize: 100,
status: 2
})
questionnaireList.value = data.list || []
} catch (e) {
console.error('获取问卷列表失败', e)
}
}
/** 获取监区列表 */
const getAreas = async () => {
try {
const data = await AreaApi.getAreaTree({})
// 提取所有监区用于下拉选择
const extractAreas = (nodes: any[]): any[] => {
const result: any[] = []
nodes.forEach(node => {
result.push({ id: node.id, name: node.name })
if (node.children && node.children.length > 0) {
result.push(...extractAreas(node.children))
}
})
return result
}
areaList.value = extractAreas(data || [])
} catch (e) {
console.error('获取监区列表失败', e)
}
}
/** 打开犯人选择器 */
const openPrisonerSelector = () => {
prisonerSelectorRef.value?.open(formData.prisonerIds)
}
/** 预览问卷 */
const handlePreviewQuestionnaire = () => {
if (!formData.questionnaireId) {
ElMessage.warning('请先选择问卷')
return
}
previewQuestionnaireId.value = formData.questionnaireId
previewVisible.value = true
}
/** 犯人选择确认 */
const handlePrisonerSelect = (selectedIds: number[]) => {
formData.prisonerIds = selectedIds
}
/** 提交 */
const handleSubmit = async () => {
try {
await formRef.value?.validate()
} catch (e) {
return
}
submitLoading.value = true
try {
const data = {
taskName: formData.taskName,
questionnaireId: formData.questionnaireId,
targetType: formData.targetType,
prisonerIds: formData.targetType === 1 ? formData.prisonerIds : undefined,
areaId: formData.targetType === 2 ? formData.areaId : undefined,
startTime: formData.startTime || undefined,
deadline: formData.deadline,
remark: formData.remark || undefined
}
if (isEdit.value) {
await QuestionnaireTaskApi.updateQuestionnaireTask({
id: formData.id,
taskName: formData.taskName,
deadline: formData.deadline,
remark: formData.remark
})
ElMessage.success('修改成功')
} else {
await QuestionnaireTaskApi.createQuestionnaireTask(data)
ElMessage.success('创建成功')
}
dialogVisible.value = false
emit('success')
} catch (e) {
console.error('提交失败', e)
} finally {
submitLoading.value = false
}
}
// 监听目标类型变化,重置相关字段
watch(() => formData.targetType, (val) => {
if (val === 1) {
formData.areaId = undefined
} else if (val === 2) {
formData.prisonerIds = []
}
})
defineExpose({ open })
</script>