From dfd32b299236e015fe80e10fae01548288d91000 Mon Sep 17 00:00:00 2001
From: liusheng <337615773@qq.com>
Date: 星期一, 02 十一月 2026 09:56:32 +0800
Subject: [PATCH] Merge branch 'subtask' of http://116.62.18.175:6699/r/~yxh/smartor-web into subtask

---
 tmp-img/crop1.png                                 |    0 
 dist.zip                                          |    0 
 src/views/followvisit/mzsatisfaction/index.vue    |   12 ++
 vue.config.js                                     |    5 +
 src/views/followvisit/record/detailpage/index.vue |  180 +++++++++++++++++++++++++++++++++++
 package.json                                      |    1 
 src/components/Pagination/index.vue               |    5 
 src/components/StatisticsCards/index.vue          |   10 ++
 src/views/followvisit/zysatisfaction/index.vue    |   12 ++
 tmp-img/crop.py                                   |   11 ++
 .npmrc                                            |    4 
 src/views/followvisit/discharge/index.vue         |   29 ++++-
 12 files changed, 257 insertions(+), 12 deletions(-)

diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..1eab6d1
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1,4 @@
+# Node 17+ 鐨� OpenSSL 3 绉婚櫎浜� md4锛寃ebpack4 / compression-webpack-plugin@5 寤� hash 鏃朵細鎶�
+# error:0308010C:digital envelope routines::unsupported
+# 杩欓噷璁� npm 璺戣剼鏈椂鑷姩甯︿笂 legacy provider锛屾瀯寤哄懡浠や笉鐢ㄥ啀鎵嬪姩璁剧幆澧冨彉閲�
+node-options=--openssl-legacy-provider
diff --git a/dist.zip b/dist.zip
new file mode 100644
index 0000000..27108ba
--- /dev/null
+++ b/dist.zip
Binary files differ
diff --git a/package.json b/package.json
index 343f698..c8787ef 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
   "scripts": {
     "dev": "vue-cli-service serve",
     "build:prod": "vue-cli-service build",
+    "build:prod:legacy": "set NODE_OPTIONS=--openssl-legacy-provider&& vue-cli-service build",
     "build:stage": "vue-cli-service build --mode staging",
     "preview": "node build/index.js --preview",
     "lint": "eslint --ext .js,.vue src"
diff --git a/src/components/Pagination/index.vue b/src/components/Pagination/index.vue
index 3dc5b9f..39fc7c9 100644
--- a/src/components/Pagination/index.vue
+++ b/src/components/Pagination/index.vue
@@ -79,7 +79,10 @@
     },
     pageSize: {
       get() {
-        return this.limit
+        // limit 鍙兘琚�滃鍑衡�濈瓑鎿嶄綔涓存椂缃负 null锛岀洿鎺ラ�忎紶浼氳 el-pagination 绠楀嚭
+        // pageCount = total / 0 = Infinity锛岄〉鐮侀噷灏卞嚭鐜� 鈥渋nfinity鈥濄�傝繖閲屽厹搴曟垚 10
+        const size = Number(this.limit)
+        return size > 0 ? size : 10
       },
       set(val) {
         this.$emit('update:limit', val)
diff --git a/src/components/StatisticsCards/index.vue b/src/components/StatisticsCards/index.vue
index 3f072b2..8365c09 100644
--- a/src/components/StatisticsCards/index.vue
+++ b/src/components/StatisticsCards/index.vue
@@ -114,6 +114,7 @@
         鏃犻渶闅忚: "no-follow-card",
         闇�闅忚: "need-follow-card",
         寰呴殢璁�: "pending-card",
+        鍙戦�佸け璐�: "failed-card",
         宸插畬鎴�: "completed-card",
         浣忛櫌鎬婚噺: "total-card",
         闂ㄨ瘖鎬婚噺: "total-card",
@@ -255,6 +256,15 @@
 }
 
 /* 寮傚父 - 娴呯孩鑹� */
+/* 鍙戦�佸け璐� - 娴呯孩 */
+.failed-card .el-card__body {
+  background: #ffeaea;
+  border-radius: 10px;
+}
+.failed-card .card-value {
+  color: #e04040;
+}
+
 .error-card .el-card__body {
   background: #fce4ec;
   border-radius: 10px;
diff --git a/src/views/followvisit/discharge/index.vue b/src/views/followvisit/discharge/index.vue
index fb47a9e..3fb51d4 100644
--- a/src/views/followvisit/discharge/index.vue
+++ b/src/views/followvisit/discharge/index.vue
@@ -1249,9 +1249,12 @@
             { value: 1, label: "琚鍙�" },
             { value: 2, label: "寰呭彂閫�" },
             { value: 3, label: "宸插彂閫�" },
-            { value: 5, label: "鍙戦�佸け璐�" },
             { value: 7, label: "瓒呮椂" },
           ],
+        },
+        {
+          value: 40, // 涓�绾э細鍙戦�佸け璐ワ紙鍗曠嫭鍒楀嚭锛屾柟渚跨洿鎺ョ瓫锛�
+          label: "鍙戦�佸け璐�",
         },
         {
           value: 20, // 涓�绾э細宸插畬鎴�
@@ -1303,6 +1306,10 @@
         },
         {
           name: "寰呴殢璁�",
+          value: 0,
+        },
+        {
+          name: "鍙戦�佸け璐�",
           value: 0,
         },
         {
@@ -1816,9 +1823,9 @@
         this.cardlist[2].value = response.rows[0].ysf;
         this.ycvalue = Number(response.rows[0].yc) || 0;
         this.jgvalue = response.rows[0].jg;
-        this.cardlist[3].value = response.rows[0].fssb;
-        this.cardlist[4].value = response.rows[0].dsf;
-        // this.cardlist[5].value = response.rows[0].yfs2;
+        this.cardlist[3].value = response.rows[0].dsf;
+        this.cardlist[4].value = response.rows[0].fssb;
+        this.cardlist[5].value = response.rows[0].ywc;
         this.yfsvalue = response.rows[0].yfs;
         this.loading = false;
         this.userList.forEach((item) => {
@@ -2060,6 +2067,11 @@
             break;
         }
         this.topqueryParams.sendstate = null;
+        // 涓�绾с�屽彂閫佸け璐ャ�嶏細鎸夌姸鎬� 5 鐩存帴杩囨护锛堜笉绛夊悓浜庡緟闅忚锛�
+        if (value === 40) {
+          this.topqueryParams.sendstateView = null;
+          this.topqueryParams.sendstate = 5;
+        }
       } else {
         // 閫変腑浜嗕簩绾э紙鍏蜂綋鐘舵�侊級
         this.topqueryParams.sendstateView = null;
@@ -2438,11 +2450,16 @@
       try {
         const res = await getTaskservelist(query);
         const row = (res && res.rows && res.rows[0]) || {};
+        // 鎺ュ彛娌¤繑鍥炵粺璁¤鏃朵繚鎸佸師鍊硷紝閬垮厤涓�娆″紓甯告妸鍗$墖鍏ㄥ埛鎴� 0
+        if (!res || !res.rows || !res.rows.length) {
+          return;
+        }
         this.cardlist[0].value = Number(row.wxsf || 0) + Number(row.xsf || 0);
         this.cardlist[1].value = Number(row.wxsf) || 0;
         this.cardlist[2].value = Number(row.xsf) || 0;
         this.cardlist[3].value = Number(row.dsf) || 0;
-        this.cardlist[4].value = Number(row.ywc) || 0;
+        this.cardlist[4].value = Number(row.fssb) || 0;
+        this.cardlist[5].value = Number(row.ywc) || 0;
         this.ycvalue = Number(row.yc) || 0;
         this.jgvalue = Number(row.jg) || 0;
       } catch (e) {
@@ -2472,6 +2489,8 @@
         query.sendstateView = 3;
       } else if (name === "寰呴殢璁�") {
         query.sendstateView = 1;
+      } else if (name === "鍙戦�佸け璐�") {
+        query.sendstates = [5];
       } else if (name === "宸插畬鎴�") {
         query.sendstateView = 2;
       } else if (name === "闇�闅忚") {
diff --git a/src/views/followvisit/mzsatisfaction/index.vue b/src/views/followvisit/mzsatisfaction/index.vue
index d50dc34..ddb0e30 100644
--- a/src/views/followvisit/mzsatisfaction/index.vue
+++ b/src/views/followvisit/mzsatisfaction/index.vue
@@ -382,7 +382,8 @@
           align="center"
           key="updateBy"
           prop="updateBy"
-          width="120"
+          min-width="120"
+          :show-overflow-tooltip="true"
         />
         <el-table-column
           label="闂ㄨ瘖婊℃剰搴︽ā鏉垮悕绉�"
@@ -1811,6 +1812,9 @@
     },
     /** 瀵煎嚭鎸夐挳鎿嶄綔 */
     handleExport() {
+      // 瀵煎嚭闇�瑕佸叏閲忔暟鎹紝涓存椂鍘绘帀鍒嗛〉锛涘鍑哄悗蹇呴』杩樺師锛屽惁鍒欏垎椤典細绠楁垚 Infinity锛堥〉鐮佹樉绀� infinity锛�
+      const originalPageNum = this.topqueryParams.pageNum;
+      const originalPageSize = this.topqueryParams.pageSize;
       this.topqueryParams.pageNum = null;
       this.topqueryParams.pageSize = null;
       this.download(
@@ -1819,7 +1823,11 @@
           ...this.topqueryParams,
         },
         `user_${new Date().getTime()}.xlsx`
-      );
+      ).finally(() => {
+        this.topqueryParams.pageNum = originalPageNum;
+        this.topqueryParams.pageSize = originalPageSize;
+        this.getList();
+      });
     },
     // 寮傚父鍒楁覆鏌�
     tableRowClassName({ row, rowIndex }) {
diff --git a/src/views/followvisit/record/detailpage/index.vue b/src/views/followvisit/record/detailpage/index.vue
index 1f704a4..61ae393 100644
--- a/src/views/followvisit/record/detailpage/index.vue
+++ b/src/views/followvisit/record/detailpage/index.vue
@@ -639,10 +639,16 @@
                   <el-button
                     v-if="showQuestionnaireForm"
                     type="primary"
+                    :disabled="!canSaveQuestionnaire"
+                    :loading="savingQuestion"
                     @click="getdetail"
                     >淇濆瓨闂嵎璇︽儏</el-button
                   >
-                  <el-button v-else type="primary" @click="yuyingetdetail"
+                  <el-button
+                    v-else
+                    type="primary"
+                    :loading="savingQuestion"
+                    @click="yuyingetdetail"
                     >淇濆瓨闂嵎璇︽儏</el-button
                   >
                 </div>
@@ -700,10 +706,15 @@
                   <el-button
                     v-if="Voicetype"
                     type="primary"
+                    :loading="savingVoice"
                     @click="yuyingetdetail"
                     >淇濆瓨闅忚璇︽儏</el-button
                   >
-                  <el-button v-else type="primary" @click="getdetail"
+                  <el-button
+                    v-else
+                    type="primary"
+                    :loading="savingQuestion"
+                    @click="getdetail"
                     >淇濆瓨闅忚璇︽儏</el-button
                   >
                 </div>
@@ -712,6 +723,38 @@
           </div>
         </div>
       </div>
+
+      <!-- 鏈�夐�夐」鎻愮ず锛氬ぇ瀛楅啋鐩彁閱掞紝鍏佽鐢ㄦ埛鍧氭寔淇濆瓨 -->
+      <el-dialog
+        :visible.sync="confirmSaveVisible"
+        width="640px"
+        append-to-body
+        :close-on-click-modal="false"
+        custom-class="unanswered-dialog"
+      >
+        <div slot="title" class="unanswered-title">
+          <i class="el-icon-warning"></i> 娉ㄦ剰锛氭湁棰樼洰娌℃湁閫夋嫨閫夐」
+        </div>
+        <div class="unanswered-warn">
+          褰撳墠闂嵎杩樻湁
+          <span class="unanswered-count">{{ unansweredQuestions.length }}</span>
+          閬撻<strong>娌℃湁閫夋嫨浠讳綍閫夐」</strong>锛岀户缁繚瀛樺皢鎸夆�滄湭浣滅瓟鈥濊褰曪細
+        </div>
+        <div class="unanswered-list">
+          <div v-for="q in unansweredQuestions" :key="q.index">
+            绗� {{ q.index }} 棰橈細{{ q.title }}
+          </div>
+        </div>
+        <div class="unanswered-tip">纭畾杩樿淇濆瓨鍚楋紵</div>
+        <span slot="footer">
+          <el-button size="medium" @click="confirmSaveVisible = false"
+            >杩斿洖琛ュ~</el-button
+          >
+          <el-button type="danger" size="medium" @click="confirmForceSave"
+            >浠嶈淇濆瓨</el-button
+          >
+        </span>
+      </el-dialog>
       <!-- 浜哄伐澶勭悊 -->
       <div class="manual-action">
         <div class="Followuserinfos">
@@ -1589,6 +1632,12 @@
         "棰勭害涓嬫澶嶈瘖",
       ],
       tableDatatop: [], //棰樼洰琛�
+      // 淇濆瓨闂嵎锛氭湭绛旈鎻愮ず寮圭獥 / 鐢ㄦ埛鍧氭寔淇濆瓨鏍囪
+      confirmSaveVisible: false,
+      forceSave: false,
+      // 闃查噸澶嶆彁浜わ細淇濆瓨涓鐢ㄦ寜閽�
+      savingQuestion: false,
+      savingVoice: false,
       voiceDatatop: [], //棰樼洰琛�
       dynamicTags: [],
       isMergeMode: false,
@@ -1742,6 +1791,26 @@
     }
   },
   computed: {
+    // 鏄惁鏈変换鎰忎竴閬撻銆岄�変簡閫夐」銆嶆垨銆屽~浜嗗娉ㄣ��
+    hasAnyAnswer() {
+      return (this.tableDatatop || []).some((item) => this.isQuestionAnswered(item));
+    },
+    // 鍙充晶澶勭悊鎰忚/澶囨敞妗嗘槸鍚︽湁鍐呭
+    hasRemarkText() {
+      const remark = this.form && this.form.remark;
+      return remark !== null && remark !== undefined && String(remark).trim() !== "";
+    },
+    // 淇濆瓨闂嵎璇︽儏鐨勫彲鐐规潯浠讹細闂嵎鏈変綔绛� 鎴� 鍙宠竟澶囨敞妗嗘湁鍐呭锛堟湁鍏朵竴鍗冲彲锛�
+    canSaveQuestionnaire() {
+      return this.hasAnyAnswer || this.hasRemarkText;
+    },
+    // 娌℃湁閫夋嫨閫夐」鐨勯鐩紙淇濆瓨鍓嶇敤澶у瓧閱掔洰鎻愮ず锛屼笉鍖哄垎鏈夋病鏈夊~澶囨敞锛�
+    // index 鐢ㄩ鐩湪闂嵎閲岀殑鍘熷搴忓彿锛屾柟渚垮拰宸﹁竟闂嵎瀵圭収
+    unansweredQuestions() {
+      return (this.tableDatatop || [])
+        .map((item, i) => ({ index: i + 1, title: item.scriptContent || "鏈懡鍚嶉鐩�", raw: item }))
+        .filter((q) => !this.hasOptionValue(q.raw));
+    },
     callStatusText() {
       if (this.customCallStatus) {
         return this.customCallStatus;
@@ -2153,8 +2222,52 @@
         }
       });
     },
+    // 棰樼洰鏄惁閫変簡閫夐」锛堝崟閫夊瓧绗︿覆 / 澶氶�夋暟缁勶級
+    hasOptionValue(item) {
+      if (!item) {
+        return false;
+      }
+      const val = item.scriptResult;
+      if (Array.isArray(val)) {
+        return val.some(
+          (v) => v !== null && v !== undefined && String(v).trim() !== ""
+        );
+      }
+      return val !== null && val !== undefined && String(val).trim() !== "";
+    },
+    // 棰樼洰鏄惁濉簡澶囨敞
+    hasRemarkValue(item) {
+      if (!item) {
+        return false;
+      }
+      const remark = item.answerps;
+      return (
+        remark !== null && remark !== undefined && String(remark).trim() !== ""
+      );
+    },
+    // 棰樼洰鏄惁宸蹭綔绛旓細閫変簡閫夐」 鎴� 濉簡澶囨敞锛堜袱鑰呮湁鍏朵竴鍗崇畻宸茬瓟锛�
+    isQuestionAnswered(item) {
+      return this.hasOptionValue(item) || this.hasRemarkValue(item);
+    },
+    // 鏈瓟棰樻彁绀洪噷鐐广�屼粛瑕佷繚瀛樸��
+    confirmForceSave() {
+      this.forceSave = true;
+      this.confirmSaveVisible = false;
+      this.getdetail();
+    },
     // 鍖绘姢浜哄憳瀛樺偍鏁版嵁
     getdetail() {
+      // 闂嵎娌′綔绛斻�佸彸杈瑰娉ㄦ涔熺┖ 鈫� 涓嶅厑璁镐繚瀛�
+      if (!this.canSaveQuestionnaire) {
+        this.$message.warning("璇峰厛鍦ㄩ棶鍗蜂腑浣滅瓟锛屾垨鍦ㄥ彸渚у~鍐欏鐞嗘剰瑙�/澶囨敞");
+        return;
+      }
+      // 杩樻湁棰樼洰娌′綔绛旓細澶у瓧鎻愮ず锛岀敤鎴峰潥鎸佹墠缁х画
+      if (this.unansweredQuestions.length > 0 && !this.forceSave) {
+        this.confirmSaveVisible = true;
+        return;
+      }
+      this.forceSave = false;
       console.log(1);
       let excep = "";
 
@@ -2209,6 +2322,11 @@
         param6: this.id,
         type: 2,
       };
+      // 闃查噸澶嶆彁浜わ細淇濆瓨涓啀娆$偣鍑荤洿鎺ュ拷鐣�
+      if (this.savingQuestion) {
+        return;
+      }
+      this.savingQuestion = true;
       savequestiondetail(obj).then((res) => {
         if (res.code == 200) {
           this.$modal.msgSuccess("鏈嶅姟淇濆瓨鎴愬姛");
@@ -2251,6 +2369,8 @@
               });
           }
         }
+      }).finally(() => {
+        this.savingQuestion = false;
       });
     },
     Torouter() {
@@ -2554,6 +2674,11 @@
         type: 1,
       };
       console.log("c", 4);
+      // 闃查噸澶嶆彁浜わ細淇濆瓨涓啀娆$偣鍑荤洿鎺ュ拷鐣�
+      if (this.savingVoice) {
+        return;
+      }
+      this.savingVoice = true;
       const orgName = localStorage.getItem("orgname");
       console.log(orgName, "orgName");
 
@@ -2596,6 +2721,8 @@
               this.Torouter();
             });
         }
+      }).finally(() => {
+        this.savingVoice = false;
       });
     },
     // 鍐嶆闅忚鏁版嵁鏇存浛
@@ -4431,4 +4558,53 @@
     margin-bottom: 12px;
   }
 }
+
+/* 鏈�夐�夐」鎻愮ず锛氬ぇ瀛楅啋鐩彁閱� */
+.unanswered-title {
+  font-size: 22px;
+  font-weight: 700;
+  color: #f56c6c;
+  display: flex;
+  align-items: center;
+}
+.unanswered-title i {
+  font-size: 26px;
+  margin-right: 10px;
+}
+.unanswered-warn {
+  font-size: 20px;
+  font-weight: 600;
+  color: #303133;
+  line-height: 1.8;
+}
+.unanswered-warn strong {
+  color: #f56c6c;
+  font-size: 22px;
+}
+.unanswered-count {
+  font-size: 40px;
+  font-weight: 700;
+  color: #f56c6c;
+  margin: 0 6px;
+  vertical-align: middle;
+}
+.unanswered-list {
+  margin-top: 14px;
+  max-height: 300px;
+  overflow-y: auto;
+  padding: 12px 16px;
+  background: #fef0f0;
+  border: 1px solid #fbc4c4;
+  border-radius: 6px;
+  font-size: 18px;
+  line-height: 2;
+  color: #303133;
+}
+.unanswered-tip {
+  margin-top: 18px;
+  font-size: 22px;
+  font-weight: 700;
+  color: #f56c6c;
+  text-align: center;
+}
 </style>
diff --git a/src/views/followvisit/zysatisfaction/index.vue b/src/views/followvisit/zysatisfaction/index.vue
index 78603be..6e8b081 100644
--- a/src/views/followvisit/zysatisfaction/index.vue
+++ b/src/views/followvisit/zysatisfaction/index.vue
@@ -413,7 +413,8 @@
           align="center"
           key="updateBy"
           prop="updateBy"
-          width="120"
+          min-width="120"
+          :show-overflow-tooltip="true"
         />
         <el-table-column
           label="浣忛櫌婊℃剰搴︽ā鏉垮悕绉�"
@@ -1899,6 +1900,9 @@
     },
     /** 瀵煎嚭鎸夐挳鎿嶄綔 */
     handleExport() {
+      // 瀵煎嚭闇�瑕佸叏閲忔暟鎹紝涓存椂鍘绘帀鍒嗛〉锛涘鍑哄悗蹇呴』杩樺師锛屽惁鍒欏垎椤典細绠楁垚 Infinity锛堥〉鐮佹樉绀� infinity锛�
+      const originalPageNum = this.topqueryParams.pageNum;
+      const originalPageSize = this.topqueryParams.pageSize;
       this.topqueryParams.pageNum = null;
       this.topqueryParams.pageSize = null;
       this.download(
@@ -1907,7 +1911,11 @@
           ...this.buildScopePayload(this.topqueryParams),
         },
         `user_${new Date().getTime()}.xlsx`
-      );
+      ).finally(() => {
+        this.topqueryParams.pageNum = originalPageNum;
+        this.topqueryParams.pageSize = originalPageSize;
+        this.getList();
+      });
     },
     // 寮傚父鍒楁覆鏌�
     tableRowClassName({ row, rowIndex }) {
diff --git a/tmp-img/crop.py b/tmp-img/crop.py
new file mode 100644
index 0000000..3355a32
--- /dev/null
+++ b/tmp-img/crop.py
@@ -0,0 +1,11 @@
+from PIL import Image
+src = r"C:\Users\86176\.dsh\attachments\v1\objects\20\20844c3f5c9a090e1010da3d62ae091792c3e25fad6bdd38cc991b054f020d95"
+out = r"D:\daima\smartor\smartor-web\tmp-img\crop1.png"
+img = Image.open(src)
+print("size", img.size)
+# 绠ご澶撮儴闄勮繎鍖哄煙
+box = (1050, 130, 1450, 420)
+crop = img.crop(box)
+crop = crop.resize((crop.width * 3, crop.height * 3), Image.LANCZOS)
+crop.save(out)
+print("saved", out, crop.size)
diff --git a/tmp-img/crop1.png b/tmp-img/crop1.png
new file mode 100644
index 0000000..9b71f8f
--- /dev/null
+++ b/tmp-img/crop1.png
Binary files differ
diff --git a/vue.config.js b/vue.config.js
index fe4bda0..822740d 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -63,6 +63,11 @@
   },
   configureWebpack: {
     name: name,
+    // Node 17+ 鐨� OpenSSL 3 鍘绘帀浜� md4锛寃ebpack4 榛樿鐢� md4 绠� hash 浼氭姤
+    // error:0308010C:digital envelope routines::unsupported锛岃繖閲屾崲鎴� sha256
+    output: {
+      hashFunction: 'sha256'
+    },
     resolve: {
       alias: {
         '@': resolve('src')

--
Gitblit v1.9.3