| | |
| | | data: data, |
| | | }); |
| | | } |
| | | // 省立同德满意度统计页题目明细 |
| | | export function statistics(data) { |
| | | return request({ |
| | | url: "/smartor/satisfaction/statistics", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | // 省立同德满意度统计页题目明细 |
| | | export function satisfactionGraph(data) { |
| | | return request({ |
| | | url: "/smartor/satisfaction/satisfactionGraph", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | |
| | | permissions: (state) => state.user.permissions, |
| | | belongWards: (state) => state.user.belongWards, |
| | | belongDepts: (state) => state.user.belongDepts, |
| | | satisfactionCategories: (state) => state.user.satisfactionCategories, |
| | | hisUserId: (state) => state.user.hisUserId, |
| | | permission_routes: (state) => state.permission.routes, |
| | | topbarRouters: (state) => state.permission.topbarRouters, |
| | |
| | | belongDepts: [], |
| | | roles: [], |
| | | permissions: [], |
| | | satisfactionCategories:{}, |
| | | // 服务类型 |
| | | Serviceauthority: [ |
| | | { |
| | |
| | | }, |
| | | SET_hisUserId: (state, hisUserId) => { |
| | | state.hisUserId = hisUserId; |
| | | }, |
| | | SET_satisfactionCategories: (state, satisfactionCategories) => { |
| | | state.satisfactionCategories = satisfactionCategories; |
| | | }, |
| | | SET_leaveldeptcodes: (state, belongDepts) => { |
| | | state.belongDepts = belongDepts; |
| | |
| | | commit("SET_nickNAME", user.nickName); |
| | | commit("SET_Id", user.userId); |
| | | commit("SET_hisUserId", user.hisUserId); |
| | | commit("SET_satisfactionCategories", user.satisfactionCategories); |
| | | |
| | | // if (user.userName == "admin") { |
| | | // commit("SET_leaveldeptcodes", []); |
| | |
| | | </div> |
| | | </div> |
| | | <div class="header-right"> |
| | | <!-- 异常选项状态 --> |
| | | <div |
| | | class="option-status" |
| | | v-if=" |
| | | templateForm.templateType != 3 && |
| | | templateForm.templateType != 4 |
| | | " |
| | | > |
| | | <el-tooltip |
| | | :content=" |
| | | checkHasAbnormalOptions(question) |
| | | ? '已有异常选项' |
| | | : '暂无异常选项' |
| | | " |
| | | placement="top" |
| | | > |
| | | <el-tag |
| | | :type=" |
| | | checkHasAbnormalOptions(question) ? 'success' : 'danger' |
| | | " |
| | | size="small" |
| | | class="status-tag" |
| | | > |
| | | <i |
| | | :class=" |
| | | checkHasAbnormalOptions(question) |
| | | ? 'el-icon-success' |
| | | : 'el-icon-warning' |
| | | " |
| | | ></i> |
| | | {{ |
| | | checkHasAbnormalOptions(question) |
| | | ? "异常选项已配置" |
| | | : "无异常选项" |
| | | }} |
| | | </el-tag> |
| | | </el-tooltip> |
| | | </div> |
| | | |
| | | <el-button |
| | | type="text" |
| | | icon="el-icon-view" |
| | |
| | | size="small" |
| | | > |
| | | 预览 |
| | | </el-button> |
| | | |
| | | <!-- 添加配置选项按钮 --> |
| | | <el-button |
| | | v-if=" |
| | | templateForm.templateType != 3 && |
| | | templateForm.templateType != 4 |
| | | " |
| | | type="text" |
| | | icon="el-icon-setting" |
| | | @click="openOptionDialog(question)" |
| | | size="small" |
| | | > |
| | | 配置选项 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- 选项配置对话框 --> |
| | | <el-dialog |
| | | title="选项异常状态配置" |
| | | :visible.sync="optionDialogVisible" |
| | | width="700px" |
| | | center |
| | | :close-on-click-modal="false" |
| | | > |
| | | <div v-if="editingQuestion" class="option-config-wrapper"> |
| | | <div class="dialog-header"> |
| | | <h4>{{ editingQuestion.scriptTopic || '无主题' }}</h4> |
| | | <p class="dialog-subtitle">{{ editingQuestion.scriptContent }}</p> |
| | | </div> |
| | | |
| | | <div class="option-list"> |
| | | <el-alert |
| | | v-if="!currentOptions.some(opt => opt.isabnormal === 1)" |
| | | title="请至少设置一个异常选项(标记为异常)" |
| | | type="warning" |
| | | :closable="false" |
| | | show-icon |
| | | style="margin-bottom: 20px;" |
| | | /> |
| | | |
| | | <div v-for="(option, index) in currentOptions" :key="index" class="option-item"> |
| | | <el-form |
| | | :model="option" |
| | | :rules="optionRules" |
| | | ref="optionForm" |
| | | size="small" |
| | | class="option-form" |
| | | > |
| | | <el-row :gutter="12" align="middle"> |
| | | <el-col :span="2"> |
| | | <div class="option-index">#{{ index + 1 }}</div> |
| | | </el-col> |
| | | |
| | | <el-col :span="12"> |
| | | <el-form-item prop="targetvalue"> |
| | | <el-input |
| | | v-model="option.targetvalue" |
| | | placeholder="请输入选项内容" |
| | | clearable |
| | | maxlength="200" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="6"> |
| | | <el-form-item prop="isabnormal"> |
| | | <el-select |
| | | v-model="option.isabnormal" |
| | | placeholder="选择状态" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="status in abnormalOptions" |
| | | :key="status.value" |
| | | :label="status.label" |
| | | :value="status.value" |
| | | > |
| | | <el-tag :type="status.type" size="small">{{ status.label }}</el-tag> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="4"> |
| | | <el-button |
| | | type="danger" |
| | | icon="el-icon-delete" |
| | | @click="removeOption(index)" |
| | | size="small" |
| | | circle |
| | | plain |
| | | /> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <!-- <el-button |
| | | type="primary" |
| | | icon="el-icon-plus" |
| | | @click="addNewOption" |
| | | size="small" |
| | | plain |
| | | style="width: 100%; margin-top: 10px;" |
| | | > |
| | | 添加选项 |
| | | </el-button> --> |
| | | </div> |
| | | </div> |
| | | |
| | | <span slot="footer" class="dialog-footer"> |
| | | <el-button @click="optionDialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="saveOptions" :loading="savingOptions"> |
| | | 保存配置 |
| | | </el-button> |
| | | </span> |
| | | </el-dialog> |
| | | <!-- 题目预览对话框 --> |
| | | <el-dialog |
| | | title="题目预览" |
| | |
| | | { required: true, message: "请选择模板类型", trigger: "change" }, |
| | | ], |
| | | }, |
| | | // 选项管理相关 |
| | | optionDialogVisible: false, |
| | | currentOptions: [], |
| | | editingQuestion: null, |
| | | optionRules: { |
| | | targetvalue: [ |
| | | { required: true, message: "请输入选项内容", trigger: "blur" }, |
| | | ], |
| | | isabnormal: [ |
| | | { required: true, message: "请选择异常状态", trigger: "change" }, |
| | | ], |
| | | }, |
| | | |
| | | // 异常状态选项 |
| | | abnormalOptions: [ |
| | | { label: "正常", value: 0, type: "success" }, |
| | | { label: "异常", value: 1, type: "danger" }, |
| | | { label: "警告", value: 2, type: "warning" }, |
| | | ], |
| | | // 模板选项 |
| | | questionnaireTemplates: [], // 问卷模板列表 |
| | | followupTemplates: [], // 语音模板列表 |
| | |
| | | |
| | | // 满意度分类ID |
| | | satisfactionCategoryIds: ["404", "405", "406", "10039", "10041", "10042"], |
| | | |
| | | questionnaireCategorys: [], |
| | | voiceCategories: [], |
| | | // 表单验证规则 |
| | | configRules: { |
| | | responsibilityDept: [ |
| | |
| | | satisfactionQuestionsCount() { |
| | | if (this.templateForm.templateType === 1) { |
| | | return this.questionList.filter((q) => |
| | | this.satisfactionCategoryIds.includes(q.categoryid?.toString()) |
| | | this.questionnaireCategorys.includes(q.categoryid) |
| | | ).length; |
| | | } else if (this.templateForm.templateType === 2) { |
| | | return this.questionList.filter((q) => |
| | | this.satisfactionCategoryIds.includes(q.scriptAssortid?.toString()) |
| | | this.voiceCategories.includes(q.scriptAssortid) |
| | | ).length; |
| | | } |
| | | }, |
| | | // 检查题目是否有异常选项 |
| | | hasAbnormalOption(question) { |
| | | return (question) => { |
| | | if (!question) return false; |
| | | |
| | | // 问卷模板 |
| | | if (this.templateForm.templateType === 1) { |
| | | const options = question.svyLibTemplateTargetoptions || []; |
| | | return options.some((opt) => opt.isabnormal === 1); |
| | | } |
| | | // 语音模板 |
| | | else if (this.templateForm.templateType === 2) { |
| | | const options = question.ivrLibaScriptTargetoptionList || []; |
| | | return options.some((opt) => opt.isabnormal === 1); |
| | | } |
| | | |
| | | return false; |
| | | }; |
| | | }, |
| | | // 筛选后的题目列表 |
| | | filteredQuestionList() { |
| | | let filtered = this.questionList; |
| | | console.log(this.questionnaireCategorys); |
| | | |
| | | // 筛选满意度题目 |
| | | if (this.templateForm.templateType === 1) { |
| | | filtered = filtered.filter((q) => |
| | | this.satisfactionCategoryIds.includes(q.categoryid?.toString()) |
| | | this.questionnaireCategorys.includes(q.categoryid) |
| | | ); |
| | | } else if (this.templateForm.templateType === 2) { |
| | | filtered = filtered.filter((q) => |
| | | this.satisfactionCategoryIds.includes(q.scriptAssortid?.toString()) |
| | | this.voiceCategories.includes(q.scriptAssortid) |
| | | ); |
| | | } |
| | | |
| | |
| | | }, |
| | | }, |
| | | created() { |
| | | if (store.getters.satisfactionCategories) { |
| | | this.questionnaireCategorys = |
| | | store.getters.satisfactionCategories.questionnaireCategorys.map( |
| | | (item) => item.categoryid |
| | | ); |
| | | this.voiceCategories = |
| | | store.getters.satisfactionCategories.voiceCategories.map( |
| | | (item) => item.categoryid |
| | | ); |
| | | } |
| | | this.getDeptOptions(); |
| | | this.loadAllTemplates(); |
| | | }, |
| | |
| | | /** 配置变更处理 */ |
| | | handleConfigChange(question) { |
| | | this.$nextTick(() => { |
| | | const index = this.filteredQuestionList.findIndex((q) => q.id === question.id); |
| | | const index = this.filteredQuestionList.findIndex( |
| | | (q) => q.id === question.id |
| | | ); |
| | | if (index !== -1) { |
| | | const formRef = this.$refs.configForm && this.$refs.configForm[index]; |
| | | if (formRef) { |
| | |
| | | async saveSingleConfig(question) { |
| | | if (!question.hasChanges) return; |
| | | |
| | | const index = this.filteredQuestionList.findIndex((q) => q.id === question.id); |
| | | console.log(index,'filteredQuestionList'); |
| | | const index = this.filteredQuestionList.findIndex( |
| | | (q) => q.id === question.id |
| | | ); |
| | | console.log(index, "filteredQuestionList"); |
| | | |
| | | if (index === -1) return; |
| | | |
| | |
| | | }, |
| | | |
| | | /** 重置单个题目配置 */ |
| | | /** 重置单个题目配置 */ |
| | | resetSingleConfig(question) { |
| | | this.$confirm("确定要重置当前题目的配置吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | |
| | | this.previewAnswer = ""; |
| | | this.previewVisible = true; |
| | | }, |
| | | /** 检查题目是否有异常选项 */ |
| | | checkHasAbnormalOptions(question) { |
| | | if (this.templateForm.templateType === 1) { |
| | | return (question.svyLibTemplateTargetoptions || []).some( |
| | | (opt) => opt.isabnormal === 1 |
| | | ); |
| | | } else if (this.templateForm.templateType === 2) { |
| | | return (question.ivrLibaScriptTargetoptionList || []).some( |
| | | (opt) => opt.isabnormal === 1 |
| | | ); |
| | | } |
| | | return false; |
| | | }, |
| | | |
| | | /** 打开选项管理对话框 */ |
| | | openOptionDialog(question) { |
| | | this.editingQuestion = question; |
| | | |
| | | // 复制选项数据 |
| | | if (this.templateForm.templateType === 1) { |
| | | this.currentOptions = JSON.parse( |
| | | JSON.stringify(question.svyLibTemplateTargetoptions || []) |
| | | ).map((opt) => ({ |
| | | ...opt, |
| | | id: opt.id, |
| | | targetvalue: opt.optioncontent || "", |
| | | isabnormal: opt.isabnormal || 0, |
| | | })); |
| | | } else if (this.templateForm.templateType === 2) { |
| | | this.currentOptions = JSON.parse( |
| | | JSON.stringify(question.ivrLibaScriptTargetoptionList || []) |
| | | ).map((opt) => ({ |
| | | ...opt, |
| | | targetvalue: opt.targetvalue || "", |
| | | isabnormal: opt.isabnormal || 0, |
| | | })); |
| | | } |
| | | |
| | | this.optionDialogVisible = true; |
| | | }, |
| | | |
| | | /** 添加新选项 */ |
| | | addNewOption() { |
| | | this.currentOptions.push({ |
| | | id: Date.now(), // 临时ID |
| | | targetvalue: "", |
| | | isabnormal: 0, |
| | | isNew: true, |
| | | }); |
| | | }, |
| | | |
| | | /** 删除选项 */ |
| | | removeOption(index) { |
| | | this.currentOptions.splice(index, 1); |
| | | }, |
| | | |
| | | /** 保存选项配置 */ |
| | | async saveOptions() { |
| | | try { |
| | | // 验证必填项 |
| | | for (const option of this.currentOptions) { |
| | | if (!option.targetvalue || option.targetvalue.trim() === "") { |
| | | this.$message.warning("请填写所有选项内容"); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // 检查是否有异常选项 |
| | | const hasAbnormal = this.currentOptions.some( |
| | | (opt) => opt.isabnormal === 1 |
| | | ); |
| | | |
| | | if (!hasAbnormal) { |
| | | this.$message.warning("请至少设置一个异常选项(isabnormal=1)"); |
| | | return; |
| | | } |
| | | |
| | | // 保存逻辑 - 更新题目对象的选项数据 |
| | | if (this.templateForm.templateType === 1) { |
| | | this.editingQuestion.svyLibTemplateTargetoptions = |
| | | this.currentOptions.map((opt) => ({ |
| | | ...opt, |
| | | optioncontent: opt.targetvalue, |
| | | isabnormal: opt.isabnormal, |
| | | })); |
| | | } else if (this.templateForm.templateType === 2) { |
| | | this.editingQuestion.ivrLibaScriptTargetoptionList = |
| | | this.currentOptions; |
| | | } |
| | | |
| | | // 触发配置变更检查 |
| | | this.handleConfigChange(this.editingQuestion); |
| | | |
| | | this.$message.success("选项配置保存成功"); |
| | | this.optionDialogVisible = false; |
| | | } catch (error) { |
| | | console.error("保存选项失败:", error); |
| | | this.$message.error("保存选项失败"); |
| | | } |
| | | }, |
| | | |
| | | /** 修改保存单个题目配置方法,添加异常选项检查 */ |
| | | async saveSingleConfig(question) { |
| | | // 检查是否有异常选项 |
| | | if (!this.checkHasAbnormalOptions(question)) { |
| | | this.$confirm("该题目没有设置异常选项,是否先配置选项?", "提示", { |
| | | confirmButtonText: "去配置", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | this.openOptionDialog(question); |
| | | }) |
| | | .catch(() => {}); |
| | | return; |
| | | } |
| | | |
| | | // 原有的保存逻辑... |
| | | if (!question.hasChanges) return; |
| | | |
| | | const index = this.filteredQuestionList.findIndex( |
| | | (q) => q.id === question.id |
| | | ); |
| | | |
| | | if (index === -1) return; |
| | | |
| | | const formRef = this.$refs.configForm && this.$refs.configForm[index]; |
| | | if (!formRef) return; |
| | | |
| | | const valid = await formRef.validate(); |
| | | if (!valid) { |
| | | this.$message.warning("请先完成必填项"); |
| | | return; |
| | | } |
| | | |
| | | // 继续原有的保存逻辑... |
| | | question.saving = true; |
| | | question.saveStatus = null; |
| | | |
| | | try { |
| | | // ... 原有的保存逻辑不变 |
| | | } catch (error) { |
| | | // ... 错误处理不变 |
| | | } finally { |
| | | question.saving = false; |
| | | } |
| | | }, |
| | | |
| | | /** 批量保存时也要检查 */ |
| | | async handleBatchSave() { |
| | | if (!this.hasChanges || this.batchSaving) return; |
| | | |
| | | // 检查所有有变更的题目是否都有异常选项 |
| | | const changedQuestions = this.questionList.filter((q) => q.hasChanges); |
| | | const questionsWithoutAbnormal = changedQuestions.filter( |
| | | (q) => !this.checkHasAbnormalOptions(q) |
| | | ); |
| | | |
| | | if (questionsWithoutAbnormal.length > 0) { |
| | | this.$confirm( |
| | | `有 ${questionsWithoutAbnormal.length} 个题目没有设置异常选项,请先配置选项。是否继续?`, |
| | | "提示", |
| | | { |
| | | confirmButtonText: "继续", |
| | | cancelButtonText: "去配置", |
| | | type: "warning", |
| | | } |
| | | ) |
| | | .then(() => { |
| | | // 继续执行批量保存 |
| | | this.executeBatchSave(changedQuestions); |
| | | }) |
| | | .catch(() => { |
| | | // 可以在这里跳转到第一个没有异常选项的题目 |
| | | if (questionsWithoutAbnormal.length > 0) { |
| | | this.openOptionDialog(questionsWithoutAbnormal[0]); |
| | | } |
| | | }); |
| | | } else { |
| | | this.executeBatchSave(changedQuestions); |
| | | } |
| | | }, |
| | | |
| | | /** 执行批量保存 */ |
| | | async executeBatchSave(changedQuestions) { |
| | | this.$confirm("确定要保存所有修改过的配置吗?", "批量保存", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(async () => { |
| | | this.batchSaving = true; |
| | | |
| | | const results = []; |
| | | for (const question of changedQuestions) { |
| | | try { |
| | | // 这里调用修改后的saveSingleConfig方法 |
| | | await this.saveSingleConfig(question); |
| | | results.push({ |
| | | id: question.id, |
| | | success: |
| | | !question.hasChanges && |
| | | question.saveStatus?.type === "success", |
| | | }); |
| | | } catch (error) { |
| | | results.push({ |
| | | id: question.id, |
| | | success: false, |
| | | }); |
| | | } |
| | | } |
| | | |
| | | this.batchSaving = false; |
| | | // ... 后续处理不变 |
| | | }) |
| | | .catch(() => { |
| | | this.batchSaving = false; |
| | | }); |
| | | }, |
| | | |
| | | /** 获取异常选项统计 */ |
| | | getAbnormalStats(question) { |
| | | if (this.templateForm.templateType === 1) { |
| | | const options = question.svyLibTemplateTargetoptions || []; |
| | | return { |
| | | total: options.length, |
| | | abnormal: options.filter((opt) => opt.isabnormal === 1).length, |
| | | warning: options.filter((opt) => opt.isabnormal === 2).length, |
| | | normal: options.filter((opt) => opt.isabnormal === 0).length, |
| | | }; |
| | | } else if (this.templateForm.templateType === 2) { |
| | | const options = question.ivrLibaScriptTargetoptionList || []; |
| | | return { |
| | | total: options.length, |
| | | abnormal: options.filter((opt) => opt.isabnormal === 1).length, |
| | | warning: options.filter((opt) => opt.isabnormal === 2).length, |
| | | normal: options.filter((opt) => opt.isabnormal === 0).length, |
| | | }; |
| | | } |
| | | return { total: 0, abnormal: 0, warning: 0, normal: 0 }; |
| | | }, |
| | | /** 搜索 */ |
| | | handleQuery() { |
| | | // 仅筛选显示,不需要重新加载 |
| | |
| | | } |
| | | |
| | | .header-right { |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: flex-end; |
| | | gap: 8px; |
| | | |
| | | .option-status { |
| | | .status-tag { |
| | | cursor: default; |
| | | |
| | | i { |
| | | margin-right: 4px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | } |
| | | .option-config-wrapper { |
| | | .dialog-header { |
| | | margin-bottom: 20px; |
| | | padding-bottom: 15px; |
| | | border-bottom: 1px solid #ebeef5; |
| | | |
| | | h4 { |
| | | margin: 0 0 8px 0; |
| | | color: #303133; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .dialog-subtitle { |
| | | margin: 0; |
| | | color: #606266; |
| | | font-size: 13px; |
| | | line-height: 1.4; |
| | | } |
| | | } |
| | | |
| | | .option-list { |
| | | .option-item { |
| | | margin-bottom: 12px; |
| | | padding: 12px; |
| | | background: #f8f9fa; |
| | | border-radius: 4px; |
| | | border: 1px solid #ebeef5; |
| | | |
| | | &:hover { |
| | | border-color: #dcdfe6; |
| | | } |
| | | |
| | | .option-form { |
| | | .option-index { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | height: 100%; |
| | | color: #909399; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | @media (max-width: 768px) { |
| | | .satisfaction-exception-config { |
| | | padding: 12px; |
| | |
| | | label-width="100px" |
| | | class="query-form" |
| | | > |
| | | <el-form-item label="患者来源" prop="patientSource"> |
| | | <el-form-item label="统计类型" prop="patientSource"> |
| | | <el-select |
| | | v-model="queryParams.patientSource" |
| | | placeholder="请选择患者来源" |
| | | v-model="queryParams.type" |
| | | placeholder="请选择统计类型" |
| | | clearable |
| | | style="width: 200px" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="source in patientSourceList" |
| | | :key="source.value" |
| | | :label="source.label" |
| | | :value="source.value" |
| | | /> |
| | | <el-option label="问卷类型" :value="2" /> |
| | | <el-option label="语音类型" :value="1" /> |
| | | <el-option label="全部" :value="null" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | |
| | | clearable |
| | | filterable |
| | | style="width: 200px" |
| | | @change="handleDeptChange" |
| | | > |
| | | <el-option |
| | | v-for="dept in deptList" |
| | |
| | | clearable |
| | | filterable |
| | | style="width: 200px" |
| | | @change="handleWardChange" |
| | | > |
| | | <el-option |
| | | v-for="ward in wardList" |
| | |
| | | min-width="300" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | <span>{{ row.scriptContent }}?</span> |
| | | <span>{{ row.scriptContent }}</span> |
| | | <el-tag |
| | | :type="row.scriptType === 1 ? 'primary' : 'success'" |
| | | size="mini" |
| | |
| | | width="100" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | {{ row.totalCount - row.answerCount }} |
| | | {{ row.noAnswerPerson }} |
| | | </template> |
| | | </el-table-column> |
| | | |
| | |
| | | width="100" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | {{ formatPercent(row.answerCount / row.totalCount) }} |
| | | {{ formatPercent(row.answerRate) }} |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | |
| | | <!-- 综合得分行 --> |
| | | <div class="summary-row"> |
| | | <div class="summary-content"> |
| | | <!-- <div class="summary-item"> |
| | | <span class="label">综合得分:</span> |
| | | <span class="value">{{ totalScore.toFixed(1) }}</span> |
| | | </div> --> |
| | | <div class="summary-item"> |
| | | <span class="label">总答题人数:</span> |
| | | <span class="value">{{ totalAnswerCount }}</span> |
| | |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="最高分" |
| | | prop="maxScore" |
| | | align="center" |
| | | width="120" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | <span class="score-text">{{ |
| | | row.maxScore.toFixed(1) |
| | | }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="最低分" |
| | | prop="minScore" |
| | | align="center" |
| | | width="120" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | <span class="score-text">{{ |
| | | row.minScore.toFixed(1) |
| | | }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="满意度等级" |
| | | prop="satisfactionLevel" |
| | | align="center" |
| | |
| | | |
| | | <script> |
| | | import * as echarts from "echarts"; |
| | | import { statistics, satisfactionGraph } from "@/api/system/user"; |
| | | import store from "@/store"; |
| | | |
| | | export default { |
| | | name: "SatisfactionStatistics", |
| | |
| | | return { |
| | | // 查询参数 |
| | | queryParams: { |
| | | type: 2, |
| | | patientSource: "", |
| | | deptCode: "", |
| | | wardCode: "", |
| | |
| | | // 各类型统计明细数据 |
| | | typeDetailData: [], |
| | | |
| | | // 柱状图数据 |
| | | chartData: [], |
| | | |
| | | // 统计信息 |
| | | totalSendCount: 12560, |
| | | totalReceiveCount: 10240, |
| | | totalSendCount: 0, |
| | | totalReceiveCount: 0, |
| | | overallRecoveryRate: 0, |
| | | |
| | | // 类型统计汇总 |
| | |
| | | { id: 403, name: "门诊满意度", color: "#409EFF" }, |
| | | { id: 404, name: "常用满意度", color: "#FF9D4D" }, |
| | | ], |
| | | |
| | | // 新增:默认服务类型数组 |
| | | defaultServiceTypes: ["6", "14", "15", "16"], |
| | | |
| | | // 新增:基础模板问题ID集合 |
| | | scriptIds: [], |
| | | |
| | | // 新增:模板ID |
| | | templateId: null, |
| | | }; |
| | | }, |
| | | |
| | | computed: { |
| | | // 计算查询开始时间 |
| | | startTime() { |
| | | if (this.queryParams.dateRange && this.queryParams.dateRange[0]) { |
| | | return this.queryParams.dateRange[0]; |
| | | } |
| | | // 默认最近7天 |
| | | const date = new Date(); |
| | | date.setDate(date.getDate() - 7); |
| | | return this.formatDate(date); |
| | | }, |
| | | |
| | | // 计算查询结束时间 |
| | | endTime() { |
| | | if (this.queryParams.dateRange && this.queryParams.dateRange[1]) { |
| | | return this.queryParams.dateRange[1]; |
| | | } |
| | | // 默认今天 |
| | | return this.formatDate(new Date()); |
| | | }, |
| | | |
| | | // 计算科室编码数组 |
| | | deptCodes() { |
| | | if (this.queryParams.deptCode) { |
| | | return [this.queryParams.deptCode]; |
| | | } |
| | | return this.deptList.map((dept) => dept.value); |
| | | }, |
| | | |
| | | // 计算病区编码数组 |
| | | hospitalDistrictCodes() { |
| | | if (this.queryParams.wardCode) { |
| | | return [this.queryParams.wardCode]; |
| | | } |
| | | return this.wardList.map((ward) => ward.value); |
| | | }, |
| | | }, |
| | | |
| | | mounted() { |
| | |
| | | }, |
| | | |
| | | methods: { |
| | | // 格式化日期 |
| | | formatDate(date) { |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(date.getDate()).padStart(2, "0"); |
| | | return `${year}-${month}-${day}`; |
| | | }, |
| | | |
| | | // 初始化数据 |
| | | async initData() { |
| | | await this.getDeptList(); |
| | |
| | | // 获取科室列表 |
| | | getDeptList() { |
| | | return new Promise((resolve) => { |
| | | setTimeout(() => { |
| | | this.deptList = [ |
| | | { value: "dept001", label: "心血管内科" }, |
| | | { value: "dept002", label: "神经内科" }, |
| | | { value: "dept003", label: "普外科" }, |
| | | { value: "dept004", label: "骨科" }, |
| | | { value: "dept005", label: "妇产科" }, |
| | | { value: "dept006", label: "儿科" }, |
| | | ]; |
| | | this.deptList = (this.$store.getters.belongDepts || []).map((dept) => { |
| | | return { |
| | | label: dept.deptName, |
| | | value: dept.deptCode, |
| | | }; |
| | | }); |
| | | resolve(); |
| | | }, 100); |
| | | }); |
| | | }, |
| | | |
| | | // 获取病区列表 |
| | | getWardList() { |
| | | return new Promise((resolve) => { |
| | | setTimeout(() => { |
| | | this.wardList = [ |
| | | { value: "ward001", label: "内科一病区" }, |
| | | { value: "ward002", label: "内科二病区" }, |
| | | { value: "ward003", label: "外科一病区" }, |
| | | { value: "ward004", label: "外科二病区" }, |
| | | { value: "ward005", label: "妇产科病区" }, |
| | | { value: "ward006", label: "儿科病区" }, |
| | | ]; |
| | | this.wardList = (this.$store.getters.belongWards || []).map((ward) => { |
| | | return { |
| | | label: ward.districtName, |
| | | value: ward.districtCode, |
| | | }; |
| | | }); |
| | | resolve(); |
| | | }, 100); |
| | | }); |
| | | }, |
| | | |
| | |
| | | async loadChartData() { |
| | | this.loading = true; |
| | | try { |
| | | // 模拟API调用 |
| | | const chartData = await this.generateChartData(); |
| | | this.renderChart(chartData); |
| | | const params = { |
| | | type: this.queryParams.type, |
| | | startTime: this.startTime, |
| | | endTime: this.endTime, |
| | | deptcodes: this.deptCodes, |
| | | hospitaldistrictcodes: this.hospitalDistrictCodes, |
| | | templateid: this.templateId, |
| | | }; |
| | | |
| | | // 计算总体回收率 |
| | | this.overallRecoveryRate = this.totalReceiveCount / this.totalSendCount; |
| | | const response = await satisfactionGraph(params); |
| | | |
| | | if (response.code === 200) { |
| | | this.processChartData(response); |
| | | } else { |
| | | this.$message.error(response.msg || "获取图表数据失败"); |
| | | // 使用mock数据 |
| | | await this.generateMockChartData(); |
| | | } |
| | | } catch (error) { |
| | | console.error("获取图表数据出错:", error); |
| | | this.$message.error("获取图表数据失败"); |
| | | // 错误时使用mock数据 |
| | | await this.generateMockChartData(); |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | | // 处理图表数据 |
| | | processChartData(apiData) { |
| | | if (!apiData || !apiData.rows || Object.keys(apiData.rows).length === 0) { |
| | | this.chartData = []; |
| | | this.totalSendCount = 0; |
| | | this.totalReceiveCount = 0; |
| | | this.overallRecoveryRate = 0; |
| | | this.renderChart([]); |
| | | return; |
| | | } |
| | | |
| | | const chartData = []; |
| | | let totalSend = 0; |
| | | let totalReceive = 0; |
| | | let index = 0; |
| | | |
| | | // 处理接口返回的满意度类型统计 |
| | | Object.entries(apiData.rows).forEach(([typeName, typeStat]) => { |
| | | const sendCount = typeStat.subidAll || 0; |
| | | const receiveCount = typeStat.fillCountAll || 0; |
| | | const recoveryRate = typeStat.receiveRate || 0; |
| | | |
| | | chartData.push({ |
| | | name: typeName, |
| | | value: recoveryRate * 100, // 转换为百分比 |
| | | sendCount: sendCount, |
| | | receiveCount: receiveCount, |
| | | averageScore: typeStat.averageScore || 0, |
| | | itemStyle: { color: this.getChartColor(index) }, |
| | | }); |
| | | |
| | | totalSend += sendCount; |
| | | totalReceive += receiveCount; |
| | | index++; |
| | | }); |
| | | |
| | | this.totalSendCount = totalSend; |
| | | this.totalReceiveCount = totalReceive; |
| | | this.overallRecoveryRate = totalSend > 0 ? totalReceive / totalSend : 0; |
| | | this.chartData = chartData; |
| | | |
| | | this.renderChart(chartData); |
| | | }, |
| | | |
| | | // 加载题目明细数据 |
| | | async loadQuestionDetailData() { |
| | | this.detailLoading = true; |
| | | try { |
| | | const params = { |
| | | type: this.queryParams.type, |
| | | startTime: this.startTime, |
| | | endTime: this.endTime, |
| | | scriptids: this.scriptIds, |
| | | templateid: this.templateId, |
| | | }; |
| | | |
| | | const response = await statistics(params); |
| | | |
| | | if (response.code === 200) { |
| | | this.processQuestionDetailData(response.rows); |
| | | } else { |
| | | this.$message.error(response.msg || "获取题目明细数据失败"); |
| | | const mockData = await this.generateMockQuestionDetail(); |
| | | this.questionDetailData = mockData.list; |
| | | this.detailTotal = mockData.total; |
| | | this.calculateSummary(mockData); |
| | | } |
| | | } catch (error) { |
| | | console.error("获取题目明细数据出错:", error); |
| | | this.$message.error("获取题目明细数据失败"); |
| | | const mockData = await this.generateMockQuestionDetail(); |
| | | this.questionDetailData = mockData.list; |
| | | this.detailTotal = mockData.total; |
| | |
| | | } |
| | | }, |
| | | |
| | | // 处理接口返回的题目明细数据 |
| | | processQuestionDetailData(apiData) { |
| | | if (!apiData || !apiData.patSatisfactionDetailEntities) { |
| | | this.questionDetailData = []; |
| | | this.detailTotal = 0; |
| | | this.totalAnswerCount = 0; |
| | | this.totalAnswerRate = 0; |
| | | return; |
| | | } |
| | | |
| | | const detailData = apiData.patSatisfactionDetailEntities.map((item) => { |
| | | const options = []; |
| | | if (item.matchedtextStats) { |
| | | Object.keys(item.matchedtextStats).forEach((key) => { |
| | | const stat = item.matchedtextStats[key]; |
| | | options.push({ |
| | | optionText: key, |
| | | chosenQuantity: stat.count || 0, |
| | | chosenPercentage: (stat.ratio || 0) / 100, |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | return { |
| | | scriptContent: item.scriptContent || "", |
| | | scriptType: 1, |
| | | answerPerson: item.answerPerson || 0, |
| | | noAnswerPerson: item.noAnswerPerson || 0, |
| | | answerCount: item.answerPerson || 0, |
| | | averageScore: item.averageScore || 0, |
| | | maxScore: item.maxScore || 0, |
| | | minScore: item.minScore || 0, |
| | | answerRate: item.answerRate || 0, |
| | | totalCount: (item.answerPerson || 0) + (item.noAnswerPerson || 0), |
| | | options: options, |
| | | }; |
| | | }); |
| | | |
| | | const startIndex = |
| | | (this.detailQueryParams.pageNum - 1) * this.detailQueryParams.pageSize; |
| | | const endIndex = startIndex + this.detailQueryParams.pageSize; |
| | | const paginatedData = detailData.slice(startIndex, endIndex); |
| | | |
| | | this.questionDetailData = paginatedData; |
| | | this.detailTotal = detailData.length; |
| | | this.totalAnswerCount = apiData.totalPerson || 0; |
| | | this.totalAnswerRate = apiData.totalAnswerRate || 0; |
| | | }, |
| | | |
| | | // 加载类型明细数据 |
| | | async loadTypeDetailData() { |
| | | this.typeDetailLoading = true; |
| | | try { |
| | | const params = { |
| | | type: this.queryParams.type, |
| | | startTime: this.startTime, |
| | | endTime: this.endTime, |
| | | deptcodes: this.deptCodes, |
| | | hospitaldistrictcodes: this.hospitalDistrictCodes, |
| | | templateid: this.templateId, |
| | | }; |
| | | |
| | | const response = await satisfactionGraph(params); |
| | | |
| | | if (response.code === 200) { |
| | | this.processTypeDetailData(response.data); |
| | | } else { |
| | | this.$message.error(response.msg || "获取类型明细数据失败"); |
| | | const mockData = await this.generateMockTypeDetail(); |
| | | this.typeDetailData = mockData; |
| | | |
| | | // 计算类型统计汇总 |
| | | this.calculateTypeSummary(mockData); |
| | | } |
| | | } catch (error) { |
| | | console.error("获取类型明细数据出错:", error); |
| | | this.$message.error("获取类型明细数据失败"); |
| | | const mockData = await this.generateMockTypeDetail(); |
| | | this.typeDetailData = mockData; |
| | | this.calculateTypeSummary(mockData); |
| | | } finally { |
| | | this.typeDetailLoading = false; |
| | | } |
| | | }, |
| | | |
| | | // 处理类型明细数据 |
| | | processTypeDetailData(apiData) { |
| | | if (!apiData || !apiData.rows || Object.keys(apiData.rows).length === 0) { |
| | | this.typeDetailData = []; |
| | | this.calculateTypeSummary([]); |
| | | return; |
| | | } |
| | | |
| | | const typeDetail = []; |
| | | Object.entries(apiData.rows).forEach(([typeName, typeStat], index) => { |
| | | const sendCount = typeStat.subidAll || 0; |
| | | const receiveCount = typeStat.fillCountAll || 0; |
| | | const recoveryRate = typeStat.receiveRate || 0; |
| | | const averageScore = typeStat.averageScore || 0; |
| | | |
| | | typeDetail.push({ |
| | | id: index + 1, |
| | | typeName: typeName, |
| | | isSpecial: false, // 根据实际情况判断 |
| | | sendCount: sendCount, |
| | | receiveCount: receiveCount, |
| | | recoveryRate: recoveryRate, |
| | | averageScore: averageScore, |
| | | maxScore: 5, // 默认值 |
| | | minScore: 0, // 默认值 |
| | | satisfactionLevel: this.getSatisfactionLevel(averageScore), |
| | | trend: "stable", // 默认稳定 |
| | | }); |
| | | }); |
| | | |
| | | this.typeDetailData = typeDetail; |
| | | this.calculateTypeSummary(typeDetail); |
| | | }, |
| | | |
| | | // 根据平均分获取满意度等级 |
| | | getSatisfactionLevel(score) { |
| | | if (score >= 4.5) return "优秀"; |
| | | if (score >= 4.0) return "良好"; |
| | | if (score >= 3.0) return "一般"; |
| | | if (score >= 2.0) return "较差"; |
| | | return "差"; |
| | | }, |
| | | |
| | | // 获取趋势 |
| | | getTrend(trend) { |
| | | if (trend > 0.1) return "up"; |
| | | if (trend < -0.1) return "down"; |
| | | return "stable"; |
| | | }, |
| | | |
| | | // 计算综合得分 |
| | |
| | | window.addEventListener("resize", this.handleChartResize); |
| | | }, |
| | | |
| | | // 生成图表数据 |
| | | generateChartData() { |
| | | return new Promise((resolve) => { |
| | | setTimeout(() => { |
| | | const data = this.satisfactionTypes.map((type) => ({ |
| | | name: type.name, |
| | | recoveryRate: Math.random() * 0.3 + 0.6, // 60%-90%的回收率 |
| | | sendCount: Math.floor(Math.random() * 3000) + 1500, // 1500-4500 |
| | | receiveCount: 0, |
| | | color: type.color, |
| | | })); |
| | | |
| | | // 计算回收数量 |
| | | data.forEach((item) => { |
| | | item.receiveCount = Math.floor(item.sendCount * item.recoveryRate); |
| | | }); |
| | | |
| | | // 更新总量 |
| | | this.totalSendCount = data.reduce( |
| | | (sum, item) => sum + item.sendCount, |
| | | 0 |
| | | ); |
| | | this.totalReceiveCount = data.reduce( |
| | | (sum, item) => sum + item.receiveCount, |
| | | 0 |
| | | ); |
| | | |
| | | resolve({ |
| | | data: data.map((item) => ({ |
| | | name: item.name, |
| | | value: item.recoveryRate * 100, // 转换为百分比 |
| | | sendCount: item.sendCount, |
| | | receiveCount: item.receiveCount, |
| | | itemStyle: { color: item.color }, |
| | | })), |
| | | }); |
| | | }, 300); |
| | | }); |
| | | }, |
| | | |
| | | // 渲染图表 |
| | | renderChart(chartData) { |
| | | if (!this.barChart) return; |
| | | |
| | | if (!chartData || chartData.length === 0) { |
| | | const emptyOption = { |
| | | title: { |
| | | text: "暂无数据", |
| | | left: "center", |
| | | top: "center", |
| | | textStyle: { |
| | | color: "#999", |
| | | fontSize: 16, |
| | | fontWeight: "normal" |
| | | } |
| | | }, |
| | | xAxis: { show: false }, |
| | | yAxis: { show: false } |
| | | }; |
| | | this.barChart.setOption(emptyOption); |
| | | return; |
| | | } |
| | | const option = { |
| | | title: { |
| | | text: "", |
| | |
| | | }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: chartData.data.map((item) => item.name), |
| | | data: chartData.map((item) => item.name), |
| | | axisLabel: { |
| | | interval: 0, |
| | | rotate: 0, |
| | |
| | | name: "填报比例", |
| | | type: "bar", |
| | | barWidth: 40, |
| | | data: chartData.data, |
| | | data: chartData, |
| | | itemStyle: { |
| | | color: (params) => { |
| | | return params.data.itemStyle.color; |
| | |
| | | this.barChart.setOption(option); |
| | | }, |
| | | |
| | | // 生成Mock图表数据 |
| | | generateMockChartData() { |
| | | return new Promise((resolve) => { |
| | | setTimeout(() => { |
| | | const data = this.satisfactionTypes.map((type, index) => ({ |
| | | name: type.name, |
| | | recoveryRate: Math.random() * 0.3 + 0.6, |
| | | sendCount: Math.floor(Math.random() * 3000) + 1500, |
| | | receiveCount: 0, |
| | | color: type.color, |
| | | })); |
| | | |
| | | data.forEach((item) => { |
| | | item.receiveCount = Math.floor(item.sendCount * item.recoveryRate); |
| | | }); |
| | | |
| | | this.totalSendCount = data.reduce( |
| | | (sum, item) => sum + item.sendCount, |
| | | 0 |
| | | ); |
| | | this.totalReceiveCount = data.reduce( |
| | | (sum, item) => sum + item.receiveCount, |
| | | 0 |
| | | ); |
| | | this.overallRecoveryRate = |
| | | this.totalSendCount > 0 |
| | | ? this.totalReceiveCount / this.totalSendCount |
| | | : 0; |
| | | |
| | | const chartData = data.map((item) => ({ |
| | | name: item.name, |
| | | value: item.recoveryRate * 100, |
| | | sendCount: item.sendCount, |
| | | receiveCount: item.receiveCount, |
| | | itemStyle: { color: item.color }, |
| | | })); |
| | | |
| | | this.chartData = chartData; |
| | | this.renderChart(chartData); |
| | | resolve(); |
| | | }, 300); |
| | | }); |
| | | }, |
| | | |
| | | // 生成Mock题目详情数据 |
| | | generateMockQuestionDetail() { |
| | | return new Promise((resolve) => { |
| | | setTimeout(() => { |
| | | const questions = [ |
| | | { |
| | | scriptContent: "您对医护人员的服务态度是否满意", |
| | | scriptContent: "您对本次就医的整体满意程度?", |
| | | scriptType: 1, |
| | | totalCount: 156, |
| | | answerPerson: 120, |
| | | noAnswerPerson: 30, |
| | | answerCount: 120, |
| | | totalCount: 150, |
| | | averageScore: 4.2, |
| | | maxScore: 5.0, |
| | | minScore: 1.0, |
| | | answerRate: 0.8, |
| | | options: [ |
| | | { |
| | | optionText: "非常满意", |
| | | chosenQuantity: 60, |
| | | chosenPercentage: 0.5, |
| | | }, |
| | | { |
| | | optionText: "满意", |
| | | chosenQuantity: 36, |
| | | chosenPercentage: 0.3, |
| | | }, |
| | | { |
| | | optionText: "一般", |
| | | chosenQuantity: 18, |
| | | chosenPercentage: 0.15, |
| | | }, |
| | | { |
| | | optionText: "不满意", |
| | | chosenQuantity: 6, |
| | | chosenPercentage: 0.05, |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | scriptContent: "您对医护人员的服务态度是否满意?", |
| | | scriptType: 1, |
| | | answerPerson: 145, |
| | | noAnswerPerson: 11, |
| | | answerCount: 145, |
| | | totalCount: 156, |
| | | averageScore: 4.5, |
| | | maxScore: 5, |
| | | minScore: 3, |
| | | answerRate: 0.93, |
| | | options: [ |
| | | { |
| | | optionText: "非常满意", |
| | |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | scriptContent: "您对医生的诊疗水平和技术能力评价如何", |
| | | scriptType: 1, |
| | | totalCount: 156, |
| | | answerCount: 142, |
| | | averageScore: 4.7, |
| | | maxScore: 5, |
| | | minScore: 3, |
| | | options: [ |
| | | { |
| | | optionText: "非常专业", |
| | | chosenQuantity: 95, |
| | | chosenPercentage: 0.67, |
| | | }, |
| | | { |
| | | optionText: "比较专业", |
| | | chosenQuantity: 40, |
| | | chosenPercentage: 0.28, |
| | | }, |
| | | { |
| | | optionText: "一般", |
| | | chosenQuantity: 5, |
| | | chosenPercentage: 0.04, |
| | | }, |
| | | { |
| | | optionText: "不够专业", |
| | | chosenQuantity: 2, |
| | | chosenPercentage: 0.01, |
| | | }, |
| | | { |
| | | optionText: "非常不专业", |
| | | chosenQuantity: 0, |
| | | chosenPercentage: 0, |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | scriptContent: "您对医院的环境和卫生状况是否满意", |
| | | scriptType: 1, |
| | | totalCount: 156, |
| | | answerCount: 138, |
| | | averageScore: 4.3, |
| | | maxScore: 5, |
| | | minScore: 2, |
| | | options: [ |
| | | { |
| | | optionText: "非常满意", |
| | | chosenQuantity: 75, |
| | | chosenPercentage: 0.54, |
| | | }, |
| | | { |
| | | optionText: "满意", |
| | | chosenQuantity: 50, |
| | | chosenPercentage: 0.36, |
| | | }, |
| | | { |
| | | optionText: "一般", |
| | | chosenQuantity: 10, |
| | | chosenPercentage: 0.07, |
| | | }, |
| | | { |
| | | optionText: "不满意", |
| | | chosenQuantity: 3, |
| | | chosenPercentage: 0.02, |
| | | }, |
| | | { |
| | | optionText: "非常不满意", |
| | | chosenQuantity: 0, |
| | | chosenPercentage: 0, |
| | | }, |
| | | ], |
| | | }, |
| | | ]; |
| | | |
| | | const startIndex = |
| | |
| | | generateMockTypeDetail() { |
| | | return new Promise((resolve) => { |
| | | setTimeout(() => { |
| | | // 在 generateMockTypeDetail 方法中替换为: |
| | | const types = [ |
| | | { |
| | | id: 401, |
| | |
| | | }); |
| | | }, |
| | | |
| | | // 获取图表颜色 |
| | | getChartColor(index) { |
| | | const colors = [ |
| | | "#36B37E", |
| | | "#4CAF50", |
| | | "#409EFF", |
| | | "#FF9D4D", |
| | | "#9B8DFF", |
| | | "#FF6B6B", |
| | | ]; |
| | | return colors[index % colors.length]; |
| | | }, |
| | | |
| | | // 处理图表响应式 |
| | | handleChartResize() { |
| | | if (this.barChart) { |
| | |
| | | handleSearch() { |
| | | this.detailQueryParams.pageNum = 1; |
| | | this.loadData(); |
| | | // 强制重新渲染图表 |
| | | setTimeout(() => { |
| | | if (this.chartData.length === 0) { |
| | | this.renderChart([]); |
| | | } |
| | | }, 100); |
| | | }, |
| | | |
| | | // 处理重置 |
| | |
| | | // 处理类型详情 |
| | | handleTypeDetail(row) { |
| | | this.$message.info(`查看类型详情:${row.typeName}`); |
| | | // 这里可以跳转到详情页面或打开详情对话框 |
| | | }, |
| | | |
| | | // 处理导出数据 |
| | | handleExportData(row) { |
| | | this.$message.success(`正在导出 ${row.typeName} 数据...`); |
| | | // 这里可以实现导出逻辑 |
| | | }, |
| | | |
| | | // 格式化百分比 |
| | |
| | | if (value === null || value === undefined) return "-"; |
| | | const num = parseFloat(value); |
| | | if (isNaN(num)) return "-"; |
| | | return `${(num * 100).toFixed(2)}%`; |
| | | // 如果值小于1,认为是小数比例,需要乘以100 |
| | | const percentValue = num < 1 ? num * 100 : num; |
| | | return `${percentValue.toFixed(2)}%`; |
| | | }, |
| | | |
| | | // 获取回收率样式类 |
| | |
| | | }; |
| | | return levelMap[level] || "info"; |
| | | }, |
| | | |
| | | // 处理科室选择变化 |
| | | handleDeptChange() { |
| | | this.loadData(); |
| | | }, |
| | | |
| | | // 处理病区选择变化 |
| | | handleWardChange() { |
| | | this.loadData(); |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | |
| | | this.objyl.suitway = this.objyl.suitway.join(","); |
| | | } |
| | | this.objyl.templateid = this.objyl.id; |
| | | this.form.libtemplateid = this.objyl.id; |
| | | |
| | | this.objyl.isoperation = 1; |
| | | this.objyl.ivrLibaTemplateScriptVOList.forEach((item) => { |
| | | item.ivrTaskScriptTargetoptionList = |
| | |
| | | [process.env.VUE_APP_BASE_API]: { |
| | | // target: `https://www.health-y.cn/lssf`, |
| | | // target: `http://192.168.100.10:8096`, |
| | | target: `http://192.168.100.10:8094`,//省立同德 |
| | | // target: `http://192.168.100.10:8094`,//省立同德 |
| | | // target: `http://192.168.100.10:8095`,//新华 |
| | | // target:`http://localhost:8095`, |
| | | target:`http://localhost:8095`, |
| | | // target:`http://35z1t16164.qicp.vip`, |
| | | // target: `http://192.168.100.172:8095`, |
| | | // target: `http://192.168.101.166:8093`, |