From 2ee7a81c2eb0df1fc473da6dbbaa13df662967c5 Mon Sep 17 00:00:00 2001
From: wwl <xchao828@163.com>
Date: 星期三, 02 七月 2025 15:43:59 +0800
Subject: [PATCH] 1

---
 src/views/111111.vue               |  335 +++++++++++++++
 src/views/index.vue                |  198 +++-----
 src/views/system/tijian/index.vue  |   12 
 /dev/null                          |  198 ---------
 package.json                       |    4 
 src/api/system/tijian.js           |    8 
 src/views/system/package/index.vue |  466 ++++++---------------
 src/utils/websocket.js             |   80 +++
 8 files changed, 641 insertions(+), 660 deletions(-)

diff --git a/package.json b/package.json
index 3a3e495..7b4bb63 100644
--- a/package.json
+++ b/package.json
@@ -68,13 +68,17 @@
     "jsencrypt": "3.0.0-rc.1",
     "jspdf": "^3.0.1",
     "lodash": "^4.17.21",
+    "mammoth": "^1.9.1",
     "moment": "^2.30.1",
     "nprogress": "0.2.0",
+    "pdf2json": "^3.1.6",
+    "pdfjs-dist": "^5.3.31",
     "pdfmake": "^0.2.20",
     "pinyin-match": "^1.2.2",
     "print-js": "^1.6.0",
     "quill": "1.3.7",
     "screenfull": "5.0.2",
+    "socket.io-client": "^4.8.1",
     "sortablejs": "1.10.2",
     "tduck-form-generator": "^1.8.0",
     "tinymce": "^7.9.1",
diff --git a/src/api/system/tijian.js b/src/api/system/tijian.js
index 26d8c33..0394c36 100644
--- a/src/api/system/tijian.js
+++ b/src/api/system/tijian.js
@@ -55,13 +55,13 @@
   });
 }
 
-export function getProParentIdDxList() {
+export function getProParentIdDxList(xb) {
   return request({
     url: "/hosp/project/getProParentIdDxList",
     method: "get",
-    // params: {
-    //   pname: pname,
-    // },
+    params: {
+      xb: xb,
+    },
   });
 }
 
diff --git a/src/utils/websocket.js b/src/utils/websocket.js
new file mode 100644
index 0000000..a525f68
--- /dev/null
+++ b/src/utils/websocket.js
@@ -0,0 +1,80 @@
+// src/utils/websocket.js
+let socket = null;
+let reconnectAttempts = 0;
+const maxReconnectAttempts = 5;
+const reconnectInterval = 5000;
+let messageCallback = null;
+
+const initWebSocket = (token, callback) => {
+  if (socket && socket.readyState === WebSocket.OPEN) {
+    console.log('WebSocket 宸茶繛鎺ワ紝鏃犻渶閲嶅鍒濆鍖�');
+    return;
+  }
+
+  if (!token) {
+    console.error('鏈彁渚� token锛屾棤娉曞缓绔� WebSocket 杩炴帴');
+    callback('error', '鏈壘鍒� token锛屾棤娉曞缓绔� WebSocket 杩炴帴');
+    return;
+  }
+
+  const wsUrl = `ws://192.168.1.2:5011/ws?token=${encodeURIComponent(token)}`;
+  console.log('灏濊瘯杩炴帴 WebSocket:', wsUrl);
+
+  try {
+    socket = new WebSocket(wsUrl);
+  } catch (error) {
+    console.error('WebSocket 鍒濆鍖栧け璐�:', error);
+    callback('error', '鏃犳硶鍒濆鍖� WebSocket 杩炴帴');
+    return;
+  }
+
+  messageCallback = callback;
+
+  socket.onopen = () => {
+    console.log('WebSocket 杩炴帴鎴愬姛');
+    reconnectAttempts = 0;
+  };
+
+  socket.onmessage = (event) => {
+    console.log('WebSocket 鏀跺埌鍘熷娑堟伅:', event.data);
+    if (messageCallback) {
+      messageCallback('message', event.data);
+    }
+  };
+
+  socket.onerror = (error) => {
+    console.error('WebSocket 閿欒:', error);
+    if (messageCallback) {
+      messageCallback('error', 'WebSocket 杩炴帴閿欒');
+    }
+  };
+
+  socket.onclose = (event) => {
+    console.log('WebSocket 杩炴帴鍏抽棴锛屼唬鐮�:', event.code, '鍘熷洜:', event.reason);
+    if (reconnectAttempts < maxReconnectAttempts) {
+      setTimeout(() => {
+        console.log(`灏濊瘯閲嶈繛 (${reconnectAttempts + 1}/${maxReconnectAttempts})`);
+        reconnectAttempts++;
+        initWebSocket(token, callback);
+      }, reconnectInterval);
+    } else {
+      if (messageCallback) {
+        messageCallback('error', 'WebSocket 閲嶈繛澶辫触锛岃妫�鏌ョ綉缁滄垨鏈嶅姟鍣�');
+      }
+    }
+  };
+};
+
+const closeWebSocket = () => {
+  if (socket) {
+    socket.close(1000, '娴忚鍣ㄥ叧闂紝姝e父鏂紑');
+    socket = null;
+    console.log('WebSocket 杩炴帴宸插叧闂�');
+  }
+};
+
+window.addEventListener('beforeunload', () => {
+  closeWebSocket();
+});
+
+export { initWebSocket, closeWebSocket };
\ No newline at end of file
diff --git a/src/views/111111.vue b/src/views/111111.vue
new file mode 100644
index 0000000..039340f
--- /dev/null
+++ b/src/views/111111.vue
@@ -0,0 +1,335 @@
+<template>
+  <div>
+    <h3>鍓嶇 Word 缂栬緫鍣紙TinyMCE锛�</h3>
+    <div class="actions">
+      <input type="file" accept=".docx" @change="importWord" ref="fileInput" style="margin-right: 10px;" />
+      <button @click="exportToWord">瀵煎嚭 Word</button>
+      <button @click="exportToPDF">瀵煎嚭 PDF</button>
+    </div>
+    <editor v-model="content" :init="editorInit" :api-key="apiKey"></editor>
+  </div>
+</template>
+<script>
+import Editor from '@tinymce/tinymce-vue';
+import { Document, Packer, Paragraph, TextRun, ImageRun, Table as DocxTable, TableRow as DocxTableRow, TableCell as DocxTableCell } from 'docx';
+import { saveAs } from 'file-saver';
+import jsPDF from 'jspdf';
+import html2canvas from 'html2canvas';
+import mammoth from 'mammoth';
+
+export default {
+  components: { Editor },
+  data() {
+    return {
+      apiKey: '3ceaxd5ckw4te35xj38vj3p5rmmeyv0x8pq2yrr92rwdiqzp',
+      content: '',
+      editorInit: {
+        height: 500,
+        menubar: true,
+        plugins: [
+          'advlist autolink lists link image charmap print preview anchor',
+          'searchreplace visualblocks code fullscreen',
+          'insertdatetime media table paste code help wordcount'
+        ],
+        toolbar:
+          'undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image table | removeformat | help',
+        images_upload_handler: (blobInfo, success, failure) => {
+          const reader = new FileReader();
+          reader.onload = () => success(reader.result);
+          reader.onerror = () => failure('鍥剧墖涓婁紶澶辫触');
+          reader.readAsDataURL(blobInfo.blob());
+        },
+        content_style: 'body { font-family: SimSun, Arial; font-size: 14px; }',
+        table_default_attributes: { border: 1 },
+        readonly: false,
+        language: 'zh-CN',
+      },
+    };
+  },
+  methods: {
+    /** 瀵煎叆 Word 鏂囨。 */
+    importWord(event) {
+      const file = event.target.files[0];
+      if (!file) {
+        this.$message.error('璇烽�夋嫨涓�涓� .docx 鏂囦欢');
+        return;
+      }
+      if (!file.name.endsWith('.docx')) {
+        this.$message.error('璇蜂笂浼� .docx 鏍煎紡鐨勬枃浠�');
+        return;
+      }
+
+      const reader = new FileReader();
+      reader.onload = async (e) => {
+        try {
+          const arrayBuffer = e.target.result;
+          const result = await mammoth.convertToHtml({ arrayBuffer });
+          this.content = result.value || '<p>鏃犲唴瀹�</p>';
+          this.$message.success('Word 鏂囨。瀵煎叆鎴愬姛');
+          this.$refs.fileInput.value = '';
+        } catch (err) {
+          console.error('瀵煎叆 Word 澶辫触锛�', err);
+          this.$message.error('瀵煎叆 Word 鏂囨。澶辫触锛�' + err.message);
+        }
+      };
+      reader.onerror = () => {
+        this.$message.error('璇诲彇鏂囦欢澶辫触');
+      };
+      reader.readAsArrayBuffer(file);
+    },
+
+    /** 瀵煎嚭涓� Word */
+    exportToWord() {
+      if (!this.content || this.content.trim() === '') {
+        this.$message.error('缂栬緫鍣ㄥ唴瀹逛负绌猴紝鏃犳硶瀵煎嚭');
+        return;
+      }
+      this.parseHtmlToDocx(this.content).then((elements) => {
+        const doc = new Document({
+          sections: [{ properties: {}, children: elements }],
+        });
+
+        Packer.toBlob(doc).then((blob) => {
+          saveAs(blob, '鏂囨。.docx');
+          this.$message.success('瀵煎嚭 Word 鎴愬姛');
+        }).catch((err) => {
+          console.error('鐢熸垚 Word 澶辫触锛�', err);
+          this.$message.error('鐢熸垚 Word 澶辫触锛�' + err.message);
+        });
+      }).catch((err) => {
+        console.error('瑙f瀽 HTML 澶辫触锛�', err);
+        this.$message.error('瑙f瀽 HTML 澶辫触锛�' + err.message);
+      });
+    },
+
+    /** 瑙f瀽 HTML 涓� docx 鍏冪礌 */
+    parseHtmlToDocx(html) {
+      return new Promise((resolve, reject) => {
+        if (!html || typeof html !== 'string') {
+          reject(new Error('鏃犳晥鐨� HTML 鍐呭'));
+          return;
+        }
+
+        console.log('Input HTML:', html); // 璋冭瘯杈撳叆 HTML
+
+        const parser = new DOMParser();
+        const doc = parser.parseFromString(html, 'text/html');
+        if (!doc || !doc.body) {
+          reject(new Error('HTML 瑙f瀽澶辫触锛氭枃妗g粨鏋勬棤鏁�'));
+          return;
+        }
+
+        const elements = [];
+        const imagePromises = [];
+
+        const processNode = (node) => {
+          if (node.nodeType !== Node.ELEMENT_NODE) {
+            return;
+          }
+
+          console.log('Processing node:', node.tagName); // 璋冭瘯鑺傜偣
+
+          if (node.tagName === 'P') {
+            const childNodes = node.childNodes ? Array.from(node.childNodes) : [];
+            const textRuns = childNodes.map((child) => {
+              if (child.nodeType === Node.TEXT_NODE) {
+                return new TextRun({ text: child.textContent || '' });
+              } else if (child.tagName === 'B') {
+                return new TextRun({ text: child.textContent || '', bold: true });
+              } else if (child.tagName === 'I') {
+                return new TextRun({ text: child.textContent || '', italics: true });
+              } else if (child.tagName === 'IMG') {
+                const promise = this.getImageBase64FromUrl(child.src).then(({ base64, width, height }) => {
+                  return new ImageRun({
+                    data: base64,
+                    transformation: { width, height },
+                  });
+                }).catch((err) => {
+                  console.error('鍥剧墖鍔犺浇澶辫触锛�', err, 'URL:', child.src);
+                  return new TextRun(`[鍥剧墖: ${child.alt || '鍥惧儚'}]`);
+                });
+                imagePromises.push(promise);
+                return promise;
+              }
+              return new TextRun({ text: child.textContent || '' });
+            }).filter(Boolean);
+            if (textRuns.length) {
+              elements.push(new Paragraph({ children: textRuns }));
+            }
+          } else if (node.tagName === 'H1') {
+            elements.push(new Paragraph({ text: node.textContent || '', heading: 'Heading1' }));
+          } else if (node.tagName === 'UL' || node.tagName === 'OL') {
+            const children = node.children ? Array.from(node.children) : [];
+            children.forEach((li) => {
+              elements.push(
+                new Paragraph({
+                  text: li.textContent || '',
+                  bullet: node.tagName === 'UL' ? { level: 0 } : { level: 0, number: true },
+                })
+              );
+            });
+          } else if (node.tagName === 'TABLE') {
+            const rows = node.querySelectorAll('tr') ? Array.from(node.querySelectorAll('tr')) : [];
+            const tableRows = rows.map((tr) => {
+              const cells = tr.querySelectorAll('td, th') ? Array.from(tr.querySelectorAll('td, th')) : [];
+              const tableCells = cells.map((cell) => {
+                return new DocxTableCell({
+                  children: [new Paragraph(cell.textContent || '')],
+                });
+              });
+              return new DocxTableRow({ children: tableCells });
+            });
+            if (tableRows.length) {
+              elements.push(new DocxTable({ rows: tableRows }));
+            }
+          }
+          const children = node.children ? Array.from(node.children) : [];
+          children.forEach(processNode);
+        };
+
+        const nodes = doc.body.childNodes ? Array.from(doc.body.childNodes) : [];
+        if (nodes.length === 0) {
+          console.warn('HTML 鏂囨。鏃犳湁鏁堣妭鐐�');
+          resolve(elements);
+          return;
+        }
+
+        nodes.forEach(processNode);
+
+        Promise.all(imagePromises).then((imageRuns) => {
+          let imageIndex = 0;
+          elements.forEach((element, index) => {
+            if (!(element instanceof Paragraph)) {
+              console.log('Skipping non-Paragraph element at index', index, 'type:', element.constructor.name);
+              return; // 璺宠繃闈� Paragraph 鍏冪礌
+            }
+            if (!Array.isArray(element.children)) {
+              console.warn('Element at index', index, 'has invalid children:', element.children);
+              element.children = []; // 鍒濆鍖栦负绌烘暟缁�
+              return;
+            }
+            console.log('Processing element at index', index, 'children:', element.children);
+            element.children = element.children.map((child, childIndex) => {
+              if (child instanceof Promise) {
+                const imageRun = imageRuns[imageIndex++] || new TextRun('[鍥剧墖鍔犺浇澶辫触]');
+                console.log('Replacing Promise at child index', childIndex, 'with:', imageRun);
+                return imageRun;
+              }
+              return child;
+            });
+          });
+          resolve(elements);
+        }).catch((err) => {
+          console.error('鍥剧墖澶勭悊澶辫触锛�', err);
+          reject(new Error('鍥剧墖澶勭悊澶辫触锛�' + err.message));
+        });
+      });
+    },
+
+    /** 鑾峰彇鍥剧墖鐨� Base64 鏁版嵁鍜屽昂瀵� */
+    getImageBase64FromUrl(url) {
+      return new Promise((resolve, reject) => {
+        if (!url) {
+          reject(new Error('鍥剧墖 URL 涓虹┖'));
+          return;
+        }
+
+        console.log('Fetching image:', url);
+
+        if (url.startsWith('data:image/')) {
+          const base64 = url.split(',')[1];
+          const img = new Image();
+          img.onload = () => {
+            const maxWidth = 200;
+            const maxHeight = 200;
+            let { width, height } = img;
+            if (width > maxWidth || height > maxHeight) {
+              const ratio = Math.min(maxWidth / width, maxHeight / height);
+              width = Math.round(width * ratio);
+              height = Math.round(height * ratio);
+            }
+            resolve({ base64, width, height });
+          };
+          img.onerror = () => reject(new Error('Base64 鍥剧墖鍔犺浇澶辫触'));
+          img.src = url;
+          return;
+        }
+
+        fetch(url, { mode: 'cors' })
+          .then((response) => {
+            if (!response.ok) throw new Error('缃戠粶鍥剧墖鍔犺浇澶辫触');
+            return response.blob();
+          })
+          .then((blob) => {
+            const reader = new FileReader();
+            reader.onloadend = () => {
+              const base64 = reader.result.split(',')[1];
+              const img = new Image();
+              img.onload = () => {
+                const maxWidth = 200;
+                const maxHeight = 200;
+                let { width, height } = img;
+                if (width > maxWidth || height > maxHeight) {
+                  const ratio = Math.min(maxWidth / width, maxHeight / height);
+                  width = Math.round(width * ratio);
+                  height = Math.round(height * ratio);
+                }
+                resolve({ base64, width, height });
+              };
+              img.onerror = () => reject(new Error('鍥剧墖璇诲彇澶辫触'));
+              img.src = reader.result;
+            };
+            reader.onerror = () => reject(new Error('鍥剧墖璇诲彇澶辫触'));
+            reader.readAsDataURL(blob);
+          })
+          .catch((err) => reject(err));
+      });
+    },
+
+    /** 瀵煎嚭涓� PDF */
+    exportToPDF() {
+      if (!this.content || this.content.trim() === '') {
+        this.$message.error('缂栬緫鍣ㄥ唴瀹逛负绌猴紝鏃犳硶瀵煎嚭');
+        return;
+      }
+      const contentElement = document.createElement('div');
+      contentElement.innerHTML = this.content;
+      contentElement.style.padding = '10px';
+      document.body.appendChild(contentElement);
+
+      html2canvas(contentElement, { scale: 2 })
+        .then((canvas) => {
+          const doc = new jsPDF();
+          const imgData = canvas.toDataURL('image/png');
+          doc.addImage(imgData, 'PNG', 10, 10, 190, 0);
+          doc.save('鏂囨。.pdf');
+          this.$message.success('瀵煎嚭 PDF 鎴愬姛');
+          document.body.removeChild(contentElement);
+        })
+        .catch((err) => {
+          console.error('鐢熸垚 PDF 澶辫触锛�', err);
+          this.$message.error('鐢熸垚 PDF 澶辫触锛�' + err.message);
+          document.body.removeChild(contentElement);
+        });
+    },
+  },
+};
+</script>
+<style>
+.actions {
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+.actions button {
+  padding: 8px 16px;
+  margin-right: 10px;
+  background-color: #409eff;
+  color: white;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+}
+.actions button:hover {
+  background-color: #66b1ff;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/ceshi.vue b/src/views/ceshi.vue
deleted file mode 100644
index 4a8f64d..0000000
--- a/src/views/ceshi.vue
+++ /dev/null
@@ -1,198 +0,0 @@
-<template>
-  <div>
-    <h3>鍓嶇 Word 缂栬緫鍣紙TinyMCE锛�</h3>
-    <editor v-model="content" :init="editorInit" :api-key="apiKey"></editor>
-    <div class="actions">
-      <button @click="exportToWord">瀵煎嚭 Word</button>
-      <button @click="exportToPDF">瀵煎嚭 PDF</button>
-    </div>
-  </div>
-</template>
-
-<script>
-import Editor from '@tinymce/tinymce-vue';
-import { Document, Packer, Paragraph, TextRun, ImageRun, Table as DocxTable, TableRow as DocxTableRow, TableCell as DocxTableCell } from 'docx';
-import { saveAs } from 'file-saver';
-import jsPDF from 'jspdf';
-import html2canvas from 'html2canvas';
-
-export default {
-  components: { Editor },
-  data() {
-    return {
-      apiKey: '3ceaxd5ckw4te35xj38vj3p5rmmeyv0x8pq2yrr92rwdiqzp', // 鎮ㄧ殑 TinyMCE API 瀵嗛挜
-      content: `
-
-      `,
-      editorInit: {
-        height: 500,
-        menubar: true,
-        plugins: [
-          'advlist autolink lists link image charmap print preview anchor',
-          'searchreplace visualblocks code fullscreen',
-          'insertdatetime media table paste code help wordcount'
-        ],
-        toolbar:
-          'undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image table | removeformat | help',
-        images_upload_handler: (blobInfo, success, failure) => {
-          const reader = new FileReader();
-          reader.onload = () => success(reader.result);
-          reader.onerror = () => failure('鍥剧墖涓婁紶澶辫触');
-          reader.readAsDataURL(blobInfo.blob());
-        },
-        content_style: 'body { font-family: SimSun, Arial; font-size: 14px; }',
-        table_default_attributes: { border: 1 },
-        readonly: false,
-        language: 'zh_CN', // 灏濊瘯 zh-CN
-      },
-    };
-  },
-  methods: {
-    exportToWord() {
-      this.parseHtmlToDocx(this.content).then((elements) => {
-        const doc = new Document({
-          sections: [{ properties: {}, children: elements }],
-        });
-
-        Packer.toBlob(doc).then((blob) => {
-          saveAs(blob, '鏂囨。.docx');
-        }).catch((err) => {
-          console.error('鐢熸垚 Word 澶辫触锛�', err);
-        });
-      }).catch((err) => {
-        console.error('瑙f瀽 HTML 澶辫触锛�', err);
-      });
-    },
-    parseHtmlToDocx(html) {
-      return new Promise((resolve, reject) => {
-        const parser = new DOMParser();
-        const doc = parser.parseFromString(html, 'text/html');
-        const elements = [];
-
-        const processNode = (node, callback) => {
-          if (node.nodeType === Node.ELEMENT_NODE) {
-            if (node.tagName === 'P') {
-              const textRuns = Array.from(node.childNodes).map((child) => {
-                if (child.nodeType === Node.TEXT_NODE) {
-                  return new TextRun({ text: child.textContent || '' });
-                } else if (child.tagName === 'B') {
-                  return new TextRun({ text: child.textContent || '', bold: true });
-                } else if (child.tagName === 'I') {
-                  return new TextRun({ text: child.textContent || '', italics: true });
-                } else if (child.tagName === 'IMG') {
-                  this.getImageBase64FromUrl(child.src).then((base64) => {
-                    callback(null, new ImageRun({
-                      data: base64,
-                      transformation: { width: 200, height: 200 },
-                    }));
-                  }).catch((err) => {
-                    console.error('鍥剧墖鍔犺浇澶辫触锛�', err);
-                    callback(null, new TextRun(`[鍥剧墖: ${child.alt || '鍥惧儚'}]`));
-                  });
-                  return null;
-                }
-                return new TextRun({ text: child.textContent || '' });
-              }).filter(Boolean);
-              if (textRuns.length) {
-                elements.push(new Paragraph({ children: textRuns }));
-              }
-            } else if (node.tagName === 'H1') {
-              elements.push(new Paragraph({ text: node.textContent || '', heading: 'Heading1' }));
-            } else if (node.tagName === 'UL' || node.tagName === 'OL') {
-              Array.from(node.children).forEach((li) => {
-                elements.push(
-                  new Paragraph({
-                    text: li.textContent || '',
-                    bullet: node.tagName === 'UL' ? { level: 0 } : { level: 0, number: true },
-                  })
-                );
-              });
-            } else if (node.tagName === 'TABLE') {
-              const rows = Array.from(node.querySelectorAll('tr')).map((tr) => {
-                const cells = Array.from(tr.querySelectorAll('td, th')).map((cell) => {
-                  return new DocxTableCell({
-                    children: [new Paragraph(cell.textContent || '')],
-                  });
-                });
-                return new DocxTableRow({ children: cells });
-              });
-              elements.push(new DocxTable({ rows }));
-            }
-            Array.from(node.children).forEach((child) => processNode(child, callback));
-          }
-          callback(null);
-        };
-
-        const nodes = Array.from(doc.body.childNodes);
-        let completed = 0;
-        const checkCompletion = () => {
-          completed++;
-          if (completed === nodes.length) {
-            resolve(elements);
-          }
-        };
-
-        if (nodes.length === 0) {
-          resolve(elements);
-        } else {
-          nodes.forEach((node) => {
-            processNode(node, checkCompletion);
-          });
-        }
-      });
-    },
-    getImageBase64FromUrl(url) {
-      return new Promise((resolve, reject) => {
-        fetch(url, { mode: 'cors' })
-          .then((response) => {
-            if (!response.ok) throw new Error('缃戠粶鍥剧墖鍔犺浇澶辫触');
-            return response.blob();
-          })
-          .then((blob) => {
-            const reader = new FileReader();
-            reader.onloadend = () => resolve(reader.result.split(',')[1]);
-            reader.onerror = () => reject(new Error('鍥剧墖璇诲彇澶辫触'));
-            reader.readAsDataURL(blob);
-          })
-          .catch((err) => reject(err));
-      });
-    },
-    exportToPDF() {
-      const contentElement = document.createElement('div');
-      contentElement.innerHTML = this.content;
-      contentElement.style.padding = '10px';
-      document.body.appendChild(contentElement);
-
-      html2canvas(contentElement, { scale: 2 })
-        .then((canvas) => {
-          const doc = new jsPDF();
-          const imgData = canvas.toDataURL('image/png');
-          doc.addImage(imgData, 'PNG', 10, 10, 190, 0);
-          doc.save('鏂囨。.pdf');
-        })
-        .catch((err) => {
-          console.error('鐢熸垚 PDF 澶辫触锛�', err);
-          document.body.removeChild(contentElement);
-        });
-    },
-  },
-};
-</script>
-
-<style>
-.actions {
-  margin-top: 10px;
-}
-.actions button {
-  padding: 8px 16px;
-  margin-right: 10px;
-  background-color: #409eff;
-  color: white;
-  border: none;
-  border-radius: 4px;
-  cursor: pointer;
-}
-.actions button:hover {
-  background-color: #66b1ff;
-}
-</style>
\ No newline at end of file
diff --git a/src/views/index.vue b/src/views/index.vue
index 1ea2590..268ec38 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -1,28 +1,5 @@
 <template>
   <div class="centre">
-    <!-- 浠婃棩閫氱煡 -->
-    <div style="display: flex; justify-content: center; align-items: center;">
-      <h4>浠婃棩閫氱煡</h4>
-    </div>
-    <div class="notice-area">
-      <el-carousel :interval="2000" direction="vertical" :autoplay="true" :loop="true" height="120px"
-        v-if="groupedNoticeList.length > 0" class="carousel">
-        <el-carousel-item v-for="(group, index) in groupedNoticeList" :key="index">
-          <div class="notice-group">
-            <div class="notice-item" v-for="notice in group" :key="notice.noticeId"
-              @click="goToNotice(notice.noticeId)">
-              <el-tag size="small" :type="notice.noticeType === '1' ? 'info' : 'warning'">
-                {{ notice.noticeType === '1' ? '閫氱煡' : '鍏憡' }}
-              </el-tag>
-              <span class="notice-title">{{ notice.noticeTitle || '鏃犳爣棰�' }}</span>
-              <span class="notice-time">{{ parseTime(notice.createTime, '{y}-{m}-{d}') || '鏃犳椂闂�' }}</span>
-            </div>
-          </div>
-        </el-carousel-item>
-      </el-carousel>
-      <div v-else class="no-notice">鏆傛棤閫氱煡</div>
-    </div>
-
     <!-- 浠婃棩缁熻 -->
     <div style="display: flex; justify-content: center; align-items: center;">
       <h4>浠婃棩缁熻</h4>
@@ -79,6 +56,7 @@
 <script>
 import { getCustomer, getOrder, getReportToday, getTobeToday, getPieChart, getChart } from "@/api/home";
 import { noticeToday } from "@/api/system/notice";
+import { initWebSocket } from "@/utils/websocket";
 const echarts = require('echarts/lib/echarts');
 require('echarts/lib/component/title');
 require('echarts/lib/component/tooltip');
@@ -101,27 +79,18 @@
       teamYYNum: [],
       PieChart: [],
       PieChart2: [],
-      noticeList: [],
-      groupedNoticeList: [], // 鍒嗙粍鍚庣殑閫氱煡鍒楄〃
       loading: false
     };
   },
 
   created() {
     this.getList();
+    this.initWebSocket();
   },
 
   watch: {
     $route(to, from) {
       window.location.reload();
-    },
-    noticeList: {
-      handler(newList) {
-        // 灏嗛�氱煡鎸夋瘡缁勪笁鏉″垎缁�
-        this.groupedNoticeList = this.chunkArray(newList, 3);
-        console.log('groupedNoticeList:', this.groupedNoticeList); // 璋冭瘯
-      },
-      deep: true
     }
   },
 
@@ -132,49 +101,91 @@
         query: { noticeId }
       });
     },
+
+    // 鍒濆鍖� WebSocket 杩炴帴
+    initWebSocket() {
+      const token = this.$store.state.user.token || '';
+      initWebSocket(token, (type, data) => {
+        if (type === 'error') {
+          this.$notify.error({
+            title: '閿欒',
+            message: data,
+            duration: 5000,
+            position: 'top-right'
+          });
+          return;
+        }
+
+        try {
+          const message = JSON.parse(data);
+          console.log('WebSocket 瑙f瀽鍚庢秷鎭�:', message);
+          if (message.noticeId && message.noticeTitle) {
+            const noticeTypeLabel = message.noticeType === '1' ? '閫氱煡' : '鍏憡';
+            this.$notify({
+              title: `鏂�${noticeTypeLabel}`,
+              message: message.noticeTitle || '鏃犳爣棰�',
+              type: 'success',
+              duration: 5000, // 鎮仠 5 绉�
+              position: 'top-right',
+              offset: 50,
+              onClick: () => {
+                this.goToNotice(message.noticeId);
+              }
+            });
+          } else {
+            console.log('鏈煡娑堟伅绫诲瀷:', message);
+          }
+        } catch (error) {
+          console.error('娑堟伅瑙f瀽澶辫触:', error, '鍘熷鏁版嵁:', data);
+          this.$notify.info({
+            title: '娑堟伅',
+            message: `鏈嶅姟鍣ㄥ洖搴斿瓧绗︿覆: ${data}`,
+            duration: 5000,
+            position: 'top-right',
+            offset: 50
+          });
+        }
+      });
+    },
+
     getList() {
       this.loading = true;
 
-      // 鏌ヨ鎵�鏈夊叕鍛�
+      // 鏌ヨ鎵�鏈夊叕鍛婏紙浠呯敤浜庡垵濮嬪寲鏁版嵁锛屽彲鏍规嵁闇�瑕佷繚鐣欐垨绉婚櫎锛�
       noticeToday().then(response => {
         console.log('Notice API response:', response);
-        this.noticeList = response.rows || response.data || [];
-        console.log('noticeList:', this.noticeList);
         this.loading = false;
-        this.$nextTick(() => {
-          console.log('Carousel updated');
-        });
       }).catch(error => {
         console.error('Notice API error:', error);
-        this.$message.error("鑾峰彇閫氱煡澶辫触锛�" + error.message);
+        this.$notify.error({
+          title: '閿欒',
+          message: `鑾峰彇閫氱煡澶辫触锛�${error.message}`,
+          duration: 5000,
+          position: 'top-right'
+        });
         this.loading = false;
       });
 
-      // 鏌ヨ浠婃棩鐧昏
       getCustomer().then(response => {
         this.Customer = response.data || response;
         this.loading = false;
       });
 
-      // 鏌ヨ浠婃棩宸叉
       getOrder().then(response => {
         this.Order = response.data || response;
         this.loading = false;
       });
 
-      // 鏌ヨ浠婃棩鎶ュ憡
       getReportToday().then(response => {
         this.ReportToday = response.data || response;
         this.loading = false;
       });
 
-      // 鏌ヨ浠婃棩寰呮
       getTobeToday().then(response => {
         this.TobeToday = response.data || response;
         this.loading = false;
       });
 
-      // 鎶樼嚎鍥�
       getChart().then(response => {
         response.data.forEach(item => {
           this.LineChart.push(item.date);
@@ -211,7 +222,6 @@
         this.loading = false;
       });
 
-      // 楗肩姸鍥�
       getPieChart().then(response => {
         if (response.data) {
           if (response.data.tjdj == 0 || !response.data.tjdj?.length) {
@@ -281,23 +291,6 @@
       });
     },
 
-    // 鏁扮粍鍒嗙粍鏂规硶
-    chunkArray(array, size) {
-      if (!array || array.length === 0) return [];
-      const result = [];
-      for (let i = 0; i < array.length; i += size) {
-        result.push(array.slice(i, i + size));
-      }
-      // 纭繚寰幆婊氬姩骞虫粦锛岃嫢涓嶈冻 size 鏉★紝琛ラ綈
-      if (array.length % size !== 0 && array.length > size) {
-        const lastGroup = result[result.length - 1];
-        while (lastGroup.length < size) {
-          lastGroup.push(array[lastGroup.length % array.length]);
-        }
-      }
-      return result;
-    },
-
     parseTime(time, cFormat) {
       if (!time) return '';
       try {
@@ -327,62 +320,6 @@
   margin: 15px;
   background-color: #f3f3f3;
   padding: 10px;
-}
-
-.notice-area {
-  width: 100%;
-  min-height: 120px;
-  /* 璋冩暣涓轰笁鏉¢�氱煡楂樺害 */
-  background-color: #fff;
-  margin: 10px 0;
-  padding: 0 20px;
-}
-
-.carousel {
-  width: 100% !important;
-}
-
-.notice-group {
-  display: flex;
-  flex-direction: column;
-  height: 120px;
-  /* 纭繚鍖呭惈涓夋潯閫氱煡 */
-}
-
-.notice-item {
-  display: flex;
-  align-items: center;
-  width: 100%;
-  height: 40px;
-  line-height: 40px;
-  font-size: 14px;
-  cursor: pointer;
-}
-
-.notice-title {
-  margin-left: 10px;
-  font-size: 14px;
-  color: #333;
-  flex-grow: 1;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-
-.notice-time {
-  font-size: 12px;
-  color: #999;
-  margin-left: 20px;
-}
-
-.no-notice {
-  width: 100%;
-  height: 120px;
-  /* 涓庤疆鎾珮搴︿竴鑷� */
-  line-height: 120px;
-  text-align: center;
-  color: #999;
-  font-size: 14px;
 }
 
 .top {
@@ -448,4 +385,27 @@
   background-color: #fff;
   margin-right: 20px;
 }
+
+/* 鑷畾涔� Element UI 閫氱煡鏍峰紡 */
+.el-notification {
+  min-width: 300px;
+  background-color: #f0f9eb;
+  border-color: #e1f3d8;
+  color: #67c23a;
+  font-size: 16px;
+  padding: 15px 20px;
+}
+
+.el-notification__title {
+  font-weight: bold;
+}
+
+.el-notification__content {
+  font-size: 14px;
+  color: #333;
+}
+
+.el-notification__closeBtn {
+  color: #999;
+}
 </style>
\ No newline at end of file
diff --git a/src/views/system/package/index.vue b/src/views/system/package/index.vue
index af9f3af..6dc6de8 100644
--- a/src/views/system/package/index.vue
+++ b/src/views/system/package/index.vue
@@ -39,16 +39,11 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <!-- 
-    element-loading-background="rgba(0, 0, 0, 0.1)"
-    element-loading-spinner="el-icon-loading"
-        element-loading-text="姝e湪鍔犺浇涓�..." -->
     <template>
       <el-table v-loading="loading" style="width: 100%" :data="packageList" @selection-change="handleSelectionChange"
         border>
         <el-table-column fixed type="selection" width="40" align="center" :show-overflow-tooltip="true" />
         <el-table-column label="搴忓彿" align="center" width="50" prop="newID" fixed />
-        <!-- <el-table-column label="缂栧彿" align="center" prop="pacId" /> -->
         <el-table-column label="浣撴绫诲埆" width="100px" align="center" prop="categoryNames" fixed>
           <template slot-scope="scope">
             <dict-tag :options="dict.type.dict_tjtype" :value="scope.row.tjCategory" />
@@ -57,55 +52,21 @@
         <el-table-column label="濂楅鍚嶇О" align="center" prop="pacName" width="150px" fixed />
         <el-table-column label="鍘熶环" width="80px" align="center" prop="price" fixed></el-table-column>
         <el-table-column label="鎶樻墸" width="80px" align="center" prop="limits" fixed></el-table-column>
-        <el-table-column label="鐜颁环" width="80px" align="center" prop="newPrice" fixed>
-        </el-table-column>
-        <el-table-column label="鍗曢」鍒楄〃" align="center" prop="allProName" width="1200px">
-        </el-table-column>
-        <!-- <el-table-column
-          label="椤圭洰鏄庣粏"
-          align="center"
-          prop="allSonName"
-          :show-overflow-tooltip="true"
-        > 
-        </el-table-column>-->
+        <el-table-column label="鐜颁环" width="80px" align="center" prop="newPrice" fixed></el-table-column>
+        <el-table-column label="鍗曢」鍒楄〃" align="center" prop="allProName" width="1200px"></el-table-column>
         <el-table-column label="濂楅鎻忚堪" align="center" :show-overflow-tooltip="true" width="120px">
           <template slot-scope="scope">
             <div class="showInline">{{ scope.row.pacRemark }}</div>
           </template>
         </el-table-column>
-        <!-- <el-table-column
-          label="鍥剧墖"
-          align="center"
-          prop="pacPhone"
-          width="100"
-          :show-overflow-tooltip="true"
-        >
-          <template slot-scope="scope">
-            <image-preview :src="scope.row.pacPhone" :width="50" :height="50" />
-          </template>
-        </el-table-column> -->
-
         <el-table-column label="鍏抽敭瀛�" width="110px" align="center" prop="keyNames"></el-table-column>
         <el-table-column label="鏄惁涓婃灦" width="94px" align="center" prop="isOnSale">
           <template slot-scope="scope">
             <dict-tag :options="dict.type.sys_yes_no" :value="scope.row.isOnSale" />
           </template>
         </el-table-column>
-        <!-- <el-table-column
-          label="璇︾粏浠嬬粛"
-          :show-overflow-tooltip="true"
-          width="100px"
-          align="center"
-          prop="detail"
-        ></el-table-column> -->
         <el-table-column label="鎺掑簭" width="50px" align="center" prop="sort"></el-table-column>
         <el-table-column label="灏忕▼搴忎环鏍�" width="90px" align="center" prop="retailPrice"></el-table-column>
-        <!-- <el-table-column
-          label="鍘熶环"
-          width="90px"
-          align="center"
-          prop="counterPrice"
-        ></el-table-column> -->
         <el-table-column label="宸插敭鏁伴噺" width="90px" align="center" prop="saleNum"></el-table-column>
         <el-table-column label="鐘舵��" align="center" prop="pacStatus" fixed="right" width="100px">
           <template slot-scope="scope">
@@ -119,8 +80,6 @@
               v-hasPermi="['system:package:edit']" title="淇敼"></el-button>
             <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
               v-hasPermi="['system:package:remove']" title="鍒犻櫎"></el-button>
-            <!-- <el-button size="mini" type="text" icon="el-icon-circle-check" @click="handleSeach(scope.row)"
-              v-hasPermi="['system:package:Seach']" title="濂楅璇︽儏"></el-button> -->
           </template>
         </el-table-column>
       </el-table>
@@ -136,8 +95,6 @@
     <!-- 淇敼浣撴濂楅瀵硅瘽妗� -->
     <el-dialog :title="title" :visible.sync="open" width="1400px" append-to-body :close-on-click-modal="false">
       <el-form ref="form" :model="form" :rules="rules" label-width="100px" :inline="true">
-        <!-- <div class="dialo">
-        <div class="dialo1"> -->
         <el-form-item label="濂楅鍚嶇О" prop="pacName">
           <span slot="label" style="display: inline-block; border-bottom: 2px solid blue" @click="handlePackage">
             濂楅鍚嶇О
@@ -145,51 +102,23 @@
           <el-input v-model="form.pacName" placeholder="璇疯緭鍏ュ椁愬悕绉�" style="width: 150px" />
         </el-form-item>
         <el-form-item label="濂楅鐘舵��" prop="pacStatus">
-          <!-- <el-radio-group v-model="form.pacStatus">
-              <el-radio :label="0">鍚敤</el-radio>
-              <el-radio :label="1">鍋滅敤</el-radio>
-            </el-radio-group> -->
           <el-select v-model="form.pacStatus" placeholder="璇烽�夋嫨鐘舵��" style="width: 150px" filterable clearable>
             <el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label"
               :value="dict.value"></el-option>
           </el-select>
         </el-form-item>
-
-        <!--  <el-form-item label="濂楅绫荤洰" prop="categoryId">
-          <el-select
-            v-model="form.categoryId"
-            placeholder="璇烽�夋嫨濂楅绫荤洰"
-            style="width: 150px"
-            @change="shangpin"
-            filterable
-            clearablez
-          >
-            <el-option
-              v-for="item in categoryList"
-              :key="item.id"
-              :label="item.name"
-              :value="item.id"
-            ></el-option>
-          </el-select>
-        </el-form-item> -->
         <el-form-item label="浣撴绫诲埆" prop="tjCategory">
           <el-select v-model="form.tjCategory" placeholder="璇烽�夋嫨浣撴绫诲埆" style="width: 150px" filterable clearable>
             <el-option v-for="dict in dict.type.dict_tjtype" :key="dict.value" :label="dict.label"
               :value="dict.value"></el-option>
           </el-select>
         </el-form-item>
-
         <el-form-item label="鏄惁涓婃灦" prop="isOnSale">
-          <!-- <el-radio-group v-model="form.pacStatus">
-              <el-radio :label="0">鍚敤</el-radio>
-              <el-radio :label="1">鍋滅敤</el-radio>
-            </el-radio-group> -->
           <el-select v-model="form.isOnSale" placeholder="璇烽�夋嫨鏄惁涓婃灦" style="width: 140px" filterable clearable>
             <el-option v-for="dict in dict.type.sys_yes_no" :key="dict.value" :label="dict.label"
               :value="dict.value"></el-option>
           </el-select>
         </el-form-item>
-
         <el-form-item label="鎺掑簭" prop="sort">
           <el-input v-model="form.sort" placeholder="璇疯緭鍏ユ帓搴�" style="width: 150px" />
         </el-form-item>
@@ -197,13 +126,12 @@
           <el-input v-model="form.retailPrice" placeholder="璇疯緭鍏ュ皬绋嬪簭浠锋牸" style="width: 150px" />
         </el-form-item>
         <el-form-item label="鍘熶环" prop="pics">
-          <el-input v-model="pics" placeholder="璇疯緭鍏ュ師浠�" style="width: 150px" />
+          <el-input v-model="pics" placeholder="璇疯緭鍏ュ師浠�" style="width: 150px" type="number" />
         </el-form-item>
         <el-form-item label="鎶樻墸">
           <el-input-number style="width: 150px" v-model="youhui" :precision="2" :step="0.1" :max="10" :min="0.1"
-          @change="debounceNumberChange" :debounce="3000"></el-input-number>
+            @change="debounceNumberChange" :debounce="3000"></el-input-number>
         </el-form-item>
-
         <el-form-item label="鐜颁环" prop="xianprice">
           <el-input v-model="form.xianprice" placeholder="鐜颁环" clearable style="width: 140px" @input="changeXianjia"
             type="number" :debounce="3000" min="0"/>
@@ -214,7 +142,6 @@
             <el-option v-for="item in keywordList" :key="item.id" :label="item.keyword" :value="item.id"></el-option>
           </el-select>
         </el-form-item>
-
         <div v-if="!isCollapsed" style="display: flex">
           <div>
             <el-form-item label="濂楅鎻忚堪" prop="pacRemark">
@@ -222,56 +149,19 @@
                 rows="2"></el-input>
             </el-form-item>
             <el-form-item label="璇︾粏浠嬬粛" prop="detail">
-              <!-- <el-input
-            v-model="form.detail"
-            placeholder="璇疯緭鍏ヨ缁嗕粙缁�"
-            style="width: 200px"
-          /> -->
               <editor v-model="form.detail" :min-height="192" style="width: 670px" />
             </el-form-item>
           </div>
-          <!-- </div> -->
           <div class="dialo2">
             <el-form-item label="鍥剧墖">
               <image-upload v-model="form.pacPhone" />
             </el-form-item>
           </div>
-          <!-- </div> -->
         </div>
       </el-form>
       <el-button type="primary" plain size="mini" @click="toggleCollapse">{{
         isCollapsed ? "灞曞紑" : "鏀惰捣"
       }}</el-button>
-      <!-- <el-button type="primary" plain size="mini" icon="el-icon-plus" @click="addmembers()">鏂板鍗曢」</el-button>
-      <el-table v-loading="loading" :data="form.tjProjectList" @selection-change="handleSelectionChange" border
-        max-height="275" style="margin: 10px 0">
-        <el-table-column label="搴忓彿" align="center" type="index" />
-        <el-table-column label="椤圭洰鍚嶇О" align="center" prop="proName" width="180px">
-          <template slot-scope="scope">
-            <el-select filterable v-model="scope.row.proName" placeholder="璇烽�夋嫨椤圭洰鍚嶇О" @change="getSelectValue">
-              <el-option v-for="(item, index) in allList" :key="index" :label="item.proName" :value="item.proName">
-              </el-option>
-            </el-select>
-          </template>
-        </el-table-column>
-        <el-table-column label="椤圭洰鏄庣粏" align="center" prop="allSonProName" width="400px" :show-overflow-tooltip="true">
-        </el-table-column>
-        <el-table-column label="鍘熶环(鍏�)" align="center" prop="proPrice" width="80px" />
-        <el-table-column label="鐜颁环(鍏�)" align="center" prop="priceNow" width="80px">
-          <template slot-scope="scope">
-            <el-input v-model="scope.row.priceNow" autocomplete="off" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
-          </template>
-        </el-table-column>
-        <el-table-column label="鎿嶄綔" fixed="right" align="center" class-name="small-padding fixed-width">
-          <template slot-scope="scope">
-            <el-button size="mini" type="text" icon="el-icon-circle-plus-outline" @click="addmembers(scope.row)"
-              v-hasPermi="['system:package:edit']" title="鏂板琛�"></el-button>
-            <el-button size="mini" type="text" icon="el-icon-delete" @click.native.prevent="Delete(scope.$index)"
-              v-hasPermi="['hosp:consumables:remove']" title="鍒犻櫎"></el-button>
-          </template>
-        </el-table-column>
-      </el-table> -->
-
       <el-row style="display: flex; width: 1300px">
         <el-col>
           <div style="text-align: center; margin-bottom: 10px; margin-top: 10px">
@@ -287,29 +177,6 @@
               :limit.sync="queryParams1.pageSize" @pagination="getDataList" />
           </div>
         </el-col>
-        <!--  <el-col :span="6">
-          <div
-            style="text-align: center; margin-bottom: 10px; margin-top: 10px"
-          >
-            鏄庣粏椤圭洰鍒楄〃
-          </div>
-          <div class="tab3">
-            <el-tree
-              class="filter-tree"
-              v-loading="loading"
-              :data="TreedataList"
-              node-key="proId"
-              :props="defaultProps"
-              :filter-node-method="filterNode"
-              show-checkbox
-              @check-change="handleCurrentChecked1"
-              :default-checked-keys="checkedListkey"
-              ref="trees"
-              :render-content="renderContent"
-            >
-            </el-tree>
-          </div>
-        </el-col> -->
         <el-col>
           <div class="grid-content bg-purple">
             <div style="
@@ -324,12 +191,10 @@
               :span-method="objectSpanMethod">
               <el-table-column prop="proName" label="妫�鏌ラ」鐩�">
               </el-table-column>
-
               <el-table-column prop="priceOrd" label="鍘熶环" align="center" width="80px">
               </el-table-column>
               <el-table-column label="鎶樻墸" width="100px">
                 <template slot-scope="scope">
-                  <!-- 鍙緭鍏ョ函鏁板瓧鎶樻墸 -->
                   <el-input v-model.number="scope.row.limits" @input="calculateDiscount(scope.row)" placeholder="杈撳叆鎶樻墸"
                     size="small" type="number" min="0" step="0.1" max="10">
                   </el-input>
@@ -343,54 +208,16 @@
                   </el-button>
                 </template>
               </el-table-column>
-              <!-- <el-table-column prop="proName" label="鏄庣粏椤圭洰" width="260px">
-              </el-table-column> -->
-
-              <!-- <el-table-column
-                label="鎿嶄綔"
-                align="center"
-                fixed="right"
-                class-name="small-padding fixed-width"
-                width="50px"
-              >
-                <template slot-scope="scope">
-                  <el-button
-                    size="mini"
-                    type="text"
-                    icon="el-icon-delete"
-                    @click="handleDeletes(scope.row)"
-                    title="鍒犻櫎"
-                  ></el-button>
-                </template> 
-              </el-table-column>-->
             </el-table>
             <h4 style="font-weight: 600;padding-left:20px">涓�鍏遍�変腑{{DataList.length}}椤癸紝鍚堣锛歿{ form.xianprice }}鍏�</h4>
           </div>
         </el-col>
       </el-row>
-
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
         <el-button @click="cancel">鍙� 娑�</el-button>
       </div>
     </el-dialog>
-
-    <!-- 濂楅璇︽儏 -->
-    <!-- <el-dialog :title="title" :visible.sync="Seachopen" width="500px" append-to-body>
-      <template>
-        <el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange">鍏ㄩ��</el-checkbox>
-        <div style="margin: 15px 0px; width: 60px"></div>
-        <el-checkbox-group v-model="newproName" @change="handleCheckedCitiesChange">
-          <el-checkbox style="margin: 15px 0px; width: 180px" v-for="item in allList" :label="item.proName"
-            :key="item.proId">{{ item.proName }}</el-checkbox>
-        </el-checkbox-group>
-
-        <div slot="footer" class="dialog-footer">
-          <el-button type="primary" @click="submitcheckbox">纭� 瀹�</el-button>
-          <el-button @click="cancel">鍙� 娑�</el-button>
-        </div>
-      </template>
-    </el-dialog> -->
     <Packages ref="aaa" @add="handleChanges" />
   </div>
 </template>
@@ -483,13 +310,16 @@
         xianprice: null,
         counterPrice: null,
         limits: 10,
+        keywords: [],
       },
       forms: {},
       youhui: 10,
-      initializing: true, // 鍒濆鍖栨爣蹇�
+      initializing: true,
       debounceTimer: null,
       rules: {
-       
+        pacName: [{ required: true, message: "璇疯緭鍏ュ椁愬悕绉�", trigger: "blur" }],
+        pics: [{ required: true, message: "璇疯緭鍏ュ師浠�", trigger: "blur" }],
+        xianprice: [{ required: true, message: "璇疯緭鍏ョ幇浠�", trigger: "blur" }],
       },
     };
   },
@@ -498,7 +328,6 @@
     this.getKeyword();
     this.getCategory();
   },
- 
   methods: {
     debounceNumberChange(currentValue, oldValue) {
       clearTimeout(this.debounceTimer);
@@ -506,16 +335,18 @@
         this.numberChange(currentValue, oldValue);
       }, 300);
     },
-   
+    numberChange(currentValue, oldValue) {
+      // 瀹炵幇鎶樻墸鍙樺寲鐨勯�昏緫锛堝鏋滈渶瑕侊級
+    },
     updateProPrice(row) {
-      const proPrice = new Big(row.priceOrd);
-      const limits = new Big(row.limits);
+      const proPrice = new Big(row.priceOrd || 0);
+      const limits = new Big(row.limits || 10);
       const result = proPrice.times(limits.div(10));
       row.priceNow = result.toNumber();
       this.form.xianprice = this.DataList.reduce((sum, item) => {
-        return sum.plus(new Big(item.priceNow || "0"));
+        return sum.plus(new Big(item.priceNow || 0));
       }, new Big(0)).toNumber();
-      this.youhui = (Math.floor((this.form.xianprice / this.pics) * 100) / 100) * 10;
+      this.youhui = this.pics ? (Math.floor((this.form.xianprice / this.pics) * 100) / 100) * 10 : 10;
     },
     calculateDiscount(row) {
       if (row.limits > 10) {
@@ -530,37 +361,79 @@
     },
     filterNode(value, data) {
       if (!value) return true;
-      return data.proName.indexOf(value) !== -1 || data.proEngName.indexOf(value) !== -1;
+      return data.proName.indexOf(value) !== -1 || (data.proEngName && data.proEngName.indexOf(value) !== -1);
     },
     getList() {
       this.loading = true;
       getPacTjProjectList().then((response) => {
-        this.allList = response.data;
+        this.allList = response.data || [];
         this.loading = false;
       });
       getPacList(this.queryParams).then((response) => {
         response.rows.forEach((item, index) => {
           item.newID = (this.queryParams.pageNum - 1) * this.queryParams.pageSize + index + 1;
         });
-        this.total = response.total;
-        this.packageList = response.rows;
+        this.total = response.total || 0;
+        this.packageList = response.rows || [];
         this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+        this.$message.error("鑾峰彇濂楅鍒楄〃澶辫触");
       });
     },
     getKeyword() {
       this.loading = true;
       listKeyword(this.queryParams).then((response) => {
-        this.keywordList = response.rows;
+        this.keywordList = response.rows || [];
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+        this.$message.error("鑾峰彇鍏抽敭瀛楀垪琛ㄥけ璐�");
       });
     },
     sel(val) {
-      let id = val;
-      this.keys = id.join(",");
+      const maleId = '4'; // 鈥滅敺鈥濈殑 ID
+      const femaleId = '5'; // 鈥滃コ鈥濈殑 ID
+      let selectedIds = [...val]; // 澶嶅埗褰撳墠閫変腑鐨� ID 鏁扮粍
+
+      // 瀹炵幇鈥滅敺鈥濆拰鈥滃コ鈥濅簰鏂ラ�昏緫
+      if (selectedIds.includes(maleId) && selectedIds.includes(femaleId)) {
+        selectedIds = selectedIds.slice(-1); // 淇濈暀鏈�鍚庨�変腑鐨勪竴涓�
+        this.$message.warning('鈥滅敺鈥濆拰鈥滃コ鈥濆叧閿瓧浜掓枼锛屽彧鑳介�夋嫨涓�涓紒');
+      }
+
+      // 鏇存柊 form.keywords 鍜� keys
+      this.form.keywords = selectedIds;
+      this.keys = selectedIds.join(',');
+
+      // 璋冪敤 getDataList锛屼紶閫掓墍鏈夐�変腑鐨勫叧閿瓧 ID锛堥�楀彿鍒嗛殧锛�
+      this.getDataList({ xb: this.keys });
     },
     getCategory() {
       this.loading = true;
       listCategory(this.queryParams).then((response) => {
-        this.categoryList = response.rows;
+        this.categoryList = response.rows || [];
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+        this.$message.error('鑾峰彇濂楅绫荤洰澶辫触');
+      });
+    },
+    getDataList(params = {}) {
+      this.loading = true;
+      const query = { ...this.queryParams1, ...params };
+      search(query).then((response) => {
+        this.Treedata = response.data.list || [];
+        this.total1 = response.data.total || 0;
+        this.pics = this.DataList.reduce((total, item) => total + (item.priceOrd || 0), 0);
+        this.$nextTick(() => {
+          this.$refs.tree.setCheckedKeys(this.checkedNodes);
+          this.initializing = false;
+        });
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+        this.$message.error('鑾峰彇椤圭洰鍒楄〃澶辫触');
       });
     },
     shangpin(vals) {},
@@ -586,8 +459,9 @@
         xianprice: null,
         counterPrice: null,
         limits: 10,
+        keywords: [],
       };
-      this.initializing = true; 
+      this.initializing = true;
       this.resetForm("form");
     },
     handleQuery() {
@@ -629,7 +503,7 @@
       this.checkedListkey = [];
       this.checkedNodes = [];
       this.getDataList();
-      this.youhui = 10
+      this.youhui = 10;
     },
     handleStatusChange(row) {
       let data = {
@@ -637,10 +511,11 @@
         pacStatus: row.pacStatus,
       };
       let text = row.pacStatus === "0" ? "鍚敤" : "鍋滅敤";
-      this.$confirm("纭瑕�" + text + row.pacName + "濂楅鍚楋紵")
+      this.$modal
+        .confirm("纭瑕�" + text + row.pacName + "濂楅鍚楋紵")
         .then(() => updateStatus(data))
         .then(() => {
-          this.msgSuccess(text + "鎴愬姛");
+          this.$modal.msgSuccess(text + "鎴愬姛");
         })
         .catch(() => {
           row.pacStatus = row.pacStatus === "0" ? "0" : "1";
@@ -676,7 +551,7 @@
       getPacInFo(row.pacId).then((response) => {
         const data = response.data || {};
         Object.keys(data).forEach((key) => {
-          this.$set(this.form, key, response.data[key]);
+          this.$set(this.form, key, data[key]);
         });
         this.youhui = data.limits || 10;
         this.pics = data.price || 0;
@@ -689,9 +564,7 @@
         this.checkedkey = this.DataList.map(item => item.proId || '');
         this.checkedListkey = [...this.checkedkey];
         this.checkedNodes = [...this.checkedkey];
-        
         return this.getDataList();
-       
       }).then(() => {
         this.loading = false;
         this.$nextTick(() => {
@@ -706,32 +579,23 @@
       if (this.form.pacName) {
         if (!this.form.tjProjectList) {
           this.form.tjProjectList = [];
-          this.form.tjProjectList.push({
-            id: parseInt(length),
-            proName: "",
-            allSonProName: "",
-            proPrice: "",
-            priceNow: "",
-            proId: "",
-          });
-        } else {
-          this.form.tjProjectList.push({
-            id: parseInt(length),
-            proName: "",
-            allSonProName: "",
-            proPrice: "",
-            priceNow: "",
-            proId: "",
-          });
         }
+        this.form.tjProjectList.push({
+          id: this.form.tjProjectList.length + 1,
+          proName: "",
+          allSonProName: "",
+          proPrice: "",
+          priceNow: "",
+          proId: "",
+        });
       } else {
-        Message.warning("璇峰厛濉啓濂楅鍚嶇О");
+        this.$modal.msgWarning("璇峰厛濉啓濂楅鍚嶇О");
       }
       this.$forceUpdate();
     },
     Delete(index) {
       if (this.form.tjProjectList.length === 0) {
-        this.$alert("璇峰厛閫夋嫨瑕佸垹闄ょ殑鏁版嵁", "鎻愮ず", { confirmButtonText: "纭畾" });
+        this.$modal.alert("璇峰厛閫夋嫨瑕佸垹闄ょ殑鏁版嵁", "鎻愮ず", { confirmButtonText: "纭畾" });
       } else {
         this.form.tjProjectList.splice(index, 1);
       }
@@ -801,20 +665,6 @@
         </span>
       );
     },
-    getDataList() {
-      this.loading = true;
-      search(this.queryParams1).then((response) => {
-        this.Treedata = response.data.list;
-        this.total1 = response.data.total;
-        this.pics = this.DataList.reduce((total, item) => total + item.priceOrd, 0);
-        this.$nextTick(() => {
-          this.$refs.tree.setCheckedKeys(this.checkedNodes);
-          this.initializing = false;
-        });
-       
-      });
-      this.loading = false;
-    },
     handleFilterInput() {
       this.queryParams1.page = 1;
       this.getDataList();
@@ -824,7 +674,7 @@
     },
     handleCurrentChecked(data, checked, indeterminate) {
       if (this.initializing) {
-        return; // 鍒濆鍖栨椂涓嶅鐞�
+        return;
       }
       if (checked) {
         if (!this.DataList.some((item) => item.proId === data.proId)) {
@@ -848,57 +698,21 @@
         }
         this.updateTotalPrice();
       }
-     
     },
     updateTotalPrice() {
       this.form.xianprice = this.DataList.reduce((sum, item) => {
-        return sum.plus(new Big(item.priceNow || "0"));
+        return sum.plus(new Big(item.priceNow || 0));
       }, new Big(0)).toNumber();
-      this.pics = this.DataList.reduce((total, item) => total + item.priceOrd, 0);
+      this.pics = this.DataList.reduce((total, item) => total + (item.priceOrd || 0), 0);
     },
     changeXianjia() {
-      if (this.form.xianprice !== 0) {
+      if (this.form.xianprice !== 0 && this.pics) {
         this.youhui = (Math.floor((this.form.xianprice / this.pics) * 100) / 100) * 10;
       }
     },
-   /*  numberChangeXianPrice(currentValue, oldValue) {
-      if (this.form.xianprice === this.lastXianPrice || !this.form.xianprice) return;
-      clearTimeout(this.debounceTimer);
-      this.debounceTimer = setTimeout(() => {
-        this.$confirm("纭畾淇敼鎵�鏈夊瓙椤圭殑鎶樻墸鍚楋紵", "鎻愮ず", {
-          confirmButtonText: "纭畾",
-          cancelButtonText: "鍙栨秷",
-          type: "warning",
-        })
-          .then(() => {
-            this.youhui = currentValue;
-            let totalYsprice = new Big(0);
-            this.DataList.forEach((item) => {
-              item.limits = this.youhui;
-              const ordPrice = new Big(item.priceOrd);
-              const discount = new Big(item.limits);
-              const result = ordPrice.times(discount.div(10));
-              item.priceNow = result.toNumber();
-              totalYsprice = totalYsprice.plus(new Big(item.priceNow));
-            });
-            if (!totalYsprice.eq(this.form.xianprice)) {
-              const diff = new Big(this.form.xianprice).minus(totalYsprice);
-              if (this.DataList.length > 0) {
-                const lastItem = this.DataList[this.DataList.length - 1];
-                const newYsPrice = new Big(lastItem.priceNow).plus(diff).toNumber();
-                this.$set(this.DataList[this.DataList.length - 1], "priceNow", newYsPrice);
-              }
-            }
-            this.lastXianPrice = this.form.xianprice;
-          })
-          .catch(() => {
-            this.youhui = oldValue;
-          });
-      }, 500);
-    }, */
     spliceData() {
-      for (var i = 0; i < this.DataList.length; i++) {
-        for (var j = i + 1; j < this.DataList.length; j++) {
+      for (let i = 0; i < this.DataList.length; i++) {
+        for (let j = i + 1; j < this.DataList.length; j++) {
           if (this.DataList[i].proId === this.DataList[j].proId) {
             this.DataList.splice(j, 1);
             j--;
@@ -910,60 +724,59 @@
     handleCurrentChecked1(data, checked, checkedNodes) {
       if (!checked) {
         this.DataList = this.DataList.filter((item) => item.proId !== data.proId);
-        this.TotalPrice1 = this.DataList.reduce((sum, item) => sum + item.priceOrd, 0);
+        this.TotalPrice1 = this.DataList.reduce((sum, item) => sum + (item.priceOrd || 0), 0);
         this.pics = this.TotalPrice1;
       } else {
         this.DataList.push(data);
-        this.pics = this.DataList.reduce((sum, item) => sum + item.priceOrd, 0);
+        this.pics = this.DataList.reduce((sum, item) => sum + (item.priceOrd || 0), 0);
         this.spliceData();
         this.TotalPrice1 = this.pics;
       }
     },
     handleDeletes(row) {
       this.DataList = this.DataList.filter((item) => item.proParentId !== row.proParentId);
-      this.TotalPrice1 = this.DataList.reduce((sum, item) => sum + item.priceOrd, 0);
+      this.TotalPrice1 = this.DataList.reduce((sum, item) => sum + (item.priceOrd || 0), 0);
     },
     submitForm() {
-      // 妫�鏌ョ幇浠锋槸鍚﹀彂鐢熷彉鍖�
-      if (this.form.xianprice !== this.lastXianPrice) {
-        this.$confirm("纭畾淇敼鎵�鏈夊瓙椤圭殑鎶樻墸鍚楋紵", "鎻愮ず", {
-          confirmButtonText: "纭畾",
-          cancelButtonText: "鍙栨秷",
-          type: "warning",
-        }).then(() => {
-          // 鏇存柊鎵�鏈夊瓙椤圭殑鎶樻墸
-          this.youhui = (Math.floor((this.form.xianprice / this.pics) * 100) / 100) * 10;
-          let totalYsprice = new Big(0);
-          this.DataList.forEach((item) => {
-            item.limits = this.youhui;
-            const ordPrice = new Big(item.priceOrd);
-            const discount = new Big(item.limits);
-            const result = ordPrice.times(discount.div(10));
-            item.priceNow = result.toNumber();
-            totalYsprice = totalYsprice.plus(new Big(item.priceNow));
-          });
-          
-          if (!totalYsprice.eq(this.form.xianprice)) {
-            const diff = new Big(this.form.xianprice).minus(totalYsprice);
-            if (this.DataList.length > 0) {
-              const lastItem = this.DataList[this.DataList.length - 1];
-              const newYsPrice = new Big(lastItem.priceNow).plus(diff).toNumber();
-              this.$set(this.DataList[this.DataList.length - 1], "priceNow", newYsPrice);
-            }
+      this.$refs.form.validate((valid) => {
+        if (valid) {
+          if (this.form.xianprice !== this.lastXianPrice) {
+            this.$modal
+              .confirm("纭畾淇敼鎵�鏈夊瓙椤圭殑鎶樻墸鍚楋紵", "鎻愮ず", {
+                confirmButtonText: "纭畾",
+                cancelButtonText: "鍙栨秷",
+                type: "warning",
+              })
+              .then(() => {
+                this.youhui = (Math.floor((this.form.xianprice / this.pics) * 100) / 100) * 10;
+                let totalYsprice = new Big(0);
+                this.DataList.forEach((item) => {
+                  item.limits = this.youhui;
+                  const ordPrice = new Big(item.priceOrd || 0);
+                  const discount = new Big(item.limits);
+                  const result = ordPrice.times(discount.div(10));
+                  item.priceNow = result.toNumber();
+                  totalYsprice = totalYsprice.plus(new Big(item.priceNow));
+                });
+                if (!totalYsprice.eq(this.form.xianprice)) {
+                  const diff = new Big(this.form.xianprice).minus(totalYsprice);
+                  if (this.DataList.length > 0) {
+                    const lastItem = this.DataList[this.DataList.length - 1];
+                    const newYsPrice = new Big(lastItem.priceNow).plus(diff).toNumber();
+                    this.$set(this.DataList[this.DataList.length - 1], "priceNow", newYsPrice);
+                  }
+                }
+                return this.saveData();
+              })
+              .catch(() => {
+                this.form.xianprice = this.lastXianPrice;
+              });
+          } else {
+            this.saveData();
           }
-          
-          // 淇濆瓨鏁版嵁
-          return this.saveData();
-        }).catch(() => {
-          // 鐢ㄦ埛鍙栨秷鎿嶄綔锛屾仮澶嶅師鏉ョ殑鐜颁环
-          this.form.xianprice = this.lastXianPrice;
-        });
-      } else {
-        // 鐜颁环娌℃湁鍙樺寲锛岀洿鎺ヤ繚瀛�
-        this.saveData();
-      }
+        }
+      });
     },
-    // 鏂板涓�涓柟娉曞鐞嗕繚瀛橀�昏緫
     saveData() {
       this.form.limits = this.youhui;
       this.form.price = this.pics;
@@ -985,7 +798,7 @@
         pacName: this.form.pacName,
         limits: this.form.limits,
         pacStatus: this.form.pacStatus,
-        packageProjects: packageProjects, 
+        packageProjects: packageProjects,
         newPrice: this.form.xianprice,
         price: this.pics,
         pacId: this.form.pacId || null,
@@ -996,10 +809,12 @@
         pacRemark: this.form.pacRemark,
         detail: this.form.detail,
       };
-      
       return saveOreditTjPacNew(data).then((res) => {
+        this.$modal.msgSuccess("淇濆瓨鎴愬姛");
         this.open = false;
         this.getList();
+      }).catch(() => {
+        this.$message.error("淇濆瓨澶辫触");
       });
     },
     handleDelete(row) {
@@ -1027,11 +842,11 @@
   },
 };
 </script>
+
 <style>
 .el-tooltip__popper {
   max-width: 800px;
 }
-
 .showInline {
   overflow: hidden;
   text-overflow: ellipsis;
@@ -1040,35 +855,22 @@
   line-clamp: 2;
   -webkit-box-orient: vertical;
 }
-
 .pag {
   width: 100%;
   display: flex;
   justify-content: center;
 }
-
 .pag1 {
   width: 30%;
 }
-
 .dialog-footer {
   position: absolute;
   left: 40%;
   bottom: 2%;
 }
-
 .tab3 {
   max-height: 400px;
   overflow-y: auto;
   border: 1px solid #d9d9d9;
 }
-
-/* .custom-tree-node {
-    flex: 1;
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    font-size: 14px;
-    padding-right: 8px;
-  } */
 </style>
\ No newline at end of file
diff --git a/src/views/system/tijian/index.vue b/src/views/system/tijian/index.vue
index 37b2ed6..fc937f1 100644
--- a/src/views/system/tijian/index.vue
+++ b/src/views/system/tijian/index.vue
@@ -2572,6 +2572,8 @@
     },
     handleClick(tab, event) {
       if (this.activeNames == "second") {
+        console.log(this.form);
+        
         this.DataList = [];
         this.checkedkey = [];
         this.TreedataList = [];
@@ -2590,15 +2592,11 @@
         });
       }
     },
-    // gaibian(val){
-    //   let pname = val;
-    //   getProParentIdDxList(pname).then(response=>{
-    //     this.Treedata = response.data.list;
-    //   })
-    // },
     getDataList() {
       this.loading = true;
-      getProParentIdDxList().then((response) => {
+      console.log(this.form);
+      
+      getProParentIdDxList(this.form.cusSex).then((response) => {
         this.Treedata = response.data.list;
 
         // 鍥炴樉 TreedataList 鍒� DataList

--
Gitblit v1.8.0