| | |
| | | <template> |
| | | <div class="questionnaire" :class="'survey-type-' + surveyType"> |
| | | <!-- 加载状态 --> |
| | | <div v-if="loading" class="loading-state"> |
| | | <div class="loading-spinner"> |
| | | <i class="el-icon-loading"></i> |
| | | <p>问卷加载中...</p> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 无数据状态 --> |
| | | <div v-else-if="isEmptyData" class="empty-state"> |
| | | <div class="empty-content"> |
| | | <i class="el-icon-document" style="font-size: 64px; color: #909399"></i> |
| | | <h3>暂无问卷数据</h3> |
| | | <p>当前没有可用的问卷,请联系管理员或稍后重试</p> |
| | | <el-button |
| | | type="primary" |
| | | @click="loadSurveyData" |
| | | icon="el-icon-refresh" |
| | | > |
| | | 重新加载 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 错误状态 --> |
| | | <div v-else-if="hasError" class="error-state"> |
| | | <div class="error-content"> |
| | | <i class="el-icon-warning" style="font-size: 64px; color: #f56c6c"></i> |
| | | <h3>数据加载失败</h3> |
| | | <p>{{ errorMessage }}</p> |
| | | <el-button |
| | | type="primary" |
| | | @click="loadSurveyData" |
| | | icon="el-icon-refresh" |
| | | > |
| | | 重新尝试 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <div class="CONTENT" v-if="!accomplish"> |
| | | <div class="preview-left"> |
| | | <div class="toptitle"> |
| | |
| | | <div style="font-size: 22px; margin-bottom: 20px; line-height: 1.5"> |
| | | {{ surveyDescription }} |
| | | </div> |
| | | </div> |
| | | <div v-if="showDeptSelect" class="dept-select-container"> |
| | | <el-form> |
| | | <el-form> |
| | | <el-form-item label="选择科室"> |
| | | <el-select |
| | | v-model="selectedDept" |
| | | filterable |
| | | clearable |
| | | placeholder="请选择科室或输入关键词搜索" |
| | | @change="handleDeptChange" |
| | | popper-class="dept-select-dropdown" |
| | | > |
| | | <el-option |
| | | v-for="dept in filteredDeptList" |
| | | :key="dept.code" |
| | | :label="`${dept.name} (${dept.code})`" |
| | | :value="dept.name" |
| | | > |
| | | <span>{{ dept.name }}</span> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-form> |
| | | </div> |
| | | <el-divider></el-divider> |
| | | |
| | |
| | | <el-input |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入您的意见或建议" |
| | | placeholder="请输入" |
| | | v-model="item.scriptResult" |
| | | clearable |
| | | > |
| | |
| | | <div class="CONTENT" v-else> |
| | | <div class="preview-lefts"> |
| | | <div class="completion-message"> |
| | | <div class="thank-you">{{ this.accomplish||'感谢您的配合!' }}</div> |
| | | <div class="thank-you">{{ this.accomplish || "感谢您的配合!" }}</div> |
| | | <div class="feedback-message">{{ completionMessage }}</div> |
| | | </div> |
| | | </div> |
| | |
| | | import { |
| | | getScriptByCondition, |
| | | saveMYDQuestionAnswer, |
| | | WLgetDept, |
| | | } from "@/api/AiCentre/index"; |
| | | |
| | | export default { |
| | |
| | | surveyTitle: "", |
| | | surveyDescription: "", |
| | | questionList: [], |
| | | deptList: [], |
| | | completionMessage: "", |
| | | accomplish: false, |
| | | showDeptSelect: false, |
| | | selectedDept: null, |
| | | deptSearchText: "", // 保留用于本地过滤 |
| | | deptList: [], |
| | | filteredDeptList: [], |
| | | |
| | | // 加密后的参数 |
| | | encryptedParams: { |
| | |
| | | param2: "", |
| | | param3: "", |
| | | param4: "", |
| | | param5: "", |
| | | param6: "30001002", |
| | | }, |
| | | |
| | | isEmptyData: false, // 新增:无数据状态 |
| | | hasError: false, // 新增:错误状态 |
| | | loading: false, // 新增:加载状态 |
| | | errorMessage: "", // 新增:错误信息 |
| | | // 测试数据 |
| | | testData: { |
| | | 1: { |
| | |
| | | // 从路由参数获取加密后的参数 |
| | | this.encryptedParams.param1 = |
| | | this.$route.query.param1 || |
| | | "DBohZ1ARKfFmCdKrBKQ6JW3ddPTtDpgSaRZaKtxBMTJ4FngT06Vy-VskiwDYJJRwfvkHrPIZlkafgZybobGtKQ=="; |
| | | "WOAq2QZd43E-qg-96SvuIFsn-sdRVxQNH4M82XhpXp_Ux4PFrPaqSFXcKaeA6oxEgNhPisA86LvU9kTAEz4xvQ=="; |
| | | this.encryptedParams.param2 = |
| | | this.$route.query.param2 || |
| | | "WQXniB7BIlizOwOQ4KZqITNrqWpLU3SD5vXdHLeYaviA-1T5Dtk70IJWAHbtcDUuYz-2ObYuMj4YKHfWhlCLzw=="; |
| | | this.encryptedParams.param3 = this.$route.query.param3 || null; |
| | | this.encryptedParams.param4 = this.$route.query.param4 || "1"; // 默认为门诊 |
| | | "XWeBh42RLYlNsMcomgw9UXhUPySkRP5EneWSueSq8F84qwYznU9heXuSx4tUMUtDvRnuJ86moJivy-kWQX12Rg=="; |
| | | this.encryptedParams.param5 = this.$route.query.param3 || "2"; // 1=住院, 2=门诊, 3=投诉建议 |
| | | this.encryptedParams.param6 = this.$route.query.param4 || "30001002"; |
| | | |
| | | this.surveyType = parseInt(this.encryptedParams.param4) || 1; |
| | | this.surveyType = parseInt(this.encryptedParams.param5) || 2; |
| | | |
| | | // 加载问卷数据 |
| | | this.loadSurveyData(); |
| | | // 获取科室列表 |
| | | this.WLgetDept(); |
| | | }, |
| | | WLgetDept() { |
| | | // 调用接口获取科室数据 |
| | | WLgetDept(this.encryptedParams.param6).then((res) => { |
| | | this.deptList = Object.entries(res.data).map(([code, name]) => ({ |
| | | code, |
| | | name, |
| | | })); |
| | | this.filteredDeptList = [...this.deptList]; |
| | | |
| | | // 加载调查数据 |
| | | if (this.surveyType === 3) { |
| | | this.showDeptSelect = true; |
| | | } |
| | | }); |
| | | }, |
| | | filterDeptList() { |
| | | if (!this.deptSearchText) { |
| | | this.filteredDeptList = [...this.deptList]; |
| | | return; |
| | | } |
| | | |
| | | const searchText = this.deptSearchText.toLowerCase(); |
| | | this.filteredDeptList = this.deptList.filter( |
| | | (dept) => |
| | | dept.name.toLowerCase().includes(searchText) || |
| | | dept.code.toLowerCase().includes(searchText) |
| | | ); |
| | | }, |
| | | // 加载调查数据 |
| | | loadSurveyData() { |
| | | this.loading = true; |
| | | this.isEmptyData = false; |
| | | this.hasError = false; |
| | | this.errorMessage = ""; |
| | | // 调用接口获取问卷数据 |
| | | getScriptByCondition(this.encryptedParams) |
| | | // 根据问卷类型设置不同的参数 |
| | | let encryptedParams = { |
| | | param1: this.encryptedParams.param1, |
| | | }; |
| | | |
| | | // 根据surveyType设置不同的参数 |
| | | switch (this.surveyType) { |
| | | case 1: // 住院 |
| | | encryptedParams.param2 = this.encryptedParams.param2; |
| | | break; |
| | | case 2: // 门诊 |
| | | encryptedParams.param3 = this.encryptedParams.param2; |
| | | break; |
| | | case 3: // 投诉建议 |
| | | encryptedParams.param4 = this.encryptedParams.param2; |
| | | break; |
| | | default: |
| | | encryptedParams.param3 = this.encryptedParams.param2; |
| | | } |
| | | getScriptByCondition(encryptedParams) |
| | | .then((res) => { |
| | | if (res.code === 200) { |
| | | if (res.data.result) { |
| | | this.accomplish = res.data.result; |
| | | return |
| | | return; |
| | | } |
| | | if ( |
| | | !res.data.svyLibTemplateScriptVOS || |
| | | res.data.svyLibTemplateScriptVOS.length === 0 |
| | | ) { |
| | | this.isEmptyData = true; |
| | | this.$message.warning("暂无问卷数据"); |
| | | return; |
| | | } |
| | | // 处理接口返回的数据 |
| | | this.questionList = res.data.svyLibTemplateScriptVOS.map((item) => { |
| | |
| | | |
| | | // 根据surveyType设置标题和描述 |
| | | switch (this.surveyType) { |
| | | case 1: // 门诊 |
| | | case 2: // 门诊 |
| | | this.surveyTitle = "门诊满意度调查"; |
| | | this.surveyDescription = |
| | | "亲爱的患者,感谢您选择我们的医疗服务。为了不断提升服务质量,请您花几分钟时间填写此问卷。"; |
| | | this.completionMessage = |
| | | "感谢您宝贵的意见!我们将不断改进门诊服务质量,为您提供更好的医疗服务体验。"; |
| | | break; |
| | | case 2: // 住院 |
| | | case 1: // 住院 |
| | | this.surveyTitle = "住院满意度调查"; |
| | | this.surveyDescription = |
| | | "亲爱的患者及家属,感谢您选择在我院住院治疗。为了提升住院服务质量,请您填写此问卷。"; |
| | |
| | | } |
| | | } else { |
| | | // 接口无数据或失败,使用测试数据 |
| | | this.useTestData(this.surveyType); |
| | | // this.useTestData(this.surveyType); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | console.error("数据获取失败:", error); |
| | | this.hasError = true; |
| | | this.errorMessage = |
| | | error.message || "问卷数据加载失败,请检查网络连接后重试"; |
| | | this.$message.error("数据加载失败"); |
| | | // 接口调用失败,使用测试数据 |
| | | this.useTestData(this.surveyType); |
| | | // this.useTestData(this.surveyType); |
| | | }) |
| | | .finally(() => { |
| | | this.loading = false; |
| | | }); |
| | | }, |
| | | |
| | | handleDeptChange(value) { |
| | | this.selectedDept = value; |
| | | // 可以在这里添加其他处理逻辑 |
| | | }, |
| | | // 使用测试数据 |
| | | useTestData(surveyType) { |
| | | const type = [1, 2, 3].includes(surveyType) ? surveyType : 1; |
| | |
| | | const res = await saveMYDQuestionAnswer(submitData); |
| | | |
| | | if (res.code === 200) { |
| | | this.accomplish = false; |
| | | this.accomplish = "问卷已提交"; |
| | | this.$message.success("提交成功!感谢您的反馈。"); |
| | | } else { |
| | | this.$message.error(res.msg || "提交失败,请稍后再试"); |
| | |
| | | }, |
| | | |
| | | // 准备提交数据 |
| | | // 准备提交数据 |
| | | prepareSubmitData() { |
| | | // 创建科室选择问题对象 |
| | | const deptQuestion = { |
| | | scriptId: "dept_selection", // 自定义ID |
| | | scriptType: 4, // 4表示问答类型 |
| | | scriptContent: "选择的科室", |
| | | scriptResult: this.selectedDept || "", // 存储选择的科室名称 |
| | | required: false, // 非必填 |
| | | sort: 999, |
| | | nextScriptno: "1", |
| | | }; |
| | | |
| | | return { |
| | | taskId: this.encryptedParams.param1, |
| | | serialnum: this.encryptedParams.param2 || this.encryptedParams.param3, |
| | | mzzy: this.surveyType, // 1=门诊, 2=住院, 3=投诉建议 |
| | | svyLibTemplateScriptVOS: this.questionList.map((item) => { |
| | | return { |
| | | scriptId: item.scriptId, |
| | | scriptType: item.scriptType, |
| | | scriptResult: |
| | | item.scriptType === 2 |
| | | ? (item.scriptResult || []).join("&") |
| | | : item.scriptResult || "", |
| | | nextScriptno: item.nextScriptno, |
| | | score: item.score, |
| | | prompt: item.prompt, |
| | | ...item, |
| | | }; |
| | | }), |
| | | mzzy: this.surveyType, |
| | | svyLibTemplateScriptVOS: [ |
| | | deptQuestion, // 将科室选择作为第一个问题 |
| | | ...this.questionList.map((item) => { |
| | | return { |
| | | scriptId: item.scriptId, |
| | | scriptType: item.scriptType, |
| | | scriptResult: |
| | | item.scriptType === 2 |
| | | ? (item.scriptResult || []).join("&") |
| | | : item.scriptResult || "", |
| | | nextScriptno: item.nextScriptno, |
| | | score: item.score, |
| | | prompt: item.prompt, |
| | | ...item, |
| | | }; |
| | | }), |
| | | ], |
| | | excep: this.checkAbnormalOptions() ? 1 : 0, |
| | | }; |
| | | }, |
| | |
| | | --theme-gradient: linear-gradient(135deg, #e53e3e, #f6ad55); |
| | | } |
| | | } |
| | | .dept-select-container { |
| | | margin: 20px 0; |
| | | padding: 20px; |
| | | background-color: #f8fafc; |
| | | border-radius: 8px; |
| | | border: 1px solid #e2e8f0; |
| | | } |
| | | |
| | | /* 调整下拉选项样式 */ |
| | | .el-select-dropdown__item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | } |
| | | /* 下拉框样式调整 */ |
| | | ::v-deep .dept-select-dropdown { |
| | | max-height: 400px; /* 限制最大高度 */ |
| | | overflow-y: auto; /* 添加滚动条 */ |
| | | |
| | | .el-select-dropdown__item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | padding: 0 20px; |
| | | height: auto; |
| | | line-height: 36px; |
| | | |
| | | span { |
| | | display: inline-block; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | /* 名称部分 */ |
| | | span:first-child { |
| | | width: 60%; |
| | | text-align: left; |
| | | } |
| | | |
| | | /* 编码部分 */ |
| | | span:last-child { |
| | | width: 40%; |
| | | text-align: right; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* 移动端适配 */ |
| | | @media (max-width: 768px) { |
| | | ::v-deep .dept-select-dropdown { |
| | | max-width: 100vw; /* 限制最大宽度为视口宽度 */ |
| | | width: auto !important; |
| | | left: 10px !important; |
| | | right: 10px !important; |
| | | |
| | | .el-select-dropdown__item { |
| | | span:first-child { |
| | | width: 50%; |
| | | } |
| | | span:last-child { |
| | | width: 50%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .CONTENT { |
| | | max-width: 900px; |
| | | margin: 0 auto; |
| | |
| | | } |
| | | } |
| | | } |
| | | .loading-state { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | min-height: 60vh; |
| | | padding: 40px; |
| | | |
| | | .loading-spinner { |
| | | text-align: center; |
| | | |
| | | .el-icon-loading { |
| | | font-size: 48px; |
| | | color: var(--primary-color); |
| | | margin-bottom: 16px; |
| | | animation: rotating 2s linear infinite; |
| | | } |
| | | |
| | | p { |
| | | color: #666; |
| | | font-size: 16px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .empty-state, |
| | | .error-state { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | min-height: 60vh; |
| | | padding: 40px; |
| | | |
| | | .empty-content, |
| | | .error-content { |
| | | text-align: center; |
| | | max-width: 400px; |
| | | |
| | | h3 { |
| | | color: #666; |
| | | font-size: 20px; |
| | | margin: 16px 0 12px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | p { |
| | | color: #999; |
| | | font-size: 14px; |
| | | margin-bottom: 24px; |
| | | line-height: 1.6; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .error-state { |
| | | .error-content { |
| | | h3 { |
| | | color: #f56c6c; |
| | | } |
| | | } |
| | | } |
| | | |
| | | @keyframes rotating { |
| | | from { |
| | | transform: rotate(0deg); |
| | | } |
| | | to { |
| | | transform: rotate(360deg); |
| | | } |
| | | } |
| | | |
| | | /* 响应式调整 */ |
| | | @media (max-width: 768px) { |
| | | .loading-state, |
| | | .empty-state, |
| | | .error-state { |
| | | padding: 20px; |
| | | min-height: 50vh; |
| | | |
| | | .el-icon-loading, |
| | | .el-icon-document, |
| | | .el-icon-warning { |
| | | font-size: 40px !important; |
| | | } |
| | | |
| | | h3 { |
| | | font-size: 18px !important; |
| | | } |
| | | |
| | | p { |
| | | font-size: 13px !important; |
| | | } |
| | | } |
| | | } |
| | | ::v-deep { |
| | | .el-checkbox-group { |
| | | display: flex; |