123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- /**
- * 调查问卷题目编辑组件 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<QuestionItem>} 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<OptionItem>} 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<QuestionItem>}
- */
- 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<OptionItem>}
- */
- 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<void>}
- */
- 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<void>}
- */
- 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<Array<OptionItem>>} 选项列表
- */
- 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<void>}
- */
- 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<void>}
- */
- 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)
- }
- }
- }
|