From 2140c5aae2032673cef7055df9738b9bacf37816 Mon Sep 17 00:00:00 2001
From: WXL <wl_5969728@163.com>
Date: 星期三, 23 九月 2026 16:46:20 +0800
Subject: [PATCH] 青岛维护

---
 pagesSub/ethicalReview/ethicalInfo.vue | 1089 ++++++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 777 insertions(+), 312 deletions(-)

diff --git a/pagesSub/ethicalReview/ethicalInfo.vue b/pagesSub/ethicalReview/ethicalInfo.vue
index e61d12c..84e3fd9 100644
--- a/pagesSub/ethicalReview/ethicalInfo.vue
+++ b/pagesSub/ethicalReview/ethicalInfo.vue
@@ -28,7 +28,7 @@
           <up-icon name="man" size="14" color="#909399" />
           <text class="compact-label">鎬у埆/骞撮緞</text>
           <text class="compact-value"
-            >{{ caseInfo.sex || "--" }}/{{ caseInfo.age || "--"
+            >{{ caseInfo.sex == 1 ? '鐢�' : caseInfo.sex == 2 ? '濂�' : '鏈煡' }}/{{ caseInfo.age || "--"
             }}{{ caseInfo.ageunit || "宀�" }}</text
           >
         </view>
@@ -213,69 +213,75 @@
       <text>褰撳墠浠诲姟宸茶秴鏃讹紝浠呭彲鏌ョ湅锛屼笉鍙搷浣�</text>
     </view>
 
-    <!-- 浼樺寲鐨勭鍚嶅脊绐楃粍浠� -->
-    <u-popup
-      :show="showSignaturePanel"
-      @close="closeSignaturePanel"
-      mode="bottom"
-      :round="20"
-      :closeable="true"
-      closeIcon="close"
-    >
-      <view class="signature-modal">
-        <view class="modal-header">
-          <text class="modal-title">鎵嬪啓绛惧悕</text>
-          <text class="modal-subtitle">璇峰湪涓嬫柟鍖哄煙杩涜绛惧悕</text>
-        </view>
+    <!-- 鍏ㄥ睆绛惧悕寮规锛圕anvas 2D 鍚屾缁樺埗锛岃В鍐虫柇鐐逛笌 iOS 鍏煎闂锛� -->
+    <view v-if="showSignaturePanel" class="fullscreen-signature">
+      <!-- 椤堕儴鏍� -->
+      <view class="fs-header">
+        <text class="fs-header-btn fs-cancel" @tap="closeSignaturePanel">鍙栨秷</text>
+        <text class="fs-title">鎵嬪啓绛惧悕</text>
+        <text class="fs-header-btn fs-clear" @tap="clearCanvas">娓呯┖</text>
+      </view>
 
-        <view class="signature-canvas-container">
-          <!-- 鎵嬪啓绛惧悕鐢诲竷 -->
-          <canvas
-            canvas-id="signatureCanvas"
-            class="signature-canvas"
-            :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"
-            @touchstart="onTouchStart"
-            @touchmove="onTouchMove"
-            @touchend="onTouchEnd"
-            disable-scroll
-          ></canvas>
+      <!-- 绛惧悕鐢诲竷鍖哄煙锛堝崰婊″墿浣欑┖闂达級 -->
+      <view class="fs-body">
+        <!-- H5 绔細canvas 鐢� JS 鍘熺敓鍒涘缓 + 鍘熺敓浜嬩欢缁戝畾锛堢粫寮�缁勪欢缂栬瘧灞傜殑鍧愭爣宸紓锛屾牴娌荤瑪杩归敊浣嶏級 -->
+        <!-- #ifdef H5 -->
+        <view id="h5CanvasWrap" ref="h5CanvasWrap" class="fs-canvas-wrap"></view>
+        <!-- #endif -->
 
-          <!-- 鎿嶄綔鎸夐挳 -->
-          <view class="canvas-actions">
-            <button class="action-btn clear-btn" @tap="clearCanvas">
-              <up-icon name="trash" size="20" />
-              <text>娓呯┖</text>
-            </button>
-            <button
-              class="action-btn redo-btn"
-              @tap="undoLastStroke"
-              :disabled="!canUndo"
-            >
-              <up-icon name="play-left" size="20" />
-              <text>鎾ら攢</text>
-            </button>
-            <button
-              class="action-btn confirm-btn"
-              @tap="confirmSignature"
-              :disabled="!hasSignature"
-            >
-              <up-icon name="checkmark" size="20" />
-              <text>纭绛惧悕</text>
-            </button>
-          </view>
+        <!-- 灏忕▼搴�/App 绔細canvas 2d 缁勪欢 -->
+        <!-- #ifndef H5 -->
+        <canvas
+          id="signatureCanvas"
+          canvas-id="signatureCanvas"
+          type="2d"
+          class="fs-canvas"
+          @touchstart="onTouchStart"
+          @touchmove="onTouchMove"
+          @touchend="onTouchEnd"
+          @touchcancel="onTouchEnd"
+          disable-scroll
+        ></canvas>
+        <!-- #endif -->
 
-          <!-- 绛惧悕棰勮 -->
-          <view v-if="tempSignatureData" class="signature-preview">
-            <text class="preview-title">绛惧悕棰勮</text>
-            <image
-              :src="tempSignatureData"
-              mode="aspectFit"
-              class="preview-image"
-            />
-          </view>
+        <view v-if="!hasSignature" class="fs-hint" @tap.stop>
+          <up-icon name="edit-pen" size="44" color="#c0c4cc" />
+          <text>璇峰湪姝ゅ尯鍩熺鍚�</text>
         </view>
       </view>
-    </u-popup>
+
+      <!-- 绗旇Е绮楃粏閫夋嫨 -->
+      <view class="fs-toolbar">
+        <view
+          class="toolbar-item"
+          v-for="s in strokeSizes"
+          :key="s.value"
+          @tap="strokeWidth = s.value"
+        >
+          <view
+            class="size-dot"
+            :style="{
+              width: s.value * 2 + 'px',
+              height: s.value * 2 + 'px',
+              background: strokeWidth == s.value ? '#0f95b0' : '#c0c4cc',
+            }"
+          ></view>
+          <text class="size-label" :class="{ active: strokeWidth == s.value }">{{
+            s.label
+          }}</text>
+        </view>
+      </view>
+
+      <!-- 搴曢儴鎿嶄綔鏍� -->
+      <view class="fs-footer">
+        <button class="fs-btn fs-undo" :disabled="!canUndo" @tap="undoLastStroke">
+          鎾ら攢
+        </button>
+        <button class="fs-btn fs-confirm" :disabled="!hasSignature" @tap="confirmSignature">
+          纭绛惧悕
+        </button>
+      </view>
+    </view>
 
     <view v-if="showHomeButton" class="home-fab" @click="goHome">
       <uni-icons type="home" size="24" color="#fff" />
@@ -290,15 +296,28 @@
       @confirm="confirmSubmit"
       @cancel="showSubmitModal = false"
     ></u-modal>
+
+    <!-- H5 鍐呭祵 PDF 棰勮缁勪欢 -->
+    <!-- #ifdef H5 -->
+    <pdf-preview
+      :show="pdfPreview.show"
+      :url="pdfPreview.url"
+      :title="pdfPreview.title"
+      @close="pdfPreview.show = false"
+    />
+    <!-- #endif -->
   </view>
 </template>
 
 <script setup>
-import { ref, computed, reactive, onMounted, nextTick } from "vue";
+import { ref, computed, reactive, onMounted, nextTick, getCurrentInstance } from "vue";
 import { onLoad, onShow } from "@dcloudio/uni-app";
 import dayjs from "dayjs";
 import { useUserStore } from "@/stores/user";
 import { useHomeButton } from "@/stores/useHomeButton";
+// #ifdef H5
+import PdfPreview from "@/components/pdf-preview/index.vue";
+// #endif
 const { showHomeButton, checkAutoLogin, goHome } = useHomeButton();
 const userStore = useUserStore();
 
@@ -318,27 +337,22 @@
 const fcid = ref(null);
 const materials = ref([]);
 
-// ==================== 鏂板锛氭牴鎹幆澧冭幏鍙� baseUrlHt ====================
+// PDF 棰勮锛圚5 绔唴宓� pdf.js 缁勪欢棰勮锛�
+const pdfPreview = reactive({ show: false, url: "", title: "" });
+
+// ============== 鏂板锛氭牴鎹幆澧冭幏鍙� baseUrlHt ==============
 const getBaseUrlHt = () => {
   // #ifdef H5
-  // H5 鐜锛氭牴鎹祻瑙堝櫒鍦板潃鍔ㄦ�佹嫾鎺�
+  // H5 鐜锛氭枃浠剁粺涓�璧般�屽悓婧愩�嶅湴鍧�锛坵indow.location.origin锛夛紝鐢遍儴缃茶椤甸潰鐨� nginx 鍙嶅悜浠g悊
+  // /profile/ 鍒板悗绔�傝繖鏍锋枃浠朵笌椤甸潰鍚屾簮锛屾祻瑙堝櫒涓嶄細瑙﹀彂 CORS锛堝交搴曡閬垮悗绔噸澶�
+  // Access-Control-Allow-Origin 澶村鑷寸殑璺ㄥ煙鎶ラ敊锛屼篃瑙勯伩浜嗛〉闈㈠湪 :8091銆佹枃浠跺湪 :443 鐨勮法鍩燂級銆�
   try {
-    const href = window.location.href;
-    
-    // 瑙勫垯 1锛�192.168.120.27:8091 鈫� 120.27 + :9095
-    if (href.startsWith('http://192.168.120.27:8091')) {
-      return 'http://192.168.120.27:9095';
-    }
-    
-    // 瑙勫垯 2锛�192.168.76.25:8091 鈫� 76.25 + :9095
-    if (href.startsWith('http://192.168.76.25:8091')) {
-      return 'http://192.168.76.25:9095';
-    }
-    
-    // 瑙勫垯 3锛氬叾浣欏叏閮ㄨ蛋鐢熶骇
-    return 'https://opo.qduh.cn';
+    return (
+      window.location.origin ||
+      `${window.location.protocol}//${window.location.host}`
+    );
   } catch {
-    return 'https://opo.qduh.cn';
+    return "https://opo.qduh.cn";
   }
   // #endif
 
@@ -376,8 +390,10 @@
 });
 
 const conclusionOptions = ref([
-  { label: "鍚屾剰", value: "1" }, // 瀵瑰簲receiveStatus 5-瀹屾垚
-  { label: "椹冲洖", value: "2" }, // 瀵瑰簲receiveStatus 6-椹冲洖
+  { label: "涓嶅悓鎰�", value: "0" },
+  { label: "鍚屾剰", value: "1" },
+  { label: "寮冩潈", value: "3" },
+  { label: "淇敼鍚庡悓鎰�", value: "4" },
 ]);
 
 // 绛惧悕鐩稿叧鏁版嵁
@@ -389,17 +405,26 @@
   serverData: null,
 });
 
-// 鎵嬪啓绛惧悕鐩稿叧
-const canvasWidth = 650;
-const canvasHeight = 300;
-let ctx = null;
+// 鎵嬪啓绛惧悕鐩稿叧锛圕anvas 2D 鍏ㄥ睆鐗堬級
+let ctx = null; // Canvas 2D 娓叉煋涓婁笅鏂囷紙鍚屾缁樺埗锛�
+let canvasNode = null; // canvas 鑺傜偣锛堢敤浜庡鍑哄浘鐗囷級
+let canvasCssWidth = 0; // 鐢诲竷 CSS 瀹藉害
+let canvasCssHeight = 0; // 鐢诲竷 CSS 楂樺害
+let canvasRect = { left: 0, top: 0 }; // 鐢诲竷鐩稿瑙嗗彛鐨勪綅缃紙閫昏緫鍍忕礌锛夛紝鐢ㄤ簬瑙︽懜鍧愭爣鎹㈢畻
+let strokes = ref([]); // 鎵�鏈夌瑪鐢伙紙姣忕瑪涓� { points: [...], width: n }锛岀敤浜庢挙閿�锛�
+let currentStroke = []; // 褰撳墠姝e湪缁樺埗鐨勭偣鏁扮粍
+let currentStrokeWidth = 4; // 褰撳墠绗旇Е绮楃粏锛坧x锛�
 let isDrawing = false;
-let lastX = 0;
-let lastY = 0;
-let strokeHistory = [];
-const tempSignatureData = ref("");
-const canUndo = computed(() => strokeHistory.length > 0);
-const hasSignature = computed(() => tempSignatureData.value !== "");
+
+// 绗旇Е绮楃粏閫夐」
+const strokeSizes = [
+  { label: "缁�", value: 2 },
+  { label: "涓�", value: 4 },
+  { label: "绮�", value: 6 },
+];
+const strokeWidth = ref(4); // 褰撳墠閫変腑鐨勭矖缁嗭紙瀵规柊绗旂敾鐢熸晥锛�
+const hasSignature = ref(false);
+const canUndo = computed(() => strokes.value.length > 0);
 
 // 涓婁紶閰嶇疆
 const uploadConfig = reactive({
@@ -455,12 +480,11 @@
   return isTimeout.value;
 });
 
-// 鏄惁鍙互鎻愪氦
+// 鏄惁鍙互鎻愪氦锛堝鏌ユ剰瑙佷负闈炲繀濉級
 const canSubmit = computed(() => {
   if (isReadonly.value) return false;
   return (
     form.value.expertconclusion !== "" &&
-    form.value.expertopinion.trim().length > 0 &&
     signatureData.signatureUrl !== "" &&
     signatureData.signatureUrl.startsWith("http") // 纭繚鏄畬鏁寸殑URL璺緞
   );
@@ -525,7 +549,11 @@
       console.log(caseInfo.value.receiveStatus, "caseInfo.value");
 
       // 璁剧疆琛ㄥ崟鏁版嵁
-      if (data.expertconclusion) {
+      if (
+        data.expertconclusion !== null &&
+        data.expertconclusion !== undefined &&
+        data.expertconclusion !== ""
+      ) {
         form.value.expertconclusion = data.expertconclusion.toString();
       }
       if (data.expertopinion) {
@@ -588,7 +616,7 @@
   }
 };
 
-// ==================== 鏂板锛氳幏鍙栧畬鏁碪RL锛堝钩鍙板畨鍏ㄧ増锛� ====================
+// ============== 鏂板锛氳幏鍙栧畬鏁碪RL锛堝钩鍙板畨鍏ㄧ増锛� ==============
 const getFullUrl = (path) => {
   if (!path) return '';
   
@@ -627,7 +655,7 @@
   return `${baseUrlHt}${path.startsWith('/') ? '' : '/'}${path}`;
 };
 
-// ==================== 淇敼锛歳eplaceUrlDomain 浣跨敤 getFullUrl ====================
+// ============== 淇敼锛歳eplaceUrlDomain 浣跨敤 getFullUrl ==============
 const replaceUrlDomain = (url) => {
   return getFullUrl(url);
 };
@@ -757,7 +785,7 @@
   }
 };
 
-// 鎵撳紑绛惧悕闈㈡澘
+// 鎵撳紑绛惧悕闈㈡澘锛堝叏灞忓脊妗嗭級
 const openSignaturePanel = () => {
   if (isReadonly.value) {
     uni.showToast({
@@ -768,132 +796,435 @@
   }
 
   showSignaturePanel.value = true;
+  // 姣忔鎵撳紑閮介噸缃鍚嶇姸鎬侊紝閲嶆柊鍒濆鍖栫敾甯�
+  strokes.value = [];
+  currentStroke = [];
+  hasSignature.value = false;
+  isDrawing = false;
+  ctx = null;
+  canvasNode = null;
+  // 绛夊緟鍏ㄥ睆寮规 DOM 娓叉煋瀹屾垚鍚庡啀鍒濆鍖栫敾甯�
   nextTick(() => {
-    initCanvas();
+    setTimeout(() => {
+      initCanvas();
+    }, 100);
   });
 };
 
 // 鍏抽棴绛惧悕闈㈡澘
 const closeSignaturePanel = () => {
   showSignaturePanel.value = false;
-  clearCanvas();
+  // #ifdef H5
+  // 绉婚櫎鍘熺敓浜嬩欢鐩戝惉锛岄伩鍏嶉噸澶嶇粦瀹�
+  if (canvasNode) {
+    canvasNode.removeEventListener("touchstart", onH5TouchStart);
+    canvasNode.removeEventListener("touchmove", onH5TouchMove);
+    canvasNode.removeEventListener("touchend", onH5TouchEnd);
+    canvasNode.removeEventListener("touchcancel", onH5TouchEnd);
+    canvasNode.removeEventListener("mousedown", onH5MouseDown);
+    canvasNode.removeEventListener("mousemove", onH5MouseMove);
+    canvasNode.removeEventListener("mouseup", onH5MouseUp);
+  }
+  // #endif
+  ctx = null;
+  canvasNode = null;
 };
 
-// 鍒濆鍖栫敾甯�
+// 鍒濆鍖栫敾甯冿紙Canvas 2D 鍚屾 API锛�
 const initCanvas = () => {
-  ctx = uni.createCanvasContext("signatureCanvas");
-  clearCanvas();
+  // #ifdef H5
+  initH5Canvas();
+  // #endif
+
+  // #ifndef H5
+  initMPCanvas();
+  // #endif
 };
 
-// 娓呯┖鐢诲竷
-const clearCanvas = () => {
-  if (!ctx) return;
+// #ifdef H5
+// ============== H5 绔細鍘熺敓 canvas 鏂规 ==============
+// 涓嶇敤 uni-app 鐨� canvas 缁勪欢锛堝叾缁勪欢灞傚 type="2d" 鐨勮妭鐐�/鍧愭爣澶勭悊鍚勭増鏈笉涓�鑷达級锛�
+// 鑰屾槸鐢� JS 鍔ㄦ�佸垱寤哄師鐢� canvas + 鍘熺敓浜嬩欢缁戝畾锛屽潗鏍囨崲绠楀畬鍏ㄥ彈鎺э紝浠庢牴涓婃秷闄ょ瑪杩瑰亸绉汇��
+const h5CanvasWrap = ref(null);
 
-  ctx.clearRect(0, 0, canvasWidth, canvasHeight);
-  ctx.setStrokeStyle("#000000");
-  ctx.setLineWidth(3);
-  ctx.setLineCap("round");
-  ctx.setLineJoin("round");
-  ctx.draw();
-  strokeHistory = [];
-  tempSignatureData.value = "";
-};
+const initH5Canvas = () => {
+  // 鎷垮鍣ㄧ湡瀹� DOM锛歶ni-app H5 閲� <view> 缂栬瘧鎴� uni-view 缁勪欢锛宺ef 鎷垮埌鐨勬槸缁勪欢瀹炰緥锛�
+  // 蹇呴』鐢� getElementById 鎴� .$el 鎵嶈兘鎷垮埌鐪熷疄 DOM锛屽惁鍒� appendChild 浼氭姤閿欍�乧anvas 鍒涘缓涓嶅嚭鏉ャ��
+  let wrap = document.getElementById("h5CanvasWrap");
+  if (!wrap && h5CanvasWrap.value) {
+    wrap = h5CanvasWrap.value.$el || h5CanvasWrap.value;
+  }
+  if (!wrap) {
+    console.error("[绛惧悕] H5 鐢诲竷瀹瑰櫒鑾峰彇澶辫触锛坕d=h5CanvasWrap 鏈壘鍒帮級");
+    return;
+  }
+  // 娓呯┖瀹瑰櫒锛岄伩鍏嶉噸澶嶅垵濮嬪寲浜х敓澶氫釜 canvas
+  wrap.innerHTML = "";
+  const canvas = document.createElement("canvas");
+  canvas.style.display = "block";
+  canvas.style.background = "#ffffff";
+  canvas.style.touchAction = "none";
+  wrap.appendChild(canvas);
 
-// 鎾ら攢涓婁竴姝�
-const undoLastStroke = () => {
-  if (strokeHistory.length == 0) return;
+  // 璇诲彇瀹瑰櫒瀹為檯娓叉煋灏哄骞惰 canvas 鎾戞弧锛堜笉渚濊禆鐧惧垎姣旓紝閬垮厤 flex 瀹瑰櫒涓В鏋愬紓甯稿鑷村昂瀵镐负 0锛�
+  const fit = () => {
+    const wr = wrap.getBoundingClientRect();
+    if (!wr.width || !wr.height) return false;
+    canvas.style.width = wr.width + "px";
+    canvas.style.height = wr.height + "px";
+    return true;
+  };
 
-  // 绉婚櫎鏈�鍚庝竴姝�
-  strokeHistory.pop();
+  const bind = () => {
+    try {
+      const rect = canvas.getBoundingClientRect();
+      canvasNode = canvas;
+      canvasCssWidth = rect.width;
+      canvasCssHeight = rect.height;
+      canvasRect = { left: rect.left, top: rect.top };
+      const dpr = window.devicePixelRatio || 2;
+      canvas.width = rect.width * dpr;
+      canvas.height = rect.height * dpr;
+      ctx = canvas.getContext("2d");
+      ctx.scale(dpr, dpr);
+      console.log("[绛惧悕] H5 鐢诲竷鍒濆鍖�", {
+        cssW: canvasCssWidth,
+        cssH: canvasCssHeight,
+        dpr,
+        rect: canvasRect,
+      });
+      redraw();
 
-  // 閲嶆柊缁樺埗
-  ctx.clearRect(0, 0, canvasWidth, canvasHeight);
-  ctx.setStrokeStyle("#000000");
-  ctx.setLineWidth(3);
-  ctx.setLineCap("round");
-  ctx.setLineJoin("round");
+      // 瑙︽懜浜嬩欢锛堟墜鏈�/骞虫澘锛�
+      canvas.addEventListener("touchstart", onH5TouchStart, { passive: false });
+      canvas.addEventListener("touchmove", onH5TouchMove, { passive: false });
+      canvas.addEventListener("touchend", onH5TouchEnd);
+      canvas.addEventListener("touchcancel", onH5TouchEnd);
+      // 榧犳爣浜嬩欢锛圥C 娴忚鍣ㄨ皟璇曪級
+      canvas.addEventListener("mousedown", onH5MouseDown);
+      canvas.addEventListener("mousemove", onH5MouseMove);
+      canvas.addEventListener("mouseup", onH5MouseUp);
+    } catch (err) {
+      console.error("[绛惧悕] H5 鐢诲竷鍒濆鍖栧け璐�:", err);
+    }
+  };
 
-  // 缁樺埗鍘嗗彶绗旇抗
-  strokeHistory.forEach((stroke) => {
-    ctx.beginPath();
-    ctx.moveTo(stroke.startX, stroke.startY);
-    ctx.lineTo(stroke.endX, stroke.endY);
-    ctx.stroke();
-  });
-
-  ctx.draw();
-
-  // 鏇存柊棰勮
-  if (strokeHistory.length == 0) {
-    tempSignatureData.value = "";
+  // 甯冨眬鍙兘灏氭湭绋冲畾锛堝脊妗嗗垰鏄剧ず锛夛紝灏哄涓� 0 鏃堕噸璇曞嚑娆�
+  if (fit()) {
+    bind();
   } else {
-    getCanvasImage();
+    let tries = 0;
+    const timer = setInterval(() => {
+      tries++;
+      if (fit() || tries >= 10) {
+        clearInterval(timer);
+        if (fit()) {
+          bind();
+        } else {
+          console.error("[绛惧悕] H5 鐢诲竷瀹瑰櫒灏哄濮嬬粓涓� 0锛屽垵濮嬪寲澶辫触");
+        }
+      }
+    }, 50);
   }
 };
 
-// 瑙︽懜寮�濮�
-const onTouchStart = (e) => {
-  if (isReadonly.value) return;
-
-  isDrawing = true;
-  const touch = e.touches[0];
-  lastX = touch.x;
-  lastY = touch.y;
-  ctx.beginPath();
-  ctx.moveTo(lastX, lastY);
+// H5 瑙︽懜鍧愭爣锛氬疄鏃跺彇 getBoundingClientRect锛屼笌瑙︽懜瑙嗗彛鍧愭爣鍚屾簮鎹㈢畻
+let lastH5TouchEndTime = 0; // 璁板綍瑙︽懜缁撴潫鏃堕棿锛岀敤浜庡睆钄借Е鎽稿悗娴忚鍣ㄦ淳鍙戠殑鍏煎 mouse 浜嬩欢
+const onH5TouchStart = (e) => {
+  if (!ctx || isReadonly.value) return;
+  const t = e.touches[0];
+  if (!t) return;
+  const rect = canvasNode.getBoundingClientRect();
+  beginStroke({ x: t.clientX - rect.left, y: t.clientY - rect.top });
 };
 
-// 瑙︽懜绉诲姩
-const onTouchMove = (e) => {
-  if (!isDrawing || isReadonly.value) return;
+const onH5TouchMove = (e) => {
+  e.preventDefault();
+  if (!isDrawing || !ctx) return;
+  const t = e.touches[0];
+  if (!t) return;
+  const rect = canvasNode.getBoundingClientRect();
+  continueStroke({ x: t.clientX - rect.left, y: t.clientY - rect.top });
+};
 
-  const touch = e.touches[0];
-  const currentX = touch.x;
-  const currentY = touch.y;
+const onH5TouchEnd = () => {
+  lastH5TouchEndTime = Date.now();
+  endStroke();
+};
 
-  ctx.lineTo(currentX, currentY);
-  ctx.stroke();
-  ctx.draw(true);
+// H5 榧犳爣浜嬩欢锛圥C 璋冭瘯鐢級
+let h5MouseDown = false;
+const onH5MouseDown = (e) => {
+  // 瑙︽懜缁撴潫鍚� 500ms 鍐呯殑鍏煎 mouse 浜嬩欢鐩存帴蹇界暐锛岄伩鍏嶅钩鏉�/鎵嬫満涓婄敾澶氫綑鐨勭偣
+  if (Date.now() - lastH5TouchEndTime < 500) return;
+  if (!ctx || isReadonly.value) return;
+  h5MouseDown = true;
+  const rect = canvasNode.getBoundingClientRect();
+  beginStroke({ x: e.clientX - rect.left, y: e.clientY - rect.top });
+};
 
-  // 淇濆瓨绗旇抗鍒板巻鍙茶褰�
-  strokeHistory.push({
-    startX: lastX,
-    startY: lastY,
-    endX: currentX,
-    endY: currentY,
+const onH5MouseMove = (e) => {
+  if (!h5MouseDown || !isDrawing || !ctx) return;
+  const rect = canvasNode.getBoundingClientRect();
+  continueStroke({ x: e.clientX - rect.left, y: e.clientY - rect.top });
+};
+
+const onH5MouseUp = () => {
+  h5MouseDown = false;
+  endStroke();
+};
+// #endif
+
+// #ifndef H5
+// ============== 灏忕▼搴�/App 绔細canvas 2d ==============
+// 涓�娆� fields({ node, rect }) 鏌ヨ鍚屾椂鎷垮埌 node 鍜屻�岄�昏緫鍍忕礌銆嶄綅缃�/灏哄锛�
+// 閬垮厤 node 涓� boundingClientRect 涓ゆ鏌ヨ涔嬮棿甯冨眬鍙樺寲瀵艰嚧鍩哄噯閿欎綅銆�
+const initMPCanvas = () => {
+  const instance = getCurrentInstance().proxy;
+  const query = uni.createSelectorQuery().in(instance);
+  query
+    .select("#signatureCanvas")
+    .fields({ node: true, rect: true })
+    .exec((res) => {
+      const node = res && res[0] && res[0].node;
+      const rect = res && res[0] && res[0].rect;
+      if (!node) {
+        console.error("[绛惧悕] 鐢诲竷鑺傜偣鑾峰彇澶辫触:", res);
+        uni.showToast({ title: "绛惧悕鍒濆鍖栧け璐ワ紝璇烽噸璇�", icon: "none" });
+        return;
+      }
+      if (rect && rect.width > 0) {
+        // 涓�娆℃煡璇㈠悓鏃舵嬁鍒拌妭鐐瑰拰浣嶇疆锛堥�昏緫鍍忕礌锛夛紝閬垮厤涓ゆ鏌ヨ闂村竷灞�鍙樺寲瀵艰嚧鍩哄噯閿欎綅
+        canvasRect = { left: rect.left, top: rect.top };
+        setupCanvas(node, rect.width, rect.height);
+        return;
+      }
+      // 閮ㄥ垎鍩虹搴� fields 鎷夸笉鍒� rect锛屽厹搴曞崟鐙煡涓�娆� boundingClientRect
+      uni
+        .createSelectorQuery()
+        .in(instance)
+        .select("#signatureCanvas")
+        .boundingClientRect((r) => {
+          if (r && r.width > 0) {
+            canvasRect = { left: r.left, top: r.top };
+            setupCanvas(node, r.width, r.height);
+          } else {
+            console.error("[绛惧悕] 鐢诲竷灏哄鑾峰彇澶辫触:", r);
+            uni.showToast({ title: "绛惧悕鍒濆鍖栧け璐ワ紝璇烽噸璇�", icon: "none" });
+          }
+        })
+        .exec();
+    });
+};
+
+// 姣忔钀界瑪鍓嶅埛鏂扮敾甯冨熀鍑嗕綅缃紝闃插尽椤甸潰婊氬姩/甯冨眬鍙樺寲瀵艰嚧鐨勫亸绉�
+const refreshCanvasRect = () => {
+  uni
+    .createSelectorQuery()
+    .in(getCurrentInstance().proxy)
+    .select("#signatureCanvas")
+    .boundingClientRect((rect) => {
+      if (rect && rect.left !== undefined) {
+        canvasRect = { left: rect.left, top: rect.top };
+      }
+    })
+    .exec();
+};
+// #endif
+
+// 閰嶇疆鐢诲竷锛堣缃墿鐞嗗儚绱狅紝闃叉妯$硦锛�
+const setupCanvas = (node, cssW, cssH) => {
+  canvasNode = node;
+  canvasCssWidth = cssW;
+  canvasCssHeight = cssH;
+  const dpr = uni.getSystemInfoSync().pixelRatio || 2;
+  node.width = cssW * dpr;
+  node.height = cssH * dpr;
+  ctx = node.getContext("2d");
+  ctx.scale(dpr, dpr);
+  // 璋冭瘯鐢細鎵撳嵃鐢诲竷灏哄锛屾帓鏌ョ鍚嶅亸绉伙紙鍚屾椂鍙嵁姝ゅ垽鏂祻瑙堝櫒璺戠殑鏄笉鏄渶鏂板寘锛�
+  console.log("[绛惧悕] 鐢诲竷鍒濆鍖�", {
+    cssW,
+    cssH,
+    dpr,
+    bufferW: node.width,
+    bufferH: node.height,
   });
+  redraw();
+};
 
-  lastX = currentX;
-  lastY = currentY;
+// 閲嶇粯鎵�鏈夌瑪鐢伙紙Canvas 2D 鍚屾 API锛屾棤涓㈠抚闂锛�
+const redraw = () => {
+  if (!ctx) return;
+
+  // 鐧借壊鑳屾櫙锛堝鍑哄浘鐗囨椂鍗充负鐧藉簳榛戝瓧锛夛紱鍏� clearRect 褰诲簳娓呯┖鍐嶉摵鐧藉簳锛岀‘淇濇挙閿�/娓呯┖鐢熸晥
+  ctx.clearRect(0, 0, canvasCssWidth, canvasCssHeight);
+  ctx.fillStyle = "#ffffff";
+  ctx.fillRect(0, 0, canvasCssWidth, canvasCssHeight);
+
+  // 绾挎潯閫氱敤鏍峰紡
+  ctx.strokeStyle = "#000000";
+  ctx.lineCap = "round";
+  ctx.lineJoin = "round";
+
+  // 閲嶇粯姣忎竴绗旓紙姣忕瑪璁板綍鑷繁鐨勭矖缁嗭紝鍒囨崲绮楃粏涓嶅奖鍝嶅凡鐢荤瑪鐢伙級
+  strokes.value.forEach((stroke) => {
+    const points = stroke.points;
+    ctx.lineWidth = stroke.width;
+    if (points.length == 1) {
+      drawDot(points[0], stroke.width);
+    } else if (points.length > 1) {
+      ctx.beginPath();
+      ctx.moveTo(points[0].x, points[0].y);
+      for (let i = 1; i < points.length; i++) {
+        ctx.lineTo(points[i].x, points[i].y);
+      }
+      ctx.stroke();
+    }
+  });
+};
+
+// 鐢讳竴涓渾鐐癸紙澶勭悊鍗曞嚮涓嶅嚭澧ㄧ殑鎯呭喌锛屽崐寰勯殢绗旇Е绮楃粏锛�
+const drawDot = (p, w) => {
+  const r = Math.max(1, (w || 3) / 2);
+  ctx.beginPath();
+  ctx.arc(p.x, p.y, r, 0, Math.PI * 2);
+  ctx.fillStyle = "#000000";
+  ctx.fill();
+};
+
+// 鑾峰彇瑙︽懜鐐圭浉瀵圭敾甯冪殑鍧愭爣锛堝皬绋嬪簭/App 绔細瑙嗗彛鍧愭爣 - 鐢诲竷浣嶇疆锛�
+// #ifndef H5
+const getTouchPos = (e) => {
+  const t = e.touches[0] || e.changedTouches[0];
+  if (!t) return { x: 0, y: 0 };
+  const cx = typeof t.clientX == "number" ? t.clientX : t.pageX;
+  const cy = typeof t.clientY == "number" ? t.clientY : t.pageY;
+  return { x: cx - canvasRect.left, y: cy - canvasRect.top };
+};
+// #endif
+
+// ============== 鍏辩敤缁樺埗閫昏緫锛圚5 鍘熺敓浜嬩欢涓庡皬绋嬪簭缁勪欢浜嬩欢鍏辩敤锛� ==============
+// 钀界瑪
+const beginStroke = (pos) => {
+  if (!ctx || isReadonly.value) return;
+  isDrawing = true;
+  currentStroke = [pos];
+  currentStrokeWidth = strokeWidth.value; // 璁板綍鏈瑪鐨勭矖缁�
+  strokes.value.push({ points: currentStroke, width: currentStrokeWidth });
+  hasSignature.value = true;
+  ctx.lineWidth = currentStrokeWidth;
+  drawDot(pos, currentStrokeWidth);
+};
+
+// 缁瑪
+const continueStroke = (pos) => {
+  if (!isDrawing || !ctx) return;
+  const prev = currentStroke[currentStroke.length - 1];
+  if (prev) {
+    ctx.beginPath();
+    ctx.lineWidth = currentStrokeWidth;
+    ctx.moveTo(prev.x, prev.y);
+    ctx.lineTo(pos.x, pos.y);
+    ctx.stroke();
+  }
+  currentStroke.push(pos);
+};
+
+// 鏀剁瑪
+const endStroke = () => {
+  if (!isDrawing) return;
+  isDrawing = false;
+  currentStroke = [];
+};
+
+// 瑙︽懜寮�濮嬶紙灏忕▼搴�/App 绔粍浠朵簨浠讹級
+// #ifndef H5
+const onTouchStart = (e) => {
+  if (!ctx || isReadonly.value) return;
+  // 钀界瑪鍓嶅紓姝ュ埛鏂扮敾甯冨熀鍑嗕綅缃紝闃插尽椤甸潰婊氬姩/甯冨眬鍙樺寲瀵艰嚧鐨勫亸绉�
+  refreshCanvasRect();
+  const pos = getTouchPos(e);
+  beginStroke(pos);
+};
+
+// 瑙︽懜绉诲姩锛圕anvas 2D 鍚屾缁樺埗锛岀嚎鏉″疄鏃惰繛缁級
+const onTouchMove = (e) => {
+  if (!isDrawing || !ctx) return;
+  const pos = getTouchPos(e);
+  continueStroke(pos);
 };
 
 // 瑙︽懜缁撴潫
 const onTouchEnd = () => {
-  if (!isDrawing) return;
+  endStroke();
+};
+// #endif
 
-  isDrawing = false;
-  ctx.closePath();
-
-  // 鏇存柊棰勮
-  getCanvasImage();
+// 娓呯┖鐢诲竷
+const clearCanvas = () => {
+  strokes.value = [];
+  currentStroke = [];
+  hasSignature.value = false;
+  redraw();
 };
 
-// 鑾峰彇鐢诲竷鍥剧墖
+// 鎾ら攢涓婁竴绗�
+const undoLastStroke = () => {
+  if (strokes.value.length == 0) return;
+  strokes.value.pop();
+  hasSignature.value = strokes.value.length > 0;
+  redraw();
+};
+
+// 鐢熸垚绛惧悕鍥剧墖锛圥romise 褰㈠紡锛岃繑鍥炰复鏃舵枃浠惰矾寰勶級
 const getCanvasImage = () => {
-  uni.canvasToTempFilePath({
-    canvasId: "signatureCanvas",
-    success: (res) => {
-      tempSignatureData.value = res.tempFilePath;
-    },
-    fail: (err) => {
-      console.error("鑾峰彇鐢诲竷鍥剧墖澶辫触:", err);
-    },
+  return new Promise((resolve, reject) => {
+    if (!canvasNode) {
+      reject(new Error("鐢诲竷鏈垵濮嬪寲"));
+      return;
+    }
+    // #ifdef MP-WEIXIN
+    uni.canvasToTempFilePath({
+      canvas: canvasNode,
+      fileType: "png",
+      success: (res) => resolve(res.tempFilePath),
+      fail: (err) => {
+        console.error("鑾峰彇鐢诲竷鍥剧墖澶辫触:", err);
+        reject(err);
+      },
+    });
+    // #endif
+    // #ifndef MP-WEIXIN
+    try {
+      canvasNode.toBlob(
+        (blob) => {
+          if (blob) {
+            // H5 绔細鐢熸垚甯� .png 鎵╁睍鍚嶇殑 File 瀵硅薄锛屼笂浼犳椂鎵嶈兘淇濈暀鍥剧墖鏍煎紡
+            if (typeof File !== "undefined") {
+              const file = new File([blob], `signature_${Date.now()}.png`, {
+                type: "image/png",
+              });
+              resolve(file);
+            } else {
+              resolve(URL.createObjectURL(blob));
+            }
+          } else {
+            reject(new Error("鐢熸垚鍥剧墖澶辫触"));
+          }
+        },
+        "image/png",
+      );
+    } catch (err) {
+      reject(err);
+    }
+    // #endif
   });
 };
 
 // 纭绛惧悕
 const confirmSignature = async () => {
-  if (!tempSignatureData.value) {
+  if (!hasSignature.value) {
     uni.showToast({
       title: "璇峰厛绛惧悕",
       icon: "none",
@@ -904,8 +1235,11 @@
   try {
     uni.showLoading({ title: "淇濆瓨绛惧悕涓�..." });
 
+    // 鐢熸垚绛惧悕鍥剧墖
+    const filePath = await getCanvasImage();
+
     // 涓婁紶绛惧悕鏂囦欢
-    const uploadResult = await uploadSignature(tempSignatureData.value);
+    const uploadResult = await uploadSignature(filePath);
 
     if (uploadResult) {
       // 浣跨敤 getFullUrl 澶勭悊绛惧悕URL
@@ -925,7 +1259,8 @@
       });
 
       showSignaturePanel.value = false;
-      clearCanvas();
+      ctx = null;
+      canvasNode = null;
     } else {
       uni.hideLoading();
       uni.showToast({
@@ -953,6 +1288,53 @@
     const expertName =
       userInfo?.nickName || userInfo?.userName || expertInfo.value.name;
 
+    // 缁熶竴瑙f瀽涓婁紶鎴愬姛鐨勫搷搴�
+    const parseUploadData = (data) => {
+      console.log("绛惧悕涓婁紶鎴愬姛:", data);
+      if (data.code == 200) {
+        resolve({
+          url: data.url,
+          fileName: data.fileName,
+          newFileName: data.newFileName,
+          originalFilename: data.originalFilename,
+          filePath: data.filePath || data.fileName,
+          size: data.size,
+        });
+      } else {
+        reject(new Error(data.msg || "涓婁紶澶辫触"));
+      }
+    };
+
+    // #ifdef H5
+    // H5 绔� filePath 涓哄甫 .png 鎵╁睍鍚嶇殑 File 瀵硅薄锛岀敤 FormData 鎵嬪姩涓婁紶浠ヤ繚鐣欏浘鐗囨牸寮�
+    if (typeof File !== "undefined" && filePath instanceof File) {
+      const formData = new FormData();
+      formData.append("file", filePath, filePath.name);
+      formData.append("bizType", "expert_review_signature");
+      formData.append("caseNo", caseInfo.value.caseNo || "");
+      formData.append("expertName", expertName);
+      formData.append("uploadType", "signature");
+
+      const xhr = new XMLHttpRequest();
+      xhr.open("POST", "/api/common/upload");
+      xhr.setRequestHeader("Authorization", `Bearer ${token}`);
+      xhr.onload = () => {
+        if (xhr.status === 200) {
+          try {
+            parseUploadData(JSON.parse(xhr.responseText));
+          } catch (e) {
+            reject(new Error("涓婁紶鍝嶅簲瑙f瀽澶辫触"));
+          }
+        } else {
+          reject(new Error(`涓婁紶澶辫触锛岀姸鎬佺爜: ${xhr.status}`));
+        }
+      };
+      xhr.onerror = () => reject(new Error("涓婁紶澶辫触"));
+      xhr.send(formData);
+      return;
+    }
+    // #endif
+
     uni.uploadFile({
       url: "/api/common/upload",
       filePath: filePath,
@@ -970,20 +1352,7 @@
       success: (res) => {
         if (res.statusCode == 200) {
           const data = JSON.parse(res.data);
-          console.log("绛惧悕涓婁紶鎴愬姛:", data);
-
-          if (data.code == 200) {
-            resolve({
-              url: data.url,
-              fileName: data.fileName,
-              newFileName: data.newFileName,
-              originalFilename: data.originalFilename,
-              filePath: data.filePath || data.fileName,
-              size: data.size,
-            });
-          } else {
-            reject(new Error(data.msg || "涓婁紶澶辫触"));
-          }
+          parseUploadData(data);
         } else {
           reject(new Error(`涓婁紶澶辫触锛岀姸鎬佺爜: ${res.statusCode}`));
         }
@@ -1038,7 +1407,7 @@
   });
 };
 
-// ==================== 淇敼锛氶瑙堢鍚� ====================
+// ============== 淇敼锛氶瑙堢鍚� ==============
 const previewSignature = () => {
   if (signatureData.signatureUrl) {
     // 浣跨敤 getFullUrl 澶勭悊URL
@@ -1060,7 +1429,7 @@
   }
 };
 
-// ==================== 淇敼锛氶瑙堟潗鏂� ====================
+// ============== 淇敼锛氶瑙堟潗鏂� ==============
 const previewMaterial = (material) => {
   if (material.url) {
     uni.showLoading({ title: "鍔犺浇涓�..." });
@@ -1069,7 +1438,21 @@
       material.type || material.url.split(".").pop().toLowerCase();
 
     // 浣跨敤 getFullUrl 澶勭悊URL
-    const fileUrl = getFullUrl(material.url);
+    let fileUrl = getFullUrl(material.url);
+
+    // URL 缂栫爜锛堝鐞嗕腑鏂囨枃浠跺悕锛岄伩鍏嶅弻閲嶇紪鐮侊級
+    try {
+      const urlObj = new URL(fileUrl);
+      // decodeURI 鍏堣В鐮佸凡鏈夌殑缂栫爜锛宔ncodeURI 閲嶆柊缂栫爜锛岀‘淇濆彧缂栫爜涓�娆�
+      fileUrl = urlObj.origin + encodeURI(decodeURI(urlObj.pathname)) + (urlObj.search || "");
+    } catch {
+      // 闄嶇骇
+      try {
+        fileUrl = encodeURI(decodeURI(fileUrl));
+      } catch {
+        fileUrl = encodeURI(fileUrl);
+      }
+    }
 
     // 鍒ゆ柇鏂囦欢绫诲瀷
     if (["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(fileExt)) {
@@ -1096,9 +1479,35 @@
         fileExt,
       )
     ) {
-      // 鏂囨。鏂囦欢涓嬭浇鍚庢墦寮�
+      // 鏂囨。鏂囦欢澶勭悊
+      // #ifdef H5
+      // H5 鐜锛氱洿鎺ョ敤娴忚鍣ㄦ墦寮�锛屾敮鎸� PDF 鍦ㄧ嚎棰勮
+      uni.hideLoading();
+      if (fileExt == "pdf") {
+        // PDF 鐢� pdf.js 缁勪欢鍐呭祵棰勮锛岄伩鍏嶆墜鏈烘祻瑙堝櫒鐩存帴瑙﹀彂涓嬭浇
+        pdfPreview.url = fileUrl;
+        pdfPreview.title = material.name || "PDF 棰勮";
+        pdfPreview.show = true;
+      } else {
+        // 鍏朵粬鏂囨。鐢� <a> 鏍囩瑙﹀彂涓嬭浇
+        const link = document.createElement("a");
+        link.href = fileUrl;
+        link.download = material.name || material.url.split("/").pop();
+        link.target = "_blank";
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+      }
+      // #endif
+
+      // #ifdef MP-WEIXIN
+      // 灏忕▼搴忕幆澧冿細涓嬭浇鍚庣敤 openDocument 鎵撳紑
+      const token = uni.getStorageSync("token") || "";
       uni.downloadFile({
         url: fileUrl,
+        header: {
+          Authorization: token ? `Bearer ${token}` : "",
+        },
         success: (res) => {
           if (res.statusCode == 200) {
             const filePath = res.tempFilePath;
@@ -1117,19 +1526,28 @@
                 });
               },
             });
+          } else {
+            console.error("涓嬭浇鐘舵�佺爜寮傚父:", res.statusCode);
+            uni.showToast({
+              title: `鏈嶅姟鍣ㄨ繑鍥炲紓甯�(${res.statusCode})`,
+              icon: "none",
+            });
           }
         },
         fail: (err) => {
           console.error("涓嬭浇鏂囦欢澶辫触:", err);
+          console.error("涓嬭浇URL:", fileUrl);
           uni.showToast({
-            title: "鏂囦欢涓嬭浇澶辫触",
+            title: "鏂囦欢涓嬭浇澶辫触锛岃妫�鏌ョ綉缁滄垨鍩熷悕閰嶇疆",
             icon: "none",
+            duration: 3000,
           });
         },
         complete: () => {
           uni.hideLoading();
         },
       });
+      // #endif
     } else {
       // 鍏朵粬鏂囦欢绫诲瀷
       uni.showToast({
@@ -1236,8 +1654,11 @@
       expertopinion: form.value.expertopinion,
       sigin: signatureData.signatureUrl,
       expertType: caseInfo.value.expertType,
-      // 鏍规嵁缁撹璁剧疆鐘舵��
-      receiveStatus: form.value.expertconclusion == "1" ? "5" : "6", // 5-瀹屾垚, 6-椹冲洖
+      // 鏍规嵁缁撹璁剧疆鐘舵�侊細鍚屾剰/淇敼鍚庡悓鎰� 鈫� 瀹屾垚(5)锛屼笉鍚屾剰/寮冩潈 鈫� 椹冲洖(6)
+      receiveStatus:
+        form.value.expertconclusion == "1" || form.value.expertconclusion == "4"
+          ? "5"
+          : "6",
       conclusiontime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
     };
 
@@ -1289,14 +1710,8 @@
     return false;
   }
   console.log(3);
+  // 瀹℃煡鎰忚涓洪潪蹇呭~锛屼笉鍐嶆牎楠�
 
-  if (!form.value.expertopinion.trim() && !isDraft) {
-    uni.showToast({
-      title: "璇疯緭鍏ュ鏌ユ剰瑙�",
-      icon: "none",
-    });
-    return false;
-  }
   console.log(signatureData, "signatureData");
 
   if (!signatureData.signatureUrl && !isDraft) {
@@ -1796,111 +2211,172 @@
   }
 }
 
-// 绛惧悕妯℃�佹鏍峰紡
-.signature-modal {
-  padding: 30rpx;
-  background: #fff;
-  border-radius: 20rpx 20rpx 0 0;
-  max-height: 80vh;
-  overflow: hidden;
+// 鍏ㄥ睆绛惧悕寮规鏍峰紡
+.fullscreen-signature {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 10080;
+  background: #f5f7fa;
+  display: flex;
+  flex-direction: column;
+  // 閫傞厤鍒樻捣灞�/鐘舵�佹爮
+  padding-top: constant(safe-area-inset-top);
+  padding-top: env(safe-area-inset-top);
 
-  .modal-header {
-    text-align: center;
-    margin-bottom: 30rpx;
+  .fs-header {
+    height: 88rpx;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 0 30rpx;
+    background: #fff;
+    border-bottom: 1rpx solid #ebeef5;
 
-    .modal-title {
+    .fs-title {
       font-size: 32rpx;
       font-weight: 600;
       color: #303133;
-      display: block;
-      margin-bottom: 8rpx;
     }
 
-    .modal-subtitle {
-      font-size: 24rpx;
-      color: #909399;
+    .fs-header-btn {
+      font-size: 28rpx;
+      padding: 10rpx 20rpx;
+
+      &.fs-cancel {
+        color: #606266;
+      }
+
+      &.fs-clear {
+        color: #f56c6c;
+      }
     }
   }
 
-  .signature-canvas-container {
-    .signature-canvas {
+  .fs-body {
+    flex: 1;
+    position: relative;
+    margin: 20rpx;
+    border-radius: 12rpx;
+    overflow: hidden;
+    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
+
+    .fs-canvas {
+      width: 100%;
+      height: 100%;
       display: block;
-      margin: 0 auto;
-      border: 2rpx solid #e4e7ed;
-      border-radius: 8rpx;
       background: #fff;
+      // H5 绔槻姝㈣Е鎽告椂椤甸潰婊氬姩
       touch-action: none;
     }
 
-    .canvas-actions {
-      display: flex;
-      gap: 20rpx;
-      margin: 20rpx 0;
-      padding: 0 20rpx;
-
-      .action-btn {
-        flex: 1;
-        height: 60rpx;
-        border-radius: 30rpx;
-        border: 1rpx solid #dcdfe6;
-        background: #fff;
-        font-size: 24rpx;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        gap: 8rpx;
-
-        &.clear-btn {
-          color: #f56c6c;
-          border-color: #f56c6c;
-        }
-
-        &.redo-btn {
-          color: #1890ff;
-          border-color: #1890ff;
-
-          &:disabled {
-            color: #c0c4cc;
-            border-color: #e4e7ed;
-          }
-        }
-
-        &.confirm-btn {
-          background: linear-gradient(135deg, #0f95b0, #89c4c1);
-          color: #fff;
-          border: none;
-
-          &:disabled {
-            background: #c0c4cc;
-            opacity: 0.6;
-          }
-        }
-
-        &:active:not(:disabled) {
-          transform: scale(0.98);
-        }
-      }
+    // H5 绔師鐢� canvas 瀹瑰櫒锛坈anvas 鐢� JS 鍒涘缓骞舵拺婊★級
+    .fs-canvas-wrap {
+      width: 100%;
+      height: 100%;
+      background: #fff;
     }
 
-    .signature-preview {
-      margin-top: 20rpx;
-      padding: 20rpx;
-      background: #f8f9fa;
-      border-radius: 8rpx;
+    .fs-hint {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      gap: 16rpx;
+      color: #c0c4cc;
+      font-size: 28rpx;
+      pointer-events: none;
+    }
+  }
 
-      .preview-title {
-        display: block;
-        font-size: 24rpx;
-        color: #606266;
-        margin-bottom: 12rpx;
+  // 绗旇Е绮楃粏閫夋嫨鏍�
+  .fs-toolbar {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 48rpx;
+    padding: 16rpx 30rpx;
+    background: #fff;
+    border-top: 1rpx solid #f0f2f5;
+
+    .toolbar-item {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      gap: 8rpx;
+      padding: 8rpx 16rpx;
+
+      .size-dot {
+        border-radius: 50%;
+        transition: background 0.2s;
       }
 
-      .preview-image {
-        width: 200rpx;
-        height: 80rpx;
-        border: 1rpx solid #dcdfe6;
-        border-radius: 4rpx;
+      .size-label {
+        font-size: 22rpx;
+        color: #909399;
+
+        &.active {
+          color: #0f95b0;
+          font-weight: 600;
+        }
+      }
+
+      &:active {
+        opacity: 0.7;
+      }
+    }
+  }
+
+  .fs-footer {
+    display: flex;
+    gap: 24rpx;
+    padding: 20rpx 30rpx;
+    padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
+    padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+    background: #fff;
+    border-top: 1rpx solid #ebeef5;
+
+    .fs-btn {
+      flex: 1;
+      height: 88rpx;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 30rpx;
+      border-radius: 44rpx;
+      margin: 0;
+      line-height: 1;
+
+      &.fs-undo {
+        color: #1890ff;
         background: #fff;
+        border: 1rpx solid #1890ff;
+
+        &:disabled {
+          color: #c0c4cc;
+          border-color: #e4e7ed;
+          background: #f5f7fa;
+        }
+      }
+
+      &.fs-confirm {
+        background: linear-gradient(135deg, #0f95b0, #89c4c1);
+        color: #fff;
+        border: none;
+
+        &:disabled {
+          background: #c0c4cc;
+          opacity: 0.6;
+        }
+      }
+
+      &::after {
+        border: none;
       }
     }
   }
@@ -1927,25 +2403,14 @@
     }
   }
 
-  .signature-modal {
-    padding: 20rpx;
+  .fullscreen-signature {
+    .fs-body {
+      margin: 12rpx;
+    }
 
-    .signature-canvas-container {
-      .signature-canvas {
-        width: 100% !important;
-        height: 250rpx !important;
-      }
-
-      .canvas-actions {
-        // flex-direction: column;
-        gap: 12rpx;
-        padding: 0;
-
-        .action-btn {
-          height: 72rpx;
-        }
-      }
+    .fs-footer {
+      gap: 16rpx;
     }
   }
 }
-</style>
+</style>
\ No newline at end of file

--
Gitblit v1.9.3