/** * 调查问卷题目编辑组件 Mixin * @fileoverview 提供题目编辑、选项管理等功能的混入 */ import { getQuestionList, addQuestion, updateQuestion } from '@/api/survey/question' import { getOptionList, addOption, updateOption } from '@/api/survey/option' import { QUESTION_TYPE_OPTIONS, QUESTION_REQUIRED_OPTIONS, QUESTION_TYPE, getQuestionTypeLabel, getQuestionTypeType, getQuestionTypeIcon, getQuestionRequiredLabel, getQuestionRequiredType, isQuestionTypeNeedOptions } from '@/constants/survey' /** * @typedef {Object} QuestionEditorData * @property {string|number} surveyId - 调查问卷ID * @property {Array} questionList - 题目列表 * @property {boolean} loading - 加载状态 * @property {number} total - 总数 * @property {number} current - 当前页码 * @property {number} size - 每页数量 * @property {boolean} questionDialogVisible - 题目对话框显示状态 * @property {string} questionDialogMode - 题目对话框模式 add|edit * @property {QuestionForm} questionForm - 题目表单 * @property {Object} questionFormRules - 题目表单验证规则 * @property {Array<{label: string, value: number}>} questionTypeOptions - 题目类型选项 * @property {Array<{label: string, value: number}>} questionRequiredOptions - 是否必填选项 * @property {Array} currentQuestionOptions - 当前题目的选项列表 * @property {boolean} optionDialogVisible - 选项对话框显示状态 * @property {string} optionDialogMode - 选项对话框模式 add|edit * @property {OptionForm} optionForm - 选项表单 * @property {Object} optionFormRules - 选项表单验证规则 * @property {string|number} currentQuestionId - 当前编辑的题目ID */ export default { name: 'QuestionEditorMixin', props: { /** * 调查问卷ID * @type {string|number} */ surveyId: { type: [String, Number], required: true } }, data() { return { /** * 题目列表 * @type {Array} */ questionList: [], /** * 加载状态 * @type {boolean} */ loading: false, /** * 总数 * @type {number} */ total: 0, /** * 当前页码 * @type {number} */ current: 1, /** * 每页数量 * @type {number} */ size: 100, /** * 题目对话框显示状态 * @type {boolean} */ questionDialogVisible: false, /** * 题目对话框模式 * @type {string} */ questionDialogMode: 'add', /** * 题目表单 * @type {QuestionForm} */ questionForm: { surveyId: '', questionNo: 1, title: '', questionType: QUESTION_TYPE.SINGLE_CHOICE, isRequired: 0 }, /** * 题目表单验证规则 * @type {Object} */ questionFormRules: { title: [ { required: true, message: '请输入题目标题', trigger: 'blur' }, { min: 1, max: 200, message: '题目标题长度在 1 到 200 个字符', trigger: 'blur' } ], questionType: [ { required: true, message: '请选择题目类型', trigger: 'change' } ], questionNo: [ { required: true, message: '请输入题目序号', trigger: 'blur' }, { type: 'number', min: 1, message: '题目序号必须大于0', trigger: 'blur' } ] }, /** * 题目类型选项 * @type {Array<{label: string, value: number}>} */ questionTypeOptions: QUESTION_TYPE_OPTIONS, /** * 是否必填选项 * @type {Array<{label: string, value: number}>} */ questionRequiredOptions: QUESTION_REQUIRED_OPTIONS, /** * 当前题目的选项列表 * @type {Array} */ currentQuestionOptions: [], /** * 选项对话框显示状态 * @type {boolean} */ optionDialogVisible: false, /** * 选项对话框显示状态 * @type {boolean} */ optionAddDialogVisible: false, /** * 选项对话框模式 * @type {string} */ optionDialogMode: 'add', /** * 选项表单 * @type {OptionForm} */ optionForm: { questionId: '', optionNo: 1, optionText: '' }, /** * 选项表单验证规则 * @type {Object} */ optionFormRules: { optionText: [ { required: true, message: '请输入选项内容', trigger: 'blur' }, { min: 1, max: 100, message: '选项内容长度在 1 到 100 个字符', trigger: 'blur' } ], optionNo: [ { required: true, message: '请输入选项序号', trigger: 'blur' }, { type: 'number', min: 1, message: '选项序号必须大于0', trigger: 'blur' } ] }, /** * 当前编辑的题目ID * @type {string|number} */ currentQuestionId: '' } }, computed: { /** * 题目对话框标题 * @returns {string} 对话框标题 */ questionDialogTitle() { return this.questionDialogMode === 'add' ? '新增题目' : '编辑题目' }, /** * 选项对话框标题 * @returns {string} 对话框标题 */ optionDialogTitle() { return this.optionDialogMode === 'add' ? '新增选项' : '编辑选项' }, /** * 是否为新增题目模式 * @returns {boolean} 是否为新增模式 */ isAddQuestionMode() { return this.questionDialogMode === 'add' }, /** * 是否为编辑题目模式 * @returns {boolean} 是否为编辑模式 */ isEditQuestionMode() { return this.questionDialogMode === 'edit' }, /** * 是否为新增选项模式 * @returns {boolean} 是否为新增模式 */ isAddOptionMode() { return this.optionDialogMode === 'add' }, /** * 是否为编辑选项模式 * @returns {boolean} 是否为编辑模式 */ isEditOptionMode() { return this.optionDialogMode === 'edit' } }, mounted() { this.loadQuestionList() }, methods: { /** * 加载题目列表 * @returns {Promise} */ async loadQuestionList() { try { this.loading = true const params = { surveyId: this.surveyId, size: this.size, current: this.current } const responseOrigin = await getQuestionList(params) const response = responseOrigin.data if (response.code === 200 && response.success) { this.questionList = response.data.records || [] this.total = response.data.total || 0 // 为每个题目加载选项 await this.loadAllQuestionOptions() } else { this.$message.error(response.msg || '获取题目列表失败') } } catch (error) { console.error('加载题目列表失败:', error) this.$message.error('加载题目列表失败') } finally { this.loading = false } }, /** * 加载所有题目的选项 * @returns {Promise} */ async loadAllQuestionOptions() { for (const question of this.questionList) { if (isQuestionTypeNeedOptions(question.questionType)) { question.options = await this.loadQuestionOptions(question.id) } else { question.options = [] } } }, /** * 加载指定题目的选项 * @param {string|number} questionId - 题目ID * @returns {Promise>} 选项列表 */ async loadQuestionOptions(questionId) { try { const params = { questionId, size: 100, current: 1 } const responseOrigin = await getOptionList(params) const response = responseOrigin.data if (response.code === 200 && response.success) { return response.data.records || [] } else { console.error('获取选项列表失败:', response.msg) return [] } } catch (error) { console.error('加载选项列表失败:', error) return [] } }, /** * 新增题目 * @returns {void} */ handleAddQuestion() { this.questionDialogMode = 'add' this.resetQuestionForm() this.questionForm.surveyId = this.surveyId this.questionForm.questionNo = this.getNextQuestionNo() this.questionDialogVisible = true }, /** * 编辑题目 * @param {QuestionItem} question - 题目数据 * @returns {void} */ handleEditQuestion(question) { this.questionDialogMode = 'edit' this.setQuestionForm(question) this.questionDialogVisible = true }, /** * 管理题目选项 * @param {QuestionItem} question - 题目数据 * @returns {void} */ async handleManageOptions(question) { if (!isQuestionTypeNeedOptions(question.questionType)) { this.$message.warning('文本类型题目不需要设置选项') return } this.currentQuestionId = question.id this.currentQuestionOptions = await this.loadQuestionOptions(question.id) this.optionDialogVisible = true }, /** * 新增选项 * @returns {void} */ handleAddOption() { this.optionDialogMode = 'add' this.optionAddDialogVisible = true this.resetOptionForm() this.optionForm.questionId = this.currentQuestionId this.optionForm.optionNo = this.getNextOptionNo() this.optionDialogVisible = true }, /** * 编辑选项 * @param {OptionItem} option - 选项数据 * @returns {void} */ handleEditOption(option) { this.optionDialogMode = 'edit' this.setOptionForm(option) this.optionDialogVisible = true this.optionAddDialogVisible = true }, /** * 提交题目表单 * @returns {Promise} */ async handleSubmitQuestion() { try { const valid = await this.$refs.questionForm.validate() if (!valid) return this.loading = true let response if (this.isAddQuestionMode) { response = await addQuestion(this.questionForm) } else { response = await updateQuestion(this.questionForm) } response = response.data if (response.code === 200 && response.success) { this.$message.success(response.msg || '操作成功') this.questionDialogVisible = false await this.loadQuestionList() } else { this.$message.error(response.msg || '操作失败') } } catch (error) { console.error('提交题目表单失败:', error) this.$message.error('操作失败') } finally { this.loading = false } }, /** * 提交选项表单 * @returns {Promise} */ async handleSubmitOption() { try { const valid = await this.$refs.optionForm.validate() if (!valid) return this.loading = true let response if (this.isAddOptionMode) { response = await addOption(this.optionForm) } else { response = await updateOption(this.optionForm) } response = response.data if (response.code === 200 && response.success) { this.$message.success(response.msg || '操作成功') // this.optionDialogVisible = false this.optionAddDialogVisible = false this.currentQuestionOptions = await this.loadQuestionOptions(this.currentQuestionId) await this.loadQuestionList() } else { this.$message.error(response.msg || '操作失败') } } catch (error) { console.error('提交选项表单失败:', error) this.$message.error('操作失败') } finally { this.loading = false } }, /** * 设置题目表单数据 * @param {QuestionItem} question - 题目数据 * @returns {void} */ setQuestionForm(question) { this.questionForm = { id: question.id, surveyId: question.surveyId, questionNo: question.questionNo, title: question.title, questionType: question.questionType, isRequired: question.isRequired } }, /** * 重置题目表单 * @returns {void} */ resetQuestionForm() { this.questionForm = { surveyId: this.surveyId, questionNo: 1, title: '', questionType: QUESTION_TYPE.SINGLE_CHOICE, isRequired: 0 } this.$nextTick(() => { this.$refs.questionForm?.clearValidate() }) }, /** * 设置选项表单数据 * @param {OptionItem} option - 选项数据 * @returns {void} */ setOptionForm(option) { this.optionForm = { id: option.id, questionId: option.questionId, optionNo: option.optionNo, optionText: option.optionText } }, /** * 重置选项表单 * @returns {void} */ resetOptionForm() { this.optionForm = { questionId: this.currentQuestionId, optionNo: 1, optionText: '' } this.$nextTick(() => { this.$refs.optionForm?.clearValidate() }) }, /** * 关闭题目对话框 * @returns {void} */ handleCloseQuestionDialog() { this.questionDialogVisible = false this.resetQuestionForm() }, /** * 关闭选项对话框 * @returns {void} */ handleCloseOptionDialog() { this.optionDialogVisible = false this.resetOptionForm() this.currentQuestionOptions = [] this.optionAddDialogVisible = false }, /** * 获取下一个题目序号 * @returns {number} 下一个序号 */ getNextQuestionNo() { if (this.questionList.length === 0) return 1 const maxNo = Math.max(...this.questionList.map(q => q.questionNo)) return maxNo + 1 }, /** * 获取下一个选项序号 * @returns {number} 下一个序号 */ getNextOptionNo() { if (this.currentQuestionOptions.length === 0) return 1 const maxNo = Math.max(...this.currentQuestionOptions.map(o => o.optionNo)) return maxNo + 1 }, /** * 获取题目类型标签 * @param {number} questionType - 题目类型 * @returns {string} 类型标签 */ getQuestionTypeLabel(questionType) { return getQuestionTypeLabel(questionType) }, /** * 获取题目类型类型 * @param {number} questionType - 题目类型 * @returns {string} 类型类型 */ getQuestionTypeType(questionType) { return getQuestionTypeType(questionType) }, /** * 获取题目类型图标 * @param {number} questionType - 题目类型 * @returns {string} 类型图标 */ getQuestionTypeIcon(questionType) { return getQuestionTypeIcon(questionType) }, /** * 获取是否必填标签 * @param {number} isRequired - 是否必填 * @returns {string} 必填标签 */ getQuestionRequiredLabel(isRequired) { return getQuestionRequiredLabel(isRequired) }, /** * 获取是否必填类型 * @param {number} isRequired - 是否必填 * @returns {string} 必填类型 */ getQuestionRequiredType(isRequired) { return getQuestionRequiredType(isRequired) }, /** * 判断题目类型是否需要选项 * @param {number} questionType - 题目类型 * @returns {boolean} 是否需要选项 */ isQuestionTypeNeedOptions(questionType) { return isQuestionTypeNeedOptions(questionType) } } }