张田田 3 mesi fa
parent
commit
f36b82595a

+ 5 - 0
miniprogram/app.json

@@ -60,6 +60,11 @@
       "root": "module/follow",
       "root": "module/follow",
       "pages": ["pages/evaluation/report"]
       "pages": ["pages/evaluation/report"]
     },
     },
+    {
+      "name": "satisfaction",
+      "root": "module/satisfaction",
+      "pages": ["pages/questionnaire/questionnaire"]
+    },
     {
     {
       "name": "care",
       "name": "care",
       "root": "module/care",
       "root": "module/care",

+ 2 - 2
miniprogram/app.ts

@@ -12,8 +12,8 @@ App<IAppOption>({
   },
   },
   onLaunch(options: WechatMiniprogram.App.LaunchShowOption) {
   onLaunch(options: WechatMiniprogram.App.LaunchShowOption) {
     appUpdate(true);
     appUpdate(true);
-    const query = useRouteQuery(options.query.scene);
-    const scene = decodeURIComponent(query.scene);
+    const scene = options.query.scene;
+    const query = useRouteQuery(scene);
     wx.setStorageSync("scene", scene);
     wx.setStorageSync("scene", scene);
     const doctorId = query.ys;
     const doctorId = query.ys;
     this.globalData.doctorId = doctorId;
     this.globalData.doctorId = doctorId;

+ 0 - 1
miniprogram/lib/request/create.ts

@@ -48,7 +48,6 @@ export function createRequest(option: IRequestCreateConfig) {
     header["appId"] = miniProgram.appId ?? "";
     header["appId"] = miniProgram.appId ?? "";
     header["version"] = miniProgram.version ?? "";
     header["version"] = miniProgram.version ?? "";
     header["env"] = miniProgram.envVersion ?? "";
     header["env"] = miniProgram.envVersion ?? "";
-
     const promise = _request<IRequestData<T>>({
     const promise = _request<IRequestData<T>>({
       url: /https?\:\/\//.test(url) ? url : `${baseURL}${url}`,
       url: /https?\:\/\//.test(url) ? url : `${baseURL}${url}`,
       header,
       header,

+ 16 - 0
miniprogram/module/satisfaction/pages/questionnaire/questionnaire.json

@@ -0,0 +1,16 @@
+{
+  "renderer": "skyline",
+  "navigationBarTextStyle": "white",
+  "navigationBarBackgroundColor": "#0f2226",
+  "backgroundColor": "#0f2226",
+  "backgroundColorContent": "#ffffff",
+  "backgroundColorTop": "#0f2226",
+  "backgroundColorBottom": "#0f2226",
+  "component": true,
+  "usingComponents": {
+    "t-icon": "tdesign-miniprogram/icon/icon",
+    "t-navbar": "tdesign-miniprogram/navbar/navbar",
+    "t-radio": "tdesign-miniprogram/radio/radio",
+    "t-radio-group": "tdesign-miniprogram/radio-group/radio-group"
+  }
+}

+ 208 - 0
miniprogram/module/satisfaction/pages/questionnaire/questionnaire.scss

@@ -0,0 +1,208 @@
+.page-container {
+  background-color: #f5f5f5;
+  height: calc(100vh - 64px);
+  box-sizing: border-box;
+}
+
+.questionnaire-container {
+  padding: 32rpx;
+  padding-bottom: 180rpx; // 为底部固定按钮留出足够空间(按钮高度88rpx + padding 48rpx + 安全区域 + 额外空间)
+  background-color: #fff;
+  min-height: 100%;
+}
+
+/* 标题 */
+.questionnaire-title {
+  font-size: 40rpx;
+  font-weight: bold;
+  color: #333;
+  text-align: center;
+  margin-bottom: 32rpx;
+}
+
+/* 介绍文字 */
+.questionnaire-intro {
+  font-size: 28rpx;
+  color: black;
+  line-height: 1.8;
+  margin-bottom: 32rpx;
+  text-align: justify;
+}
+
+/* 分数说明 */
+.score-explanation {
+  margin-bottom: 32rpx;
+}
+
+.score-explanation-text {
+  font-size: 28rpx;
+  color: #333;
+  line-height: 1.6;
+  font-weight: bold;
+}
+
+/* 问题组 */
+.question-groups {
+  margin-bottom: 32rpx;
+}
+
+.question-group {
+  margin-bottom: 32rpx;
+}
+
+.group-title {
+  font-size: 35rpx;
+  font-weight: bold;
+  color: #333;
+}
+
+/* 问题列表 */
+.question-list {
+  display: flex;
+  flex-direction: column;
+  gap: 32rpx;
+}
+
+.question-item {
+  padding-bottom: 32rpx;
+  border-bottom: 1px solid #f0f0f0;
+
+  &:last-child {
+    border-bottom: none;
+  }
+}
+
+.question-name {
+  font-size: 28rpx;
+  color: #333;
+  line-height: 1.6;
+  font-weight: bold;
+  margin: 20rpx 0 20rpx 0;
+}
+
+/* 分数选择 */
+.score-radio-group {
+  width: 100%;
+}
+
+.score-options {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  flex-wrap: wrap;
+}
+
+.score-radio-item {
+  margin-right: 32rpx;
+
+  &:last-child {
+    margin-right: 0;
+  }
+}
+
+/* 其他意见 */
+.other-comment-section {
+  margin-top: 32rpx;
+  margin-bottom: 32rpx;
+}
+
+.other-comment-title {
+  font-size: 28rpx;
+  color: #333;
+  margin-bottom: 16rpx;
+}
+
+.other-comment-input {
+  width: 100%;
+  min-height: 200rpx;
+  padding: 20rpx;
+  background-color: #f8f8f8;
+  border: 2rpx solid #e0e0e0;
+  border-radius: 8rpx;
+  font-size: 26rpx;
+  color: #333;
+  box-sizing: border-box;
+  line-height: 1.6;
+}
+
+/* 结束语 */
+.questionnaire-end {
+  font-size: 26rpx;
+  color: black;
+  margin-top: 22rpx;
+  margin-bottom: 32rpx;
+  padding-top: 32rpx;
+  border-top: 1px solid #f0f0f0;
+}
+
+/* 加载状态 */
+.loading-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  min-height: 400rpx;
+  padding: 32rpx;
+}
+
+.loading-text {
+  font-size: 28rpx;
+  color: #999;
+}
+
+/* 空数据状态 */
+.empty-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  min-height: 400rpx;
+  padding: 32rpx;
+}
+
+.empty-text {
+  font-size: 28rpx;
+  color: #999;
+}
+
+/* 保存按钮 */
+.save-button-container {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 24rpx 32rpx;
+  padding-bottom: calc(env(safe-area-inset-bottom));
+  background-color: #fff;
+  border-top: 1px solid #f0f0f0;
+  box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
+  z-index: 100;
+  box-sizing: border-box;
+}
+
+.save-button {
+  width: 100%;
+  height: 88rpx;
+  line-height: 88rpx;
+  background: linear-gradient(135deg, #1d6ff6 0%, #0d5dd8 100%);
+  border-radius: 44rpx;
+  color: #fff;
+  font-size: 32rpx;
+  font-weight: bold;
+  border: none;
+  display: block;
+  text-align: center;
+  transition: all 0.3s;
+
+  &::after {
+    border: none;
+  }
+
+  &[disabled] {
+    background: #ccc;
+    opacity: 0.6;
+  }
+
+  &:not([disabled]):active {
+    opacity: 0.8;
+    transform: scale(0.98);
+  }
+}

+ 232 - 0
miniprogram/module/satisfaction/pages/questionnaire/questionnaire.ts

@@ -0,0 +1,232 @@
+// import { Get } from "../../../../lib/request/method";
+import {
+  getQuestionnaireMethod,
+  submitQuestionnaireMethod,
+} from "../../request";
+
+export interface Questionnaire {
+  id: number; //	满意度调研问卷ID
+  name: string; //满意度调研问卷名称
+  startSentence: string; //开头语
+  endSentence: string; //结束语
+  suggestion: string; //其他意见
+  groups: Array<{
+    name: string; //	问题组名称
+    items: Array<{
+      name: string;
+      score?: string; // 原始分数(可选)
+      selectedScore?: number | string; // 用户选择的分数
+    }>;
+  }>;
+}
+Page({
+  onLoad(options: { id?: string }) {
+    console.log(options, "options");
+    // 如果没有传递 id,使用默认 id 1,或者可以根据实际情况调整
+    const questionnaireId = options.id ? Number(options.id) : 1;
+    this.setData({
+      "followDetail.id": questionnaireId,
+    });
+    console.log(this.data.followDetail.id, "this.data.followDetail.id");
+    console.log(questionnaireId, "questionnaireId");
+    if (questionnaireId) {
+      this.getQuestionnaire(questionnaireId);
+    } else {
+      wx.showToast({
+        title: "问卷ID不能为空",
+        icon: "none",
+        duration: 2000,
+      });
+    }
+  },
+
+  data: {
+    scoreList: [
+      {
+        label: "1分",
+        value: "1",
+      },
+      {
+        label: "2分",
+        value: "2",
+      },
+      {
+        label: "3分",
+        value: "3",
+      },
+      {
+        label: "4分",
+        value: "4",
+      },
+      {
+        label: "5分",
+        value: "5",
+      },
+    ],
+    followDetail: {
+      id: 0,
+    } as Questionnaire,
+    loading: true, // 加载状态
+    showEmpty: false, // 显示空数据
+    saving: false, // 保存状态
+    suggestion: "", // 其他意见
+  },
+
+  async getQuestionnaire(id: number) {
+    // 模拟后端数据(根据图片内容)
+    // const mockData: Questionnaire = {
+    //   id: id,
+    //   name: "就诊体验满意度",
+    //   startSentence:
+    //     "感谢您选择我院就诊,为了持续改进医疗服务质量,我们诚挚地邀请您参与本次满意度调研。您的真实反馈对我们非常重要,我们将严格保密您的个人信息,请您根据实际就诊体验,花费几分钟时间完成本次调研。",
+    //   endSentence:
+    //     "再次感谢您的参与,我们会根据您的反馈持续改进服务质量再次感谢您的参与,我们会根据您的反馈持续改进服务质量。再次感谢您的参与,我们会根据您的反馈持续改进服务质量再次感谢您的参与,我们会根据您的反馈持续改进服务质量再次感谢您的参与,我们会根据您的反馈持续改进服务质量再次感谢您的参与,我们会根据您的反馈持续改进服务质量",
+    //   suggestion: "其他意见",
+    //   groups: [
+    //     {
+    //       name: "一、就诊环境评价",
+    //       items: [
+    //         {
+    //           name: "医院门诊/住院部的整洁度",
+    //           score: "1",
+    //         },
+    //         {
+    //           name: "医院的通风情况",
+    //           score: "2",
+    //         },
+    //         {
+    //           name: "医院的采光情况",
+    //           score: "3",
+    //         },
+    //         {
+    //           name: "医院的噪音控制",
+    //           score: "4",
+    //         },
+    //         {
+    //           name: "候诊区域的舒适度(座椅、空间等)",
+    //           score: "5",
+    //         },
+    //       ],
+    //     },
+    //   ],
+    // };
+
+    // 模拟网络延迟
+    // await new Promise((resolve) => setTimeout(resolve, 500));
+
+    // this.setData({
+    //   followDetail: mockData,
+    //   loading: false,
+    // });
+    // 实际接口调用
+    let list = await getQuestionnaireMethod(id);
+    console.log(list, "list");
+    const questionnaireData = list?.data as Questionnaire;
+    // 确保 id 字段存在,如果后端没有返回 id,使用传入的 id
+    const finalData = questionnaireData
+      ? { ...questionnaireData, id: questionnaireData.id || id }
+      : ({ id } as Questionnaire);
+    this.setData({
+      loading: false,
+      followDetail: finalData,
+      showEmpty: JSON.stringify(list?.data) === "{}" ? true : false,
+    });
+    console.log(this.data.followDetail, "this.data.followDetail");
+  },
+
+  // 选择分数(radio-group change 事件)
+  onScoreChange(e: WechatMiniprogram.CustomEvent) {
+    console.log(e, "改变分数");
+    const { groupIndex, itemIndex } = e.currentTarget.dataset;
+    const score = e.detail.value;
+    const groups = [...this.data.followDetail.groups];
+    groups[groupIndex].items[itemIndex].selectedScore = score;
+
+    this.setData({
+      "followDetail.groups": groups,
+    });
+  },
+
+  // 输入其他意见
+  onCommentInput(e: WechatMiniprogram.Input) {
+    this.setData({
+      suggestion: e.detail.value,
+    });
+  },
+
+  // 保存问卷
+  async onSave() {
+    // 检查是否所有问题都已回答
+    const groups = this.data.followDetail?.groups || [];
+    // let allAnswered = true;
+    // let unansweredQuestions: string[] = [];
+    console.log(groups, "groups");
+    // for (const group of groups) {
+    //   for (const item of group.items) {
+    //     if (!item.selectedScore) {
+    //       allAnswered = false;
+    //       unansweredQuestions.push(item.name);
+    //     }
+    //   }
+    // }
+
+    // if (!allAnswered) {
+    //   wx.showToast({
+    //     title: "请完成所有问题的评分",
+    //     icon: "none",
+    //     duration: 2000,
+    //   });
+    //   return;
+    // }
+    console.log(this.data.followDetail.id, "提交=====");
+    // 准备提交数据
+    const submitData = {
+      groups: groups.map((group) => ({
+        name: group.name,
+        items: group.items.map((item) => ({
+          name: item.name,
+          score: item.selectedScore ? String(item.selectedScore) : "",
+        })),
+      })),
+      startSentence: this.data.followDetail.startSentence,
+      endSentence: this.data.followDetail.endSentence,
+      suggestion: this.data.suggestion,
+      name: this.data.followDetail.name,
+    };
+    console.log(submitData, "提交的数据==submitData");
+    this.setData({
+      saving: true,
+    });
+
+    try {
+      // 实际接口调用
+      await submitQuestionnaireMethod(
+        this.data.followDetail.id,
+        submitData as AnyObject
+      );
+      wx.showToast({
+        title: "提交成功",
+        icon: "success",
+        duration: 2000,
+      });
+      setTimeout(() => {
+        wx.redirectTo({
+          url: "/pages/home/home",
+        });
+      }, 2000);
+    } catch (error) {
+      wx.showToast({
+        title: "保存失败,请重试",
+        icon: "none",
+        duration: 2000,
+      });
+      this.setData({
+        saving: false,
+      });
+    } finally {
+      this.setData({
+        saving: false,
+      });
+    }
+  },
+});

+ 68 - 0
miniprogram/module/satisfaction/pages/questionnaire/questionnaire.wxml

@@ -0,0 +1,68 @@
+<t-navbar title="满意度问卷" left-arrow />
+<scroll-view class="page-container" scroll-y>
+  <!-- 加载状态 -->
+  <view class="loading-container" wx:if="{{loading}}">
+    <view class="loading-text">加载中...</view>
+  </view>
+
+  <!-- 问卷内容 -->
+  <view class="questionnaire-container" wx:elif="{{!showEmpty}}">
+    <!-- 标题 -->
+    <view class="questionnaire-title">{{followDetail.name || '满意度问卷'}}</view>
+
+    <!-- 介绍文字 -->
+    <view class="questionnaire-intro" wx:if="{{followDetail.startSentence}}">
+      {{followDetail.startSentence}}
+    </view>
+
+    <!-- 分数说明 -->
+    <view class="score-explanation">
+      <text class="score-explanation-text">分数说明:1分表示非常不满意,2分表示不满意,3分表示一般,4分表示满意,5分表示非常满意</text>
+    </view>
+
+    <!-- 问题组 -->
+    <view class="question-groups" wx:if="{{followDetail.groups}}">
+      <view class="question-group" wx:for="{{followDetail.groups}}" wx:key="index" wx:for-index="groupIndex">
+        <!-- 问题组标题 -->
+        <view class="group-title">{{item.name}}</view>
+
+        <!-- 问题列表 -->
+        <view class="question-list">
+          <view class="question-item" wx:for="{{item.items}}" wx:key="index" wx:for-index="itemIndex">
+            <!-- 问题名称 -->
+            <view class="question-name">{{itemIndex + 1}}、{{item.name}}</view>
+
+            <!-- 分数选择(单选) -->
+            <t-radio-group value="{{item.selectedScore || item.score}}" borderless t-class="box" bind:change="onScoreChange" data-group-index="{{groupIndex}}" data-item-index="{{itemIndex}}">
+              <t-radio block="{{false}}" :label="{{item.label}}" :value="{{item.value}}" wx:for="{{scoreList}}" wx:key="value" style="margin-right:20rpx" />
+            </t-radio-group>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 其他意见 -->
+    <view class="other-comment-section">
+      <view class="other-comment-title">其他意见:</view>
+      <textarea class="other-comment-input" placeholder="请给我们留言您的其他意见" value="{{suggestion}}" bindinput="onCommentInput" maxlength="500" show-confirm-bar="{{false}}" />
+    </view>
+
+    <!-- 结束语 -->
+    <view class="questionnaire-end">
+      {{followDetail.endSentence}}
+    </view>
+  </view>
+
+  <!-- 无数据提示 -->
+  <view class="empty-container" wx:elif="{{showEmpty}}">
+    <view class="empty-text">暂无问卷数据</view>
+  </view>
+</scroll-view>
+
+<!-- 保存按钮 -->
+<view class="save-button-container" wx:if="{{!showEmpty}}">
+  <button class="save-button" bindtap="onSave" disabled="{{saving}}">
+    <text wx:if="{{saving}}">保存中...</text>
+    <text wx:else>保存</text>
+  </button>
+</view>

+ 20 - 0
miniprogram/module/satisfaction/request.ts

@@ -0,0 +1,20 @@
+import { Get, Post } from "../../lib/request/method";
+
+// 获取问卷详情
+export function getQuestionnaireMethod(id: number) {
+  return Post(`/satisfiesyManage/getContent/${id}`, {
+    transform({ data }: AnyObject) {
+      return data;
+    },
+  });
+}
+// 满意度调研问卷提交
+export function submitQuestionnaireMethod(id: number, data: AnyObject) {
+  console.log(data, "data===");
+  return Post(`/satisfiesyManage/submitContent/${id}`, {
+    ...data,
+    transform({ data }: AnyObject) {
+      return data;
+    },
+  });
+}

+ 1 - 1
miniprogram/pages/home/home.ts

@@ -166,7 +166,7 @@ Page({
     appUpdate();
     appUpdate();
     const query = useRouteQuery(options.scene!);
     const query = useRouteQuery(options.scene!);
     if (query.ys) wx.setStorageSync("doctorId", query.ys);
     if (query.ys) wx.setStorageSync("doctorId", query.ys);
-    if (query.scene) wx.setStorageSync("scene", query.scene);
+    if (options.scene) wx.setStorageSync("scene", options.scene);
     this.initFabAnimated();
     this.initFabAnimated();
     if (options.switchType) {
     if (options.switchType) {
       this.setData({
       this.setData({

+ 1 - 1
miniprogram/pages/mine/mine.scss

@@ -27,7 +27,7 @@
   left: 0;
   left: 0;
   right: 0;
   right: 0;
   bottom: 0;
   bottom: 0;
-  z-index: 100;
+  // z-index: 100;
   background-color: #fff;
   background-color: #fff;
   padding-bottom: env(safe-area-inset-bottom);
   padding-bottom: env(safe-area-inset-bottom);
   box-shadow: 0 -2rpx 6rpx rgba(0, 0, 0, 0.1);
   box-shadow: 0 -2rpx 6rpx rgba(0, 0, 0, 0.1);