hrp-ui-base 1.4.7 → 1.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (373) hide show
  1. package/README.md +51 -51
  2. package/dist/components.cjs +1 -1
  3. package/dist/components.es.js +25 -25
  4. package/dist/style.css +1 -1
  5. package/package.json +101 -101
  6. package/packages/approval-process/.eslintrc +6 -6
  7. package/packages/approval-process/README.md +22 -22
  8. package/packages/approval-process/index.ts +65 -65
  9. package/packages/approval-process/package.json +17 -17
  10. package/packages/approval-process/packages/components/choose-member/BO/departBo.ts +8 -8
  11. package/packages/approval-process/packages/components/choose-member/BO/memberBo.ts +14 -14
  12. package/packages/approval-process/packages/components/choose-member/BO/roleBo.ts +16 -16
  13. package/packages/approval-process/packages/components/choose-member/index.vue +496 -496
  14. package/packages/approval-process/packages/components/choose-member/styles/common.scss +133 -133
  15. package/packages/approval-process/packages/components/comment/assets/download.svg +4 -4
  16. package/packages/approval-process/packages/components/comment/assets/mp3.svg +11 -11
  17. package/packages/approval-process/packages/components/comment/assets/mp4.svg +11 -11
  18. package/packages/approval-process/packages/components/comment/assets/other.svg +11 -11
  19. package/packages/approval-process/packages/components/comment/assets/pdf.svg +11 -11
  20. package/packages/approval-process/packages/components/comment/assets/ppt.svg +11 -11
  21. package/packages/approval-process/packages/components/comment/assets/preview.svg +4 -4
  22. package/packages/approval-process/packages/components/comment/assets/word.svg +11 -11
  23. package/packages/approval-process/packages/components/comment/assets/xls.svg +11 -11
  24. package/packages/approval-process/packages/components/comment/index.vue +843 -843
  25. package/packages/approval-process/packages/components/comment/modules/previewMp.vue +53 -53
  26. package/packages/approval-process/packages/components/comment/styles/comment-style.scss +303 -303
  27. package/packages/approval-process/packages/components/image-cropper/index.vue +202 -202
  28. package/packages/approval-process/packages/components/sign/index.vue +766 -766
  29. package/packages/approval-process/packages/components/sign/modules/full-screen-sign.vue +19 -19
  30. package/packages/approval-process/packages/components/urge-dialog/index.vue +120 -120
  31. package/packages/approval-process/packages/components/vue-esign/index.vue +289 -289
  32. package/packages/approval-process/packages/flow/approve-component/approve-success.vue +81 -81
  33. package/packages/approval-process/packages/flow/approve-component/route-params-error.vue +48 -48
  34. package/packages/approval-process/packages/flow/approve-component/submit-success.vue +58 -58
  35. package/packages/approval-process/packages/flow/examine-approve-itr.vue +978 -978
  36. package/packages/approval-process/packages/flow/examine-approve.vue +1065 -1065
  37. package/packages/approval-process/packages/flow/form-component/associated-approval-form/README.md +38 -38
  38. package/packages/approval-process/packages/flow/form-component/associated-approval-form/index.vue +158 -158
  39. package/packages/approval-process/packages/flow/form-component/associated-approval-form/modules/associated-approval-form-mobile.vue +297 -297
  40. package/packages/approval-process/packages/flow/form-component/associated-approval-form/modules/associated-approval-form-pc.vue +314 -314
  41. package/packages/approval-process/packages/flow/form-component/associated-approval-form/style/curd-area-mobile.scss +109 -109
  42. package/packages/approval-process/packages/flow/form-component/associated-approval-form/style/curd-area.scss +95 -95
  43. package/packages/approval-process/packages/flow/form-component/associated-approval-form/style/out-put.scss +47 -47
  44. package/packages/approval-process/packages/flow/form-component/show-sign.vue +27 -27
  45. package/packages/approval-process/packages/flow/styles/common.scss +133 -133
  46. package/packages/approval-process/packages/flow/styles/examine-approve-pc.scss +465 -465
  47. package/packages/approval-process/packages/flow/styles/examine-approve-phone.scss +292 -292
  48. package/packages/approval-process/packages/flow/styles/submit-approve-pc.scss +379 -379
  49. package/packages/approval-process/packages/flow/styles/submit-approve-phone.scss +336 -336
  50. package/packages/approval-process/packages/flow/submit-approve-itr.vue +622 -622
  51. package/packages/approval-process/packages/flow/submit-approve.vue +627 -627
  52. package/packages/approval-process/packages/flow/systemCom/BO/departBo.ts +8 -8
  53. package/packages/approval-process/packages/flow/systemCom/BO/memberBo.ts +14 -14
  54. package/packages/approval-process/packages/flow/systemCom/BO/roleBo.ts +16 -16
  55. package/packages/approval-process/packages/flow/systemCom/choose-depart.vue +398 -398
  56. package/packages/approval-process/packages/flow/systemCom/choose-member-phone.vue +514 -514
  57. package/packages/approval-process/packages/flow/systemCom/choose-member.vue +564 -564
  58. package/packages/approval-process/packages/flow/systemCom/choose-role.vue +300 -300
  59. package/packages/approval-process/packages/styles/common.scss +133 -133
  60. package/packages/approval-process/packages/styles/flex-common.scss +117 -117
  61. package/packages/approval-process/packages/styles/flexCommon.scss +72 -72
  62. package/packages/approval-process/packages/styles/main.css +216 -216
  63. package/packages/approval-process/packages/styles/router-page.scss +370 -370
  64. package/packages/approval-process/packages/styles/style-set.ts +207 -207
  65. package/packages/approval-process/packages/types.d.ts +3 -3
  66. package/packages/approval-process/packages/utils/base.ts +15 -15
  67. package/packages/approval-process/packages/utils/crypto.ts +66 -66
  68. package/packages/approval-process/packages/utils/ddUtils.ts +56 -56
  69. package/packages/approval-process/packages/utils/debounce.ts +35 -35
  70. package/packages/approval-process/packages/utils/des.js +1107 -1107
  71. package/packages/approval-process/packages/utils/download.ts +38 -38
  72. package/packages/approval-process/packages/utils/editor.ts +36 -36
  73. package/packages/approval-process/packages/utils/getEnv.ts +36 -36
  74. package/packages/approval-process/packages/utils/os.ts +20 -20
  75. package/packages/approval-process/packages/utils/path-util.ts +31 -31
  76. package/packages/approval-process/packages/utils/routerUtil.ts +304 -304
  77. package/packages/approval-process/packages/utils/string-utils.ts +13 -13
  78. package/packages/approval-process/packages/utils/throttle.ts +44 -44
  79. package/packages/approval-process/packages/utils/validate.ts +92 -92
  80. package/packages/approval-process/packages/utils/ws.ts +218 -218
  81. package/packages/approval-process/tsconfig.json +72 -72
  82. package/packages/approval-process/vite.config.ts +56 -56
  83. package/src/api/bms/dict/bo/DictConfigBO.ts +23 -23
  84. package/src/api/bms/dict/bo/DictConfigBaseVO.ts +24 -24
  85. package/src/api/bms/dict/bo/DictConfigSearchBO.ts +12 -12
  86. package/src/api/bms/dict/bo/DictImportBO.ts +6 -6
  87. package/src/api/bms/dict/bo/DictVO.ts +21 -21
  88. package/src/api/bms/dict/bo/PageBaseVO.ts +8 -8
  89. package/src/api/bms/dict/bo/TotalDictBO.ts +11 -11
  90. package/src/api/bms/dict/bo/TotalDictPageVO.ts +17 -17
  91. package/src/api/bms/dict/dict-controller.ts +78 -78
  92. package/src/api/bms/file/FileExportController.ts +19 -19
  93. package/src/api/bms/file/FileUploadController.ts +131 -131
  94. package/src/api/bms/file/bo/FileBaseInfo.ts +13 -13
  95. package/src/api/bms/file/bo/FileExportVO.ts +17 -17
  96. package/src/api/bms/file/bo/FilePreviewVO.ts +8 -8
  97. package/src/api/bms/file/bo/FileUploadInfo.ts +22 -22
  98. package/src/api/bms/file/bo/FileUrlVO.ts +10 -10
  99. package/src/api/bms/file/bo/StsTokenVO.ts +20 -20
  100. package/src/api/bms/flow/FlowCommentController.ts +50 -50
  101. package/src/api/bms/flow/FlowInstanceController.ts +179 -179
  102. package/src/api/bms/flow/FlowProcessController.ts +55 -55
  103. package/src/api/bms/flow/FlowSheetController.ts +27 -27
  104. package/src/api/bms/flow/FlowSignController.ts +76 -76
  105. package/src/api/bms/flow/bo/AssociatedApprovalBO.ts +15 -15
  106. package/src/api/bms/flow/bo/AssociatedApprovalVO.ts +35 -35
  107. package/src/api/bms/flow/bo/AuditBaseInfo.ts +39 -39
  108. package/src/api/bms/flow/bo/ButtonVo.ts +7 -7
  109. package/src/api/bms/flow/bo/DingCallbackBo.ts +13 -13
  110. package/src/api/bms/flow/bo/FlowCommentBO.ts +12 -12
  111. package/src/api/bms/flow/bo/FlowCommentVO.ts +23 -23
  112. package/src/api/bms/flow/bo/FlowInstanceBO.ts +14 -14
  113. package/src/api/bms/flow/bo/FlowNodeVO.ts +24 -24
  114. package/src/api/bms/flow/bo/FlowNoticeBO.ts +17 -17
  115. package/src/api/bms/flow/bo/FlowProcessBO.ts +13 -13
  116. package/src/api/bms/flow/bo/FlowProcessVO.ts +9 -9
  117. package/src/api/bms/flow/bo/FlowReplyVO.ts +23 -23
  118. package/src/api/bms/flow/bo/FlowSheetVO.ts +16 -16
  119. package/src/api/bms/flow/bo/InstanceBackBO.ts +9 -9
  120. package/src/api/bms/flow/bo/InstanceNodeBO.ts +13 -13
  121. package/src/api/bms/flow/bo/InstanceNodeVO.ts +21 -21
  122. package/src/api/bms/flow/bo/NodeShowVo.ts +19 -19
  123. package/src/api/bms/flow/bo/ReviewBO.ts +11 -11
  124. package/src/api/bms/flow/bo/SheetSearchBO.ts +11 -11
  125. package/src/api/bms/flow/bo/SystemVO.ts +7 -7
  126. package/src/api/bms/flow/bo/TransmitBO.ts +9 -9
  127. package/src/api/bms/flow/bo/UrgeFlowBO.ts +7 -7
  128. package/src/api/bms/flow/bo/UserBaseInfoVo.ts +13 -13
  129. package/src/api/bms/flow/bo/UserInfoVo.ts +44 -44
  130. package/src/api/bms/flow/bo/UserResultVo.ts +17 -17
  131. package/src/api/bms/flow/bo/ValueMapBo.ts +7 -7
  132. package/src/api/bms/home/HomeController.ts +106 -106
  133. package/src/api/bms/home/bo/CollectedMenuBo.ts +9 -9
  134. package/src/api/bms/home/bo/DeptBaseVo.ts +9 -9
  135. package/src/api/bms/home/bo/DomainVO.ts +13 -13
  136. package/src/api/bms/home/bo/FunPermission.ts +17 -17
  137. package/src/api/bms/home/bo/GroupVO.ts +10 -10
  138. package/src/api/bms/home/bo/HomeMenu.ts +22 -22
  139. package/src/api/bms/home/bo/HomeMenuVo.ts +11 -11
  140. package/src/api/bms/home/bo/PersonalizationBo.ts +17 -17
  141. package/src/api/bms/home/bo/PersonalizationVo.ts +19 -19
  142. package/src/api/bms/home/bo/PhoneMenu.ts +14 -14
  143. package/src/api/bms/home/bo/SearchMenuVo.ts +10 -10
  144. package/src/api/bms/home/bo/UserBaseInfoVo.ts +11 -11
  145. package/src/api/bms/permission-manage/PermissionController.ts +133 -133
  146. package/src/api/bms/permission-manage/bo/DataScopeConfig.ts +17 -17
  147. package/src/api/bms/permission-manage/bo/DataScopePoint.ts +15 -15
  148. package/src/api/bms/permission-manage/bo/DeptBaseVo.ts +9 -9
  149. package/src/api/bms/permission-manage/bo/FunPermissionConfig.ts +18 -18
  150. package/src/api/bms/permission-manage/bo/FunPointVo.ts +9 -9
  151. package/src/api/bms/permission-manage/bo/PermissionGroupBo.ts +11 -11
  152. package/src/api/bms/permission-manage/bo/PermissionGroupMemberVo.ts +18 -18
  153. package/src/api/bms/permission-manage/bo/PermissionGroupPageVo.ts +17 -17
  154. package/src/api/bms/permission-manage/bo/PermissionGroupVo.ts +17 -17
  155. package/src/api/bms/permission-manage/bo/RoleBaseVo.ts +9 -9
  156. package/src/api/bms/permission-manage/bo/TheFirstStepVo.ts +13 -13
  157. package/src/api/bms/permission-manage/bo/TheSecondStepVo.ts +14 -14
  158. package/src/api/bms/permission-manage/bo/UserBaseInfoVo.ts +11 -11
  159. package/src/api/bms/tabulation/TabulationController.ts +78 -78
  160. package/src/api/bms/tabulation/bo/FieldConditionBO.ts +10 -10
  161. package/src/api/bms/tabulation/bo/FieldConditionVO.ts +10 -10
  162. package/src/api/bms/tabulation/bo/OperateButtonVO.ts +9 -9
  163. package/src/api/bms/tabulation/bo/PersonViewVO.ts +33 -33
  164. package/src/api/bms/tabulation/bo/SearchVO.ts +33 -33
  165. package/src/api/bms/tabulation/bo/TabulationBO.ts +14 -14
  166. package/src/api/bms/tabulation/bo/ViewSortBO.ts +7 -7
  167. package/src/api/bms/tabulation/bo/ViewVO.ts +12 -12
  168. package/src/api/common/FileBaseInfo.ts +10 -10
  169. package/src/api/common/ResponseBean.ts +9 -9
  170. package/src/api/common/ResponseBeanList.ts +15 -15
  171. package/src/api/common/ResponseBeanTablePage.ts +21 -21
  172. package/src/api/common/TableQueryCriteriaVO.ts +10 -10
  173. package/src/api/common/bo/PageResult.ts +22 -22
  174. package/src/api/hrms/dept/bo/DeptBaseVo.ts +6 -6
  175. package/src/api/hrms/dept/bo/DeptTreeVo.ts +23 -23
  176. package/src/api/hrms/dept/bo/DeptUserVo.ts +9 -9
  177. package/src/api/hrms/dept/bo/DeptUsersVo.ts +10 -10
  178. package/src/api/hrms/dept/bo/DeptVo.ts +19 -19
  179. package/src/api/hrms/dept/bo/UserBaseInfoVo.ts +7 -7
  180. package/src/api/hrms/dept/deptController.ts +100 -100
  181. package/src/api/hrms/role/bo/RoleBaseVo.ts +6 -6
  182. package/src/api/hrms/role/bo/RoleGroupVo.ts +11 -11
  183. package/src/api/hrms/role/bo/RoleVo.ts +22 -22
  184. package/src/api/hrms/role/roleController.ts +19 -19
  185. package/src/api/hrms/user/bo/UserBaseInfoVo.ts +11 -11
  186. package/src/api/hrms/user/bo/UserCardVO.ts +13 -13
  187. package/src/api/hrms/user/bo/UserDeptBO.ts +13 -13
  188. package/src/api/hrms/user/bo/UserRoleBO.ts +11 -11
  189. package/src/api/hrms/user/bo/UserVO.ts +23 -23
  190. package/src/api/hrms/user/userController.ts +86 -86
  191. package/src/api/index.ts +9 -9
  192. package/src/api/notice/NoticeController.ts +73 -73
  193. package/src/api/notice/bo/NoticePageBO.ts +24 -24
  194. package/src/api/notice/bo/NoticeVO.ts +31 -31
  195. package/src/api/work-order-pc/flow/Bo/ValueMapBo.ts +7 -7
  196. package/src/api/work-order-pc/flow/ItrFlowProcessController.ts +41 -41
  197. package/src/api/work-order-pc/flow/Vo/FlowNodeVO.ts +24 -24
  198. package/src/api/work-order-pc/flow/Vo/UserInfoVo.ts +44 -44
  199. package/src/assets/layout/avatar-default.svg +6 -6
  200. package/src/assets/layout/collect-active.svg +7 -7
  201. package/src/assets/layout/collect-default.svg +7 -7
  202. package/src/assets/layout/download-active.svg +8 -8
  203. package/src/assets/layout/download-default.svg +8 -8
  204. package/src/assets/layout/export-dark.svg +7 -7
  205. package/src/assets/layout/export-default.svg +7 -7
  206. package/src/assets/layout/font-active.svg +6 -6
  207. package/src/assets/layout/font-default.svg +6 -6
  208. package/src/assets/layout/help-active.svg +10 -10
  209. package/src/assets/layout/help-default.svg +10 -10
  210. package/src/assets/layout/home-active.svg +6 -6
  211. package/src/assets/layout/home.svg +6 -6
  212. package/src/assets/layout/menu-expand.svg +6 -6
  213. package/src/assets/layout/menu-position-active.svg +6 -6
  214. package/src/assets/layout/menu-position-default.svg +6 -6
  215. package/src/assets/layout/message-active.svg +9 -9
  216. package/src/assets/layout/message-default.svg +9 -9
  217. package/src/assets/layout/moon.svg +8 -8
  218. package/src/assets/layout/router-all-dark.svg +11 -11
  219. package/src/assets/layout/router-all-default.svg +17 -17
  220. package/src/assets/layout/search-active.svg +20 -20
  221. package/src/assets/layout/search-default.svg +20 -20
  222. package/src/assets/layout/star-default.svg +3 -3
  223. package/src/assets/layout/sun-active.svg +6 -6
  224. package/src/assets/layout/sun-default.svg +6 -6
  225. package/src/assets/layout/switch-moon.svg +3 -3
  226. package/src/assets/layout/switch-sun.svg +41 -41
  227. package/src/assets/layout/todo-active.svg +12 -12
  228. package/src/assets/layout/todo-default.svg +12 -12
  229. package/src/assets/layout/user-dark.svg +13 -13
  230. package/src/assets/layout/user-default.svg +13 -13
  231. package/src/assets/no-permiss-dark.svg +16 -16
  232. package/src/assets/no-permiss-day.svg +16 -16
  233. package/src/assets/process/drawer/manyLevel1close.svg +9 -9
  234. package/src/assets/process/drawer/manyLevel1open.svg +6 -6
  235. package/src/assets/process/drawer/manyLevel2close.svg +6 -6
  236. package/src/assets/process/drawer/manyLevel2open.svg +6 -6
  237. package/src/assets/table-delete/table-delete.svg +2 -2
  238. package/src/components/annex-img-upload/assets/download.svg +4 -4
  239. package/src/components/annex-img-upload/assets/mp3.svg +11 -11
  240. package/src/components/annex-img-upload/assets/mp4.svg +11 -11
  241. package/src/components/annex-img-upload/assets/other.svg +11 -11
  242. package/src/components/annex-img-upload/assets/pdf.svg +11 -11
  243. package/src/components/annex-img-upload/assets/ppt.svg +11 -11
  244. package/src/components/annex-img-upload/assets/preview.svg +4 -4
  245. package/src/components/annex-img-upload/assets/word.svg +11 -11
  246. package/src/components/annex-img-upload/assets/xls.svg +11 -11
  247. package/src/components/annex-img-upload/index.vue +609 -609
  248. package/src/components/annex-img-upload/modules/previewMp.vue +53 -53
  249. package/src/components/annex-upload/assets/download.svg +4 -4
  250. package/src/components/annex-upload/assets/mp3.svg +11 -11
  251. package/src/components/annex-upload/assets/mp4.svg +11 -11
  252. package/src/components/annex-upload/assets/other.svg +11 -11
  253. package/src/components/annex-upload/assets/pdf.svg +11 -11
  254. package/src/components/annex-upload/assets/ppt.svg +11 -11
  255. package/src/components/annex-upload/assets/preview.svg +4 -4
  256. package/src/components/annex-upload/assets/word.svg +11 -11
  257. package/src/components/annex-upload/assets/xls.svg +11 -11
  258. package/src/components/annex-upload/index.vue +800 -800
  259. package/src/components/annex-upload/modules/previewMp.vue +53 -53
  260. package/src/components/annex-upload-weijian/assets/download.svg +4 -4
  261. package/src/components/annex-upload-weijian/assets/mp3.svg +11 -11
  262. package/src/components/annex-upload-weijian/assets/mp4.svg +11 -11
  263. package/src/components/annex-upload-weijian/assets/other.svg +11 -11
  264. package/src/components/annex-upload-weijian/assets/pdf.svg +11 -11
  265. package/src/components/annex-upload-weijian/assets/ppt.svg +11 -11
  266. package/src/components/annex-upload-weijian/assets/preview.svg +4 -4
  267. package/src/components/annex-upload-weijian/assets/word.svg +11 -11
  268. package/src/components/annex-upload-weijian/assets/xls.svg +11 -11
  269. package/src/components/annex-upload-weijian/index.vue +772 -772
  270. package/src/components/annex-upload-weijian/modules/previewMp.vue +53 -53
  271. package/src/components/base-layout/index.vue +199 -199
  272. package/src/components/button.vue +14 -14
  273. package/src/components/layout/ApprovalCenterDrawer.vue +231 -231
  274. package/src/components/layout/ExportCenterDrawer.vue +164 -164
  275. package/src/components/layout/LayoutContainer.vue +542 -542
  276. package/src/components/layout/SysHeader.vue +90 -90
  277. package/src/components/layout/SysHeaderLeft.vue +114 -114
  278. package/src/components/layout/SysHeaderRight.vue +112 -112
  279. package/src/components/layout/SysHeaderTabs.vue +289 -289
  280. package/src/components/layout/components/avatar-component.vue +77 -77
  281. package/src/components/layout/components/dark-component.vue +82 -82
  282. package/src/components/layout/components/download-component.vue +49 -49
  283. package/src/components/layout/components/font-size-component.vue +66 -66
  284. package/src/components/layout/components/menu-position-component.vue +97 -97
  285. package/src/components/layout/components/message-component.vue +64 -64
  286. package/src/components/layout/components/todo-component.vue +55 -55
  287. package/src/components/layout/index.ts +34 -34
  288. package/src/components/layout/message/message-dictionary.ts +27 -27
  289. package/src/components/layout/message/message-icon//345/205/250/351/203/250/351/200/232/347/237/245-/350/223/235.svg +8 -8
  290. package/src/components/layout/message/message-icon//345/205/250/351/203/250/351/200/232/347/237/245-/351/273/221.svg +8 -8
  291. package/src/components/layout/message/message-icon//345/205/250/351/203/250/351/200/232/347/237/245.svg +8 -8
  292. package/src/components/layout/message/message-icon//345/205/254/345/221/212/351/200/232/347/237/245-/350/223/235.svg +12 -12
  293. package/src/components/layout/message/message-icon//345/205/254/345/221/212/351/200/232/347/237/245-/351/273/221.svg +12 -12
  294. package/src/components/layout/message/message-icon//345/205/254/345/221/212/351/200/232/347/237/245.svg +12 -12
  295. package/src/components/layout/message/message-icon//345/256/241/346/211/271/351/200/232/347/237/245-/350/223/235.svg +8 -8
  296. package/src/components/layout/message/message-icon//345/256/241/346/211/271/351/200/232/347/237/245-/351/273/221.svg +8 -8
  297. package/src/components/layout/message/message-icon//345/256/241/346/211/271/351/200/232/347/237/245.svg +8 -8
  298. package/src/components/layout/message/message-icon//345/257/274/345/207/272/351/200/232/347/237/245-/350/223/235.svg +13 -13
  299. package/src/components/layout/message/message-icon//345/257/274/345/207/272/351/200/232/347/237/245-/351/273/221.svg +13 -13
  300. package/src/components/layout/message/message-icon//345/257/274/345/207/272/351/200/232/347/237/245.svg +13 -13
  301. package/src/components/layout/message/message-icon//346/234/252/350/257/273/351/200/232/347/237/245-/350/223/235.svg +11 -11
  302. package/src/components/layout/message/message-icon//346/234/252/350/257/273/351/200/232/347/237/245-/351/273/221.svg +11 -11
  303. package/src/components/layout/message/message-icon//346/234/252/350/257/273/351/200/232/347/237/245.svg +11 -11
  304. package/src/components/layout/message/message-icon//347/251/272/347/212/266/346/200/201.svg +17 -17
  305. package/src/components/layout/message/message-icon//347/263/273/347/273/237/351/200/232/347/237/245-/350/223/235.svg +13 -13
  306. package/src/components/layout/message/message-icon//347/263/273/347/273/237/351/200/232/347/237/245-/351/273/221.svg +13 -13
  307. package/src/components/layout/message/message-icon//347/263/273/347/273/237/351/200/232/347/237/245.svg +13 -13
  308. package/src/components/layout/message/message-notification-drawer.vue +533 -533
  309. package/src/components/layout/personal-sign/ImageCropper.vue +187 -187
  310. package/src/components/layout/personal-sign/PersonalSignDialog.vue +658 -658
  311. package/src/components/layout/personalization-guide-dialog.vue +255 -255
  312. package/src/components/layout/plugin.ts +32 -32
  313. package/src/components/layout/sideMenu-global.scss +115 -115
  314. package/src/components/layout/sideMenu.scss +312 -312
  315. package/src/components/layout/sideMenu.vue +541 -541
  316. package/src/components/layout/sideMenuSonList.vue +185 -185
  317. package/src/components/layout/stores/index.ts +5 -5
  318. package/src/components/layout/stores/useLayoutConfigStore.ts +231 -231
  319. package/src/components/layout/stores/useLayoutMenuStore.ts +232 -232
  320. package/src/components/layout/stores/useLayoutNoticeStore.ts +77 -77
  321. package/src/components/layout/stores/useLayoutTabsStore.ts +367 -367
  322. package/src/components/layout/styles/icon.scss +72 -72
  323. package/src/components/layout/types.ts +98 -98
  324. package/src/components/layout/utils/redirect.ts +144 -144
  325. package/src/components/list-search-content/components/index.ts +11 -11
  326. package/src/components/list-search-content/components/search-date-range.vue +74 -74
  327. package/src/components/list-search-content/components/search-date-time-range.vue +48 -48
  328. package/src/components/list-search-content/components/search-date.vue +97 -97
  329. package/src/components/list-search-content/components/search-input-number.vue +59 -59
  330. package/src/components/list-search-content/components/search-input.vue +46 -46
  331. package/src/components/list-search-content/components/search-multiple_select.vue +69 -69
  332. package/src/components/list-search-content/components/search-select.vue +64 -64
  333. package/src/components/list-search-content/index.vue +803 -803
  334. package/src/components/list-search-content/modules/add-views-package-dialog.vue +147 -147
  335. package/src/components/list-search-content/modules/search-condition-area.vue +252 -252
  336. package/src/components/list-search-content/modules/views-package-manage-dialog.vue +265 -265
  337. package/src/components/switch.vue +16 -16
  338. package/src/components.ts +67 -67
  339. package/src/enum/system/StorageEnum.ts +4 -4
  340. package/src/global.d.ts +19 -19
  341. package/src/index.ts +4 -4
  342. package/src/init.ts +37 -37
  343. package/src/styles/common.scss +133 -133
  344. package/src/styles/flex-common.scss +117 -117
  345. package/src/styles/flexCommon.scss +72 -72
  346. package/src/styles/main.css +277 -277
  347. package/src/styles/router-page.scss +370 -370
  348. package/src/styles/style-set.ts +809 -809
  349. package/src/utils/HttpUtil.ts +427 -427
  350. package/src/utils/base.ts +47 -47
  351. package/src/utils/cell-style.ts +11 -11
  352. package/src/utils/crypto.ts +98 -98
  353. package/src/utils/dd-utils.ts +157 -157
  354. package/src/utils/debounce.ts +35 -35
  355. package/src/utils/debounceleading.ts +48 -48
  356. package/src/utils/download.ts +38 -38
  357. package/src/utils/exitLogin.ts +58 -58
  358. package/src/utils/filter-key.ts +4 -4
  359. package/src/utils/formatter-handler.ts +12 -12
  360. package/src/utils/get-dict.ts +26 -26
  361. package/src/utils/get-env.ts +36 -36
  362. package/src/utils/login-path.ts +35 -35
  363. package/src/utils/path-utils.ts +31 -31
  364. package/src/utils/permit-utils.ts +68 -68
  365. package/src/utils/router-utils.ts +321 -321
  366. package/src/utils/string-utils.ts +166 -166
  367. package/src/utils/style-set.ts +750 -750
  368. package/src/utils/throttle.ts +62 -62
  369. package/src/utils/transform-data.ts +31 -31
  370. package/src/utils/validate.ts +92 -92
  371. package/src/utils/ws.ts +218 -218
  372. package/src/utils/wujie-son.ts +50 -50
  373. package/src/utils.ts +24 -24
@@ -1,658 +1,658 @@
1
- <!--
2
- * @description: 个人签名弹窗
3
- * @fileName: PersonalSignDialog.vue
4
- -->
5
- <template>
6
- <el-dialog
7
- v-model="dialogVisible"
8
- title="个人签名"
9
- width="600"
10
- center
11
- show-close
12
- close-on-press-escape
13
- >
14
- <div class="personal-sign-content">
15
- <div v-if="!toSignStatus" class="signbox">
16
- <div v-if="isSign" class="haveSign">
17
- <div class="signImgBox">
18
- <el-image style="width: 133px; height: 100px" :src="nowBindUrlMsg" fit="contain" v-if="nowBind" />
19
- <!-- 未签名提示 -->
20
- <div v-else class="no-sign-tip">
21
- <svg class="tip-icon" viewBox="0 0 1024 1024" width="32" height="32">
22
- <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" fill="#d9d9d9"/>
23
- <path d="M464 336a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm32 120h64c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-64c-4.4 0-8-3.6-8-8V464c0-4.4 3.6-8 8-8z" fill="#d9d9d9"/>
24
- </svg>
25
- <span class="tip-text">当前还未进行签字</span>
26
- </div>
27
- </div>
28
- <el-button class="sign-btn" type="primary" @click="handleReSignClick">{{ nowBind ? "重签" : "去签字" }}</el-button>
29
- </div>
30
- <div v-else class="unHaveSign">
31
- <div class="unHaveSignUp">
32
- <div class="qrCodeImgBox">
33
- <QrcodeVue :size="143" :value="codeContent" class="order-print-big-qrcode" ref="QrCodeRef" />
34
- <!-- 已扫描状态覆盖层 -->
35
- <div v-if="ifScanned" class="qrcode-status-overlay scanned">
36
- <svg class="status-icon" viewBox="0 0 1024 1024" width="24" height="24">
37
- <path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" fill="#333333"/>
38
- </svg>
39
- <span class="status-text">已扫描</span>
40
- </div>
41
- <!-- 已过期状态覆盖层 -->
42
- <div v-if="ifExpired" class="qrcode-status-overlay expired" @click="handleReSignClick">
43
- <svg class="status-icon" viewBox="0 0 1024 1024" width="24" height="24">
44
- <path d="M960 416V192l-73.056 73.056a447.712 447.712 0 0 0-373.6-201.088C265.92 63.968 65.312 264.544 65.312 512S265.92 960.032 513.344 960.032a448.064 448.064 0 0 0 415.232-279.488 38.368 38.368 0 1 0-70.272-29.568 371.36 371.36 0 0 1-344.96 232.064c-205.408 0-371.36-165.984-371.36-371.04 0-205.088 165.952-371.04 371.36-371.04 108.896 0 206.752 47.2 274.464 122.016L736 314.016h224a32 32 0 0 0 32-32z" fill="#b0b0b0"/>
45
- </svg>
46
- <span class="status-text">二维码已过期,点击刷新</span>
47
- </div>
48
- </div>
49
- <div class="qrCodeBtnBox">
50
- <el-button type="primary" size="default" @click="handleReturnClick">取消</el-button>
51
- </div>
52
- </div>
53
- <div class="uploadContent">使用钉钉扫码签名或点击上传</div>
54
- <div class="uploadContent">为保证签名识别效果最佳,请选择纯白色背景、减少阴影的图片</div>
55
- <div class="upload-link">
56
- <a href="javascript:;" @click="triggerFileInput">
57
- 已有签名?点击上传
58
- </a>
59
- </div>
60
- <!-- 隐藏的 file input -->
61
- <input
62
- type="file"
63
- ref="fileInput"
64
- style="display: none;"
65
- accept=".jpg,.jpeg,.png,.bmp,.raw,.heif,.tiff,.gif"
66
- @change="handleFileChange"
67
- >
68
- </div>
69
- </div>
70
- </div>
71
- <template #footer>
72
- <div class="dialog-footer">
73
- <el-button @click="dialogVisible = false">取消</el-button>
74
- <el-button type="primary" @click="handleSignSave">保存</el-button>
75
- </div>
76
- </template>
77
- <!-- 图片裁剪弹框 -->
78
- <ImageCropper ref="imageCropperRef" @success="handleCropSuccess"></ImageCropper>
79
- </el-dialog>
80
- </template>
81
-
82
- <script lang="ts" setup>
83
- import { ref, watch } from 'vue'
84
- import { ElMessage } from 'element-plus'
85
- import QrcodeVue from 'qrcode.vue'
86
- import ImageCropper from './ImageCropper.vue'
87
- import FlowSignController from '../../../api/bms/flow/FlowSignController'
88
- import FileUploadController from '../../../api/bms/file/FileUploadController'
89
-
90
- const dialogVisible = ref<boolean>(false)
91
- const nowBind = ref<string>('')
92
- const nowBindUrlMsg = ref<string>('')
93
- const toSignStatus = ref<boolean>(false)
94
- const isSign = ref<boolean>(true)
95
- const currentImgId = ref<string>('')
96
-
97
- const codeContent = ref<string>('')
98
- const qrCodeUUID = ref<string>('')
99
- const ifScanned = ref<boolean>(false)
100
- const ifExpired = ref<boolean>(false)
101
- const qrCodeTimeRecord = ref<any>()
102
- const queryQrCodeTimeRecord = ref<any>()
103
-
104
- const fileInput = ref<HTMLInputElement>()
105
- const imageCropperRef = ref<any>()
106
-
107
- // 允许的图片格式
108
- const allowedTypes = ['image/jpeg','image/png','image/bmp','image/raw','image/heif','image/tiff','image/gif']
109
- const allowedExtensions = ['.jpg','.jpeg','.png','.bmp','.raw','.heif','.tiff','.gif']
110
- const maxSize = 5 * 1024 * 1024
111
-
112
- watch(nowBind, () => {
113
- isSign.value = true
114
- })
115
-
116
- watch(dialogVisible, (newVal) => {
117
- if (newVal === false) {
118
- dialogClose()
119
- }
120
- })
121
-
122
- const pageId = ref<string>('01-01-04-001')
123
-
124
- const uploadOss = async (file: any) => {
125
- FileUploadController.upload(file, pageId.value).then((data: any) => {
126
- if (data.code === 200 && data.res) {
127
- nowBind.value = data.res.url
128
- nowBindUrlMsg.value = data.res.url
129
- currentImgId.value = data.res.fileId
130
- isSign.value = true
131
- handleReturnClick()
132
- } else {
133
- ElMessage.error("数据发生错误,请重新上传!")
134
- }
135
- }).catch((err) => {
136
- ElMessage.error("oss上传发生错误,请重试!")
137
- console.error(err)
138
- })
139
- }
140
-
141
- const handleReSignClick = async () => {
142
- generateQrCode()
143
- await updateQrCodeStatus("unscanned")
144
- startQueryStatus()
145
- isSign.value = false
146
- }
147
-
148
- const handleReturnClick = async () => {
149
- await updateQrCodeStatus("expired")
150
- clearInterval(qrCodeTimeRecord.value)
151
- clearInterval(queryQrCodeTimeRecord.value)
152
- isSign.value = true
153
- }
154
-
155
- // 触发文件选择
156
- const triggerFileInput = () => {
157
- if (!fileInput.value) return
158
- fileInput.value.click()
159
- }
160
-
161
- const handleFileChange = async (event: any) => {
162
- const file = event.target.files[0]
163
- if (!file) return
164
-
165
- if (!allowedTypes.includes(file.type)) {
166
- ElMessage.error(`仅支持 ${allowedExtensions.join(', ')} 格式`)
167
- resetFileInput()
168
- return
169
- }
170
- if (file.size > maxSize) {
171
- const maxSizeMB = (maxSize / (1024 * 1024)).toFixed(1)
172
- ElMessage.error(`文件大小不能超过 ${maxSizeMB}MB`)
173
- resetFileInput()
174
- return
175
- }
176
-
177
- try {
178
- const processedBlob = await extractSignatureImageTransparent(file)
179
- imageCropperRef.value?.open(processedBlob)
180
- } catch (e) {
181
- console.error(e)
182
- ElMessage.error('签名图片处理失败')
183
- } finally {
184
- resetFileInput()
185
- }
186
- }
187
-
188
- const resetFileInput = () => {
189
- if (fileInput.value) {
190
- fileInput.value.value = ''
191
- }
192
- }
193
-
194
- // 裁剪成功回调
195
- const handleCropSuccess = async (croppedBlob: Blob) => {
196
- try {
197
- const croppedFile = new File(
198
- [croppedBlob],
199
- '签名文件.png',
200
- { type: 'image/png' }
201
- )
202
- await uploadOss(croppedFile)
203
- } catch (e) {
204
- console.error(e)
205
- ElMessage.error('签名图片上传失败')
206
- }
207
- }
208
-
209
- const generateUUID = () => {
210
- const timestamp = performance.now().toString(36)
211
- const random = Math.random().toString(36).slice(2)
212
- const random2 = Math.random().toString(36).slice(2)
213
- return `${timestamp}-${random}-${random2}`.replace(/\./g, '')
214
- }
215
-
216
- const generateQrCode = () => {
217
- ifScanned.value = false
218
- ifExpired.value = false
219
- qrCodeUUID.value = generateUUID()
220
- const urlStr = `${window.location.origin}/mobile/login/index.html?host=${localStorage.getItem('TenantHost')}&url=${'/full-screen-sign-process'}?qrCodeUUID=${qrCodeUUID.value}`
221
- codeContent.value = urlStr
222
- }
223
-
224
- const startPolling = () => {
225
- qrCodeTimeRecord.value = setInterval(async () => {
226
- try {
227
- const data = await FlowSignController.startPolling(qrCodeUUID.value)
228
- if (data.code === 200 && data.res) {
229
- clearInterval(qrCodeTimeRecord.value)
230
- nowBind.value = data.res.url
231
- nowBindUrlMsg.value = data.res.url
232
- currentImgId.value = data.res.fileId
233
- }
234
- } catch (e) {
235
- // ignore
236
- }
237
- }, 2000)
238
- }
239
-
240
- const handleSignSave = () => {
241
- FlowSignController.personalSignSave(currentImgId.value).then(data => {
242
- if (data.code === 200) {
243
- ElMessage.success('个人签名上传成功')
244
- }
245
- }).catch(() => {
246
- dialogVisible.value = false
247
- }).finally(() => {
248
- dialogVisible.value = false
249
- })
250
- }
251
-
252
- const getPersonalSign = async () => {
253
- await FlowSignController.gerPersonalSign().then(data => {
254
- if (data.code === 200 && data.res) {
255
- nowBind.value = data.res.url
256
- nowBindUrlMsg.value = data.res.url
257
- }
258
- })
259
- }
260
-
261
- const startQueryStatus = () => {
262
- queryQrCodeTimeRecord.value = setInterval(() => {
263
- FlowSignController.queryQrCodeStatus(qrCodeUUID.value).then(data => {
264
- if (data.code === 200 && data.res === "scanned") {
265
- clearInterval(queryQrCodeTimeRecord.value)
266
- ifScanned.value = true
267
- startPolling()
268
- } else if (data.code === 200 && (data.res === null || data.res === "expired")) {
269
- clearInterval(queryQrCodeTimeRecord.value)
270
- ifExpired.value = true
271
- ElMessage.error("二维码已过期,请刷新")
272
- }
273
- })
274
- }, 2000)
275
- }
276
-
277
- const updateQrCodeStatus = (status: string) => {
278
- if (status === 'scanned') {
279
- ifScanned.value = true
280
- } else if (status === 'expired') {
281
- ifExpired.value = true
282
- }
283
- FlowSignController.updateQrCodeStatus(status, qrCodeUUID.value).then(data => {
284
- if (data.code === 200 && data.res) {
285
- console.log("二维码状态更新成功")
286
- } else if (data.code === 200 && !data.res) {
287
- ElMessage.error(data.msg || "二维码失效请刷新后重试")
288
- }
289
- })
290
- }
291
-
292
- const dialogClose = () => {
293
- clearInterval(qrCodeTimeRecord.value)
294
- clearInterval(queryQrCodeTimeRecord.value)
295
- ifScanned.value = false
296
- ifExpired.value = false
297
- isSign.value = true
298
- }
299
-
300
- /** 打开弹窗 */
301
- const open = () => {
302
- dialogVisible.value = true
303
- getPersonalSign()
304
- }
305
-
306
- // ====== 图片处理工具函数 ======
307
-
308
- const extractSignatureImageTransparent = (file: File): Promise<Blob> => {
309
- return new Promise((resolve, reject) => {
310
- const img = new Image()
311
- img.onload = () => {
312
- const canvas = document.createElement('canvas')
313
- const ctx = canvas.getContext('2d')!
314
-
315
- canvas.width = img.width
316
- canvas.height = img.height
317
- ctx.drawImage(img, 0, 0)
318
-
319
- const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
320
- const data = imageData.data
321
-
322
- const grays: number[] = []
323
- let min = 255, max = 0
324
- for (let i = 0; i < data.length; i += 4) {
325
- const gray = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2]
326
- grays.push(gray)
327
- min = Math.min(min, gray)
328
- max = Math.max(max, gray)
329
- }
330
-
331
- const threshold = min + (max - min) * 0.35
332
-
333
- for (let i = 0; i < data.length; i += 4) {
334
- const gray = grays[i / 4]
335
- if (gray > threshold) {
336
- data[i + 3] = 0
337
- } else {
338
- data[i] = data[i + 1] = data[i + 2] = 0
339
- data[i + 3] = 255
340
- }
341
- }
342
-
343
- enhanceContrast(data)
344
- for (let i = 0; i < 2; i++) {
345
- dilateAlpha(data, canvas.width, canvas.height, 1)
346
- fillGapsAlpha(data, canvas.width, canvas.height)
347
- }
348
- removeHorizontalLines(data, canvas.width, canvas.height)
349
- ctx.putImageData(imageData, 0, 0)
350
-
351
- canvas.toBlob(
352
- blob => blob ? resolve(blob) : reject(),
353
- 'image/png',
354
- 1
355
- )
356
- }
357
-
358
- img.onerror = reject
359
- img.src = URL.createObjectURL(file)
360
- })
361
- }
362
-
363
- const dilateAlpha = (data: Uint8ClampedArray, width: number, height: number, radius = 1) => {
364
- const copy = new Uint8ClampedArray(data)
365
- const idx = (x: number, y: number) => (y * width + x) * 4
366
-
367
- for (let y = radius; y < height - radius; y++) {
368
- for (let x = radius; x < width - radius; x++) {
369
- const center = idx(x, y)
370
- if (copy[center + 3] === 255) continue
371
-
372
- let hasOpaqueNeighbor = false
373
- for (let dy = -radius; dy <= radius && !hasOpaqueNeighbor; dy++) {
374
- for (let dx = -radius; dx <= radius; dx++) {
375
- const i = idx(x + dx, y + dy)
376
- if (copy[i + 3] === 255) {
377
- hasOpaqueNeighbor = true
378
- break
379
- }
380
- }
381
- }
382
-
383
- if (hasOpaqueNeighbor) {
384
- data[center + 3] = 255
385
- for (let c = 0; c < 3; c++) {
386
- data[center + c] = copy[center + c]
387
- }
388
- }
389
- }
390
- }
391
- }
392
-
393
- const fillGapsAlpha = (data: Uint8ClampedArray, width: number, height: number) => {
394
- const copy = new Uint8ClampedArray(data)
395
- const idx = (x: number, y: number) => (y * width + x) * 4
396
-
397
- for (let y = 1; y < height - 1; y++) {
398
- for (let x = 1; x < width - 1; x++) {
399
- const i = idx(x, y)
400
- if (copy[i + 3] === 255) continue
401
-
402
- let opaqueCount = 0
403
- let r = [0, 0, 0]
404
- for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {
405
- const neighbor = idx(x + dx, y + dy)
406
- if (copy[neighbor + 3] === 255) {
407
- opaqueCount++
408
- for (let c = 0; c < 3; c++) r[c] += copy[neighbor + c]
409
- }
410
- }
411
-
412
- if (opaqueCount >= 2) {
413
- data[i + 3] = 255
414
- for (let c = 0; c < 3; c++) data[i + c] = Math.round(r[c] / opaqueCount)
415
- }
416
- }
417
- }
418
- }
419
-
420
- const enhanceContrast = (data: Uint8ClampedArray) => {
421
- for (let i = 0; i < data.length; i += 4) {
422
- if (data[i + 3] === 255) {
423
- for (let c = 0; c < 3; c++) {
424
- const val = data[i + c] / 255
425
- data[i + c] = Math.min(255, Math.pow(val, 0.4) * 255)
426
- }
427
- }
428
- }
429
- }
430
-
431
- const removeHorizontalLines = (data: Uint8ClampedArray, width: number, height: number) => {
432
- const threshold = width * 0.7
433
- const idx = (x: number, y: number) => (y * width + x) * 4
434
-
435
- for (let y = 0; y < height; y++) {
436
- let count = 0
437
- let startX = 0
438
- for (let x = 0; x < width; x++) {
439
- if (data[idx(x, y) + 3] === 255) {
440
- if (count === 0) startX = x
441
- count++
442
- } else {
443
- if (count > threshold) {
444
- for (let i = startX; i < x; i++) {
445
- data[idx(i, y) + 3] = 0
446
- }
447
- }
448
- count = 0
449
- }
450
- }
451
- if (count > threshold) {
452
- for (let i = startX; i < width; i++) {
453
- data[idx(i, y) + 3] = 0
454
- }
455
- }
456
- }
457
- }
458
-
459
- defineExpose({ open })
460
- </script>
461
-
462
- <style scoped lang="scss">
463
- .personal-sign-content {
464
- width: 100%;
465
- min-height: 220px;
466
- }
467
-
468
- .signbox {
469
- margin-top: 8px;
470
- width: 100%;
471
- display: flex;
472
- flex-direction: row;
473
- justify-content: flex-start;
474
- align-items: flex-start;
475
- flex-wrap: wrap;
476
- }
477
-
478
- .haveSign {
479
- display: flex;
480
- justify-content: center;
481
- align-items: flex-start;
482
- width: 100%;
483
- margin: 0 auto;
484
- min-height: 197px;
485
- text-align: center;
486
- position: relative;
487
- }
488
-
489
- .sign-btn {
490
- position: absolute;
491
- right: calc(50% - 145px);
492
- top: 72.5px;
493
- margin-left: 16px;
494
- }
495
-
496
- .unHaveSign {
497
- width: 100%;
498
- margin: 0 auto;
499
- min-height: 197px;
500
- text-align: center;
501
- position: relative;
502
-
503
- .uploadContent {
504
- margin-bottom: 5px;
505
- font-size: 11.5px;
506
- }
507
-
508
- .upload-link {
509
- font-weight: 500;
510
- font-size: 10px;
511
- color: #409eff;
512
- cursor: pointer;
513
- }
514
- }
515
-
516
- .signImgBox, .qrCodeImgBox {
517
- box-sizing: border-box;
518
- width: 145px;
519
- height: 145px;
520
- overflow: hidden;
521
- margin: 0 auto 15px;
522
- position: relative;
523
- }
524
-
525
- .signImgBox {
526
- width: 133px;
527
- height: 100px;
528
- margin-top: 50px;
529
- display: flex;
530
- align-items: center;
531
- justify-content: center;
532
-
533
- .no-sign-tip {
534
- display: flex;
535
- flex-direction: column;
536
- align-items: center;
537
- justify-content: center;
538
- width: 100%;
539
- height: 100%;
540
- background: #fafafa;
541
- border-radius: 4px;
542
- gap: 8px;
543
-
544
- .tip-icon {
545
- flex-shrink: 0;
546
- }
547
-
548
- .tip-text {
549
- font-size: 12px;
550
- color: #999;
551
- text-align: center;
552
- line-height: 1.4;
553
- padding: 0 8px;
554
- }
555
- }
556
- }
557
-
558
- .unHaveSignUp {
559
- display: flex;
560
- justify-content: center;
561
- position: relative;
562
-
563
- .qrCodeBtnBox {
564
- position: absolute;
565
- right: calc(50% - 145px);
566
- top: 72.5px;
567
- margin-left: 16px;
568
- display: flex;
569
- flex-direction: column;
570
- gap: 8px;
571
-
572
- .el-button + .el-button {
573
- margin-left: 0 !important;
574
- }
575
-
576
- .el-button {
577
- min-width: 60px;
578
- }
579
- }
580
-
581
- .uploadContent {
582
- margin-top: 8px;
583
- }
584
- }
585
-
586
- // 二维码状态覆盖层样式
587
- .qrcode-status-overlay {
588
- position: absolute;
589
- top: 0;
590
- left: 0;
591
- right: 0;
592
- bottom: 0;
593
- display: flex;
594
- flex-direction: column;
595
- align-items: center;
596
- justify-content: center;
597
- gap: 6px;
598
- animation: fadeIn 0.25s ease-out;
599
- z-index: 10;
600
-
601
- &::before {
602
- content: '';
603
- position: absolute;
604
- top: 0;
605
- left: 0;
606
- right: 0;
607
- bottom: 0;
608
- background-image:
609
- repeating-linear-gradient(0deg, transparent, transparent 1px, currentColor 1px, currentColor 2px),
610
- repeating-linear-gradient(90deg, transparent, transparent 1px, currentColor 1px, currentColor 2px);
611
- opacity: 0.25;
612
- z-index: 1;
613
- }
614
-
615
- .status-icon {
616
- flex-shrink: 0;
617
- position: relative;
618
- z-index: 3;
619
- }
620
-
621
- .status-text {
622
- font-size: 12px;
623
- font-weight: 400;
624
- letter-spacing: 0.5px;
625
- line-height: 1;
626
- position: relative;
627
- z-index: 3;
628
- }
629
-
630
- &.scanned {
631
- background: rgba(255, 255, 255, 0.96);
632
- color: rgba(100, 100, 100, 0.3);
633
-
634
- .status-text {
635
- color: #333333;
636
- }
637
- }
638
-
639
- &.expired {
640
- background: rgba(40, 40, 40, 0.92);
641
- color: rgba(180, 180, 180, 0.3);
642
- cursor: pointer;
643
-
644
- .status-text {
645
- color: #e0e0e0;
646
- }
647
-
648
- .status-icon path {
649
- fill: #b0b0b0;
650
- }
651
- }
652
- }
653
-
654
- @keyframes fadeIn {
655
- from { opacity: 0; }
656
- to { opacity: 1; }
657
- }
658
- </style>
1
+ <!--
2
+ * @description: 个人签名弹窗
3
+ * @fileName: PersonalSignDialog.vue
4
+ -->
5
+ <template>
6
+ <el-dialog
7
+ v-model="dialogVisible"
8
+ title="个人签名"
9
+ width="600"
10
+ center
11
+ show-close
12
+ close-on-press-escape
13
+ >
14
+ <div class="personal-sign-content">
15
+ <div v-if="!toSignStatus" class="signbox">
16
+ <div v-if="isSign" class="haveSign">
17
+ <div class="signImgBox">
18
+ <el-image style="width: 133px; height: 100px" :src="nowBindUrlMsg" fit="contain" v-if="nowBind" />
19
+ <!-- 未签名提示 -->
20
+ <div v-else class="no-sign-tip">
21
+ <svg class="tip-icon" viewBox="0 0 1024 1024" width="32" height="32">
22
+ <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" fill="#d9d9d9"/>
23
+ <path d="M464 336a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm32 120h64c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-64c-4.4 0-8-3.6-8-8V464c0-4.4 3.6-8 8-8z" fill="#d9d9d9"/>
24
+ </svg>
25
+ <span class="tip-text">当前还未进行签字</span>
26
+ </div>
27
+ </div>
28
+ <el-button class="sign-btn" type="primary" @click="handleReSignClick">{{ nowBind ? "重签" : "去签字" }}</el-button>
29
+ </div>
30
+ <div v-else class="unHaveSign">
31
+ <div class="unHaveSignUp">
32
+ <div class="qrCodeImgBox">
33
+ <QrcodeVue :size="143" :value="codeContent" class="order-print-big-qrcode" ref="QrCodeRef" />
34
+ <!-- 已扫描状态覆盖层 -->
35
+ <div v-if="ifScanned" class="qrcode-status-overlay scanned">
36
+ <svg class="status-icon" viewBox="0 0 1024 1024" width="24" height="24">
37
+ <path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" fill="#333333"/>
38
+ </svg>
39
+ <span class="status-text">已扫描</span>
40
+ </div>
41
+ <!-- 已过期状态覆盖层 -->
42
+ <div v-if="ifExpired" class="qrcode-status-overlay expired" @click="handleReSignClick">
43
+ <svg class="status-icon" viewBox="0 0 1024 1024" width="24" height="24">
44
+ <path d="M960 416V192l-73.056 73.056a447.712 447.712 0 0 0-373.6-201.088C265.92 63.968 65.312 264.544 65.312 512S265.92 960.032 513.344 960.032a448.064 448.064 0 0 0 415.232-279.488 38.368 38.368 0 1 0-70.272-29.568 371.36 371.36 0 0 1-344.96 232.064c-205.408 0-371.36-165.984-371.36-371.04 0-205.088 165.952-371.04 371.36-371.04 108.896 0 206.752 47.2 274.464 122.016L736 314.016h224a32 32 0 0 0 32-32z" fill="#b0b0b0"/>
45
+ </svg>
46
+ <span class="status-text">二维码已过期,点击刷新</span>
47
+ </div>
48
+ </div>
49
+ <div class="qrCodeBtnBox">
50
+ <el-button type="primary" size="default" @click="handleReturnClick">取消</el-button>
51
+ </div>
52
+ </div>
53
+ <div class="uploadContent">使用钉钉扫码签名或点击上传</div>
54
+ <div class="uploadContent">为保证签名识别效果最佳,请选择纯白色背景、减少阴影的图片</div>
55
+ <div class="upload-link">
56
+ <a href="javascript:;" @click="triggerFileInput">
57
+ 已有签名?点击上传
58
+ </a>
59
+ </div>
60
+ <!-- 隐藏的 file input -->
61
+ <input
62
+ type="file"
63
+ ref="fileInput"
64
+ style="display: none;"
65
+ accept=".jpg,.jpeg,.png,.bmp,.raw,.heif,.tiff,.gif"
66
+ @change="handleFileChange"
67
+ >
68
+ </div>
69
+ </div>
70
+ </div>
71
+ <template #footer>
72
+ <div class="dialog-footer">
73
+ <el-button @click="dialogVisible = false">取消</el-button>
74
+ <el-button type="primary" @click="handleSignSave">保存</el-button>
75
+ </div>
76
+ </template>
77
+ <!-- 图片裁剪弹框 -->
78
+ <ImageCropper ref="imageCropperRef" @success="handleCropSuccess"></ImageCropper>
79
+ </el-dialog>
80
+ </template>
81
+
82
+ <script lang="ts" setup>
83
+ import { ref, watch } from 'vue'
84
+ import { ElMessage } from 'element-plus'
85
+ import QrcodeVue from 'qrcode.vue'
86
+ import ImageCropper from './ImageCropper.vue'
87
+ import FlowSignController from '../../../api/bms/flow/FlowSignController'
88
+ import FileUploadController from '../../../api/bms/file/FileUploadController'
89
+
90
+ const dialogVisible = ref<boolean>(false)
91
+ const nowBind = ref<string>('')
92
+ const nowBindUrlMsg = ref<string>('')
93
+ const toSignStatus = ref<boolean>(false)
94
+ const isSign = ref<boolean>(true)
95
+ const currentImgId = ref<string>('')
96
+
97
+ const codeContent = ref<string>('')
98
+ const qrCodeUUID = ref<string>('')
99
+ const ifScanned = ref<boolean>(false)
100
+ const ifExpired = ref<boolean>(false)
101
+ const qrCodeTimeRecord = ref<any>()
102
+ const queryQrCodeTimeRecord = ref<any>()
103
+
104
+ const fileInput = ref<HTMLInputElement>()
105
+ const imageCropperRef = ref<any>()
106
+
107
+ // 允许的图片格式
108
+ const allowedTypes = ['image/jpeg','image/png','image/bmp','image/raw','image/heif','image/tiff','image/gif']
109
+ const allowedExtensions = ['.jpg','.jpeg','.png','.bmp','.raw','.heif','.tiff','.gif']
110
+ const maxSize = 5 * 1024 * 1024
111
+
112
+ watch(nowBind, () => {
113
+ isSign.value = true
114
+ })
115
+
116
+ watch(dialogVisible, (newVal) => {
117
+ if (newVal === false) {
118
+ dialogClose()
119
+ }
120
+ })
121
+
122
+ const pageId = ref<string>('01-01-04-001')
123
+
124
+ const uploadOss = async (file: any) => {
125
+ FileUploadController.upload(file, pageId.value).then((data: any) => {
126
+ if (data.code === 200 && data.res) {
127
+ nowBind.value = data.res.url
128
+ nowBindUrlMsg.value = data.res.url
129
+ currentImgId.value = data.res.fileId
130
+ isSign.value = true
131
+ handleReturnClick()
132
+ } else {
133
+ ElMessage.error("数据发生错误,请重新上传!")
134
+ }
135
+ }).catch((err) => {
136
+ ElMessage.error("oss上传发生错误,请重试!")
137
+ console.error(err)
138
+ })
139
+ }
140
+
141
+ const handleReSignClick = async () => {
142
+ generateQrCode()
143
+ await updateQrCodeStatus("unscanned")
144
+ startQueryStatus()
145
+ isSign.value = false
146
+ }
147
+
148
+ const handleReturnClick = async () => {
149
+ await updateQrCodeStatus("expired")
150
+ clearInterval(qrCodeTimeRecord.value)
151
+ clearInterval(queryQrCodeTimeRecord.value)
152
+ isSign.value = true
153
+ }
154
+
155
+ // 触发文件选择
156
+ const triggerFileInput = () => {
157
+ if (!fileInput.value) return
158
+ fileInput.value.click()
159
+ }
160
+
161
+ const handleFileChange = async (event: any) => {
162
+ const file = event.target.files[0]
163
+ if (!file) return
164
+
165
+ if (!allowedTypes.includes(file.type)) {
166
+ ElMessage.error(`仅支持 ${allowedExtensions.join(', ')} 格式`)
167
+ resetFileInput()
168
+ return
169
+ }
170
+ if (file.size > maxSize) {
171
+ const maxSizeMB = (maxSize / (1024 * 1024)).toFixed(1)
172
+ ElMessage.error(`文件大小不能超过 ${maxSizeMB}MB`)
173
+ resetFileInput()
174
+ return
175
+ }
176
+
177
+ try {
178
+ const processedBlob = await extractSignatureImageTransparent(file)
179
+ imageCropperRef.value?.open(processedBlob)
180
+ } catch (e) {
181
+ console.error(e)
182
+ ElMessage.error('签名图片处理失败')
183
+ } finally {
184
+ resetFileInput()
185
+ }
186
+ }
187
+
188
+ const resetFileInput = () => {
189
+ if (fileInput.value) {
190
+ fileInput.value.value = ''
191
+ }
192
+ }
193
+
194
+ // 裁剪成功回调
195
+ const handleCropSuccess = async (croppedBlob: Blob) => {
196
+ try {
197
+ const croppedFile = new File(
198
+ [croppedBlob],
199
+ '签名文件.png',
200
+ { type: 'image/png' }
201
+ )
202
+ await uploadOss(croppedFile)
203
+ } catch (e) {
204
+ console.error(e)
205
+ ElMessage.error('签名图片上传失败')
206
+ }
207
+ }
208
+
209
+ const generateUUID = () => {
210
+ const timestamp = performance.now().toString(36)
211
+ const random = Math.random().toString(36).slice(2)
212
+ const random2 = Math.random().toString(36).slice(2)
213
+ return `${timestamp}-${random}-${random2}`.replace(/\./g, '')
214
+ }
215
+
216
+ const generateQrCode = () => {
217
+ ifScanned.value = false
218
+ ifExpired.value = false
219
+ qrCodeUUID.value = generateUUID()
220
+ const urlStr = `${window.location.origin}/mobile/login/index.html?host=${localStorage.getItem('TenantHost')}&url=${'/full-screen-sign-process'}?qrCodeUUID=${qrCodeUUID.value}`
221
+ codeContent.value = urlStr
222
+ }
223
+
224
+ const startPolling = () => {
225
+ qrCodeTimeRecord.value = setInterval(async () => {
226
+ try {
227
+ const data = await FlowSignController.startPolling(qrCodeUUID.value)
228
+ if (data.code === 200 && data.res) {
229
+ clearInterval(qrCodeTimeRecord.value)
230
+ nowBind.value = data.res.url
231
+ nowBindUrlMsg.value = data.res.url
232
+ currentImgId.value = data.res.fileId
233
+ }
234
+ } catch (e) {
235
+ // ignore
236
+ }
237
+ }, 2000)
238
+ }
239
+
240
+ const handleSignSave = () => {
241
+ FlowSignController.personalSignSave(currentImgId.value).then(data => {
242
+ if (data.code === 200) {
243
+ ElMessage.success('个人签名上传成功')
244
+ }
245
+ }).catch(() => {
246
+ dialogVisible.value = false
247
+ }).finally(() => {
248
+ dialogVisible.value = false
249
+ })
250
+ }
251
+
252
+ const getPersonalSign = async () => {
253
+ await FlowSignController.gerPersonalSign().then(data => {
254
+ if (data.code === 200 && data.res) {
255
+ nowBind.value = data.res.url
256
+ nowBindUrlMsg.value = data.res.url
257
+ }
258
+ })
259
+ }
260
+
261
+ const startQueryStatus = () => {
262
+ queryQrCodeTimeRecord.value = setInterval(() => {
263
+ FlowSignController.queryQrCodeStatus(qrCodeUUID.value).then(data => {
264
+ if (data.code === 200 && data.res === "scanned") {
265
+ clearInterval(queryQrCodeTimeRecord.value)
266
+ ifScanned.value = true
267
+ startPolling()
268
+ } else if (data.code === 200 && (data.res === null || data.res === "expired")) {
269
+ clearInterval(queryQrCodeTimeRecord.value)
270
+ ifExpired.value = true
271
+ ElMessage.error("二维码已过期,请刷新")
272
+ }
273
+ })
274
+ }, 2000)
275
+ }
276
+
277
+ const updateQrCodeStatus = (status: string) => {
278
+ if (status === 'scanned') {
279
+ ifScanned.value = true
280
+ } else if (status === 'expired') {
281
+ ifExpired.value = true
282
+ }
283
+ FlowSignController.updateQrCodeStatus(status, qrCodeUUID.value).then(data => {
284
+ if (data.code === 200 && data.res) {
285
+ console.log("二维码状态更新成功")
286
+ } else if (data.code === 200 && !data.res) {
287
+ ElMessage.error(data.msg || "二维码失效请刷新后重试")
288
+ }
289
+ })
290
+ }
291
+
292
+ const dialogClose = () => {
293
+ clearInterval(qrCodeTimeRecord.value)
294
+ clearInterval(queryQrCodeTimeRecord.value)
295
+ ifScanned.value = false
296
+ ifExpired.value = false
297
+ isSign.value = true
298
+ }
299
+
300
+ /** 打开弹窗 */
301
+ const open = () => {
302
+ dialogVisible.value = true
303
+ getPersonalSign()
304
+ }
305
+
306
+ // ====== 图片处理工具函数 ======
307
+
308
+ const extractSignatureImageTransparent = (file: File): Promise<Blob> => {
309
+ return new Promise((resolve, reject) => {
310
+ const img = new Image()
311
+ img.onload = () => {
312
+ const canvas = document.createElement('canvas')
313
+ const ctx = canvas.getContext('2d')!
314
+
315
+ canvas.width = img.width
316
+ canvas.height = img.height
317
+ ctx.drawImage(img, 0, 0)
318
+
319
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
320
+ const data = imageData.data
321
+
322
+ const grays: number[] = []
323
+ let min = 255, max = 0
324
+ for (let i = 0; i < data.length; i += 4) {
325
+ const gray = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2]
326
+ grays.push(gray)
327
+ min = Math.min(min, gray)
328
+ max = Math.max(max, gray)
329
+ }
330
+
331
+ const threshold = min + (max - min) * 0.35
332
+
333
+ for (let i = 0; i < data.length; i += 4) {
334
+ const gray = grays[i / 4]
335
+ if (gray > threshold) {
336
+ data[i + 3] = 0
337
+ } else {
338
+ data[i] = data[i + 1] = data[i + 2] = 0
339
+ data[i + 3] = 255
340
+ }
341
+ }
342
+
343
+ enhanceContrast(data)
344
+ for (let i = 0; i < 2; i++) {
345
+ dilateAlpha(data, canvas.width, canvas.height, 1)
346
+ fillGapsAlpha(data, canvas.width, canvas.height)
347
+ }
348
+ removeHorizontalLines(data, canvas.width, canvas.height)
349
+ ctx.putImageData(imageData, 0, 0)
350
+
351
+ canvas.toBlob(
352
+ blob => blob ? resolve(blob) : reject(),
353
+ 'image/png',
354
+ 1
355
+ )
356
+ }
357
+
358
+ img.onerror = reject
359
+ img.src = URL.createObjectURL(file)
360
+ })
361
+ }
362
+
363
+ const dilateAlpha = (data: Uint8ClampedArray, width: number, height: number, radius = 1) => {
364
+ const copy = new Uint8ClampedArray(data)
365
+ const idx = (x: number, y: number) => (y * width + x) * 4
366
+
367
+ for (let y = radius; y < height - radius; y++) {
368
+ for (let x = radius; x < width - radius; x++) {
369
+ const center = idx(x, y)
370
+ if (copy[center + 3] === 255) continue
371
+
372
+ let hasOpaqueNeighbor = false
373
+ for (let dy = -radius; dy <= radius && !hasOpaqueNeighbor; dy++) {
374
+ for (let dx = -radius; dx <= radius; dx++) {
375
+ const i = idx(x + dx, y + dy)
376
+ if (copy[i + 3] === 255) {
377
+ hasOpaqueNeighbor = true
378
+ break
379
+ }
380
+ }
381
+ }
382
+
383
+ if (hasOpaqueNeighbor) {
384
+ data[center + 3] = 255
385
+ for (let c = 0; c < 3; c++) {
386
+ data[center + c] = copy[center + c]
387
+ }
388
+ }
389
+ }
390
+ }
391
+ }
392
+
393
+ const fillGapsAlpha = (data: Uint8ClampedArray, width: number, height: number) => {
394
+ const copy = new Uint8ClampedArray(data)
395
+ const idx = (x: number, y: number) => (y * width + x) * 4
396
+
397
+ for (let y = 1; y < height - 1; y++) {
398
+ for (let x = 1; x < width - 1; x++) {
399
+ const i = idx(x, y)
400
+ if (copy[i + 3] === 255) continue
401
+
402
+ let opaqueCount = 0
403
+ let r = [0, 0, 0]
404
+ for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {
405
+ const neighbor = idx(x + dx, y + dy)
406
+ if (copy[neighbor + 3] === 255) {
407
+ opaqueCount++
408
+ for (let c = 0; c < 3; c++) r[c] += copy[neighbor + c]
409
+ }
410
+ }
411
+
412
+ if (opaqueCount >= 2) {
413
+ data[i + 3] = 255
414
+ for (let c = 0; c < 3; c++) data[i + c] = Math.round(r[c] / opaqueCount)
415
+ }
416
+ }
417
+ }
418
+ }
419
+
420
+ const enhanceContrast = (data: Uint8ClampedArray) => {
421
+ for (let i = 0; i < data.length; i += 4) {
422
+ if (data[i + 3] === 255) {
423
+ for (let c = 0; c < 3; c++) {
424
+ const val = data[i + c] / 255
425
+ data[i + c] = Math.min(255, Math.pow(val, 0.4) * 255)
426
+ }
427
+ }
428
+ }
429
+ }
430
+
431
+ const removeHorizontalLines = (data: Uint8ClampedArray, width: number, height: number) => {
432
+ const threshold = width * 0.7
433
+ const idx = (x: number, y: number) => (y * width + x) * 4
434
+
435
+ for (let y = 0; y < height; y++) {
436
+ let count = 0
437
+ let startX = 0
438
+ for (let x = 0; x < width; x++) {
439
+ if (data[idx(x, y) + 3] === 255) {
440
+ if (count === 0) startX = x
441
+ count++
442
+ } else {
443
+ if (count > threshold) {
444
+ for (let i = startX; i < x; i++) {
445
+ data[idx(i, y) + 3] = 0
446
+ }
447
+ }
448
+ count = 0
449
+ }
450
+ }
451
+ if (count > threshold) {
452
+ for (let i = startX; i < width; i++) {
453
+ data[idx(i, y) + 3] = 0
454
+ }
455
+ }
456
+ }
457
+ }
458
+
459
+ defineExpose({ open })
460
+ </script>
461
+
462
+ <style scoped lang="scss">
463
+ .personal-sign-content {
464
+ width: 100%;
465
+ min-height: 220px;
466
+ }
467
+
468
+ .signbox {
469
+ margin-top: 8px;
470
+ width: 100%;
471
+ display: flex;
472
+ flex-direction: row;
473
+ justify-content: flex-start;
474
+ align-items: flex-start;
475
+ flex-wrap: wrap;
476
+ }
477
+
478
+ .haveSign {
479
+ display: flex;
480
+ justify-content: center;
481
+ align-items: flex-start;
482
+ width: 100%;
483
+ margin: 0 auto;
484
+ min-height: 197px;
485
+ text-align: center;
486
+ position: relative;
487
+ }
488
+
489
+ .sign-btn {
490
+ position: absolute;
491
+ right: calc(50% - 145px);
492
+ top: 72.5px;
493
+ margin-left: 16px;
494
+ }
495
+
496
+ .unHaveSign {
497
+ width: 100%;
498
+ margin: 0 auto;
499
+ min-height: 197px;
500
+ text-align: center;
501
+ position: relative;
502
+
503
+ .uploadContent {
504
+ margin-bottom: 5px;
505
+ font-size: 11.5px;
506
+ }
507
+
508
+ .upload-link {
509
+ font-weight: 500;
510
+ font-size: 10px;
511
+ color: #409eff;
512
+ cursor: pointer;
513
+ }
514
+ }
515
+
516
+ .signImgBox, .qrCodeImgBox {
517
+ box-sizing: border-box;
518
+ width: 145px;
519
+ height: 145px;
520
+ overflow: hidden;
521
+ margin: 0 auto 15px;
522
+ position: relative;
523
+ }
524
+
525
+ .signImgBox {
526
+ width: 133px;
527
+ height: 100px;
528
+ margin-top: 50px;
529
+ display: flex;
530
+ align-items: center;
531
+ justify-content: center;
532
+
533
+ .no-sign-tip {
534
+ display: flex;
535
+ flex-direction: column;
536
+ align-items: center;
537
+ justify-content: center;
538
+ width: 100%;
539
+ height: 100%;
540
+ background: #fafafa;
541
+ border-radius: 4px;
542
+ gap: 8px;
543
+
544
+ .tip-icon {
545
+ flex-shrink: 0;
546
+ }
547
+
548
+ .tip-text {
549
+ font-size: 12px;
550
+ color: #999;
551
+ text-align: center;
552
+ line-height: 1.4;
553
+ padding: 0 8px;
554
+ }
555
+ }
556
+ }
557
+
558
+ .unHaveSignUp {
559
+ display: flex;
560
+ justify-content: center;
561
+ position: relative;
562
+
563
+ .qrCodeBtnBox {
564
+ position: absolute;
565
+ right: calc(50% - 145px);
566
+ top: 72.5px;
567
+ margin-left: 16px;
568
+ display: flex;
569
+ flex-direction: column;
570
+ gap: 8px;
571
+
572
+ .el-button + .el-button {
573
+ margin-left: 0 !important;
574
+ }
575
+
576
+ .el-button {
577
+ min-width: 60px;
578
+ }
579
+ }
580
+
581
+ .uploadContent {
582
+ margin-top: 8px;
583
+ }
584
+ }
585
+
586
+ // 二维码状态覆盖层样式
587
+ .qrcode-status-overlay {
588
+ position: absolute;
589
+ top: 0;
590
+ left: 0;
591
+ right: 0;
592
+ bottom: 0;
593
+ display: flex;
594
+ flex-direction: column;
595
+ align-items: center;
596
+ justify-content: center;
597
+ gap: 6px;
598
+ animation: fadeIn 0.25s ease-out;
599
+ z-index: 10;
600
+
601
+ &::before {
602
+ content: '';
603
+ position: absolute;
604
+ top: 0;
605
+ left: 0;
606
+ right: 0;
607
+ bottom: 0;
608
+ background-image:
609
+ repeating-linear-gradient(0deg, transparent, transparent 1px, currentColor 1px, currentColor 2px),
610
+ repeating-linear-gradient(90deg, transparent, transparent 1px, currentColor 1px, currentColor 2px);
611
+ opacity: 0.25;
612
+ z-index: 1;
613
+ }
614
+
615
+ .status-icon {
616
+ flex-shrink: 0;
617
+ position: relative;
618
+ z-index: 3;
619
+ }
620
+
621
+ .status-text {
622
+ font-size: 12px;
623
+ font-weight: 400;
624
+ letter-spacing: 0.5px;
625
+ line-height: 1;
626
+ position: relative;
627
+ z-index: 3;
628
+ }
629
+
630
+ &.scanned {
631
+ background: rgba(255, 255, 255, 0.96);
632
+ color: rgba(100, 100, 100, 0.3);
633
+
634
+ .status-text {
635
+ color: #333333;
636
+ }
637
+ }
638
+
639
+ &.expired {
640
+ background: rgba(40, 40, 40, 0.92);
641
+ color: rgba(180, 180, 180, 0.3);
642
+ cursor: pointer;
643
+
644
+ .status-text {
645
+ color: #e0e0e0;
646
+ }
647
+
648
+ .status-icon path {
649
+ fill: #b0b0b0;
650
+ }
651
+ }
652
+ }
653
+
654
+ @keyframes fadeIn {
655
+ from { opacity: 0; }
656
+ to { opacity: 1; }
657
+ }
658
+ </style>