From fca192d3c38c5dcfbb6ace8bc71d6078f6a079b2 Mon Sep 17 00:00:00 2001
From: 啊鑫 <t2856754968@163.com>
Date: 星期日, 20 七月 2025 18:09:06 +0800
Subject: [PATCH] LLJ附件系统全面优化:多格式文件预览与APK兼容性

---
 APK兼容性优化说明.md        |  217 +++++++++
 pages/QC/LLJ/Add.vue |  852 ++++++++++++++++++++++++++++++++++--
 FTP文件操作接口文档-更新版.md   |  296 ++++++++++++
 store/index.js       |    5 
 4 files changed, 1,310 insertions(+), 60 deletions(-)

diff --git "a/APK\345\205\274\345\256\271\346\200\247\344\274\230\345\214\226\350\257\264\346\230\216.md" "b/APK\345\205\274\345\256\271\346\200\247\344\274\230\345\214\226\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..77c2e81
--- /dev/null
+++ "b/APK\345\205\274\345\256\271\346\200\247\344\274\230\345\214\226\350\257\264\346\230\216.md"
@@ -0,0 +1,217 @@
+# APK鍏煎鎬т紭鍖栬鏄�
+
+## 馃幆 姒傝堪
+閽堝UniApp椤圭洰鎵撳寘鎴怉ndroid APK鏃剁殑鍏煎鎬ч棶棰橈紝宸插疄鏂藉叏闈㈢殑浼樺寲鏂规锛岀‘淇濇墍鏈夊姛鑳藉湪APP鐜涓甯歌繍琛屻��
+
+## 馃敡 宸茶В鍐崇殑鍏煎鎬ч棶棰�
+
+### 1. 鏂囦欢棰勮鍔熻兘鍏煎鎬�
+
+#### 闂鎻忚堪
+- H5鐜鍜孉PP鐜鐨勬枃浠跺姞杞芥満鍒朵笉鍚�
+- 缃戠粶鍥剧墖鍦ˋPP涓彲鑳芥棤娉曠洿鎺ユ樉绀�
+- 涓嶅悓鏂囦欢绫诲瀷闇�瑕佷笉鍚岀殑澶勭悊绛栫暐
+
+#### 瑙e喅鏂规
+```javascript
+// 鍥剧墖棰勮 - APP鐜鍏堜笅杞藉埌鏈湴
+// #ifdef APP-PLUS
+uni.downloadFile({
+    url: url,
+    success: (res) => {
+        // 浣跨敤鏈湴璺緞棰勮
+        uni.navigateTo({
+            url: `/pages/fileView/imageView?url=${encodeURIComponent(res.tempFilePath)}`
+        });
+    }
+});
+// #endif
+
+// H5鐜鐩存帴浣跨敤缃戠粶URL
+// #ifdef H5
+uni.navigateTo({
+    url: `/pages/fileView/imageView?url=${encodeURIComponent(url)}`
+});
+// #endif
+```
+
+### 2. 鏂囦欢涓嬭浇鍔熻兘浼樺寲
+
+#### 闂鎻忚堪
+- H5鐜浣跨敤blob涓嬭浇锛孉PP闇�瑕乽ni.downloadFile
+- APP涓枃浠跺瓨鍌ㄨ矾寰勫拰鏉冮檺澶勭悊
+- 涓嬭浇杩涘害鏄剧ず鍜岀敤鎴蜂綋楠�
+
+#### 瑙e喅鏂规
+```javascript
+// APP鐜浼樺寲
+// #ifdef APP-PLUS
+const saveDir = plus.os.name === 'Android' ? 
+    plus.io.convertLocalFileSystemURL('_downloads/') : 
+    plus.io.convertLocalFileSystemURL('_documents/');
+
+const downloadTask = uni.downloadFile({
+    url: url,
+    filePath: `${saveDir}${fileName}`, // 鎸囧畾淇濆瓨璺緞
+    success: (res) => {
+        // 鎻愪緵鎵撳紑鏂囦欢閫夐」
+        uni.showModal({
+            title: '涓嬭浇鎴愬姛',
+            confirmText: '鎵撳紑鏂囦欢',
+            success: (modalRes) => {
+                if (modalRes.confirm) {
+                    plus.runtime.openFile(res.filePath);
+                }
+            }
+        });
+    }
+});
+
+// 鏄剧ず涓嬭浇杩涘害
+downloadTask.onProgressUpdate((res) => {
+    const progress = Math.round(res.progress);
+    uni.showLoading({ title: `涓嬭浇涓� ${progress}%` });
+});
+// #endif
+```
+
+### 3. 缃戠粶璇锋眰鍏煎鎬�
+
+#### 闂鎻忚堪
+- CORS闂鍦ˋPP涓笉瀛樺湪锛屼絾鏈夊叾浠栭檺鍒�
+- 璇锋眰澶村拰鍝嶅簲澶勭悊鍙兘涓嶅悓
+- 瓒呮椂鍜岄敊璇鐞嗘満鍒�
+
+#### 瑙e喅鏂规
+- **APP鐜**锛氭棤闇�澶勭悊CORS锛屼娇鐢ㄥ師鐢熺綉缁滆姹�
+- **H5鐜**锛氫繚鐣機ORS澶勭悊鍜屽绉嶄笅杞介檷绾х瓥鐣�
+- **缁熶竴閿欒澶勭悊**锛氭彁渚涘弸濂界殑閿欒鎻愮ず
+
+### 4. UI缁勪欢閫傞厤
+
+#### 闂鎻忚堪
+- 寮圭獥鍦ㄤ笉鍚屽睆骞曞昂瀵镐笅鐨勬樉绀�
+- CSS鏍峰紡鍦ˋPP涓殑娓叉煋宸紓
+- 瑙︽懜浜や簰鐨勪紭鍖�
+
+#### 瑙e喅鏂规
+```css
+/* APP鐜涓撶敤鏍峰紡 */
+/* #ifdef APP-PLUS */
+.file-preview-popup {
+    width: 85vw;
+    max-height: 75vh;
+}
+.file-preview-content {
+    max-height: 350px;
+}
+/* #endif */
+```
+
+## 馃摫 鏀寔鐨勬枃浠剁被鍨�
+
+### 瀹屽叏鏀寔棰勮
+- **鏂囨湰鏂囦欢**: txt, log, md, csv, json, xml
+- **鍥剧墖鏂囦欢**: jpg, jpeg, png, gif, bmp, webp
+
+### 鍙嬪ソ鎻愮ず涓嬭浇
+- **Office鏂囨。**: doc, docx, xls, xlsx, ppt, pptx
+- **PDF鏂囦欢**: pdf
+- **鍏朵粬鏍煎紡**: 鏄剧ず涓嶆敮鎸佹彁绀猴紝鎻愪緵涓嬭浇閫夐」
+
+## 馃敀 鏉冮檺鍜屽畨鍏�
+
+### Android鏉冮檺
+APP浼氳嚜鍔ㄧ敵璇蜂互涓嬫潈闄愶細
+- `WRITE_EXTERNAL_STORAGE`: 鏂囦欢鍐欏叆鏉冮檺
+- `INTERNET`: 缃戠粶璁块棶鏉冮檺
+- `ACCESS_NETWORK_STATE`: 缃戠粶鐘舵�佹娴�
+
+### 鏂囦欢瀛樺偍浣嶇疆
+- **Android**: `_downloads/` 鐩綍锛堢敤鎴峰彲璁块棶锛�
+- **iOS**: `_documents/` 鐩綍锛堝簲鐢ㄦ矙鐩掞級
+
+## 馃殌 鎬ц兘浼樺寲
+
+### 涓嬭浇浼樺寲
+- 鏄剧ず瀹炴椂涓嬭浇杩涘害
+- 鑷姩閲嶈瘯鏈哄埗
+- 缃戠粶鐘舵�佹娴�
+- 鏂囦欢澶у皬棰勬
+
+### 鍐呭瓨绠$悊
+- 鍙婃椂閲婃斁涓存椂鏂囦欢
+- 鍥剧墖棰勮浣跨敤鏈湴缂撳瓨
+- 閬垮厤澶ф枃浠跺唴瀛樺崰鐢�
+
+### 鐢ㄦ埛浣撻獙
+- 鍔犺浇鐘舵�佸弽棣�
+- 鍙嬪ソ鐨勯敊璇彁绀�
+- 涓�閿墦寮�涓嬭浇鏂囦欢
+- 鍙栨秷涓嬭浇鍔熻兘
+
+## 馃攳 娴嬭瘯寤鸿
+
+### 璁惧鍏煎鎬ф祴璇�
+1. **涓嶅悓Android鐗堟湰**: 5.0+ 鏀寔
+2. **涓嶅悓灞忓箷灏哄**: 4瀵竳7瀵稿钩鏉�
+3. **涓嶅悓鍝佺墝PDA**: 纭繚纭欢鍔熻兘姝e父
+
+### 鍔熻兘娴嬭瘯娓呭崟
+- [ ] 鏂囦欢棰勮锛堝悇绉嶆牸寮忥級
+- [ ] 鏂囦欢涓嬭浇锛堢綉缁�/绂荤嚎锛�
+- [ ] 瀛樺偍鏉冮檺鐢宠
+- [ ] 澶ф枃浠跺鐞嗭紙>10MB锛�
+- [ ] 缃戠粶涓柇鎭㈠
+- [ ] 寮圭獥鏄剧ず閫傞厤
+
+### 鎬ц兘娴嬭瘯
+- [ ] 鍚姩閫熷害
+- [ ] 鍐呭瓨鍗犵敤
+- [ ] 鏂囦欢鎿嶄綔鍝嶅簲鏃堕棿
+- [ ] 鎵归噺涓嬭浇鎬ц兘
+
+## 馃搵 閮ㄧ讲娉ㄦ剰浜嬮」
+
+### manifest.json 閰嶇疆
+```json
+{
+    "app-plus": {
+        "modules": {
+            "FileSystem": {}
+        },
+        "permissions": {
+            "鍐欏叆绯荤粺瀛樺偍": {
+                "request": "always"
+            }
+        }
+    }
+}
+```
+
+### 鎵撳寘閰嶇疆
+- 鍚敤鏂囦欢绯荤粺妯″潡
+- 閰嶇疆瀛樺偍鏉冮檺
+- 璁剧疆缃戠粶瀹夊叏绛栫暐
+- 浼樺寲鍖呭ぇ灏�
+
+## 馃悰 宸茬煡闄愬埗
+
+1. **澶ф枃浠堕瑙�**: 瓒呰繃50MB鐨勬枃浠跺缓璁洿鎺ヤ笅杞�
+2. **缃戠粶渚濊禆**: 棰勮鍔熻兘闇�瑕佺ǔ瀹氱殑缃戠粶杩炴帴
+3. **瀛樺偍绌洪棿**: 涓嬭浇鏂囦欢浼氬崰鐢ㄨ澶囧瓨鍌ㄧ┖闂�
+4. **绯荤粺搴旂敤**: 鏌愪簺鏂囦欢绫诲瀷闇�瑕佸畨瑁呭搴旂殑闃呰鍣�
+
+## 馃摓 鎶�鏈敮鎸�
+
+濡傞亣鍒板吋瀹规�ч棶棰橈細
+1. 妫�鏌ヨ澶嘇ndroid鐗堟湰锛堝缓璁�5.0+锛�
+2. 纭瀛樺偍鏉冮檺宸叉巿浜�
+3. 娴嬭瘯缃戠粶杩炴帴鐘跺喌
+4. 鏌ョ湅鎺у埗鍙伴敊璇棩蹇�
+
+---
+
+**鏂囨。鐗堟湰**: v1.0  
+**鏈�鍚庢洿鏂�**: 2025-07-20  
+**閫傜敤鐗堟湰**: UniApp v2.0.2+
\ No newline at end of file
diff --git "a/FTP\346\226\207\344\273\266\346\223\215\344\275\234\346\216\245\345\217\243\346\226\207\346\241\243-\346\233\264\346\226\260\347\211\210.md" "b/FTP\346\226\207\344\273\266\346\223\215\344\275\234\346\216\245\345\217\243\346\226\207\346\241\243-\346\233\264\346\226\260\347\211\210.md"
new file mode 100644
index 0000000..1e5bb22
--- /dev/null
+++ "b/FTP\346\226\207\344\273\266\346\223\215\344\275\234\346\216\245\345\217\243\346\226\207\346\241\243-\346\233\264\346\226\260\347\211\210.md"
@@ -0,0 +1,296 @@
+# 闄勪欢绠$悊绯荤粺 - FTP鏂囦欢鎿嶄綔鎺ュ彛鏂囨。 (鏇存柊鐗�)
+
+## 馃毃 閲嶈鏇存柊锛欳ORS璺ㄥ煙闂瑙e喅鏂规
+
+### 闂鎻忚堪
+鍦℉5鐜涓娇鐢� `DownloadFtpFile` 鎺ュ彛鏃跺嚭鐜颁互涓嬮敊璇細
+```
+Refused to get unsafe header "content-disposition"
+```
+
+### 鍘熷洜鍒嗘瀽
+杩欐槸鐢变簬娴忚鍣ㄧ殑CORS锛堣法鍩熻祫婧愬叡浜級瀹夊叏绛栫暐瀵艰嚧鐨勶紝娴忚鍣ㄩ樆姝簡鍓嶇璁块棶鏌愪簺HTTP鍝嶅簲澶淬��
+
+### 瑙e喅鏂规
+
+#### 鍚庣CORS閰嶇疆锛堝繀椤伙級
+鍚庣鎺ュ彛蹇呴』璁剧疆浠ヤ笅CORS鍝嶅簲澶达細
+
+```http
+Access-Control-Allow-Origin: *
+Access-Control-Allow-Methods: GET, POST, OPTIONS
+Access-Control-Allow-Headers: Content-Type, Authorization
+Access-Control-Expose-Headers: Content-Disposition, Content-Length, Content-Type
+```
+
+**鍏抽敭鐐�**: `Access-Control-Expose-Headers` 蹇呴』鍖呭惈 `Content-Disposition`锛屽惁鍒欏墠绔棤娉曡鍙栨枃浠跺悕淇℃伅銆�
+
+#### 鍓嶇澶氱幆澧冨吋瀹癸紙宸插疄鐜帮級
+鍓嶇浠g爜宸叉洿鏂颁负澶氱幆澧冨吋瀹癸細
+
+1. **H5鐜**: 浣跨敤澶氱涓嬭浇鏂瑰紡鐨勯檷绾х瓥鐣�
+   - 鏂规硶1: `<a>` 鏍囩涓嬭浇
+   - 鏂规硶2: `window.open` 涓嬭浇
+   - 鏂规硶3: `fetch` + `blob` 涓嬭浇
+
+2. **APP鐜**: 浣跨敤 `uni.downloadFile` 
+3. **灏忕▼搴忕幆澧�**: 浣跨敤 `uni.downloadFile`
+
+---
+
+## 鍩虹淇℃伅
+- **鏈嶅姟鍣ㄥ湴鍧�**: `http://36.26.21.214:10054/api`
+- **FTP鏈嶅姟鍣�**: `ftp://36.26.21.214`
+- **妯″潡**: LLJ (鏉ユ枡妫�楠�)
+
+---
+
+## 1. 鑾峰彇闄勪欢鍒楄〃
+
+### 鎺ュ彛淇℃伅
+- **URL**: `/LLJ/getAttachments`
+- **鏂规硶**: `POST`
+- **鐢ㄩ��**: 鑾峰彇鎸囧畾妫�楠屽崟鐨勯檮浠跺垪琛�
+
+### 璇锋眰鍙傛暟
+```json
+{
+  "releaseNo": "妫�楠屽崟鍙�"
+}
+```
+
+### 鍝嶅簲鏍煎紡
+```json
+{
+  "status": 0,
+  "message": "鎴愬姛",
+  "data": {
+    "tbBillList": [
+      {
+        "id": 123456,
+        "itemNo": "鐗╂枡缂栧彿",
+        "fattach": "闄勪欢鏂囦欢鍚�.pdf",
+        "ftype": "鏂囦欢绫诲瀷",
+        "fversion": "鐗堟湰鍙�",
+        "fdate": "鍙楁帶鏃ユ湡",
+        "createBy": "涓婁紶浜�",
+        "createDate": "涓婁紶鏃堕棿"
+      }
+    ]
+  }
+}
+```
+
+---
+
+## 2. 棰勮FTP鏂囦欢
+
+### 鎺ュ彛淇℃伅
+- **URL**: `/LLJ/PreviewFtpFile`
+- **鏂规硶**: `GET`
+- **鐢ㄩ��**: 鑾峰彇FTP鏈嶅姟鍣ㄤ笂鏂囦欢鐨勯瑙堟祦
+
+### 璇锋眰鍙傛暟
+| 鍙傛暟鍚� | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|--------|------|------|------|
+| itemNo | string | 鏄� | 鐗╂枡缂栧彿 |
+| fileName | string | 鏄� | 鏂囦欢鍚嶏紙闇�URL缂栫爜锛� |
+| ftpServer | string | 鏄� | FTP鏈嶅姟鍣ㄥ湴鍧� `ftp://36.26.21.214` |
+
+### CORS閰嶇疆瑕佹眰
+```http
+Access-Control-Allow-Origin: *
+Access-Control-Allow-Methods: GET, OPTIONS
+Access-Control-Allow-Headers: Content-Type
+Access-Control-Expose-Headers: Content-Type, Content-Length
+```
+
+### 鏀寔鐨勬枃浠剁被鍨�
+- **PDF鏂囦欢**: `pdf`
+- **鍥剧墖鏂囦欢**: `jpg`, `jpeg`, `png`, `gif`, `bmp`, `webp`
+- **鏂囨湰鏂囦欢**: `txt`, `log`, `md`, `csv`
+- **Office鏂囨。**: `doc`, `docx`, `xls`, `xlsx`, `ppt`, `pptx`
+
+---
+
+## 3. 涓嬭浇FTP鏂囦欢 猸� 閲嶇偣鏇存柊
+
+### 鎺ュ彛淇℃伅
+- **URL**: `/LLJ/DownloadFtpFile`
+- **鏂规硶**: `GET`
+- **鐢ㄩ��**: 浠嶧TP鏈嶅姟鍣ㄤ笅杞芥枃浠�
+
+### 璇锋眰鍙傛暟
+| 鍙傛暟鍚� | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|--------|------|------|------|
+| itemNo | string | 鏄� | 鐗╂枡缂栧彿 |
+| fileName | string | 鏄� | 鏂囦欢鍚嶏紙闇�URL缂栫爜锛� |
+| ftpServer | string | 鏄� | FTP鏈嶅姟鍣ㄥ湴鍧� `ftp://36.26.21.214` |
+
+### 鈿狅笍 鍏抽敭CORS閰嶇疆
+```http
+Access-Control-Allow-Origin: *
+Access-Control-Allow-Methods: GET, OPTIONS
+Access-Control-Allow-Headers: Content-Type, Authorization
+Access-Control-Expose-Headers: Content-Disposition, Content-Length, Content-Type
+```
+
+### 鍝嶅簲澶磋姹�
+```http
+Content-Type: application/octet-stream (鎴栧叿浣撶殑MIME绫诲瀷)
+Content-Disposition: attachment; filename="鏂囦欢鍚�.pdf"
+Content-Length: 鏂囦欢澶у皬
+Access-Control-Allow-Origin: *
+Access-Control-Expose-Headers: Content-Disposition, Content-Length, Content-Type
+```
+
+### 鍓嶇澶勭悊绛栫暐
+```javascript
+// H5鐜锛氬绉嶄笅杞芥柟寮�
+1. <a> 鏍囩涓嬭浇 (棣栭��)
+2. window.open 涓嬭浇 (澶囬��)
+3. fetch + blob 涓嬭浇 (鍏滃簳)
+
+// APP/灏忕▼搴忕幆澧冿細
+uni.downloadFile (鍘熺敓涓嬭浇)
+```
+
+---
+
+## 4. 瀹炵幇寤鸿
+
+### 鍚庣CORS涓棿浠堕厤缃ず渚�
+
+#### .NET Core 绀轰緥
+```csharp
+public void ConfigureServices(IServiceCollection services)
+{
+    services.AddCors(options =>
+    {
+        options.AddPolicy("AllowAll",
+            builder =>
+            {
+                builder
+                    .AllowAnyOrigin()
+                    .AllowAnyMethod()
+                    .AllowAnyHeader()
+                    .WithExposedHeaders("Content-Disposition", "Content-Length", "Content-Type");
+            });
+    });
+}
+
+public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+{
+    app.UseCors("AllowAll");
+}
+```
+
+#### Express.js 绀轰緥
+```javascript
+app.use((req, res, next) => {
+    res.header('Access-Control-Allow-Origin', '*');
+    res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
+    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
+    res.header('Access-Control-Expose-Headers', 'Content-Disposition, Content-Length, Content-Type');
+    next();
+});
+```
+
+### 鏂囦欢涓嬭浇鍝嶅簲绀轰緥
+```http
+HTTP/1.1 200 OK
+Content-Type: application/pdf
+Content-Disposition: attachment; filename="娴嬭瘯鏂囨。.pdf"
+Content-Length: 1024000
+Access-Control-Allow-Origin: *
+Access-Control-Expose-Headers: Content-Disposition, Content-Length, Content-Type
+
+[鏂囦欢浜岃繘鍒舵暟鎹甝
+```
+
+---
+
+## 5. 鏁呴殰鎺掗櫎
+
+### 甯歌闂鍙婅В鍐虫柟妗�
+
+#### 闂1: "Refused to get unsafe header"
+**鍘熷洜**: 鍚庣鏈缃� `Access-Control-Expose-Headers`
+**瑙e喅**: 娣诲姞 `Access-Control-Expose-Headers: Content-Disposition, Content-Length, Content-Type`
+
+#### 闂2: CORS棰勬璇锋眰澶辫触
+**鍘熷洜**: 鍚庣鏈鐞� OPTIONS 璇锋眰
+**瑙e喅**: 娣诲姞 OPTIONS 鏂规硶鏀寔鍜岀浉搴旂殑CORS澶�
+
+#### 闂3: 鏂囦欢涓嬭浇浣嗘枃浠跺悕涔辩爜
+**鍘熷洜**: Content-Disposition 涓殑鏂囦欢鍚嶇紪鐮侀棶棰�
+**瑙e喅**: 浣跨敤 UTF-8 缂栫爜鎴� RFC 5987 鏍煎紡
+```http
+Content-Disposition: attachment; filename*=UTF-8''%E6%B5%8B%E8%AF%95%E6%96%87%E6%A1%A3.pdf
+```
+
+#### 闂4: 澶ф枃浠朵笅杞借秴鏃�
+**鍘熷洜**: 鏈嶅姟鍣ㄦ垨缃戠粶瓒呮椂
+**瑙e喅**: 
+- 澧炲姞鏈嶅姟鍣ㄨ秴鏃惰缃�
+- 瀹炵幇鏂偣缁紶
+- 鎻愪緵鍒嗙墖涓嬭浇
+
+---
+
+## 6. 娴嬭瘯鏂规
+
+### 娴嬭瘯鐜
+1. **H5鐜**: Chrome, Safari, Firefox
+2. **APP鐜**: Android, iOS 鍘熺敓搴旂敤
+3. **灏忕▼搴忕幆澧�**: 寰俊灏忕▼搴�
+
+### 娴嬭瘯鐢ㄤ緥
+```javascript
+// 娴嬭瘯CORS閰嶇疆
+fetch('http://36.26.21.214:10054/api/LLJ/DownloadFtpFile?itemNo=TEST&fileName=test.pdf&ftpServer=ftp%3A//36.26.21.214', {
+    method: 'GET'
+})
+.then(response => {
+    console.log('Content-Disposition:', response.headers.get('Content-Disposition'));
+    // 搴旇鑳芥甯歌幏鍙栧埌鏂囦欢鍚�
+})
+.catch(error => {
+    console.error('CORS Error:', error);
+});
+```
+
+---
+
+## 7. 鐩戞帶涓庢棩蹇�
+
+### 寤鸿鐩戞帶鎸囨爣
+- 涓嬭浇鎴愬姛鐜�
+- 涓嬭浇鑰楁椂
+- CORS閿欒棰戠巼
+- 鏂囦欢涓嶅瓨鍦ㄩ敊璇鐜�
+
+### 鏃ュ織璁板綍寤鸿
+```javascript
+// 璁板綍涓嬭浇璇锋眰
+{
+    "timestamp": "2025-07-20T10:00:00Z",
+    "method": "DownloadFtpFile",
+    "itemNo": "ABC123",
+    "fileName": "test.pdf",
+    "userAgent": "Mozilla/5.0...",
+    "success": true,
+    "errorCode": null,
+    "responseTime": 1500
+}
+```
+
+---
+
+**鏂囨。鐗堟湰**: v2.0 (CORS鏇存柊鐗�)  
+**鏈�鍚庢洿鏂�**: 2025-07-20  
+**鏇存柊鍐呭**: 
+- 鏂板CORS璺ㄥ煙闂瑙e喅鏂规
+- 鏇存柊鍓嶇澶氱幆澧冧笅杞界瓥鐣�
+- 娣诲姞鍚庣CORS閰嶇疆绀轰緥
+- 瀹屽杽鏁呴殰鎺掗櫎鎸囧崡
\ No newline at end of file
diff --git a/pages/QC/LLJ/Add.vue b/pages/QC/LLJ/Add.vue
index 04f0623..52ddd2a 100644
--- a/pages/QC/LLJ/Add.vue
+++ b/pages/QC/LLJ/Add.vue
@@ -103,12 +103,6 @@
 
 
 
-		<!-- 鎿嶄綔鎸夐挳鍖� -->
-		<view class="action-buttons" v-if="this.current">
-		            <button class="secondary-btn" @click="handleEmergencyRelease">绱ф�ユ斁琛�</button>
-		            <button class="secondary-btn" @click="handleWithdraw">鎾ゅ洖</button>
-		            <button class="secondary-btn" @click="getInspectionItems">鑾峰彇妫�楠岄」鐩�</button>
-		</view>
 		
 
 
@@ -147,14 +141,22 @@
 			</table>
 		</view>
 
-		<!-- 鎿嶄綔鎸夐挳鍖� -->
-		<view class="action-buttons">
-			<button class="secondary-btn" @click="addDestruction" v-if="this.current">鐮村潖瀹為獙</button>
-			<button class="secondary-btn" @click="uploadImages">涓婁紶/鏌ョ湅鍥剧墖</button>
-			<button class="secondary-btn" @click="fetchDrawingNumber">璋冨彇PLM鍥剧焊</button>
-			<button class="secondary-btn" @click="viewAttachmentInfo">鏌ョ湅闄勪欢淇℃伅</button>
-			<button class="secondary-btn" @click="addDefectDescription" v-if="this.current">娣诲姞涓嶈壇鎻忚堪</button>
-			<button class="primary-btn" @click="submitInspection" v-if="this.current">妫�楠屾彁浜�</button>
+		<!-- 椤甸潰鍐呭鍖哄煙 -->
+		<view class="content-wrapper">
+			<!-- 涓哄簳閮ㄦ寜閽暀鍑虹┖闂� -->
+		</view>
+		
+		<!-- 鍥哄畾鍦ㄥ簳閮ㄧ殑鎿嶄綔鎸夐挳鍖� -->
+		<view class="fixed-action-buttons">
+			<button class="action-btn" @click="handleEmergencyRelease" v-if="this.current">绱ф�ユ斁琛�</button>
+			<button class="action-btn" @click="handleWithdraw" v-if="this.current">鎾ゅ洖</button>
+			<button class="action-btn" @click="getInspectionItems" v-if="this.current">鑾峰彇妫�楠岄」鐩�</button>
+			<button class="action-btn" @click="addDestruction" v-if="this.current">鐮村潖瀹為獙</button>
+			<button class="action-btn" @click="uploadImages">涓婁紶/鏌ョ湅鍥剧墖</button>
+			<button class="action-btn" @click="fetchDrawingNumber">璋冨彇PLM鍥剧焊</button>
+			<button class="action-btn" @click="viewAttachmentInfo">鏌ョ湅闄勪欢淇℃伅</button>
+			<button class="action-btn" @click="addDefectDescription" v-if="this.current">娣诲姞涓嶈壇鎻忚堪</button>
+			<button class="action-btn primary" @click="submitInspection" v-if="this.current">妫�楠屾彁浜�</button>
 		</view>
 		<view v-if="remarksPopup" class="overlay">
 			<view class="popup">
@@ -189,8 +191,8 @@
 		
 		<view class="barcode">
 			<u-modal :show="drawingShow" title="鍥剧焊鏄庣粏" @confirm="drawingConfirm" @cancel="drawingCancel"
-				showCancelButton>
-				<uni-table border stripe emptyText="鏆傛棤鏇村鏁版嵁" style="margin-left: 5px;margin-right: 5px;height: 500px;">
+				showCancelButton :z-index="1000">
+				<uni-table border stripe emptyText="鏆傛棤鏇村鏁版嵁" style="margin-left: 5px;margin-right: 5px;height: 400px;max-height: 60vh;overflow-y: auto;">
 					<uni-tr>
 						<uni-th align="center">鐩稿叧鏂囨。</uni-th>
 						<uni-th align="center" width="90">鏈夋棤鍏宠仈PDF鏂囦欢</uni-th>
@@ -227,19 +229,16 @@
 					<div class="attachment-detail-row"><span class="attachment-label">鍙楁帶鏃ユ湡锛�</span><span>{{ selectedAttachment.fdate }}</span></div>
 					<div class="attachment-detail-row"><span class="attachment-label">涓婁紶浜猴細</span><span>{{ selectedAttachment.createBy }}</span></div>
 					<div class="attachment-detail-row"><span class="attachment-label">涓婁紶鏃堕棿锛�</span><span>{{ selectedAttachment.createDate }}</span></div>
-					<div v-if="isPreviewable(selectedAttachment.fattach)" class="attachment-preview-area">
-						<div v-if="['pdf','jpg','jpeg','png','gif'].includes(selectedAttachment.fattach.trim().split('.').pop().toLowerCase())">
-							<iframe :src="getAttachmentUrl(selectedAttachment)" style="width:100%;height:320px;border-radius:10px;background:#f8fafc;" frameborder="0"></iframe>
-						</div>
-						<div v-else-if="['txt'].includes(selectedAttachment.fattach.trim().split('.').pop().toLowerCase())">
-							<iframe :src="getAttachmentUrl(selectedAttachment)" style="width:100%;height:320px;border-radius:10px;background:#f8fafc;" frameborder="0"></iframe>
-						</div>
-						<div v-else-if="['doc','docx','xls','xlsx'].includes(selectedAttachment.fattach.trim().split('.').pop().toLowerCase())">
-							<iframe :src="'https://view.officeapps.live.com/op/view.aspx?src=' + encodeURIComponent(getAttachmentUrl(selectedAttachment))" style="width:100%;height:320px;border-radius:10px;background:#f8fafc;" frameborder="0"></iframe>
-						</div>
-					</div>
-					<div v-else class="attachment-download-area">
-						<button class="attachment-download-link" @click="downloadAttachment(selectedAttachment)">涓嬭浇闄勪欢</button>
+					<div class="attachment-actions-detail">
+						<button class="attachment-action-btn preview-btn" 
+							@click="previewFtpFile(selectedAttachment)" 
+							v-if="isPreviewable(selectedAttachment.fattach)">
+							馃攳 鍦ㄧ嚎棰勮
+						</button>
+						<button class="attachment-action-btn download-btn" 
+							@click="downloadAttachment(selectedAttachment)">
+							馃摜 涓嬭浇鏂囦欢
+						</button>
 					</div>
 				</div>
 				<div v-else class="attachment-detail-empty">鏆傛棤闄勪欢淇℃伅</div>
@@ -254,16 +253,58 @@
 				<div v-else-if="attachments.length === 0">鏆傛棤闄勪欢</div>
 				<ul class="attachment-list" v-else>
 					<li v-for="item in attachments" :key="item.id">
-						<span class="attachment-name" @click="showAttachmentDetailDialog(item)">
-							{{ item.fattach }}
-						</span>
+						<div class="attachment-info">
+							<span class="attachment-name" @click="showAttachmentDetailDialog(item)">
+								{{ item.fattach }}
+							</span>
+							<div class="attachment-meta">
+								<span class="attachment-type">{{ item.ftype || '鏈煡绫诲瀷' }}</span>
+							</div>
+						</div>
 						<div class="attachment-actions">
 							<button class="secondary-btn" @click="showAttachmentDetailDialog(item)">璇︽儏</button>
+							<button class="secondary-btn preview-btn" @click="previewFtpFile(item)" 
+								v-if="isPreviewable(item.fattach)">棰勮</button>
 							<button class="secondary-btn" @click="downloadAttachment(item)">涓嬭浇</button>
 						</div>
 					</li>
 				</ul>
 				<button class="attachment-popup-close" @click="closeAttachmentPopup">鍏抽棴</button>
+			</view>
+		</view>
+		
+		<!-- 鏂囦欢棰勮寮圭獥 -->
+		<view v-if="showFilePreviewPopup" class="overlay">
+			<view class="popup file-preview-popup">
+				<h3 class="file-preview-title">{{ previewTitle }}</h3>
+				<div class="file-preview-divider"></div>
+				<div class="file-preview-content">
+					<!-- 鏂囨湰鍐呭棰勮 -->
+					<pre v-if="previewType === 'text'">{{ previewContent }}</pre>
+					
+					<!-- 鍥剧墖鍐呭棰勮 -->
+					<view v-else-if="previewType === 'image'" class="image-preview-container">
+						<image :src="previewContent" mode="widthFix" style="width: 100%; max-height: 400px;"></image>
+					</view>
+					
+					<!-- Excel 绛� Office 鏂囦欢鎻愮ず -->
+					<view v-else-if="previewType === 'excel'" class="unsupported-preview">
+						<view class="unsupported-icon">馃搳</view>
+						<view class="unsupported-text">Excel 鏂囦欢鏆備笉鏀寔鍦ㄧ嚎棰勮</view>
+						<view class="unsupported-hint">璇风偣鍑讳笅杞芥寜閽幏鍙栧畬鏁存枃浠�</view>
+					</view>
+					
+					<!-- 涓嶆敮鎸佺殑鏂囦欢绫诲瀷 -->
+					<view v-else class="unsupported-preview">
+						<view class="unsupported-icon">馃搫</view>
+						<view class="unsupported-text">姝ゆ枃浠舵牸寮忔殏涓嶆敮鎸侀瑙�</view>
+						<view class="unsupported-hint">璇风偣鍑讳笅杞芥寜閽幏鍙栧畬鏁存枃浠�</view>
+					</view>
+				</div>
+				<div class="file-preview-actions">
+					<button v-if="previewType !== 'text'" class="file-preview-btn download-btn" @click="downloadPreviewFile">馃摜 涓嬭浇鏂囦欢</button>
+					<button class="file-preview-btn close-btn" @click="closeFilePreview">鍏抽棴</button>
+				</div>
 			</view>
 		</view>
 	</view>
@@ -325,6 +366,11 @@
 				attachmentsLoading: false,
 				selectedAttachment: null,
 				showAttachmentDetail: false,
+				showFilePreviewPopup: false,
+				previewContent: '',
+				previewTitle: '',
+				previewItemNo: '',
+				previewType: '', // 'text', 'image', 'excel', 'unsupported'
 				
 			}
 		},
@@ -1286,6 +1332,11 @@
 					this.attachmentsLoading = false;
 					if (res.status === 0) {
 						this.attachments = res.data.tbBillList;
+						// 涓烘瘡涓檮浠惰缃粯璁ゅ彲鐢ㄧ姸鎬�
+						this.attachments.forEach((item, index) => {
+							this.$set(item, 'ftpAvailable', true); // 榛樿璁や负鏂囦欢鍙敤
+							this.$set(item, 'checking', false);
+						});
 					} else if (res.status === 1 && res.message === "璇ユ楠屽崟鏈笂浼犻檮浠朵俊鎭紒") {
 						uni.showToast({ title: res.message, icon: "none" });
 					} else {
@@ -1297,10 +1348,10 @@
 				this.showAttachmentPopup = false;
 			},
 			getAttachmentUrl(item) {
-				const baseUrl = "http://192.168.1.22:10054";
 				// 鍘婚櫎鎵�鏈夌┖鐧藉瓧绗︼紙鍖呮嫭涓嫳鏂囩┖鏍笺�佸埗琛ㄧ绛夛級
-				let fileName = item.fattach.replace(/[\s\u3000]+/g, '').trim();
-				return baseUrl + "/api/LLJ/DownloadAttachment?itemNo=" + encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName);
+				let fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim();
+				// 缁熶竴浣跨敤FTP涓嬭浇鎺ュ彛锛屽寘鍚獸TP鏈嶅姟鍣ㄥ湴鍧�
+				return this.$store.state.serverInfo.serverAPI + "/LLJ/DownloadFtpFile?itemNo=" + encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer);
 			},
 			showAttachmentDetailDialog(item) {
 				console.log('鏌ョ湅璇︽儏', item);
@@ -1319,7 +1370,13 @@
 				const ext = filename.trim().split('.').pop().toLowerCase();
 				// 鏀寔鍦ㄧ嚎棰勮鐨勬枃浠剁被鍨�
 				return [
-					'pdf', 'jpg', 'jpeg', 'png', 'gif', 'txt', 'doc', 'docx', 'xls', 'xlsx'
+					'pdf',           // PDF鏂囦欢
+					'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp',  // 鍥剧墖鏂囦欢
+					'txt', 'log', 'md',  // 鏂囨湰鏂囦欢
+					'doc', 'docx',   // Word鏂囨。
+					'xls', 'xlsx',   // Excel琛ㄦ牸
+					'ppt', 'pptx',   // PowerPoint婕旂ず鏂囩
+					'csv'            // CSV鏂囦欢
 				].includes(ext);
 			},
 			// 澶勭悊闄勪欢涓嬭浇閿欒
@@ -1330,32 +1387,474 @@
 					showCancel: false
 				});
 			},
-			downloadAttachment(item) {
-				const baseUrl = "http://192.168.1.22:10054";
-				// 鍘婚櫎鎵�鏈夌┖鏍笺�佸叏瑙掔┖鏍笺�佸洖杞︺�佹崲琛�
+			// 棰勮FTP鏂囦欢
+			previewFtpFile(item) {
 				const fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim();
-				const url = baseUrl + "/api/Llj/DownloadFtpFile?itemNo=" + encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName);
-				uni.downloadFile({
+				const fileExt = fileName.split('.').pop().toLowerCase();
+				
+				// 妫�鏌ユ枃浠剁被鍨嬫槸鍚︽敮鎸侀瑙�
+				if (!this.isPreviewable(fileName)) {
+					uni.showModal({
+						title: '涓嶆敮鎸侀瑙�',
+						content: '璇ユ枃浠剁被鍨嬩笉鏀寔鍦ㄧ嚎棰勮锛岃涓嬭浇鍚庢煡鐪�',
+						showCancel: false
+					});
+					return;
+				}
+				
+				const previewUrl = this.$store.state.serverInfo.serverAPI + "/LLJ/PreviewFtpFile?itemNo=" + encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer);
+				
+				// 鏍规嵁鏂囦欢绫诲瀷杩涜涓嶅悓鐨勯瑙堝鐞�
+				if (['pdf'].includes(fileExt)) {
+					this.previewPdfFile(previewUrl, fileName);
+				} else if (['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(fileExt)) {
+					this.previewImageFile(previewUrl, fileName);
+				} else if (['txt'].includes(fileExt)) {
+					this.previewTextFile(previewUrl, fileName);
+				} else if (['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'].includes(fileExt)) {
+					this.previewOfficeFile(previewUrl, fileName);
+				} else {
+					// 灏濊瘯閫氱敤棰勮
+					this.previewGenericFile(previewUrl, fileName);
+				}
+			},
+			
+			// 棰勮PDF鏂囦欢
+			previewPdfFile(url, fileName) {
+				// 鍏堜笅杞絇DF鏂囦欢锛岃浆涓篵ase64鍚庨瑙�
+				uni.request({
 					url: url,
+					method: 'GET',
+					responseType: 'arraybuffer',
 					success: (res) => {
 						if (res.statusCode === 200) {
-							if (typeof plus !== 'undefined' && plus.runtime && plus.runtime.openFile) {
-								plus.runtime.openFile({ path: res.tempFilePath }, () => {
-									uni.showToast({ title: '鎵撳紑鎴愬姛', icon: 'success' });
-								}, (e) => {
-									uni.showModal({ title: '鎻愮ず', content: '鏂囦欢涓嬭浇鎴愬姛锛屼絾鏃犳硶鑷姩鎵撳紑銆傝鍦ㄦ枃浠剁鐞嗕腑鎵嬪姩鏌ユ壘骞舵墦寮�銆�', showCancel: false });
-								});
-							} else {
-								uni.showModal({ title: '鎻愮ず', content: '鏂囦欢涓嬭浇鎴愬姛锛屼絾褰撳墠鐜鏃犳硶鑷姩鎵撳紑銆傝鍦ㄦ枃浠剁鐞嗕腑鎵嬪姩鏌ユ壘骞舵墦寮�銆�', showCancel: false });
-							}
+							const base64Data = uni.arrayBufferToBase64(res.data);
+							// 瀛樺偍鍒板叏灞�鍙橀噺
+							getApp().globalData.tempPDF = base64Data;
+							uni.navigateTo({
+								url: `/pages/fileView/pdfView`
+							});
 						} else {
-							uni.showModal({ title: '涓嬭浇澶辫触', content: `涓嬭浇澶辫触锛岀姸鎬佺爜锛�${res.statusCode}`, showCancel: false });
+							this.handlePreviewError(res.statusCode, fileName);
 						}
 					},
 					fail: (error) => {
-						uni.showModal({ title: '涓嬭浇澶辫触', content: `涓嬭浇澶辫触锛岃妫�鏌ョ綉缁滆繛鎺ャ��${error.errMsg}`, showCancel: false });
+						this.handlePreviewError(0, fileName, error.errMsg);
 					}
 				});
+			},
+			
+			// 棰勮鍥剧墖鏂囦欢
+			previewImageFile(url, fileName) {
+				// #ifdef APP-PLUS
+				// APP鐜锛氬厛涓嬭浇鍒版湰鍦板啀棰勮锛岄伩鍏嶇綉缁滃浘鐗囧姞杞介棶棰�
+				uni.showLoading({ title: '鍔犺浇鍥剧墖...' });
+				uni.downloadFile({
+					url: url,
+					success: (res) => {
+						uni.hideLoading();
+						if (res.statusCode === 200) {
+							// 浣跨敤鏈湴涓存椂璺緞
+							uni.navigateTo({
+								url: `/pages/fileView/imageView?url=${encodeURIComponent(res.tempFilePath)}`
+							});
+						} else {
+							this.handlePreviewError(res.statusCode, fileName);
+						}
+					},
+					fail: (error) => {
+						uni.hideLoading();
+						this.handlePreviewError(0, fileName, error.errMsg);
+					}
+				});
+				// #endif
+				
+				// #ifdef H5 || MP
+				// H5鍜屽皬绋嬪簭锛氱洿鎺ヤ娇鐢ㄧ綉缁淯RL
+				uni.navigateTo({
+					url: `/pages/fileView/imageView?url=${encodeURIComponent(url)}`
+				});
+				// #endif
+			},
+			
+			// 棰勮鏂囨湰鏂囦欢
+			previewTextFile(url, fileName) {
+				// 鏂囨湰鏂囦欢鐩存帴鏄剧ず鍦ㄥ脊绐椾腑
+				uni.showLoading({ title: '鍔犺浇鏂囦欢鍐呭...' });
+				uni.request({
+					url: url,
+					method: 'GET',
+					success: (res) => {
+						uni.hideLoading();
+						if (res.statusCode === 200) {
+							const fileType = this.getFileType(fileName);
+							
+							if (fileType === 'text') {
+								// 鏂囨湰鏂囦欢锛氭樉绀哄唴瀹�
+								this.showFilePreview(res.data, fileName);
+							} else if (fileType === 'image') {
+								// 鍥剧墖鏂囦欢锛氭樉绀哄浘鐗嘦RL
+								this.showFilePreview(url, fileName);
+							} else {
+								// 鍏朵粬鏂囦欢绫诲瀷锛氭樉绀烘彁绀轰俊鎭�
+								this.showFilePreview('', fileName);
+							}
+						} else {
+							this.handlePreviewError(res.statusCode, fileName);
+						}
+					},
+					fail: (error) => {
+						uni.hideLoading();
+						this.handlePreviewError(0, fileName, error.errMsg);
+					}
+				});
+			},
+			
+			// 妫�娴嬫枃浠剁被鍨�
+			getFileType(fileName) {
+				const fileExt = fileName.split('.').pop().toLowerCase();
+				
+				if (['txt', 'log', 'md', 'csv', 'json', 'xml'].includes(fileExt)) {
+					return 'text';
+				} else if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(fileExt)) {
+					return 'image';
+				} else if (['xls', 'xlsx', 'doc', 'docx', 'ppt', 'pptx'].includes(fileExt)) {
+					return 'excel';
+				} else {
+					return 'unsupported';
+				}
+			},
+			
+			// 鏄剧ず鏂囦欢棰勮寮圭獥
+			showFilePreview(content, fileName) {
+				this.previewContent = content;
+				this.previewTitle = fileName;
+				this.previewItemNo = this.selectedAttachment?.itemNo || '';
+				this.previewType = this.getFileType(fileName);
+				this.showFilePreviewPopup = true;
+			},
+			
+			// 鍏抽棴鏂囦欢棰勮寮圭獥
+			closeFilePreview() {
+				this.showFilePreviewPopup = false;
+				this.previewContent = '';
+				this.previewTitle = '';
+				this.previewItemNo = '';
+				this.previewType = '';
+			},
+			
+			// 涓嬭浇棰勮鏂囦欢
+			downloadPreviewFile() {
+				const item = { fattach: this.previewTitle, itemNo: this.previewItemNo };
+				this.downloadAttachment(item);
+				this.closeFilePreview();
+			},
+			
+			// 棰勮Office鏂囦欢
+			previewOfficeFile(url, fileName) {
+				// 鍏堟鏌xcel鏂囦欢锛屼娇鐢ㄤ笓闂ㄧ殑Excel棰勮椤甸潰
+				const fileExt = fileName.split('.').pop().toLowerCase();
+				if (['xls', 'xlsx'].includes(fileExt)) {
+					// Excel鏂囦欢棰勮
+					uni.request({
+						url: url,
+						method: 'GET',
+						responseType: 'arraybuffer',
+						success: (res) => {
+							if (res.statusCode === 200) {
+								const base64Data = uni.arrayBufferToBase64(res.data);
+								// 瀛樺偍 Base64 鏁版嵁鍒版湰鍦板瓨鍌�
+								uni.setStorageSync('excelBase64Data', base64Data);
+								uni.navigateTo({
+									url: `/pages/fileView/excelView`
+								});
+							} else {
+								this.handlePreviewError(res.statusCode, fileName);
+							}
+						},
+						fail: (error) => {
+							this.handlePreviewError(0, fileName, error.errMsg);
+						}
+					});
+				} else if (['doc', 'docx'].includes(fileExt)) {
+					// Word鏂囦欢锛屽皾璇曚娇鐢╓ord棰勮椤甸潰鎴栬�呭井杞湪绾块瑙�
+					try {
+						const officePreviewUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}`;
+						// 濡傛灉鏈墂ebView椤甸潰锛屼娇鐢╳ebView棰勮
+						this.previewGenericFile(officePreviewUrl, fileName);
+					} catch (error) {
+						this.handlePreviewError(0, fileName, '涓嶆敮鎸佹Office鏂囦欢绫诲瀷鐨勯瑙�');
+					}
+				} else {
+					// 鍏朵粬Office鏂囦欢锛屼娇鐢ㄥ井杞湪绾块瑙堟湇鍔�
+					const officePreviewUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}`;
+					this.previewGenericFile(officePreviewUrl, fileName);
+				}
+			},
+			
+			// 閫氱敤鏂囦欢棰勮
+			previewGenericFile(url, fileName) {
+				// 鐢变簬娌℃湁閫氱敤鐨剋ebView椤甸潰锛屾樉绀烘彁绀哄苟鎻愪緵涓嬭浇
+				uni.showModal({
+					title: '鏂囦欢棰勮',
+					content: `鏂囦欢 "${fileName}" 闇�瑕佷笅杞藉悗鏌ョ湅锛屾槸鍚︾珛鍗充笅杞斤紵`,
+					showCancel: true,
+					confirmText: '涓嬭浇',
+					cancelText: '鍙栨秷',
+					success: (res) => {
+						if (res.confirm) {
+							const item = { fattach: fileName, itemNo: this.selectedAttachment.itemNo };
+							this.downloadAttachment(item);
+						}
+					}
+				});
+			},
+			
+			// 澶勭悊棰勮閿欒
+			handlePreviewError(statusCode, fileName, errorMsg = '') {
+				let message = '';
+				if (statusCode === 404) {
+					message = `鏂囦欢 ${fileName} 鍦‵TP鏈嶅姟鍣ㄤ笂涓嶅瓨鍦╜;
+				} else if (statusCode === 0) {
+					message = `棰勮澶辫触锛�${errorMsg}`;
+				} else {
+					message = `棰勮澶辫触锛岀姸鎬佺爜锛�${statusCode}`;
+				}
+				
+				uni.showModal({
+					title: '棰勮澶辫触',
+					content: message,
+					showCancel: true,
+					confirmText: '涓嬭浇',
+					cancelText: '鍙栨秷',
+					success: (res) => {
+						if (res.confirm) {
+							// 鐢ㄦ埛閫夋嫨涓嬭浇鏂囦欢
+							const item = { fattach: fileName, itemNo: this.selectedAttachment.itemNo };
+							this.downloadAttachment(item);
+						}
+					}
+				});
+			},
+			
+			downloadAttachment(item) {
+				// 鍘婚櫎鎵�鏈夌┖鏍笺�佸叏瑙掔┖鏍笺�佸洖杞︺�佹崲琛�
+				const fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim();
+				// 浣跨敤閰嶇疆鐨勬湇鍔″櫒鍦板潃鍜孎TP鏈嶅姟鍣ㄥ湴鍧�
+				const url = this.$store.state.serverInfo.serverAPI + "/LLJ/DownloadFtpFile?itemNo=" + encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer);
+				
+				// 妫�鏌ヨ繍琛岀幆澧�
+				// #ifdef H5
+				// H5鐜锛氫娇鐢ㄦ祻瑙堝櫒涓嬭浇
+				this.downloadFileInBrowser(url, fileName);
+				// #endif
+				
+				// #ifdef APP-PLUS
+				// APP鐜锛氫娇鐢╱ni.downloadFile
+				this.downloadFileInApp(url, fileName);
+				// #endif
+				
+				// #ifdef MP
+				// 灏忕▼搴忕幆澧冿細浣跨敤uni.downloadFile
+				this.downloadFileInApp(url, fileName);
+				// #endif
+			},
+			
+			// 鍦ㄦ祻瑙堝櫒涓笅杞芥枃浠�
+			downloadFileInBrowser(url, fileName) {
+				uni.showLoading({ title: '姝e湪鍑嗗涓嬭浇...' });
+				
+				// 鏂规硶1锛氬垱寤洪殣钘忕殑a鏍囩涓嬭浇
+				try {
+					const link = document.createElement('a');
+					link.href = url;
+					link.download = fileName;
+					link.style.display = 'none';
+					document.body.appendChild(link);
+					link.click();
+					document.body.removeChild(link);
+					
+					uni.hideLoading();
+					uni.showToast({ 
+						title: '涓嬭浇宸插紑濮�', 
+						icon: 'success',
+						duration: 2000
+					});
+				} catch (error) {
+					console.log('a鏍囩涓嬭浇澶辫触锛屽皾璇晈indow.open鏂瑰紡:', error);
+					// 鏂规硶2锛氫娇鐢╳indow.open
+					try {
+						window.open(url, '_blank');
+						uni.hideLoading();
+						uni.showToast({ 
+							title: '涓嬭浇宸插紑濮�', 
+							icon: 'success',
+							duration: 2000
+						});
+					} catch (error2) {
+						console.log('window.open涓嬭浇澶辫触锛屽皾璇昮etch鏂瑰紡:', error2);
+						// 鏂规硶3锛氫娇鐢╢etch涓嬭浇
+						this.downloadFileWithFetch(url, fileName);
+					}
+				}
+			},
+			
+			// 浣跨敤fetch涓嬭浇鏂囦欢
+			downloadFileWithFetch(url, fileName) {
+				fetch(url)
+					.then(response => {
+						if (!response.ok) {
+							throw new Error(`HTTP error! status: ${response.status}`);
+						}
+						return response.blob();
+					})
+					.then(blob => {
+						// 鍒涘缓blob URL
+						const blobUrl = window.URL.createObjectURL(blob);
+						
+						// 鍒涘缓涓嬭浇閾炬帴
+						const link = document.createElement('a');
+						link.href = blobUrl;
+						link.download = fileName;
+						link.style.display = 'none';
+						document.body.appendChild(link);
+						link.click();
+						document.body.removeChild(link);
+						
+						// 閲婃斁blob URL
+						window.URL.revokeObjectURL(blobUrl);
+						
+						uni.hideLoading();
+						uni.showToast({ 
+							title: '涓嬭浇鎴愬姛', 
+							icon: 'success',
+							duration: 2000
+						});
+					})
+					.catch(error => {
+						console.error('Fetch涓嬭浇澶辫触:', error);
+						uni.hideLoading();
+						if (error.message.includes('404')) {
+							uni.showModal({ 
+								title: '鏂囦欢涓嶅瓨鍦�', 
+								content: `璇ラ檮浠跺湪FTP鏈嶅姟鍣ㄤ笂涓嶅瓨鍦╜,
+								showCancel: false 
+							});
+						} else {
+							uni.showModal({ 
+								title: '涓嬭浇澶辫触', 
+								content: `涓嬭浇澶辫触: ${error.message}`,
+								showCancel: false 
+							});
+						}
+					});
+			},
+			
+			// 鍦ˋPP涓笅杞芥枃浠�
+			downloadFileInApp(url, fileName) {
+				// #ifdef APP-PLUS
+				uni.showLoading({ title: '浠嶧TP鏈嶅姟鍣ㄤ笅杞戒腑...' });
+				
+				// Android 鑾峰彇瀛樺偍璺緞
+				const saveDir = plus.os.name === 'Android' ? plus.io.convertLocalFileSystemURL('_downloads/') : plus.io.convertLocalFileSystemURL('_documents/');
+				const filePath = `${saveDir}${fileName}`;
+				
+				const downloadTask = uni.downloadFile({
+					url: url,
+					filePath: filePath, // 鎸囧畾淇濆瓨璺緞
+					success: (res) => {
+						uni.hideLoading();
+						if (res.statusCode === 200) {
+							const fileInfo = {
+								name: fileName,
+								path: res.filePath || filePath,
+								tempPath: res.tempFilePath
+							};
+							
+							uni.showModal({
+								title: '涓嬭浇鎴愬姛',
+								content: `鏂囦欢宸蹭繚瀛樺埌锛�${fileInfo.path}`,
+								showCancel: true,
+								confirmText: '鎵撳紑鏂囦欢',
+								cancelText: '纭畾',
+								success: (modalRes) => {
+									if (modalRes.confirm) {
+										// 鐢ㄦ埛閫夋嫨鎵撳紑鏂囦欢
+										this.openFileInApp(fileInfo);
+									}
+								}
+							});
+						} else if (res.statusCode === 404) {
+							uni.showModal({ 
+								title: '鏂囦欢涓嶅瓨鍦�', 
+								content: `璇ラ檮浠跺湪FTP鏈嶅姟鍣ㄤ笂涓嶅瓨鍦╜,
+								showCancel: false 
+							});
+						} else {
+							uni.showModal({ 
+								title: '涓嬭浇澶辫触', 
+								content: `鐘舵�佺爜锛�${res.statusCode}`,
+								showCancel: false 
+							});
+						}
+					},
+					fail: (error) => {
+						uni.hideLoading();
+						console.error('涓嬭浇澶辫触:', error);
+						uni.showModal({ 
+							title: '涓嬭浇澶辫触', 
+							content: `缃戠粶閿欒锛�${error.errMsg}`,
+							showCancel: false 
+						});
+					}
+				});
+				
+				// 鐩戝惉涓嬭浇杩涘害
+				downloadTask.onProgressUpdate((res) => {
+					const progress = Math.round(res.progress);
+					uni.showLoading({ 
+						title: `涓嬭浇涓� ${progress}%`,
+						mask: true 
+					});
+				});
+				// #endif
+				
+				// #ifdef MP
+				// 灏忕▼搴忕幆澧冪殑绠�鍖栧疄鐜�
+				uni.showLoading({ title: '涓嬭浇涓�...' });
+				uni.downloadFile({
+					url: url,
+					success: (res) => {
+						uni.hideLoading();
+						if (res.statusCode === 200) {
+							uni.showToast({ title: '涓嬭浇瀹屾垚', icon: 'success' });
+						}
+					},
+					fail: (error) => {
+						uni.hideLoading();
+						uni.showModal({ title: '涓嬭浇澶辫触', content: error.errMsg, showCancel: false });
+					}
+				});
+				// #endif
+			},
+			
+			// APP涓墦寮�鏂囦欢
+			openFileInApp(fileInfo) {
+				// #ifdef APP-PLUS
+				if (typeof plus !== 'undefined') {
+					const filePath = fileInfo.path || fileInfo.tempPath;
+					
+					// 灏濊瘯鎵撳紑鏂囦欢
+					plus.runtime.openFile(filePath, {}, (error) => {
+						console.error('鎵撳紑鏂囦欢澶辫触:', error);
+						uni.showModal({
+							title: '鏃犳硶鎵撳紑',
+							content: '绯荤粺涓病鏈夋壘鍒拌兘鎵撳紑姝ゆ枃浠剁殑搴旂敤绋嬪簭',
+							showCancel: false
+						});
+					});
+				}
+				// #endif
 			},
 
 		}
@@ -1368,9 +1867,11 @@
 		font-family: 'Microsoft YaHei', 'Segoe UI', sans-serif;
 		max-width: 1000px;
 		margin: 0 auto;
-		padding: 20px;
+		padding: 20px 20px 160px 20px; /* 搴曢儴澧炲姞padding涓哄浐瀹氭寜閽暀绌洪棿 */
 		background-color: #fff;
 		box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+		min-height: 100vh;
+		position: relative;
 	}
 
 	/* 澶撮儴鏍峰紡 */
@@ -1491,7 +1992,60 @@
 		background-color: #f1f5f9;
 	}
 
-	/* 鎸夐挳鏍峰紡 */
+	/* 鍥哄畾搴曢儴鎸夐挳鏍峰紡 */
+	.fixed-action-buttons {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		background-color: #fff;
+		box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
+		padding: 10px 15px 20px 15px;
+		z-index: 100;
+		display: flex;
+		flex-direction: column;
+		gap: 8px;
+		max-height: 150px;
+		overflow-y: auto;
+	}
+
+	.action-btn {
+		background-color: #ecf0f1;
+		color: #34495e;
+		padding: 12px 15px;
+		border: none;
+		border-radius: 6px;
+		cursor: pointer;
+		font-size: 14px;
+		font-weight: 500;
+		transition: all 0.3s ease;
+		text-align: center;
+		min-height: 44px;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.action-btn:hover {
+		background-color: #d5dbdb;
+		transform: translateY(-1px);
+	}
+
+	.action-btn.primary {
+		background-color: #3498db;
+		color: #fff;
+	}
+
+	.action-btn.primary:hover {
+		background-color: #2980b9;
+	}
+
+	/* 鍐呭鍖呰鍣紝涓哄簳閮ㄦ寜閽暀鍑虹┖闂� */
+	.content-wrapper {
+		height: 20px; /* 棰濆鐨勭┖鐧藉尯鍩� */
+	}
+
+	/* 鍘熸湁鎸夐挳鏍峰紡淇濇寔鍏煎 */
 	.action-buttons {
 		display: flex;
 		gap: 10px;
@@ -1600,7 +2154,7 @@
 		display: flex;
 		justify-content: center;
 		align-items: center;
-		z-index: 10;
+		z-index: 1000; /* 鎻愰珮灞傜骇锛岀‘淇濆湪鍥哄畾鎸夐挳涓婃柟 */
 	}
 
 	/* 寮圭獥鏁翠綋缇庡寲 */
@@ -1612,6 +2166,9 @@
 		border: none;
 		position: relative;
 		min-width: 260px;
+		z-index: 1001; /* 纭繚寮圭獥鍐呭鍦ㄦ渶涓婂眰 */
+		max-height: 80vh; /* 闄愬埗鏈�澶ч珮搴︼紝閬垮厤琚簳閮ㄦ寜閽伄鎸� */
+		overflow-y: auto; /* 鍐呭杩囧鏃跺彲婊氬姩 */
 	}
 	.attachment-popup-title {
 		font-size: 22px;
@@ -1690,6 +2247,164 @@
 		background: linear-gradient(90deg,#bdbdbd 0%,#e0e0e0 100%);
 		color: #1976d2;
 	}
+	
+	/* 闄勪欢璇︽儏椤甸潰鐨勬搷浣滄寜閽� */
+	.attachment-actions-detail {
+		margin: 20px 0;
+		display: flex;
+		gap: 12px;
+		justify-content: center;
+		flex-wrap: wrap;
+	}
+	.attachment-action-btn {
+		padding: 10px 20px;
+		border: none;
+		border-radius: 8px;
+		font-size: 14px;
+		font-weight: 600;
+		cursor: pointer;
+		transition: all 0.3s ease;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		min-width: 120px;
+		box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+	}
+	.attachment-action-btn.preview-btn {
+		background: linear-gradient(135deg, #4CAF50, #45a049);
+		color: white;
+	}
+	.attachment-action-btn.preview-btn:hover {
+		background: linear-gradient(135deg, #45a049, #3d8b40);
+		transform: translateY(-1px);
+		box-shadow: 0 4px 8px rgba(0,0,0,0.15);
+	}
+	.attachment-action-btn.download-btn {
+		background: linear-gradient(135deg, #2196F3, #1976D2);
+		color: white;
+	}
+	.attachment-action-btn.download-btn:hover {
+		background: linear-gradient(135deg, #1976D2, #1565C0);
+		transform: translateY(-1px);
+		box-shadow: 0 4px 8px rgba(0,0,0,0.15);
+	}
+	
+	/* 鏂囦欢棰勮寮圭獥鏍峰紡 */
+	.file-preview-popup {
+		width: 80vw;
+		max-width: 600px;
+		max-height: 70vh;
+		display: flex;
+		flex-direction: column;
+	}
+	
+	/* APP鐜閫傞厤 */
+	/* #ifdef APP-PLUS */
+	.file-preview-popup {
+		width: 85vw;
+		max-height: 75vh;
+	}
+	.file-preview-content {
+		max-height: 350px;
+	}
+	/* #endif */
+	.file-preview-title {
+		font-size: 18px;
+		font-weight: 700;
+		color: #222;
+		margin-bottom: 8px;
+		text-align: center;
+		word-break: break-all;
+	}
+	.file-preview-divider {
+		height: 1px;
+		background: linear-gradient(90deg,#e0e7ef 0%,#f5f7fa 100%);
+		margin-bottom: 16px;
+	}
+	.file-preview-content {
+		flex: 1;
+		max-height: 400px;
+		overflow-y: auto;
+		background: #f8fafc;
+		border-radius: 8px;
+		padding: 16px;
+		margin-bottom: 16px;
+		border: 1px solid #e2e8f0;
+	}
+	.file-preview-content pre {
+		font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
+		font-size: 12px;
+		line-height: 1.5;
+		color: #2d3748;
+		white-space: pre-wrap;
+		word-wrap: break-word;
+		margin: 0;
+	}
+	
+	/* 鍥剧墖棰勮鏍峰紡 */
+	.image-preview-container {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		min-height: 200px;
+	}
+	
+	/* 涓嶆敮鎸佹枃浠剁被鍨嬬殑鎻愮ず鏍峰紡 */
+	.unsupported-preview {
+		text-align: center;
+		padding: 40px 20px;
+		color: #666;
+	}
+	.unsupported-icon {
+		font-size: 48px;
+		margin-bottom: 16px;
+	}
+	.unsupported-text {
+		font-size: 16px;
+		font-weight: 600;
+		color: #333;
+		margin-bottom: 8px;
+	}
+	.unsupported-hint {
+		font-size: 14px;
+		color: #999;
+		line-height: 1.4;
+	}
+	
+	.file-preview-actions {
+		display: flex;
+		gap: 12px;
+		justify-content: center;
+	}
+	.file-preview-btn {
+		padding: 8px 20px;
+		border: none;
+		border-radius: 6px;
+		font-size: 14px;
+		font-weight: 600;
+		cursor: pointer;
+		transition: all 0.3s ease;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		min-width: 120px;
+	}
+	.file-preview-btn.download-btn {
+		background: linear-gradient(135deg, #2196F3, #1976D2);
+		color: white;
+	}
+	.file-preview-btn.download-btn:hover {
+		background: linear-gradient(135deg, #1976D2, #1565C0);
+		transform: translateY(-1px);
+	}
+	.file-preview-btn.close-btn {
+		background: linear-gradient(135deg, #e0e0e0, #bdbdbd);
+		color: #444;
+	}
+	.file-preview-btn.close-btn:hover {
+		background: linear-gradient(135deg, #bdbdbd, #9e9e9e);
+		transform: translateY(-1px);
+	}
 	/* 鍒楄〃寮圭獥缇庡寲锛堜繚鐣欏師鏈夛級 */
 	.attachment-list {
 		padding: 0;
@@ -1702,24 +2417,36 @@
 		display: flex;
 		align-items: center;
 		justify-content: space-between;
-		padding: 8px 0;
+		padding: 12px 0;
 		border-bottom: 1px solid #f0f0f0;
 	}
-	.attachment-name {
+	.attachment-info {
 		flex: 1;
+		margin-right: 10px;
+	}
+	.attachment-name {
 		color: #3498db;
 		cursor: pointer;
 		font-weight: 500;
 		transition: color 0.2s;
-		margin-right: 10px;
+		display: block;
+		margin-bottom: 4px;
 	}
 	.attachment-name:hover {
 		color: #217dbb;
 		text-decoration: underline;
 	}
+	.attachment-meta {
+		font-size: 12px;
+	}
+	.attachment-type {
+		color: #7f8c8d;
+		font-style: italic;
+	}
 	.attachment-actions {
 		display: flex;
 		gap: 8px;
+		flex-shrink: 0;
 	}
 	.attachment-list .secondary-btn {
 		padding: 4px 10px;
@@ -1734,6 +2461,15 @@
 		background: #e6f0fa;
 		color: #1976d2;
 	}
+	.preview-btn {
+		background: #e8f5e8 !important;
+		color: #2e7d2e !important;
+		border-color: #a5d6a5 !important;
+	}
+	.preview-btn:hover {
+		background: #d4eecc !important;
+		color: #1e5f1e !important;
+	}
 	.attachment-popup-close {
 		margin-top: 18px;
 		width: 100%;
diff --git a/store/index.js b/store/index.js
index d92d393..e087f48 100644
--- a/store/index.js
+++ b/store/index.js
@@ -10,9 +10,10 @@
 			networkFlag:'鍐呯綉', 
 			serverURLInt:'http://192.168.11.251:10055',//鏈嶅姟鍣ㄤ綋妫� 10.0.1.104:10054
 			serverURL:'http://localhost:10055',//鏈湴璋冭瘯鍦板潃
-			serverAPI:'http://localhost:5184/api',//褰撳墠姝e湪浣跨敤鐨勬湇鍔″櫒,榛樿涓哄缃�  localhost
+			// serverAPI:'http://localhost:5184/api',//褰撳墠姝e湪浣跨敤鐨勬湇鍔″櫒,榛樿涓哄缃�  localhost
 			//serverAPI:'http://192.168.1.22:10054/api',//鍐呯綉 
-			//serverAPI:'http://36.26.21.214:10054/api',
+			serverAPI:'http://36.26.21.214:10054/api',
+			ftpServer:'ftp://36.26.21.214',//FTP鏈嶅姟鍣ㄥ湴鍧�
 		}
 	},
 	mutations: {

--
Gitblit v1.9.3