From 3a0c40c068c78bd114f1334edae4579f73613c5c Mon Sep 17 00:00:00 2001
From: yy2205 <2238220225@qq.com>
Date: Mon, 21 Jul 2025 09:35:30 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9B=BE=E4=BD=B3=E7=82=9C=E4=B8=80=E6=AC=A1?=
=?UTF-8?q?=E6=8F=90=E4=BA=A4=E6=89=80=E6=9C=89=E5=89=8D=E7=AB=AF=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.env | 2 +-
.env.dev | 6 +-
.env.prod | 5 +-
docs/examCache-usage.md | 299 ++++++++++++++++++++++++
index.html | 8 +
package.json | 7 +-
src/api/processImage/index.ts | 2 +-
src/api/tblist/patientexamlist/index.ts | 4 +-
src/plugins/unocss/index.ts | 2 +-
src/store/modules/examCache.ts | 85 +++++++
src/utils/examCacheExample.ts | 108 +++++++++
src/utils/ukey.ts | 134 +++++++----
src/views/ECG/ECGForm.vue | 40 ++--
src/views/ECG/translate/index2.vue | 46 +++-
src/views/system/mail/log/log.data.ts | 14 +-
vite.config.ts | 23 +-
16 files changed, 701 insertions(+), 84 deletions(-)
create mode 100644 docs/examCache-usage.md
create mode 100644 src/store/modules/examCache.ts
create mode 100644 src/utils/examCacheExample.ts
diff --git a/.env b/.env
index 9e68b819..26b5170b 100644
--- a/.env
+++ b/.env
@@ -2,7 +2,7 @@
VITE_APP_TITLE=区域云心电系统
# 项目本地运行端口号
-VITE_PORT=8080
+VITE_PORT=80
# open 运行 npm run dev 时自动打开浏览器
VITE_OPEN=true
diff --git a/.env.dev b/.env.dev
index b3b4ef9d..637d2329 100644
--- a/.env.dev
+++ b/.env.dev
@@ -4,8 +4,8 @@ NODE_ENV=production
VITE_DEV=false
# 请求路径 http://10.55.253.199:8073
-VITE_BASE_URL='http://111.57.76.10:8072' #111服务器
-#VITE_BASE_URL='/adminecg' #222服务器
+#VITE_BASE_URL='http://111.57.76.10:8072' #111服务器
+VITE_BASE_URL='/adminecg' #222服务器
# VITE_BASE_URL='/admin-api'
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
@@ -26,7 +26,7 @@ VITE_DROP_CONSOLE=false
VITE_SOURCEMAP=true
# 打包路径
-VITE_BASE_PATH=/ecg
+VITE_BASE_PATH=/ecg #222服务器
# 输出路径
VITE_OUT_DIR=dist
diff --git a/.env.prod b/.env.prod
index 2537ed81..8bd73507 100644
--- a/.env.prod
+++ b/.env.prod
@@ -4,7 +4,8 @@ NODE_ENV=production
VITE_DEV=false
# 请求路径
-VITE_BASE_URL='http://114.55.171.231:48080'
+#VITE_BASE_URL='http://114.55.171.231:48080'
+VITE_BASE_URL='http://192.168.1.12:8072'
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
VITE_UPLOAD_TYPE=server
@@ -12,7 +13,7 @@ VITE_UPLOAD_TYPE=server
VITE_UPLOAD_URL='http://114.55.171.231:48080/admin-api/infra/file/upload'
# 接口地址
-VITE_API_URL=/admin-api
+VITE_API_URL=/adminecg/admin-api
# 是否删除debugger
VITE_DROP_DEBUGGER=false
diff --git a/docs/examCache-usage.md b/docs/examCache-usage.md
new file mode 100644
index 00000000..96477ad8
--- /dev/null
+++ b/docs/examCache-usage.md
@@ -0,0 +1,299 @@
+# examCache 缓存系统使用指南
+
+## 概述
+
+`examCache` 是一个基于 Pinia 的缓存系统,用于在页面间存储和获取 `examId`、`orgId` 等检查相关信息。该系统支持自动持久化到 localStorage,并具有缓存有效期管理功能。
+
+## 特性
+
+- ✅ 基于 Pinia 状态管理
+- ✅ 自动持久化到 localStorage
+- ✅ 缓存有效期管理(24小时)
+- ✅ 支持从 URL 参数自动更新缓存
+- ✅ TypeScript 类型支持
+- ✅ 优雅的降级处理
+
+## 安装和配置
+
+### 1. 确保已安装依赖
+
+项目已经安装了必要的依赖:
+- `pinia`
+- `pinia-plugin-persistedstate`
+
+### 2. 缓存 Store 已配置
+
+缓存 Store 位于 `src/store/modules/examCache.ts`,已在主 Store 中注册。
+
+## 基本使用
+
+### 1. 设置缓存
+
+在页面跳转前设置缓存:
+
+```typescript
+import { useExamCacheStore } from '@/store/modules/examCache'
+
+const examCacheStore = useExamCacheStore()
+
+// 设置缓存
+examCacheStore.setCache('EXAM001', 'ORG001', 'http://example.com/image.jpg')
+```
+
+### 2. 获取缓存
+
+在目标页面获取缓存:
+
+```typescript
+import { useExamCacheStore } from '@/store/modules/examCache'
+
+const examCacheStore = useExamCacheStore()
+
+// 获取有效的 examId 和 orgId
+const examId = examCacheStore.getValidExamId
+const orgId = examCacheStore.getValidOrgId
+const imgUrl = examCacheStore.imgUrl
+
+// 检查缓存是否有效
+if (examCacheStore.isCacheValid) {
+ console.log('缓存有效,可以使用')
+} else {
+ console.log('缓存已过期或不存在')
+}
+```
+
+### 3. 从 URL 参数更新缓存
+
+当页面通过 URL 参数传递数据时,可以自动更新缓存:
+
+```typescript
+import { useExamCacheStore } from '@/store/modules/examCache'
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+const examCacheStore = useExamCacheStore()
+
+// 从路由参数更新缓存
+examCacheStore.updateFromRoute(route.query)
+```
+
+### 4. 清除缓存
+
+```typescript
+import { useExamCacheStore } from '@/store/modules/examCache'
+
+const examCacheStore = useExamCacheStore()
+
+// 清除缓存
+examCacheStore.clearCache()
+```
+
+## 实际应用场景
+
+### 场景1: ECGForm 页面跳转到 translate 页面
+
+**在 ECGForm.vue 中:**
+
+```typescript
+// 在跳转前设置缓存
+const examCacheStore = useExamCacheStore()
+examCacheStore.setCache(rowinfo.value.examId, rowinfo.value.orgId, imageUrl)
+
+// 跳转到 translate 页面
+router.push({
+ path: '/translate',
+ query: {
+ img: imageUrl,
+ examId: rowinfo.value.examId,
+ orgId: rowinfo.value.orgId
+ }
+})
+```
+
+**在 translate/index.vue 中:**
+
+```typescript
+import { onMounted } from 'vue'
+import { useRoute } from 'vue-router'
+import { useExamCacheStore } from '@/store/modules/examCache'
+
+const route = useRoute()
+const examCacheStore = useExamCacheStore()
+
+onMounted(() => {
+ // 优先从 URL 参数获取,如果没有则从缓存获取
+ const examId = (route.query.examId as string) || examCacheStore.getValidExamId
+ const orgId = (route.query.orgId as string) || examCacheStore.getValidOrgId
+ const imgUrl = (route.query.img as string) || examCacheStore.imgUrl
+
+ // 如果从 URL 参数获取到数据,更新缓存
+ if (route.query.examId || route.query.orgId || route.query.img) {
+ examCacheStore.updateFromRoute(route.query)
+ }
+})
+```
+
+### 场景2: ukey.ts 中使用缓存
+
+**修改 ukey.ts 中的 uploadPdf 函数:**
+
+```typescript
+import { useExamCacheStore } from '@/store/modules/examCache'
+
+const uploadPdf = async (byteArray: number[], filename: string = 'signed.pdf') => {
+ try {
+ const fileStream = byteArray.join(',')
+
+ // 获取缓存 store
+ const examCacheStore = useExamCacheStore()
+
+ // 优先从缓存获取 examId 和 orgId,如果没有则从 route.query 获取
+ const examId = examCacheStore.getValidExamId || (route.query.examId as string) || ''
+ const orgId = examCacheStore.getValidOrgId || (route.query.orgId as string) || ''
+
+ const formData = new FormData()
+ formData.append('examId', examId)
+ formData.append('orgId', orgId)
+ // ... 其他代码
+ } catch (error) {
+ console.error('文件上传失败:', error)
+ throw error
+ }
+}
+```
+
+## API 参考
+
+### Store 状态
+
+```typescript
+interface ExamCacheState {
+ examId: string // 检查编号
+ orgId: string // 机构编号
+ imgUrl: string // 图片地址
+ timestamp: number // 缓存时间戳
+}
+```
+
+### Getters
+
+- `isCacheValid`: 检查缓存是否有效(24小时内)
+- `getValidExamId`: 获取有效的 examId
+- `getValidOrgId`: 获取有效的 orgId
+
+### Actions
+
+- `setCache(examId: string, orgId: string, imgUrl?: string)`: 设置缓存
+- `clearCache()`: 清除缓存
+- `updateFromRoute(query: any)`: 从路由参数更新缓存
+
+## 缓存策略
+
+### 优先级顺序
+
+1. **URL 参数** - 最高优先级
+2. **缓存数据** - 次优先级
+3. **空值** - 最低优先级
+
+### 有效期管理
+
+- 缓存有效期为 **24 小时**
+- 过期后需要重新设置
+- 时间戳不持久化,每次页面刷新后重新计算
+
+### 持久化配置
+
+```typescript
+persist: {
+ key: 'exam-cache',
+ storage: localStorage,
+ paths: ['examId', 'orgId', 'imgUrl'] // 只持久化这些字段
+}
+```
+
+## 最佳实践
+
+### 1. 在页面跳转前设置缓存
+
+```typescript
+// 推荐:在跳转前设置缓存
+const examCacheStore = useExamCacheStore()
+examCacheStore.setCache(examId, orgId, imgUrl)
+router.push({ path: '/target', query: { examId, orgId, img: imgUrl } })
+```
+
+### 2. 在目标页面优先使用 URL 参数
+
+```typescript
+// 推荐:优先使用 URL 参数,然后回退到缓存
+const examId = (route.query.examId as string) || examCacheStore.getValidExamId
+const orgId = (route.query.orgId as string) || examCacheStore.getValidOrgId
+```
+
+### 3. 定期清理过期缓存
+
+```typescript
+// 在应用启动时检查缓存有效性
+const examCacheStore = useExamCacheStore()
+if (!examCacheStore.isCacheValid) {
+ examCacheStore.clearCache()
+}
+```
+
+### 4. 错误处理
+
+```typescript
+// 添加错误处理
+try {
+ const examId = examCacheStore.getValidExamId
+ if (!examId) {
+ throw new Error('未找到有效的检查编号')
+ }
+ // 使用 examId
+} catch (error) {
+ console.error('获取缓存失败:', error)
+ // 处理错误
+}
+```
+
+## 注意事项
+
+1. **缓存有效期**: 缓存有效期为 24 小时,过期后需要重新设置
+2. **类型安全**: 使用 TypeScript 确保类型安全
+3. **降级处理**: 始终提供降级方案,避免因缓存问题导致功能不可用
+4. **调试**: 使用浏览器开发者工具查看 localStorage 中的缓存数据
+5. **清理**: 在适当的时候清理过期缓存
+
+## 故障排除
+
+### 常见问题
+
+1. **缓存不生效**
+ - 检查 Pinia 是否正确配置
+ - 检查 localStorage 是否可用
+ - 检查缓存是否已过期
+
+2. **类型错误**
+ - 确保正确导入类型
+ - 检查 TypeScript 配置
+
+3. **缓存数据丢失**
+ - 检查 localStorage 存储限制
+ - 检查浏览器隐私设置
+
+### 调试方法
+
+```typescript
+// 在浏览器控制台中调试
+const examCacheStore = useExamCacheStore()
+console.log('缓存状态:', {
+ examId: examCacheStore.examId,
+ orgId: examCacheStore.orgId,
+ imgUrl: examCacheStore.imgUrl,
+ timestamp: examCacheStore.timestamp,
+ isValid: examCacheStore.isCacheValid
+})
+
+// 查看 localStorage
+console.log('localStorage:', localStorage.getItem('exam-cache'))
+```
\ No newline at end of file
diff --git a/index.html b/index.html
index d240e7d6..27f81c74 100644
--- a/index.html
+++ b/index.html
@@ -14,6 +14,14 @@
content="芋道管理系统 基于 vue3 + CompositionAPI + typescript + vite3 + element plus 的后台开源免费管理系统!"
/>
diff --git a/package.json b/package.json
index a19c55c1..1f8c05d6 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"scripts": {
"i": "pnpm install",
"dev": "vite",
+ "local": "vite --mode local",
"dev-server": "vite --mode dev",
"ts:check": "vue-tsc --noEmit",
"build:local": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build",
@@ -52,7 +53,7 @@
"highlight.js": "^11.9.0",
"html2canvas": "^1.4.1",
"jsencrypt": "^3.3.2",
- "jspdf": "^2.5.1",
+ "jspdf": "^2.5.2",
"lodash-es": "^4.17.21",
"min-dash": "^4.1.1",
"mitt": "^3.0.1",
@@ -68,6 +69,7 @@
"vue-clipboard3": "^2.0.0",
"vue-dompurify-html": "^4.1.4",
"vue-i18n": "9.10.2",
+ "vue-pdf-embed": "^2.1.2",
"vue-qrcode": "^2.2.2",
"vue-router": "^4.3.0",
"vue-types": "^5.1.1",
@@ -101,6 +103,7 @@
"bpmn-js": "8.9.0",
"bpmn-js-properties-panel": "0.46.0",
"consola": "^3.2.3",
+ "esbuild": "^0.25.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-define-config": "^2.1.0",
@@ -134,7 +137,7 @@
"vite-plugin-progress": "^0.0.7",
"vite-plugin-purge-icons": "^0.10.0",
"vite-plugin-svg-icons": "^2.0.1",
- "vite-plugin-top-level-await": "^1.3.1",
+ "vite-plugin-top-level-await": "^1.5.0",
"vue-eslint-parser": "^9.3.2",
"vue-tsc": "^1.8.27"
},
diff --git a/src/api/processImage/index.ts b/src/api/processImage/index.ts
index b96b8286..07cedcb7 100644
--- a/src/api/processImage/index.ts
+++ b/src/api/processImage/index.ts
@@ -5,7 +5,7 @@ const originImageAddress = 'https://zzxmc.gw12320.com/ecgimage/'
export const processImageApi = {
// const apiUrl = 'https://zzxmc.gw12320.com/processImage'
apiUrl222 : 'https://pacs.gw12320.com/adminecg/admin-api/tblist/ecganalysisparas/rpc-processImage', //222服务器
- apiUrl111 : 'http://111.57.76.10:8072/admin-api/tblist/ecganalysisparas/rpc-processImage', //111服务器
+ apiUrl111 : 'http://192.168.1.12:8072/admin-api/tblist/ecganalysisparas/rpc-processImage', //111服务器
// const apiUrl = 'https://localhost:48080/admin-api/tblist/ecganalysisparas/rpc-processImage'
// const imageAddress = 'F://陕西省咸阳市礼泉县心电图FTP/ecgimage/'
diff --git a/src/api/tblist/patientexamlist/index.ts b/src/api/tblist/patientexamlist/index.ts
index d88f2b1e..22e8e12b 100644
--- a/src/api/tblist/patientexamlist/index.ts
+++ b/src/api/tblist/patientexamlist/index.ts
@@ -88,8 +88,8 @@ export const PatientexamlistApi = {
},
// 超声审核
- examine: async (id: String, doctorname: string, doctorid: string) => {
- return await request.get({ url: `/tblist/patientexamlist/examine?id=${id}&doctorname=${doctorname}&doctorid=${doctorid}` })
+ examine: async (id: String, doctorname: string, doctorid: string, ecgId: string, doctorDiagResult: string) => {
+ return await request.get({ url: `/tblist/patientexamlist/examine?id=${id}&doctorname=${doctorname}&doctorid=${doctorid}&ecgid=${ecgId}&doctorDiagResult=${doctorDiagResult}` })
},
// dicom数据同步
dicomDataSync: async () => {
diff --git a/src/plugins/unocss/index.ts b/src/plugins/unocss/index.ts
index d366b5a2..390481d6 100644
--- a/src/plugins/unocss/index.ts
+++ b/src/plugins/unocss/index.ts
@@ -1 +1 @@
-import 'virtual:uno.css'
+import 'uno.css'
diff --git a/src/store/modules/examCache.ts b/src/store/modules/examCache.ts
new file mode 100644
index 00000000..a7f48550
--- /dev/null
+++ b/src/store/modules/examCache.ts
@@ -0,0 +1,85 @@
+import { defineStore } from 'pinia'
+
+interface ExamCacheState {
+ examId: string
+ orgId: string
+ imgUrl: string
+ timestamp: number
+}
+
+export const useExamCacheStore = defineStore('examCache', {
+ state: (): ExamCacheState => ({
+ examId: '',
+ orgId: '',
+ imgUrl: '',
+ timestamp: 0
+ }),
+
+ getters: {
+ // 检查缓存是否有效(24小时内)
+ isCacheValid(state) {
+ const now = Date.now()
+ const cacheAge = now - state.timestamp
+ const maxAge = 24 * 60 * 60 * 1000 // 24小时
+ return state.timestamp > 0 && cacheAge < maxAge
+ },
+
+ // 获取有效的examId
+ getValidExamId(state) {
+ const now = Date.now()
+ const cacheAge = now - state.timestamp
+ const maxAge = 24 * 60 * 60 * 1000 // 24小时
+ const isValid = state.timestamp > 0 && cacheAge < maxAge
+ return state.examId && isValid ? state.examId : ''
+ },
+
+ // 获取有效的orgId
+ getValidOrgId(state) {
+ const now = Date.now()
+ const cacheAge = now - state.timestamp
+ const maxAge = 24 * 60 * 60 * 1000 // 24小时
+ const isValid = state.timestamp > 0 && cacheAge < maxAge
+ return state.orgId && isValid ? state.orgId : ''
+ }
+ },
+
+ actions: {
+ // 设置缓存
+ setCache(examId: string, orgId: string, imgUrl: string = '') {
+ this.examId = examId
+ this.orgId = orgId
+ this.imgUrl = imgUrl
+ this.timestamp = Date.now()
+ },
+
+ // 清除缓存
+ clearCache() {
+ this.examId = ''
+ this.orgId = ''
+ this.imgUrl = ''
+ this.timestamp = 0
+ },
+
+ // 从URL参数更新缓存
+ updateFromRoute(query: any) {
+ if (query.examId) {
+ this.examId = query.examId
+ }
+ if (query.orgId) {
+ this.orgId = query.orgId
+ }
+ if (query.img) {
+ this.imgUrl = query.img
+ }
+ this.timestamp = Date.now()
+ }
+ },
+
+ // 持久化配置
+ persist: {
+ key: 'exam-cache',
+ storage: localStorage,
+ // 只持久化examId和orgId,不持久化timestamp
+ paths: ['examId', 'orgId', 'imgUrl']
+ }
+})
\ No newline at end of file
diff --git a/src/utils/examCacheExample.ts b/src/utils/examCacheExample.ts
new file mode 100644
index 00000000..ad86d016
--- /dev/null
+++ b/src/utils/examCacheExample.ts
@@ -0,0 +1,108 @@
+/**
+ * examCache 缓存系统使用示例
+ *
+ * 这个文件展示了如何在不同的页面中使用 examCache 来存储和获取 examId 和 orgId
+ */
+
+import { useExamCacheStore } from '@/store/modules/examCache'
+
+/**
+ * 示例1: 在页面A中设置缓存
+ */
+export const setExamCacheExample = () => {
+ const examCacheStore = useExamCacheStore()
+
+ // 设置缓存
+ examCacheStore.setCache('EXAM001', 'ORG001', 'http://example.com/image.jpg')
+
+ console.log('缓存已设置:', {
+ examId: examCacheStore.examId,
+ orgId: examCacheStore.orgId,
+ imgUrl: examCacheStore.imgUrl,
+ timestamp: examCacheStore.timestamp
+ })
+}
+
+/**
+ * 示例2: 在页面B中获取缓存
+ */
+export const getExamCacheExample = () => {
+ const examCacheStore = useExamCacheStore()
+
+ // 获取有效的examId和orgId
+ const examId = examCacheStore.getValidExamId
+ const orgId = examCacheStore.getValidOrgId
+ const imgUrl = examCacheStore.imgUrl
+
+ console.log('从缓存获取:', { examId, orgId, imgUrl })
+
+ // 检查缓存是否有效
+ if (examCacheStore.isCacheValid) {
+ console.log('缓存有效,可以使用')
+ return { examId, orgId, imgUrl }
+ } else {
+ console.log('缓存已过期或不存在')
+ return null
+ }
+}
+
+/**
+ * 示例3: 从URL参数更新缓存
+ */
+export const updateFromRouteExample = (query: any) => {
+ const examCacheStore = useExamCacheStore()
+
+ // 从路由参数更新缓存
+ examCacheStore.updateFromRoute(query)
+
+ console.log('缓存已从路由参数更新')
+}
+
+/**
+ * 示例4: 清除缓存
+ */
+export const clearCacheExample = () => {
+ const examCacheStore = useExamCacheStore()
+
+ // 清除缓存
+ examCacheStore.clearCache()
+
+ console.log('缓存已清除')
+}
+
+/**
+ * 示例5: 在Vue组件中使用
+ */
+export const useExamCacheInComponent = () => {
+ const examCacheStore = useExamCacheStore()
+
+ return {
+ // 获取缓存数据
+ examId: examCacheStore.getValidExamId,
+ orgId: examCacheStore.getValidOrgId,
+ imgUrl: examCacheStore.imgUrl,
+ isCacheValid: examCacheStore.isCacheValid,
+
+ // 缓存操作方法
+ setCache: examCacheStore.setCache,
+ clearCache: examCacheStore.clearCache,
+ updateFromRoute: examCacheStore.updateFromRoute
+ }
+}
+
+/**
+ * 使用说明:
+ *
+ * 1. 在页面跳转前设置缓存:
+ * const examCacheStore = useExamCacheStore()
+ * examCacheStore.setCache(examId, orgId, imgUrl)
+ *
+ * 2. 在目标页面获取缓存:
+ * const examCacheStore = useExamCacheStore()
+ * const examId = examCacheStore.getValidExamId
+ * const orgId = examCacheStore.getValidOrgId
+ *
+ * 3. 缓存会自动持久化到localStorage,页面刷新后仍然可用
+ * 4. 缓存有效期为24小时,过期后需要重新设置
+ * 5. 如果URL参数中有examId/orgId,会优先使用URL参数,并自动更新缓存
+ */
\ No newline at end of file
diff --git a/src/utils/ukey.ts b/src/utils/ukey.ts
index a60d074f..302f9ff4 100644
--- a/src/utils/ukey.ts
+++ b/src/utils/ukey.ts
@@ -1,5 +1,7 @@
import axios from 'axios'
-
+import request from "@/config/axios";
+import { useExamCacheStore } from '@/store/modules/examCache'
+const route = useRoute()
// 类型声明
declare global {
interface Window {
@@ -10,45 +12,81 @@ declare global {
// 动态加载 SDK
export const loadUKeySdk = () => {
return new Promise
((resolve, reject) => {
+ // 输出调试信息
+ // console.log('=== SDK 加载调试信息 ===')
+ // console.log('当前页面URL:', window.location.href)
+ // console.log('当前域名:', window.location.origin)
+ // console.log('当前路径:', window.location.pathname)
+ // console.log('当前协议:', window.location.protocol)
+ // console.log('当前主机:', window.location.host)
+ // console.log('当前端口:', window.location.port)
+ // console.log('完整域名:', window.location.hostname)
+ // console.log('========================')
+
// 如果已经加载过,直接返回
if (window.QysUKeySdk) {
resolve()
return
}
- const script = document.createElement('script')
- script.src = '/sdk.min.v1.0.0.js'
- script.type = 'text/javascript'
-
- let retryCount = 0
- const maxRetries = 10
- const checkInterval = 100 // 100ms 检查一次
-
- const checkSDK = () => {
- if (window.QysUKeySdk) {
- console.log('SDK 加载成功')
- resolve()
- } else if (retryCount < maxRetries) {
- retryCount++
- console.log(`等待 SDK 初始化... (${retryCount}/${maxRetries})`)
- setTimeout(checkSDK, checkInterval)
- } else {
- reject(new Error('SDK 加载失败:QysUKeySdk 未定义,请检查 SDK 文件是否正确'))
+ // 尝试多个可能的路径
+ const possiblePaths = [
+ '/sdk.min.v1.0.0.js',
+ './sdk.min.v1.0.0.js',
+ `${window.location.origin}/sdk.min.v1.0.0.js`,
+ '/ecg/sdk.min.v1.0.0.js',
+ './ecg/sdk.min.v1.0.0.js'
+ ]
+
+ let currentPathIndex = 0
+
+ const tryLoadScript = () => {
+ if (currentPathIndex >= possiblePaths.length) {
+ reject(new Error('所有SDK路径都尝试失败,请检查SDK文件是否存在'))
+ return
}
+
+ const script = document.createElement('script')
+ script.src = possiblePaths[currentPathIndex]
+ script.type = 'text/javascript'
+
+ let retryCount = 0
+ const maxRetries = 10
+ const checkInterval = 100 // 100ms 检查一次
+
+ const checkSDK = () => {
+ if (window.QysUKeySdk) {
+ console.log('SDK 加载成功,路径:', possiblePaths[currentPathIndex])
+ resolve()
+ } else if (retryCount < maxRetries) {
+ retryCount++
+ console.log(`等待 SDK 初始化... (${retryCount}/${maxRetries})`)
+ setTimeout(checkSDK, checkInterval)
+ } else {
+ // 当前路径失败,尝试下一个路径
+ console.log(`路径 ${possiblePaths[currentPathIndex]} 失败,尝试下一个路径`)
+ currentPathIndex++
+ tryLoadScript()
+ }
+ }
+
+ script.onload = () => {
+ console.log('SDK 脚本加载完成,路径:', possiblePaths[currentPathIndex])
+ // 脚本加载完成后,给一点时间让 SDK 初始化
+ setTimeout(checkSDK, 50)
+ }
+
+ script.onerror = (error) => {
+ console.error('SDK 脚本加载失败,路径:', possiblePaths[currentPathIndex], error)
+ // 当前路径失败,尝试下一个路径
+ currentPathIndex++
+ tryLoadScript()
+ }
+
+ document.head.appendChild(script)
}
-
- script.onload = () => {
- console.log('SDK 脚本加载完成,开始检查初始化状态')
- // 脚本加载完成后,给一点时间让 SDK 初始化
- setTimeout(checkSDK, 50)
- }
-
- script.onerror = (error) => {
- console.error('SDK 脚本加载失败:', error)
- reject(new Error('SDK 脚本加载失败:' + error))
- }
-
- document.head.appendChild(script)
+
+ tryLoadScript()
})
}
@@ -65,26 +103,39 @@ const downloadPdf = (data: any, filename: string = 'signed.pdf') => {
URL.revokeObjectURL(url)
}
-const uploadPdf = async (byteArray: number[], filename: string = 'signed.pdf') => {
+const uploadPdf = async (byteArray: number[], filename) => {
// return
try {
// 将字节数组转换为逗号分隔的字符串
const fileStream = byteArray.join(',');
+ // 获取缓存store
+ const examCacheStore = useExamCacheStore()
+
+ // 优先从缓存获取examId和orgId,如果没有则从route.query获取
+ const examId = examCacheStore.getValidExamId || (route.query.examId as string) || ''
+ const orgId = examCacheStore.getValidOrgId || (route.query.orgId as string) || ''
+ // console.log(examId,orgId)
const formData = new FormData();
+ formData.append('examId', examId);
+ formData.append('orgId', orgId);
formData.append('fileStream', fileStream);
- formData.append('filename', filename);
+ formData.append('filename', filename??`examId=${examId}&orgId=${orgId}`);
formData.append('fileType', 'pdf');
- formData.append('savePath', 'F:/陕西省咸阳市礼泉县心电图FTP/ecgimage/imgOcrAndProcess/');
+ formData.append('savePath', 'F:/陕西省咸阳市礼泉县心电图FTP/ecgimage/signature/');
- const response = await fetch('https://zzxmc.gw12320.com/uploadFile', {
+ // const url = "http://localhost:58080/uploadFile"
+ // const url = 'http://zzxmc.gw12320.com/uploadFile'
+ /*const response = await fetch(url, {
method: 'POST',
body: formData
});
+*/
+ const response = await request.post({url:`/tblist/ecganalysisparas/rpc-uploadPdf`,data:formData})
- const result = await response.json();
- console.log('上传结果:', result);
- return result;
+ // const result = await response.json();
+ console.log('上传结果:', response);
+ return response;
} catch (error) {
console.error('文件上传失败:', error);
throw error;
@@ -97,13 +148,16 @@ export const getUKeySdk = () => {
const sdk = new window.QysUKeySdk({
signCb: async function(file) {
try {
- await uploadPdf(file[0].rawFileData)
+ console.log(file)
+ await uploadPdf(file[0].rawFileData,null)
+
console.log('签署完成,文件已上传到服务器', file);
} catch (error) {
console.error('文件处理失败:', error)
}
}
})
+ console.log(route)
return sdk
} catch (error) {
console.error('创建 SDK 实例失败:', error)
diff --git a/src/views/ECG/ECGForm.vue b/src/views/ECG/ECGForm.vue
index 508fe7ce..8e12f797 100644
--- a/src/views/ECG/ECGForm.vue
+++ b/src/views/ECG/ECGForm.vue
@@ -602,6 +602,8 @@ import { processImageApi } from '@/api/processImage'
import {encodeBase64} from "@/utils/base64"
import Dialog from "@/components/Dialog/src/Dialog.vue";
import { log } from 'console'
+import { useExamCacheStore } from '@/store/modules/examCache'
+import { getConfig, getConfigKey } from '@/api/infra/config'
const { toClipboard } = useClipboard()
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
@@ -737,36 +739,48 @@ const open = async (row: any) => {
}
//审核功能
async function process() {
+ /* console.log(queryParams.value)
+ console.log(rowinfo.value)
+ return */
+
uKeyable.value=false
if (applyFormVO.value.reportstatus === '已分析') {
+
// 审核确认
await message.delConfirm('是否进行审核操作', '审核')
// uKeyable.value=true
- const response = await PatientexamlistApi.examine(keyid.value,Profilevo.value.doctorname,Profilevo.value.doctorID)
+ const response = await PatientexamlistApi.examine(keyid.value,Profilevo.value.doctorname,Profilevo.value.doctorID,queryParams.value.id,queryParams.value.doctorDiagResult)
if (response) {
+ console.log(response)
message.alertSuccess('审核成功')
- let data = Object.assign({},processImageApi.paramsList[rowinfo.value.orgName])
+ /* let data = Object.assign({},processImageApi.paramsList[rowinfo.value.orgName])
data.imagePath = queryParams.value.ecgJsonDataFilePath
data.examId = rowinfo.value.examId
data.orgId = rowinfo.value.orgId
data.watermarkText = data.step + queryParams.value.doctorDiagResult
- // console.log(data)
data.imagePath = processImageApi.urlToAddress(data.imagePath)
- // const processResponse = await processImageApi.processImg(data,processImageApi.apiUrl111)
- const processResponse = await processImageApi.processImg(data,processImageApi.apiUrl222)
+ const processResponse = await processImageApi.processImg(data,processImageApi.apiUrl111) // 移动项目
+ // const processResponse = await processImageApi.processImg(data,processImageApi.apiUrl222) // 礼泉县项目
// 1. 解析外层 JSON 的 data 字符串
const dataObj = JSON.parse(processResponse.data.data);
// 2. 直接获取 updateSuccess 的值
- const updateSuccess = dataObj.updateSuccess;
- if (!updateSuccess){
- message.warning('诊断图片生成异常')
- }else{
- const imageUrl = processImageApi.addressToUrl(dataObj.imagePath)
- // 使用 window.open 打开新标签页
- window.open(`/translate?img=${encodeURIComponent(imageUrl)}`, '_blank')
- }
+ const updateSuccess = dataObj.updateSuccess; */
+
+ // const imageUrl = processImageApi.addressToUrl(dataObj.imagePath)
+ // console.log(imageUrl)
+ // 更新缓存
+ const examCacheStore = useExamCacheStore()
+ examCacheStore.setCache(rowinfo.value.examId, rowinfo.value.orgId, response)
+
+ // 使用 window.open 打开新标签页 仅222礼泉县需要开启
+ // window.open(`/translate?img=${encodeURIComponent(response)}`, '_blank')
+ const config = await getConfigKey("pdf.sign.key")
+ if(config == "1"){
+ router.push({path:'/translate',query:{img:response}})
+ }
+
await getPatientexamlist(keyid.value)
emit('success')
diff --git a/src/views/ECG/translate/index2.vue b/src/views/ECG/translate/index2.vue
index 9d2264e0..1a86d162 100644
--- a/src/views/ECG/translate/index2.vue
+++ b/src/views/ECG/translate/index2.vue
@@ -37,7 +37,7 @@ import { ref, onMounted, nextTick } from 'vue'
import { loadUKeySdk, getUKeySdk } from '@/utils/ukey'
import { ElLoading, ElMessage } from 'element-plus'
import axios from 'axios'
-import { log } from 'console'
+import request from '@/config/axios'
const ukeySdk = ref(null)
const container = ref(null)
@@ -49,12 +49,33 @@ import { log } from 'console'
loading.value = true
const pdfParam = route.query.img
console.log('PDF参数:', pdfParam)
+
try {
- await loadUKeySdk()
- ukeySdk.value = getUKeySdk()
- if (!ukeySdk.value) {
- throw new Error('SDK 初始化失败')
+ // 尝试加载 SDK,最多重试 3 次
+ let sdkLoaded = false
+ for (let i = 0; i < 3; i++) {
+ try {
+ console.log(`尝试加载 SDK (第 ${i + 1} 次)`)
+ await loadUKeySdk()
+ ukeySdk.value = getUKeySdk()
+ if (ukeySdk.value) {
+ sdkLoaded = true
+ break
+ }
+ } catch (error) {
+ console.error(`第 ${i + 1} 次加载失败:`, error)
+ if (i === 2) {
+ throw error
+ }
+ // 等待 1 秒后重试
+ await new Promise(resolve => setTimeout(resolve, 1000))
+ }
}
+
+ if (!sdkLoaded) {
+ throw new Error('SDK 初始化失败,请刷新页面重试')
+ }
+
ElMessage.success('SDK 加载成功')
// 如果有URL参数,使用URL参数;否则使用默认URL
@@ -73,7 +94,7 @@ import { log } from 'console'
})
} catch (error: any) {
console.error('SDK加载失败:', error)
- ElMessage.error(error.message || 'SDK 加载失败')
+ ElMessage.error(error.message || 'SDK 加载失败,请刷新页面重试')
} finally {
loading.value = false
}
@@ -131,9 +152,17 @@ import { log } from 'console'
console.log('开始设置loading状态')
loading.value = true
try {
- console.log('开始获取PDF文件:', pdf)
+ // 转换PDF URL,使用代理路径
+ let pdfUrl = pdf
+ if (pdf.includes('zzxmc.gw12320.com')) {
+ // 将外部域名替换为代理路径
+ pdfUrl = pdf.replace('https://zzxmc.gw12320.com', '')
+ console.log('转换后的PDF URL:', pdfUrl)
+ }
+
+ console.log('开始获取PDF文件:', pdfUrl)
// 获取 PDF 文件
- const response = await axios.get(pdf, {
+ const response = await axios.get(pdfUrl, {
responseType: 'blob',
timeout: 30000, // 设置超时时间
headers: {
@@ -141,7 +170,6 @@ import { log } from 'console'
'Pragma': 'no-cache'
}
})
-
console.log('PDF文件获取成功,响应状态:', response.status)
console.log('开始创建File对象')
// 创建 File 对象
diff --git a/src/views/system/mail/log/log.data.ts b/src/views/system/mail/log/log.data.ts
index 62cbf516..1bdeae01 100644
--- a/src/views/system/mail/log/log.data.ts
+++ b/src/views/system/mail/log/log.data.ts
@@ -3,7 +3,12 @@ import { dateFormatter } from '@/utils/formatTime'
import * as MailAccountApi from '@/api/system/mail/account'
// 邮箱账号的列表
-const accountList = await MailAccountApi.getSimpleMailAccountList()
+let accountList: any[] = []
+
+// 初始化账号列表
+const initAccountList = async () => {
+ accountList = await MailAccountApi.getSimpleMailAccountList()
+}
// CrudSchema:https://doc.iocoder.cn/vue3/crud-schema/
const crudSchemas = reactive([
@@ -74,7 +79,12 @@ const crudSchemas = reactive([
search: {
show: true,
component: 'Select',
- api: () => accountList,
+ api: async () => {
+ if (accountList.length === 0) {
+ await initAccountList()
+ }
+ return accountList
+ },
componentProps: {
optionsAlias: {
labelField: 'mail',
diff --git a/vite.config.ts b/vite.config.ts
index 8cba9150..8ba30f1c 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -29,14 +29,21 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
host: "0.0.0.0",
open: env.VITE_OPEN === 'true',
// 本地跨域代理. 目前注释的原因:暂时没有用途,server 端已经支持跨域
- // proxy: {
- // ['/admin-api']: {
- // target: env.VITE_BASE_URL,
- // ws: false,
- // changeOrigin: true,
- // rewrite: (path) => path.replace(new RegExp(`^/admin-api`), ''),
- // },
- // },
+ proxy: {
+ ['/admin-api']: {
+ target: env.VITE_BASE_URL,
+ ws: false,
+ changeOrigin: true,
+ rewrite: (path) => path.replace(new RegExp(`^/admin-api`), ''),
+ },
+ // 添加PDF文件代理
+ ['/ecgimage']: {
+ target: 'https://zzxmc.gw12320.com',
+ ws: false,
+ changeOrigin: true,
+ secure: false, // 如果是https可能需要设置为false
+ },
+ },
},
// 项目使用的vite插件。 单独提取到build/vite/plugin中管理
plugins: createVitePlugins(),