smart-admin-web 10.15.0 → 10.20.0

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 (432) hide show
  1. package/README.md +1 -1
  2. package/dist/api/index.d.ts +4 -2
  3. package/dist/api/index.js +1 -1
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/schema.d.ts +39 -5
  6. package/dist/assets/icons/ph-subset.json.js +5 -5
  7. package/dist/components/AppIcon.vue.js +9 -9
  8. package/dist/components/AppIcon.vue.js.map +1 -1
  9. package/dist/components/AppWatermark.vue.d.ts +14 -0
  10. package/dist/components/AppWatermark.vue.js +38 -0
  11. package/dist/components/AppWatermark.vue.js.map +1 -0
  12. package/dist/components/AppWatermark2.vue.js +5 -0
  13. package/dist/components/AppWatermark2.vue.js.map +1 -0
  14. package/dist/components/Chart/PieChart.vue.d.ts +1 -1
  15. package/dist/components/Chart/echarts.js +15 -20
  16. package/dist/components/Chart/echarts.js.map +1 -1
  17. package/dist/components/CronEditor/index.vue.js +1 -1
  18. package/dist/components/CronEditor/index2.vue.js +2 -1
  19. package/dist/components/CronEditor/index2.vue.js.map +1 -1
  20. package/dist/components/DetailPage/index.vue.js +2 -2
  21. package/dist/components/DetailPage/index2.vue.js.map +1 -1
  22. package/dist/components/DictTag/index.vue.js.map +1 -1
  23. package/dist/components/ErrorBoundary/index.vue.js +2 -2
  24. package/dist/components/ErrorBoundary/index2.vue.js +1 -1
  25. package/dist/components/ErrorBoundary/index2.vue.js.map +1 -1
  26. package/dist/components/FormContainer/index.vue.js +91 -56
  27. package/dist/components/FormContainer/index.vue.js.map +1 -1
  28. package/dist/components/ImportWizard/index.vue.js +1 -1
  29. package/dist/components/ImportWizard/index2.vue.js +44 -41
  30. package/dist/components/ImportWizard/index2.vue.js.map +1 -1
  31. package/dist/components/MenuSearch.vue.js +2 -2
  32. package/dist/components/MenuSearch2.vue.js +19 -18
  33. package/dist/components/MenuSearch2.vue.js.map +1 -1
  34. package/dist/components/PasswordStrength/index.vue.js +2 -2
  35. package/dist/components/PasswordStrength/index2.vue.js +20 -18
  36. package/dist/components/PasswordStrength/index2.vue.js.map +1 -1
  37. package/dist/components/TableTotal/index.vue.d.ts +6 -0
  38. package/dist/components/{SelectTable → TableTotal}/index.vue.js +1 -1
  39. package/dist/components/TableTotal/index2.vue.js +16 -0
  40. package/dist/components/TableTotal/index2.vue.js.map +1 -0
  41. package/dist/components/TableZoomButton/index.vue.js.map +1 -1
  42. package/dist/components/UserPicker/index.vue.js +2 -2
  43. package/dist/components/UserPicker/index2.vue.js +176 -117
  44. package/dist/components/UserPicker/index2.vue.js.map +1 -1
  45. package/dist/composables/useBatchDelete.js.map +1 -1
  46. package/dist/composables/useLayoutMenu.js +17 -16
  47. package/dist/composables/useLayoutMenu.js.map +1 -1
  48. package/dist/composables/useModule.js +39 -29
  49. package/dist/composables/useModule.js.map +1 -1
  50. package/dist/composables/useShellBreakpoint.d.ts +19 -0
  51. package/dist/composables/useShellBreakpoint.js +37 -0
  52. package/dist/composables/useShellBreakpoint.js.map +1 -0
  53. package/dist/composables/useSite.d.ts +15 -0
  54. package/dist/composables/useSite.js +16 -14
  55. package/dist/composables/useSite.js.map +1 -1
  56. package/dist/composables/useTabTitle.js.map +1 -1
  57. package/dist/composables/useTableTitle.d.ts +6 -0
  58. package/dist/composables/useTableTitle.js +11 -0
  59. package/dist/composables/useTableTitle.js.map +1 -0
  60. package/dist/composables/useTableZoom.d.ts +5 -0
  61. package/dist/composables/useTableZoom.js.map +1 -1
  62. package/dist/composables/useTheme.d.ts +2 -2
  63. package/dist/composables/useTheme.js +23 -20
  64. package/dist/composables/useTheme.js.map +1 -1
  65. package/dist/createSmartAdmin.d.ts +12 -2
  66. package/dist/createSmartAdmin.js +72 -60
  67. package/dist/createSmartAdmin.js.map +1 -1
  68. package/dist/index.d.ts +5 -2
  69. package/dist/index.js +219 -205
  70. package/dist/index.js.map +1 -1
  71. package/dist/layouts/AccentPicker.vue.js +8 -0
  72. package/dist/layouts/AccentPicker.vue.js.map +1 -0
  73. package/dist/layouts/AccentPicker2.vue.js +131 -0
  74. package/dist/layouts/AccentPicker2.vue.js.map +1 -0
  75. package/dist/layouts/AppHeader.vue.d.ts +8 -1
  76. package/dist/layouts/AppHeader.vue.js +2 -2
  77. package/dist/layouts/AppHeader2.vue.js +317 -235
  78. package/dist/layouts/AppHeader2.vue.js.map +1 -1
  79. package/dist/layouts/NoticeBell.vue.js +2 -2
  80. package/dist/layouts/NoticeBell2.vue.js +1 -1
  81. package/dist/layouts/NoticeBell2.vue.js.map +1 -1
  82. package/dist/layouts/PersonalLayout.vue.js +2 -2
  83. package/dist/layouts/PersonalLayout2.vue.js +69 -47
  84. package/dist/layouts/PersonalLayout2.vue.js.map +1 -1
  85. package/dist/layouts/SettingsDrawer.vue.js +3 -3
  86. package/dist/layouts/SettingsDrawer2.vue.js +261 -325
  87. package/dist/layouts/SettingsDrawer2.vue.js.map +1 -1
  88. package/dist/layouts/TabsBar.vue.js +2 -2
  89. package/dist/layouts/TabsBar2.vue.js +97 -69
  90. package/dist/layouts/TabsBar2.vue.js.map +1 -1
  91. package/dist/layouts/components/SideNav.vue.d.ts +3 -0
  92. package/dist/layouts/components/SideNav.vue.js +1 -1
  93. package/dist/layouts/components/SideNav2.vue.js +54 -26
  94. package/dist/layouts/components/SideNav2.vue.js.map +1 -1
  95. package/dist/layouts/default.vue.js +1 -1
  96. package/dist/layouts/default2.vue.js +170 -140
  97. package/dist/layouts/default2.vue.js.map +1 -1
  98. package/dist/lib/loginHero.d.ts +2 -2
  99. package/dist/lib/loginHero.js.map +1 -1
  100. package/dist/lib/runtime.js +1 -1
  101. package/dist/lib/watermark.d.ts +110 -0
  102. package/dist/lib/watermark.js +151 -0
  103. package/dist/lib/watermark.js.map +1 -0
  104. package/dist/locales/en-US.d.ts +221 -15
  105. package/dist/locales/en-US.js +230 -21
  106. package/dist/locales/en-US.js.map +1 -1
  107. package/dist/locales/kernelMenuTitles.d.ts +1 -0
  108. package/dist/locales/kernelMenuTitles.js +128 -0
  109. package/dist/locales/kernelMenuTitles.js.map +1 -0
  110. package/dist/locales/menuTitle.d.ts +3 -0
  111. package/dist/locales/menuTitle.js +14 -12
  112. package/dist/locales/menuTitle.js.map +1 -1
  113. package/dist/locales/zh-CN.d.ts +221 -15
  114. package/dist/locales/zh-CN.js +238 -28
  115. package/dist/locales/zh-CN.js.map +1 -1
  116. package/dist/router/index.js +41 -32
  117. package/dist/router/index.js.map +1 -1
  118. package/dist/router/namedPage.d.ts +4 -0
  119. package/dist/router/namedPage.js +7 -7
  120. package/dist/router/namedPage.js.map +1 -1
  121. package/dist/router/viewRegistry.js +8 -8
  122. package/dist/router/viewRegistry.js.map +1 -1
  123. package/dist/stores/app.d.ts +0 -12
  124. package/dist/stores/app.js +11 -12
  125. package/dist/stores/app.js.map +1 -1
  126. package/dist/stores/tabs.js.map +1 -1
  127. package/dist/stores/user.d.ts +10 -0
  128. package/dist/stores/user.js.map +1 -1
  129. package/dist/style.css +1 -1
  130. package/dist/theme/accentTokens.d.ts +27 -0
  131. package/dist/theme/accentTokens.js +54 -0
  132. package/dist/theme/accentTokens.js.map +1 -0
  133. package/dist/theme/accents.d.ts +21 -1
  134. package/dist/theme/accents.js +11 -2
  135. package/dist/theme/accents.js.map +1 -1
  136. package/dist/theme/mix.d.ts +58 -7
  137. package/dist/theme/mix.js +115 -30
  138. package/dist/theme/mix.js.map +1 -1
  139. package/dist/theme/naive-theme.d.ts +5 -2
  140. package/dist/theme/naive-theme.js +405 -75
  141. package/dist/theme/naive-theme.js.map +1 -1
  142. package/dist/utils/jwt.d.ts +8 -0
  143. package/dist/utils/jwt.js +14 -0
  144. package/dist/utils/jwt.js.map +1 -0
  145. package/dist/utils/menuIcon.d.ts +2 -0
  146. package/dist/utils/menuIcon.js +8 -0
  147. package/dist/utils/menuIcon.js.map +1 -0
  148. package/dist/utils/oauthBrand.js.map +1 -1
  149. package/dist/utils/tableFilter.d.ts +88 -0
  150. package/dist/utils/tableFilter.js +116 -0
  151. package/dist/utils/tableFilter.js.map +1 -0
  152. package/dist/utils/tableToolbar.d.ts +18 -0
  153. package/dist/utils/tableToolbar.js +11 -0
  154. package/dist/utils/tableToolbar.js.map +1 -0
  155. package/dist/utils/treeFilter.d.ts +20 -0
  156. package/dist/utils/treeFilter.js +24 -0
  157. package/dist/utils/treeFilter.js.map +1 -0
  158. package/dist/views/dashboard/ShortcutPicker.vue.js +1 -1
  159. package/dist/views/dashboard/ShortcutPicker2.vue.js +73 -48
  160. package/dist/views/dashboard/ShortcutPicker2.vue.js.map +1 -1
  161. package/dist/views/dashboard/biz.vue.js +1 -1
  162. package/dist/views/dashboard/biz2.vue.js +310 -257
  163. package/dist/views/dashboard/biz2.vue.js.map +1 -1
  164. package/dist/views/dashboard/workbench.vue.js +2 -2
  165. package/dist/views/dashboard/workbench2.vue.js +141 -155
  166. package/dist/views/dashboard/workbench2.vue.js.map +1 -1
  167. package/dist/views/embed/iframe.vue.js +2 -2
  168. package/dist/views/embed/iframe2.vue.js +22 -15
  169. package/dist/views/embed/iframe2.vue.js.map +1 -1
  170. package/dist/views/error/404.vue.js +2 -2
  171. package/dist/views/error/4042.vue.js +33 -24
  172. package/dist/views/error/4042.vue.js.map +1 -1
  173. package/dist/views/error/BootError.vue.js +1 -1
  174. package/dist/views/error/BootError2.vue.js +59 -48
  175. package/dist/views/error/BootError2.vue.js.map +1 -1
  176. package/dist/views/error/MissingRoute.vue.js +2 -2
  177. package/dist/views/error/MissingRoute2.vue.js +34 -15
  178. package/dist/views/error/MissingRoute2.vue.js.map +1 -1
  179. package/dist/views/login/LoginForm.vue.d.ts +7 -1
  180. package/dist/views/login/LoginForm.vue.js +2 -2
  181. package/dist/views/login/LoginForm2.vue.js +580 -488
  182. package/dist/views/login/LoginForm2.vue.js.map +1 -1
  183. package/dist/views/login/components/LoginDesk.vue.d.ts +36 -0
  184. package/dist/views/login/components/LoginDesk.vue.js +9 -0
  185. package/dist/views/login/components/LoginDesk.vue.js.map +1 -0
  186. package/dist/views/login/components/LoginDesk2.vue.js +100 -0
  187. package/dist/views/login/components/LoginDesk2.vue.js.map +1 -0
  188. package/dist/views/login/components/LoginHeroPanel.vue.js +3 -3
  189. package/dist/views/login/components/LoginHeroPanel2.vue.js +34 -39
  190. package/dist/views/login/components/LoginHeroPanel2.vue.js.map +1 -1
  191. package/dist/views/login/index.vue.js +1 -1
  192. package/dist/views/login/index2.vue.js +141 -71
  193. package/dist/views/login/index2.vue.js.map +1 -1
  194. package/dist/views/login/layout.d.ts +11 -0
  195. package/dist/views/login/layout.js +27 -0
  196. package/dist/views/login/layout.js.map +1 -0
  197. package/dist/views/mfa/index.vue.js +2 -2
  198. package/dist/views/mfa/index2.vue.js +367 -321
  199. package/dist/views/mfa/index2.vue.js.map +1 -1
  200. package/dist/views/module/index.vue.js +2 -2
  201. package/dist/views/module/index2.vue.js +132 -99
  202. package/dist/views/module/index2.vue.js.map +1 -1
  203. package/dist/views/oauth/callback.vue.js +2 -2
  204. package/dist/views/oauth/callback2.vue.js +65 -51
  205. package/dist/views/oauth/callback2.vue.js.map +1 -1
  206. package/dist/views/personal/bindings.vue.js +2 -2
  207. package/dist/views/personal/bindings2.vue.js +98 -94
  208. package/dist/views/personal/bindings2.vue.js.map +1 -1
  209. package/dist/views/personal/notice2.vue.js +103 -76
  210. package/dist/views/personal/notice2.vue.js.map +1 -1
  211. package/dist/views/personal/password2.vue.js +47 -40
  212. package/dist/views/personal/password2.vue.js.map +1 -1
  213. package/dist/views/personal/profile.vue.js +3 -3
  214. package/dist/views/personal/profile2.vue.js +24 -18
  215. package/dist/views/personal/profile2.vue.js.map +1 -1
  216. package/dist/views/personal/security.vue.js +2 -2
  217. package/dist/views/personal/security2.vue.js +65 -53
  218. package/dist/views/personal/security2.vue.js.map +1 -1
  219. package/dist/views/personal/sessions.vue.js +2 -2
  220. package/dist/views/personal/sessions2.vue.js +67 -62
  221. package/dist/views/personal/sessions2.vue.js.map +1 -1
  222. package/dist/views/system/ai-model/components/ProviderCard.vue.js +2 -2
  223. package/dist/views/system/ai-model/components/ProviderCard2.vue.js +142 -130
  224. package/dist/views/system/ai-model/components/ProviderCard2.vue.js.map +1 -1
  225. package/dist/views/system/ai-model/components/ProviderForm.vue.js +2 -2
  226. package/dist/views/system/ai-model/components/ProviderForm2.vue.js +71 -70
  227. package/dist/views/system/ai-model/components/ProviderForm2.vue.js.map +1 -1
  228. package/dist/views/system/ai-model/index.vue.js +2 -2
  229. package/dist/views/system/ai-model/index2.vue.js +99 -99
  230. package/dist/views/system/ai-model/index2.vue.js.map +1 -1
  231. package/dist/views/system/ai-model/presets.d.ts +1 -1
  232. package/dist/views/system/ai-model/presets.js +18 -18
  233. package/dist/views/system/ai-model/presets.js.map +1 -1
  234. package/dist/views/system/ai-usage/index.vue.js +2 -2
  235. package/dist/views/system/ai-usage/index2.vue.js +356 -258
  236. package/dist/views/system/ai-usage/index2.vue.js.map +1 -1
  237. package/dist/views/system/api-docs/index.vue.js +5 -2
  238. package/dist/views/system/api-docs/index.vue.js.map +1 -1
  239. package/dist/views/system/api-docs/index2.vue.js +92 -42
  240. package/dist/views/system/api-docs/index2.vue.js.map +1 -1
  241. package/dist/views/system/cache/index.vue.js +2 -2
  242. package/dist/views/system/cache/index2.vue.js +74 -80
  243. package/dist/views/system/cache/index2.vue.js.map +1 -1
  244. package/dist/views/system/config/components/AdminMock.vue.js +2 -2
  245. package/dist/views/system/config/components/AdminMock2.vue.js +60 -43
  246. package/dist/views/system/config/components/AdminMock2.vue.js.map +1 -1
  247. package/dist/views/system/config/components/AdvancedSection.vue.js +5 -2
  248. package/dist/views/system/config/components/AdvancedSection.vue.js.map +1 -1
  249. package/dist/views/system/config/components/AdvancedSection2.vue.js +98 -70
  250. package/dist/views/system/config/components/AdvancedSection2.vue.js.map +1 -1
  251. package/dist/views/system/config/components/BrandSection.vue.js +2 -2
  252. package/dist/views/system/config/components/BrandSection2.vue.js +121 -119
  253. package/dist/views/system/config/components/BrandSection2.vue.js.map +1 -1
  254. package/dist/views/system/config/components/CfgGroup.vue.js +3 -3
  255. package/dist/views/system/config/components/CfgGroup2.vue.js.map +1 -1
  256. package/dist/views/system/config/components/CfgRow.vue.js +1 -1
  257. package/dist/views/system/config/components/CfgRow2.vue.js +24 -23
  258. package/dist/views/system/config/components/CfgRow2.vue.js.map +1 -1
  259. package/dist/views/system/config/components/CfgSegment.vue.js +1 -1
  260. package/dist/views/system/config/components/CfgSegment2.vue.js +16 -15
  261. package/dist/views/system/config/components/CfgSegment2.vue.js.map +1 -1
  262. package/dist/views/system/config/components/ConfigFormModal.vue.js +2 -2
  263. package/dist/views/system/config/components/ConfigFormModal2.vue.js +35 -33
  264. package/dist/views/system/config/components/ConfigFormModal2.vue.js.map +1 -1
  265. package/dist/views/system/config/components/JobSection.vue.js +2 -2
  266. package/dist/views/system/config/components/JobSection2.vue.js +46 -44
  267. package/dist/views/system/config/components/JobSection2.vue.js.map +1 -1
  268. package/dist/views/system/config/components/LoginMock.vue.js +3 -3
  269. package/dist/views/system/config/components/LoginMock2.vue.js +83 -72
  270. package/dist/views/system/config/components/LoginMock2.vue.js.map +1 -1
  271. package/dist/views/system/config/components/LogoCropper.vue.js +1 -1
  272. package/dist/views/system/config/components/LogoCropper2.vue.js.map +1 -1
  273. package/dist/views/system/config/components/LogoEditor.vue.js +2 -2
  274. package/dist/views/system/config/components/LogoEditor2.vue.js +11 -13
  275. package/dist/views/system/config/components/LogoEditor2.vue.js.map +1 -1
  276. package/dist/views/system/config/components/ProviderSheet.vue.js +3 -3
  277. package/dist/views/system/config/components/ProviderSheet2.vue.js.map +1 -1
  278. package/dist/views/system/config/components/SectionLayout.vue.d.ts +4 -2
  279. package/dist/views/system/config/components/SectionLayout.vue.js +2 -2
  280. package/dist/views/system/config/components/SectionLayout2.vue.js +51 -27
  281. package/dist/views/system/config/components/SectionLayout2.vue.js.map +1 -1
  282. package/dist/views/system/config/components/SecuritySection.vue.js +2 -2
  283. package/dist/views/system/config/components/SecuritySection2.vue.js +207 -206
  284. package/dist/views/system/config/components/SecuritySection2.vue.js.map +1 -1
  285. package/dist/views/system/config/components/SensitiveSection.vue.js +2 -2
  286. package/dist/views/system/config/components/SensitiveSection2.vue.js +107 -102
  287. package/dist/views/system/config/components/SensitiveSection2.vue.js.map +1 -1
  288. package/dist/views/system/config/components/UploadSection.vue.js +2 -2
  289. package/dist/views/system/config/components/UploadSection2.vue.js +86 -77
  290. package/dist/views/system/config/components/UploadSection2.vue.js.map +1 -1
  291. package/dist/views/system/config/components/WatermarkSection.vue.js +8 -0
  292. package/dist/views/system/config/components/WatermarkSection.vue.js.map +1 -0
  293. package/dist/views/system/config/components/WatermarkSection2.vue.js +284 -0
  294. package/dist/views/system/config/components/WatermarkSection2.vue.js.map +1 -0
  295. package/dist/views/system/config/draft.d.ts +2 -2
  296. package/dist/views/system/config/draft.js.map +1 -1
  297. package/dist/views/system/config/groups.d.ts +4 -1
  298. package/dist/views/system/config/groups.js +54 -49
  299. package/dist/views/system/config/groups.js.map +1 -1
  300. package/dist/views/system/config/index.vue.js +3 -2
  301. package/dist/views/system/config/index.vue.js.map +1 -1
  302. package/dist/views/system/config/index2.vue.js +173 -148
  303. package/dist/views/system/config/index2.vue.js.map +1 -1
  304. package/dist/views/system/config/layoutMode.d.ts +23 -0
  305. package/dist/views/system/config/layoutMode.js +28 -0
  306. package/dist/views/system/config/layoutMode.js.map +1 -0
  307. package/dist/views/system/dict/index.vue.js +2 -2
  308. package/dist/views/system/dict/index2.vue.js +471 -366
  309. package/dist/views/system/dict/index2.vue.js.map +1 -1
  310. package/dist/views/system/file/index2.vue.js +91 -76
  311. package/dist/views/system/file/index2.vue.js.map +1 -1
  312. package/dist/views/system/job/index.vue.js +3 -3
  313. package/dist/views/system/job/index2.vue.js +897 -828
  314. package/dist/views/system/job/index2.vue.js.map +1 -1
  315. package/dist/views/system/job-log/components/AttemptTable.vue.d.ts +8 -0
  316. package/dist/views/system/job-log/components/AttemptTable.vue.js +5 -0
  317. package/dist/views/system/job-log/components/AttemptTable.vue.js.map +1 -0
  318. package/dist/views/system/job-log/components/AttemptTable2.vue.js +54 -0
  319. package/dist/views/system/job-log/components/AttemptTable2.vue.js.map +1 -0
  320. package/dist/views/system/job-log/index.vue.js +2 -2
  321. package/dist/views/system/job-log/index2.vue.js +193 -207
  322. package/dist/views/system/job-log/index2.vue.js.map +1 -1
  323. package/dist/views/system/job-log/runStatus.d.ts +13 -0
  324. package/dist/views/system/job-log/runStatus.js +26 -0
  325. package/dist/views/system/job-log/runStatus.js.map +1 -0
  326. package/dist/views/system/job-monitor/index.vue.js +2 -2
  327. package/dist/views/system/job-monitor/index2.vue.js +172 -180
  328. package/dist/views/system/job-monitor/index2.vue.js.map +1 -1
  329. package/dist/views/system/log/exception/index.vue.js +2 -2
  330. package/dist/views/system/log/exception/index2.vue.js +135 -96
  331. package/dist/views/system/log/exception/index2.vue.js.map +1 -1
  332. package/dist/views/system/log/login/index2.vue.js +62 -41
  333. package/dist/views/system/log/login/index2.vue.js.map +1 -1
  334. package/dist/views/system/log/op/index.vue.js +2 -2
  335. package/dist/views/system/log/op/index2.vue.js +200 -162
  336. package/dist/views/system/log/op/index2.vue.js.map +1 -1
  337. package/dist/views/system/menu/components/ButtonManager.vue.js +2 -2
  338. package/dist/views/system/menu/components/ButtonManager2.vue.js +206 -190
  339. package/dist/views/system/menu/components/ButtonManager2.vue.js.map +1 -1
  340. package/dist/views/system/menu/components/MenuFormModal.vue.js +2 -2
  341. package/dist/views/system/menu/components/MenuFormModal2.vue.js +127 -118
  342. package/dist/views/system/menu/components/MenuFormModal2.vue.js.map +1 -1
  343. package/dist/views/system/menu/index.vue.js +1 -1
  344. package/dist/views/system/menu/index2.vue.js +216 -183
  345. package/dist/views/system/menu/index2.vue.js.map +1 -1
  346. package/dist/views/system/module/index.vue.js +1 -1
  347. package/dist/views/system/module/index2.vue.js +177 -144
  348. package/dist/views/system/module/index2.vue.js.map +1 -1
  349. package/dist/views/system/monitor/index.vue.js +2 -2
  350. package/dist/views/system/monitor/index2.vue.js +134 -149
  351. package/dist/views/system/monitor/index2.vue.js.map +1 -1
  352. package/dist/views/system/notice/index.vue.js +1 -1
  353. package/dist/views/system/notice/index2.vue.js +209 -181
  354. package/dist/views/system/notice/index2.vue.js.map +1 -1
  355. package/dist/views/system/org/index.vue.js +1 -1
  356. package/dist/views/system/org/index2.vue.js +278 -248
  357. package/dist/views/system/org/index2.vue.js.map +1 -1
  358. package/dist/views/system/position/index.vue.js +5 -2
  359. package/dist/views/system/position/index.vue.js.map +1 -1
  360. package/dist/views/system/position/index2.vue.js +128 -92
  361. package/dist/views/system/position/index2.vue.js.map +1 -1
  362. package/dist/views/system/recycle/index.vue.js +2 -2
  363. package/dist/views/system/recycle/index2.vue.js +90 -67
  364. package/dist/views/system/recycle/index2.vue.js.map +1 -1
  365. package/dist/views/system/role/components/GrantMenuSheet.vue.d.ts +20 -0
  366. package/dist/views/system/role/components/GrantMenuSheet.vue.js +8 -0
  367. package/dist/views/system/role/components/GrantMenuSheet.vue.js.map +1 -0
  368. package/dist/views/system/role/components/GrantMenuSheet2.vue.js +117 -0
  369. package/dist/views/system/role/components/GrantMenuSheet2.vue.js.map +1 -0
  370. package/dist/views/system/role/components/GrantMenuTable.vue.d.ts +6 -4
  371. package/dist/views/system/role/components/GrantMenuTable.vue.js +2 -2
  372. package/dist/views/system/role/components/GrantMenuTable2.vue.js +279 -177
  373. package/dist/views/system/role/components/GrantMenuTable2.vue.js.map +1 -1
  374. package/dist/views/system/role/components/MatchText.vue.d.ts +7 -0
  375. package/dist/views/system/role/components/MatchText.vue.js +8 -0
  376. package/dist/views/system/role/components/MatchText.vue.js.map +1 -0
  377. package/dist/views/system/role/components/MatchText2.vue.js +29 -0
  378. package/dist/views/system/role/components/MatchText2.vue.js.map +1 -0
  379. package/dist/views/system/role/components/grantMenuGroups.d.ts +36 -0
  380. package/dist/views/system/role/components/grantMenuGroups.js +110 -48
  381. package/dist/views/system/role/components/grantMenuGroups.js.map +1 -1
  382. package/dist/views/system/role/index.vue.js +5 -2
  383. package/dist/views/system/role/index.vue.js.map +1 -1
  384. package/dist/views/system/role/index2.vue.js +181 -165
  385. package/dist/views/system/role/index2.vue.js.map +1 -1
  386. package/dist/views/system/session/index2.vue.js +75 -49
  387. package/dist/views/system/session/index2.vue.js.map +1 -1
  388. package/dist/views/system/user/components/ResetPasswordModal.vue.js +2 -2
  389. package/dist/views/system/user/components/ResetPasswordModal2.vue.js +2 -0
  390. package/dist/views/system/user/components/ResetPasswordModal2.vue.js.map +1 -1
  391. package/dist/views/system/user/components/UserFormModal.vue.js +2 -2
  392. package/dist/views/system/user/components/UserFormModal2.vue.js +77 -76
  393. package/dist/views/system/user/components/UserFormModal2.vue.js.map +1 -1
  394. package/dist/views/system/user/index.vue.js +2 -2
  395. package/dist/views/system/user/index2.vue.js +362 -275
  396. package/dist/views/system/user/index2.vue.js.map +1 -1
  397. package/package.json +3 -5
  398. package/dist/components/SelectTable/index.vue.d.ts +0 -76
  399. package/dist/components/SelectTable/index2.vue.js +0 -256
  400. package/dist/components/SelectTable/index2.vue.js.map +0 -1
  401. package/dist/composables/useTableRowSort.d.ts +0 -20
  402. package/dist/composables/useTableRowSort.js +0 -59
  403. package/dist/composables/useTableRowSort.js.map +0 -1
  404. package/dist/layouts/LayoutModeCards.vue.js +0 -8
  405. package/dist/layouts/LayoutModeCards.vue.js.map +0 -1
  406. package/dist/layouts/LayoutModeCards2.vue.js +0 -67
  407. package/dist/layouts/LayoutModeCards2.vue.js.map +0 -1
  408. package/dist/layouts/SettingRow.vue.d.ts +0 -16
  409. package/dist/layouts/SettingRow.vue.js +0 -8
  410. package/dist/layouts/SettingRow.vue.js.map +0 -1
  411. package/dist/layouts/SettingRow2.vue.js +0 -23
  412. package/dist/layouts/SettingRow2.vue.js.map +0 -1
  413. package/dist/views/login/skins/AuroraGlass.vue.js +0 -8
  414. package/dist/views/login/skins/AuroraGlass.vue.js.map +0 -1
  415. package/dist/views/login/skins/AuroraGlass2.vue.js +0 -80
  416. package/dist/views/login/skins/AuroraGlass2.vue.js.map +0 -1
  417. package/dist/views/login/skins/SplitPanel.vue.d.ts +0 -3
  418. package/dist/views/login/skins/SplitPanel.vue.js +0 -8
  419. package/dist/views/login/skins/SplitPanel.vue.js.map +0 -1
  420. package/dist/views/login/skins/SplitPanel2.vue.js +0 -65
  421. package/dist/views/login/skins/SplitPanel2.vue.js.map +0 -1
  422. package/dist/views/login/skins/Spotlight.vue.d.ts +0 -3
  423. package/dist/views/login/skins/Spotlight.vue.js +0 -8
  424. package/dist/views/login/skins/Spotlight.vue.js.map +0 -1
  425. package/dist/views/login/skins/Spotlight2.vue.js +0 -36
  426. package/dist/views/login/skins/Spotlight2.vue.js.map +0 -1
  427. package/dist/views/login/skins.d.ts +0 -8
  428. package/dist/views/login/skins.js +0 -13
  429. package/dist/views/login/skins.js.map +0 -1
  430. /package/dist/components/{SelectTable → TableTotal}/index.vue.js.map +0 -0
  431. /package/dist/layouts/{LayoutModeCards.vue.d.ts → AccentPicker.vue.d.ts} +0 -0
  432. /package/dist/views/{login/skins/AuroraGlass.vue.d.ts → system/config/components/WatermarkSection.vue.d.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"oauthBrand.js","sources":["../../src/utils/oauthBrand.ts"],"sourcesContent":["// 外部登录品牌 UI 纯逻辑:可被 vitest 直接驱动,不依赖浏览器。\n// 契约见 docs/adr/0007-external-login-brand-ui-and-providers.md。\nimport { t } from '#/locales'\n\n/** 登录页最多平铺的品牌圆钮数;超出进「…」菜单。 */\nexport const SSO_VISIBLE_MAX = 4\n\n/** 预置精修品牌标的 provider code(大小写不敏感)。 */\nexport const BRAND_CODES = ['github', 'wechat', 'wecom', 'dingtalk', 'gitee', 'qq'] as const\nexport type BrandCode = (typeof BRAND_CODES)[number]\n\nconst BRAND_SET = new Set<string>(BRAND_CODES)\n\n/**\n * 登录页临时展示全部品牌圆标(图标验收用)。\n * true:铺全 6 个,不做 N=4 溢出;false:恢复仅后端 enabled providers。\n * 图标验收时在本地把 false 改成 true,验收完改回来,不要带着 true 提交。\n */\nexport const PREVIEW_ALL_SSO_BRANDS = false\n\n/**\n * 内置品牌显示名。走 i18n 而非硬编码中文常量表 —— 硬编码在英文界面下会露出「微信/企业微信/钉钉」。\n * 调用方须在 computed 里取值,切语言才会重算。\n */\nexport function brandDisplayName(code: BrandCode): string {\n return t(`oauth.brand.${code}`)\n}\n\n/** 图标预览用的假 provider 列表(与后端是否启用无关)。 */\nexport function previewAllBrandProviders(): SsoProviderLike[] {\n return BRAND_CODES.map(code => ({ code, displayName: brandDisplayName(code) }))\n}\n\n/**\n * 配置页卡片上给出「按账号自动关联」开关的 provider。只有企业微信的外部标识是企业统一分配的账号(userid),\n * 钉钉 / 微信 / GitHub 的标识是 unionid 或数字 id,按账号名对不上本地账号;\n * 自定义 provider 真要开(如 sub 就是工号的 OIDC),到「其他配置」加 sys.externalauth.{code}.linkByAccount。\n */\nexport const LINK_BY_ACCOUNT_CODES: readonly string[] = ['wecom']\n\n/** 服务器上有对应扩展包的官方厂商类型,按「登录方式」页的展示顺序。 */\nexport const OFFICIAL_TYPES = ['wecom', 'dingtalk', 'github', 'wechat'] as const\n\n/** 一行的状态:类型没装 / 已装未配 / 已配可用。 */\nexport type ProviderState = 'notInstalled' | 'unconfigured' | 'configured'\n\n/** 配置页一行:后端目录里有的类型与 provider,不是预置品牌全量。 */\nexport type ConfigProviderRow = {\n code: string\n /** 类型码;代码注册的 provider 没有类型,为空串 */\n type: string\n displayName: string\n icon?: string | null\n state: ProviderState\n /** db = 在本页配置;code = 消费者用代码注册(只读);none = 没有配置 */\n source: 'db' | 'code' | 'none'\n /** 已配置且可用:开关可操作,预览与登录页才会出现它 */\n registered: boolean\n enabled: boolean\n /** 未绑定的外部身份按账号名关联同名本地账号(sys.externalauth.{code}.linkByAccount) */\n linkByAccount: boolean\n}\n\ntype CatalogLike = {\n types: readonly { type: string }[]\n providers: readonly {\n code: string\n type: string\n displayName: string\n icon?: string | null\n source: string\n installed: boolean\n configured: boolean\n }[]\n}\n\ntype AdminLike = { code: string; enabled: boolean; linkByAccount?: boolean }\n\n/**\n * 配置页列表 = 四个官方厂商(按固定顺序,没装的也列出并标「未安装」)\n * + 目录里其余的 provider(OIDC 条目、代码注册的)。\n * 未配置或未安装的行开关强制关:没配好的方式开了也没用,也不写库。\n */\nexport function buildConfigProviderRows(\n catalog: CatalogLike,\n admin: readonly AdminLike[],\n): ConfigProviderRow[] {\n const installed = new Set(catalog.types.map(x => x.type))\n const adminByCode = new Map(admin.map(a => [a.code, a]))\n const byCode = new Map(catalog.providers.map(p => [p.code, p]))\n const rows: ConfigProviderRow[] = []\n const seen = new Set<string>()\n\n const push = (code: string, official: boolean) => {\n seen.add(code)\n const p = byCode.get(code)\n const fromCode = p?.source === 'code'\n const typeInstalled = fromCode || (p ? installed.has(p.type) : installed.has(code))\n const state: ProviderState = !typeInstalled\n ? 'notInstalled'\n : p?.configured\n ? 'configured'\n : 'unconfigured'\n const a = adminByCode.get(code)\n const registered = state === 'configured'\n rows.push({\n code,\n type: p?.type ?? (official ? code : ''),\n displayName: p?.displayName || (isBrandCode(code) ? brandDisplayName(code) : code),\n icon: p?.icon ?? null,\n state,\n source: fromCode ? 'code' : p ? 'db' : 'none',\n registered,\n enabled: registered && !!a?.enabled,\n linkByAccount: registered && !!a?.linkByAccount,\n })\n }\n\n for (const code of OFFICIAL_TYPES) push(code, true)\n for (const p of catalog.providers) if (!seen.has(p.code)) push(p.code, false)\n return rows\n}\n\nexport type SsoProviderLike = {\n code: string\n displayName: string\n icon?: string | null\n}\n\nexport type BindingLike = {\n provider: string\n displayName?: string | null\n boundAt: string\n}\n\nexport type IconResolve =\n | { kind: 'brand'; code: BrandCode }\n | { kind: 'iconify'; name: string }\n | { kind: 'letter'; letter: string }\n\nexport type BindingRow = {\n code: string\n displayName: string\n icon?: string | null\n /** false = 运营已关但仍有绑定 */\n enabled: boolean\n binding?: BindingLike\n}\n\n/** 是否为可渲染的 Iconify 名称;拒绝 URL / data URI。 */\nexport function isIconifyName(icon: string | null | undefined): boolean {\n if (icon == null) return false\n const s = icon.trim()\n if (!s) return false\n if (/^https?:\\/\\//i.test(s) || s.startsWith('//') || /^data:/i.test(s)) return false\n // collection:name(离线 Iconify 常见形态)\n return /^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9._-]*$/i.test(s)\n}\n\nexport function isBrandCode(code: string | null | undefined): code is BrandCode {\n return !!code && BRAND_SET.has(code.toLowerCase())\n}\n\n/** brand map → Iconify 名 → 首字母。 */\nexport function resolveProviderIcon(code: string, icon?: string | null): IconResolve {\n const c = (code ?? '').trim().toLowerCase()\n if (isBrandCode(c)) return { kind: 'brand', code: c }\n if (isIconifyName(icon)) return { kind: 'iconify', name: icon!.trim() }\n const letter = (code ?? '?').trim().charAt(0).toUpperCase() || '?'\n return { kind: 'letter', letter }\n}\n\nconst WECOM_AUTO_LOGIN_KEY = 'smart.sso.wecomAutoLogin'\n\n/**\n * 在企业微信客户端里打开登录页时,是否直接发起企业微信登录(客户端内走网页授权,静默、不用点)。\n * 企业微信的 UA 带 wxwork(同时也带微信的 MicroMessenger,不能拿它判断)。\n * 每个浏览器会话只返回一次 true:登录失败或主动退出后再回到登录页就停下,让人自己选登录方式,不会来回跳。\n */\nexport function claimWeComAutoLogin(opts: {\n userAgent: string\n providers: readonly { code: string }[]\n storage: Pick<Storage, 'getItem' | 'setItem'>\n}): boolean {\n if (!/wxwork/i.test(opts.userAgent)) return false\n if (!opts.providers.some(p => p.code === 'wecom')) return false\n try {\n if (opts.storage.getItem(WECOM_AUTO_LOGIN_KEY)) return false\n opts.storage.setItem(WECOM_AUTO_LOGIN_KEY, '1')\n return true\n } catch {\n // 存储不可用时记不住「已经跳过一次」,宁可不自动跳\n return false\n }\n}\n\n/** 严格保序切分:前 max 平铺,其余进溢出。 */\nexport function splitLoginProviders<T>(\n providers: readonly T[],\n maxVisible = SSO_VISIBLE_MAX,\n): {\n visible: T[]\n overflow: T[]\n} {\n const list = providers.slice()\n if (list.length <= maxVisible) return { visible: list, overflow: [] }\n return { visible: list.slice(0, maxVisible), overflow: list.slice(maxVisible) }\n}\n\n/**\n * 绑定页行:已启用 providers(API 序) ∪ 仅存在于 bindings 的已停用项(接在后面)。\n * 不做 N=4 截断。\n */\nexport function mergeBindingRows(\n providers: readonly SsoProviderLike[],\n bindings: readonly BindingLike[],\n): BindingRow[] {\n const bindByProvider = new Map<string, BindingLike>()\n for (const b of bindings) bindByProvider.set(b.provider, b)\n\n const rows: BindingRow[] = []\n const seen = new Set<string>()\n\n for (const p of providers) {\n seen.add(p.code)\n rows.push({\n code: p.code,\n displayName: p.displayName,\n icon: p.icon,\n enabled: true,\n binding: bindByProvider.get(p.code),\n })\n }\n\n for (const b of bindings) {\n if (seen.has(b.provider)) continue\n rows.push({\n code: b.provider,\n displayName: (b.displayName && b.displayName.trim()) || b.provider,\n icon: null,\n enabled: false,\n binding: b,\n })\n }\n\n return rows\n}\n"],"names":["SSO_VISIBLE_MAX","BRAND_CODES","BRAND_SET","PREVIEW_ALL_SSO_BRANDS","brandDisplayName","code","t","previewAllBrandProviders","LINK_BY_ACCOUNT_CODES","OFFICIAL_TYPES","buildConfigProviderRows","catalog","admin","installed","x","adminByCode","a","byCode","p","rows","seen","push","official","fromCode","state","registered","isBrandCode","isIconifyName","icon","s","resolveProviderIcon","c","WECOM_AUTO_LOGIN_KEY","claimWeComAutoLogin","opts","splitLoginProviders","providers","maxVisible","list","mergeBindingRows","bindings","bindByProvider","b"],"mappings":"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"}
1
+ {"version":3,"file":"oauthBrand.js","sources":["../../src/utils/oauthBrand.ts"],"sourcesContent":["// 外部登录品牌 UI 纯逻辑:可被 vitest 直接驱动,不依赖浏览器。\n// 约定:登录页平铺的品牌圆钮不超过 SSO_VISIBLE_MAX 个,其余收进「…」菜单;预置精修品牌标见 BRAND_CODES。\nimport { t } from '#/locales'\n\n/** 登录页最多平铺的品牌圆钮数;超出进「…」菜单。 */\nexport const SSO_VISIBLE_MAX = 4\n\n/** 预置精修品牌标的 provider code(大小写不敏感)。 */\nexport const BRAND_CODES = ['github', 'wechat', 'wecom', 'dingtalk', 'gitee', 'qq'] as const\nexport type BrandCode = (typeof BRAND_CODES)[number]\n\nconst BRAND_SET = new Set<string>(BRAND_CODES)\n\n/**\n * 登录页临时展示全部品牌圆标(图标验收用)。\n * true:铺全 6 个,不做 N=4 溢出;false:恢复仅后端 enabled providers。\n * 图标验收时在本地把 false 改成 true,验收完改回来,不要带着 true 提交。\n */\nexport const PREVIEW_ALL_SSO_BRANDS = false\n\n/**\n * 内置品牌显示名。走 i18n 而非硬编码中文常量表 —— 硬编码在英文界面下会露出「微信/企业微信/钉钉」。\n * 调用方须在 computed 里取值,切语言才会重算。\n */\nexport function brandDisplayName(code: BrandCode): string {\n return t(`oauth.brand.${code}`)\n}\n\n/** 图标预览用的假 provider 列表(与后端是否启用无关)。 */\nexport function previewAllBrandProviders(): SsoProviderLike[] {\n return BRAND_CODES.map(code => ({ code, displayName: brandDisplayName(code) }))\n}\n\n/**\n * 配置页卡片上给出「按账号自动关联」开关的 provider。只有企业微信的外部标识是企业统一分配的账号(userid),\n * 钉钉 / 微信 / GitHub 的标识是 unionid 或数字 id,按账号名对不上本地账号;\n * 自定义 provider 真要开(如 sub 就是工号的 OIDC),到「其他配置」加 sys.externalauth.{code}.linkByAccount。\n */\nexport const LINK_BY_ACCOUNT_CODES: readonly string[] = ['wecom']\n\n/** 服务器上有对应扩展包的官方厂商类型,按「登录方式」页的展示顺序。 */\nexport const OFFICIAL_TYPES = ['wecom', 'dingtalk', 'github', 'wechat'] as const\n\n/** 一行的状态:类型没装 / 已装未配 / 已配可用。 */\nexport type ProviderState = 'notInstalled' | 'unconfigured' | 'configured'\n\n/** 配置页一行:后端目录里有的类型与 provider,不是预置品牌全量。 */\nexport type ConfigProviderRow = {\n code: string\n /** 类型码;代码注册的 provider 没有类型,为空串 */\n type: string\n displayName: string\n icon?: string | null\n state: ProviderState\n /** db = 在本页配置;code = 消费者用代码注册(只读);none = 没有配置 */\n source: 'db' | 'code' | 'none'\n /** 已配置且可用:开关可操作,预览与登录页才会出现它 */\n registered: boolean\n enabled: boolean\n /** 未绑定的外部身份按账号名关联同名本地账号(sys.externalauth.{code}.linkByAccount) */\n linkByAccount: boolean\n}\n\ntype CatalogLike = {\n types: readonly { type: string }[]\n providers: readonly {\n code: string\n type: string\n displayName: string\n icon?: string | null\n source: string\n installed: boolean\n configured: boolean\n }[]\n}\n\ntype AdminLike = { code: string; enabled: boolean; linkByAccount?: boolean }\n\n/**\n * 配置页列表 = 四个官方厂商(按固定顺序,没装的也列出并标「未安装」)\n * + 目录里其余的 provider(OIDC 条目、代码注册的)。\n * 未配置或未安装的行开关强制关:没配好的方式开了也没用,也不写库。\n */\nexport function buildConfigProviderRows(\n catalog: CatalogLike,\n admin: readonly AdminLike[],\n): ConfigProviderRow[] {\n const installed = new Set(catalog.types.map(x => x.type))\n const adminByCode = new Map(admin.map(a => [a.code, a]))\n const byCode = new Map(catalog.providers.map(p => [p.code, p]))\n const rows: ConfigProviderRow[] = []\n const seen = new Set<string>()\n\n const push = (code: string, official: boolean) => {\n seen.add(code)\n const p = byCode.get(code)\n const fromCode = p?.source === 'code'\n const typeInstalled = fromCode || (p ? installed.has(p.type) : installed.has(code))\n const state: ProviderState = !typeInstalled\n ? 'notInstalled'\n : p?.configured\n ? 'configured'\n : 'unconfigured'\n const a = adminByCode.get(code)\n const registered = state === 'configured'\n rows.push({\n code,\n type: p?.type ?? (official ? code : ''),\n displayName: p?.displayName || (isBrandCode(code) ? brandDisplayName(code) : code),\n icon: p?.icon ?? null,\n state,\n source: fromCode ? 'code' : p ? 'db' : 'none',\n registered,\n enabled: registered && !!a?.enabled,\n linkByAccount: registered && !!a?.linkByAccount,\n })\n }\n\n for (const code of OFFICIAL_TYPES) push(code, true)\n for (const p of catalog.providers) if (!seen.has(p.code)) push(p.code, false)\n return rows\n}\n\nexport type SsoProviderLike = {\n code: string\n displayName: string\n icon?: string | null\n}\n\nexport type BindingLike = {\n provider: string\n displayName?: string | null\n boundAt: string\n}\n\nexport type IconResolve =\n | { kind: 'brand'; code: BrandCode }\n | { kind: 'iconify'; name: string }\n | { kind: 'letter'; letter: string }\n\nexport type BindingRow = {\n code: string\n displayName: string\n icon?: string | null\n /** false = 运营已关但仍有绑定 */\n enabled: boolean\n binding?: BindingLike\n}\n\n/** 是否为可渲染的 Iconify 名称;拒绝 URL / data URI。 */\nexport function isIconifyName(icon: string | null | undefined): boolean {\n if (icon == null) return false\n const s = icon.trim()\n if (!s) return false\n if (/^https?:\\/\\//i.test(s) || s.startsWith('//') || /^data:/i.test(s)) return false\n // collection:name(离线 Iconify 常见形态)\n return /^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9._-]*$/i.test(s)\n}\n\nexport function isBrandCode(code: string | null | undefined): code is BrandCode {\n return !!code && BRAND_SET.has(code.toLowerCase())\n}\n\n/** brand map → Iconify 名 → 首字母。 */\nexport function resolveProviderIcon(code: string, icon?: string | null): IconResolve {\n const c = (code ?? '').trim().toLowerCase()\n if (isBrandCode(c)) return { kind: 'brand', code: c }\n if (isIconifyName(icon)) return { kind: 'iconify', name: icon!.trim() }\n const letter = (code ?? '?').trim().charAt(0).toUpperCase() || '?'\n return { kind: 'letter', letter }\n}\n\nconst WECOM_AUTO_LOGIN_KEY = 'smart.sso.wecomAutoLogin'\n\n/**\n * 在企业微信客户端里打开登录页时,是否直接发起企业微信登录(客户端内走网页授权,静默、不用点)。\n * 企业微信的 UA 带 wxwork(同时也带微信的 MicroMessenger,不能拿它判断)。\n * 每个浏览器会话只返回一次 true:登录失败或主动退出后再回到登录页就停下,让人自己选登录方式,不会来回跳。\n */\nexport function claimWeComAutoLogin(opts: {\n userAgent: string\n providers: readonly { code: string }[]\n storage: Pick<Storage, 'getItem' | 'setItem'>\n}): boolean {\n if (!/wxwork/i.test(opts.userAgent)) return false\n if (!opts.providers.some(p => p.code === 'wecom')) return false\n try {\n if (opts.storage.getItem(WECOM_AUTO_LOGIN_KEY)) return false\n opts.storage.setItem(WECOM_AUTO_LOGIN_KEY, '1')\n return true\n } catch {\n // 存储不可用时记不住「已经跳过一次」,宁可不自动跳\n return false\n }\n}\n\n/** 严格保序切分:前 max 平铺,其余进溢出。 */\nexport function splitLoginProviders<T>(\n providers: readonly T[],\n maxVisible = SSO_VISIBLE_MAX,\n): {\n visible: T[]\n overflow: T[]\n} {\n const list = providers.slice()\n if (list.length <= maxVisible) return { visible: list, overflow: [] }\n return { visible: list.slice(0, maxVisible), overflow: list.slice(maxVisible) }\n}\n\n/**\n * 绑定页行:已启用 providers(API 序) ∪ 仅存在于 bindings 的已停用项(接在后面)。\n * 不做 N=4 截断。\n */\nexport function mergeBindingRows(\n providers: readonly SsoProviderLike[],\n bindings: readonly BindingLike[],\n): BindingRow[] {\n const bindByProvider = new Map<string, BindingLike>()\n for (const b of bindings) bindByProvider.set(b.provider, b)\n\n const rows: BindingRow[] = []\n const seen = new Set<string>()\n\n for (const p of providers) {\n seen.add(p.code)\n rows.push({\n code: p.code,\n displayName: p.displayName,\n icon: p.icon,\n enabled: true,\n binding: bindByProvider.get(p.code),\n })\n }\n\n for (const b of bindings) {\n if (seen.has(b.provider)) continue\n rows.push({\n code: b.provider,\n displayName: (b.displayName && b.displayName.trim()) || b.provider,\n icon: null,\n enabled: false,\n binding: b,\n })\n }\n\n return rows\n}\n"],"names":["SSO_VISIBLE_MAX","BRAND_CODES","BRAND_SET","PREVIEW_ALL_SSO_BRANDS","brandDisplayName","code","t","previewAllBrandProviders","LINK_BY_ACCOUNT_CODES","OFFICIAL_TYPES","buildConfigProviderRows","catalog","admin","installed","x","adminByCode","a","byCode","p","rows","seen","push","official","fromCode","state","registered","isBrandCode","isIconifyName","icon","s","resolveProviderIcon","c","WECOM_AUTO_LOGIN_KEY","claimWeComAutoLogin","opts","splitLoginProviders","providers","maxVisible","list","mergeBindingRows","bindings","bindByProvider","b"],"mappings":";AAKO,MAAMA,IAAkB,GAGlBC,IAAc,CAAC,UAAU,UAAU,SAAS,YAAY,SAAS,IAAI,GAG5EC,IAAY,IAAI,IAAYD,CAAW,GAOhCE,IAAyB;AAM/B,SAASC,EAAiBC,GAAyB;AACxD,SAAOC,EAAE,eAAeD,CAAI,EAAE;AAChC;AAGO,SAASE,IAA8C;AAC5D,SAAON,EAAY,IAAI,CAAAI,OAAS,EAAE,MAAAA,GAAM,aAAaD,EAAiBC,CAAI,EAAA,EAAI;AAChF;AAOO,MAAMG,IAA2C,CAAC,OAAO,GAGnDC,IAAiB,CAAC,SAAS,YAAY,UAAU,QAAQ;AA0C/D,SAASC,EACdC,GACAC,GACqB;AACrB,QAAMC,IAAY,IAAI,IAAIF,EAAQ,MAAM,IAAI,CAAAG,MAAKA,EAAE,IAAI,CAAC,GAClDC,IAAc,IAAI,IAAIH,EAAM,IAAI,CAAAI,MAAK,CAACA,EAAE,MAAMA,CAAC,CAAC,CAAC,GACjDC,IAAS,IAAI,IAAIN,EAAQ,UAAU,IAAI,CAAAO,MAAK,CAACA,EAAE,MAAMA,CAAC,CAAC,CAAC,GACxDC,IAA4B,CAAA,GAC5BC,wBAAW,IAAA,GAEXC,IAAO,CAAChB,GAAciB,MAAsB;AAChD,IAAAF,EAAK,IAAIf,CAAI;AACb,UAAMa,IAAID,EAAO,IAAIZ,CAAI,GACnBkB,KAAWL,KAAA,gBAAAA,EAAG,YAAW,QAEzBM,IADgBD,MAAaL,IAAIL,EAAU,IAAIK,EAAE,IAAI,IAAIL,EAAU,IAAIR,CAAI,KAG7Ea,KAAA,QAAAA,EAAG,aACD,eACA,iBAHF,gBAIEF,IAAID,EAAY,IAAIV,CAAI,GACxBoB,IAAaD,MAAU;AAC7B,IAAAL,EAAK,KAAK;AAAA,MACR,MAAAd;AAAA,MACA,OAAMa,KAAA,gBAAAA,EAAG,UAASI,IAAWjB,IAAO;AAAA,MACpC,cAAaa,KAAA,gBAAAA,EAAG,iBAAgBQ,EAAYrB,CAAI,IAAID,EAAiBC,CAAI,IAAIA;AAAA,MAC7E,OAAMa,KAAA,gBAAAA,EAAG,SAAQ;AAAA,MACjB,OAAAM;AAAA,MACA,QAAQD,IAAW,SAASL,IAAI,OAAO;AAAA,MACvC,YAAAO;AAAA,MACA,SAASA,KAAc,CAAC,EAACT,KAAA,QAAAA,EAAG;AAAA,MAC5B,eAAeS,KAAc,CAAC,EAACT,KAAA,QAAAA,EAAG;AAAA,IAAA,CACnC;AAAA,EACH;AAEA,aAAWX,KAAQI,EAAgB,CAAAY,EAAKhB,GAAM,EAAI;AAClD,aAAWa,KAAKP,EAAQ,UAAW,CAAKS,EAAK,IAAIF,EAAE,IAAI,KAAGG,EAAKH,EAAE,MAAM,EAAK;AAC5E,SAAOC;AACT;AA6BO,SAASQ,EAAcC,GAA0C;AACtE,MAAIA,KAAQ,KAAM,QAAO;AACzB,QAAMC,IAAID,EAAK,KAAA;AAEf,SADI,CAACC,KACD,gBAAgB,KAAKA,CAAC,KAAKA,EAAE,WAAW,IAAI,KAAK,UAAU,KAAKA,CAAC,IAAU,KAExE,6CAA6C,KAAKA,CAAC;AAC5D;AAEO,SAASH,EAAYrB,GAAoD;AAC9E,SAAO,CAAC,CAACA,KAAQH,EAAU,IAAIG,EAAK,aAAa;AACnD;AAGO,SAASyB,EAAoBzB,GAAcuB,GAAmC;AACnF,QAAMG,KAAK1B,KAAQ,IAAI,KAAA,EAAO,YAAA;AAC9B,SAAIqB,EAAYK,CAAC,IAAU,EAAE,MAAM,SAAS,MAAMA,EAAA,IAC9CJ,EAAcC,CAAI,IAAU,EAAE,MAAM,WAAW,MAAMA,EAAM,OAAK,IAE7D,EAAE,MAAM,UAAU,SADTvB,KAAQ,KAAK,OAAO,OAAO,CAAC,EAAE,YAAA,KAAiB,IACtC;AAC3B;AAEA,MAAM2B,IAAuB;AAOtB,SAASC,EAAoBC,GAIxB;AAEV,MADI,CAAC,UAAU,KAAKA,EAAK,SAAS,KAC9B,CAACA,EAAK,UAAU,KAAK,OAAKhB,EAAE,SAAS,OAAO,EAAG,QAAO;AAC1D,MAAI;AACF,WAAIgB,EAAK,QAAQ,QAAQF,CAAoB,IAAU,MACvDE,EAAK,QAAQ,QAAQF,GAAsB,GAAG,GACvC;AAAA,EACT,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;AAGO,SAASG,EACdC,GACAC,IAAarC,GAIb;AACA,QAAMsC,IAAOF,EAAU,MAAA;AACvB,SAAIE,EAAK,UAAUD,IAAmB,EAAE,SAASC,GAAM,UAAU,GAAC,IAC3D,EAAE,SAASA,EAAK,MAAM,GAAGD,CAAU,GAAG,UAAUC,EAAK,MAAMD,CAAU,EAAA;AAC9E;AAMO,SAASE,EACdH,GACAI,GACc;AACd,QAAMC,wBAAqB,IAAA;AAC3B,aAAWC,KAAKF,EAAU,CAAAC,EAAe,IAAIC,EAAE,UAAUA,CAAC;AAE1D,QAAMvB,IAAqB,CAAA,GACrBC,wBAAW,IAAA;AAEjB,aAAWF,KAAKkB;AACd,IAAAhB,EAAK,IAAIF,EAAE,IAAI,GACfC,EAAK,KAAK;AAAA,MACR,MAAMD,EAAE;AAAA,MACR,aAAaA,EAAE;AAAA,MACf,MAAMA,EAAE;AAAA,MACR,SAAS;AAAA,MACT,SAASuB,EAAe,IAAIvB,EAAE,IAAI;AAAA,IAAA,CACnC;AAGH,aAAWwB,KAAKF;AACd,IAAIpB,EAAK,IAAIsB,EAAE,QAAQ,KACvBvB,EAAK,KAAK;AAAA,MACR,MAAMuB,EAAE;AAAA,MACR,aAAcA,EAAE,eAAeA,EAAE,YAAY,KAAA,KAAWA,EAAE;AAAA,MAC1D,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAASA;AAAA,IAAA,CACV;AAGH,SAAOvB;AACT;"}
@@ -0,0 +1,88 @@
1
+ import { FilterAction, FilterSerializer, SmartTableColumn, SmartTableFetcher, SmartTableInst } from 'smart-naive-table';
2
+ /**
3
+ * 各类搜索项该开放的比较符 —— 取决于后端怎么过滤,而不是前端想开放什么:
4
+ * 开放了后端做不到的比较符,界面就在说谎。
5
+ */
6
+ export declare const SEARCH_ACTIONS: {
7
+ fuzzy: FilterAction[];
8
+ exact: FilterAction[];
9
+ dayRange: FilterAction[];
10
+ };
11
+ /** 后端表达不了的原因。 */
12
+ export type FilterProblem = 'multiple' | 'unsupported' | 'logic';
13
+ export interface FlatFilterOptions {
14
+ /** 列 key → 后端参数名。条件构造器下 `search.key` 被忽略,列 key 与后端参数名不同的要在这里改名。 */
15
+ rename?: Record<string, string>;
16
+ /** 日期区间字段(列 key):gte → 起、lte → 止、equal → 当天,合并成 `[起, 止]`。 */
17
+ ranges?: readonly string[];
18
+ /** 出现后端表达不了的条件时的通知;默认 console.warn + 一条 toast。测试里注入它。 */
19
+ report?: (problems: {
20
+ field: string;
21
+ problem: FilterProblem;
22
+ }[]) => void;
23
+ }
24
+ /**
25
+ * 条件构造器的过滤态 → 后端扁平参数。每个表格(或全局)一个实例,实例内记住上一次上报的问题以去重。
26
+ */
27
+ export declare function createFlatFilterSerializer(options?: FlatFilterOptions): FilterSerializer;
28
+ /**
29
+ * 表头漏斗(列头的过滤图标)全站统一开,但只开给「后端有对应参数」的列。
30
+ *
31
+ * 判据就是列上有没有 `search`:声明了 `search` 的列,后端一定认这个参数(条件构造器与漏斗写的是同一份过滤态,
32
+ * 经同一个序列化器翻成扁平参数)。没有 `search` 的列后端不认,给它漏斗等于开放一个点了没反应的控件,所以不开。
33
+ * 通用 `filters` 协议是后端另一项工作;等它落地,这里放宽判据即可,页面不用改。
34
+ *
35
+ * 各页在列数组声明之后调一次。**原地改并返回同一个数组**:列数组换了身份会让 SmartTable 当成列定义变了而重置状态。
36
+ * 比较符沿用 `search.actions`(同样受后端能力约束);带字典选项的列强制单选,因为后端同一字段只收一个值。
37
+ */
38
+ export declare function deriveHeaderFilters<C extends SmartTableColumn<any>>(columns: C[]): C[];
39
+ /** 全局默认:不改名、没有日期区间字段。有日期区间或改名需求的页面自己 `createFlatFilterSerializer(...)` 后传给 `:filter-serializer`。 */
40
+ export declare const flatFilterSerializer: FilterSerializer;
41
+ /**
42
+ * 当前搜索条件的扁平形态 = `inst.params`(外部联动参数、排序)叠上条件构造器的序列化结果。
43
+ * 导出这类「带当前筛选」的场景用它,取到的键与列表请求一致(序列化器要与该表格的 `:filter-serializer` 是同一个,
44
+ * 否则键会对不上)。
45
+ * `filters` 在类型上是 Ref、通过组件实例读出来是值,`unref` 两种都吃。
46
+ */
47
+ export declare function flatSearchOf(inst: Pick<SmartTableInst, 'params' | 'filters'> | undefined, serializer?: FilterSerializer): Record<string, any>;
48
+ /**
49
+ * 用路由 query 预置一个「等于」条件(角色反查、任务记录跳转)。
50
+ * 值没变返回 false 且不写:实例就绪与 query 变化可能各触发一次 watch,第二次不该再发请求。
51
+ * 远程模式下 `setFilter` 自己会回第 1 页重查,调用方不用再 `search()`。
52
+ */
53
+ export declare function presetEqualFilter(inst: Pick<SmartTableInst, 'filters' | 'setFilter'>, key: string, value: number | undefined): boolean;
54
+ /**
55
+ * 取「尽量多」的一页:先要 `size` 条;部署把后端 `MaxPageSize` 调得比它小时(48001,`args.max` 是服务端的实际上限),
56
+ * 按服务端上限降级重试一次,而不是让搜索整体不可用。降级后取不全,调用方按 `total > items.length` 判断截断。
57
+ * 其它错误、以及回报的上限并不比请求的小(再试也没用)的 48001 原样抛出。
58
+ */
59
+ export declare function loadWithinServerMax<T>(load: (p: {
60
+ page: number;
61
+ pageSize: number;
62
+ }) => Promise<{
63
+ items: T[];
64
+ total: number;
65
+ }>, size: number): Promise<{
66
+ items: T[];
67
+ total: number;
68
+ }>;
69
+ /**
70
+ * 把条件构造器的原始过滤态原样交给 fetcher(键 `__filters`),不摊平。
71
+ * 给「后端没有过滤能力、由前端求值」的表格用,配合 createClientFilterFetcher。
72
+ */
73
+ export declare const passthroughFilterSerializer: FilterSerializer;
74
+ /**
75
+ * 后端没有过滤能力的分页接口,也能用内置条件搜索:
76
+ * - 没有生效条件:原样走服务端分页(不多取数据);
77
+ * - 有条件:一次取全量(上限 10000),用 3.0 的 applyFilters 在前端求值,再切页。
78
+ * 条件由前端求值,所以 15 个比较符都能用,不受后端能力限制。
79
+ * 全量超过 10000 条时只能覆盖前 10000 条,此时调 onTruncated 提示用户。
80
+ * 要配 `:filter-serializer="passthroughFilterSerializer"` 才能拿到原始过滤态。
81
+ */
82
+ export declare function createClientFilterFetcher<T extends Record<string, any>>(load: (p: {
83
+ page: number;
84
+ pageSize: number;
85
+ }) => Promise<{
86
+ items: T[];
87
+ total: number;
88
+ }>, fields: readonly string[], onTruncated?: () => void): SmartTableFetcher<T>;
@@ -0,0 +1,116 @@
1
+ import { createDiscreteApi as b } from "naive-ui";
2
+ import { activeConditions as y, applyFilters as z } from "smart-naive-table";
3
+ import { unref as F } from "vue";
4
+ import { ApiError as w } from "../api/index.js";
5
+ import { t as A } from "../locales/index.js";
6
+ const I = {
7
+ /** 后端 `Contains`(账号、名称、路径……) */
8
+ fuzzy: ["contains"],
9
+ /** 后端 `==`(枚举、状态、id) */
10
+ exact: ["equal"],
11
+ /** 后端 `>=` 起、`<=` 止;「等于」= 当天(与 api 层 dayRange 给结束日补 23:59:59 配合) */
12
+ dayRange: ["gte", "lte", "equal"]
13
+ }, _ = ["equal", "contains"], S = (e) => e == null ? "" : String(e);
14
+ let d;
15
+ function k(e) {
16
+ console.warn("[SmartAdmin] 搜索条件超出后端能力,已按可识别的条件查询:", e), d ?? (d = b(["message"]).message), d.warning(A("table.filterFallback"));
17
+ }
18
+ function q(e = {}) {
19
+ const { rename: a = {}, ranges: i = [], report: n = k } = e;
20
+ let t = "";
21
+ return (l) => {
22
+ const r = {}, s = [];
23
+ for (const [o, h] of Object.entries(l)) {
24
+ const m = y(h);
25
+ if (m.length === 0) continue;
26
+ const v = a[o] ?? o;
27
+ if (i.includes(o)) {
28
+ let c, g;
29
+ for (const u of m)
30
+ u.action === "gte" && c === void 0 ? c = u.value : u.action === "lte" && g === void 0 ? g = u.value : u.action === "equal" && c === void 0 && g === void 0 ? (c = u.value, g = u.value) : s.push({ field: o, problem: "unsupported" });
31
+ m.length > 1 && h.logic === "or" && s.push({ field: o, problem: "logic" }), (c !== void 0 || g !== void 0) && (r[v] = [S(c), S(g)]);
32
+ continue;
33
+ }
34
+ const p = m.filter((c) => _.includes(c.action));
35
+ if (p.length < m.length && s.push({ field: o, problem: "unsupported" }), p.length > 1 && s.push({ field: o, problem: "multiple" }), p[0]) {
36
+ const c = typeof p[0].value == "string" ? p[0].value.trim() : p[0].value;
37
+ c !== "" && (r[v] = c);
38
+ }
39
+ }
40
+ const f = s.map((o) => `${o.field}:${o.problem}`).join("|");
41
+ return f && f !== t && n(s), t = f, r;
42
+ };
43
+ }
44
+ function D(e) {
45
+ for (const a of e) {
46
+ const i = a;
47
+ if (!("key" in i) || i.filter !== void 0) continue;
48
+ const n = i.search === !0 ? {} : i.search;
49
+ if (!n || typeof n != "object") continue;
50
+ const t = n;
51
+ if (t.render) continue;
52
+ const l = {};
53
+ t.key && (l.key = t.key), t.actions && (l.actions = t.actions), i.options && (l.multiple = !1);
54
+ const r = {
55
+ input: "input",
56
+ number: "number",
57
+ select: "select",
58
+ date: "date",
59
+ daterange: "date"
60
+ }[t.type ?? ""];
61
+ r && r !== "input" && r !== "select" && (l.type = r), i.filter = l;
62
+ }
63
+ return e;
64
+ }
65
+ const x = q();
66
+ function H(e, a = x) {
67
+ return e ? { ...e.params, ...a(F(e.filters) ?? {}) } : {};
68
+ }
69
+ function $(e, a, i) {
70
+ var t, l, r;
71
+ return ((r = (l = (t = F(e.filters)) == null ? void 0 : t[a]) == null ? void 0 : l.conditions[0]) == null ? void 0 : r.value) === i ? !1 : (e.setFilter(
72
+ a,
73
+ i === void 0 ? null : { logic: "and", conditions: [{ action: "equal", value: i }] }
74
+ ), !0);
75
+ }
76
+ const C = 1e4;
77
+ async function N(e, a) {
78
+ var i;
79
+ try {
80
+ return await e({ page: 1, pageSize: a });
81
+ } catch (n) {
82
+ const t = n instanceof w && n.code === 48001 ? Number((i = n.args) == null ? void 0 : i.max) : NaN;
83
+ if (!Number.isFinite(t) || t <= 0 || t >= a) throw n;
84
+ return e({ page: 1, pageSize: t });
85
+ }
86
+ }
87
+ const M = (e) => ({ __filters: e });
88
+ function O() {
89
+ console.warn("[SmartAdmin] 数据超过 10000 条,搜索只覆盖前 10000 条"), d ?? (d = b(["message"]).message), d.warning(A("table.filterTruncated"));
90
+ }
91
+ function W(e, a, i = O) {
92
+ return async (n) => {
93
+ const t = n.__filters;
94
+ if (!(!!t && Object.values(t).some((o) => y(o).length > 0))) return e({ page: n.page, pageSize: n.pageSize });
95
+ const r = await N(e, C);
96
+ r.total > r.items.length && i();
97
+ const s = z(
98
+ r.items,
99
+ a.map((o) => ({ key: o, field: o })),
100
+ t
101
+ ), f = (n.page - 1) * n.pageSize;
102
+ return { items: s.slice(f, f + n.pageSize), total: s.length };
103
+ };
104
+ }
105
+ export {
106
+ I as SEARCH_ACTIONS,
107
+ W as createClientFilterFetcher,
108
+ q as createFlatFilterSerializer,
109
+ D as deriveHeaderFilters,
110
+ x as flatFilterSerializer,
111
+ H as flatSearchOf,
112
+ N as loadWithinServerMax,
113
+ M as passthroughFilterSerializer,
114
+ $ as presetEqualFilter
115
+ };
116
+ //# sourceMappingURL=tableFilter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tableFilter.js","sources":["../../src/utils/tableFilter.ts"],"sourcesContent":["// 条件构造器(smart-naive-table 3.0 的 search.container: 'table')与后端扁平参数之间的接缝。\n//\n// 3.0 的条件构造器产出 `filters: [{ field, logic, conditions: [{ action, value }] }]`,\n// 而内核各分页端点收的是扁平参数(Account / Name / StartTime…,见 api/index.ts)。\n// 后端能表达的条件远比 15 个比较符少 —— 文本只有 Contains,枚举与 id 只有相等,日期只有 >= / <=,\n// 同一字段只收一个值 —— 所以这里不做通用翻译,只把「后端真正支持的那一小撮」翻成扁平键,\n// api 层的参数形态因此保持扁平。当前限制:后端没有通用 filters 协议。\n//\n// 后端表达不了的组合(同一字段两个「包含」、日期区间用「或」连接……)不抛错:\n// 3.0 在点击事件和请求前都会直接调用序列化器,抛错到不了页面的 @error,只会落到全局 errorHandler。\n// 做法是按第一个能识别的条件生效,console.warn,并提示用户一次(同一问题不重复弹)。\nimport { createDiscreteApi } from 'naive-ui'\nimport { activeConditions, applyFilters } from 'smart-naive-table'\nimport type {\n FilterAction,\n FilterConfig,\n FilterSerializer,\n FilterState,\n SmartTableColumn,\n SmartTableFetcher,\n SmartTableInst,\n} from 'smart-naive-table'\nimport { unref } from 'vue'\nimport { ApiError } from '#/api'\nimport { t } from '#/locales'\n\n/**\n * 各类搜索项该开放的比较符 —— 取决于后端怎么过滤,而不是前端想开放什么:\n * 开放了后端做不到的比较符,界面就在说谎。\n */\nexport const SEARCH_ACTIONS: {\n fuzzy: FilterAction[]\n exact: FilterAction[]\n dayRange: FilterAction[]\n} = {\n /** 后端 `Contains`(账号、名称、路径……) */\n fuzzy: ['contains'],\n /** 后端 `==`(枚举、状态、id) */\n exact: ['equal'],\n /** 后端 `>=` 起、`<=` 止;「等于」= 当天(与 api 层 dayRange 给结束日补 23:59:59 配合) */\n dayRange: ['gte', 'lte', 'equal'],\n}\n\n/** 后端表达不了的原因。 */\nexport type FilterProblem = 'multiple' | 'unsupported' | 'logic'\n\nexport interface FlatFilterOptions {\n /** 列 key → 后端参数名。条件构造器下 `search.key` 被忽略,列 key 与后端参数名不同的要在这里改名。 */\n rename?: Record<string, string>\n /** 日期区间字段(列 key):gte → 起、lte → 止、equal → 当天,合并成 `[起, 止]`。 */\n ranges?: readonly string[]\n /** 出现后端表达不了的条件时的通知;默认 console.warn + 一条 toast。测试里注入它。 */\n report?: (problems: { field: string; problem: FilterProblem }[]) => void\n}\n\n/** 标量字段(文本、枚举、id)后端只认这两个比较符。 */\nconst SCALAR_ACTIONS: readonly FilterAction[] = ['equal', 'contains']\n\nconst text = (v: unknown) => (v == null ? '' : String(v))\n\nlet toast: ReturnType<typeof createDiscreteApi<'message'>>['message'] | undefined\nfunction defaultReport(problems: { field: string; problem: FilterProblem }[]) {\n console.warn('[SmartAdmin] 搜索条件超出后端能力,已按可识别的条件查询:', problems)\n toast ??= createDiscreteApi(['message']).message\n toast.warning(t('table.filterFallback'))\n}\n\n/**\n * 条件构造器的过滤态 → 后端扁平参数。每个表格(或全局)一个实例,实例内记住上一次上报的问题以去重。\n */\nexport function createFlatFilterSerializer(options: FlatFilterOptions = {}): FilterSerializer {\n const { rename = {}, ranges = [], report = defaultReport } = options\n let lastSignature = ''\n\n return (state: FilterState) => {\n const out: Record<string, unknown> = {}\n const problems: { field: string; problem: FilterProblem }[] = []\n\n for (const [field, value] of Object.entries(state)) {\n const conds = activeConditions(value)\n if (conds.length === 0) continue\n const key = rename[field] ?? field\n\n if (ranges.includes(field)) {\n let start: unknown\n let end: unknown\n for (const c of conds) {\n if (c.action === 'gte' && start === undefined) start = c.value\n else if (c.action === 'lte' && end === undefined) end = c.value\n else if (c.action === 'equal' && start === undefined && end === undefined) {\n start = c.value\n end = c.value\n } else problems.push({ field, problem: 'unsupported' })\n }\n if (conds.length > 1 && value.logic === 'or') problems.push({ field, problem: 'logic' })\n if (start !== undefined || end !== undefined) out[key] = [text(start), text(end)]\n continue\n }\n\n const usable = conds.filter(c => SCALAR_ACTIONS.includes(c.action))\n if (usable.length < conds.length) problems.push({ field, problem: 'unsupported' })\n if (usable.length > 1) problems.push({ field, problem: 'multiple' })\n if (usable[0]) {\n // 文本去掉首尾空格:条件构造器的序列化结果不经过库的 cleanParams(它才会 trim),\n // 粘贴进来的「 admin 」不 trim 就查不到。只有空白的值等同于没填。\n const v = typeof usable[0].value === 'string' ? usable[0].value.trim() : usable[0].value\n if (v !== '') out[key] = v\n }\n }\n\n const signature = problems.map(p => `${p.field}:${p.problem}`).join('|')\n if (signature && signature !== lastSignature) report(problems)\n lastSignature = signature\n return out\n }\n}\n\n/**\n * 表头漏斗(列头的过滤图标)全站统一开,但只开给「后端有对应参数」的列。\n *\n * 判据就是列上有没有 `search`:声明了 `search` 的列,后端一定认这个参数(条件构造器与漏斗写的是同一份过滤态,\n * 经同一个序列化器翻成扁平参数)。没有 `search` 的列后端不认,给它漏斗等于开放一个点了没反应的控件,所以不开。\n * 通用 `filters` 协议是后端另一项工作;等它落地,这里放宽判据即可,页面不用改。\n *\n * 各页在列数组声明之后调一次。**原地改并返回同一个数组**:列数组换了身份会让 SmartTable 当成列定义变了而重置状态。\n * 比较符沿用 `search.actions`(同样受后端能力约束);带字典选项的列强制单选,因为后端同一字段只收一个值。\n */\nexport function deriveHeaderFilters<C extends SmartTableColumn<any>>(columns: C[]): C[] {\n for (const col of columns) {\n const c = col as C & { search?: unknown; filter?: unknown; options?: unknown }\n // 特殊列(序号 / 勾选)没有 key;已显式写了 filter(包括 false)的以列上为准\n if (!('key' in c) || c.filter !== undefined) continue\n const search = c.search === true ? {} : c.search\n if (!search || typeof search !== 'object') continue\n const s = search as { actions?: FilterAction[]; type?: string; key?: string; render?: unknown }\n if (s.render) continue // 自定义控件进不了条件构造器,漏斗同样画不出来\n const filter: FilterConfig = {}\n if (s.key) filter.key = s.key\n if (s.actions) filter.actions = s.actions\n if (c.options) filter.multiple = false\n // 搜索控件 → 过滤值控件;switch 没有对应的,走库的缺省推断\n const type = {\n input: 'input',\n number: 'number',\n select: 'select',\n date: 'date',\n daterange: 'date',\n }[s.type ?? ''] as FilterConfig['type']\n if (type && type !== 'input' && type !== 'select') filter.type = type\n ;(c as { filter?: unknown }).filter = filter\n }\n return columns\n}\n\n/** 全局默认:不改名、没有日期区间字段。有日期区间或改名需求的页面自己 `createFlatFilterSerializer(...)` 后传给 `:filter-serializer`。 */\nexport const flatFilterSerializer: FilterSerializer = createFlatFilterSerializer()\n\n/**\n * 当前搜索条件的扁平形态 = `inst.params`(外部联动参数、排序)叠上条件构造器的序列化结果。\n * 导出这类「带当前筛选」的场景用它,取到的键与列表请求一致(序列化器要与该表格的 `:filter-serializer` 是同一个,\n * 否则键会对不上)。\n * `filters` 在类型上是 Ref、通过组件实例读出来是值,`unref` 两种都吃。\n */\nexport function flatSearchOf(\n inst: Pick<SmartTableInst, 'params' | 'filters'> | undefined,\n serializer: FilterSerializer = flatFilterSerializer,\n): Record<string, any> {\n if (!inst) return {}\n return { ...inst.params, ...serializer(unref(inst.filters) ?? {}) }\n}\n\n/**\n * 用路由 query 预置一个「等于」条件(角色反查、任务记录跳转)。\n * 值没变返回 false 且不写:实例就绪与 query 变化可能各触发一次 watch,第二次不该再发请求。\n * 远程模式下 `setFilter` 自己会回第 1 页重查,调用方不用再 `search()`。\n */\nexport function presetEqualFilter(\n inst: Pick<SmartTableInst, 'filters' | 'setFilter'>,\n key: string,\n value: number | undefined,\n): boolean {\n const current = unref(inst.filters)?.[key]?.conditions[0]?.value\n if (current === value) return false\n inst.setFilter(\n key,\n value === undefined ? null : { logic: 'and', conditions: [{ action: 'equal', value }] },\n )\n return true\n}\n\n/** 取全量时的单页条数:后端 MaxPageSize 的默认上限。 */\nconst LOAD_ALL_SIZE = 10000\n\n/**\n * 取「尽量多」的一页:先要 `size` 条;部署把后端 `MaxPageSize` 调得比它小时(48001,`args.max` 是服务端的实际上限),\n * 按服务端上限降级重试一次,而不是让搜索整体不可用。降级后取不全,调用方按 `total > items.length` 判断截断。\n * 其它错误、以及回报的上限并不比请求的小(再试也没用)的 48001 原样抛出。\n */\nexport async function loadWithinServerMax<T>(\n load: (p: { page: number; pageSize: number }) => Promise<{ items: T[]; total: number }>,\n size: number,\n): Promise<{ items: T[]; total: number }> {\n try {\n return await load({ page: 1, pageSize: size })\n } catch (e) {\n const max = e instanceof ApiError && e.code === 48001 ? Number(e.args?.max) : NaN\n if (!Number.isFinite(max) || max <= 0 || max >= size) throw e\n return load({ page: 1, pageSize: max })\n }\n}\n\n/**\n * 把条件构造器的原始过滤态原样交给 fetcher(键 `__filters`),不摊平。\n * 给「后端没有过滤能力、由前端求值」的表格用,配合 createClientFilterFetcher。\n */\nexport const passthroughFilterSerializer: FilterSerializer = state => ({ __filters: state })\n\nfunction defaultTruncated() {\n console.warn('[SmartAdmin] 数据超过 10000 条,搜索只覆盖前 10000 条')\n toast ??= createDiscreteApi(['message']).message\n toast.warning(t('table.filterTruncated'))\n}\n\n/**\n * 后端没有过滤能力的分页接口,也能用内置条件搜索:\n * - 没有生效条件:原样走服务端分页(不多取数据);\n * - 有条件:一次取全量(上限 10000),用 3.0 的 applyFilters 在前端求值,再切页。\n * 条件由前端求值,所以 15 个比较符都能用,不受后端能力限制。\n * 全量超过 10000 条时只能覆盖前 10000 条,此时调 onTruncated 提示用户。\n * 要配 `:filter-serializer=\"passthroughFilterSerializer\"` 才能拿到原始过滤态。\n */\nexport function createClientFilterFetcher<T extends Record<string, any>>(\n load: (p: { page: number; pageSize: number }) => Promise<{ items: T[]; total: number }>,\n fields: readonly string[],\n onTruncated: () => void = defaultTruncated,\n): SmartTableFetcher<T> {\n return async params => {\n const state = (params as { __filters?: FilterState }).__filters\n const active = !!state && Object.values(state).some(v => activeConditions(v).length > 0)\n if (!active) return load({ page: params.page, pageSize: params.pageSize })\n\n const all = await loadWithinServerMax(load, LOAD_ALL_SIZE)\n if (all.total > all.items.length) onTruncated()\n const rows = applyFilters(\n all.items,\n fields.map(field => ({ key: field, field })),\n state,\n )\n const start = (params.page - 1) * params.pageSize\n return { items: rows.slice(start, start + params.pageSize), total: rows.length }\n }\n}\n"],"names":["SEARCH_ACTIONS","SCALAR_ACTIONS","text","v","toast","defaultReport","problems","createDiscreteApi","t","createFlatFilterSerializer","options","rename","ranges","report","lastSignature","state","out","field","value","conds","activeConditions","key","start","end","c","usable","signature","p","deriveHeaderFilters","columns","col","search","s","filter","type","flatFilterSerializer","flatSearchOf","inst","serializer","unref","presetEqualFilter","_c","_b","_a","LOAD_ALL_SIZE","loadWithinServerMax","load","size","e","max","ApiError","passthroughFilterSerializer","defaultTruncated","createClientFilterFetcher","fields","onTruncated","params","all","rows","applyFilters"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { ToolbarConfig } from 'smart-naive-table';
2
+ /**
3
+ * 整页列表统一的工具栏:刷新 / 放大还原 / 列设置三个内置图标。
4
+ * 放大(maximize)统一开:宽表想临时铺满整个视口看全貌时用,所有表格同一个入口、同一个位置;
5
+ * 即便整页列表本来就铺满内容区,也保持显示。
6
+ * 3.0 的 `toolbar` 没有全局配置,所以每个表格显式传这一份,让按钮组合是写出来的而不是靠默认。
7
+ * 密度不放在表格上:它是「系统设置」里的全局偏好,表格再给一个入口只会让同一个设置有两处可改。
8
+ * 「更多」菜单没有选项就不出现,需要的页面自己 `{ ...TABLE_TOOLBAR, more: [...] }`。
9
+ * 静态数据的表格 3.0 会自己把刷新藏起来(点了也没用),这里写 true 不会强行显示。
10
+ */
11
+ export declare const TABLE_TOOLBAR: ToolbarConfig;
12
+ /**
13
+ * 定高表格(fill-height)的虚拟滚动行高下限,每张 fill-height 的表都要传 `:min-row-height="TABLE_MIN_ROW_HEIGHT"`。
14
+ * smart-naive-table 按密度默认给 40(紧凑)/ 48(舒适),而本项目紧凑行实际只有约 33px:
15
+ * 虚拟列表拿这个下限去估算没渲染的行,估高大于真实行高,滚到底就少渲染最后几行、底部留白
16
+ * (48 条的表看不到最后 3 条,100 条的表滚到底只到第 96 行)。下限必须 ≤ 任何密度下的真实行高,宁小勿大。
17
+ */
18
+ export declare const TABLE_MIN_ROW_HEIGHT = 30;
@@ -0,0 +1,11 @@
1
+ const e = Object.freeze({
2
+ refresh: !0,
3
+ density: !1,
4
+ columnSettings: !0,
5
+ maximize: !0
6
+ }), t = 30;
7
+ export {
8
+ t as TABLE_MIN_ROW_HEIGHT,
9
+ e as TABLE_TOOLBAR
10
+ };
11
+ //# sourceMappingURL=tableToolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tableToolbar.js","sources":["../../src/utils/tableToolbar.ts"],"sourcesContent":["import type { ToolbarConfig } from 'smart-naive-table'\n\n/**\n * 整页列表统一的工具栏:刷新 / 放大还原 / 列设置三个内置图标。\n * 放大(maximize)统一开:宽表想临时铺满整个视口看全貌时用,所有表格同一个入口、同一个位置;\n * 即便整页列表本来就铺满内容区,也保持显示。\n * 3.0 的 `toolbar` 没有全局配置,所以每个表格显式传这一份,让按钮组合是写出来的而不是靠默认。\n * 密度不放在表格上:它是「系统设置」里的全局偏好,表格再给一个入口只会让同一个设置有两处可改。\n * 「更多」菜单没有选项就不出现,需要的页面自己 `{ ...TABLE_TOOLBAR, more: [...] }`。\n * 静态数据的表格 3.0 会自己把刷新藏起来(点了也没用),这里写 true 不会强行显示。\n */\nexport const TABLE_TOOLBAR: ToolbarConfig = Object.freeze({\n refresh: true,\n density: false,\n columnSettings: true,\n maximize: true,\n})\n\n/**\n * 定高表格(fill-height)的虚拟滚动行高下限,每张 fill-height 的表都要传 `:min-row-height=\"TABLE_MIN_ROW_HEIGHT\"`。\n * smart-naive-table 按密度默认给 40(紧凑)/ 48(舒适),而本项目紧凑行实际只有约 33px:\n * 虚拟列表拿这个下限去估算没渲染的行,估高大于真实行高,滚到底就少渲染最后几行、底部留白\n * (48 条的表看不到最后 3 条,100 条的表滚到底只到第 96 行)。下限必须 ≤ 任何密度下的真实行高,宁小勿大。\n */\nexport const TABLE_MIN_ROW_HEIGHT = 30\n"],"names":["TABLE_TOOLBAR","TABLE_MIN_ROW_HEIGHT"],"mappings":"AAWO,MAAMA,IAA+B,OAAO,OAAO;AAAA,EACxD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,UAAU;AACZ,CAAC,GAQYC,IAAuB;"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 树形表格的「条件构造器 → 过滤整棵树」。
3
+ *
4
+ * 为什么不用 SmartTable 的静态模式过滤:3.0 静态模式的 `applyFilters` 是平铺的 `rows.filter(...)`,
5
+ * 不递归 `children`,在树上只会命中顶层行。所以树表走远程模式 —— fetcher 里调这里,
6
+ * 条件构造器只负责收集条件(经全局序列化器摊平成 `{ 列key: 值 }` 传进来),过滤由我们自己递归做。
7
+ */
8
+ /** 参数键 → 取该节点对应文本的函数。键必须与搜索列的 key 一致(条件构造器下参数名就是列 key)。 */
9
+ export type TreeSearchFields<T> = Record<string, (node: T) => string | null | undefined>;
10
+ /** 当前参数里有没有生效的树搜索条件。 */
11
+ export declare function hasTreeCondition<T>(params: Record<string, unknown>, fields: TreeSearchFields<T>): boolean;
12
+ /**
13
+ * 按条件过滤整棵树:命中节点连同整棵子树保留,仅因后代命中的祖先作为祖先链保留(契约见 `filterTree`)。
14
+ * 多个条件是「且」:同一个节点的每个字段都得包含对应搜索词(大小写不敏感)。
15
+ * 没有条件时返回整棵树的一份浅拷贝数组(节点仍是原引用)—— 数组必须是新的,
16
+ * 否则表格内部按引用判断「数据没变」,不会再发 `loaded`,依赖它的展开状态就不会更新。
17
+ */
18
+ export declare function filterTreeByParams<T extends {
19
+ children?: T[];
20
+ }>(nodes: T[], params: Record<string, unknown>, fields: TreeSearchFields<T>): T[];
@@ -0,0 +1,24 @@
1
+ import { filterTree as u } from "./tree.js";
2
+ function s(e, t) {
3
+ const r = [];
4
+ for (const n of Object.keys(t)) {
5
+ const o = e[n], i = o == null ? "" : String(o).trim().toLowerCase();
6
+ i && r.push([n, i]);
7
+ }
8
+ return r;
9
+ }
10
+ function l(e, t) {
11
+ return s(e, t).length > 0;
12
+ }
13
+ function a(e, t, r) {
14
+ const n = s(t, r);
15
+ return n.length === 0 ? [...e] : u(
16
+ e,
17
+ (o) => n.every(([i, c]) => (r[i](o) ?? "").toLowerCase().includes(c))
18
+ );
19
+ }
20
+ export {
21
+ a as filterTreeByParams,
22
+ l as hasTreeCondition
23
+ };
24
+ //# sourceMappingURL=treeFilter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"treeFilter.js","sources":["../../src/utils/treeFilter.ts"],"sourcesContent":["import { filterTree } from './tree'\n\n/**\n * 树形表格的「条件构造器 → 过滤整棵树」。\n *\n * 为什么不用 SmartTable 的静态模式过滤:3.0 静态模式的 `applyFilters` 是平铺的 `rows.filter(...)`,\n * 不递归 `children`,在树上只会命中顶层行。所以树表走远程模式 —— fetcher 里调这里,\n * 条件构造器只负责收集条件(经全局序列化器摊平成 `{ 列key: 值 }` 传进来),过滤由我们自己递归做。\n */\n\n/** 参数键 → 取该节点对应文本的函数。键必须与搜索列的 key 一致(条件构造器下参数名就是列 key)。 */\nexport type TreeSearchFields<T> = Record<string, (node: T) => string | null | undefined>\n\n/** 取出生效的条件:`[键, 小写化去空白的搜索词]`。空串 / 纯空白 / null / undefined 不算条件;未声明的键忽略(page、pageSize 等)。 */\nfunction activeNeedles<T>(\n params: Record<string, unknown>,\n fields: TreeSearchFields<T>,\n): [string, string][] {\n const out: [string, string][] = []\n for (const key of Object.keys(fields)) {\n const raw = params[key]\n const needle = raw == null ? '' : String(raw).trim().toLowerCase()\n if (needle) out.push([key, needle])\n }\n return out\n}\n\n/** 当前参数里有没有生效的树搜索条件。 */\nexport function hasTreeCondition<T>(\n params: Record<string, unknown>,\n fields: TreeSearchFields<T>,\n): boolean {\n return activeNeedles(params, fields).length > 0\n}\n\n/**\n * 按条件过滤整棵树:命中节点连同整棵子树保留,仅因后代命中的祖先作为祖先链保留(契约见 `filterTree`)。\n * 多个条件是「且」:同一个节点的每个字段都得包含对应搜索词(大小写不敏感)。\n * 没有条件时返回整棵树的一份浅拷贝数组(节点仍是原引用)—— 数组必须是新的,\n * 否则表格内部按引用判断「数据没变」,不会再发 `loaded`,依赖它的展开状态就不会更新。\n */\nexport function filterTreeByParams<T extends { children?: T[] }>(\n nodes: T[],\n params: Record<string, unknown>,\n fields: TreeSearchFields<T>,\n): T[] {\n const needles = activeNeedles(params, fields)\n if (needles.length === 0) return [...nodes]\n return filterTree(nodes, node =>\n needles.every(([key, needle]) => (fields[key]!(node) ?? '').toLowerCase().includes(needle)),\n )\n}\n"],"names":["activeNeedles","params","fields","out","key","raw","needle","hasTreeCondition","filterTreeByParams","nodes","needles","filterTree","node"],"mappings":";AAcA,SAASA,EACPC,GACAC,GACoB;AACpB,QAAMC,IAA0B,CAAA;AAChC,aAAWC,KAAO,OAAO,KAAKF,CAAM,GAAG;AACrC,UAAMG,IAAMJ,EAAOG,CAAG,GAChBE,IAASD,KAAO,OAAO,KAAK,OAAOA,CAAG,EAAE,KAAA,EAAO,YAAA;AACrD,IAAIC,KAAQH,EAAI,KAAK,CAACC,GAAKE,CAAM,CAAC;AAAA,EACpC;AACA,SAAOH;AACT;AAGO,SAASI,EACdN,GACAC,GACS;AACT,SAAOF,EAAcC,GAAQC,CAAM,EAAE,SAAS;AAChD;AAQO,SAASM,EACdC,GACAR,GACAC,GACK;AACL,QAAMQ,IAAUV,EAAcC,GAAQC,CAAM;AAC5C,SAAIQ,EAAQ,WAAW,IAAU,CAAC,GAAGD,CAAK,IACnCE;AAAA,IAAWF;AAAA,IAAO,OACvBC,EAAQ,MAAM,CAAC,CAACN,GAAKE,CAAM,OAAOJ,EAAOE,CAAG,EAAGQ,CAAI,KAAK,IAAI,cAAc,SAASN,CAAM,CAAC;AAAA,EAAA;AAE9F;"}
@@ -1,7 +1,7 @@
1
1
  import o from "./ShortcutPicker2.vue.js";
2
2
  /* empty css */
3
3
  import t from "../../_virtual/_plugin-vue_export-helper.js";
4
- const m = /* @__PURE__ */ t(o, [["__scopeId", "data-v-1ba501cb"]]);
4
+ const m = /* @__PURE__ */ t(o, [["__scopeId", "data-v-f9cc8618"]]);
5
5
  export {
6
6
  m as default
7
7
  };
@@ -1,74 +1,99 @@
1
- import { defineComponent as _, useModel as g, ref as m, watch as M, nextTick as b, computed as B, openBlock as n, createBlock as f, unref as l, withCtx as i, createVNode as s, createCommentVNode as S, createElementBlock as h, Fragment as z, renderList as E, createElementVNode as L, toDisplayString as P, mergeModels as v } from "vue";
2
- import { NModal as V, NCard as q, NInput as F, NScrollbar as I, NEmpty as R } from "naive-ui";
3
- import { useI18n as T } from "vue-i18n";
4
- import { useMenuFlat as U } from "../../composables/useMenuFlat.js";
5
- import { translateMenuTitle as $ } from "../../locales/menuTitle.js";
6
- import A from "../../components/AppIcon.vue.js";
7
- const D = ["onClick"], W = /* @__PURE__ */ _({
1
+ import { defineComponent as N, useModel as b, ref as m, watch as C, nextTick as M, computed as L, openBlock as s, createBlock as c, unref as l, withCtx as a, createVNode as i, createElementBlock as B, Fragment as I, renderList as S, createElementVNode as h, toDisplayString as z, mergeModels as k } from "vue";
2
+ import { NModal as E, NCard as P, NInput as q, NScrollbar as F, NList as R, NListItem as T, NEmpty as U } from "naive-ui";
3
+ import { Icon as V } from "@iconify/vue";
4
+ import { useI18n as $ } from "vue-i18n";
5
+ import { useMenuFlat as A } from "../../composables/useMenuFlat.js";
6
+ import { translateMenuTitle as D } from "../../locales/menuTitle.js";
7
+ import j from "../../components/AppIcon.vue.js";
8
+ const G = { class: "row" }, Z = /* @__PURE__ */ N({
8
9
  __name: "ShortcutPicker",
9
- props: /* @__PURE__ */ v({
10
+ props: /* @__PURE__ */ k({
10
11
  excludePaths: {}
11
12
  }, {
12
13
  show: { type: Boolean, default: !1 },
13
14
  showModifiers: {}
14
15
  }),
15
- emits: /* @__PURE__ */ v(["pick"], ["update:show"]),
16
- setup(u, { emit: w }) {
17
- const k = u, y = w, a = g(u, "show"), { t: c } = T(), x = U(), r = m(""), p = m();
18
- M(a, (o) => {
19
- o && (r.value = "", b(() => {
16
+ emits: /* @__PURE__ */ k(["pick"], ["update:show"]),
17
+ setup(u, { emit: v }) {
18
+ const w = u, y = v, n = b(u, "show"), { t: d } = $(), x = A(), r = m(""), p = m();
19
+ C(n, (o) => {
20
+ o && (r.value = "", M(() => {
20
21
  var t;
21
22
  return (t = p.value) == null ? void 0 : t.focus();
22
23
  }));
23
24
  });
24
- function C(o) {
25
- return $(o.title);
25
+ function _(o) {
26
+ return D(o.title);
26
27
  }
27
- const d = B(() => {
28
- const o = new Set(k.excludePaths), t = r.value.trim().toLowerCase();
29
- return x.value.filter((e) => !o.has(e.path)).map((e) => ({ ...e, display: C(e) })).filter((e) => !t || e.display.toLowerCase().includes(t));
28
+ const f = L(() => {
29
+ const o = new Set(w.excludePaths), t = r.value.trim().toLowerCase();
30
+ return x.value.filter((e) => !o.has(e.path)).map((e) => ({ ...e, display: _(e) })).filter((e) => !t || e.display.toLowerCase().includes(t));
30
31
  });
31
- function N(o) {
32
- y("pick", o), a.value = !1;
32
+ function g(o) {
33
+ y("pick", o), n.value = !1;
33
34
  }
34
- return (o, t) => (n(), f(l(V), {
35
- show: a.value,
36
- "onUpdate:show": t[1] || (t[1] = (e) => a.value = e)
35
+ return (o, t) => (s(), c(l(E), {
36
+ show: n.value,
37
+ "onUpdate:show": t[1] || (t[1] = (e) => n.value = e)
37
38
  }, {
38
- default: i(() => [
39
- s(l(q), {
40
- style: { width: "420px" },
41
- title: l(c)("biz.quickAdd"),
39
+ default: a(() => [
40
+ i(l(P), {
41
+ class: "picker",
42
+ title: l(d)("biz.quickAdd"),
42
43
  bordered: !1,
43
44
  size: "small",
44
- role: "dialog"
45
+ role: "dialog",
46
+ "content-style": "padding: 4px 14px 14px"
45
47
  }, {
46
- default: i(() => [
47
- s(l(F), {
48
+ default: a(() => [
49
+ i(l(q), {
48
50
  ref_key: "inputRef",
49
51
  ref: p,
50
52
  value: r.value,
51
53
  "onUpdate:value": t[0] || (t[0] = (e) => r.value = e),
52
- placeholder: l(c)("common.search"),
54
+ placeholder: l(d)("common.search"),
53
55
  clearable: ""
54
- }, null, 8, ["value", "placeholder"]),
55
- s(l(I), { style: { "max-height": "320px", "margin-top": "12px" } }, {
56
- default: i(() => [
57
- d.value.length ? S("", !0) : (n(), f(l(R), {
56
+ }, {
57
+ prefix: a(() => [
58
+ i(l(V), {
59
+ icon: "ph:magnifying-glass",
60
+ width: 16,
61
+ class: "faint"
62
+ })
63
+ ]),
64
+ _: 1
65
+ }, 8, ["value", "placeholder"]),
66
+ i(l(F), { class: "list" }, {
67
+ default: a(() => [
68
+ f.value.length ? (s(), c(l(R), {
58
69
  key: 0,
70
+ hoverable: "",
71
+ clickable: "",
72
+ "show-divider": !1
73
+ }, {
74
+ default: a(() => [
75
+ (s(!0), B(I, null, S(f.value, (e) => (s(), c(l(T), {
76
+ key: e.path,
77
+ class: "list-item",
78
+ onClick: (H) => g(e.path)
79
+ }, {
80
+ default: a(() => [
81
+ h("div", G, [
82
+ i(j, {
83
+ icon: e.icon || "ph:squares-four",
84
+ size: 16
85
+ }, null, 8, ["icon"]),
86
+ h("span", null, z(e.display), 1)
87
+ ])
88
+ ]),
89
+ _: 2
90
+ }, 1032, ["onClick"]))), 128))
91
+ ]),
92
+ _: 1
93
+ })) : (s(), c(l(U), {
94
+ key: 1,
59
95
  class: "empty"
60
- })),
61
- (n(!0), h(z, null, E(d.value, (e) => (n(), h("div", {
62
- key: e.path,
63
- class: "row",
64
- onClick: (j) => N(e.path)
65
- }, [
66
- s(A, {
67
- icon: e.icon || "ph:squares-four",
68
- size: 16
69
- }, null, 8, ["icon"]),
70
- L("span", null, P(e.display), 1)
71
- ], 8, D))), 128))
96
+ }))
72
97
  ]),
73
98
  _: 1
74
99
  })
@@ -81,6 +106,6 @@ const D = ["onClick"], W = /* @__PURE__ */ _({
81
106
  }
82
107
  });
83
108
  export {
84
- W as default
109
+ Z as default
85
110
  };
86
111
  //# sourceMappingURL=ShortcutPicker2.vue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ShortcutPicker2.vue.js","sources":["../../../src/views/dashboard/ShortcutPicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 复用 MenuSearch.vue 的\"搜索+标题翻译+高亮候选\"思路,但不是同一个组件——MenuSearch 是全局命令面板,\n// 点了就跳转;这里点了是回调 pick(仅置顶,不导航),排除列表也不一样(排除已置顶,不是排除当前页)。\nimport { computed, nextTick, ref, watch } from 'vue'\nimport { NModal, NCard, NInput, NEmpty, NScrollbar } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { useMenuFlat, type MenuLeaf } from '#/composables/useMenuFlat'\nimport { translateMenuTitle } from '#/locales/menuTitle'\nimport AppIcon from '#/components/AppIcon.vue'\n\nconst props = defineProps<{ excludePaths: string[] }>()\nconst emit = defineEmits<{ pick: [path: string] }>()\nconst show = defineModel<boolean>('show', { default: false })\n\nconst { t } = useI18n()\nconst leaves = useMenuFlat()\nconst keyword = ref('')\nconst inputRef = ref<InstanceType<typeof NInput>>()\n\nwatch(show, v => {\n if (v) {\n keyword.value = ''\n nextTick(() => inputRef.value?.focus())\n }\n})\n\nfunction label(item: MenuLeaf) {\n return translateMenuTitle(item.title)\n}\n\nconst candidates = computed(() => {\n const excluded = new Set(props.excludePaths)\n const kw = keyword.value.trim().toLowerCase()\n return leaves.value\n .filter(l => !excluded.has(l.path))\n .map(l => ({ ...l, display: label(l) }))\n .filter(l => !kw || l.display.toLowerCase().includes(kw))\n})\n\nfunction pick(path: string) {\n emit('pick', path)\n show.value = false\n}\n</script>\n\n<template>\n <n-modal v-model:show=\"show\">\n <n-card\n style=\"width: 420px\"\n :title=\"t('biz.quickAdd')\"\n :bordered=\"false\"\n size=\"small\"\n role=\"dialog\"\n >\n <n-input ref=\"inputRef\" v-model:value=\"keyword\" :placeholder=\"t('common.search')\" clearable />\n <n-scrollbar style=\"max-height: 320px; margin-top: 12px\">\n <n-empty v-if=\"!candidates.length\" class=\"empty\" />\n <div v-for=\"c in candidates\" :key=\"c.path\" class=\"row\" @click=\"pick(c.path)\">\n <AppIcon :icon=\"c.icon || 'ph:squares-four'\" :size=\"16\" />\n <span>{{ c.display }}</span>\n </div>\n </n-scrollbar>\n </n-card>\n </n-modal>\n</template>\n\n<style scoped>\n.row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-radius: var(--radius-md);\n cursor: pointer;\n}\n.row:hover {\n background: var(--color-fill-hover);\n}\n.empty {\n padding: 24px 0;\n}\n</style>\n"],"names":["props","__props","emit","__emit","show","_useModel","t","useI18n","leaves","useMenuFlat","keyword","ref","inputRef","watch","v","nextTick","_a","label","item","translateMenuTitle","candidates","computed","excluded","kw","l","pick","path","_createBlock","_unref","NModal","$event","_createVNode","NCard","NInput","NScrollbar","NEmpty","_createElementBlock","_Fragment","_renderList","c","AppIcon","_createElementVNode","_toDisplayString"],"mappings":";;;;;;;;;;;;;;;;AAUA,UAAMA,IAAQC,GACRC,IAAOC,GACPC,IAAOC,EAAoBJ,GAAC,MAA0B,GAEtD,EAAE,GAAAK,EAAA,IAAMC,EAAA,GACRC,IAASC,EAAA,GACTC,IAAUC,EAAI,EAAE,GAChBC,IAAWD,EAAA;AAEjB,IAAAE,EAAMT,GAAM,CAAAU,MAAK;AACf,MAAIA,MACFJ,EAAQ,QAAQ,IAChBK,EAAS,MAAA;;AAAM,gBAAAC,IAAAJ,EAAS,UAAT,gBAAAI,EAAgB;AAAA,OAAO;AAAA,IAE1C,CAAC;AAED,aAASC,EAAMC,GAAgB;AAC7B,aAAOC,EAAmBD,EAAK,KAAK;AAAA,IACtC;AAEA,UAAME,IAAaC,EAAS,MAAM;AAChC,YAAMC,IAAW,IAAI,IAAItB,EAAM,YAAY,GACrCuB,IAAKb,EAAQ,MAAM,KAAA,EAAO,YAAA;AAChC,aAAOF,EAAO,MACX,OAAO,CAAAgB,MAAK,CAACF,EAAS,IAAIE,EAAE,IAAI,CAAC,EACjC,IAAI,CAAAA,OAAM,EAAE,GAAGA,GAAG,SAASP,EAAMO,CAAC,EAAA,EAAI,EACtC,OAAO,CAAAA,MAAK,CAACD,KAAMC,EAAE,QAAQ,YAAA,EAAc,SAASD,CAAE,CAAC;AAAA,IAC5D,CAAC;AAED,aAASE,EAAKC,GAAc;AAC1B,MAAAxB,EAAK,QAAQwB,CAAI,GACjBtB,EAAK,QAAQ;AAAA,IACf;2BAIEuB,EAiBUC,EAAAC,CAAA,GAAA;AAAA,MAjBO,MAAMzB,EAAA;AAAA,8CAAAA,EAAI,QAAA0B;AAAA,IAAA;iBACzB,MAeS;AAAA,QAfTC,EAeSH,EAAAI,CAAA,GAAA;AAAA,UAdP,OAAA,EAAA,OAAA,QAAA;AAAA,UACC,OAAOJ,EAAAtB,CAAA,EAAC,cAAA;AAAA,UACR,UAAU;AAAA,UACX,MAAK;AAAA,UACL,MAAK;AAAA,QAAA;qBAEL,MAA8F;AAAA,YAA9FyB,EAA8FH,EAAAK,CAAA,GAAA;AAAA,uBAAjF;AAAA,cAAJ,KAAIrB;AAAA,cAAmB,OAAOF,EAAA;AAAA,uDAAAA,EAAO,QAAAoB;AAAA,cAAG,aAAaF,EAAAtB,CAAA,EAAC,eAAA;AAAA,cAAmB,WAAA;AAAA,YAAA;YAClFyB,EAMcH,EAAAM,CAAA,GAAA,EAND,OAAA,EAAA,cAAA,SAAA,cAAA,OAAA,KAA2C;AAAA,yBACtD,MAAmD;AAAA,gBAAnCd,EAAA,MAAW,2BAA3BO,EAAmDC,EAAAO,CAAA,GAAA;AAAA;kBAAhB,OAAM;AAAA,gBAAA;wBACzCC,EAGMC,GAAA,MAAAC,EAHWlB,EAAA,OAAU,CAAfmB,YAAZH,EAGM,OAAA;AAAA,kBAHwB,KAAKG,EAAE;AAAA,kBAAM,OAAM;AAAA,kBAAO,SAAK,CAAAT,MAAEL,EAAKc,EAAE,IAAI;AAAA,gBAAA;kBACxER,EAA0DS,GAAA;AAAA,oBAAhD,MAAMD,EAAE,QAAI;AAAA,oBAAwB,MAAM;AAAA,kBAAA;kBACpDE,EAA4B,QAAA,MAAAC,EAAnBH,EAAE,OAAO,GAAA,CAAA;AAAA,gBAAA;;;;;;;;;;;;"}
1
+ {"version":3,"file":"ShortcutPicker2.vue.js","sources":["../../../src/views/dashboard/ShortcutPicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 复用 MenuSearch.vue 的\"搜索+标题翻译+高亮候选\"思路,但不是同一个组件——MenuSearch 是全局命令面板,\n// 点了就跳转;这里点了是回调 pick(仅置顶,不导航),排除列表也不一样(排除已置顶,不是排除当前页)。\nimport { computed, nextTick, ref, watch } from 'vue'\nimport { NModal, NCard, NInput, NEmpty, NScrollbar, NList, NListItem } from 'naive-ui'\nimport { Icon } from '@iconify/vue'\nimport { useI18n } from 'vue-i18n'\nimport { useMenuFlat, type MenuLeaf } from '#/composables/useMenuFlat'\nimport { translateMenuTitle } from '#/locales/menuTitle'\nimport AppIcon from '#/components/AppIcon.vue'\n\nconst props = defineProps<{ excludePaths: string[] }>()\nconst emit = defineEmits<{ pick: [path: string] }>()\nconst show = defineModel<boolean>('show', { default: false })\n\nconst { t } = useI18n()\nconst leaves = useMenuFlat()\nconst keyword = ref('')\nconst inputRef = ref<InstanceType<typeof NInput>>()\n\nwatch(show, v => {\n if (v) {\n keyword.value = ''\n nextTick(() => inputRef.value?.focus())\n }\n})\n\nfunction label(item: MenuLeaf) {\n return translateMenuTitle(item.title)\n}\n\nconst candidates = computed(() => {\n const excluded = new Set(props.excludePaths)\n const kw = keyword.value.trim().toLowerCase()\n return leaves.value\n .filter(l => !excluded.has(l.path))\n .map(l => ({ ...l, display: label(l) }))\n .filter(l => !kw || l.display.toLowerCase().includes(kw))\n})\n\nfunction pick(path: string) {\n emit('pick', path)\n show.value = false\n}\n</script>\n\n<template>\n <n-modal v-model:show=\"show\">\n <n-card\n class=\"picker\"\n :title=\"t('biz.quickAdd')\"\n :bordered=\"false\"\n size=\"small\"\n role=\"dialog\"\n content-style=\"padding: 4px 14px 14px\"\n >\n <n-input ref=\"inputRef\" v-model:value=\"keyword\" :placeholder=\"t('common.search')\" clearable>\n <template #prefix><Icon icon=\"ph:magnifying-glass\" :width=\"16\" class=\"faint\" /></template>\n </n-input>\n <n-scrollbar class=\"list\">\n <n-list v-if=\"candidates.length\" hoverable clickable :show-divider=\"false\">\n <n-list-item\n v-for=\"c in candidates\"\n :key=\"c.path\"\n class=\"list-item\"\n @click=\"pick(c.path)\"\n >\n <div class=\"row\">\n <AppIcon :icon=\"c.icon || 'ph:squares-four'\" :size=\"16\" />\n <span>{{ c.display }}</span>\n </div>\n </n-list-item>\n </n-list>\n <n-empty v-else class=\"empty\" />\n </n-scrollbar>\n </n-card>\n </n-modal>\n</template>\n\n<style scoped>\n.picker {\n width: 420px;\n max-width: calc(100vw - 32px);\n}\n.faint {\n color: var(--text-3);\n}\n.list {\n max-height: 320px;\n margin-top: 8px;\n}\n.list-item {\n border-radius: var(--radius-md);\n padding: 8px 10px;\n}\n.row {\n display: flex;\n align-items: center;\n gap: 10px;\n min-height: 22px;\n}\n.empty {\n padding: 26px 0;\n}\n</style>\n"],"names":["props","__props","emit","__emit","show","_useModel","t","useI18n","leaves","useMenuFlat","keyword","ref","inputRef","watch","v","nextTick","_a","label","item","translateMenuTitle","candidates","computed","excluded","kw","l","pick","path","_createBlock","_unref","NModal","$event","_createVNode","NCard","NInput","Icon","NScrollbar","NList","_createElementBlock","_Fragment","_renderList","c","NListItem","_createElementVNode","_hoisted_1","AppIcon","_toDisplayString","NEmpty"],"mappings":";;;;;;;;;;;;;;;;;AAWA,UAAMA,IAAQC,GACRC,IAAOC,GACPC,IAAOC,EAAoBJ,GAAC,MAA0B,GAEtD,EAAE,GAAAK,EAAA,IAAMC,EAAA,GACRC,IAASC,EAAA,GACTC,IAAUC,EAAI,EAAE,GAChBC,IAAWD,EAAA;AAEjB,IAAAE,EAAMT,GAAM,CAAAU,MAAK;AACf,MAAIA,MACFJ,EAAQ,QAAQ,IAChBK,EAAS,MAAA;;AAAM,gBAAAC,IAAAJ,EAAS,UAAT,gBAAAI,EAAgB;AAAA,OAAO;AAAA,IAE1C,CAAC;AAED,aAASC,EAAMC,GAAgB;AAC7B,aAAOC,EAAmBD,EAAK,KAAK;AAAA,IACtC;AAEA,UAAME,IAAaC,EAAS,MAAM;AAChC,YAAMC,IAAW,IAAI,IAAItB,EAAM,YAAY,GACrCuB,IAAKb,EAAQ,MAAM,KAAA,EAAO,YAAA;AAChC,aAAOF,EAAO,MACX,OAAO,CAAAgB,MAAK,CAACF,EAAS,IAAIE,EAAE,IAAI,CAAC,EACjC,IAAI,CAAAA,OAAM,EAAE,GAAGA,GAAG,SAASP,EAAMO,CAAC,EAAA,EAAI,EACtC,OAAO,CAAAA,MAAK,CAACD,KAAMC,EAAE,QAAQ,YAAA,EAAc,SAASD,CAAE,CAAC;AAAA,IAC5D,CAAC;AAED,aAASE,EAAKC,GAAc;AAC1B,MAAAxB,EAAK,QAAQwB,CAAI,GACjBtB,EAAK,QAAQ;AAAA,IACf;2BAIEuB,EA6BUC,EAAAC,CAAA,GAAA;AAAA,MA7BO,MAAMzB,EAAA;AAAA,8CAAAA,EAAI,QAAA0B;AAAA,IAAA;iBACzB,MA2BS;AAAA,QA3BTC,EA2BSH,EAAAI,CAAA,GAAA;AAAA,UA1BP,OAAM;AAAA,UACL,OAAOJ,EAAAtB,CAAA,EAAC,cAAA;AAAA,UACR,UAAU;AAAA,UACX,MAAK;AAAA,UACL,MAAK;AAAA,UACL,iBAAc;AAAA,QAAA;qBAEd,MAEU;AAAA,YAFVyB,EAEUH,EAAAK,CAAA,GAAA;AAAA,uBAFG;AAAA,cAAJ,KAAIrB;AAAA,cAAmB,OAAOF,EAAA;AAAA,uDAAAA,EAAO,QAAAoB;AAAA,cAAG,aAAaF,EAAAtB,CAAA,EAAC,eAAA;AAAA,cAAmB,WAAA;AAAA,YAAA;cACrE,UAAO,MAA6D;AAAA,gBAA7DyB,EAA6DH,EAAAM,CAAA,GAAA;AAAA,kBAAvD,MAAK;AAAA,kBAAuB,OAAO;AAAA,kBAAI,OAAM;AAAA,gBAAA;;;;YAEvEH,EAecH,EAAAO,CAAA,GAAA,EAfD,OAAM,UAAM;AAAA,yBACvB,MAYS;AAAA,gBAZKf,EAAA,MAAW,eAAzBO,EAYSC,EAAAQ,CAAA,GAAA;AAAA;kBAZwB,WAAA;AAAA,kBAAU,WAAA;AAAA,kBAAW,gBAAc;AAAA,gBAAA;6BAEhE,MAAuB;AAAA,4BADzBC,EAUcC,GAAA,MAAAC,EATAnB,EAAA,OAAU,CAAfoB,YADTb,EAUcC,EAAAa,CAAA,GAAA;AAAA,sBARX,KAAKD,EAAE;AAAA,sBACR,OAAM;AAAA,sBACL,SAAK,CAAAV,MAAEL,EAAKe,EAAE,IAAI;AAAA,oBAAA;iCAEnB,MAGM;AAAA,wBAHNE,EAGM,OAHNC,GAGM;AAAA,0BAFJZ,EAA0Da,GAAA;AAAA,4BAAhD,MAAMJ,EAAE,QAAI;AAAA,4BAAwB,MAAM;AAAA,0BAAA;0BACpDE,EAA4B,QAAA,MAAAG,EAAnBL,EAAE,OAAO,GAAA,CAAA;AAAA,wBAAA;;;;;;4BAIxBb,EAAgCC,EAAAkB,CAAA,GAAA;AAAA;kBAAhB,OAAM;AAAA,gBAAA;;;;;;;;;;;;"}
@@ -1,7 +1,7 @@
1
1
  import o from "./biz2.vue.js";
2
2
  /* empty css */
3
3
  import t from "../../_virtual/_plugin-vue_export-helper.js";
4
- const p = /* @__PURE__ */ t(o, [["__scopeId", "data-v-4a6a23e0"]]);
4
+ const p = /* @__PURE__ */ t(o, [["__scopeId", "data-v-5e7dfd7f"]]);
5
5
  export {
6
6
  p as default
7
7
  };