Browse Source

feature(个人中心):添加头像修改、用户密码修改

wanglj 10 months ago
parent
commit
e04543d835
60 changed files with 1811 additions and 498 deletions
  1. 2 1
      .env.development
  2. 1 0
      .env.production
  3. 1 0
      .gitignore
  4. 14 2
      components.d.ts
  5. 1 0
      package.json
  6. 8 1
      pnpm-lock.yaml
  7. 1 0
      public/MP_verify_jHeJdSz4SdWv5M6w.txt
  8. 1 1
      src/api/execution/flow.ts
  9. 1 1
      src/api/execution/index.ts
  10. 7 1
      src/api/login/index.ts
  11. 66 0
      src/api/platform/appoint/index.ts
  12. 35 0
      src/api/platform/home/assign.ts
  13. 39 0
      src/api/platform/home/cell.ts
  14. 67 0
      src/api/platform/home/index.ts
  15. 39 0
      src/api/platform/home/molecular.ts
  16. 35 0
      src/api/platform/home/service.ts
  17. 34 0
      src/api/platform/home/user.ts
  18. 1 1
      src/api/project/index.ts
  19. 1 1
      src/api/system/dept.ts
  20. 1 1
      src/api/system/dict.ts
  21. 1 1
      src/api/system/index.ts
  22. 1 1
      src/api/system/news.ts
  23. 1 1
      src/api/system/user.ts
  24. 1 1
      src/api/training/index.ts
  25. BIN
      src/assets/img/logo-login.png
  26. BIN
      src/assets/img/仪器预约.png
  27. BIN
      src/assets/img/入室申请.png
  28. BIN
      src/assets/img/入室预约.png
  29. BIN
      src/assets/img/动物笼位.png
  30. BIN
      src/assets/img/培训考试.png
  31. BIN
      src/assets/img/技术委托.png
  32. BIN
      src/assets/img/更多应用.png
  33. BIN
      src/assets/img/试剂耗材.png
  34. 89 0
      src/layout/entry.vue
  35. 40 24
      src/layout/index.vue
  36. 56 13
      src/router.ts
  37. 20 6
      src/stores/userInfo.ts
  38. 5 0
      src/theme/index.scss
  39. 1 0
      src/types/index.d.ts
  40. 0 222
      src/utils/micro_request.js
  41. 1 1
      src/utils/micro_request.ts
  42. 5 3
      src/utils/request.ts
  43. 299 0
      src/view/entry/add.vue
  44. 157 0
      src/view/entry/index.vue
  45. 30 15
      src/view/exam/cover.vue
  46. 77 63
      src/view/home/index.vue
  47. 3 3
      src/view/instr/appointList/index.vue
  48. 18 35
      src/view/instr/list.vue
  49. 64 10
      src/view/login/index.vue
  50. 6 4
      src/view/notice/detail.vue
  51. 122 0
      src/view/notice/index.vue
  52. 7 8
      src/view/register/index.vue
  53. 70 30
      src/view/service/index.vue
  54. 21 20
      src/view/todo/index.vue
  55. 2 9
      src/view/training/enroll.vue
  56. 188 6
      src/view/user/edit.vue
  57. 10 9
      src/view/user/index.vue
  58. 150 0
      src/view/user/password.vue
  59. 1 1
      tsconfig.json
  60. 10 2
      vite.config.ts

+ 2 - 1
.env.development

@@ -2,7 +2,7 @@
  # @Author: wanglj 471442253@qq.com
  # @Date: 2023-07-11 14:10:47
  # @LastEditors: wanglj wanglijie@dashoo.cn
- # @LastEditTime: 2025-03-12 16:03:15
+ # @LastEditTime: 2025-03-19 15:53:47
  # @Description: file content
  # @FilePath: \labsop_meno\frontend\packages\base\.env.development
 ###
@@ -21,6 +21,7 @@ VITE_FINANCE = dashoo.labsop.finance-34000
 VITE_SCI = dashoo.labsop.scientific-34000
 VITE_INSTR_ADMIN = dashoo.labsop.apparatus-34000
 VITE_LEARNING = dashoo.labsop.learning-34000
+VITE_PLATFORM_API = dashoo.labsop.platform-34000
 VITE_LABORATORY: dashoo.labsop.laboratory-34000
 
 #公共配置

+ 1 - 0
.env.production

@@ -22,6 +22,7 @@ VITE_SCI = dashoo.labsop.scientific-34000
 VITE_WORKFLOW = dashoo.labsop.workflow-34000
 VITE_INSTR_ADMIN = dashoo.labsop.apparatus-34000
 VITE_LEARNING = dashoo.labsop.learning-34000
+VITE_PLATFORM_API = dashoo.labsop.platform-34000
 
 #公共配置
 VITE_UPLOAD = /weedfs/upload

+ 1 - 0
.gitignore

@@ -23,3 +23,4 @@ dist-ssr
 *.sln
 *.sw?
 .history/
+*.orig

+ 14 - 2
components.d.ts

@@ -12,23 +12,35 @@ declare module 'vue' {
     VanButton: typeof import('vant/es')['Button']
     VanCell: typeof import('vant/es')['Cell']
     VanCellGroup: typeof import('vant/es')['CellGroup']
+    VanCheckbox: typeof import('vant/es')['Checkbox']
+    VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
+    VanCol: typeof import('vant/es')['Col']
+    VanDatePicker: typeof import('vant/es')['DatePicker']
+    VanDialog: typeof import('vant/es')['Dialog']
     VanEmpty: typeof import('vant/es')['Empty']
     VanField: typeof import('vant/es')['Field']
+    VanFloatingBubble: typeof import('vant/es')['FloatingBubble']
     VanForm: typeof import('vant/es')['Form']
     VanIcon: typeof import('vant/es')['Icon']
     VanList: typeof import('vant/es')['List']
     VanNotify: typeof import('vant/es')['Notify']
     VanPicker: typeof import('vant/es')['Picker']
+    VanPickerGroup: typeof import('vant/es')['PickerGroup']
     VanPopup: typeof import('vant/es')['Popup']
     VanRadio: typeof import('vant/es')['Radio']
     VanRadioGroup: typeof import('vant/es')['RadioGroup']
+    VanRow: typeof import('vant/es')['Row']
     VanSearch: typeof import('vant/es')['Search']
+    VanStep: typeof import('vant/es')['Step']
+    VanSteps: typeof import('vant/es')['Steps']
+    VanSwipe: typeof import('vant/es')['Swipe']
+    VanSwipeItem: typeof import('vant/es')['SwipeItem']
     VanTab: typeof import('vant/es')['Tab']
     VanTabbar: typeof import('vant/es')['Tabbar']
     VanTabbarItem: typeof import('vant/es')['TabbarItem']
     VanTabs: typeof import('vant/es')['Tabs']
-    VanTextarea: typeof import('vant/es')['Textarea']
+    VanTag: typeof import('vant/es')['Tag']
     VanTextEllipsis: typeof import('vant/es')['TextEllipsis']
-    VanToast: typeof import('vant/es')['Toast']
+    VanUploader: typeof import('vant/es')['Uploader']
   }
 }

+ 1 - 0
package.json

@@ -12,6 +12,7 @@
     "@vitejs/plugin-basic-ssl": "^2.0.0",
     "await-to-js": "^3.0.0",
     "axios": "^1.8.2",
+    "cropperjs": "1.5.13",
     "moment": "^2.30.1",
     "pinia": "^3.0.1",
     "postcss-px-to-viewport": "^1.1.1",

+ 8 - 1
pnpm-lock.yaml

@@ -1,7 +1,7 @@
 lockfileVersion: '6.0'
 
 settings:
-  autoInstallPeers: false
+  autoInstallPeers: true
   excludeLinksFromLockfile: false
 
 dependencies:
@@ -14,6 +14,9 @@ dependencies:
   axios:
     specifier: ^1.8.2
     version: 1.8.2
+  cropperjs:
+    specifier: 1.5.13
+    version: 1.5.13
   moment:
     specifier: ^2.30.1
     version: 2.30.1
@@ -713,6 +716,10 @@ packages:
       is-what: 4.1.16
     dev: false
 
+  /cropperjs@1.5.13:
+    resolution: {integrity: sha512-by7jKAo73y5/Do0K6sxdTKHgndY0NMjG2bEdgeJxycbcmHuCiMXqw8sxy5C5Y5WTOTcDGmbT7Sr5CgKOXR06OA==}
+    dev: false
+
   /csstype@3.1.3:
     resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 

+ 1 - 0
public/MP_verify_jHeJdSz4SdWv5M6w.txt

@@ -0,0 +1 @@
+jHeJdSz4SdWv5M6w

+ 1 - 1
src/api/execution/flow.ts

@@ -6,7 +6,7 @@
  * @Description: file content
  * @FilePath: \labsop_meno\frontend\packages\scientific\src\api\base\flow.ts
  */
-import request from '/@/utils/micro_request.js'
+import request from '/@/utils/micro_request'
 const basePath = import.meta.env.VITE_WORKFLOW
 export function useFlowApi() {
 	return {

+ 1 - 1
src/api/execution/index.ts

@@ -6,7 +6,7 @@
  * @Description: file content
  * @FilePath: \frontend\packages\vue-next-admin-sub\src\api\execution\index.ts
  */
-import request from '/@/utils/micro_request.js'
+import request from '/@/utils/micro_request'
 const basePath = import.meta.env.VITE_WORKFLOW
 const sciPath = import.meta.env.VITE_SCI
 // 参数设置

+ 7 - 1
src/api/login/index.ts

@@ -1,4 +1,4 @@
-import request from '/@/utils/micro_request.js'
+import request from '/@/utils/micro_request'
 const basePath = import.meta.env.VITE_ADMIN
 
 /**
@@ -13,6 +13,12 @@ export function useLoginApi() {
     signIn: (query?: object) => {
       return request.postRequestWithClientInfo(basePath, 'System', 'Login', query)
     },
+    weChatLoginOpenId: (query?: object) => {
+      return request.postRequestWithClientInfo(basePath, 'System', 'WeChatLoginOpenId', query)
+    },
+    weChatLogin: (query?: object) => {
+      return request.postRequestWithClientInfo(basePath, 'System', 'WeChatLogin', query)
+    },
     signOut: (query?: object) => {
       return request.postRequest(basePath, 'System', 'Logout', query)
     },

+ 66 - 0
src/api/platform/appoint/index.ts

@@ -0,0 +1,66 @@
+/*
+ * @Author: wanglj wanglijie@dashoo.cn
+ * @Date: 2025-02-27 10:35:40
+ * @LastEditors: wanglj wanglijie@dashoo.cn
+ * @LastEditTime: 2025-03-20 16:52:11
+ * @Description: file content
+ * @FilePath: \labsop_backup2\frontend\components\labsop-api\src\api\platform\appoint\index.ts
+ */
+import request from '/@/utils/micro_request.js';
+const basePath = import.meta.env.VITE_PLATFORM_API;
+export function usePlatformAppointApi() {
+  return {
+    // 创建
+    create(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'Create', params);
+    },
+    // 列表
+    getList(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'GetList', params);
+    },
+    // 详情
+    getDetail(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'GetEntityById', params);
+    },
+    // 修改
+    updateById(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'UpdateById', params);
+    },
+    // 删除
+    deleteById(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'DeleteById', params);
+    },
+    // 审批
+    approve(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'Approve', params);
+    },
+    // 撤销
+    quashApprove(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'QuashApprove', params);
+    },
+    // 首页获取平台预约列表
+    getHomePlatAppointList(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'GetListByPlatformId', params);
+    },
+    // 待确认列表
+    getConfirmedQueue(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'GetConfirmedQueue', params);
+    },
+    // 获取次月待出室人员
+    nextMonthPerson(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'NextMonthPerson', params);
+    },
+    // 入室确认
+    markNextMonthEntry(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'MarkNextMonthEntry', params);
+    },
+    // 上传
+    createFile(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'CreateFile', params);
+    },
+    // 重新上传
+    updateByIdAfterRefuse(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformAppoint', 'UpdateByIdAfterRefuse', params);
+    }
+  };
+}

+ 35 - 0
src/api/platform/home/assign.ts

@@ -0,0 +1,35 @@
+/*
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-07-19 13:42:40
+ * @LastEditors: wanglj
+ * @LastEditTime: 2025-01-20 10:09:56
+ * @Description: file content
+ * @FilePath: \labsop_backup2\frontend\components\labsop-api\src\api\platform\home\cell.ts
+ */
+import request from '/@/utils/micro_request.js';
+const basePath = import.meta.env.VITE_PLATFORM_API;
+export function useCellAssignApi() {
+  return {
+    // 资源分配
+    // 资源信息
+    assignInfo(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformCellAssign', 'AssignInfo', params);
+    },
+    // 队列
+    assignQueue(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformCellAssign', 'AssignQueue', params);
+    },
+    // 分配
+    create(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformCellAssign', 'Create', params);
+    },
+    // 获取用户平台
+    getUserPlat(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformCellAssign', 'PlatByMember', params);
+    },
+    // 下载缴费单
+    downBillfile(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformCellAssign', 'DownBillfile', params);
+    }
+  };
+}

+ 39 - 0
src/api/platform/home/cell.ts

@@ -0,0 +1,39 @@
+/*
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-07-19 13:42:40
+ * @LastEditors: wanglj
+ * @LastEditTime: 2025-01-20 10:09:56
+ * @Description: file content
+ * @FilePath: \labsop_backup2\frontend\components\labsop-api\src\api\platform\home\cell.ts
+ */
+import request from '/@/utils/micro_request.js';
+const basePath = import.meta.env.VITE_PLATFORM_API;
+export function useCellApi() {
+  return {
+    // 细胞培养间
+    // 创建
+    createCell(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformCell', 'CreateCell', params);
+    },
+    // 删除
+    deleteCellByIds(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformCell', 'DeleteCellByIds', params);
+    },
+    // 详情
+    getCellEntityById(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformCell', 'GetCellEntityById', params);
+    },
+    // 列表
+    getCellList(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformCell', 'GetCellList', params);
+    },
+    // 更新
+    updateCellById(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformCell', 'UpdateCellById', params);
+    },
+    // 更新状态
+    updateCellStatusById(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformCell', 'UpdateCellStatusById', params);
+    }
+  };
+}

+ 67 - 0
src/api/platform/home/index.ts

@@ -0,0 +1,67 @@
+/*
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-07-19 13:42:40
+ * @LastEditors: wanglj
+ * @LastEditTime: 2025-01-20 10:43:14
+ * @Description: file content
+ * @FilePath: \labsop_backup2\frontend\components\labsop-api\src\api\platform\home\index.ts
+ */
+import request from '/@/utils/micro_request.js';
+const basePath = import.meta.env.VITE_PLATFORM_API;
+export function usePlatformApi() {
+	return {
+		// 平台管理
+		// 列表
+		create(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatform', 'Create', params);
+		},
+		// 删除
+		deleteByIds(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatform', 'DeleteByIds', params);
+		},
+		// 列表
+		getPlatformList(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatform', 'GetList', params);
+		},
+		getAllPlatformList(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatform', 'GetPlatFormList', params);
+		},
+		// 平台资源类型
+		getResourceTypeDict(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatform', 'GetResourceTypeDict', params);
+		},
+		// 详情
+		getDetail(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatform', 'GetEntityById', params);
+		},
+		// 编辑
+		update(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatform', 'UpdateById', params);
+		},
+    // 资源
+    // 创建
+		createResource(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformResource', 'CreateResource', params);
+		},
+		// 删除
+		deleteResourceByIds(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformResource', 'DeleteResourceByIds', params);
+		},
+		// 详情
+		getResourceEntityById(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformResource', 'GetResourceEntityById', params);
+		},
+		// 列表
+		getResourceList(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformResource', 'GetResourceList', params);
+		},
+		// 更新
+		updateResourceById(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformResource', 'UpdateResourceById', params);
+		},
+		// 更新状态
+		updateResourceStatusById(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformResource', 'UpdateResourceStatusById', params);
+		},
+	};
+}

+ 39 - 0
src/api/platform/home/molecular.ts

@@ -0,0 +1,39 @@
+/*
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-07-19 13:42:40
+ * @LastEditors: wanglj
+ * @LastEditTime: 2025-01-20 10:09:56
+ * @Description: file content
+ * @FilePath: \labsop_backup2\frontend\components\labsop-api\src\api\platform\home\cell.ts
+ */
+import request from '/@/utils/micro_request.js';
+const basePath = import.meta.env.VITE_PLATFORM_API;
+export function userMoleApi() {
+	return {
+		// 分子室
+		// 创建
+		createMolecular(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformMolecular', 'CreateMolecular', params);
+		},
+		// 删除
+		deleteMolecularByIds(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformMolecular', 'DeleteMolecularByIds', params);
+		},
+		// 详情
+		getMolecularEntityById(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformMolecular', 'GetMolecularEntityById', params);
+		},
+		// 列表
+		getMolecularList(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformMolecular', 'GetMolecularList', params);
+		},
+		// 更新
+		updateMolecularById(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformMolecular', 'UpdateMolecularById', params);
+		},
+		// 更新状态
+		updateMolecularStatusById(params?: Object) {
+			return request.postRequest(basePath, 'PlatPlatformMolecular', 'UpdateMolecularStatusById', params);
+		},
+	};
+}

+ 35 - 0
src/api/platform/home/service.ts

@@ -0,0 +1,35 @@
+/*
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-07-19 13:42:40
+ * @LastEditors: wanglj
+ * @LastEditTime: 2025-01-20 10:43:14
+ * @Description: file content
+ * @FilePath: \labsop_backup2\frontend\components\labsop-api\src\api\platform\home\index.ts
+ */
+import request from '/@/utils/micro_request.js';
+const basePath = import.meta.env.VITE_PLATFORM_API;
+export function usePlatformServiceApi() {
+  // 服务管理
+  return {
+    // 创建
+    create(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformService', 'Create', params);
+    },
+    // 更新
+    update(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformService', 'UpdateById', params);
+    },
+    // 删除
+    deleteByIds(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformService', 'DeleteByIds', params);
+    },
+    // 列表 
+    getList(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformService', 'GetList', params);
+    },
+    // 详情
+    getDetail(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatformService', 'GetEntityById', params);
+    },
+  };
+}

+ 34 - 0
src/api/platform/home/user.ts

@@ -0,0 +1,34 @@
+/*
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-07-19 13:42:40
+ * @LastEditors: wanglj
+ * @LastEditTime: 2025-01-20 10:09:56
+ * @Description: file content
+ * @FilePath: \labsop_backup2\frontend\components\labsop-api\src\api\platform\home\cell.ts
+ */
+import request from '/@/utils/micro_request.js';
+const basePath = import.meta.env.VITE_PLATFORM_API;
+export function usePlatUserApi() {
+  return {
+    // 列表
+    getUserList(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatform', 'GetPlatformMemberByPlatformId', params);
+    },
+    // 列表
+    getUserDetail(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatform', 'GetPlatformMemberById', params);
+    },
+    // 添加
+    add(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatform', 'CreatePlatformMember', params);
+    },
+    // 更新
+    update(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatform', 'UpdatePlatformMember', params);
+    },
+    // 删除
+    delete(params?: Object) {
+      return request.postRequest(basePath, 'PlatPlatform', 'DeleteMemberByIds', params);
+    },
+  };
+}

+ 1 - 1
src/api/project/index.ts

@@ -6,7 +6,7 @@
  * @Description: file content
  * @FilePath: \labsop_backup\frontend\components\labsop-api\src\api\base\project\index.ts
  */
-import request from '/@/utils/micro_request.js';
+import request from '/@/utils/micro_request';
 /**
  * @method addInstr 新增仪器
  */

+ 1 - 1
src/api/system/dept.ts

@@ -6,7 +6,7 @@
  * @Description: file content
  * @FilePath: \labsop_meno\frontend\packages\vue-next-admin\src\api\system\dept.ts
  */
-import request from '/@/utils/micro_request.js'
+import request from '/@/utils/micro_request'
 /**
  * 以下为模拟接口地址,gitee 的不通,就换自己的真实接口地址
  *

+ 1 - 1
src/api/system/dict.ts

@@ -6,7 +6,7 @@
  * @Description: file content
  * @FilePath: \labsop_meno\frontend\packages\vue-next-admin\src\api\system\dict.ts
  */
-import request from '/@/utils/micro_request.js'
+import request from '/@/utils/micro_request'
 const basePath = import.meta.env.VITE_ADMIN
 // 参数设置
 export function useDictApi() {

+ 1 - 1
src/api/system/index.ts

@@ -6,7 +6,7 @@
  * @Description: file content
  * @FilePath: \labsop_meno\frontend\packages\instr\src\api\base\index.ts
  */
-import request from '/@/utils/micro_request.js'
+import request from '/@/utils/micro_request'
 import requestCommon from '/@/utils/request'
 const basePath = import.meta.env.VITE_ADMIN
 const commonPath = import.meta.env.VITE_COMMON

+ 1 - 1
src/api/system/news.ts

@@ -6,7 +6,7 @@
  * @Description: file content
  * @FilePath: \labsop_meno\frontend\packages\base\src\api\system\notice.ts
  */
-import request from '/@/utils/micro_request.js'
+import request from '/@/utils/micro_request'
 const basePath = import.meta.env.VITE_ADMIN
 const webPath = import.meta.env.VITE_WEBSITE_API
 // 参数设置

+ 1 - 1
src/api/system/user.ts

@@ -6,7 +6,7 @@
  * @Description: file content
  * @FilePath: \labsop_meno\frontend\packages\base\src\api\system\user.ts
  */
-import request from '/@/utils/micro_request.js'
+import request from '/@/utils/micro_request'
 const basePath = import.meta.env.VITE_ADMIN
 const insrtPath = import.meta.env.VITE_INSTR_ADMIN
 // 日程管理

+ 1 - 1
src/api/training/index.ts

@@ -6,7 +6,7 @@
  * @Description: file content
  * @FilePath: \labsop_meno\frontend\packages\instr\src\api\base\index.ts
  */
-import request from '/@/utils/micro_request.js'
+import request from '/@/utils/micro_request'
 const basePath = import.meta.env.VITE_LEARNING
 // 培训考试
 export function useTrainingApi() {

BIN
src/assets/img/logo-login.png


BIN
src/assets/img/仪器预约.png


BIN
src/assets/img/入室申请.png


BIN
src/assets/img/入室预约.png


BIN
src/assets/img/动物笼位.png


BIN
src/assets/img/培训考试.png


BIN
src/assets/img/技术委托.png


BIN
src/assets/img/更多应用.png


BIN
src/assets/img/试剂耗材.png


+ 89 - 0
src/layout/entry.vue

@@ -0,0 +1,89 @@
+<!--
+ * @Author: wanglj wanglijie@dashoo.cn
+ * @Date: 2025-03-13 09:07:55
+ * @LastEditors: wanglj wanglijie@dashoo.cn
+ * @LastEditTime: 2025-03-20 16:38:29
+ * @FilePath: \labsop-h5\src\layout\index.vue
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+-->
+<template>
+  <router-view></router-view>
+  <van-tabbar route>
+    <van-tabbar-item replace to="/entry" icon="send-gift-o">入室申请</van-tabbar-item>
+    <van-tabbar-item replace to="/entry/mine" icon="coupon-o">我的入室</van-tabbar-item>
+    <van-tabbar-item replace to="/entry/appoint" icon="cluster-o">入室预约</van-tabbar-item>
+  </van-tabbar>
+</template>
+
+<script lang="ts" setup>
+  import to from 'await-to-js'
+  import { showDialog } from 'vant'
+  import { ref } from 'vue'
+  import { useRouter } from 'vue-router'
+  import { useUserInfo } from '/@/stores/userInfo'
+  import { Local } from '/@/utils/storage'
+
+  const active = ref(0)
+  const router = useRouter()
+
+  const onRouterPush = (val: string) => {
+    router.push(val)
+  }
+  const scan = async () => {
+    const res = await useUserInfo().scanCode()
+    if (res) {
+      window.location.href = res as string
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .footer-nav {
+    height: 60px;
+    font-weight: bold;
+    ul {
+      height: 60px;
+      display: flex;
+      li {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        font-size: 12px;
+        &.center-icon {
+          height: 60px;
+          flex: 0 0 60px;
+          padding: 10px;
+          align-self: flex-end;
+          i {
+            padding: 10px;
+            background: #1d66dc;
+            border-radius: 50%;
+          }
+        }
+        img {
+          height: 24px;
+          width: 24px;
+        }
+        .img-container {
+          padding: 6px;
+          height: 24px;
+          width: 24px;
+          background-color: #419fe5;
+          border-radius: 50%;
+          overflow: visible;
+        }
+        &.service .img-container {
+          background-color: #f59969;
+        }
+        &.todo .img-container {
+          background-color: #20b5ae;
+        }
+        &.user .img-container {
+          background-color: #595cac;
+        }
+      }
+    }
+  }
+</style>

+ 40 - 24
src/layout/index.vue

@@ -2,7 +2,7 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-13 09:07:55
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-19 15:36:25
+ * @LastEditTime: 2025-03-21 09:17:49
  * @FilePath: \labsop-h5\src\layout\index.vue
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 -->
@@ -10,31 +10,35 @@
   <router-view></router-view>
   <footer class="footer-nav">
     <ul>
-      <li @click="onRouterPush('/home')">
-        <div class="img-container">
+      <li @click="onRouterPush('/home')" :class="{ active: route.path == '/home' }">
+        <!-- <div class="img-container">
           <img src="../assets/img/home-icon.png" alt="" />
-        </div>
+        </div> -->
+        <van-icon name="wap-home-o" :size="24" />
         首页
       </li>
-      <li class="service" @click="onRouterPush('/service')">
-        <div class="img-container">
+      <li class="service" @click="onRouterPush('/todo')" :class="{ active: route.path == '/todo' }">
+        <!-- <div class="img-container">
           <img src="../assets/img/service-icon.png" alt="" />
-        </div>
-        服务
+        </div> -->
+        <van-icon name="cluster-o" :size="24" />
+        待办
       </li>
       <li class="center-icon" @click="scan">
         <van-icon name="scan" :size="50" color="#fff" />
       </li>
-      <li class="todo" @click="onRouterPush('/todo')">
-        <div class="img-container">
+      <li class="todo" @click="onRouterPush('/notice')" :class="{ active: route.path == '/notice' }">
+        <!-- <div class="img-container">
           <img src="../assets/img/todo-icon.png" alt="" />
-        </div>
-        待办
+        </div> -->
+        <van-icon name="todo-list-o" :size="24" />
+        通知
       </li>
-      <li class="user" @click="onRouterPush('/user')">
-        <div class="img-container">
+      <li class="user" @click="onRouterPush('/user')" :class="{ active: route.path == '/user' }">
+        <!-- <div class="img-container">
           <img src="../assets/img/user-icon.png" alt="" />
-        </div>
+        </div> -->
+        <van-icon name="user-o" :size="24" />
         我的
       </li>
     </ul>
@@ -44,13 +48,20 @@
 <script lang="ts" setup>
   import to from 'await-to-js'
   import { showDialog } from 'vant'
-  import { ref } from 'vue'
-  import { useRouter } from 'vue-router'
+  import { ref, watch } from 'vue'
+  import { useRouter, useRoute } from 'vue-router'
   import { useUserInfo } from '/@/stores/userInfo'
   import { Local } from '/@/utils/storage'
 
   const active = ref(0)
   const router = useRouter()
+  const route = useRoute()
+  watch(
+    () => route.path,
+    () => {
+      console.log(route.path)
+    }
+  )
 
   const onRouterPush = (val: string) => {
     router.push(val)
@@ -66,7 +77,6 @@
 <style lang="scss" scoped>
   .footer-nav {
     height: 60px;
-    font-weight: bold;
     ul {
       height: 60px;
       display: flex;
@@ -76,6 +86,10 @@
         flex-direction: column;
         align-items: center;
         justify-content: center;
+        font-size: 12px;
+        &.active {
+          color: #1989fa;
+        }
         &.center-icon {
           height: 60px;
           flex: 0 0 60px;
@@ -88,16 +102,18 @@
           }
         }
         img {
-          height: 24px;
-          width: 24px;
+          height: 20px;
+          width: 20px;
         }
         .img-container {
-          padding: 6px;
-          height: 24px;
-          width: 24px;
+          height: 28px;
+          width: 28px;
           background-color: #419fe5;
           border-radius: 50%;
-          overflow: visible;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          margin-bottom: 4px;
         }
         &.service .img-container {
           background-color: #f59969;

+ 56 - 13
src/router.ts

@@ -2,13 +2,14 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-10 11:40:15
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-19 15:35:49
+ * @LastEditTime: 2025-03-21 14:49:28
  * @FilePath: \vue3-ts\src\router.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
-import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
+import { createRouter, createWebHistory } from 'vue-router'
 import { Local } from './utils/storage'
 import { useUserInfo } from '/@/stores/userInfo'
+import { showDialog } from 'vant'
 const routes = [
   {
     name: 'notFound',
@@ -92,7 +93,7 @@ const routes = [
         path: '/todo',
         component: () => import('/@/view/todo/index.vue'),
         meta: {
-          title: '待办'
+          title: '待办事项'
         }
       },
       {
@@ -103,10 +104,18 @@ const routes = [
           title: '审批详情'
         }
       },
+      {
+        name: 'notice',
+        path: '/notice',
+        component: () => import('/@/view/notice/index.vue'),
+        meta: {
+          title: '通知公告'
+        }
+      },
       {
         name: 'noticeDetail',
-        path: '/todo/notice-detail',
-        component: () => import('/@/view/todo/notice-detail.vue'),
+        path: '/notice/detail',
+        component: () => import('/@/view/notice/detail.vue'),
         meta: {
           title: '通知详情'
         }
@@ -127,6 +136,14 @@ const routes = [
           title: '个人信息编辑'
         }
       },
+      {
+        name: 'userPassword',
+        path: '/user/password',
+        component: () => import('/@/view/user/password.vue'),
+        meta: {
+          title: '修改密码'
+        }
+      },
       {
         name: 'training',
         path: '/training',
@@ -160,6 +177,29 @@ const routes = [
         }
       },
     ]
+  },
+  {
+    path: '/',
+    redirect: '/login',
+    component: () => import('/@/layout/entry.vue'),
+    children: [
+      {
+        name: 'entry',
+        path: '/entry',
+        component: () => import('/@/view/entry/index.vue'),
+        meta: {
+          title: '入室申请'
+        }
+      },
+      {
+        name: 'entryAdd',
+        path: '/entry/add',
+        component: () => import('/@/view/entry/add.vue'),
+        meta: {
+          title: '新增入室申请'
+        }
+      }
+    ]
   }
 ]
 
@@ -170,13 +210,13 @@ const router = createRouter({
 const whiteList = ['/login', '/register', '/training', '/training/enroll']
 router.beforeEach((to, from, next) => {
   const storesUseUserInfo = useUserInfo()
-  // 微信授权码获取openId
-  const code = to.query.code
-  if (!storesUseUserInfo.openId && code) {
-    if (typeof code === 'string') {
-      storesUseUserInfo.setOpenId(code)
-    }
-  }
+  // // 微信授权码获取openId
+  // const code = to.query.code
+  // if (!storesUseUserInfo.openId && code) {
+  //   if (typeof code === 'string') {
+  //     storesUseUserInfo.setOpenId(code)
+  //   }
+  // }
   const title = to?.meta?.title
   if (title) {
     document.title = title as string
@@ -186,7 +226,10 @@ router.beforeEach((to, from, next) => {
     next()
   } else {
     if (!token) {
-      next(`/login?redirect=${to.path}&params=${JSON.stringify(to.query ? to.query : to.params)}`)
+      next({
+        path: '/login',
+        query: to.query
+      })
     } else {
       if (!storesUseUserInfo.userInfos.id) {
         storesUseUserInfo.setUserInfos()

+ 20 - 6
src/stores/userInfo.ts

@@ -2,7 +2,7 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-17 14:46:02
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-19 15:18:54
+ * @LastEditTime: 2025-03-21 11:19:34
  * @FilePath: \labsop-h5\src\view\stores\userInfo.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -40,6 +40,7 @@ export const useUserInfo = defineStore('userInfo', {
       pgName: ''
     },
     openId: '',
+    openIdFlag: false,
     sdkConfig: {
       app_id: '',
       timestamp: 0,
@@ -101,27 +102,40 @@ export const useUserInfo = defineStore('userInfo', {
           userRoleNames: userRoleNames.join('、'),
           userType: userInfo.userType,
           creditScore: userInfo.creditScore,
-          pgName: projectGroupRes.pgName
+          pgName: projectGroupRes ? projectGroupRes.pgName : '',
+          projectGroup: res?.data?.projectGroupRes
         }
         resolve(userInfos)
       })
     },
     async setOpenId(code: string) {
+      if(this.openIdFlag || this.openId) return
+      const local = localStorage.getItem('openId')
+      if(local) {
+        this.openId = local
+        return
+      }
+      this.openIdFlag = true
       const [err, res]: ToResponse = await to(sysApi.getOpenId({ code }))
-      if (err || res?.data?.code != '200') return
-      this.openId = res?.data?.data?.openid || ''
+      if (err) {
+        this.openIdFlag = false 
+        return
+      }
+      this.openId = res?.data?.unionid || ''
+      localStorage.setItem('openId', this.openId)
+      this.openIdFlag = false 
     },
     async getSdkConfig() {
       const [err, res]: ToResponse = await to(sysApi.getSDKTicket({ url: window.location.href }))
       if (err) return
       this.sdkConfig = res?.data
       wx.config({
-        debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
+        debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
         appId: this.sdkConfig.app_id, // 必填,公众号的唯一标识
         timestamp: this.sdkConfig.timestamp, // 必填,生成签名的时间戳
         nonceStr: this.sdkConfig.nonce_str, // 必填,生成签名的随机串
         signature: this.sdkConfig.signature, // 必填,签名
-        jsApiList: ['scanQRCode'] // 必填,需要使用的JS接口列表
+        jsApiList: ['scanQRCode', 'chooseImage'] // 必填,需要使用的JS接口列表
       })
       // wx.ready(function () {
       //   showDialog({

+ 5 - 0
src/theme/index.scss

@@ -25,6 +25,11 @@ body,
     overflow-y: auto;
     background-color: #f7f8fa;
   }
+  .entry-container {
+    height: calc(100vh - 48px);
+    overflow-y: auto;
+    background-color: #f7f8fa;
+  }
 }
 
 h1,

+ 1 - 0
src/types/index.d.ts

@@ -43,6 +43,7 @@ declare interface UserInfos<T = any> {
 declare interface UserInfosState {
   userInfos: UserInfos
   openId: string
+  openIdFlag: boolean
   sdkConfig: {
     app_id: string
     timestamp: number

+ 0 - 222
src/utils/micro_request.js

@@ -1,222 +0,0 @@
-/*
- * @Author: wanglj wanglijie@dashoo.cn
- * @Date: 2025-03-13 09:07:55
- * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-18 17:31:12
- * @FilePath: \labsop-h5\src\utils\micro_request.js
- * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
- */
-/*
- * @Date: 2021-11-27 15:27:19
- * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-12 16:31:09
- * @FilePath: \labsop_backup\frontend\packages\base\src\utils\micro_request.js
- * @Description: file content
- */
-import axios from "axios";
-import { showNotify, showDialog } from 'vant';
-import { Local, Session } from '/@/utils/storage.ts';
-import errorCode from "/@/utils/errorCode.js";
-
-axios.defaults.headers["Content-Type"] = "application/json;charset=utf-8";
-
-const service = axios.create({
-  // axios中请求配置有baseURL选项,表示请求URL公共部分
-  baseURL: import.meta.env.VITE_API_URL,
-  // 超时
-  timeout: 60000
-});
-
-// request拦截器
-service.interceptors.request.use(
-  config => {
-    config.headers["Tenant"] = import.meta.env.VITE_TENANT;
-    // 是否需要设置 token
-    const isToken = (config.headers || {}).isToken === false;
-    if (Local.get('token') && !isToken) {
-      config.headers["Authorization"] = "Bearer " + Local.get('token'); // 让每个请求携带自定义token 请根据实际情况自行修改
-    }
-    return config;
-  },
-  error => {
-    console.log(error);
-    Promise.reject(error);
-  }
-);
-
-// 响应拦截器
-service.interceptors.response.use(
-  res => {
-    if (res.request.responseType == "blob") {
-      // 判断是否是文件流
-      let fileReader = new FileReader();
-      fileReader.readAsText(res.data);
-      fileReader.onload = function() {
-        try {
-          let jsonData = JSON.parse(this.result); // 解析成功说明是普通对象数据,后端返回的是文件上传的错误信息
-          res.data = jsonData;
-          processResponse(res);
-          return;
-        } catch (err) {
-          // 解析成对象失败,说明是文件流
-          downLoadBlobFile(res);
-          return;
-        }
-      };
-    }
-
-    // 常规响应处理
-    return processResponse(res);
-  },
-  error => {
-    console.log("err" + error);
-    showNotify({
-      message: error.message,
-      type: "danger",
-      duration: 5 * 1000
-    });
-    return Promise.reject(error);
-  }
-);
-
-service.postRequest = function postRequest(basePath, srvName, funcName, data) {
-  if (data == undefined) {
-    let nullParam = {
-      nullparam: 0
-    };
-    data = nullParam;
-  }
-
-  return service.request({
-    url: basePath,
-    method: "post",
-    headers: {
-      "Content-Type": "application/rpcx",
-      "X-RPCX-SerializeType": "1",
-      "X-RPCX-ServicePath": srvName,
-      "X-RPCX-ServiceMethod": funcName
-    },
-    data: data
-  });
-};
-// 发出请求并要求把客户端信息传输给后端(IP和User-Agent)
-service.postRequestWithClientInfo = function postRequest(
-  basePath,
-  srvName,
-  funcName,
-  data
-) {
-  if (data == undefined) {
-    let nullParam = { nullparam: 0 };
-    data = nullParam;
-  }
-  return service.request({
-    url: basePath,
-    method: "post",
-    headers: {
-      "Content-Type": "application/rpcx",
-      "X-RPCX-SerializeType": "1",
-      "X-RPCX-ServicePath": srvName,
-      "X-RPCX-ServiceMethod": funcName,
-      "X-RPCX-Meta": "need_clint_Info=1"
-    },
-    data: data
-  });
-};
-
-// Excel文件下载(服务端生成文件流)
-service.downloadExcel = function downloadExcel(
-  basePath,
-  srvName,
-  funcName,
-  data
-) {
-  if (data == undefined) {
-    let nullParam = {
-      nullparam: 0
-    };
-    data = nullParam;
-  }
-  var base_Path = "";
-  if (basePath == process.env.VUE_APP_FOSHAN_PATH) {
-    base_Path = process.env.VUE_APP_MicroSrvProxy_foshan_API + basePath;
-  } else if (basePath == process.env.VUE_APP_AdminPath) {
-    base_Path = process.env.VUE_APP_MicroSrvProxy_API + basePath;
-  }
-  service.request({
-    url: base_Path,
-    method: "post",
-    responseType: "blob",
-    headers: {
-      "Content-Type": "application/rpcx",
-      "X-RPCX-SerializeType": "1",
-      "X-RPCX-ServicePath": srvName,
-      "X-RPCX-ServiceMethod": funcName
-    },
-    data: data
-  });
-};
-
-function processResponse(res) {
-  // 未设置状态码则默认成功状态
-  const code = res.data.code || 200;
-  // 获取错误信息
-  const message = errorCode[code] || res.data.message || errorCode["default"];
-  if (code === 401) {
-    showDialog({
-      message: "登录状态已过期,请重新登录"
-    }).then(() => {
-      // / 清除缓存/token等
-      Session.clear();
-      Local.remove('token')
-      // 使用 reload 时,不需要调用 resetRoute() 重置路由
-      window.location.reload();
-    })
-  } else if (code === 500) {
-    showNotify({
-      message: message,
-      type: "danger"
-    });
-    return Promise.reject(new Error(message));
-  } else if (code !== 200) {
-    showNotify({ message: message, type: 'danger' });
-    return Promise.reject("error");
-  } else {
-    // if (res.data.msg) {
-    //   Message({
-    //     message: res.data.msg,
-    //     type: "success"
-    //   });
-    // }
-    return res.data;
-  }
-}
-
-function downLoadBlobFile(res, mimeType) {
-  if (mimeType == undefined) {
-    mimeType = "application/vnd.ms-excel";
-  }
-  const aLink = document.createElement("a");
-  var blob = new Blob([res.data], {
-    type: mimeType
-  });
-  // //从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名;
-  var patt = new RegExp("filename=([^;]+\\.[^\\.;]+);*");
-  var contentDisposition = decodeURI(
-    res.headers["content-disposition"] || res.headers["Content-Disposition"]
-  );
-  var result = patt.exec(contentDisposition);
-  var fileName = "data.xlsx";
-  if (result != undefined) {
-    fileName = result[1];
-    fileName = decodeURI(escape(fileName));
-    fileName = fileName.replace(/\"/g, "");
-  }
-  aLink.href = URL.createObjectURL(blob);
-  aLink.setAttribute("download", fileName); // 设置下载文件名称
-  document.body.appendChild(aLink);
-  aLink.click();
-  document.body.appendChild(aLink);
-}
-
-export default service;

+ 1 - 1
src/utils/micro_request.ts

@@ -156,7 +156,7 @@ function processResponse(res) {
       // / 清除缓存/token等
       Session.clear();
       Local.remove('token')
-      router.push('/login')
+      location.reload()
     })
   } else if (code === 500) {
     showNotify({

+ 5 - 3
src/utils/request.ts

@@ -2,13 +2,15 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-18 20:02:42
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-19 15:02:34
+ * @LastEditTime: 2025-03-21 10:00:02
  * @FilePath: \labsop_h5\src\utils\request.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 import axios, { AxiosInstance } from 'axios'
 import { showNotify, showDialog } from 'vant'
 import { Local, Session } from '/@/utils/storage'
+import { useRouter } from 'vue-router'
+const router = useRouter()
 
 // 配置新建一个 axios 实例
 const service: AxiosInstance = axios.create({
@@ -48,7 +50,7 @@ service.interceptors.response.use(
           Session.clear()
           Local.remove('token')
           // 使用 reload 时,不需要调用 resetRoute() 重置路由
-          window.location.reload()
+          location.reload()
         })
       } else if (res.code === 500) {
         showNotify({
@@ -56,7 +58,7 @@ service.interceptors.response.use(
           type: 'danger'
         })
         return Promise.reject(new Error(res.message))
-      } else if (res.code !== 200) {
+      } else {
         showNotify({ message: res.message, type: 'danger' })
         return Promise.reject('error')
       }

+ 299 - 0
src/view/entry/add.vue

@@ -0,0 +1,299 @@
+<!--
+ * @Author: wanglj wanglijie@dashoo.cn
+ * @Date: 2025-03-18 20:02:42
+ * @LastEditors: wanglj wanglijie@dashoo.cn
+ * @LastEditTime: 2025-03-21 09:46:32
+ * @FilePath: \labsop_h5\src\view\training\enroll.vue
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+-->
+<template>
+  <div class="app-container">
+    <van-form ref="formRef" @submit="onSubmit('20')" class="mt10" required="auto">
+      <!-- <h4 class="mb8 mt8">申请人课题组信息</h4>
+      <van-cell-group>
+        <van-field v-model="state.form.pgName" label="课题组" placeholder="课题组" readonly :rules="[{ required: true }]" />
+        <van-field v-model="state.form.mentorName" label="课题组负责人" placeholder="课题组负责人" readonly />
+        <van-field v-model="state.form.mentorDeptName" label="课题组负责人科室" placeholder="课题组负责人科室" readonly />
+        <van-field v-model="state.form.mentorPhone" label="课题组负责人电话" placeholder="课题组负责人电话" readonly />
+      </van-cell-group> -->
+      <h4 class="mb8 mt8">申请入室平台</h4>
+      <van-cell-group v-for="(item, index) in state.form.platformList" :key="item.id">
+        <van-field label="申请平台" placeholder="申请平台">
+          <template #input>
+            <van-checkbox v-model="item.isChecked" name="10">{{ item.platformName }}</van-checkbox>
+          </template>
+        </van-field>
+        <van-field v-model.number="item.platformTime" label="申请时长(月)" placeholder="申请时长(月)" type="number" :disabled="!item.isChecked">
+          <template #extra>个月</template>
+        </van-field>
+        <template v-if="item.platformType == '10'">
+          <van-field v-model="item.cellType" label="拟培养细胞种类" placeholder="拟培养细胞种类" :disabled="!item.isChecked" />
+          <van-field v-model="item.cellType" label="细胞房预约需求" placeholder="细胞房预约需求">
+            <template #input>
+              <van-radio-group v-model="item.cellSourceType" :disabled="!item.isChecked" direction="horizontal">
+                <van-radio v-for="o in item.platformResourceType" :name="o.dictValue">{{ o.dictLabel }}</van-radio>
+              </van-radio-group>
+            </template>
+          </van-field>
+        </template>
+        <template v-else-if="item.platformType == '20'">
+          <van-field v-model="item.platOtherNeed" label="其他需求" placeholder="其他需求" :disabled="!item.isChecked" />
+        </template>
+      </van-cell-group>
+      <h4 class="mb8 mt8">申请人信息</h4>
+      <van-cell-group>
+        <van-field v-model="state.form.memberName" label="申请人姓名" placeholder="申请人姓名" readonly :rules="[{ required: true }]" />
+        <van-field v-model="state.form.memberType" label="人员类型" placeholder="人员类型" readonly :rules="[{ required: true }]">
+          <template #input>{{ getDictLabel(userTypeList, state.form.memberType) }}</template>
+        </van-field>
+        <van-field v-model="state.form.memberPhone" label="申请人手机号" placeholder="申请人手机号" readonly />
+        <van-field v-model="state.form.deptName" label="申请人科室" placeholder="申请人科室" readonly />
+        <van-field v-model="state.form.pgName" label="课题组" placeholder="课题组" readonly :rules="[{ required: true }]" />
+      </van-cell-group>
+      <h4 class="mb8 mt8">安全承诺</h4>
+      <van-checkbox v-model="state.safePromise">本人承诺:如时候遵守实验室及个平台的各项规章制度,遵守在室的积分管理制度。</van-checkbox>
+      <van-checkbox v-model="state.safeRead" :disabled="!state.isRead">
+        我已完整阅读并同意
+        <a href="javascript:void(0);" @click.stop="onRead">《预约须知》</a>
+        的内容,承诺如时候遵守实验室及个平台的各项规章制度,遵守在室的积分管理制度。
+      </van-checkbox>
+      <div style="margin: 16px">
+        <van-button round block type="primary" native-type="submit"> 提交 </van-button>
+      </div>
+    </van-form>
+  </div>
+  <van-dialog v-model:show="noticeShow" :title="noticeInfo.noticeTitle" show-cancel-button>
+    <div class="ck-editor" v-html="noticeInfo.noticeContent"></div>
+  </van-dialog>
+</template>
+
+<script name="home" lang="ts" setup>
+  import to from 'await-to-js'
+  import { onMounted, reactive, ref } from 'vue'
+  import { useRouter, useRoute } from 'vue-router'
+  import { useDictApi } from '/@/api/system/dict'
+  import { formatDate } from '/@/utils/formatTime'
+  import { storeToRefs } from 'pinia'
+  import { useUserInfo } from '/@/stores/userInfo'
+  import { getDictLabel } from '/@/utils/other'
+  import { useTrainingApi } from '/@/api/training'
+  import { usePlatformApi } from '/@/api/platform/home'
+  import { usePlatformAppointApi } from '/@/api/platform/appoint'
+
+  import { showDialog, showNotify } from 'vant'
+  const platformAppointApi = usePlatformAppointApi()
+  const trainingApi = useTrainingApi()
+  const storesUseUserInfo = useUserInfo()
+  const { userInfos, openId } = storeToRefs(storesUseUserInfo)
+  const router = useRouter()
+  const route = useRoute()
+  const formRef = ref()
+  const showPicker = ref(false)
+  const dictApi = useDictApi()
+  const userTypeList = ref(<RowDicDataType[]>[])
+  const platformApi = usePlatformApi()
+  const platformList = ref()
+  const state = reactive({
+    safePromise: false,
+    safeRead: false,
+    isRead: false,
+    form: {
+      id: 0,
+      deptId: null,
+      deptName: '',
+      isTemporary: '10',
+      memberId: 0,
+      memberName: '',
+      memberPhone: '',
+      memberType: '',
+      mentorObj: null,
+      pgId: null,
+      pgName: '',
+      mentorId: null,
+      mentorName: '',
+      mentorDeptName: '',
+      mentorPhone: '',
+      platformId: null,
+      platformName: '',
+      platformTime: null,
+      platformType: '',
+      platformList: [] as any[],
+      isCellChecked: '20',
+      cellType: '',
+      cellSourceType: '',
+      isMolecularChecked: '20',
+      molecularTime: null,
+      platOtherNeed: ''
+    }
+  })
+  const noticeShow = ref(false)
+  const noticeInfo = reactive({
+    noticeTitle: '',
+    noticeContent: ''
+  })
+  const getDicts = async () => {
+    await Promise.all([dictApi.getDictDataByType('sys_user_type'), platformApi.getAllPlatformList({ noPage: true })]).then(([type, plat]) => {
+      userTypeList.value = type.data.values || []
+      const platList = plat?.data?.list || []
+      platformList.value = platList.map((item: any) => {
+        const options = JSON.parse(item.platformResourceType)
+        return {
+          isChecked: false,
+          platformId: item.id,
+          platformName: item.platformName,
+          platformType: item.platformType,
+          platformTime: null,
+          cellType: '',
+          cellSourceType: options?.length ? options[0].dictValue : '',
+          platOtherNeed: '',
+          platformResourceType: options,
+          platformDesc: item.platformDesc
+        }
+      })
+    })
+  }
+  const initForm = async () => {
+    state.form = {
+      id: 0,
+      deptId: null,
+      deptName: '',
+      isTemporary: '10',
+      memberId: 0,
+      memberName: '',
+      memberPhone: '',
+      memberType: '',
+      mentorObj: null,
+      pgId: null,
+      pgName: '',
+      mentorId: null,
+      mentorName: '',
+      mentorDeptName: '',
+      mentorPhone: '',
+      platformId: null,
+      platformName: '',
+      platformTime: null,
+      platformType: '',
+      platformList: [...platformList.value],
+      isCellChecked: '20',
+      cellType: '',
+      cellSourceType: '',
+      isMolecularChecked: '20',
+      molecularTime: null,
+      platOtherNeed: ''
+    }
+    state.form.memberId = userInfos.value.id
+    state.form.memberName = userInfos.value.nickName
+    state.form.memberType = userInfos.value.userType
+    state.form.memberPhone = userInfos.value.phone
+    state.form.deptId = userInfos.value.deptId
+    state.form.deptName = userInfos.value.deptName
+    const { projectGroup } = userInfos.value
+    if (projectGroup) {
+      state.form.pgId = projectGroup.id
+      state.form.pgName = projectGroup.pgName
+      state.form.mentorId = projectGroup.pgLeaderId
+      state.form.mentorName = projectGroup.pgLeaderName
+      state.form.mentorDeptName = projectGroup.pgOrgPaths
+      state.form.mentorPhone = projectGroup.pgLeaderContact
+    } else {
+      showNotify({
+        message: '当前用户未加入课题组,无法发起入室申请',
+        type: 'danger'
+      })
+    }
+  }
+  const onRead = () => {
+    noticeShow.value = true
+    noticeInfo.noticeTitle = '预约须知'
+    const arr = state.form.platformList.map((item: any) => {
+      return item.isChecked ? item.platformDesc : ''
+    })
+    if (arr.length) {
+      noticeInfo.noticeContent = arr.join('\n')
+      state.isRead = true
+    }
+  }
+  const onSubmit = async (type: string) => {
+    if (!state.safePromise || !state.safeRead) {
+      showNotify({
+        type: 'warning',
+        message: '请阅读并勾选安全承诺!'
+      })
+      return
+    }
+    const [errValid] = await to(formRef.value.validate())
+    if (errValid) return
+    const params = JSON.parse(JSON.stringify(state.form))
+    params.isTemporary = type
+    const arr = params.platformList.filter((item: any) => item.isChecked)
+    params.platformList = arr.map((item: any) => {
+      return {
+        ...item,
+        isChecked: '10'
+      }
+    })
+    if (!params.platformList.length) {
+      showNotify({
+        type: 'warning',
+        message: '请选择平台'
+      })
+      return
+    }
+    for (const item of params.platformList) {
+      if (!item.platformTime) {
+        showNotify({
+          type: 'warning',
+          message: '请选择平台预约时间'
+        })
+        return
+      }
+    }
+    const [err]: ToResponse = await to(platformAppointApi.create(params))
+    if (err) return
+    showNotify({
+      type: 'success',
+      message: '入室申请创建成功'
+    })
+    router.push('/entry')
+  }
+  onMounted(async () => {
+    await getDicts()
+    initForm()
+  })
+</script>
+
+<style lang="scss" scoped>
+  .app-container {
+    header {
+      padding: 14px;
+      background-color: #f9ffff;
+      border-radius: 4px;
+      margin-top: 10px;
+    }
+    h4 {
+      margin: 10px 0;
+      height: 20px;
+      line-height: 20px;
+      &::before {
+        display: inline-block;
+        content: '';
+        background-color: #3c78e3;
+        width: 4px;
+        height: 20px;
+        margin-right: 4px;
+        vertical-align: top;
+      }
+    }
+    :deep(.flow-cell) {
+      margin-left: 22px;
+      display: flex;
+      justify-content: space-between;
+      color: #333;
+    }
+  }
+  .ck-editor {
+    height: calc(100vh - 100px);
+    overflow-y: auto;
+    padding: 0 10px;
+  }
+</style>

+ 157 - 0
src/view/entry/index.vue

@@ -0,0 +1,157 @@
+<!--
+ * @Author: wanglj wanglijie@dashoo.cn
+ * @Date: 2025-03-11 18:02:10
+ * @LastEditors: wanglj wanglijie@dashoo.cn
+ * @LastEditTime: 2025-03-20 17:51:51
+ * @FilePath: \vant-demo-master\vant\vue3-ts\src\view\login\index.vue
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+-->
+<template>
+  <div class="entry-container">
+    <van-tabs v-model:active="state.queryParams.approveStatus" @change="changeType">
+      <van-tab title="审批中" name="10,20"></van-tab>
+      <van-tab title="已通过" name="30"></van-tab>
+      <van-tab title="全部申请" name="0"></van-tab>
+    </van-tabs>
+    <van-list v-model:loading="state.loading" :finished="state.finished" finished-text="没有更多了" @load="onLoad">
+      <van-cell v-for="item in state.list" :key="item" @click="toDetail(item.id)">
+        <template #default>
+          <div class="list">
+            <header class="flex justify-between">
+              <strong class="title">{{ `${item.memberName}的${item.platformName}入室申请` }}</strong>
+              <van-tag v-if="item.approveStatus == 10" type="warning">待提交</van-tag>
+              <van-tag v-else-if="item.approveStatus == 20" type="primary">审批中</van-tag>
+              <van-tag v-else-if="item.approveStatus == 30" type="success">审批通过</van-tag>
+              <van-tag v-else-if="item.approveStatus == 40" type="danger">审批退回</van-tag>
+            </header>
+            <p class="inst-title">
+              <span>课题名称</span>
+              <span class="title ml8">{{ item.pgName }}</span>
+            </p>
+            <p class="inst-title">
+              <span>申请平台</span>
+              <span class="title ml8">{{ item.platformName }}</span>
+            </p>
+            <p class="inst-title">
+              <span>申请时长</span>
+              <span class="title ml8">{{ item.platformTime }}个月</span>
+            </p>
+            <p class="inst-title">
+              <span>入室周期</span>
+              <span class="title ml8">{{ item.appointEndDate ? `${formatDate(new Date(item.appointEndDate), 'YYYY-mm-dd')}~${formatDate(new Date(item.appointEndDate), 'YYYY-mm-dd')}` : '-' }}</span>
+            </p>
+            <footer class="flex justify-between mt4">
+              <span class="title">{{ item.memberName }}</span>
+              <span class="time">{{ formatDate(new Date(item.createdTime), 'YYYY-mm-dd') }}</span>
+            </footer>
+          </div>
+        </template>
+      </van-cell>
+    </van-list>
+    <van-floating-bubble v-model:offset="offset" icon="plus" @click="onClick" axis="y" />
+  </div>
+</template>
+
+<script name="home" lang="ts" setup>
+  import to from 'await-to-js'
+  import { formatDate } from '/@/utils/formatTime'
+  import { onMounted, reactive, ref } from 'vue'
+  import { useRouter, useRoute } from 'vue-router'
+  import { usePlatformAppointApi } from '/@/api/platform/appoint'
+  const platformAppointApi = usePlatformAppointApi()
+  const router = useRouter()
+  const route = useRoute()
+  const offset = ref({ x: -80, y: 400 });
+  const state = reactive({
+    queryParams: {
+      approveStatus: '10,20',
+      pageNum: 1,
+      pageSize: 10
+    },
+    finished: false,
+    loading: true,
+    list: [] as any[]
+  })
+  const changeType = (name: string) => {
+    state.queryParams.pageNum = 1
+    onLoad()
+  }
+  const onLoad = async () => {
+    const [err, res]: ToResponse = await to(platformAppointApi.getList(state.queryParams))
+    if (err) return
+    const list = res?.data?.list || []
+    for (const item of list) {
+      state.list.push(item)
+    }
+    state.loading = false
+    state.queryParams.pageNum++
+    if (state.list.length < state.queryParams.pageSize) {
+      state.finished = true
+    }
+  }
+  const toDetail = (id: number) => {
+    router.push({
+      path: '/todo/approval-detail',
+      query: {
+        id
+      }
+    })
+  }
+  const onClick = () => {
+    router.push('/entry/add')
+  }
+  onMounted(() => {
+    const type = route.query.type
+    if (type) {
+      state.queryParams.approveStatus = type as string
+    }
+    onLoad()
+  })
+</script>
+
+<style lang="scss" scoped>
+  .entry-container {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    .van-list {
+      height: calc(100% - 62px);
+      padding: 10px;
+      border-radius: 4px;
+      flex: 1;
+      overflow-y: auto;
+      .van-cell {
+        background-color: #fff;
+        + .van-cell {
+          margin-top: 10px;
+        }
+        header,
+        footer {
+          color: #333;
+        }
+        .title {
+          flex: 1;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          text-align: left;
+        }
+        .inst-title {
+          color: #333;
+          text-align: left;
+          flex: 1;
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+          margin-top: 4px;
+          span:first-child {
+            color: rgb(120, 120, 120);
+          }
+        }
+        .time {
+          color: #f69a4d;
+        }
+      }
+    }
+  }
+</style>

+ 30 - 15
src/view/exam/cover.vue

@@ -2,7 +2,7 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-11 18:02:10
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-18 14:56:23
+ * @LastEditTime: 2025-03-19 19:44:54
  * @FilePath: \vant-demo-master\vant\vue3-ts\src\view\login\index.vue
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 -->
@@ -24,13 +24,21 @@
 <script name="home" lang="ts" setup>
   import to from 'await-to-js'
   import { computed, onMounted, reactive, ref } from 'vue'
-  import { useRouter } from 'vue-router'
+  import { useRouter, useRoute } from 'vue-router'
   import { formatDate } from '/@/utils/formatTime'
   import { useDictApi } from '/@/api/system/dict'
   import { showConfirmDialog, showNotify } from 'vant'
   import { useTrainingApi } from '/@/api/training/index'
+  import { storeToRefs } from 'pinia'
+  import { useUserInfo } from '/@/stores/userInfo'
+  import { Local } from '/@/utils/storage'
+  import { useLoginApi } from '/@/api/login/index'
   const router = useRouter()
+  const route = useRoute()
   const trainingApi = useTrainingApi()
+  const storesUseUserInfo = useUserInfo()
+  const { userInfos, openId } = storeToRefs(storesUseUserInfo)
+  const loginApi = useLoginApi()
   const state = reactive({
     form: {
       name: '',
@@ -51,7 +59,7 @@
     router.go(-1)
   }
   const initExam = async () => {
-    const [err, res]: ToResponse = await to(trainingApi.getMineExamResultInfo({ id: 233 }))
+    const [err, res]: ToResponse = await to(trainingApi.getMineExamResultInfo({ id: route.query.state }))
     if (err) return
     state.form = res?.data
   }
@@ -61,27 +69,34 @@
         message: '该考试未配置试卷,请联系管理员配置试卷',
         type: 'warning'
       })
-  	} else {
-  		// 进入试卷获取试卷的前置条件
-  		checkEnterTestPaper();
-  	}
+    } else {
+      // 进入试卷获取试卷的前置条件
+      checkEnterTestPaper()
+    }
   }
   const checkEnterTestPaper = () => {
-  	trainingApi.enterExam({ examId: state.form.examId }).then((res: any) => {
-  		if (res.code == 200) {
-  			localStorage.setItem('currentStartTime', res.data.currentStartTime);
-  			localStorage.setItem('duration', state.form.duration + '');
+    trainingApi.enterExam({ examId: state.form.examId }).then((res: any) => {
+      if (res.code == 200) {
+        localStorage.setItem('currentStartTime', res.data.currentStartTime)
+        localStorage.setItem('duration', state.form.duration + '')
         router.push({
           path: '/exam',
           query: {
             answerId: res.data.answerId,
-            epId: state.form.epId,
+            epId: state.form.epId
           }
         })
-  		}
-  	});
+      }
+    })
+  }
+  const openIdLogin = async () => {
+    const [err, res]: ToResponse = await to(loginApi.weChatLoginOpenId({ openId: openId.value }))
+    if (err) return
+    Local.set('token', res?.data.token)
   }
-  onMounted(() => {
+  onMounted(async () => {
+    await storesUseUserInfo.setOpenId(route.query.code as string)
+    await openIdLogin()
     initExam()
   })
 </script>

+ 77 - 63
src/view/home/index.vue

@@ -2,7 +2,7 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-11 18:02:10
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-19 11:51:27
+ * @LastEditTime: 2025-03-21 16:47:22
  * @FilePath: \vant-demo-master\vant\vue3-ts\src\view\login\index.vue
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 -->
@@ -24,40 +24,51 @@
       <h4>常用功能</h4>
       <ul class="nav">
         <li @click="onRouterPush('/instr-list')">
-          <img src="../../assets/img/home-equip.png" alt="" />
+          <img src="../../assets/img/仪器预约.png" alt="" />
           <p>仪器预约</p>
         </li>
-        <li>
-          <img src="../../assets/img/home-in.png" alt="" />
-          <p>入室申请</p>
+        <li @click="onRouterPush('/entry')">
+          <img src="../../assets/img/入室申请.png" alt="" />
+          <p>入室管理</p>
         </li>
-        <li>
-          <img src="../../assets/img/home-appoint.png" alt="" />
+        <!-- <li>
+          <img src="../../assets/img/入室预约.png" alt="" />
           <p>入室预约</p>
-        </li>
+        </li> -->
         <li>
-          <img src="../../assets/img/home-tech.png" alt="" />
+          <img src="../../assets/img/技术委托.png" alt="" />
           <p>技术委托</p>
         </li>
         <li @click="onRouterPush('/training')">
-          <img src="../../assets/img/home-training.png" alt="" />
-          <p>培训报名</p>
+          <img src="../../assets/img/培训考试.png" alt="" />
+          <p>培训考试</p>
         </li>
         <li @click="onRouterPush('/exam-cover')">
-          <img src="../../assets/img/home-animal.png" alt="" />
+          <img src="../../assets/img/动物笼位.png" alt="" />
           <p>动物笼位</p>
         </li>
         <li>
-          <img src="../../assets/img/home-file.png" alt="" />
-          <p>资料下载</p>
+          <img src="../../assets/img/试剂耗材.png" alt="" />
+          <p>试剂耗材</p>
         </li>
         <li>
-          <img src="../../assets/img/home-fund.png" alt="" />
-          <p>财务中心</p>
+          <img src="../../assets/img/更多应用.png" alt="" />
+          <p>更多应用</p>
         </li>
       </ul>
     </div>
-    <div class="card">
+    <div class="swipe-con flex justify-between mt10">
+      <van-swipe class="my-swipe" :autoplay="5000" :show-indicators="false" vertical height="30">
+        <van-swipe-item v-for="item in noticeList" :key="item.id" @click="onRouterPush('/notice/detail', { id: item.id })">
+          <div class="flex justify-between">
+            <span>{{ `【${item.noticeType === '10' ? '公告' : '通知'}】${item.noticeTitle}` }}</span>
+            <span class="time">{{ formatDate(new Date(item.noticeTime), 'YYYY-mm-dd') }}</span>
+          </div>
+        </van-swipe-item>
+      </van-swipe>
+      <a href="javascript:void(0);" @click="onRouterPush('/notice',)">更多</a>
+    </div>
+    <!-- <div class="card">
       <h4>
         审批流程
         <span class="link" @click="onRouterPush('/todo', { type: 'approval' })">全部审批>></span>
@@ -74,21 +85,7 @@
           </div>
         </li>
       </ul>
-    </div>
-    <div class="card">
-      <h4>
-        通知公告
-        <span class="link" @click="onRouterPush('/todo', { type: 'notice' })">全部通知>></span>
-      </h4>
-      <ul class="approval">
-        <li v-for="item in noticeList" :key="item.id" @click="onRouterPush('/todo/notice-detail', { id: item.id })">
-          <div class="flex justify-between">
-            <van-text-ellipsis :content="`【${item.noticeType === '10' ? '公告' : '通知'}】${item.noticeTitle}`" />
-            <span class="time">{{ formatDate(new Date(item.noticeTime), 'YYYY-mm-dd') }}</span>
-          </div>
-        </li>
-      </ul>
-    </div>
+    </div> -->
     <div class="card mb20">
       <h4>
         培训通知
@@ -96,10 +93,11 @@
       </h4>
       <ul class="approval">
         <li v-for="item in trainingList" :key="item.id" @click="onRouterPush('/training/enroll', { state: item.id })">
-          <div class="flex justify-between">
-            <van-text-ellipsis :content="`${item.title}`" />
-            <span class="time">{{ formatDate(new Date(item.startTime), 'YYYY-mm-dd') }}</span>
-          </div>
+          <van-text-ellipsis :content="`${item.title}`" />
+          <footer class="flex justify-between">
+            <span>发布人:{{ item.createdName }}</span>
+            <span>{{ formatDate(new Date(item.startTime), 'YYYY-mm-dd') }}</span>
+          </footer>
         </li>
       </ul>
     </div>
@@ -156,7 +154,8 @@
     })
   }
   const getApprovalList = async () => {
-    const [err, res]: ToResponse = await to(executionApi.getOwApproveList({ platformId: 1000103, noPage: true }))
+    // todo 平台id参数需要删除
+    const [err, res]: ToResponse = await to(executionApi.getOwApproveList({ platformId: 1000103, pageNum: 1, pageSize: 3 }))
     if (err) return
     approvalList.value = res?.data?.list || []
   }
@@ -166,7 +165,7 @@
     noticeList.value = res?.data?.list || []
   }
   const getTrainingList = async () => {
-    const [err, res]: ToResponse = await to(trainingApi.getList({ noPage: true }))
+    const [err, res]: ToResponse = await to(trainingApi.getList({ pageNum: 1, pageSize: 3 }))
     if (err) return
     trainingList.value = res?.data?.list || []
   }
@@ -235,6 +234,7 @@
       }
     }
     .card {
+      min-height: 60px;
       margin-top: 10px;
       border-radius: 4px;
       background-color: #fff;
@@ -265,6 +265,7 @@
         display: flex;
         margin: 10px 0;
         flex-wrap: wrap;
+        font-weight: normal;
         li {
           flex: 0 0 25%;
           display: flex;
@@ -275,45 +276,58 @@
             margin-top: 10px;
           }
           img {
-            padding: 12px;
-            height: 24px;
-            width: 24px;
-            background-color: #1c9bfd;
-            border-radius: 50%;
-            overflow: visible;
+            height: 48px;
+            width: 48px;
             margin-bottom: 4px;
           }
-          &:nth-child(2) img {
-            background-color: #ff751b;
-          }
-          &:nth-child(3) img {
-            background-color: #0dda83;
-          }
-          &:nth-child(4) img {
-            background-color: #595cac;
-          }
-          &:nth-child(5) img {
-            background-color: #0dda83;
-          }
-          &:nth-child(6) img {
-            background-color: #595cac;
-          }
-          &:nth-child(8) img {
-            background-color: #ff751b;
-          }
         }
       }
       .approval {
         li {
           padding: 10px;
-          background-color: #f9ffff;
+          background-color: #f9f9f9;
           margin-top: 8px;
           border-bottom: 1px solid #ecf5fa;
+          .van-text-ellipsis {
+            font-weight: bold;
+            font-size: 16px;
+          }
+          footer {
+            color: #969799;
+          }
           a {
             color: #3c78e3;
           }
         }
       }
     }
+    .swipe-con {
+      display: flex;
+      .my-swipe {
+        flex: 1;
+        height: 30px !important;
+        line-height: 30px !important;
+        :deep(.flex) {
+          height: 30px;
+          overflow: hidden;
+          span {
+            display: inline-block;
+            height: 30px;
+            line-height: 30px;
+          }
+          span:first-child {
+            flex: 1;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+          }
+        }
+      }
+      > a {
+        flex: 0 0 28px;
+        margin-left: 4px;
+        color: #3c78e3;
+      }
+    }
   }
 </style>

+ 3 - 3
src/view/instr/appointList/index.vue

@@ -1,8 +1,8 @@
 <!--
  * @Author: liuzhenlin 461480418@qq.ocm
  * @Date: 2023-01-12 11:57:48
- * @LastEditors: liuzhenlin
- * @LastEditTime: 2023-01-16 13:56:57
+ * @LastEditors: wanglj wanglijie@dashoo.cn
+ * @LastEditTime: 2025-03-21 17:11:05
  * @Description: file content
  * @FilePath: \opms\pages\schedule\index.vue
 -->
@@ -24,7 +24,7 @@
     <!-- </van-pull-refresh> -->
     <van-tabbar route :placeholder="true">
       <van-tabbar-item replace to="/instr-list" icon="printer">仪器列表</van-tabbar-item>
-      <van-tabbar-item replace to="/instr-appoint-record" icon="label">预约详情</van-tabbar-item>
+      <van-tabbar-item replace to="/instr-appoint-record" icon="label">我的预约</van-tabbar-item>
     </van-tabbar>
   </div>
 </template>

+ 18 - 35
src/view/instr/list.vue

@@ -26,49 +26,24 @@
       <div class="inst-wrap">
         <van-list v-model:loading="state.loading" :finished="state.finished" finished-text="没有更多了" @load="onLoad">
           <div class="inst-item mb40" v-for="(v, index) in state.instList" :key="index">
-            <div class="header flex_between">
-              <div class="flex">
-                <div class="flex">
-                  <img @click="handleFollowInst(v)" v-if="v.following" class="follow-icon mr10" src="../../assets/img/follow.png" />
-                  <img @click="handleFollowInst(v)" v-else class="follow-icon mr10" src="../../assets/img/unfollow.png" />
-                  <div class="primary-color instr-status-btn">{{ v.instName }}</div>
-                </div>
-              </div>
-              <div style="width: 50px">
-                <div class="success-color instr-status-btn" v-if="v.instStatus == '10'">正常</div>
-                <div class="warning-color instr-status-btn" v-if="v.instStatus == '20'">故障</div>
-                <div class="danger-color instr-status-btn" v-if="v.instStatus == '30'">报废</div>
-              </div>
-            </div>
-            <div class="inst-info mt10 mb10">
+            <div class="inst-info mt4 mb4">
               <div class="i-left" @click="openDetail(v)">
-                <img :showLoading="true" :src="v.instPicture" width="122px" height="83px" />
+                <img :showLoading="true" :src="v.instPicture" width="100px" height="80px" />
               </div>
               <div class="i-right ml10">
                 <div @click="openDetail(v)">
-                  <div class="flex flex-top mb10 ml2">
-                    <div class="detailTxt">{{ v.instCode }}</div>
+                  <div class="flex flex-top mb4 ml2">
+                    <div class="detailTxt name">{{ v.instCode }}</div>
                   </div>
-                  <div class="flex flex-top mb10">
+                  <div class="flex flex-top mb4 mt-auto">
                     <img class="i-r-icon" src="../../assets/img/user.png" v-if="v.instHeadName" />
                     <div class="detailTxt">{{ v.instHeadName }}</div>
                   </div>
-                  <div class="flex flex-top mb10">
+                  <div class="flex flex-top">
                     <img class="i-r-icon" src="../../assets/img/address.png" v-if="v.placeAddress" />
                     <div class="detailTxt">{{ v.placeAddress + setLaboratoryName(v.laboratoryName) }}</div>
                   </div>
                 </div>
-                <div class="flex flex-between" style="padding-right: 10px">
-                  <van-button
-                    type="primary"
-                    style="width: 70px; height: 31px; margin: 0"
-                    v-if="v.instStatus == '10' && v.isAppointment == '10'"
-                    @click="openAppoint(v)"
-                    size="small"
-                  >
-                    预约
-                  </van-button>
-                </div>
               </div>
             </div>
           </div>
@@ -76,8 +51,9 @@
       </div>
     </div>
     <van-tabbar route :placeholder="true">
-      <van-tabbar-item replace to="/instr-list" icon="printer">仪器列表</van-tabbar-item>
-      <van-tabbar-item replace to="/instr-appoint-record" icon="label">预约详情</van-tabbar-item>
+      <van-tabbar-item replace to="/instr-list" icon="printer">关注仪器</van-tabbar-item>
+      <van-tabbar-item replace to="/instr-list" icon="printer">全部仪器</van-tabbar-item>
+      <van-tabbar-item replace to="/instr-appoint-record" icon="label">我的预约</van-tabbar-item>
     </van-tabbar>
     <!-- 仪器详情 -->
     <van-popup v-model:show="state.popupShow" round :closeable="true">
@@ -323,7 +299,7 @@
         // padding: 10px 10px 0 10px;
       }
       .inst-item {
-        padding: 10px;
+        padding: 4px;
         box-shadow: -2px 0px 9px rgba(0, 0, 0, 0.12);
         margin-bottom: 14px;
         border-radius: 6px;
@@ -437,8 +413,12 @@
     text-align: center;
   }
   .detailTxt {
-    font-size: 14px;
+    font-size: 12px;
     color: #333333;
+    &.name {
+      font-weight: bold;
+      font-size: 16px;
+    }
   }
   .labelTit {
     margin-bottom: 10px;
@@ -459,4 +439,7 @@
     font-weight: bold;
     font-size: 14px;
   }
+  .mt-auto {
+    margin-top: auto;
+  }
 </style>

+ 64 - 10
src/view/login/index.vue

@@ -2,15 +2,17 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-11 18:02:10
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-18 17:05:19
+ * @LastEditTime: 2025-03-21 10:12:40
  * @FilePath: \vant-demo-master\vant\vue3-ts\src\view\login\index.vue
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 -->
 <template>
   <div class="login-container">
     <header>
-      <img width="300" src="../../assets/img/logo.png" />
-      <h4>智慧实验室移动工作台</h4>
+      <div class="logo mt20 mb20">
+        <img width="100" src="../../assets/img/logo-login.png" />
+        <h4>临床医学公共实验中心</h4>
+      </div>
       <van-field v-model="state.form.userName" placeholder="请输入用户名" />
       <van-field v-model="state.form.password" type="password" placeholder="请输入密码" class="mt10" />
       <!-- <van-row justify="space-between">
@@ -19,6 +21,7 @@
       </van-row> -->
       <van-button type="primary" round class="mt10" @click="onSignIn">登录</van-button>
       <van-button round class="mt10" @click="onRegister">注册</van-button>
+      <!-- <van-button round class="mt10" @click="blueTooth">蓝牙</van-button> -->
     </header>
     <footer>
       <img width="124" src="../../assets/img/slogan.png" />
@@ -35,6 +38,7 @@
   import { useRouter, useRoute } from 'vue-router'
   import { storeToRefs } from 'pinia'
   import { useUserInfo } from '/@/stores/userInfo'
+  import { showDialog } from 'vant'
   const router = useRouter()
   const route = useRoute()
   const sm3 = crypto.sm3
@@ -54,9 +58,13 @@
     state.loading.signIn = true
     const params = JSON.parse(JSON.stringify(state.form))
     params.password = sm3(params.password)
+    params.openId = openId.value
     // params.password = (params.password)
     // sm3
-    const [err, res]: ToResponse = await to(loginApi.signIn(params))
+    console.log(openId.value, 'openIdddddddddddd');
+    
+    const post = params.openId ? loginApi.weChatLogin : loginApi.signIn
+    const [err, res]: ToResponse = await to(post(params))
     if (err) {
       state.loading.signIn = false
       return
@@ -65,16 +73,57 @@
     Local.set('token', res?.data.token)
     router.push('/home')
   }
+  const openIdLogin = async () => {
+    const [err, res]: ToResponse = await to(loginApi.weChatLoginOpenId({ openId: openId.value }))
+    if (err) return
+    Local.set('token', res?.data.token)
+    router.push('/home')
+  }
   const onRegister = () => {
     router.push('/register')
   }
-  onMounted(async () => {
-    // 微信授权码获取openId
-    if (route.query.code) {
-      const code = route.query.code
-      if (typeof code === 'string') {
-        await storesUseUserInfo.setOpenId(code)
+  const blueTooth = async () => {
+    try {
+      // 请求蓝牙设备
+      // @ts-ignore
+      if (!navigator?.bluetooth?.requestDevice) {
+        showDialog({
+          message: '当前浏览器不支持蓝牙功能,请升级浏览器版本或更换浏览器。',
+          confirmButtonText: '确定'
+        })
       }
+      // @ts-ignore
+      const device = await navigator.bluetooth.requestDevice({
+        filters: [{ services: ['generic_access'] }],
+        optionalServices: [],
+        acceptAllDevices: false
+      })
+      // 连接到设备
+      const server = await device.gatt.connect()
+
+      // 获取服务
+      const service = await server.getPrimaryService('generic_access')
+
+      // 获取特征
+      const characteristic = await service.getCharacteristic('device_name')
+
+      // 读取特征值
+      const value = await characteristic.readValue()
+
+      // 将特征值转换为字符串
+      const decoder = new TextDecoder('utf-8')
+      const deviceName = decoder.decode(value)
+
+      console.log('Device Name:', deviceName)
+    } catch (error) {
+      console.error('Error:', error)
+    }
+  }
+  onMounted(async () => {
+    const code: string = route.query.code ? route.query.code.toString() : ''
+    if(code) {
+      await storesUseUserInfo.setOpenId(code)
+      openIdLogin()
     }
   })
 </script>
@@ -87,6 +136,11 @@
     margin: 0 auto;
     padding: 40px 0;
     flex-direction: column;
+    .logo {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+    }
     header {
       display: flex;
       flex-direction: column;

+ 6 - 4
src/view/todo/notice-detail.vue → src/view/notice/detail.vue

@@ -2,14 +2,14 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-11 18:02:10
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-19 15:10:14
+ * @LastEditTime: 2025-03-20 16:08:36
  * @FilePath: \vant-demo-master\vant\vue3-ts\src\view\login\index.vue
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 -->
 <template>
   <div class="app-container">
     <header>
-      <h4>【{{ state.form.noticeType }}】{{ state.form.noticeTitle }}</h4>
+      <h4>【{{ state.form.noticeType === '10' ? '公告' : '通知' }}】{{ state.form.noticeTitle }}</h4>
     </header>
     <h4>通知内容</h4>
     <div class="content" v-html="state.form.noticeContent"></div>
@@ -61,9 +61,11 @@
       background-color: #f9ffff;
       border-radius: 4px;
       margin-top: 10px;
+      h4 {
+        word-break: break-all;
+      }
     }
-    h4 {
-      height: 18px;
+    > h4 {
       line-height: 18px;
       display: flex;
       margin: 10px 0;

+ 122 - 0
src/view/notice/index.vue

@@ -0,0 +1,122 @@
+<!--
+ * @Author: wanglj wanglijie@dashoo.cn
+ * @Date: 2025-03-11 18:02:10
+ * @LastEditors: wanglj wanglijie@dashoo.cn
+ * @LastEditTime: 2025-03-21 10:48:52
+ * @FilePath: \vant-demo-master\vant\vue3-ts\src\view\login\index.vue
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+-->
+<template>
+  <div class="app-container">
+    <van-list v-model:loading="state.loading" :finished="state.finished" finished-text="没有更多了" @load="onLoad">
+      <van-cell v-for="item in state.list" :key="item" @click="toDetail(item.id)">
+        <template #default>
+          <h4 class="inst-title">{{ `${item.noticeType === '10' ? '公告' : '通知'}:${item.noticeTitle}` }}</h4>
+          <div class="flex justify-between">
+            <span>发布人:{{ item.createdName }}</span>
+            <span>{{ formatDate(new Date(item.noticeTime), 'YYYY-mm-dd') }}</span>
+          </div>
+        </template>
+      </van-cell>
+    </van-list>
+  </div>
+</template>
+
+<script name="home" lang="ts" setup>
+  import to from 'await-to-js'
+  import { formatDate } from '/@/utils/formatTime'
+  import { onMounted, reactive, ref } from 'vue'
+  import { useRouter, useRoute } from 'vue-router'
+  import { useExecutionApi } from '/@/api/execution'
+  import { useNewsApi } from '/@/api/system/news'
+  const executionApi = useExecutionApi()
+  const newsApi = useNewsApi()
+  const router = useRouter()
+  const route = useRoute()
+  const state = reactive({
+    queryParams: {
+      type: 'notice',
+      pageNum: 1,
+      pageSize: 10
+    },
+    finished: false,
+    loading: true,
+    list: [] as any[]
+  })
+  const onClickRight = () => {
+    router.go(-1)
+  }
+  const changeType = (str: string) => {
+    state.queryParams.type = str
+    state.queryParams.pageNum = 1
+    onLoad()
+  }
+  const onLoad = async () => {
+    const post = state.queryParams.type == 'approval' ? executionApi.getOwApproveList : newsApi.getNoticeList
+    const [err, res]: ToResponse = await to(post({ platformId: 1000103, noPage: true }))
+    if (err) return
+    const list = res?.data?.list || []
+    for (const item of list) {
+      state.list.push(item)
+    }
+    state.loading = false
+    state.queryParams.pageNum++
+    if (state.list.length < state.queryParams.pageSize) {
+      state.finished = true
+    }
+  }
+  const toDetail = (id: number) => {
+    router.push({
+      path: '/notice/detail',
+      query: {
+        id
+      }
+    })
+  }
+  onMounted(() => {
+    const type = route.query.type
+    if (type) {
+      state.queryParams.type = type as string
+    }
+    onLoad()
+  })
+</script>
+
+<style lang="scss" scoped>
+  .app-container {
+    .van-row .van-button {
+      flex: 1;
+      & + .van-button {
+        margin-left: 10px;
+      }
+    }
+    .van-list {
+      height: calc(100% - 20px);
+      border-radius: 4px;
+      .van-cell {
+        background-color: #fff;
+        margin-top: 10px;
+        header,
+        footer {
+          color: #333;
+        }
+        .title {
+          font-weight: bold;
+        }
+        .inst-title {
+          color: #333;
+          text-align: left;
+          flex: 1;
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+          font-size: 16px;
+          margin-bottom: 4px;
+        }
+        .time {
+          color: #f69a4d;
+        }
+      }
+    }
+  }
+</style>

+ 7 - 8
src/view/register/index.vue

@@ -2,7 +2,7 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-11 18:02:10
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-17 14:10:34
+ * @LastEditTime: 2025-03-19 18:23:43
  * @FilePath: \vant-demo-master\vant\vue3-ts\src\view\login\index.vue
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 -->
@@ -37,14 +37,14 @@
           <van-field label="性别" :rules="[{ required: false }]">
             <template #input>
               <van-radio-group v-model="state.form.sex" label="性别" placeholder="性别" direction="horizontal">
-                <van-radio v-for="item in userSexList" :name="item.dictValue">{{ item.dictLabel }}</van-radio>
+                <van-radio v-for="item in userSexList" :name="item.dictValue" :key="item.dictValue">{{ item.dictLabel }}</van-radio>
               </van-radio-group>
             </template>
           </van-field>
           <van-field label="用户类型">
             <template #input>
               <van-radio-group v-model="state.form.userType" label="用户类型" placeholder="用户类型" direction="horizontal">
-                <van-radio v-for="item in userTypeList" :name="item.dictValue">{{ item.dictLabel }}</van-radio>
+                <van-radio v-for="item in userTypeList" :name="item.dictValue" :key="item.dictValue">{{ item.dictLabel }}</van-radio>
               </van-radio-group>
             </template>
           </van-field>
@@ -63,7 +63,7 @@
           <van-field label="证件类型" :rules="[{ required: true, message: '请选择证件类型' }]">
             <template #input>
               <van-radio-group v-model="state.form.idType" label="证件类型" placeholder="证件类型" direction="horizontal">
-                <van-radio v-for="item in userCertList" :name="item.dictValue">{{ item.dictLabel }}</van-radio>
+                <van-radio v-for="item in userCertList" :name="item.dictValue" :key="item.id">{{ item.dictLabel }}</van-radio>
               </van-radio-group>
             </template>
           </van-field>
@@ -103,7 +103,7 @@
             <template #input>
               <div class="w100">
                 <van-button type="primary" block @click="addProject">新增</van-button>
-                <template v-for="(item, index) in state.form.projectList" :key="index" class="flex project-item mb4">
+                <template v-for="(item, index) in state.form.projectList" :key="index">
                   <van-field v-model="item.projectName" placeholder="项目名称" class="mt10"></van-field>
                   <van-field v-model="item.projectTypeName" is-link readonly label="项目类型" placeholder="项目类型" @click="openPjtType(item, index)" />
                   <van-field v-model="item.projectSource" placeholder="项目来源"></van-field>
@@ -165,9 +165,8 @@
   import { useDictApi } from '/@/api/system/dict'
   import { useProApi } from '/@/api/project'
   import { useDeptApi } from '/@/api/system/dept'
-  import { useRouter } from 'vue-router'
+  import { useRouter, useRoute } from 'vue-router'
   import { showNotify } from 'vant'
-  import other, { getDictLabel } from '/@/utils/other'
   const sm3 = crypto.sm3
   const loginApi = useLoginApi()
   const router = useRouter()
@@ -337,7 +336,7 @@
     })
     router.push('/login')
   }
-  onMounted(() => {
+  onMounted(async () => {
     getDicts()
   })
 </script>

+ 70 - 30
src/view/service/index.vue

@@ -2,28 +2,29 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-11 18:02:10
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-19 15:44:54
+ * @LastEditTime: 2025-03-21 11:51:04
  * @FilePath: \vant-demo-master\vant\vue3-ts\src\view\login\index.vue
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 -->
 <template>
   <div class="app-container">
-    <van-row class="pl10 pr10 pt10">
+    <van-row class="pt10">
       <van-button type="success" size="small" @click="changeType('instr')">仪器预约</van-button>
-      <van-button type="primary" size="small" @click="changeType('entry')">平台入室</van-button>
-      <van-button type="primary" size="small" @click="changeType('notice')">技术委托</van-button>
+      <van-button type="warning" size="small" @click="changeType('entry')">平台入室</van-button>
+      <van-button type="primary" size="small" @click="changeType('tech')">技术委托</van-button>
     </van-row>
     <van-list v-model:loading="state.loading" :finished="state.finished" finished-text="没有更多了" @load="onLoad">
-      <van-cell v-for="item in state.list" :key="item" @click="toDetail(item.id)">
+      <van-cell v-for="item in state.list" :key="item">
         <template #default>
-          <div v-if="state.queryParams.type == 'approval'" class="list">
+          <div v-if="state.queryParams.type == 'instr'" class="list">
             <header class="flex justify-between">
-              <van-text-ellipsis class="title" :content="item.defName" />
-              <van-tag v-if="item.isFinish == '10'" type="warning">已完成</van-tag>
-              <van-tag v-else-if="item.isFinish == '20'" type="primary">未完成</van-tag>
+              <van-text-ellipsis
+                :content="`预约时间:${formatDate(new Date(item.startTime), 'YYYY-mm-dd HH:MM')}~${formatDate(new Date(item.endTime), 'HH:MM')}`"
+              />
+              <van-tag type="primary">{{ setStatus(item.appointStatus) }}</van-tag>
             </header>
             <p class="inst-title">
-              <van-text-ellipsis :content="item.instTitle" />
+              <van-text-ellipsis :content="`预约名称:${item.instName}-${item.instNameEn}`" />
             </p>
             <footer class="flex justify-between">
               <van-text-ellipsis :content="item.candidate" />
@@ -47,13 +48,15 @@
   import { useRouter, useRoute } from 'vue-router'
   import { useExecutionApi } from '/@/api/execution'
   import { useNewsApi } from '/@/api/system/news'
+  import { useMyAppointApi } from '/@/api/appoint'
+  const appointApi = useMyAppointApi()
   const executionApi = useExecutionApi()
   const newsApi = useNewsApi()
   const router = useRouter()
   const route = useRoute()
   const state = reactive({
     queryParams: {
-      type: 'approval',
+      type: 'instr',
       pageNum: 1,
       pageSize: 10
     },
@@ -61,8 +64,41 @@
     loading: true,
     list: [] as any[]
   })
-  const onClickRight = () => {
-    router.go(-1)
+  const setStatus = (key: string) => {
+    let str = ''
+    switch (key) {
+      case '10':
+        str = '待审核'
+        break
+      case '11':
+        str = '已退回'
+        break
+      case '20':
+        str = '已通过'
+        break
+      case '30':
+        str = '已驳回'
+        break
+      case '40':
+        str = '已取消'
+        break
+      case '50':
+        str = '已上机'
+        break
+      case '60':
+        str = '已完成'
+        break
+      case '70':
+        str = '审核超时'
+        break
+      case '80':
+        str = '超时取消'
+        break
+      case '90':
+        str = '已超时'
+        break
+    }
+    return str
   }
   const changeType = (str: string) => {
     state.queryParams.type = str
@@ -70,27 +106,25 @@
     onLoad()
   }
   const onLoad = async () => {
-    const post = state.queryParams.type == 'approval' ? executionApi.getOwApproveList : newsApi.getNoticeList
-    const [err, res]: ToResponse = await to(post({ platformId: 1000103, noPage: true }))
+    let post: any = null
+    if (state.queryParams.type === 'instr') {
+      post = appointApi.appointRecords
+    }
+    const [err, res]: ToResponse = await to(post(state.queryParams))
     if (err) return
-    state.list = res?.data?.list || []
+    const list = res?.data?.list || []
+    for (const item of list) {
+      state.list.push(item)
+    }
     state.loading = false
     state.queryParams.pageNum++
     if (state.list.length < state.queryParams.pageSize) {
       state.finished = true
     }
   }
-  const toDetail = (id: number) => {
-    router.push({
-      path: state.queryParams.type === 'approval' ? '/todo/approval-detail' : '/todo/notice-detail',
-      query: {
-        id
-      }
-    })
-  }
   onMounted(() => {
     const type = route.query.type
-    if(type) {
+    if (type) {
       state.queryParams.type = type as string
     }
     onLoad()
@@ -99,18 +133,24 @@
 
 <style lang="scss" scoped>
   .app-container {
-    .van-row .van-button {
-      flex: 1;
-      & + .van-button {
-        margin-left: 10px;
+    display: flex;
+    flex-direction: column;
+    .van-row {
+      flex: 0 0 30px;
+      .van-button {
+        flex: 1;
+        & + .van-button {
+          margin-left: 10px;
+        }
       }
     }
     .van-list {
-      height: calc(100% - 62px);
+      height: calc(100vh - 48px);
       background-color: #fff;
       margin: 10px 0;
       padding: 0 10px;
       border-radius: 4px;
+      overflow-y: auto;
       .van-cell {
         background-color: #f9ffff;
         margin-top: 10px;

+ 21 - 20
src/view/todo/index.vue

@@ -2,20 +2,16 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-11 18:02:10
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-19 12:00:44
+ * @LastEditTime: 2025-03-21 09:38:30
  * @FilePath: \vant-demo-master\vant\vue3-ts\src\view\login\index.vue
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 -->
 <template>
   <div class="app-container">
-    <van-row class="pl10 pr10 pt10">
-      <van-button type="success" size="small" @click="changeType('approval')">审批流程</van-button>
-      <van-button type="primary" size="small" @click="changeType('notice')">系统公告</van-button>
-    </van-row>
     <van-list v-model:loading="state.loading" :finished="state.finished" finished-text="没有更多了" @load="onLoad">
       <van-cell v-for="item in state.list" :key="item" @click="toDetail(item.id)">
         <template #default>
-          <div v-if="state.queryParams.type == 'approval'" class="list">
+          <div class="list">
             <header class="flex justify-between">
               <van-text-ellipsis class="title" :content="item.defName" />
               <van-tag v-if="item.isFinish == '10'" type="warning">已完成</van-tag>
@@ -26,13 +22,9 @@
             </p>
             <footer class="flex justify-between">
               <van-text-ellipsis :content="item.candidate" />
-              <span class="time">{{ formatDate(new Date(item.createdTime), 'YYYY-mm-dd') }}</span>
+              <span>{{ formatDate(new Date(item.createdTime), 'YYYY-mm-dd') }}</span>
             </footer>
           </div>
-          <div v-else class="flex justify-between">
-            <van-text-ellipsis class="inst-title" :content="`【${item.noticeType === '10' ? '公告' : '通知'}】${item.noticeTitle}`" />
-            <span class="time">{{ formatDate(new Date(item.noticeTime), 'YYYY-mm-dd') }}</span>
-          </div>
         </template>
       </van-cell>
     </van-list>
@@ -72,7 +64,10 @@
     const post = state.queryParams.type == 'approval' ? executionApi.getOwApproveList : newsApi.getNoticeList
     const [err, res]: ToResponse = await to(post({ platformId: 1000103, noPage: true }))
     if (err) return
-    state.list = res?.data?.list || []
+    const list = res?.data?.list || []
+    for(const item of list) {
+      state.list.push(item)
+    }
     state.loading = false
     state.queryParams.pageNum++
     if (state.list.length < state.queryParams.pageSize) {
@@ -81,7 +76,7 @@
   }
   const toDetail = (id: number) => {
     router.push({
-      path: state.queryParams.type === 'approval' ? '/todo/approval-detail' : '/todo/notice-detail',
+      path: '/todo/approval-detail',
       query: {
         id
       }
@@ -105,17 +100,18 @@
       }
     }
     .van-list {
-      height: calc(100% - 62px);
-      background-color: #fff;
-      margin: 10px 0;
-      padding: 0 10px;
+      height: calc(100% - 20px);
       border-radius: 4px;
       .van-cell {
-        background-color: #f9ffff;
+        background-color: #fff;
         margin-top: 10px;
-        header,
-        footer {
+        header {
           color: #333;
+          font-size: 16px;
+        }
+        footer {
+          color: #969799;
+          margin-top: 4px;
         }
         .title {
           font-weight: bold;
@@ -123,6 +119,11 @@
         .inst-title {
           color: #333;
           text-align: left;
+          flex: 1;
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+          margin-top: 4px;
         }
         .time {
           color: #f69a4d;

+ 2 - 9
src/view/training/enroll.vue

@@ -2,7 +2,7 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-18 20:02:42
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-19 15:23:08
+ * @LastEditTime: 2025-03-19 18:39:48
  * @FilePath: \labsop_h5\src\view\training\enroll.vue
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 -->
@@ -76,14 +76,6 @@
   }
   const initForm = async () => {
     state.form.trainingNoticeId = route.query.state ? +route.query.state : 0
-    // // 微信授权码获取openId
-    // const code = route.query.code
-    // if (!openId.value && code) {
-    //   if (typeof code === 'string') {
-    //     await storesUseUserInfo.setOpenId(code)
-    //     state.form.wechatOpenId = openId.value
-    //   }
-    // }
     const date = new Date()
     state.form.trainingDate = [formatDate(date, 'YYYY'), formatDate(date, 'mm'), formatDate(date, 'dd')]
     if (userInfos.value.id) {
@@ -121,6 +113,7 @@
     router.push('/home')
   }
   onMounted(() => {
+    storesUseUserInfo.setOpenId(route.query.code as string)
     getDicts()
     initForm()
   })

+ 188 - 6
src/view/user/edit.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <header>
+    <!-- <header>
       <img :src="userInfos.avatar" alt="" />
       <div class="content">
         <p class="bold">
@@ -13,9 +13,14 @@
           <van-text-ellipsis :content="userInfos.deptName" />
         </p>
       </div>
-    </header>
+    </header> -->
     <van-form ref="formRef" @submit="onSubmit" class="mt10" required="auto">
       <van-cell-group>
+        <van-cell class="flex" is-link size="normal" title="头像" @click="editAvatar">
+          <template #value>
+            <img class="avatar" width="30" height="30" :src="userInfos.avatar" alt="" />
+          </template>
+        </van-cell>
         <van-field v-model="state.form.nickName" label="用户昵称" placeholder="用户昵称" :rules="[{ required: true, message: '请输入用户昵称' }]" />
         <van-field v-model="state.form.phone" label="手机号" placeholder="手机号"> </van-field>
         <van-field v-model="state.form.email" label="电子邮箱" placeholder="电子邮箱" />
@@ -32,6 +37,29 @@
         <van-button round block type="primary" native-type="submit"> 保存 </van-button>
       </div>
     </van-form>
+    <!-- 剪裁图片组件 -->
+    <van-popup class="bg-tran" v-model:show="cropperState.isShowDialog" closeable @close="closeDialog" position="bottom" :style="{ padding: '10px' }">
+      <div class="cropper-warp">
+        <div class="cropper-warp-left">
+          <img :src="cropperState.cropperImg as string" class="cropper-warp-left-img" />
+        </div>
+        <!-- <div class="cropper-warp-right">
+          <div class="cropper-warp-right-title">预览</div>
+          <div class="cropper-warp-right-item">
+            <div class="cropper-warp-right-value">
+              <img :src="cropperState.cropperImgBase64" class="cropper-warp-right-value-img" />
+            </div>
+            <div class="cropper-warp-right-label">200 x 200</div>
+          </div>
+        </div> -->
+      </div>
+      <van-row align="center" justify="end" class="mt10">
+        <van-uploader :after-read="afterRead">
+          <van-button icon="plus">上传头像</van-button>
+        </van-uploader>
+        <van-button type="primary" @click="onAvatarSubmit" class="ml10">确 定</van-button>
+      </van-row>
+    </van-popup>
   </div>
 </template>
 
@@ -39,12 +67,15 @@
   import { storeToRefs } from 'pinia'
   import { useUserInfo } from '/@/stores/userInfo'
   import { Local } from '/@/utils/storage'
-  import { showConfirmDialog, showNotify } from 'vant'
+  import { showConfirmDialog, showDialog, showNotify } from 'vant'
   import { useRouter } from 'vue-router'
-  import { onMounted, reactive, ref } from 'vue'
+  import { nextTick, onMounted, reactive, ref } from 'vue'
   import { useUserApi } from '/@/api/system/user'
   import to from 'await-to-js'
   import { useDictApi } from '/@/api/system/dict'
+  import Cropper from 'cropperjs'
+  import 'cropperjs/dist/cropper.css'
+  import axios from 'axios'
 
   const router = useRouter()
   const storesUseUserInfo = useUserInfo()
@@ -71,13 +102,150 @@
       submitTxt: ''
     }
   })
+  // 定义变量内容
+  const cropperState = reactive({
+    isShowDialog: false,
+    cropperImg: '' as ArrayBuffer | string,
+    cropperImgBase64: '',
+    cropper: '' as RefType
+  })
+  const cropper = ref()
   const getDicts = () => {
     Promise.all([dictApi.getDictDataByType('sys_com_sex')]).then(([sex]) => {
       userSexList.value = sex.data.values || []
     })
   }
-  const onClickRight = () => {
-    router.go(-1)
+  const afterRead = (res: any) => {
+    const rawFile = res.file
+    console.log(rawFile, rawFile.type, 'rawFileeeeeeeeeee')
+    if (
+      rawFile.type !== 'image/jpeg' &&
+      rawFile.type !== 'image/jpg' &&
+      rawFile.type !== 'image/png' &&
+      rawFile.type !== 'image/bmp' &&
+      rawFile.type !== 'image/gif'
+    ) {
+      showNotify({
+        message: '上传图片必须是JPG/PNG/BMP/GIF类型!',
+        type: 'warning'
+      })
+      return false
+    } else if (rawFile.size / 1024 / 1024 > 2) {
+      showNotify({
+        message: '图片大小不能超过2MB!',
+        type: 'warning'
+      })
+      return false
+    }
+    const reader = new FileReader()
+    reader.readAsDataURL(rawFile)
+    reader.onload = () => {
+      cropperState.cropperImg = reader.result
+      cropperState.cropper.destroy()
+      setTimeout(() => {
+        initCropper()
+      })
+    }
+    return false
+  }
+  // 打开弹窗
+  const openDialog = (imgs: string) => {
+    cropperState.cropperImg = imgs
+    cropperState.isShowDialog = true
+    nextTick(() => {
+      initCropper()
+    })
+  }
+  // 初始化cropperjs图片裁剪
+  const initCropper = () => {
+    const letImg = <HTMLImageElement>document.querySelector('.cropper-warp-left-img')
+    cropperState.cropper = new Cropper(letImg, {
+      viewMode: 1,
+      dragMode: 'move',
+      initialAspectRatio: 1,
+      aspectRatio: 1,
+      preview: '.before',
+      background: false,
+      autoCropArea: 0.6,
+      cropBoxMovable: true, // 是否允许剪裁框拖动
+      cropBoxResizable: false, // 是否允许剪裁框缩放
+      zoomable: true,
+      ready() {
+        cropperState.cropper.setCropBoxData({
+          width: 200, // 宽度
+          height: 200 // 高度
+        })
+      },
+      crop: () => {
+        cropperState.cropperImgBase64 = cropperState.cropper.getCroppedCanvas().toDataURL('image/jpeg')
+      }
+    })
+  }
+  const editAvatar = () => {
+    openDialog(state.form.avatar)
+  }
+  const onAvatarSubmit = () => {
+    const base64Url = cropperState.cropper.getCroppedCanvas().toDataURL('image/jpeg')
+    let file = dataURLtoFile(base64Url, 'avatar.png')
+    uploadBaseFunc(file)
+  }
+  // base64转file
+  const dataURLtoFile = (dataurl, filename) => {
+    // 获取到base64编码
+    const arr = dataurl.split(',')
+    // 将base64编码转为字符串
+    const bstr = window.atob(arr[1])
+    let n = bstr.length
+    const u8arr = new Uint8Array(n) // 创建初始化为0的,包含length个元素的无符号整型数组
+    while (n--) {
+      u8arr[n] = bstr.charCodeAt(n)
+    }
+    return new File([u8arr], filename, {
+      type: 'image/png'
+    })
+  }
+  // 上传图片获取地址
+  const uploadBaseFunc = (file) => {
+    const formData = new FormData()
+    formData.append('file', file)
+    const uploadUrl = import.meta.env.VITE_UPLOAD // 上传的图片服务器地址
+    axios
+      .post(uploadUrl, formData, {
+        headers: {
+          'Content-Type': 'multipart/form-data'
+        }
+      })
+      .then(async (res) => {
+        if (res) {
+          if (res.data.Code == 200) {
+            // 图片上传成功,直接修改
+            const [err]: ToResponse = await to(userApi.setAvatar({ fileUrl: res?.data.Data || '' }))
+            if (err) return
+            showNotify({
+              type: 'success',
+              message: '修改成功'
+            })
+            storesUseUserInfo.setUserInfos()
+            closeDialog()
+          }
+        }
+      }).catch(() => {
+        showNotify({
+          type: 'warning',
+          message: '上传失败'
+        })
+      })
+  }
+  // 关闭弹窗
+  const closeDialog = () => {
+    if (cropperState.cropper) {
+      cropperState.cropper.destroy()
+      cropperState.isShowDialog = false
+      cropperState.cropperImg = ''
+      cropperState.cropperImgBase64 = ''
+      cropperState.cropper = ''
+      cropperState.isShowDialog = false
+    }
   }
   const initForm = async () => {
     const [err, res]: ToResponse = await to(userApi.getProfile())
@@ -137,5 +305,19 @@
         }
       }
     }
+    .avatar {
+      vertical-align: middle;
+    }
+  }
+  .cropper-warp {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background-color: #eee;
+    .cropper-warp-left {
+      height: 350px;
+      width: 350px;
+    }
   }
 </style>

+ 10 - 9
src/view/user/index.vue

@@ -4,10 +4,10 @@
       <img :src="userInfos.avatar" alt="" />
       <div class="content">
         <p class="bold">
-          <van-text-ellipsis :content="`${userInfos.nickName}-${userInfos.userRoleNames}`" />
+          <van-text-ellipsis :content="`${userInfos.nickName}(信用分:${userInfos.creditScore})`" />
           <van-icon name="setting" size="20" @click="onRouterPush('/user/edit')" />
         </p>
-        <p>
+        <p class="flex justify-between">
           <van-text-ellipsis :content="userInfos.pgName" />
         </p>
         <p>
@@ -16,16 +16,16 @@
       </div>
     </header>
     <div class="main">
-      <ul class="links">
+      <!-- <ul class="links">
         <li @click="onRouterPush('/training')">培训通知</li>
         <li>我的报名</li>
         <li>我的学习</li>
         <li>我的考试</li>
-      </ul>
-      <ul>
+      </ul> -->
+      <!-- <ul>
         <li>使用指引</li>
         <li>安全积分:{{ userInfos.creditScore }}分</li>
-      </ul>
+      </ul> -->
       <div class="card">
         <h4>经费统计</h4>
         <ul class="nav">
@@ -49,7 +49,8 @@
       </div>
     </div>
     <footer>
-      <van-button class="w100" @click="signOut" type="primary">切换账号</van-button>
+      <van-button class="w100" color="#1cb4fd" plain @click="onRouterPush('/user/password')">修改密码</van-button>
+      <van-button class="w100 mt10" @click="signOut" type="primary">切换账号</van-button>
     </footer>
   </div>
 </template>
@@ -62,7 +63,7 @@
   import { useRouter } from 'vue-router'
   const router = useRouter()
   const storesUseUserInfo = useUserInfo()
-  const { userInfos, sdkConfig } = storeToRefs(storesUseUserInfo)
+  const { userInfos, sdkConfig, openId } = storeToRefs(storesUseUserInfo)
   const onRouterPush = (val: string) => {
     router.push(val)
   }
@@ -71,7 +72,7 @@
       message: '确认切换账号?'
     }).then(() => {
       Local.clear()
-      window.location.reload()
+      router.push('/login')
     })
   }
 </script>

+ 150 - 0
src/view/user/password.vue

@@ -0,0 +1,150 @@
+<template>
+  <div class="app-container">
+    <van-form ref="formRef" @submit="onSubmit" class="mt10" required="auto">
+      <van-cell-group>
+        <van-field
+          v-model="state.form.oldPassword"
+          type="password"
+          label="旧密码"
+          placeholder="旧密码"
+          :rules="[{ required: true, message: '请输入旧密码' }]"
+        />
+        <van-field
+          v-model="state.form.newPassword"
+          type="password"
+          label="新密码"
+          placeholder="新密码"
+          :rules="[{ required: true, message: '请输入新密码', validator: checkPassword }]"
+        />
+        <van-field
+          v-model="state.form.newPassword2"
+          type="password"
+          label="确认密码"
+          placeholder="确认密码"
+          :rules="[{ required: true, message: '请输入确认密码', validator: checkConfirmPassword }]"
+        />
+      </van-cell-group>
+      <div style="margin: 16px">
+        <van-button round block type="primary" native-type="submit">保存</van-button>
+      </div>
+    </van-form>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { storeToRefs } from 'pinia'
+  import { useUserInfo } from '/@/stores/userInfo'
+  import { Local, Session } from '/@/utils/storage'
+  import { showConfirmDialog, showDialog, showNotify } from 'vant'
+  import { useRouter } from 'vue-router'
+  import { nextTick, onMounted, reactive, ref } from 'vue'
+  import { useUserApi } from '/@/api/system/user'
+  import to from 'await-to-js'
+  import { useDictApi } from '/@/api/system/dict'
+  import Cropper from 'cropperjs'
+  import 'cropperjs/dist/cropper.css'
+  import axios from 'axios' 
+  import crypto from 'sm-crypto';
+  const sm3 = crypto.sm3;
+  const router = useRouter()
+  const storesUseUserInfo = useUserInfo()
+  const { userInfos } = storeToRefs(storesUseUserInfo)
+  const userApi = useUserApi()
+  const formRef = ref()
+  const dictApi = useDictApi()
+  const userSexList = ref(<RowDicDataType[]>[])
+  const state = reactive({
+    form: {
+      oldPassword: '',
+      newPassword: '',
+      newPassword2: ''
+    },
+    dialog: {
+      isShowDialog: false,
+      type: '',
+      title: '',
+      submitTxt: ''
+    }
+  })
+  const checkPassword = (value: string) => {
+    if (/^[\w\S]{6,18}$/.test(value) && /[a-z]+/.test(value) && /[A-Z]+/.test(value) && /\d+/.test(value) && /[^a-zA-Z0-9]+/.test(value)) {
+      return true
+    } else {
+      return '密码必须包含大小写字母、数字和特殊字符,长度在6~18之间'
+    }
+  }
+  const checkConfirmPassword = (value: string) => {
+    if (value !== state.form.newPassword) {
+      return '两次输入的密码不一致'
+    } else {
+      return true
+    }
+  }
+  const onSubmit = async () => {
+    const [errValid] = await to(formRef.value.validate())
+    if (errValid) return
+    const params = JSON.parse(JSON.stringify(state.form))
+    params.oldPassword = sm3(params.oldPassword)
+    params.newPassword = sm3(params.newPassword)
+    const [err]: ToResponse = await to(userApi.changePassword(params))
+    if (err) return
+    showNotify({
+      type: 'success',
+      message: '修改成功,请重新登录'
+    })
+    Session.clear();
+    Local.remove('token')
+    router.push('/login')
+  }
+  onMounted(() => {})
+</script>
+
+<style lang="scss" scoped>
+  .app-container {
+    header {
+      background-color: #1c9bfd;
+      color: #fff;
+      padding: 10px;
+      border-radius: 8px;
+      margin-top: 10px;
+      display: flex;
+      img {
+        width: 100px;
+        height: 100px;
+        border-radius: 50%;
+        margin-right: 10px;
+      }
+      .content {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+        justify-content: space-around;
+        overflow: hidden;
+        .bold {
+          display: flex;
+          align-items: center;
+          font-weight: bold;
+          // flex-wrap: nowrap;
+          white-space: nowrap;
+          .van-text-ellipsis {
+            flex: 1;
+          }
+        }
+      }
+    }
+    .avatar {
+      vertical-align: middle;
+    }
+  }
+  .cropper-warp {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background-color: #eee;
+    .cropper-warp-left {
+      height: 350px;
+      width: 350px;
+    }
+  }
+</style>

+ 1 - 1
tsconfig.json

@@ -21,6 +21,6 @@
       ]
     }
   },
-  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "src/utils/errorCode.js", "src/utils/micro_request.js", "src/types/*.d.ts", "src/types/**/*.ts", "src/api/appoint/index.ts"],
+  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "src/utils/errorCode.js", "src/utils/micro_request.ts", "src/types/*.d.ts", "src/types/**/*.ts", "src/api/appoint/index.ts"],
   "references": [{ "path": "./tsconfig.node.json" }]
 }

+ 10 - 2
vite.config.ts

@@ -2,7 +2,7 @@
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-10 11:40:15
  * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-17 10:14:26
+ * @LastEditTime: 2025-03-20 13:58:08
  * @FilePath: \vue3-ts\vite.config.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -30,6 +30,14 @@ export default defineConfig({
   },
   server: {
     port: 4200,
-    https: false
+    https: false,
+    allowedHosts: ['4ik677er1300.vicp.fun'],
+    proxy: {
+      // '/wechat': {
+      //   target: 'http://gz-zyyfy-wx.labsop.cn',
+      //   ws: true,
+      //   changeOrigin: true
+      // },
+    },
   },
 });