@robot-admin/naive-ui-components 0.10.2 → 0.11.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 (652) hide show
  1. package/CHANGELOG.md +111 -94
  2. package/LICENSE +21 -21
  3. package/README.md +463 -315
  4. package/README_EN.md +404 -0
  5. package/SECURITY.md +23 -0
  6. package/dist/C_ActionBar.cjs +1 -1
  7. package/dist/C_ActionBar.css +46 -48
  8. package/dist/C_ActionBar.d.cts +1 -1
  9. package/dist/C_ActionBar.js +1 -1
  10. package/dist/C_ActionBar2.cjs +1 -0
  11. package/dist/C_ActionBar2.js +1 -2
  12. package/dist/C_AntV.cjs +1 -1
  13. package/dist/C_AntV.css +3 -3
  14. package/dist/C_AntV.d.cts +1 -1
  15. package/dist/C_AntV.js +1 -1
  16. package/dist/C_AntV2.cjs +4 -0
  17. package/dist/C_AntV2.js +3 -4
  18. package/dist/C_AudioPlayer.cjs +1 -1
  19. package/dist/C_AudioPlayer.css +15 -7
  20. package/dist/C_AudioPlayer.d.cts +1 -1
  21. package/dist/C_AudioPlayer.js +1 -1
  22. package/dist/C_AudioPlayer2.cjs +1 -0
  23. package/dist/C_AudioPlayer2.js +1 -2
  24. package/dist/C_AvatarGroup.cjs +1 -1
  25. package/dist/C_AvatarGroup.css +15 -7
  26. package/dist/C_AvatarGroup.d.cts +1 -1
  27. package/dist/C_AvatarGroup2.cjs +1 -0
  28. package/dist/C_AvatarGroup2.js +1 -2
  29. package/dist/C_Barcode.cjs +1 -1
  30. package/dist/C_Barcode.css +0 -2
  31. package/dist/C_Barcode.d.cts +1 -1
  32. package/dist/C_Barcode2.cjs +1 -0
  33. package/dist/C_Barcode2.js +1 -2
  34. package/dist/C_Breadcrumb.cjs +1 -1
  35. package/dist/C_Breadcrumb.css +0 -2
  36. package/dist/C_Breadcrumb.d.cts +2 -2
  37. package/dist/C_Breadcrumb.d.ts +1 -1
  38. package/dist/C_Breadcrumb.js +1 -1
  39. package/dist/C_Breadcrumb2.cjs +1 -0
  40. package/dist/C_Breadcrumb2.js +1 -2
  41. package/dist/C_Captcha.cjs +1 -1
  42. package/dist/C_Captcha.css +46 -21
  43. package/dist/C_Captcha.d.cts +3 -2
  44. package/dist/C_Captcha.d.ts +3 -2
  45. package/dist/C_Captcha.js +1 -1
  46. package/dist/C_Captcha2.cjs +1 -0
  47. package/dist/C_Captcha2.js +1 -2
  48. package/dist/C_Cascade.cjs +1 -1
  49. package/dist/C_Cascade.css +0 -2
  50. package/dist/C_Cascade.d.cts +1 -1
  51. package/dist/C_Cascade.d.ts +1 -1
  52. package/dist/C_Cascade2.cjs +1 -0
  53. package/dist/C_Cascade2.js +1 -2
  54. package/dist/C_Chat.cjs +1 -1
  55. package/dist/C_Chat.css +0 -2
  56. package/dist/C_Chat.d.cts +1 -1
  57. package/dist/C_Chat.d.ts +1 -1
  58. package/dist/C_Chat.js +1 -1
  59. package/dist/C_Chat2.cjs +1 -0
  60. package/dist/C_Chat2.js +1 -2
  61. package/dist/C_City.cjs +1 -1
  62. package/dist/C_City.css +28 -30
  63. package/dist/C_City.d.cts +1 -1
  64. package/dist/C_City.d.ts +1 -1
  65. package/dist/C_City2.cjs +1 -0
  66. package/dist/C_City2.js +1 -2
  67. package/dist/C_Code.cjs +1 -1
  68. package/dist/C_Code.css +241 -4
  69. package/dist/C_Code.d.cts +1 -1
  70. package/dist/C_Code.d.ts +1 -1
  71. package/dist/C_Code.js +1 -1
  72. package/dist/C_Code2.cjs +1 -0
  73. package/dist/C_Code2.js +1 -2
  74. package/dist/C_CollapsePanel.cjs +1 -1
  75. package/dist/C_CollapsePanel.css +12 -6
  76. package/dist/C_CollapsePanel.d.cts +1 -1
  77. package/dist/C_CollapsePanel.js +1 -1
  78. package/dist/C_CollapsePanel2.cjs +1 -0
  79. package/dist/C_CollapsePanel2.js +1 -2
  80. package/dist/C_ContextMenu.cjs +1 -1
  81. package/dist/C_ContextMenu.css +16 -18
  82. package/dist/C_ContextMenu.d.cts +1 -1
  83. package/dist/C_ContextMenu.d.ts +1 -1
  84. package/dist/C_ContextMenu.js +1 -1
  85. package/dist/C_ContextMenu2.cjs +1 -0
  86. package/dist/C_ContextMenu2.js +1 -2
  87. package/dist/C_Cron.cjs +1 -1
  88. package/dist/C_Cron.css +18 -8
  89. package/dist/C_Cron.d.cts +1 -1
  90. package/dist/C_Cron.js +1 -1
  91. package/dist/C_Cron2.cjs +1 -0
  92. package/dist/C_Cron2.js +1 -2
  93. package/dist/C_Date.cjs +1 -1
  94. package/dist/C_Date.css +1 -0
  95. package/dist/C_Date.d.cts +2 -2
  96. package/dist/C_Date.d.ts +2 -2
  97. package/dist/C_Date2.cjs +1 -0
  98. package/dist/C_Date2.js +1 -2
  99. package/dist/C_Draggable.cjs +1 -1
  100. package/dist/C_Draggable.css +9 -5
  101. package/dist/C_Draggable.d.cts +1 -1
  102. package/dist/C_Draggable2.cjs +1 -0
  103. package/dist/C_Draggable2.js +1 -2
  104. package/dist/C_Editor.cjs +1 -1
  105. package/dist/C_Editor.css +80 -67
  106. package/dist/C_Editor.d.cts +1 -1
  107. package/dist/C_Editor.d.ts +1 -1
  108. package/dist/C_Editor2.cjs +1 -0
  109. package/dist/C_Editor2.js +1 -2
  110. package/dist/C_FilePreview.cjs +1 -1
  111. package/dist/C_FilePreview.css +0 -2
  112. package/dist/C_FilePreview.d.cts +1 -1
  113. package/dist/C_FilePreview.js +1 -1
  114. package/dist/C_FilePreview2.cjs +1 -0
  115. package/dist/C_FilePreview2.js +1 -2
  116. package/dist/C_Form.base.css +980 -0
  117. package/dist/C_Form.cjs +1 -1
  118. package/dist/C_Form.css +256 -155
  119. package/dist/C_Form.d.cts +2 -2
  120. package/dist/C_Form.d.ts +2 -2
  121. package/dist/C_Form.js +1 -1
  122. package/dist/C_Form2.cjs +1 -0
  123. package/dist/C_Form2.js +1 -2
  124. package/dist/C_FormSearch.cjs +1 -1
  125. package/dist/C_FormSearch.css +47 -47
  126. package/dist/C_FormSearch.d.cts +2 -2
  127. package/dist/C_FormSearch.d.ts +2 -2
  128. package/dist/C_FormSearch2.cjs +1 -0
  129. package/dist/C_FormSearch2.js +1 -2
  130. package/dist/C_FormulaEditor.cjs +1 -1
  131. package/dist/C_FormulaEditor.css +64 -38
  132. package/dist/C_FormulaEditor.d.cts +1 -1
  133. package/dist/C_FormulaEditor.js +1 -1
  134. package/dist/C_FormulaEditor2.cjs +2 -0
  135. package/dist/C_FormulaEditor2.js +2 -2
  136. package/dist/C_FullCalendar.cjs +1 -1
  137. package/dist/C_FullCalendar.css +0 -2
  138. package/dist/C_FullCalendar.d.cts +1 -1
  139. package/dist/C_FullCalendar2.cjs +1 -0
  140. package/dist/C_FullCalendar2.js +1 -2
  141. package/dist/C_GlobalSearch.cjs +1 -1
  142. package/dist/C_GlobalSearch.css +111 -113
  143. package/dist/C_GlobalSearch.d.cts +1 -1
  144. package/dist/C_GlobalSearch2.cjs +1 -0
  145. package/dist/C_GlobalSearch2.js +1 -2
  146. package/dist/C_Guide.cjs +1 -1
  147. package/dist/C_Guide.css +1 -0
  148. package/dist/C_Guide.d.cts +1 -1
  149. package/dist/C_Guide.d.ts +1 -1
  150. package/dist/C_Guide.js +1 -1
  151. package/dist/C_Guide2.cjs +1 -0
  152. package/dist/C_Guide2.js +1 -2
  153. package/dist/C_Icon.cjs +1 -1
  154. package/dist/C_Icon.css +58 -0
  155. package/dist/C_Icon.d.cts +1 -1
  156. package/dist/C_Icon.d.ts +1 -1
  157. package/dist/C_Icon2.cjs +1 -0
  158. package/dist/C_Icon2.js +1 -2
  159. package/dist/C_ImageCropper.cjs +1 -1
  160. package/dist/C_ImageCropper.css +0 -2
  161. package/dist/C_ImageCropper.d.cts +1 -1
  162. package/dist/C_ImageCropper.js +1 -1
  163. package/dist/C_ImageCropper2.cjs +1 -0
  164. package/dist/C_ImageCropper2.js +1 -2
  165. package/dist/C_Language.cjs +1 -1
  166. package/dist/C_Language.css +1 -0
  167. package/dist/C_Language.d.cts +1 -1
  168. package/dist/C_Language.d.ts +1 -1
  169. package/dist/C_Language.js +1 -1
  170. package/dist/C_Language2.cjs +1 -0
  171. package/dist/C_Language2.js +1 -2
  172. package/dist/C_Login.cjs +1 -1
  173. package/dist/C_Login.css +92 -76
  174. package/dist/C_Login.d.cts +1 -1
  175. package/dist/C_Login.d.ts +1 -1
  176. package/dist/C_Login.js +1 -1
  177. package/dist/C_Login2.cjs +1 -0
  178. package/dist/C_Login2.js +1 -2
  179. package/dist/C_Map.cjs +1 -1
  180. package/dist/C_Map.css +665 -5
  181. package/dist/C_Map.d.cts +1 -1
  182. package/dist/C_Map2.cjs +1 -0
  183. package/dist/C_Map2.js +1 -2
  184. package/dist/C_Markdown.cjs +1 -1
  185. package/dist/C_Markdown.css +940 -35
  186. package/dist/C_Markdown.d.cts +1 -1
  187. package/dist/C_Markdown.d.ts +1 -1
  188. package/dist/C_Markdown2.cjs +1 -0
  189. package/dist/C_Markdown2.js +1 -2
  190. package/dist/C_Menu.cjs +1 -1
  191. package/dist/C_Menu.css +1 -0
  192. package/dist/C_Menu.d.cts +3 -3
  193. package/dist/C_Menu.d.ts +3 -3
  194. package/dist/C_Menu2.cjs +1 -0
  195. package/dist/C_Menu2.js +1 -2
  196. package/dist/C_NotificationCenter.cjs +1 -1
  197. package/dist/C_NotificationCenter.css +95 -47
  198. package/dist/C_NotificationCenter.d.cts +1 -1
  199. package/dist/C_NotificationCenter.js +1 -1
  200. package/dist/C_NotificationCenter2.cjs +1 -0
  201. package/dist/C_NotificationCenter2.js +1 -2
  202. package/dist/C_OrgChart.cjs +1 -1
  203. package/dist/C_OrgChart.css +6 -4
  204. package/dist/C_OrgChart.d.cts +1 -1
  205. package/dist/C_OrgChart.d.ts +1 -1
  206. package/dist/C_OrgChart2.cjs +1 -0
  207. package/dist/C_OrgChart2.js +1 -2
  208. package/dist/C_Progress.cjs +1 -1
  209. package/dist/C_Progress.css +1 -0
  210. package/dist/C_Progress.d.cts +1 -1
  211. package/dist/C_Progress.d.ts +1 -1
  212. package/dist/C_Progress2.cjs +1 -0
  213. package/dist/C_Progress2.js +1 -2
  214. package/dist/C_QRCode.cjs +1 -1
  215. package/dist/C_QRCode.css +8 -10
  216. package/dist/C_QRCode.d.cts +1 -1
  217. package/dist/C_QRCode2.cjs +1 -0
  218. package/dist/C_QRCode2.js +1 -2
  219. package/dist/C_Signature.cjs +1 -1
  220. package/dist/C_Signature.css +0 -2
  221. package/dist/C_Signature.d.cts +1 -1
  222. package/dist/C_Signature.js +1 -1
  223. package/dist/C_Signature2.cjs +1 -0
  224. package/dist/C_Signature2.js +1 -2
  225. package/dist/C_Skeleton.cjs +1 -1
  226. package/dist/C_Skeleton.css +0 -2
  227. package/dist/C_Skeleton.d.cts +1 -1
  228. package/dist/C_Skeleton.d.ts +1 -1
  229. package/dist/C_Skeleton2.cjs +1 -0
  230. package/dist/C_Skeleton2.js +1 -2
  231. package/dist/C_SplitPane.cjs +1 -1
  232. package/dist/C_SplitPane.css +27 -11
  233. package/dist/C_SplitPane.d.cts +1 -1
  234. package/dist/C_SplitPane.js +1 -1
  235. package/dist/C_SplitPane2.cjs +1 -0
  236. package/dist/C_SplitPane2.js +1 -2
  237. package/dist/C_Steps.cjs +1 -1
  238. package/dist/C_Steps.css +37 -39
  239. package/dist/C_Steps.d.cts +1 -1
  240. package/dist/C_Steps.d.ts +1 -1
  241. package/dist/C_Steps2.cjs +1 -0
  242. package/dist/C_Steps2.js +1 -2
  243. package/dist/C_Table.base.css +1588 -0
  244. package/dist/C_Table.cjs +1 -1
  245. package/dist/C_Table.css +1202 -112
  246. package/dist/C_Table.d.cts +2 -2
  247. package/dist/C_Table.d.ts +11 -2
  248. package/dist/C_Table.js +1 -1
  249. package/dist/C_TagsView.cjs +1 -1
  250. package/dist/C_TagsView.css +44 -40
  251. package/dist/C_TagsView.d.cts +2 -2
  252. package/dist/C_TagsView.d.ts +2 -2
  253. package/dist/C_TagsView.js +1 -1
  254. package/dist/C_TagsView2.cjs +1 -0
  255. package/dist/C_TagsView2.js +1 -2
  256. package/dist/C_Theme.cjs +1 -1
  257. package/dist/C_Theme.css +1 -0
  258. package/dist/C_Theme.d.cts +1 -1
  259. package/dist/C_Theme.d.ts +1 -1
  260. package/dist/C_Theme.js +1 -1
  261. package/dist/C_Theme2.cjs +1 -0
  262. package/dist/C_Theme2.js +1 -2
  263. package/dist/C_Time.cjs +1 -1
  264. package/dist/C_Time.css +0 -2
  265. package/dist/C_Time.d.cts +2 -2
  266. package/dist/C_Time.d.ts +2 -2
  267. package/dist/C_Time2.cjs +1 -0
  268. package/dist/C_Time2.js +1 -2
  269. package/dist/C_Timeline.cjs +1 -1
  270. package/dist/C_Timeline.css +44 -46
  271. package/dist/C_Timeline.d.cts +1 -1
  272. package/dist/C_Timeline.js +1 -1
  273. package/dist/C_Timeline2.cjs +1 -0
  274. package/dist/C_Timeline2.js +1 -2
  275. package/dist/C_Transfer.cjs +1 -1
  276. package/dist/C_Transfer.css +21 -9
  277. package/dist/C_Transfer.d.cts +1 -1
  278. package/dist/C_Transfer.js +1 -1
  279. package/dist/C_Transfer2.cjs +1 -0
  280. package/dist/C_Transfer2.js +1 -2
  281. package/dist/C_Tree.cjs +1 -1
  282. package/dist/C_Tree.css +0 -2
  283. package/dist/C_Tree.d.cts +1 -1
  284. package/dist/C_Tree.js +1 -1
  285. package/dist/C_Tree2.cjs +1 -0
  286. package/dist/C_Tree2.js +1 -2
  287. package/dist/C_Upload.cjs +1 -1
  288. package/dist/C_Upload.css +17 -17
  289. package/dist/C_Upload.d.cts +1 -1
  290. package/dist/C_Upload.js +1 -1
  291. package/dist/C_Upload2.cjs +1 -0
  292. package/dist/C_Upload2.js +1 -25
  293. package/dist/C_VideoPlayer.cjs +1 -1
  294. package/dist/C_VideoPlayer.css +35 -24
  295. package/dist/C_VideoPlayer.d.cts +2 -2
  296. package/dist/C_VideoPlayer.d.ts +2 -2
  297. package/dist/C_VideoPlayer.js +1 -1
  298. package/dist/C_VideoPlayer2.cjs +3 -0
  299. package/dist/C_VideoPlayer2.js +3 -4
  300. package/dist/C_VtableGantt.cjs +1 -1
  301. package/dist/C_VtableGantt.css +15 -7
  302. package/dist/C_VtableGantt.d.cts +1 -1
  303. package/dist/C_VtableGantt.d.ts +1 -1
  304. package/dist/C_VtableGantt.js +1 -1
  305. package/dist/C_VtableGantt2.cjs +1 -0
  306. package/dist/C_VtableGantt2.js +1 -2
  307. package/dist/C_WaterFall.cjs +1 -1
  308. package/dist/C_WaterFall.css +15 -17
  309. package/dist/C_WaterFall.d.cts +1 -1
  310. package/dist/C_WaterFall2.cjs +1 -0
  311. package/dist/C_WaterFall2.js +1 -2
  312. package/dist/C_WorkFlow.cjs +1 -1
  313. package/dist/C_WorkFlow.css +257 -197
  314. package/dist/C_WorkFlow.d.cts +1 -1
  315. package/dist/C_WorkFlow.js +1 -1
  316. package/dist/C_WorkFlow2.cjs +1 -0
  317. package/dist/C_WorkFlow2.js +1 -2
  318. package/dist/city.cjs +1 -0
  319. package/dist/city.js +1 -2
  320. package/dist/config.cjs +1 -0
  321. package/dist/config.d.cts +33 -0
  322. package/dist/config.d.ts +33 -0
  323. package/dist/config.js +1 -0
  324. package/dist/constants.d.cts +272 -0
  325. package/dist/constants.d.ts +2 -3
  326. package/dist/constants2.d.cts +177 -0
  327. package/dist/constants2.d.ts +3 -4
  328. package/dist/constants3.d.cts +479 -0
  329. package/dist/constants3.d.ts +16 -12
  330. package/dist/constants4.d.cts +420 -0
  331. package/dist/constants4.d.ts +27 -37
  332. package/dist/constants5.d.cts +491 -0
  333. package/dist/constants5.d.ts +20 -4
  334. package/dist/data.cjs +1 -0
  335. package/dist/data.d.cts +67 -0
  336. package/dist/data.d.ts +12 -12
  337. package/dist/data.js +1 -0
  338. package/dist/export-helper.cjs +1 -0
  339. package/dist/helpers.cjs +1 -0
  340. package/dist/helpers.js +1 -0
  341. package/dist/html.cjs +1 -0
  342. package/dist/html.js +1 -0
  343. package/dist/images/marker-icon-2x.png +0 -0
  344. package/dist/images/marker-icon.png +0 -0
  345. package/dist/images/marker-shadow.png +0 -0
  346. package/dist/index.cjs +1 -2
  347. package/dist/index.d.cts +60 -99
  348. package/dist/index.d.ts +75 -164
  349. package/dist/index.js +1 -2
  350. package/dist/index.vue.d.cts +79 -0
  351. package/dist/index.vue.d.ts +3 -4
  352. package/dist/index10.vue.d.cts +47 -0
  353. package/dist/index10.vue.d.ts +80 -40
  354. package/dist/index11.vue.d.cts +88 -0
  355. package/dist/index11.vue.d.ts +68 -78
  356. package/dist/index12.vue.d.cts +79 -0
  357. package/dist/index12.vue.d.ts +49 -45
  358. package/dist/index13.vue.d.cts +64 -0
  359. package/dist/index13.vue.d.ts +99 -34
  360. package/dist/index14.vue.d.cts +106 -0
  361. package/dist/index14.vue.d.ts +82 -99
  362. package/dist/index15.vue.d.cts +91 -0
  363. package/dist/index15.vue.d.ts +25 -83
  364. package/dist/index16.vue.d.cts +34 -0
  365. package/dist/index16.vue.d.ts +113 -24
  366. package/dist/index17.vue.d.cts +124 -0
  367. package/dist/index17.vue.d.ts +76 -110
  368. package/dist/index18.vue.d.cts +83 -0
  369. package/dist/index18.vue.d.ts +67 -73
  370. package/dist/index19.vue.d.cts +101 -0
  371. package/dist/index19.vue.d.ts +93 -59
  372. package/dist/index2.vue.d.cts +82 -0
  373. package/dist/index2.vue.d.ts +7 -8
  374. package/dist/index20.vue.d.cts +54 -0
  375. package/dist/index20.vue.d.ts +40 -88
  376. package/dist/index21.vue.d.cts +103 -0
  377. package/dist/index21.vue.d.ts +89 -41
  378. package/dist/index22.vue.d.cts +45 -0
  379. package/dist/index22.vue.d.ts +37 -96
  380. package/dist/index23.vue.d.cts +19 -0
  381. package/dist/index23.vue.d.ts +8 -35
  382. package/dist/{index28.vue.d.ts → index24.vue.d.cts} +4 -5
  383. package/dist/index24.vue.d.ts +650 -86
  384. package/dist/index25.vue.d.cts +96 -0
  385. package/dist/index25.vue.d.ts +86 -10
  386. package/dist/index26.vue.d.cts +111 -0
  387. package/dist/index26.vue.d.ts +4 -5
  388. package/dist/index27.vue.d.cts +80 -0
  389. package/dist/index27.vue.d.ts +2 -3
  390. package/dist/index3.vue.d.cts +87 -0
  391. package/dist/index3.vue.d.ts +4 -5
  392. package/dist/index4.vue.d.cts +37 -0
  393. package/dist/index4.vue.d.ts +6 -7
  394. package/dist/index5.vue.d.cts +133 -0
  395. package/dist/index5.vue.d.ts +2 -3
  396. package/dist/index6.vue.d.cts +29 -0
  397. package/dist/index6.vue.d.ts +25 -38
  398. package/dist/index7.vue.d.cts +32 -0
  399. package/dist/index7.vue.d.ts +117 -23
  400. package/dist/index8.vue.d.cts +127 -0
  401. package/dist/index8.vue.d.ts +17 -111
  402. package/dist/index9.vue.d.cts +34 -0
  403. package/dist/index9.vue.d.ts +38 -26
  404. package/dist/public.d.ts +166 -0
  405. package/dist/resolver.cjs +1 -2
  406. package/dist/resolver.d.cts +6 -4
  407. package/dist/resolver.d.ts +6 -4
  408. package/dist/resolver.js +1 -2
  409. package/dist/storage.cjs +1 -0
  410. package/dist/storage.js +1 -2
  411. package/dist/style.css +9864 -7702
  412. package/dist/types.d.cts +107 -0
  413. package/dist/types.d.ts +52 -102
  414. package/dist/types2.d.cts +58 -0
  415. package/dist/useCalendarEvents.d.cts +150 -0
  416. package/dist/useCalendarEvents.d.ts +3 -4
  417. package/dist/useCollapsePanel.d.cts +133 -0
  418. package/dist/useCollapsePanel.d.ts +2 -3
  419. package/dist/useCropperCore.d.cts +104 -0
  420. package/dist/useCropperCore.d.ts +11 -9
  421. package/dist/useDraggableLayout.d.cts +196 -0
  422. package/dist/useDraggableLayout.d.ts +17 -15
  423. package/dist/useDynamicFormState.d.cts +749 -0
  424. package/dist/useDynamicFormState.d.ts +234 -112
  425. package/dist/useEdgeInteraction.d.cts +324 -0
  426. package/dist/useEdgeInteraction.d.ts +6 -7
  427. package/dist/useFullscreen.d.cts +165 -0
  428. package/dist/useFullscreen.d.ts +1 -2
  429. package/dist/useGlobalSearch.d.cts +110 -0
  430. package/dist/useGlobalSearch.d.ts +17 -9
  431. package/dist/useInfiniteScroll.d.cts +177 -0
  432. package/dist/useInfiniteScroll.d.ts +13 -5
  433. package/dist/useQRCode.d.cts +89 -0
  434. package/dist/useQRCode.d.ts +5 -3
  435. package/dist/useSearchState.d.cts +871 -0
  436. package/dist/useSearchState.d.ts +830 -30
  437. package/dist/useSignatureHistory.d.cts +197 -0
  438. package/dist/useSignatureHistory.d.ts +10 -5
  439. package/dist/useSplitResize.d.cts +157 -0
  440. package/dist/useSplitResize.d.ts +3 -4
  441. package/dist/useTableQuery.cjs +3 -0
  442. package/dist/{useModalEdit.d.ts → useTableQuery.d.cts} +624 -211
  443. package/dist/useTableQuery.d.ts +1721 -0
  444. package/dist/useTableQuery.js +3 -0
  445. package/dist/useTimeSelection.d.cts +116 -0
  446. package/dist/useTimeSelection.d.ts +20 -9
  447. package/dist/useTreeOperations.d.cts +185 -0
  448. package/dist/useTreeOperations.d.ts +13 -11
  449. package/dist/useWorkflowValidation.d.cts +1051 -0
  450. package/dist/useWorkflowValidation.d.ts +62 -63
  451. package/dist/verification.d.cts +58 -0
  452. package/dist/verification.d.ts +58 -0
  453. package/package.json +102 -69
  454. package/dist/C_ActionBar-BAcfOfRw.css.map +0 -1
  455. package/dist/C_ActionBar-DG2HW7t5.css.map +0 -1
  456. package/dist/C_ActionBar.js.map +0 -1
  457. package/dist/C_ActionBar2.js.map +0 -1
  458. package/dist/C_AntV-D_jTK8oZ.css.map +0 -1
  459. package/dist/C_AntV-DanTAq22.css.map +0 -1
  460. package/dist/C_AntV.js.map +0 -1
  461. package/dist/C_AntV2.js.map +0 -1
  462. package/dist/C_AudioPlayer-BnYIJXPD.css.map +0 -1
  463. package/dist/C_AudioPlayer-Cj4lUdZo.css.map +0 -1
  464. package/dist/C_AudioPlayer.js.map +0 -1
  465. package/dist/C_AudioPlayer2.js.map +0 -1
  466. package/dist/C_AvatarGroup-B9d-GJ9c.css.map +0 -1
  467. package/dist/C_AvatarGroup-D3PwsKME.css.map +0 -1
  468. package/dist/C_AvatarGroup.js.map +0 -1
  469. package/dist/C_AvatarGroup2.js.map +0 -1
  470. package/dist/C_Barcode-CAU2mFcx.css.map +0 -1
  471. package/dist/C_Barcode-D2oJaEg3.css.map +0 -1
  472. package/dist/C_Barcode.js.map +0 -1
  473. package/dist/C_Barcode2.js.map +0 -1
  474. package/dist/C_Breadcrumb-BiH0d9AI.css.map +0 -1
  475. package/dist/C_Breadcrumb-DOcLt9uX.css.map +0 -1
  476. package/dist/C_Breadcrumb.js.map +0 -1
  477. package/dist/C_Breadcrumb2.js.map +0 -1
  478. package/dist/C_Captcha-B3yrtG7t.css.map +0 -1
  479. package/dist/C_Captcha-D4hDdWDu.css.map +0 -1
  480. package/dist/C_Captcha.js.map +0 -1
  481. package/dist/C_Captcha2.js.map +0 -1
  482. package/dist/C_Cascade-CmCrLVft.css.map +0 -1
  483. package/dist/C_Cascade-U6A5AJJb.css.map +0 -1
  484. package/dist/C_Cascade.js.map +0 -1
  485. package/dist/C_Cascade2.js.map +0 -1
  486. package/dist/C_Chat-DeUv6uuI.css.map +0 -1
  487. package/dist/C_Chat-DwatUQqu.css.map +0 -1
  488. package/dist/C_Chat.js.map +0 -1
  489. package/dist/C_Chat2.js.map +0 -1
  490. package/dist/C_City-DdkX6u64.css.map +0 -1
  491. package/dist/C_City-iP-dIvui.css.map +0 -1
  492. package/dist/C_City.js.map +0 -1
  493. package/dist/C_City2.js.map +0 -1
  494. package/dist/C_Code-BWmJMNS7.css.map +0 -1
  495. package/dist/C_Code-k14vjgVl.css.map +0 -1
  496. package/dist/C_Code.js.map +0 -1
  497. package/dist/C_Code2.js.map +0 -1
  498. package/dist/C_CollapsePanel-BSKcWSmY.css.map +0 -1
  499. package/dist/C_CollapsePanel-DRvRq8tB.css.map +0 -1
  500. package/dist/C_CollapsePanel.js.map +0 -1
  501. package/dist/C_CollapsePanel2.js.map +0 -1
  502. package/dist/C_ContextMenu-C9iuxG2n.css.map +0 -1
  503. package/dist/C_ContextMenu-CD0iaSju.css.map +0 -1
  504. package/dist/C_ContextMenu.js.map +0 -1
  505. package/dist/C_ContextMenu2.js.map +0 -1
  506. package/dist/C_Cron-BIQ2By9v.css.map +0 -1
  507. package/dist/C_Cron-BNSAGD6A.css.map +0 -1
  508. package/dist/C_Cron.js.map +0 -1
  509. package/dist/C_Cron2.js.map +0 -1
  510. package/dist/C_Date.js.map +0 -1
  511. package/dist/C_Date2.js.map +0 -1
  512. package/dist/C_Draggable-BDAbk4s5.css.map +0 -1
  513. package/dist/C_Draggable-UhfDLXop.css.map +0 -1
  514. package/dist/C_Draggable.js.map +0 -1
  515. package/dist/C_Draggable2.js.map +0 -1
  516. package/dist/C_Editor-CZOaChru.css.map +0 -1
  517. package/dist/C_Editor-kJAQq5z6.css.map +0 -1
  518. package/dist/C_Editor.js.map +0 -1
  519. package/dist/C_Editor2.js.map +0 -1
  520. package/dist/C_FilePreview-Bs_omgJS.css.map +0 -1
  521. package/dist/C_FilePreview-DbwoyF2j.css.map +0 -1
  522. package/dist/C_FilePreview.js.map +0 -1
  523. package/dist/C_FilePreview2.js.map +0 -1
  524. package/dist/C_Form-B_2ql62V.css.map +0 -1
  525. package/dist/C_Form-CX-7xsz3.css.map +0 -1
  526. package/dist/C_Form.js.map +0 -1
  527. package/dist/C_Form2.js.map +0 -1
  528. package/dist/C_FormSearch-C3F8peAY.css.map +0 -1
  529. package/dist/C_FormSearch-qW56bJPo.css.map +0 -1
  530. package/dist/C_FormSearch.js.map +0 -1
  531. package/dist/C_FormSearch2.js.map +0 -1
  532. package/dist/C_FormulaEditor-ChOvDbsZ.css.map +0 -1
  533. package/dist/C_FormulaEditor-mfiIUDY4.css.map +0 -1
  534. package/dist/C_FormulaEditor.js.map +0 -1
  535. package/dist/C_FormulaEditor2.js.map +0 -1
  536. package/dist/C_FullCalendar-C2nsqrgS.css.map +0 -1
  537. package/dist/C_FullCalendar-DdFtXVhy.css.map +0 -1
  538. package/dist/C_FullCalendar.js.map +0 -1
  539. package/dist/C_FullCalendar2.js.map +0 -1
  540. package/dist/C_GlobalSearch-Dq4Wo01K.css.map +0 -1
  541. package/dist/C_GlobalSearch-RB7J43gu.css.map +0 -1
  542. package/dist/C_GlobalSearch.js.map +0 -1
  543. package/dist/C_GlobalSearch2.js.map +0 -1
  544. package/dist/C_Guide.js.map +0 -1
  545. package/dist/C_Guide2.js.map +0 -1
  546. package/dist/C_Icon.js.map +0 -1
  547. package/dist/C_Icon2.js.map +0 -1
  548. package/dist/C_ImageCropper-CdTX2nxU.css.map +0 -1
  549. package/dist/C_ImageCropper-ilOqPklJ.css.map +0 -1
  550. package/dist/C_ImageCropper.js.map +0 -1
  551. package/dist/C_ImageCropper2.js.map +0 -1
  552. package/dist/C_Language.js.map +0 -1
  553. package/dist/C_Language2.js.map +0 -1
  554. package/dist/C_Login-BrQBs_m4.css.map +0 -1
  555. package/dist/C_Login-xMI1_ojE.css.map +0 -1
  556. package/dist/C_Login.js.map +0 -1
  557. package/dist/C_Login2.js.map +0 -1
  558. package/dist/C_Map-CWMMN58_.css.map +0 -1
  559. package/dist/C_Map-xSR2H-9o.css.map +0 -1
  560. package/dist/C_Map.js.map +0 -1
  561. package/dist/C_Map2.js.map +0 -1
  562. package/dist/C_Markdown-C7lTVJ1S.css.map +0 -1
  563. package/dist/C_Markdown-CZDm_baQ.css.map +0 -1
  564. package/dist/C_Markdown.js.map +0 -1
  565. package/dist/C_Markdown2.js.map +0 -1
  566. package/dist/C_Menu.js.map +0 -1
  567. package/dist/C_Menu2.js.map +0 -1
  568. package/dist/C_NotificationCenter-D7GGGWg7.css.map +0 -1
  569. package/dist/C_NotificationCenter-SSmeHA45.css.map +0 -1
  570. package/dist/C_NotificationCenter.js.map +0 -1
  571. package/dist/C_NotificationCenter2.js.map +0 -1
  572. package/dist/C_OrgChart-BXEp4i4w.css.map +0 -1
  573. package/dist/C_OrgChart-DqBufOv5.css.map +0 -1
  574. package/dist/C_OrgChart.js.map +0 -1
  575. package/dist/C_OrgChart2.js.map +0 -1
  576. package/dist/C_Progress.js.map +0 -1
  577. package/dist/C_Progress2.js.map +0 -1
  578. package/dist/C_QRCode-B_1lHUdx.css.map +0 -1
  579. package/dist/C_QRCode-CuQRYu0s.css.map +0 -1
  580. package/dist/C_QRCode.js.map +0 -1
  581. package/dist/C_QRCode2.js.map +0 -1
  582. package/dist/C_Signature-Bp-RBpAl.css.map +0 -1
  583. package/dist/C_Signature-Cb2VW1ok.css.map +0 -1
  584. package/dist/C_Signature.js.map +0 -1
  585. package/dist/C_Signature2.js.map +0 -1
  586. package/dist/C_Skeleton-BJoFYwrF.css.map +0 -1
  587. package/dist/C_Skeleton-Djsjw8XY.css.map +0 -1
  588. package/dist/C_Skeleton.js.map +0 -1
  589. package/dist/C_Skeleton2.js.map +0 -1
  590. package/dist/C_SplitPane-CgGWYsRE.css.map +0 -1
  591. package/dist/C_SplitPane-DGf6WEeR.css.map +0 -1
  592. package/dist/C_SplitPane.js.map +0 -1
  593. package/dist/C_SplitPane2.js.map +0 -1
  594. package/dist/C_Steps-27Exh7PM.css.map +0 -1
  595. package/dist/C_Steps-DU9ZsFrs.css.map +0 -1
  596. package/dist/C_Steps.js.map +0 -1
  597. package/dist/C_Steps2.js.map +0 -1
  598. package/dist/C_Table--kbQaaBv.css.map +0 -1
  599. package/dist/C_Table-D631kdXC.css.map +0 -1
  600. package/dist/C_Table.js.map +0 -1
  601. package/dist/C_Table2.js +0 -4
  602. package/dist/C_Table2.js.map +0 -1
  603. package/dist/C_TagsView-DtGtYhBH.css.map +0 -1
  604. package/dist/C_TagsView-s1dnSJAV.css.map +0 -1
  605. package/dist/C_TagsView.js.map +0 -1
  606. package/dist/C_TagsView2.js.map +0 -1
  607. package/dist/C_Theme.js.map +0 -1
  608. package/dist/C_Theme2.js.map +0 -1
  609. package/dist/C_Time-BQ9PGy8B.css.map +0 -1
  610. package/dist/C_Time-CO73krC6.css.map +0 -1
  611. package/dist/C_Time.js.map +0 -1
  612. package/dist/C_Time2.js.map +0 -1
  613. package/dist/C_Timeline-DdESmqwL.css.map +0 -1
  614. package/dist/C_Timeline-irQcO-b7.css.map +0 -1
  615. package/dist/C_Timeline.js.map +0 -1
  616. package/dist/C_Timeline2.js.map +0 -1
  617. package/dist/C_Transfer-11y3k-WD.css.map +0 -1
  618. package/dist/C_Transfer-EzdHzZNv.css.map +0 -1
  619. package/dist/C_Transfer.js.map +0 -1
  620. package/dist/C_Transfer2.js.map +0 -1
  621. package/dist/C_Tree-CsXgGfhn.css.map +0 -1
  622. package/dist/C_Tree-P7eRpnOa.css.map +0 -1
  623. package/dist/C_Tree.js.map +0 -1
  624. package/dist/C_Tree2.js.map +0 -1
  625. package/dist/C_Upload-CHjiVVh7.css.map +0 -1
  626. package/dist/C_Upload-DE3OLsZD.css.map +0 -1
  627. package/dist/C_Upload.js.map +0 -1
  628. package/dist/C_Upload2.js.map +0 -1
  629. package/dist/C_VideoPlayer-CnaNOFtW.css.map +0 -1
  630. package/dist/C_VideoPlayer-cnP88zNV.css.map +0 -1
  631. package/dist/C_VideoPlayer.js.map +0 -1
  632. package/dist/C_VideoPlayer2.js.map +0 -1
  633. package/dist/C_VtableGantt-CZlJgvpC.css.map +0 -1
  634. package/dist/C_VtableGantt-DKfQkql0.css.map +0 -1
  635. package/dist/C_VtableGantt.js.map +0 -1
  636. package/dist/C_VtableGantt2.js.map +0 -1
  637. package/dist/C_WaterFall-BJ_39ext.css.map +0 -1
  638. package/dist/C_WaterFall-cYc1vntE.css.map +0 -1
  639. package/dist/C_WaterFall.js.map +0 -1
  640. package/dist/C_WaterFall2.js.map +0 -1
  641. package/dist/C_WorkFlow-BFzRGGhJ.css.map +0 -1
  642. package/dist/C_WorkFlow-DSyCOvRp.css.map +0 -1
  643. package/dist/C_WorkFlow.js.map +0 -1
  644. package/dist/C_WorkFlow2.js.map +0 -1
  645. package/dist/city.js.map +0 -1
  646. package/dist/index.cjs.map +0 -1
  647. package/dist/index.js.map +0 -1
  648. package/dist/index2.d.ts +0 -120
  649. package/dist/resolver.cjs.map +0 -1
  650. package/dist/resolver.js.map +0 -1
  651. package/dist/storage.js.map +0 -1
  652. /package/dist/{chunk.js → chunk.cjs} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_AudioPlayer.js","names":["theme","showCover","showPlaylist","tracks"],"sources":["../src/components/C_AudioPlayer/types.ts","../src/components/C_AudioPlayer/index.vue","../src/components/C_AudioPlayer/index.vue"],"sourcesContent":["/**\n * @Description: Audio player component type definitions\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\n */\n\n/** Single audio track */\nexport interface AudioTrack {\n /** Unique identifier */\n id: string | number\n /** Track title */\n title: string\n /** Artist / author */\n artist?: string\n /** Audio source URL */\n src: string\n /** Cover image URL */\n cover?: string\n /** Duration in seconds (optional, for static display) */\n duration?: number\n /** Extra data */\n extra?: Record<string, unknown>\n}\n\n/** Component props */\nexport interface AudioPlayerProps {\n /** Playlist */\n tracks: AudioTrack[]\n /** Initial active index */\n initialIndex?: number\n /** Whether to show the playlist panel */\n showPlaylist?: boolean\n /** Whether to show the cover art */\n showCover?: boolean\n /** Whether to auto-play on mount */\n autoplay?: boolean\n /** Playback mode */\n mode?: 'list' | 'loop' | 'single' | 'shuffle'\n /** Theme appearance */\n theme?: 'default' | 'minimal'\n}\n\n/** Default props */\nexport const DEFAULT_AUDIO_PLAYER_PROPS: Partial<AudioPlayerProps> = {\n initialIndex: 0,\n showPlaylist: true,\n showCover: true,\n autoplay: false,\n mode: 'list',\n theme: 'default',\n}\n\n/** Playback mode icon map */\nexport const MODE_ICON_MAP: Record<string, { icon: string; label: string }> = {\n list: { icon: 'mdi:repeat', label: 'List Loop' },\n loop: { icon: 'mdi:repeat-once', label: 'Single Loop' },\n single: { icon: 'mdi:numeric-1-box', label: 'Play Once' },\n shuffle: { icon: 'mdi:shuffle-variant', label: 'Shuffle' },\n}\n\n/** Format seconds to mm:ss */\nexport function formatTime(seconds: number): string {\n if (!Number.isFinite(seconds) || seconds < 0) return '00:00'\n const m = Math.floor(seconds / 60)\n const s = Math.floor(seconds % 60)\n return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`\n}\n","<!--\n * @Description: 音频播放器组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n class=\"c-audio-player\"\n :class=\"{ 'is-minimal': theme === 'minimal' }\"\n >\n <!-- ================ Now Playing ================ -->\n <div class=\"c-audio-player__now-playing\">\n <div\n v-if=\"showCover\"\n class=\"c-audio-player__cover\"\n >\n <img\n v-if=\"currentTrack?.cover\"\n :src=\"currentTrack.cover\"\n :alt=\"currentTrack.title\"\n />\n <C_Icon\n v-else\n name=\"mdi:music-note\"\n class=\"c-audio-player__cover-placeholder\"\n />\n </div>\n <div class=\"c-audio-player__info\">\n <div class=\"c-audio-player__title\">{{\n currentTrack?.title ?? '未选择曲目'\n }}</div>\n <div\n v-if=\"currentTrack?.artist\"\n class=\"c-audio-player__artist\"\n >\n {{ currentTrack.artist }}\n </div>\n </div>\n </div>\n\n <!-- ================ Progress ================ -->\n <div\n v-if=\"theme !== 'minimal'\"\n class=\"c-audio-player__progress\"\n >\n <div\n class=\"c-audio-player__progress-bar\"\n @click=\"seekByClick\"\n >\n <div\n class=\"c-audio-player__progress-fill\"\n :style=\"{ width: `${progressPercent}%` }\"\n />\n </div>\n <div class=\"c-audio-player__time\">\n <span>{{ formatTime(currentTime) }}</span>\n <span>{{ formatTime(totalDuration) }}</span>\n </div>\n </div>\n\n <!-- ================ Controls ================ -->\n <div class=\"c-audio-player__controls\">\n <button\n class=\"c-audio-player__ctrl-btn is-mode\"\n :title=\"currentModeLabel\"\n @click=\"cycleMode\"\n >\n <C_Icon :name=\"currentModeIcon\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-skip\"\n title=\"上一曲\"\n @click=\"prev\"\n >\n <C_Icon name=\"mdi:skip-previous\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-play\"\n :title=\"isPlaying ? '暂停' : '播放'\"\n @click=\"togglePlay\"\n >\n <C_Icon :name=\"isPlaying ? 'mdi:pause' : 'mdi:play'\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-skip\"\n title=\"下一曲\"\n @click=\"next\"\n >\n <C_Icon name=\"mdi:skip-next\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-mode\"\n title=\"播放列表\"\n @click=\"playlistVisible = !playlistVisible\"\n >\n <C_Icon name=\"mdi:playlist-music\" />\n </button>\n </div>\n\n <!-- ================ Volume ================ -->\n <div\n v-if=\"theme !== 'minimal'\"\n class=\"c-audio-player__volume\"\n >\n <C_Icon\n :name=\"volumeIcon\"\n class=\"c-audio-player__volume-icon\"\n @click=\"toggleMute\"\n />\n <NSlider\n v-model:value=\"volume\"\n :min=\"0\"\n :max=\"100\"\n :step=\"1\"\n class=\"c-audio-player__volume-slider\"\n @update:value=\"handleVolumeChange\"\n />\n </div>\n\n <!-- ================ Playlist ================ -->\n <div\n v-if=\"showPlaylist && playlistVisible && theme !== 'minimal'\"\n class=\"c-audio-player__playlist\"\n >\n <div class=\"c-audio-player__playlist-header\">\n <span>播放列表</span>\n <span>{{ tracks.length }} 首</span>\n </div>\n <div\n v-for=\"(track, idx) in tracks\"\n :key=\"track.id\"\n class=\"c-audio-player__track\"\n :class=\"{ 'is-active': idx === activeIndex }\"\n @click=\"playTrack(idx)\"\n >\n <span class=\"c-audio-player__track-index\">\n <C_Icon\n v-if=\"idx === activeIndex && isPlaying\"\n name=\"mdi:volume-high\"\n />\n <template v-else>{{ idx + 1 }}</template>\n </span>\n <div class=\"c-audio-player__track-info\">\n <div class=\"c-audio-player__track-title\">{{ track.title }}</div>\n <div\n v-if=\"track.artist\"\n class=\"c-audio-player__track-artist\"\n >\n {{ track.artist }}\n </div>\n </div>\n <span\n v-if=\"track.duration\"\n class=\"c-audio-player__track-duration\"\n >\n {{ formatTime(track.duration) }}\n </span>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, onBeforeUnmount, ref, watch } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_AUDIO_PLAYER_PROPS,\n formatTime,\n MODE_ICON_MAP,\n type AudioPlayerProps,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n tracks: AudioPlayerProps['tracks']\n initialIndex?: number\n showPlaylist?: boolean\n showCover?: boolean\n autoplay?: boolean\n mode?: AudioPlayerProps['mode']\n theme?: AudioPlayerProps['theme']\n }>(),\n {\n initialIndex: DEFAULT_AUDIO_PLAYER_PROPS.initialIndex,\n showPlaylist: DEFAULT_AUDIO_PLAYER_PROPS.showPlaylist,\n showCover: true,\n autoplay: DEFAULT_AUDIO_PLAYER_PROPS.autoplay,\n mode: DEFAULT_AUDIO_PLAYER_PROPS.mode,\n theme: DEFAULT_AUDIO_PLAYER_PROPS.theme,\n }\n )\n\n const emit = defineEmits<{\n play: [index: number]\n pause: []\n ended: [index: number]\n modeChange: [mode: string]\n }>()\n\n // ==================== Audio Core ====================\n\n let audio: HTMLAudioElement | null = null\n\n const activeIndex = ref(props.initialIndex ?? 0)\n const isPlaying = ref(false)\n const currentTime = ref(0)\n const totalDuration = ref(0)\n const volume = ref(70)\n const prevVolume = ref(70)\n const playlistVisible = ref(props.showPlaylist)\n const playMode = ref(props.mode ?? 'list')\n\n const currentTrack = computed(() => props.tracks[activeIndex.value])\n\n const progressPercent = computed(() => {\n if (totalDuration.value === 0) return 0\n return (currentTime.value / totalDuration.value) * 100\n })\n\n const volumeIcon = computed(() => {\n if (volume.value === 0) return 'mdi:volume-off'\n if (volume.value < 40) return 'mdi:volume-low'\n if (volume.value < 70) return 'mdi:volume-medium'\n return 'mdi:volume-high'\n })\n\n const currentModeIcon = computed(\n () => MODE_ICON_MAP[playMode.value]?.icon ?? 'mdi:repeat'\n )\n const currentModeLabel = computed(\n () => MODE_ICON_MAP[playMode.value]?.label ?? '循环'\n )\n\n /** 初始化音频实例 */\n function initAudio() {\n if (audio) {\n audio.pause()\n audio.removeEventListener('timeupdate', onTimeUpdate)\n audio.removeEventListener('loadedmetadata', onMetaLoaded)\n audio.removeEventListener('ended', onEnded)\n }\n const track = currentTrack.value\n if (!track) return\n\n audio = new Audio(track.src)\n audio.volume = volume.value / 100\n audio.addEventListener('timeupdate', onTimeUpdate)\n audio.addEventListener('loadedmetadata', onMetaLoaded)\n audio.addEventListener('ended', onEnded)\n }\n\n /** 播放进度更新回调 */\n function onTimeUpdate() {\n if (audio) currentTime.value = audio.currentTime\n }\n\n /** 音频元数据加载完成回调 */\n function onMetaLoaded() {\n if (audio) totalDuration.value = audio.duration\n }\n\n /** 播放结束回调,根据模式决定下一步 */\n function onEnded() {\n emit('ended', activeIndex.value)\n switch (playMode.value) {\n case 'loop':\n audio?.play().catch(() => {})\n break\n case 'single':\n isPlaying.value = false\n break\n case 'shuffle':\n playTrack(Math.floor(Math.random() * props.tracks.length))\n break\n default: // list\n if (activeIndex.value < props.tracks.length - 1) next()\n else {\n activeIndex.value = 0\n initAudio()\n audio?.play().catch(() => {})\n }\n }\n }\n\n /** 切换播放/暂停 */\n function togglePlay() {\n if (!audio) initAudio()\n if (isPlaying.value) {\n audio?.pause()\n isPlaying.value = false\n emit('pause')\n } else {\n audio?.play().catch(() => {})\n isPlaying.value = true\n emit('play', activeIndex.value)\n }\n }\n\n /** 播放指定索引的曲目 */\n function playTrack(idx: number) {\n activeIndex.value = idx\n initAudio()\n audio?.play().catch(() => {})\n isPlaying.value = true\n emit('play', idx)\n }\n\n /** 上一曲 */\n function prev() {\n const idx =\n activeIndex.value <= 0 ? props.tracks.length - 1 : activeIndex.value - 1\n playTrack(idx)\n }\n\n /** 下一曲 */\n function next() {\n const idx =\n activeIndex.value >= props.tracks.length - 1 ? 0 : activeIndex.value + 1\n playTrack(idx)\n }\n\n /** 点击进度条跳转播放位置 */\n function seekByClick(e: MouseEvent) {\n if (!audio || !totalDuration.value) return\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()\n const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width))\n audio.currentTime = ratio * totalDuration.value\n currentTime.value = audio.currentTime\n }\n\n /** 处理音量变更 */\n function handleVolumeChange(val: number) {\n volume.value = val\n if (audio) audio.volume = val / 100\n }\n\n /** 切换静音状态 */\n function toggleMute() {\n if (volume.value > 0) {\n prevVolume.value = volume.value\n volume.value = 0\n } else {\n volume.value = prevVolume.value || 70\n }\n if (audio) audio.volume = volume.value / 100\n }\n\n const MODES = ['list', 'loop', 'single', 'shuffle'] as const\n /** 循环切换播放模式 */\n function cycleMode() {\n const idx = MODES.indexOf(playMode.value as (typeof MODES)[number])\n playMode.value = MODES[(idx + 1) % MODES.length]\n emit('modeChange', playMode.value)\n }\n\n // ==================== Lifecycle ====================\n\n watch(\n () => props.tracks,\n () => {\n activeIndex.value = 0\n initAudio()\n }\n )\n\n watch(\n () => props.initialIndex,\n val => {\n if (val !== undefined && val !== activeIndex.value) {\n activeIndex.value = val\n initAudio()\n }\n }\n )\n\n onBeforeUnmount(() => {\n if (audio) {\n audio.pause()\n audio.removeEventListener('timeupdate', onTimeUpdate)\n audio.removeEventListener('loadedmetadata', onMetaLoaded)\n audio.removeEventListener('ended', onEnded)\n audio = null\n }\n })\n\n // 自动播放\n if (props.autoplay && props.tracks.length > 0) {\n playTrack(activeIndex.value)\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Description: 音频播放器组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n class=\"c-audio-player\"\n :class=\"{ 'is-minimal': theme === 'minimal' }\"\n >\n <!-- ================ Now Playing ================ -->\n <div class=\"c-audio-player__now-playing\">\n <div\n v-if=\"showCover\"\n class=\"c-audio-player__cover\"\n >\n <img\n v-if=\"currentTrack?.cover\"\n :src=\"currentTrack.cover\"\n :alt=\"currentTrack.title\"\n />\n <C_Icon\n v-else\n name=\"mdi:music-note\"\n class=\"c-audio-player__cover-placeholder\"\n />\n </div>\n <div class=\"c-audio-player__info\">\n <div class=\"c-audio-player__title\">{{\n currentTrack?.title ?? '未选择曲目'\n }}</div>\n <div\n v-if=\"currentTrack?.artist\"\n class=\"c-audio-player__artist\"\n >\n {{ currentTrack.artist }}\n </div>\n </div>\n </div>\n\n <!-- ================ Progress ================ -->\n <div\n v-if=\"theme !== 'minimal'\"\n class=\"c-audio-player__progress\"\n >\n <div\n class=\"c-audio-player__progress-bar\"\n @click=\"seekByClick\"\n >\n <div\n class=\"c-audio-player__progress-fill\"\n :style=\"{ width: `${progressPercent}%` }\"\n />\n </div>\n <div class=\"c-audio-player__time\">\n <span>{{ formatTime(currentTime) }}</span>\n <span>{{ formatTime(totalDuration) }}</span>\n </div>\n </div>\n\n <!-- ================ Controls ================ -->\n <div class=\"c-audio-player__controls\">\n <button\n class=\"c-audio-player__ctrl-btn is-mode\"\n :title=\"currentModeLabel\"\n @click=\"cycleMode\"\n >\n <C_Icon :name=\"currentModeIcon\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-skip\"\n title=\"上一曲\"\n @click=\"prev\"\n >\n <C_Icon name=\"mdi:skip-previous\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-play\"\n :title=\"isPlaying ? '暂停' : '播放'\"\n @click=\"togglePlay\"\n >\n <C_Icon :name=\"isPlaying ? 'mdi:pause' : 'mdi:play'\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-skip\"\n title=\"下一曲\"\n @click=\"next\"\n >\n <C_Icon name=\"mdi:skip-next\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-mode\"\n title=\"播放列表\"\n @click=\"playlistVisible = !playlistVisible\"\n >\n <C_Icon name=\"mdi:playlist-music\" />\n </button>\n </div>\n\n <!-- ================ Volume ================ -->\n <div\n v-if=\"theme !== 'minimal'\"\n class=\"c-audio-player__volume\"\n >\n <C_Icon\n :name=\"volumeIcon\"\n class=\"c-audio-player__volume-icon\"\n @click=\"toggleMute\"\n />\n <NSlider\n v-model:value=\"volume\"\n :min=\"0\"\n :max=\"100\"\n :step=\"1\"\n class=\"c-audio-player__volume-slider\"\n @update:value=\"handleVolumeChange\"\n />\n </div>\n\n <!-- ================ Playlist ================ -->\n <div\n v-if=\"showPlaylist && playlistVisible && theme !== 'minimal'\"\n class=\"c-audio-player__playlist\"\n >\n <div class=\"c-audio-player__playlist-header\">\n <span>播放列表</span>\n <span>{{ tracks.length }} 首</span>\n </div>\n <div\n v-for=\"(track, idx) in tracks\"\n :key=\"track.id\"\n class=\"c-audio-player__track\"\n :class=\"{ 'is-active': idx === activeIndex }\"\n @click=\"playTrack(idx)\"\n >\n <span class=\"c-audio-player__track-index\">\n <C_Icon\n v-if=\"idx === activeIndex && isPlaying\"\n name=\"mdi:volume-high\"\n />\n <template v-else>{{ idx + 1 }}</template>\n </span>\n <div class=\"c-audio-player__track-info\">\n <div class=\"c-audio-player__track-title\">{{ track.title }}</div>\n <div\n v-if=\"track.artist\"\n class=\"c-audio-player__track-artist\"\n >\n {{ track.artist }}\n </div>\n </div>\n <span\n v-if=\"track.duration\"\n class=\"c-audio-player__track-duration\"\n >\n {{ formatTime(track.duration) }}\n </span>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, onBeforeUnmount, ref, watch } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_AUDIO_PLAYER_PROPS,\n formatTime,\n MODE_ICON_MAP,\n type AudioPlayerProps,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n tracks: AudioPlayerProps['tracks']\n initialIndex?: number\n showPlaylist?: boolean\n showCover?: boolean\n autoplay?: boolean\n mode?: AudioPlayerProps['mode']\n theme?: AudioPlayerProps['theme']\n }>(),\n {\n initialIndex: DEFAULT_AUDIO_PLAYER_PROPS.initialIndex,\n showPlaylist: DEFAULT_AUDIO_PLAYER_PROPS.showPlaylist,\n showCover: true,\n autoplay: DEFAULT_AUDIO_PLAYER_PROPS.autoplay,\n mode: DEFAULT_AUDIO_PLAYER_PROPS.mode,\n theme: DEFAULT_AUDIO_PLAYER_PROPS.theme,\n }\n )\n\n const emit = defineEmits<{\n play: [index: number]\n pause: []\n ended: [index: number]\n modeChange: [mode: string]\n }>()\n\n // ==================== Audio Core ====================\n\n let audio: HTMLAudioElement | null = null\n\n const activeIndex = ref(props.initialIndex ?? 0)\n const isPlaying = ref(false)\n const currentTime = ref(0)\n const totalDuration = ref(0)\n const volume = ref(70)\n const prevVolume = ref(70)\n const playlistVisible = ref(props.showPlaylist)\n const playMode = ref(props.mode ?? 'list')\n\n const currentTrack = computed(() => props.tracks[activeIndex.value])\n\n const progressPercent = computed(() => {\n if (totalDuration.value === 0) return 0\n return (currentTime.value / totalDuration.value) * 100\n })\n\n const volumeIcon = computed(() => {\n if (volume.value === 0) return 'mdi:volume-off'\n if (volume.value < 40) return 'mdi:volume-low'\n if (volume.value < 70) return 'mdi:volume-medium'\n return 'mdi:volume-high'\n })\n\n const currentModeIcon = computed(\n () => MODE_ICON_MAP[playMode.value]?.icon ?? 'mdi:repeat'\n )\n const currentModeLabel = computed(\n () => MODE_ICON_MAP[playMode.value]?.label ?? '循环'\n )\n\n /** 初始化音频实例 */\n function initAudio() {\n if (audio) {\n audio.pause()\n audio.removeEventListener('timeupdate', onTimeUpdate)\n audio.removeEventListener('loadedmetadata', onMetaLoaded)\n audio.removeEventListener('ended', onEnded)\n }\n const track = currentTrack.value\n if (!track) return\n\n audio = new Audio(track.src)\n audio.volume = volume.value / 100\n audio.addEventListener('timeupdate', onTimeUpdate)\n audio.addEventListener('loadedmetadata', onMetaLoaded)\n audio.addEventListener('ended', onEnded)\n }\n\n /** 播放进度更新回调 */\n function onTimeUpdate() {\n if (audio) currentTime.value = audio.currentTime\n }\n\n /** 音频元数据加载完成回调 */\n function onMetaLoaded() {\n if (audio) totalDuration.value = audio.duration\n }\n\n /** 播放结束回调,根据模式决定下一步 */\n function onEnded() {\n emit('ended', activeIndex.value)\n switch (playMode.value) {\n case 'loop':\n audio?.play().catch(() => {})\n break\n case 'single':\n isPlaying.value = false\n break\n case 'shuffle':\n playTrack(Math.floor(Math.random() * props.tracks.length))\n break\n default: // list\n if (activeIndex.value < props.tracks.length - 1) next()\n else {\n activeIndex.value = 0\n initAudio()\n audio?.play().catch(() => {})\n }\n }\n }\n\n /** 切换播放/暂停 */\n function togglePlay() {\n if (!audio) initAudio()\n if (isPlaying.value) {\n audio?.pause()\n isPlaying.value = false\n emit('pause')\n } else {\n audio?.play().catch(() => {})\n isPlaying.value = true\n emit('play', activeIndex.value)\n }\n }\n\n /** 播放指定索引的曲目 */\n function playTrack(idx: number) {\n activeIndex.value = idx\n initAudio()\n audio?.play().catch(() => {})\n isPlaying.value = true\n emit('play', idx)\n }\n\n /** 上一曲 */\n function prev() {\n const idx =\n activeIndex.value <= 0 ? props.tracks.length - 1 : activeIndex.value - 1\n playTrack(idx)\n }\n\n /** 下一曲 */\n function next() {\n const idx =\n activeIndex.value >= props.tracks.length - 1 ? 0 : activeIndex.value + 1\n playTrack(idx)\n }\n\n /** 点击进度条跳转播放位置 */\n function seekByClick(e: MouseEvent) {\n if (!audio || !totalDuration.value) return\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()\n const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width))\n audio.currentTime = ratio * totalDuration.value\n currentTime.value = audio.currentTime\n }\n\n /** 处理音量变更 */\n function handleVolumeChange(val: number) {\n volume.value = val\n if (audio) audio.volume = val / 100\n }\n\n /** 切换静音状态 */\n function toggleMute() {\n if (volume.value > 0) {\n prevVolume.value = volume.value\n volume.value = 0\n } else {\n volume.value = prevVolume.value || 70\n }\n if (audio) audio.volume = volume.value / 100\n }\n\n const MODES = ['list', 'loop', 'single', 'shuffle'] as const\n /** 循环切换播放模式 */\n function cycleMode() {\n const idx = MODES.indexOf(playMode.value as (typeof MODES)[number])\n playMode.value = MODES[(idx + 1) % MODES.length]\n emit('modeChange', playMode.value)\n }\n\n // ==================== Lifecycle ====================\n\n watch(\n () => props.tracks,\n () => {\n activeIndex.value = 0\n initAudio()\n }\n )\n\n watch(\n () => props.initialIndex,\n val => {\n if (val !== undefined && val !== activeIndex.value) {\n activeIndex.value = val\n initAudio()\n }\n }\n )\n\n onBeforeUnmount(() => {\n if (audio) {\n audio.pause()\n audio.removeEventListener('timeupdate', onTimeUpdate)\n audio.removeEventListener('loadedmetadata', onMetaLoaded)\n audio.removeEventListener('ended', onEnded)\n audio = null\n }\n })\n\n // 自动播放\n if (props.autoplay && props.tracks.length > 0) {\n playTrack(activeIndex.value)\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_AudioPlayer2.js","names":["theme","showCover","showPlaylist","tracks"],"sources":["../src/components/C_AudioPlayer/types.ts","../src/components/C_AudioPlayer/index.vue","../src/components/C_AudioPlayer/index.vue"],"sourcesContent":["/**\n * @Description: Audio player component type definitions\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\n */\n\n/** Single audio track */\nexport interface AudioTrack {\n /** Unique identifier */\n id: string | number\n /** Track title */\n title: string\n /** Artist / author */\n artist?: string\n /** Audio source URL */\n src: string\n /** Cover image URL */\n cover?: string\n /** Duration in seconds (optional, for static display) */\n duration?: number\n /** Extra data */\n extra?: Record<string, unknown>\n}\n\n/** Component props */\nexport interface AudioPlayerProps {\n /** Playlist */\n tracks: AudioTrack[]\n /** Initial active index */\n initialIndex?: number\n /** Whether to show the playlist panel */\n showPlaylist?: boolean\n /** Whether to show the cover art */\n showCover?: boolean\n /** Whether to auto-play on mount */\n autoplay?: boolean\n /** Playback mode */\n mode?: 'list' | 'loop' | 'single' | 'shuffle'\n /** Theme appearance */\n theme?: 'default' | 'minimal'\n}\n\n/** Default props */\nexport const DEFAULT_AUDIO_PLAYER_PROPS: Partial<AudioPlayerProps> = {\n initialIndex: 0,\n showPlaylist: true,\n showCover: true,\n autoplay: false,\n mode: 'list',\n theme: 'default',\n}\n\n/** Playback mode icon map */\nexport const MODE_ICON_MAP: Record<string, { icon: string; label: string }> = {\n list: { icon: 'mdi:repeat', label: 'List Loop' },\n loop: { icon: 'mdi:repeat-once', label: 'Single Loop' },\n single: { icon: 'mdi:numeric-1-box', label: 'Play Once' },\n shuffle: { icon: 'mdi:shuffle-variant', label: 'Shuffle' },\n}\n\n/** Format seconds to mm:ss */\nexport function formatTime(seconds: number): string {\n if (!Number.isFinite(seconds) || seconds < 0) return '00:00'\n const m = Math.floor(seconds / 60)\n const s = Math.floor(seconds % 60)\n return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`\n}\n","<!--\n * @Description: 音频播放器组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n class=\"c-audio-player\"\n :class=\"{ 'is-minimal': theme === 'minimal' }\"\n >\n <!-- ================ Now Playing ================ -->\n <div class=\"c-audio-player__now-playing\">\n <div\n v-if=\"showCover\"\n class=\"c-audio-player__cover\"\n >\n <img\n v-if=\"currentTrack?.cover\"\n :src=\"currentTrack.cover\"\n :alt=\"currentTrack.title\"\n />\n <C_Icon\n v-else\n name=\"mdi:music-note\"\n class=\"c-audio-player__cover-placeholder\"\n />\n </div>\n <div class=\"c-audio-player__info\">\n <div class=\"c-audio-player__title\">{{\n currentTrack?.title ?? '未选择曲目'\n }}</div>\n <div\n v-if=\"currentTrack?.artist\"\n class=\"c-audio-player__artist\"\n >\n {{ currentTrack.artist }}\n </div>\n </div>\n </div>\n\n <!-- ================ Progress ================ -->\n <div\n v-if=\"theme !== 'minimal'\"\n class=\"c-audio-player__progress\"\n >\n <div\n class=\"c-audio-player__progress-bar\"\n @click=\"seekByClick\"\n >\n <div\n class=\"c-audio-player__progress-fill\"\n :style=\"{ width: `${progressPercent}%` }\"\n />\n </div>\n <div class=\"c-audio-player__time\">\n <span>{{ formatTime(currentTime) }}</span>\n <span>{{ formatTime(totalDuration) }}</span>\n </div>\n </div>\n\n <!-- ================ Controls ================ -->\n <div class=\"c-audio-player__controls\">\n <button\n class=\"c-audio-player__ctrl-btn is-mode\"\n :title=\"currentModeLabel\"\n @click=\"cycleMode\"\n >\n <C_Icon :name=\"currentModeIcon\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-skip\"\n title=\"上一曲\"\n @click=\"prev\"\n >\n <C_Icon name=\"mdi:skip-previous\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-play\"\n :title=\"isPlaying ? '暂停' : '播放'\"\n @click=\"togglePlay\"\n >\n <C_Icon :name=\"isPlaying ? 'mdi:pause' : 'mdi:play'\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-skip\"\n title=\"下一曲\"\n @click=\"next\"\n >\n <C_Icon name=\"mdi:skip-next\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-mode\"\n title=\"播放列表\"\n @click=\"playlistVisible = !playlistVisible\"\n >\n <C_Icon name=\"mdi:playlist-music\" />\n </button>\n </div>\n\n <!-- ================ Volume ================ -->\n <div\n v-if=\"theme !== 'minimal'\"\n class=\"c-audio-player__volume\"\n >\n <C_Icon\n :name=\"volumeIcon\"\n class=\"c-audio-player__volume-icon\"\n @click=\"toggleMute\"\n />\n <NSlider\n v-model:value=\"volume\"\n :min=\"0\"\n :max=\"100\"\n :step=\"1\"\n class=\"c-audio-player__volume-slider\"\n @update:value=\"handleVolumeChange\"\n />\n </div>\n\n <!-- ================ Playlist ================ -->\n <div\n v-if=\"showPlaylist && playlistVisible && theme !== 'minimal'\"\n class=\"c-audio-player__playlist\"\n >\n <div class=\"c-audio-player__playlist-header\">\n <span>播放列表</span>\n <span>{{ tracks.length }} 首</span>\n </div>\n <div\n v-for=\"(track, idx) in tracks\"\n :key=\"track.id\"\n class=\"c-audio-player__track\"\n :class=\"{ 'is-active': idx === activeIndex }\"\n @click=\"playTrack(idx)\"\n >\n <span class=\"c-audio-player__track-index\">\n <C_Icon\n v-if=\"idx === activeIndex && isPlaying\"\n name=\"mdi:volume-high\"\n />\n <template v-else>{{ idx + 1 }}</template>\n </span>\n <div class=\"c-audio-player__track-info\">\n <div class=\"c-audio-player__track-title\">{{ track.title }}</div>\n <div\n v-if=\"track.artist\"\n class=\"c-audio-player__track-artist\"\n >\n {{ track.artist }}\n </div>\n </div>\n <span\n v-if=\"track.duration\"\n class=\"c-audio-player__track-duration\"\n >\n {{ formatTime(track.duration) }}\n </span>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, onBeforeUnmount, ref, watch } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_AUDIO_PLAYER_PROPS,\n formatTime,\n MODE_ICON_MAP,\n type AudioPlayerProps,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n tracks: AudioPlayerProps['tracks']\n initialIndex?: number\n showPlaylist?: boolean\n showCover?: boolean\n autoplay?: boolean\n mode?: AudioPlayerProps['mode']\n theme?: AudioPlayerProps['theme']\n }>(),\n {\n initialIndex: DEFAULT_AUDIO_PLAYER_PROPS.initialIndex,\n showPlaylist: DEFAULT_AUDIO_PLAYER_PROPS.showPlaylist,\n showCover: true,\n autoplay: DEFAULT_AUDIO_PLAYER_PROPS.autoplay,\n mode: DEFAULT_AUDIO_PLAYER_PROPS.mode,\n theme: DEFAULT_AUDIO_PLAYER_PROPS.theme,\n }\n )\n\n const emit = defineEmits<{\n play: [index: number]\n pause: []\n ended: [index: number]\n modeChange: [mode: string]\n }>()\n\n // ==================== Audio Core ====================\n\n let audio: HTMLAudioElement | null = null\n\n const activeIndex = ref(props.initialIndex ?? 0)\n const isPlaying = ref(false)\n const currentTime = ref(0)\n const totalDuration = ref(0)\n const volume = ref(70)\n const prevVolume = ref(70)\n const playlistVisible = ref(props.showPlaylist)\n const playMode = ref(props.mode ?? 'list')\n\n const currentTrack = computed(() => props.tracks[activeIndex.value])\n\n const progressPercent = computed(() => {\n if (totalDuration.value === 0) return 0\n return (currentTime.value / totalDuration.value) * 100\n })\n\n const volumeIcon = computed(() => {\n if (volume.value === 0) return 'mdi:volume-off'\n if (volume.value < 40) return 'mdi:volume-low'\n if (volume.value < 70) return 'mdi:volume-medium'\n return 'mdi:volume-high'\n })\n\n const currentModeIcon = computed(\n () => MODE_ICON_MAP[playMode.value]?.icon ?? 'mdi:repeat'\n )\n const currentModeLabel = computed(\n () => MODE_ICON_MAP[playMode.value]?.label ?? '循环'\n )\n\n /** 初始化音频实例 */\n function initAudio() {\n if (audio) {\n audio.pause()\n audio.removeEventListener('timeupdate', onTimeUpdate)\n audio.removeEventListener('loadedmetadata', onMetaLoaded)\n audio.removeEventListener('ended', onEnded)\n }\n const track = currentTrack.value\n if (!track) return\n\n audio = new Audio(track.src)\n audio.volume = volume.value / 100\n audio.addEventListener('timeupdate', onTimeUpdate)\n audio.addEventListener('loadedmetadata', onMetaLoaded)\n audio.addEventListener('ended', onEnded)\n }\n\n /** 播放进度更新回调 */\n function onTimeUpdate() {\n if (audio) currentTime.value = audio.currentTime\n }\n\n /** 音频元数据加载完成回调 */\n function onMetaLoaded() {\n if (audio) totalDuration.value = audio.duration\n }\n\n /** 播放结束回调,根据模式决定下一步 */\n function onEnded() {\n emit('ended', activeIndex.value)\n switch (playMode.value) {\n case 'loop':\n audio?.play().catch(() => {})\n break\n case 'single':\n isPlaying.value = false\n break\n case 'shuffle':\n playTrack(Math.floor(Math.random() * props.tracks.length))\n break\n default: // list\n if (activeIndex.value < props.tracks.length - 1) next()\n else {\n activeIndex.value = 0\n initAudio()\n audio?.play().catch(() => {})\n }\n }\n }\n\n /** 切换播放/暂停 */\n function togglePlay() {\n if (!audio) initAudio()\n if (isPlaying.value) {\n audio?.pause()\n isPlaying.value = false\n emit('pause')\n } else {\n audio?.play().catch(() => {})\n isPlaying.value = true\n emit('play', activeIndex.value)\n }\n }\n\n /** 播放指定索引的曲目 */\n function playTrack(idx: number) {\n activeIndex.value = idx\n initAudio()\n audio?.play().catch(() => {})\n isPlaying.value = true\n emit('play', idx)\n }\n\n /** 上一曲 */\n function prev() {\n const idx =\n activeIndex.value <= 0 ? props.tracks.length - 1 : activeIndex.value - 1\n playTrack(idx)\n }\n\n /** 下一曲 */\n function next() {\n const idx =\n activeIndex.value >= props.tracks.length - 1 ? 0 : activeIndex.value + 1\n playTrack(idx)\n }\n\n /** 点击进度条跳转播放位置 */\n function seekByClick(e: MouseEvent) {\n if (!audio || !totalDuration.value) return\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()\n const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width))\n audio.currentTime = ratio * totalDuration.value\n currentTime.value = audio.currentTime\n }\n\n /** 处理音量变更 */\n function handleVolumeChange(val: number) {\n volume.value = val\n if (audio) audio.volume = val / 100\n }\n\n /** 切换静音状态 */\n function toggleMute() {\n if (volume.value > 0) {\n prevVolume.value = volume.value\n volume.value = 0\n } else {\n volume.value = prevVolume.value || 70\n }\n if (audio) audio.volume = volume.value / 100\n }\n\n const MODES = ['list', 'loop', 'single', 'shuffle'] as const\n /** 循环切换播放模式 */\n function cycleMode() {\n const idx = MODES.indexOf(playMode.value as (typeof MODES)[number])\n playMode.value = MODES[(idx + 1) % MODES.length]\n emit('modeChange', playMode.value)\n }\n\n // ==================== Lifecycle ====================\n\n watch(\n () => props.tracks,\n () => {\n activeIndex.value = 0\n initAudio()\n }\n )\n\n watch(\n () => props.initialIndex,\n val => {\n if (val !== undefined && val !== activeIndex.value) {\n activeIndex.value = val\n initAudio()\n }\n }\n )\n\n onBeforeUnmount(() => {\n if (audio) {\n audio.pause()\n audio.removeEventListener('timeupdate', onTimeUpdate)\n audio.removeEventListener('loadedmetadata', onMetaLoaded)\n audio.removeEventListener('ended', onEnded)\n audio = null\n }\n })\n\n // 自动播放\n if (props.autoplay && props.tracks.length > 0) {\n playTrack(activeIndex.value)\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Description: 音频播放器组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n class=\"c-audio-player\"\n :class=\"{ 'is-minimal': theme === 'minimal' }\"\n >\n <!-- ================ Now Playing ================ -->\n <div class=\"c-audio-player__now-playing\">\n <div\n v-if=\"showCover\"\n class=\"c-audio-player__cover\"\n >\n <img\n v-if=\"currentTrack?.cover\"\n :src=\"currentTrack.cover\"\n :alt=\"currentTrack.title\"\n />\n <C_Icon\n v-else\n name=\"mdi:music-note\"\n class=\"c-audio-player__cover-placeholder\"\n />\n </div>\n <div class=\"c-audio-player__info\">\n <div class=\"c-audio-player__title\">{{\n currentTrack?.title ?? '未选择曲目'\n }}</div>\n <div\n v-if=\"currentTrack?.artist\"\n class=\"c-audio-player__artist\"\n >\n {{ currentTrack.artist }}\n </div>\n </div>\n </div>\n\n <!-- ================ Progress ================ -->\n <div\n v-if=\"theme !== 'minimal'\"\n class=\"c-audio-player__progress\"\n >\n <div\n class=\"c-audio-player__progress-bar\"\n @click=\"seekByClick\"\n >\n <div\n class=\"c-audio-player__progress-fill\"\n :style=\"{ width: `${progressPercent}%` }\"\n />\n </div>\n <div class=\"c-audio-player__time\">\n <span>{{ formatTime(currentTime) }}</span>\n <span>{{ formatTime(totalDuration) }}</span>\n </div>\n </div>\n\n <!-- ================ Controls ================ -->\n <div class=\"c-audio-player__controls\">\n <button\n class=\"c-audio-player__ctrl-btn is-mode\"\n :title=\"currentModeLabel\"\n @click=\"cycleMode\"\n >\n <C_Icon :name=\"currentModeIcon\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-skip\"\n title=\"上一曲\"\n @click=\"prev\"\n >\n <C_Icon name=\"mdi:skip-previous\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-play\"\n :title=\"isPlaying ? '暂停' : '播放'\"\n @click=\"togglePlay\"\n >\n <C_Icon :name=\"isPlaying ? 'mdi:pause' : 'mdi:play'\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-skip\"\n title=\"下一曲\"\n @click=\"next\"\n >\n <C_Icon name=\"mdi:skip-next\" />\n </button>\n <button\n class=\"c-audio-player__ctrl-btn is-mode\"\n title=\"播放列表\"\n @click=\"playlistVisible = !playlistVisible\"\n >\n <C_Icon name=\"mdi:playlist-music\" />\n </button>\n </div>\n\n <!-- ================ Volume ================ -->\n <div\n v-if=\"theme !== 'minimal'\"\n class=\"c-audio-player__volume\"\n >\n <C_Icon\n :name=\"volumeIcon\"\n class=\"c-audio-player__volume-icon\"\n @click=\"toggleMute\"\n />\n <NSlider\n v-model:value=\"volume\"\n :min=\"0\"\n :max=\"100\"\n :step=\"1\"\n class=\"c-audio-player__volume-slider\"\n @update:value=\"handleVolumeChange\"\n />\n </div>\n\n <!-- ================ Playlist ================ -->\n <div\n v-if=\"showPlaylist && playlistVisible && theme !== 'minimal'\"\n class=\"c-audio-player__playlist\"\n >\n <div class=\"c-audio-player__playlist-header\">\n <span>播放列表</span>\n <span>{{ tracks.length }} 首</span>\n </div>\n <div\n v-for=\"(track, idx) in tracks\"\n :key=\"track.id\"\n class=\"c-audio-player__track\"\n :class=\"{ 'is-active': idx === activeIndex }\"\n @click=\"playTrack(idx)\"\n >\n <span class=\"c-audio-player__track-index\">\n <C_Icon\n v-if=\"idx === activeIndex && isPlaying\"\n name=\"mdi:volume-high\"\n />\n <template v-else>{{ idx + 1 }}</template>\n </span>\n <div class=\"c-audio-player__track-info\">\n <div class=\"c-audio-player__track-title\">{{ track.title }}</div>\n <div\n v-if=\"track.artist\"\n class=\"c-audio-player__track-artist\"\n >\n {{ track.artist }}\n </div>\n </div>\n <span\n v-if=\"track.duration\"\n class=\"c-audio-player__track-duration\"\n >\n {{ formatTime(track.duration) }}\n </span>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, onBeforeUnmount, ref, watch } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_AUDIO_PLAYER_PROPS,\n formatTime,\n MODE_ICON_MAP,\n type AudioPlayerProps,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n tracks: AudioPlayerProps['tracks']\n initialIndex?: number\n showPlaylist?: boolean\n showCover?: boolean\n autoplay?: boolean\n mode?: AudioPlayerProps['mode']\n theme?: AudioPlayerProps['theme']\n }>(),\n {\n initialIndex: DEFAULT_AUDIO_PLAYER_PROPS.initialIndex,\n showPlaylist: DEFAULT_AUDIO_PLAYER_PROPS.showPlaylist,\n showCover: true,\n autoplay: DEFAULT_AUDIO_PLAYER_PROPS.autoplay,\n mode: DEFAULT_AUDIO_PLAYER_PROPS.mode,\n theme: DEFAULT_AUDIO_PLAYER_PROPS.theme,\n }\n )\n\n const emit = defineEmits<{\n play: [index: number]\n pause: []\n ended: [index: number]\n modeChange: [mode: string]\n }>()\n\n // ==================== Audio Core ====================\n\n let audio: HTMLAudioElement | null = null\n\n const activeIndex = ref(props.initialIndex ?? 0)\n const isPlaying = ref(false)\n const currentTime = ref(0)\n const totalDuration = ref(0)\n const volume = ref(70)\n const prevVolume = ref(70)\n const playlistVisible = ref(props.showPlaylist)\n const playMode = ref(props.mode ?? 'list')\n\n const currentTrack = computed(() => props.tracks[activeIndex.value])\n\n const progressPercent = computed(() => {\n if (totalDuration.value === 0) return 0\n return (currentTime.value / totalDuration.value) * 100\n })\n\n const volumeIcon = computed(() => {\n if (volume.value === 0) return 'mdi:volume-off'\n if (volume.value < 40) return 'mdi:volume-low'\n if (volume.value < 70) return 'mdi:volume-medium'\n return 'mdi:volume-high'\n })\n\n const currentModeIcon = computed(\n () => MODE_ICON_MAP[playMode.value]?.icon ?? 'mdi:repeat'\n )\n const currentModeLabel = computed(\n () => MODE_ICON_MAP[playMode.value]?.label ?? '循环'\n )\n\n /** 初始化音频实例 */\n function initAudio() {\n if (audio) {\n audio.pause()\n audio.removeEventListener('timeupdate', onTimeUpdate)\n audio.removeEventListener('loadedmetadata', onMetaLoaded)\n audio.removeEventListener('ended', onEnded)\n }\n const track = currentTrack.value\n if (!track) return\n\n audio = new Audio(track.src)\n audio.volume = volume.value / 100\n audio.addEventListener('timeupdate', onTimeUpdate)\n audio.addEventListener('loadedmetadata', onMetaLoaded)\n audio.addEventListener('ended', onEnded)\n }\n\n /** 播放进度更新回调 */\n function onTimeUpdate() {\n if (audio) currentTime.value = audio.currentTime\n }\n\n /** 音频元数据加载完成回调 */\n function onMetaLoaded() {\n if (audio) totalDuration.value = audio.duration\n }\n\n /** 播放结束回调,根据模式决定下一步 */\n function onEnded() {\n emit('ended', activeIndex.value)\n switch (playMode.value) {\n case 'loop':\n audio?.play().catch(() => {})\n break\n case 'single':\n isPlaying.value = false\n break\n case 'shuffle':\n playTrack(Math.floor(Math.random() * props.tracks.length))\n break\n default: // list\n if (activeIndex.value < props.tracks.length - 1) next()\n else {\n activeIndex.value = 0\n initAudio()\n audio?.play().catch(() => {})\n }\n }\n }\n\n /** 切换播放/暂停 */\n function togglePlay() {\n if (!audio) initAudio()\n if (isPlaying.value) {\n audio?.pause()\n isPlaying.value = false\n emit('pause')\n } else {\n audio?.play().catch(() => {})\n isPlaying.value = true\n emit('play', activeIndex.value)\n }\n }\n\n /** 播放指定索引的曲目 */\n function playTrack(idx: number) {\n activeIndex.value = idx\n initAudio()\n audio?.play().catch(() => {})\n isPlaying.value = true\n emit('play', idx)\n }\n\n /** 上一曲 */\n function prev() {\n const idx =\n activeIndex.value <= 0 ? props.tracks.length - 1 : activeIndex.value - 1\n playTrack(idx)\n }\n\n /** 下一曲 */\n function next() {\n const idx =\n activeIndex.value >= props.tracks.length - 1 ? 0 : activeIndex.value + 1\n playTrack(idx)\n }\n\n /** 点击进度条跳转播放位置 */\n function seekByClick(e: MouseEvent) {\n if (!audio || !totalDuration.value) return\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()\n const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width))\n audio.currentTime = ratio * totalDuration.value\n currentTime.value = audio.currentTime\n }\n\n /** 处理音量变更 */\n function handleVolumeChange(val: number) {\n volume.value = val\n if (audio) audio.volume = val / 100\n }\n\n /** 切换静音状态 */\n function toggleMute() {\n if (volume.value > 0) {\n prevVolume.value = volume.value\n volume.value = 0\n } else {\n volume.value = prevVolume.value || 70\n }\n if (audio) audio.volume = volume.value / 100\n }\n\n const MODES = ['list', 'loop', 'single', 'shuffle'] as const\n /** 循环切换播放模式 */\n function cycleMode() {\n const idx = MODES.indexOf(playMode.value as (typeof MODES)[number])\n playMode.value = MODES[(idx + 1) % MODES.length]\n emit('modeChange', playMode.value)\n }\n\n // ==================== Lifecycle ====================\n\n watch(\n () => props.tracks,\n () => {\n activeIndex.value = 0\n initAudio()\n }\n )\n\n watch(\n () => props.initialIndex,\n val => {\n if (val !== undefined && val !== activeIndex.value) {\n activeIndex.value = val\n initAudio()\n }\n }\n )\n\n onBeforeUnmount(() => {\n if (audio) {\n audio.pause()\n audio.removeEventListener('timeupdate', onTimeUpdate)\n audio.removeEventListener('loadedmetadata', onMetaLoaded)\n audio.removeEventListener('ended', onEnded)\n audio = null\n }\n })\n\n // 自动播放\n if (props.autoplay && props.tracks.length > 0) {\n playTrack(activeIndex.value)\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_AvatarGroup-B9d-GJ9c.css","names":[],"sources":["../src/components/C_AvatarGroup/index.vue?vue&type=style&index=0&scoped=e9981c87&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled *//**\n * C_AvatarGroup — 头像组组件样式\n */\n.c-avatar-group[data-v-e9981c87] {\n display: inline-flex;\n align-items: center;\n /* ======================== 单个头像 ======================== */\n /* ---- 头像图片 ---- */\n /* ---- 首字母 fallback ---- */\n /* ---- 状态指示点 ---- */\n /* ======================== 溢出 +N ======================== */\n}\n.c-avatar-group.is-rtl[data-v-e9981c87] {\n flex-direction: row-reverse;\n}\n.c-avatar-group__item[data-v-e9981c87] {\n position: relative;\n flex-shrink: 0;\n border: 2px solid var(--card-color);\n background: var(--card-color);\n transition: transform 0.2s, z-index 0.2s, box-shadow 0.2s;\n cursor: default;\n z-index: 1;\n}\n.c-avatar-group__item.is-circle[data-v-e9981c87] {\n border-radius: 50%;\n}\n.c-avatar-group__item.is-square[data-v-e9981c87] {\n border-radius: 6px;\n}\n.c-avatar-group__item.is-clickable[data-v-e9981c87] {\n cursor: pointer;\n}\n.c-avatar-group__item.is-clickable[data-v-e9981c87]:hover {\n transform: translateY(-3px) scale(1.08);\n z-index: 10;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n.c-avatar-group__item[data-v-e9981c87]:hover {\n z-index: 10;\n}\n.c-avatar-group__img[data-v-e9981c87] {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.is-circle .c-avatar-group__img[data-v-e9981c87] {\n border-radius: 50%;\n}\n.is-square .c-avatar-group__img[data-v-e9981c87] {\n border-radius: 4px;\n}\n.c-avatar-group__initials[data-v-e9981c87] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n color: #fff;\n font-weight: 600;\n user-select: none;\n}\n.is-circle .c-avatar-group__initials[data-v-e9981c87] {\n border-radius: 50%;\n}\n.is-square .c-avatar-group__initials[data-v-e9981c87] {\n border-radius: 4px;\n}\n.c-avatar-group__status[data-v-e9981c87] {\n position: absolute;\n bottom: 0;\n right: 0;\n border-radius: 50%;\n border: 2px solid var(--card-color);\n z-index: 2;\n}\n.c-avatar-group__overflow[data-v-e9981c87] {\n position: relative;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 2px solid var(--card-color);\n background: color-mix(in srgb, var(--primary-color) 12%, var(--card-color));\n color: var(--primary-color);\n font-weight: 700;\n z-index: 0;\n transition: transform 0.2s, box-shadow 0.2s;\n}\n.c-avatar-group__overflow.is-circle[data-v-e9981c87] {\n border-radius: 50%;\n}\n.c-avatar-group__overflow.is-square[data-v-e9981c87] {\n border-radius: 6px;\n}\n.c-avatar-group__overflow.is-clickable[data-v-e9981c87] {\n cursor: pointer;\n}\n.c-avatar-group__overflow.is-clickable[data-v-e9981c87]:hover {\n transform: translateY(-3px) scale(1.08);\n z-index: 10;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n background: color-mix(in srgb, var(--primary-color) 20%, var(--card-color));\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_AvatarGroup-D3PwsKME.css","names":[],"sources":["../src/components/C_AvatarGroup/index.vue?vue&type=style&index=0&scoped=e9981c87&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled *//**\n * C_AvatarGroup — 头像组组件样式\n */\n.c-avatar-group[data-v-e9981c87] {\n display: inline-flex;\n align-items: center;\n /* ======================== 单个头像 ======================== */\n /* ---- 头像图片 ---- */\n /* ---- 首字母 fallback ---- */\n /* ---- 状态指示点 ---- */\n /* ======================== 溢出 +N ======================== */\n}\n.c-avatar-group.is-rtl[data-v-e9981c87] {\n flex-direction: row-reverse;\n}\n.c-avatar-group__item[data-v-e9981c87] {\n position: relative;\n flex-shrink: 0;\n border: 2px solid var(--card-color);\n background: var(--card-color);\n transition: transform 0.2s, z-index 0.2s, box-shadow 0.2s;\n cursor: default;\n z-index: 1;\n}\n.c-avatar-group__item.is-circle[data-v-e9981c87] {\n border-radius: 50%;\n}\n.c-avatar-group__item.is-square[data-v-e9981c87] {\n border-radius: 6px;\n}\n.c-avatar-group__item.is-clickable[data-v-e9981c87] {\n cursor: pointer;\n}\n.c-avatar-group__item.is-clickable[data-v-e9981c87]:hover {\n transform: translateY(-3px) scale(1.08);\n z-index: 10;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n.c-avatar-group__item[data-v-e9981c87]:hover {\n z-index: 10;\n}\n.c-avatar-group__img[data-v-e9981c87] {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.is-circle .c-avatar-group__img[data-v-e9981c87] {\n border-radius: 50%;\n}\n.is-square .c-avatar-group__img[data-v-e9981c87] {\n border-radius: 4px;\n}\n.c-avatar-group__initials[data-v-e9981c87] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n color: #fff;\n font-weight: 600;\n user-select: none;\n}\n.is-circle .c-avatar-group__initials[data-v-e9981c87] {\n border-radius: 50%;\n}\n.is-square .c-avatar-group__initials[data-v-e9981c87] {\n border-radius: 4px;\n}\n.c-avatar-group__status[data-v-e9981c87] {\n position: absolute;\n bottom: 0;\n right: 0;\n border-radius: 50%;\n border: 2px solid var(--card-color);\n z-index: 2;\n}\n.c-avatar-group__overflow[data-v-e9981c87] {\n position: relative;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 2px solid var(--card-color);\n background: color-mix(in srgb, var(--primary-color) 12%, var(--card-color));\n color: var(--primary-color);\n font-weight: 700;\n z-index: 0;\n transition: transform 0.2s, box-shadow 0.2s;\n}\n.c-avatar-group__overflow.is-circle[data-v-e9981c87] {\n border-radius: 50%;\n}\n.c-avatar-group__overflow.is-square[data-v-e9981c87] {\n border-radius: 6px;\n}\n.c-avatar-group__overflow.is-clickable[data-v-e9981c87] {\n cursor: pointer;\n}\n.c-avatar-group__overflow.is-clickable[data-v-e9981c87]:hover {\n transform: translateY(-3px) scale(1.08);\n z-index: 10;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n background: color-mix(in srgb, var(--primary-color) 20%, var(--card-color));\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_AvatarGroup.js","names":[],"sources":["../src/components/C_AvatarGroup/types.ts","../src/components/C_AvatarGroup/index.vue","../src/components/C_AvatarGroup/index.vue"],"sourcesContent":["/**\n * @Description: 头像组组件类型定义\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n */\n\n/** 单个头像数据 */\nexport interface AvatarItem {\n /** 唯一标识 */\n id: string | number\n /** 头像图片 URL */\n src?: string\n /** 姓名(用于首字母 fallback 和 tooltip) */\n name: string\n /** 悬浮提示(默认取 name) */\n tooltip?: string\n /** 在线状态 */\n status?: 'online' | 'offline' | 'busy' | 'away'\n /** 额外数据 */\n extra?: Record<string, unknown>\n}\n\n/** 组件 Props */\nexport interface AvatarGroupProps {\n /** 头像数据列表 */\n items: AvatarItem[]\n /** 最多显示数量(超出 +N) */\n max?: number\n /** 头像尺寸 (px) */\n size?: number\n /** 头像间重叠偏移量 (px),负值表示堆叠 */\n overlap?: number\n /** 形状 */\n shape?: 'circle' | 'square'\n /** 是否显示状态指示点 */\n showStatus?: boolean\n /** 是否显示悬浮提示 */\n showTooltip?: boolean\n /** 是否可点击 */\n clickable?: boolean\n /** +N 溢出按钮是否可点击 */\n overflowClickable?: boolean\n /** 堆叠方向 */\n direction?: 'ltr' | 'rtl'\n}\n\n/** 默认 Props */\nexport const DEFAULT_AVATAR_GROUP_PROPS: Partial<AvatarGroupProps> = {\n max: 5,\n size: 40,\n overlap: -10,\n shape: 'circle',\n showStatus: true,\n showTooltip: true,\n clickable: false,\n overflowClickable: true,\n direction: 'ltr',\n}\n\n/** 状态颜色映射 */\nexport const STATUS_COLOR_MAP: Record<string, string> = {\n online: '#18a058',\n offline: '#909399',\n busy: '#e53e3e',\n away: '#f0a020',\n}\n\n/** 从名字中提取首字母 */\nexport function getInitials(name: string): string {\n if (!name) return '?'\n const parts = name.trim().split(/\\s+/)\n if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase()\n // 中文直接取最后一个字\n if (/[\\u4E00-\\u9FFF]/.test(name)) return name.slice(-1)\n return name.slice(0, 2).toUpperCase()\n}\n\n/** 根据名字生成稳定的背景色 */\nexport function nameToColor(name: string): string {\n const palette = [\n '#6366f1',\n '#8b5cf6',\n '#a855f7',\n '#d946ef',\n '#ec4899',\n '#f43f5e',\n '#ef4444',\n '#f97316',\n '#eab308',\n '#84cc16',\n '#22c55e',\n '#14b8a6',\n '#06b6d4',\n '#0ea5e9',\n '#3b82f6',\n '#6d28d9',\n ]\n let hash = 0\n for (let i = 0; i < name.length; i++) {\n hash = name.charCodeAt(i) + ((hash * 31) | 0)\n }\n return palette[Math.abs(hash) % palette.length]\n}\n","<!--\n * @Description: 头像组组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n class=\"c-avatar-group\"\n :class=\"{ 'is-rtl': mergedProps.direction === 'rtl' }\"\n >\n <NTooltip\n v-for=\"(item, idx) in visibleItems\"\n :key=\"item.id\"\n :disabled=\"!mergedProps.showTooltip\"\n placement=\"top\"\n >\n <template #trigger>\n <div\n class=\"c-avatar-group__item\"\n :class=\"[\n `is-${mergedProps.shape}`,\n { 'is-clickable': mergedProps.clickable },\n ]\"\n :style=\"itemStyle(idx)\"\n @click=\"mergedProps.clickable && emit('itemClick', item)\"\n >\n <img\n v-if=\"item.src\"\n :src=\"item.src\"\n :alt=\"item.name\"\n class=\"c-avatar-group__img\"\n />\n <div\n v-else\n class=\"c-avatar-group__initials\"\n :style=\"{\n background: nameToColor(item.name),\n fontSize: initialsFontSize,\n }\"\n >\n {{ getInitials(item.name) }}\n </div>\n\n <span\n v-if=\"mergedProps.showStatus && item.status\"\n class=\"c-avatar-group__status\"\n :style=\"statusStyle(item.status)\"\n />\n </div>\n </template>\n {{ item.tooltip ?? item.name }}\n </NTooltip>\n\n <!-- 溢出 +N -->\n <NTooltip\n v-if=\"overflowCount > 0\"\n placement=\"top\"\n >\n <template #trigger>\n <div\n class=\"c-avatar-group__overflow\"\n :class=\"[\n `is-${mergedProps.shape}`,\n { 'is-clickable': mergedProps.overflowClickable },\n ]\"\n :style=\"overflowStyle\"\n @click=\"\n mergedProps.overflowClickable && emit('overflowClick', hiddenItems)\n \"\n >\n +{{ overflowCount }}\n </div>\n </template>\n <div style=\"max-width: 200px\">\n <div\n v-for=\"h in hiddenItems\"\n :key=\"h.id\"\n style=\"line-height: 1.6\"\n >\n {{ h.name }}\n </div>\n </div>\n </NTooltip>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import {\n DEFAULT_AVATAR_GROUP_PROPS,\n getInitials,\n nameToColor,\n STATUS_COLOR_MAP,\n type AvatarGroupProps,\n type AvatarItem,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n items: AvatarGroupProps['items']\n max?: number\n size?: number\n overlap?: number\n shape?: AvatarGroupProps['shape']\n showStatus?: boolean\n showTooltip?: boolean\n clickable?: boolean\n overflowClickable?: boolean\n direction?: AvatarGroupProps['direction']\n }>(),\n {\n max: DEFAULT_AVATAR_GROUP_PROPS.max,\n size: DEFAULT_AVATAR_GROUP_PROPS.size,\n overlap: DEFAULT_AVATAR_GROUP_PROPS.overlap,\n shape: DEFAULT_AVATAR_GROUP_PROPS.shape,\n showStatus: DEFAULT_AVATAR_GROUP_PROPS.showStatus,\n showTooltip: DEFAULT_AVATAR_GROUP_PROPS.showTooltip,\n clickable: DEFAULT_AVATAR_GROUP_PROPS.clickable,\n overflowClickable: DEFAULT_AVATAR_GROUP_PROPS.overflowClickable,\n direction: DEFAULT_AVATAR_GROUP_PROPS.direction,\n }\n )\n\n const emit = defineEmits<{\n itemClick: [item: AvatarItem]\n overflowClick: [hiddenItems: AvatarItem[]]\n }>()\n\n const mergedProps = computed(() => ({\n ...DEFAULT_AVATAR_GROUP_PROPS,\n ...props,\n }))\n\n const visibleItems = computed(() => {\n const max = mergedProps.value.max!\n return props.items.length > max ? props.items.slice(0, max) : props.items\n })\n\n const hiddenItems = computed(() => {\n const max = mergedProps.value.max!\n return props.items.length > max ? props.items.slice(max) : []\n })\n\n const overflowCount = computed(() => hiddenItems.value.length)\n\n const initialsFontSize = computed(\n () => `${Math.round(mergedProps.value.size! * 0.38)}px`\n )\n\n /** 计算头像项定位样式 */\n function itemStyle(idx: number) {\n const s = mergedProps.value.size!\n const overlap = mergedProps.value.overlap!\n return {\n width: `${s}px`,\n height: `${s}px`,\n marginLeft: idx === 0 ? '0' : `${overlap}px`,\n }\n }\n\n const overflowStyle = computed(() => {\n const s = mergedProps.value.size!\n const overlap = mergedProps.value.overlap!\n return {\n width: `${s}px`,\n height: `${s}px`,\n marginLeft: `${overlap}px`,\n fontSize: `${Math.round(s * 0.32)}px`,\n }\n })\n\n /** 计算状态指示点样式 */\n function statusStyle(status: string) {\n const dotSize = Math.max(8, Math.round(mergedProps.value.size! * 0.24))\n return {\n width: `${dotSize}px`,\n height: `${dotSize}px`,\n backgroundColor: STATUS_COLOR_MAP[status] ?? STATUS_COLOR_MAP.offline,\n }\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Description: 头像组组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n class=\"c-avatar-group\"\n :class=\"{ 'is-rtl': mergedProps.direction === 'rtl' }\"\n >\n <NTooltip\n v-for=\"(item, idx) in visibleItems\"\n :key=\"item.id\"\n :disabled=\"!mergedProps.showTooltip\"\n placement=\"top\"\n >\n <template #trigger>\n <div\n class=\"c-avatar-group__item\"\n :class=\"[\n `is-${mergedProps.shape}`,\n { 'is-clickable': mergedProps.clickable },\n ]\"\n :style=\"itemStyle(idx)\"\n @click=\"mergedProps.clickable && emit('itemClick', item)\"\n >\n <img\n v-if=\"item.src\"\n :src=\"item.src\"\n :alt=\"item.name\"\n class=\"c-avatar-group__img\"\n />\n <div\n v-else\n class=\"c-avatar-group__initials\"\n :style=\"{\n background: nameToColor(item.name),\n fontSize: initialsFontSize,\n }\"\n >\n {{ getInitials(item.name) }}\n </div>\n\n <span\n v-if=\"mergedProps.showStatus && item.status\"\n class=\"c-avatar-group__status\"\n :style=\"statusStyle(item.status)\"\n />\n </div>\n </template>\n {{ item.tooltip ?? item.name }}\n </NTooltip>\n\n <!-- 溢出 +N -->\n <NTooltip\n v-if=\"overflowCount > 0\"\n placement=\"top\"\n >\n <template #trigger>\n <div\n class=\"c-avatar-group__overflow\"\n :class=\"[\n `is-${mergedProps.shape}`,\n { 'is-clickable': mergedProps.overflowClickable },\n ]\"\n :style=\"overflowStyle\"\n @click=\"\n mergedProps.overflowClickable && emit('overflowClick', hiddenItems)\n \"\n >\n +{{ overflowCount }}\n </div>\n </template>\n <div style=\"max-width: 200px\">\n <div\n v-for=\"h in hiddenItems\"\n :key=\"h.id\"\n style=\"line-height: 1.6\"\n >\n {{ h.name }}\n </div>\n </div>\n </NTooltip>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import {\n DEFAULT_AVATAR_GROUP_PROPS,\n getInitials,\n nameToColor,\n STATUS_COLOR_MAP,\n type AvatarGroupProps,\n type AvatarItem,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n items: AvatarGroupProps['items']\n max?: number\n size?: number\n overlap?: number\n shape?: AvatarGroupProps['shape']\n showStatus?: boolean\n showTooltip?: boolean\n clickable?: boolean\n overflowClickable?: boolean\n direction?: AvatarGroupProps['direction']\n }>(),\n {\n max: DEFAULT_AVATAR_GROUP_PROPS.max,\n size: DEFAULT_AVATAR_GROUP_PROPS.size,\n overlap: DEFAULT_AVATAR_GROUP_PROPS.overlap,\n shape: DEFAULT_AVATAR_GROUP_PROPS.shape,\n showStatus: DEFAULT_AVATAR_GROUP_PROPS.showStatus,\n showTooltip: DEFAULT_AVATAR_GROUP_PROPS.showTooltip,\n clickable: DEFAULT_AVATAR_GROUP_PROPS.clickable,\n overflowClickable: DEFAULT_AVATAR_GROUP_PROPS.overflowClickable,\n direction: DEFAULT_AVATAR_GROUP_PROPS.direction,\n }\n )\n\n const emit = defineEmits<{\n itemClick: [item: AvatarItem]\n overflowClick: [hiddenItems: AvatarItem[]]\n }>()\n\n const mergedProps = computed(() => ({\n ...DEFAULT_AVATAR_GROUP_PROPS,\n ...props,\n }))\n\n const visibleItems = computed(() => {\n const max = mergedProps.value.max!\n return props.items.length > max ? props.items.slice(0, max) : props.items\n })\n\n const hiddenItems = computed(() => {\n const max = mergedProps.value.max!\n return props.items.length > max ? props.items.slice(max) : []\n })\n\n const overflowCount = computed(() => hiddenItems.value.length)\n\n const initialsFontSize = computed(\n () => `${Math.round(mergedProps.value.size! * 0.38)}px`\n )\n\n /** 计算头像项定位样式 */\n function itemStyle(idx: number) {\n const s = mergedProps.value.size!\n const overlap = mergedProps.value.overlap!\n return {\n width: `${s}px`,\n height: `${s}px`,\n marginLeft: idx === 0 ? '0' : `${overlap}px`,\n }\n }\n\n const overflowStyle = computed(() => {\n const s = mergedProps.value.size!\n const overlap = mergedProps.value.overlap!\n return {\n width: `${s}px`,\n height: `${s}px`,\n marginLeft: `${overlap}px`,\n fontSize: `${Math.round(s * 0.32)}px`,\n }\n })\n\n /** 计算状态指示点样式 */\n function statusStyle(status: string) {\n const dotSize = Math.max(8, Math.round(mergedProps.value.size! * 0.24))\n return {\n width: `${dotSize}px`,\n height: `${dotSize}px`,\n backgroundColor: STATUS_COLOR_MAP[status] ?? STATUS_COLOR_MAP.offline,\n }\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_AvatarGroup2.js","names":[],"sources":["../src/components/C_AvatarGroup/types.ts","../src/components/C_AvatarGroup/index.vue","../src/components/C_AvatarGroup/index.vue"],"sourcesContent":["/**\n * @Description: 头像组组件类型定义\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n */\n\n/** 单个头像数据 */\nexport interface AvatarItem {\n /** 唯一标识 */\n id: string | number\n /** 头像图片 URL */\n src?: string\n /** 姓名(用于首字母 fallback 和 tooltip) */\n name: string\n /** 悬浮提示(默认取 name) */\n tooltip?: string\n /** 在线状态 */\n status?: 'online' | 'offline' | 'busy' | 'away'\n /** 额外数据 */\n extra?: Record<string, unknown>\n}\n\n/** 组件 Props */\nexport interface AvatarGroupProps {\n /** 头像数据列表 */\n items: AvatarItem[]\n /** 最多显示数量(超出 +N) */\n max?: number\n /** 头像尺寸 (px) */\n size?: number\n /** 头像间重叠偏移量 (px),负值表示堆叠 */\n overlap?: number\n /** 形状 */\n shape?: 'circle' | 'square'\n /** 是否显示状态指示点 */\n showStatus?: boolean\n /** 是否显示悬浮提示 */\n showTooltip?: boolean\n /** 是否可点击 */\n clickable?: boolean\n /** +N 溢出按钮是否可点击 */\n overflowClickable?: boolean\n /** 堆叠方向 */\n direction?: 'ltr' | 'rtl'\n}\n\n/** 默认 Props */\nexport const DEFAULT_AVATAR_GROUP_PROPS: Partial<AvatarGroupProps> = {\n max: 5,\n size: 40,\n overlap: -10,\n shape: 'circle',\n showStatus: true,\n showTooltip: true,\n clickable: false,\n overflowClickable: true,\n direction: 'ltr',\n}\n\n/** 状态颜色映射 */\nexport const STATUS_COLOR_MAP: Record<string, string> = {\n online: '#18a058',\n offline: '#909399',\n busy: '#e53e3e',\n away: '#f0a020',\n}\n\n/** 从名字中提取首字母 */\nexport function getInitials(name: string): string {\n if (!name) return '?'\n const parts = name.trim().split(/\\s+/)\n if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase()\n // 中文直接取最后一个字\n if (/[\\u4E00-\\u9FFF]/.test(name)) return name.slice(-1)\n return name.slice(0, 2).toUpperCase()\n}\n\n/** 根据名字生成稳定的背景色 */\nexport function nameToColor(name: string): string {\n const palette = [\n '#6366f1',\n '#8b5cf6',\n '#a855f7',\n '#d946ef',\n '#ec4899',\n '#f43f5e',\n '#ef4444',\n '#f97316',\n '#eab308',\n '#84cc16',\n '#22c55e',\n '#14b8a6',\n '#06b6d4',\n '#0ea5e9',\n '#3b82f6',\n '#6d28d9',\n ]\n let hash = 0\n for (let i = 0; i < name.length; i++) {\n hash = name.charCodeAt(i) + ((hash * 31) | 0)\n }\n return palette[Math.abs(hash) % palette.length]\n}\n","<!--\n * @Description: 头像组组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n class=\"c-avatar-group\"\n :class=\"{ 'is-rtl': mergedProps.direction === 'rtl' }\"\n >\n <NTooltip\n v-for=\"(item, idx) in visibleItems\"\n :key=\"item.id\"\n :disabled=\"!mergedProps.showTooltip\"\n placement=\"top\"\n >\n <template #trigger>\n <div\n class=\"c-avatar-group__item\"\n :class=\"[\n `is-${mergedProps.shape}`,\n { 'is-clickable': mergedProps.clickable },\n ]\"\n :style=\"itemStyle(idx)\"\n @click=\"mergedProps.clickable && emit('itemClick', item)\"\n >\n <img\n v-if=\"item.src\"\n :src=\"item.src\"\n :alt=\"item.name\"\n class=\"c-avatar-group__img\"\n />\n <div\n v-else\n class=\"c-avatar-group__initials\"\n :style=\"{\n background: nameToColor(item.name),\n fontSize: initialsFontSize,\n }\"\n >\n {{ getInitials(item.name) }}\n </div>\n\n <span\n v-if=\"mergedProps.showStatus && item.status\"\n class=\"c-avatar-group__status\"\n :style=\"statusStyle(item.status)\"\n />\n </div>\n </template>\n {{ item.tooltip ?? item.name }}\n </NTooltip>\n\n <!-- 溢出 +N -->\n <NTooltip\n v-if=\"overflowCount > 0\"\n placement=\"top\"\n >\n <template #trigger>\n <div\n class=\"c-avatar-group__overflow\"\n :class=\"[\n `is-${mergedProps.shape}`,\n { 'is-clickable': mergedProps.overflowClickable },\n ]\"\n :style=\"overflowStyle\"\n @click=\"\n mergedProps.overflowClickable && emit('overflowClick', hiddenItems)\n \"\n >\n +{{ overflowCount }}\n </div>\n </template>\n <div style=\"max-width: 200px\">\n <div\n v-for=\"h in hiddenItems\"\n :key=\"h.id\"\n style=\"line-height: 1.6\"\n >\n {{ h.name }}\n </div>\n </div>\n </NTooltip>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import {\n DEFAULT_AVATAR_GROUP_PROPS,\n getInitials,\n nameToColor,\n STATUS_COLOR_MAP,\n type AvatarGroupProps,\n type AvatarItem,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n items: AvatarGroupProps['items']\n max?: number\n size?: number\n overlap?: number\n shape?: AvatarGroupProps['shape']\n showStatus?: boolean\n showTooltip?: boolean\n clickable?: boolean\n overflowClickable?: boolean\n direction?: AvatarGroupProps['direction']\n }>(),\n {\n max: DEFAULT_AVATAR_GROUP_PROPS.max,\n size: DEFAULT_AVATAR_GROUP_PROPS.size,\n overlap: DEFAULT_AVATAR_GROUP_PROPS.overlap,\n shape: DEFAULT_AVATAR_GROUP_PROPS.shape,\n showStatus: DEFAULT_AVATAR_GROUP_PROPS.showStatus,\n showTooltip: DEFAULT_AVATAR_GROUP_PROPS.showTooltip,\n clickable: DEFAULT_AVATAR_GROUP_PROPS.clickable,\n overflowClickable: DEFAULT_AVATAR_GROUP_PROPS.overflowClickable,\n direction: DEFAULT_AVATAR_GROUP_PROPS.direction,\n }\n )\n\n const emit = defineEmits<{\n itemClick: [item: AvatarItem]\n overflowClick: [hiddenItems: AvatarItem[]]\n }>()\n\n const mergedProps = computed(() => ({\n ...DEFAULT_AVATAR_GROUP_PROPS,\n ...props,\n }))\n\n const visibleItems = computed(() => {\n const max = mergedProps.value.max!\n return props.items.length > max ? props.items.slice(0, max) : props.items\n })\n\n const hiddenItems = computed(() => {\n const max = mergedProps.value.max!\n return props.items.length > max ? props.items.slice(max) : []\n })\n\n const overflowCount = computed(() => hiddenItems.value.length)\n\n const initialsFontSize = computed(\n () => `${Math.round(mergedProps.value.size! * 0.38)}px`\n )\n\n /** 计算头像项定位样式 */\n function itemStyle(idx: number) {\n const s = mergedProps.value.size!\n const overlap = mergedProps.value.overlap!\n return {\n width: `${s}px`,\n height: `${s}px`,\n marginLeft: idx === 0 ? '0' : `${overlap}px`,\n }\n }\n\n const overflowStyle = computed(() => {\n const s = mergedProps.value.size!\n const overlap = mergedProps.value.overlap!\n return {\n width: `${s}px`,\n height: `${s}px`,\n marginLeft: `${overlap}px`,\n fontSize: `${Math.round(s * 0.32)}px`,\n }\n })\n\n /** 计算状态指示点样式 */\n function statusStyle(status: string) {\n const dotSize = Math.max(8, Math.round(mergedProps.value.size! * 0.24))\n return {\n width: `${dotSize}px`,\n height: `${dotSize}px`,\n backgroundColor: STATUS_COLOR_MAP[status] ?? STATUS_COLOR_MAP.offline,\n }\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Description: 头像组组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n class=\"c-avatar-group\"\n :class=\"{ 'is-rtl': mergedProps.direction === 'rtl' }\"\n >\n <NTooltip\n v-for=\"(item, idx) in visibleItems\"\n :key=\"item.id\"\n :disabled=\"!mergedProps.showTooltip\"\n placement=\"top\"\n >\n <template #trigger>\n <div\n class=\"c-avatar-group__item\"\n :class=\"[\n `is-${mergedProps.shape}`,\n { 'is-clickable': mergedProps.clickable },\n ]\"\n :style=\"itemStyle(idx)\"\n @click=\"mergedProps.clickable && emit('itemClick', item)\"\n >\n <img\n v-if=\"item.src\"\n :src=\"item.src\"\n :alt=\"item.name\"\n class=\"c-avatar-group__img\"\n />\n <div\n v-else\n class=\"c-avatar-group__initials\"\n :style=\"{\n background: nameToColor(item.name),\n fontSize: initialsFontSize,\n }\"\n >\n {{ getInitials(item.name) }}\n </div>\n\n <span\n v-if=\"mergedProps.showStatus && item.status\"\n class=\"c-avatar-group__status\"\n :style=\"statusStyle(item.status)\"\n />\n </div>\n </template>\n {{ item.tooltip ?? item.name }}\n </NTooltip>\n\n <!-- 溢出 +N -->\n <NTooltip\n v-if=\"overflowCount > 0\"\n placement=\"top\"\n >\n <template #trigger>\n <div\n class=\"c-avatar-group__overflow\"\n :class=\"[\n `is-${mergedProps.shape}`,\n { 'is-clickable': mergedProps.overflowClickable },\n ]\"\n :style=\"overflowStyle\"\n @click=\"\n mergedProps.overflowClickable && emit('overflowClick', hiddenItems)\n \"\n >\n +{{ overflowCount }}\n </div>\n </template>\n <div style=\"max-width: 200px\">\n <div\n v-for=\"h in hiddenItems\"\n :key=\"h.id\"\n style=\"line-height: 1.6\"\n >\n {{ h.name }}\n </div>\n </div>\n </NTooltip>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import {\n DEFAULT_AVATAR_GROUP_PROPS,\n getInitials,\n nameToColor,\n STATUS_COLOR_MAP,\n type AvatarGroupProps,\n type AvatarItem,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n items: AvatarGroupProps['items']\n max?: number\n size?: number\n overlap?: number\n shape?: AvatarGroupProps['shape']\n showStatus?: boolean\n showTooltip?: boolean\n clickable?: boolean\n overflowClickable?: boolean\n direction?: AvatarGroupProps['direction']\n }>(),\n {\n max: DEFAULT_AVATAR_GROUP_PROPS.max,\n size: DEFAULT_AVATAR_GROUP_PROPS.size,\n overlap: DEFAULT_AVATAR_GROUP_PROPS.overlap,\n shape: DEFAULT_AVATAR_GROUP_PROPS.shape,\n showStatus: DEFAULT_AVATAR_GROUP_PROPS.showStatus,\n showTooltip: DEFAULT_AVATAR_GROUP_PROPS.showTooltip,\n clickable: DEFAULT_AVATAR_GROUP_PROPS.clickable,\n overflowClickable: DEFAULT_AVATAR_GROUP_PROPS.overflowClickable,\n direction: DEFAULT_AVATAR_GROUP_PROPS.direction,\n }\n )\n\n const emit = defineEmits<{\n itemClick: [item: AvatarItem]\n overflowClick: [hiddenItems: AvatarItem[]]\n }>()\n\n const mergedProps = computed(() => ({\n ...DEFAULT_AVATAR_GROUP_PROPS,\n ...props,\n }))\n\n const visibleItems = computed(() => {\n const max = mergedProps.value.max!\n return props.items.length > max ? props.items.slice(0, max) : props.items\n })\n\n const hiddenItems = computed(() => {\n const max = mergedProps.value.max!\n return props.items.length > max ? props.items.slice(max) : []\n })\n\n const overflowCount = computed(() => hiddenItems.value.length)\n\n const initialsFontSize = computed(\n () => `${Math.round(mergedProps.value.size! * 0.38)}px`\n )\n\n /** 计算头像项定位样式 */\n function itemStyle(idx: number) {\n const s = mergedProps.value.size!\n const overlap = mergedProps.value.overlap!\n return {\n width: `${s}px`,\n height: `${s}px`,\n marginLeft: idx === 0 ? '0' : `${overlap}px`,\n }\n }\n\n const overflowStyle = computed(() => {\n const s = mergedProps.value.size!\n const overlap = mergedProps.value.overlap!\n return {\n width: `${s}px`,\n height: `${s}px`,\n marginLeft: `${overlap}px`,\n fontSize: `${Math.round(s * 0.32)}px`,\n }\n })\n\n /** 计算状态指示点样式 */\n function statusStyle(status: string) {\n const dotSize = Math.max(8, Math.round(mergedProps.value.size! * 0.24))\n return {\n width: `${dotSize}px`,\n height: `${dotSize}px`,\n backgroundColor: STATUS_COLOR_MAP[status] ?? STATUS_COLOR_MAP.offline,\n }\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Barcode-CAU2mFcx.css","names":[],"sources":["../src/components/C_Barcode/index.vue?vue&type=style&index=0&scoped=a8846f89&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-barcode[data-v-a8846f89] {\n display: inline-flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n max-width: 100%;\n}\n.c-barcode .barcode-wrapper[data-v-a8846f89] {\n display: inline-block;\n padding: 8px;\n background: #ffffff;\n border-radius: 4px;\n transition: all 0.3s ease;\n overflow: hidden;\n max-width: 100%;\n}\n.c-barcode .barcode-wrapper.with-border[data-v-a8846f89] {\n border: 1px dashed #e0e0e0;\n}\n.c-barcode .barcode-wrapper[data-v-a8846f89]:hover {\n border-color: var(--c-primary, #409eff);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n.c-barcode .barcode-wrapper[data-v-a8846f89] svg {\n max-width: 100%;\n height: auto;\n display: block;\n overflow: visible;\n}\n.c-barcode .barcode-label[data-v-a8846f89] {\n font-size: 14px;\n color: #666;\n text-align: center;\n font-weight: 500;\n word-break: break-all;\n max-width: 100%;\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Barcode-D2oJaEg3.css","names":[],"sources":["../src/components/C_Barcode/index.vue?vue&type=style&index=0&scoped=a8846f89&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-barcode[data-v-a8846f89] {\n display: inline-flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n max-width: 100%;\n}\n.c-barcode .barcode-wrapper[data-v-a8846f89] {\n display: inline-block;\n padding: 8px;\n background: #ffffff;\n border-radius: 4px;\n transition: all 0.3s ease;\n overflow: hidden;\n max-width: 100%;\n}\n.c-barcode .barcode-wrapper.with-border[data-v-a8846f89] {\n border: 1px dashed #e0e0e0;\n}\n.c-barcode .barcode-wrapper[data-v-a8846f89]:hover {\n border-color: var(--c-primary, #409eff);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n.c-barcode .barcode-wrapper[data-v-a8846f89] svg {\n max-width: 100%;\n height: auto;\n display: block;\n overflow: visible;\n}\n.c-barcode .barcode-label[data-v-a8846f89] {\n font-size: 14px;\n color: #666;\n text-align: center;\n font-weight: 500;\n word-break: break-all;\n max-width: 100%;\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Barcode.js","names":["showBorder","showLabel","label"],"sources":["../src/components/C_Barcode/index.vue","../src/components/C_Barcode/index.vue"],"sourcesContent":["<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-12-02\n * @Description: 条形码组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-barcode\">\n <div class=\"barcode-wrapper\" :class=\"{ 'with-border': showBorder }\">\n <VueBarcode v-bind=\"barcodeProps\" @error=\"handleError\" />\n </div>\n <div v-if=\"showLabel && label\" class=\"barcode-label\">\n {{ label }}\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport VueBarcode from \"@chenfengyuan/vue-barcode\";\n\ndefineOptions({ name: \"C_Barcode\" });\n\ninterface Props {\n value: string;\n format?:\n | \"CODE128\"\n | \"CODE39\"\n | \"EAN13\"\n | \"EAN8\"\n | \"UPC\"\n | \"ITF14\"\n | \"MSI\"\n | \"pharmacode\";\n width?: number;\n height?: number;\n showText?: boolean;\n fontSize?: number;\n textPosition?: \"bottom\" | \"top\";\n lineColor?: string;\n background?: string;\n showBorder?: boolean;\n label?: string;\n showLabel?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n format: \"CODE128\",\n width: 2,\n height: 80,\n showText: true,\n fontSize: 20,\n textPosition: \"bottom\",\n lineColor: \"#000000\",\n background: \"#FFFFFF\",\n showBorder: true,\n label: \"\",\n showLabel: false,\n});\n\nconst emit = defineEmits<{\n error: [error: Error];\n}>();\n\nconst barcodeProps = computed(() => ({\n value: props.value,\n format: props.format,\n width: props.width,\n height: props.height,\n displayValue: props.showText,\n fontSize: props.fontSize,\n textPosition: props.textPosition,\n lineColor: props.lineColor,\n background: props.background,\n}));\n\nconst handleError = (error: Error) => {\n console.error(\"条形码生成失败:\", error);\n emit(\"error\", error);\n};\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-12-02\n * @Description: 条形码组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-barcode\">\n <div class=\"barcode-wrapper\" :class=\"{ 'with-border': showBorder }\">\n <VueBarcode v-bind=\"barcodeProps\" @error=\"handleError\" />\n </div>\n <div v-if=\"showLabel && label\" class=\"barcode-label\">\n {{ label }}\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport VueBarcode from \"@chenfengyuan/vue-barcode\";\n\ndefineOptions({ name: \"C_Barcode\" });\n\ninterface Props {\n value: string;\n format?:\n | \"CODE128\"\n | \"CODE39\"\n | \"EAN13\"\n | \"EAN8\"\n | \"UPC\"\n | \"ITF14\"\n | \"MSI\"\n | \"pharmacode\";\n width?: number;\n height?: number;\n showText?: boolean;\n fontSize?: number;\n textPosition?: \"bottom\" | \"top\";\n lineColor?: string;\n background?: string;\n showBorder?: boolean;\n label?: string;\n showLabel?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n format: \"CODE128\",\n width: 2,\n height: 80,\n showText: true,\n fontSize: 20,\n textPosition: \"bottom\",\n lineColor: \"#000000\",\n background: \"#FFFFFF\",\n showBorder: true,\n label: \"\",\n showLabel: false,\n});\n\nconst emit = defineEmits<{\n error: [error: Error];\n}>();\n\nconst barcodeProps = computed(() => ({\n value: props.value,\n format: props.format,\n width: props.width,\n height: props.height,\n displayValue: props.showText,\n fontSize: props.fontSize,\n textPosition: props.textPosition,\n lineColor: props.lineColor,\n background: props.background,\n}));\n\nconst handleError = (error: Error) => {\n console.error(\"条形码生成失败:\", error);\n emit(\"error\", error);\n};\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n"],"mappings":"8lBA+CA,IAAM,EAAQ,EAcR,EAAO,EAIP,GAAA,EAAA,EAAA,eAA+B,CACnC,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,aAAc,EAAM,SACpB,SAAU,EAAM,SAChB,aAAc,EAAM,aACpB,UAAW,EAAM,UACjB,WAAY,EAAM,WACnB,EAAE,CAEG,EAAe,GAAiB,CACpC,QAAQ,MAAM,WAAY,EAAM,CAChC,EAAK,QAAS,EAAM,2DChEd,MAPN,EAOM,EAAA,EAAA,EAAA,oBAJE,MAAA,CAFD,OAAA,EAAA,EAAA,gBAAK,CAAC,kBAAiB,CAAA,cAA0BA,EAAAA,WAAU,CAAA,CAAA,CAAA,CAAA,EAAA,EAAA,EAAA,cAAA,EAAA,EAAA,OACL,EAAA,QAAA,EAAA,EAAA,EAAA,YAArC,EAAqC,MAAzB,CAAG,QAAO,EAAW,CAAA,CAAA,KAAA,GAAA,CAAA,CAAA,EAAA,CAE5CC,EAAAA,WAAaC,EAAAA,QAAAA,EAAAA,EAAAA,YAAAA,EAAAA,EAAAA,EAAAA,oBAElB,MAFN,GAAA,EAAA,EAAA,iBACKA,EAAAA,MAAK,CAAA,EAAA,GAAA,EAAA,EAAA,oBAAA,OAAA,GAAA,CAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Barcode2.js","names":["showBorder","showLabel","label"],"sources":["../src/components/C_Barcode/index.vue","../src/components/C_Barcode/index.vue"],"sourcesContent":["<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-12-02\n * @Description: 条形码组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-barcode\">\n <div class=\"barcode-wrapper\" :class=\"{ 'with-border': showBorder }\">\n <VueBarcode v-bind=\"barcodeProps\" @error=\"handleError\" />\n </div>\n <div v-if=\"showLabel && label\" class=\"barcode-label\">\n {{ label }}\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport VueBarcode from \"@chenfengyuan/vue-barcode\";\n\ndefineOptions({ name: \"C_Barcode\" });\n\ninterface Props {\n value: string;\n format?:\n | \"CODE128\"\n | \"CODE39\"\n | \"EAN13\"\n | \"EAN8\"\n | \"UPC\"\n | \"ITF14\"\n | \"MSI\"\n | \"pharmacode\";\n width?: number;\n height?: number;\n showText?: boolean;\n fontSize?: number;\n textPosition?: \"bottom\" | \"top\";\n lineColor?: string;\n background?: string;\n showBorder?: boolean;\n label?: string;\n showLabel?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n format: \"CODE128\",\n width: 2,\n height: 80,\n showText: true,\n fontSize: 20,\n textPosition: \"bottom\",\n lineColor: \"#000000\",\n background: \"#FFFFFF\",\n showBorder: true,\n label: \"\",\n showLabel: false,\n});\n\nconst emit = defineEmits<{\n error: [error: Error];\n}>();\n\nconst barcodeProps = computed(() => ({\n value: props.value,\n format: props.format,\n width: props.width,\n height: props.height,\n displayValue: props.showText,\n fontSize: props.fontSize,\n textPosition: props.textPosition,\n lineColor: props.lineColor,\n background: props.background,\n}));\n\nconst handleError = (error: Error) => {\n console.error(\"条形码生成失败:\", error);\n emit(\"error\", error);\n};\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-12-02\n * @Description: 条形码组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-barcode\">\n <div class=\"barcode-wrapper\" :class=\"{ 'with-border': showBorder }\">\n <VueBarcode v-bind=\"barcodeProps\" @error=\"handleError\" />\n </div>\n <div v-if=\"showLabel && label\" class=\"barcode-label\">\n {{ label }}\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport VueBarcode from \"@chenfengyuan/vue-barcode\";\n\ndefineOptions({ name: \"C_Barcode\" });\n\ninterface Props {\n value: string;\n format?:\n | \"CODE128\"\n | \"CODE39\"\n | \"EAN13\"\n | \"EAN8\"\n | \"UPC\"\n | \"ITF14\"\n | \"MSI\"\n | \"pharmacode\";\n width?: number;\n height?: number;\n showText?: boolean;\n fontSize?: number;\n textPosition?: \"bottom\" | \"top\";\n lineColor?: string;\n background?: string;\n showBorder?: boolean;\n label?: string;\n showLabel?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n format: \"CODE128\",\n width: 2,\n height: 80,\n showText: true,\n fontSize: 20,\n textPosition: \"bottom\",\n lineColor: \"#000000\",\n background: \"#FFFFFF\",\n showBorder: true,\n label: \"\",\n showLabel: false,\n});\n\nconst emit = defineEmits<{\n error: [error: Error];\n}>();\n\nconst barcodeProps = computed(() => ({\n value: props.value,\n format: props.format,\n width: props.width,\n height: props.height,\n displayValue: props.showText,\n fontSize: props.fontSize,\n textPosition: props.textPosition,\n lineColor: props.lineColor,\n background: props.background,\n}));\n\nconst handleError = (error: Error) => {\n console.error(\"条形码生成失败:\", error);\n emit(\"error\", error);\n};\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n"],"mappings":"6vBA+CA,IAAM,EAAQ,EAcR,EAAO,EAIP,EAAe,OAAgB,CACnC,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,aAAc,EAAM,SACpB,SAAU,EAAM,SAChB,aAAc,EAAM,aACpB,UAAW,EAAM,UACjB,WAAY,EAAM,WACnB,EAAE,CAEG,EAAe,GAAiB,CACpC,QAAQ,MAAM,WAAY,EAAM,CAChC,EAAK,QAAS,EAAM,oBCvEpB,EAOM,MAPN,EAOM,CANJ,EAEM,MAAA,CAFD,MAAK,EAAA,CAAC,kBAAiB,CAAA,cAA0BA,EAAAA,WAAU,CAAA,CAAA,CAAA,CAAA,CAC9D,EAAyD,EAAA,EAAA,CAAzD,EAAoB,EAAqC,MAAzB,CAAG,QAAO,EAAW,CAAA,CAAA,KAAA,GAAA,CAAA,CAAA,EAAA,CAE5CC,EAAAA,WAAaC,EAAAA,OAAAA,GAAAA,CAAxB,EAEM,MAFN,EAEM,EADDA,EAAAA,MAAK,CAAA,EAAA,EAAA,EAAA,OAAA,GAAA,CAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Breadcrumb-BiH0d9AI.css","names":[],"sources":["../src/components/C_Breadcrumb/index.vue?vue&type=style&index=0&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-breadcrumb {\n --c-breadcrumb-icon-size: 16px;\n --c-breadcrumb-gap: 4px;\n --c-breadcrumb-font-size: inherit;\n display: flex;\n align-items: center;\n height: 100%;\n}\n.c-breadcrumb__trigger, .c-breadcrumb__link {\n display: inline-flex;\n align-items: center;\n gap: var(--c-breadcrumb-gap);\n font-size: var(--c-breadcrumb-font-size);\n line-height: 1;\n cursor: pointer;\n}\n.c-breadcrumb .c-icon {\n flex-shrink: 0;\n}\n.c-breadcrumb .n-breadcrumb-item {\n display: inline-flex !important;\n align-items: center;\n}\n.c-breadcrumb .n-breadcrumb-item .n-breadcrumb-item__separator {\n display: inline-flex;\n align-items: center;\n line-height: 1;\n margin-top: -4px;\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Breadcrumb-DOcLt9uX.css","names":[],"sources":["../src/components/C_Breadcrumb/index.vue?vue&type=style&index=0&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-breadcrumb {\n --c-breadcrumb-icon-size: 16px;\n --c-breadcrumb-gap: 4px;\n --c-breadcrumb-font-size: inherit;\n display: flex;\n align-items: center;\n height: 100%;\n}\n.c-breadcrumb__trigger, .c-breadcrumb__link {\n display: inline-flex;\n align-items: center;\n gap: var(--c-breadcrumb-gap);\n font-size: var(--c-breadcrumb-font-size);\n line-height: 1;\n cursor: pointer;\n}\n.c-breadcrumb .c-icon {\n flex-shrink: 0;\n}\n.c-breadcrumb .n-breadcrumb-item {\n display: inline-flex !important;\n align-items: center;\n}\n.c-breadcrumb .n-breadcrumb-item .n-breadcrumb-item__separator {\n display: inline-flex;\n align-items: center;\n line-height: 1;\n margin-top: -4px;\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Breadcrumb.js","names":["iconSize","showIcon"],"sources":["../src/components/C_Breadcrumb/index.vue","../src/components/C_Breadcrumb/index.vue"],"sourcesContent":["<!--\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\n *\n * 支持两种模式:\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\n * 2. 手动模式 — 传入 `items` 完全自定义\n *\n * @example 自动模式(零配置)\n * ```vue\n * <C_Breadcrumb\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 手动模式\n * ```vue\n * <C_Breadcrumb\n * :items=\"customBreadcrumbs\"\n * @select=\"handleSelect\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div\n class=\"c-breadcrumb flex-1 min-w-0\"\n :style=\"{\n '--c-breadcrumb-icon-size': `${iconSize}px`,\n '--c-breadcrumb-gap': `${Math.max(Math.round(iconSize / 4), 2)}px`,\n }\"\n >\n <NBreadcrumb>\n <NBreadcrumbItem\n v-for=\"(item, index) in breadcrumbItems\"\n :key=\"index\"\n >\n <NDropdown\n v-if=\"item.children?.length\"\n :options=\"item.children\"\n @select=\"handleSelect\"\n >\n <div class=\"c-breadcrumb__trigger\">\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n :size=\"iconSize\"\n />\n {{ item.label }}\n </div>\n </NDropdown>\n <span\n v-else\n class=\"c-breadcrumb__link\"\n @click=\"handleSelect(item.key)\"\n >\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n :size=\"iconSize\"\n />\n {{ item.label }}\n </span>\n </NBreadcrumbItem>\n </NBreadcrumb>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useRoute } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { BreadcrumbItem } from '../_shared'\n\n defineOptions({ name: 'C_Breadcrumb' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 手动传入面包屑数据\n * 不传时自动从 `route.matched` 生成(零配置)\n */\n items?: BreadcrumbItem[]\n /** 是否显示图标 */\n showIcon?: boolean\n /** 图标大小 (px),默认 16 */\n iconSize?: number\n /**\n * 标签文本格式化函数(用于 i18n)\n * 仅在自动模式下生效\n */\n labelFormatter?: (label: string) => string\n }>(),\n {\n showIcon: true,\n iconSize: 16,\n }\n )\n\n const emit = defineEmits<{\n /** 面包屑项被选中(返回路径 key) */\n select: [path: string]\n }>()\n\n const route = useRoute()\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n /** 从 route.matched 自动生成面包屑 */\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\n return route.matched\n .filter((record: any) => record.meta?.title)\n .map((record: any) => ({\n key: record.path,\n label: formatLabel((record.meta.title || '') as string),\n icon: record.meta.icon as string | undefined,\n children: record.children?.length\n ? record.children.map((child: any) => ({\n key: child.path,\n label: formatLabel((child.meta?.title || '') as string),\n ...(child.children?.length && {\n children: child.children.map((grandChild: any) => ({\n key: grandChild.path,\n label: formatLabel((grandChild.meta?.title || '') as string),\n })),\n }),\n }))\n : [],\n }))\n })\n\n const breadcrumbItems = computed<BreadcrumbItem[]>(\n () => props.items ?? autoBreadcrumb.value\n )\n\n const handleSelect = (key: string) => {\n emit('select', key)\n }\n</script>\n\n<style lang=\"scss\">\n .c-breadcrumb {\n // 暴露 CSS 变量,方便主项目覆盖\n --c-breadcrumb-icon-size: 16px;\n --c-breadcrumb-gap: 4px;\n --c-breadcrumb-font-size: inherit;\n\n display: flex;\n align-items: center;\n height: 100%;\n\n &__trigger,\n &__link {\n display: inline-flex;\n align-items: center;\n gap: var(--c-breadcrumb-gap);\n font-size: var(--c-breadcrumb-font-size);\n line-height: 1;\n cursor: pointer;\n }\n\n // 确保 C_Icon 在面包屑中尺寸一致\n .c-icon {\n flex-shrink: 0;\n }\n\n // 修复分隔符 \"/\" 与文本/图标垂直居中对齐\n // 原因:Naive UI 分隔符默认 line-height (~1.4) 导致 \"/\" 基线偏低\n .n-breadcrumb-item {\n display: inline-flex !important;\n align-items: center;\n\n .n-breadcrumb-item__separator {\n display: inline-flex;\n align-items: center;\n line-height: 1;\n margin-top: -4px;\n }\n }\n }\n</style>\n","<!--\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\n *\n * 支持两种模式:\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\n * 2. 手动模式 — 传入 `items` 完全自定义\n *\n * @example 自动模式(零配置)\n * ```vue\n * <C_Breadcrumb\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 手动模式\n * ```vue\n * <C_Breadcrumb\n * :items=\"customBreadcrumbs\"\n * @select=\"handleSelect\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div\n class=\"c-breadcrumb flex-1 min-w-0\"\n :style=\"{\n '--c-breadcrumb-icon-size': `${iconSize}px`,\n '--c-breadcrumb-gap': `${Math.max(Math.round(iconSize / 4), 2)}px`,\n }\"\n >\n <NBreadcrumb>\n <NBreadcrumbItem\n v-for=\"(item, index) in breadcrumbItems\"\n :key=\"index\"\n >\n <NDropdown\n v-if=\"item.children?.length\"\n :options=\"item.children\"\n @select=\"handleSelect\"\n >\n <div class=\"c-breadcrumb__trigger\">\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n :size=\"iconSize\"\n />\n {{ item.label }}\n </div>\n </NDropdown>\n <span\n v-else\n class=\"c-breadcrumb__link\"\n @click=\"handleSelect(item.key)\"\n >\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n :size=\"iconSize\"\n />\n {{ item.label }}\n </span>\n </NBreadcrumbItem>\n </NBreadcrumb>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useRoute } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { BreadcrumbItem } from '../_shared'\n\n defineOptions({ name: 'C_Breadcrumb' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 手动传入面包屑数据\n * 不传时自动从 `route.matched` 生成(零配置)\n */\n items?: BreadcrumbItem[]\n /** 是否显示图标 */\n showIcon?: boolean\n /** 图标大小 (px),默认 16 */\n iconSize?: number\n /**\n * 标签文本格式化函数(用于 i18n)\n * 仅在自动模式下生效\n */\n labelFormatter?: (label: string) => string\n }>(),\n {\n showIcon: true,\n iconSize: 16,\n }\n )\n\n const emit = defineEmits<{\n /** 面包屑项被选中(返回路径 key) */\n select: [path: string]\n }>()\n\n const route = useRoute()\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n /** 从 route.matched 自动生成面包屑 */\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\n return route.matched\n .filter((record: any) => record.meta?.title)\n .map((record: any) => ({\n key: record.path,\n label: formatLabel((record.meta.title || '') as string),\n icon: record.meta.icon as string | undefined,\n children: record.children?.length\n ? record.children.map((child: any) => ({\n key: child.path,\n label: formatLabel((child.meta?.title || '') as string),\n ...(child.children?.length && {\n children: child.children.map((grandChild: any) => ({\n key: grandChild.path,\n label: formatLabel((grandChild.meta?.title || '') as string),\n })),\n }),\n }))\n : [],\n }))\n })\n\n const breadcrumbItems = computed<BreadcrumbItem[]>(\n () => props.items ?? autoBreadcrumb.value\n )\n\n const handleSelect = (key: string) => {\n emit('select', key)\n }\n</script>\n\n<style lang=\"scss\">\n .c-breadcrumb {\n // 暴露 CSS 变量,方便主项目覆盖\n --c-breadcrumb-icon-size: 16px;\n --c-breadcrumb-gap: 4px;\n --c-breadcrumb-font-size: inherit;\n\n display: flex;\n align-items: center;\n height: 100%;\n\n &__trigger,\n &__link {\n display: inline-flex;\n align-items: center;\n gap: var(--c-breadcrumb-gap);\n font-size: var(--c-breadcrumb-font-size);\n line-height: 1;\n cursor: pointer;\n }\n\n // 确保 C_Icon 在面包屑中尺寸一致\n .c-icon {\n flex-shrink: 0;\n }\n\n // 修复分隔符 \"/\" 与文本/图标垂直居中对齐\n // 原因:Naive UI 分隔符默认 line-height (~1.4) 导致 \"/\" 基线偏低\n .n-breadcrumb-item {\n display: inline-flex !important;\n align-items: center;\n\n .n-breadcrumb-item__separator {\n display: inline-flex;\n align-items: center;\n line-height: 1;\n margin-top: -4px;\n }\n }\n }\n</style>\n"],"mappings":"yWA6EE,IAAM,EAAQ,EAuBR,EAAO,EAKP,GAAA,EAAA,EAAA,WAAiB,CAEjB,EAAe,GACnB,EAAM,eAAiB,EAAM,eAAe,EAAM,CAAG,EAGjD,GAAA,EAAA,EAAA,cACG,EAAM,QACV,OAAQ,GAAgB,EAAO,MAAM,MAAK,CAC1C,IAAK,IAAiB,CACrB,IAAK,EAAO,KACZ,MAAO,EAAa,EAAO,KAAK,OAAS,GAAc,CACvD,KAAM,EAAO,KAAK,KAClB,SAAU,EAAO,UAAU,OACvB,EAAO,SAAS,IAAK,IAAgB,CACnC,IAAK,EAAM,KACX,MAAO,EAAa,EAAM,MAAM,OAAS,GAAc,CACvD,GAAI,EAAM,UAAU,QAAU,CAC5B,SAAU,EAAM,SAAS,IAAK,IAAqB,CACjD,IAAK,EAAW,KAChB,MAAO,EAAa,EAAW,MAAM,OAAS,GAAA,CAC/C,EAAE,CAAA,CAEN,EAAC,CACF,EAAA,CACL,EAAC,CACL,CAEK,GAAA,EAAA,EAAA,cACE,EAAM,OAAS,EAAe,MACtC,CAEM,EAAgB,GAAgB,CACpC,EAAK,SAAU,EAAG,uHCxEd,MAAA,CAvCJ,MAAM,8BACL,OAAA,EAAA,EAAA,gBAAK,+BAAyCA,EAAAA,SAAQ,4BAAqC,KAAK,IAAI,KAAK,MAAMA,EAAAA,SAAQ,EAAA,CAAA,EAAA,CAAA,0BAqC1G,EAAA,KAAA,2BA9B8B,GAAA,EAAA,EAAA,WAAA,GAAA,EAAA,EAAA,EAAA,oBA6BxB,EAAA,SAAA,MAAA,EAAA,EAAA,YA7BQ,EAAA,OAAhB,EAAM,yCA6BE,EAAA,CA5Bf,IAAK,EAAK,CAAA,2BAeC,CAZJ,EAAK,UAAU,SAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,aAYX,EAAA,OAXT,QAAS,EAAK,SACd,SAAQ,8BASH,EAAA,EAAA,EAAA,oBAAA,MAPN,EAOM,CALIC,EAAAA,WAAAA,EAAAA,EAAAA,YAAAA,EAAAA,EAAAA,EAAAA,cAAAA,EAAAA,EAAAA,OAGN,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACX,KAAMD,EAAAA,6FACP,KAAA,EAAA,EAAA,iBACC,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,CAAA,CAAA,qEAcV,OAAA,OATL,MAAM,qBACL,QAAK,GAAE,EAAa,EAAK,IAAA,GAGlBC,EAAAA,WAAAA,EAAAA,EAAAA,YAAAA,EAAAA,EAAAA,EAAAA,cAAAA,EAAAA,EAAAA,OAGN,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACX,KAAMD,EAAAA,6FACP,KAAA,EAAA,EAAA,iBACC,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Breadcrumb2.js","names":["iconSize","showIcon"],"sources":["../src/components/C_Breadcrumb/index.vue","../src/components/C_Breadcrumb/index.vue"],"sourcesContent":["<!--\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\n *\n * 支持两种模式:\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\n * 2. 手动模式 — 传入 `items` 完全自定义\n *\n * @example 自动模式(零配置)\n * ```vue\n * <C_Breadcrumb\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 手动模式\n * ```vue\n * <C_Breadcrumb\n * :items=\"customBreadcrumbs\"\n * @select=\"handleSelect\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div\n class=\"c-breadcrumb flex-1 min-w-0\"\n :style=\"{\n '--c-breadcrumb-icon-size': `${iconSize}px`,\n '--c-breadcrumb-gap': `${Math.max(Math.round(iconSize / 4), 2)}px`,\n }\"\n >\n <NBreadcrumb>\n <NBreadcrumbItem\n v-for=\"(item, index) in breadcrumbItems\"\n :key=\"index\"\n >\n <NDropdown\n v-if=\"item.children?.length\"\n :options=\"item.children\"\n @select=\"handleSelect\"\n >\n <div class=\"c-breadcrumb__trigger\">\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n :size=\"iconSize\"\n />\n {{ item.label }}\n </div>\n </NDropdown>\n <span\n v-else\n class=\"c-breadcrumb__link\"\n @click=\"handleSelect(item.key)\"\n >\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n :size=\"iconSize\"\n />\n {{ item.label }}\n </span>\n </NBreadcrumbItem>\n </NBreadcrumb>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useRoute } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { BreadcrumbItem } from '../_shared'\n\n defineOptions({ name: 'C_Breadcrumb' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 手动传入面包屑数据\n * 不传时自动从 `route.matched` 生成(零配置)\n */\n items?: BreadcrumbItem[]\n /** 是否显示图标 */\n showIcon?: boolean\n /** 图标大小 (px),默认 16 */\n iconSize?: number\n /**\n * 标签文本格式化函数(用于 i18n)\n * 仅在自动模式下生效\n */\n labelFormatter?: (label: string) => string\n }>(),\n {\n showIcon: true,\n iconSize: 16,\n }\n )\n\n const emit = defineEmits<{\n /** 面包屑项被选中(返回路径 key) */\n select: [path: string]\n }>()\n\n const route = useRoute()\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n /** 从 route.matched 自动生成面包屑 */\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\n return route.matched\n .filter((record: any) => record.meta?.title)\n .map((record: any) => ({\n key: record.path,\n label: formatLabel((record.meta.title || '') as string),\n icon: record.meta.icon as string | undefined,\n children: record.children?.length\n ? record.children.map((child: any) => ({\n key: child.path,\n label: formatLabel((child.meta?.title || '') as string),\n ...(child.children?.length && {\n children: child.children.map((grandChild: any) => ({\n key: grandChild.path,\n label: formatLabel((grandChild.meta?.title || '') as string),\n })),\n }),\n }))\n : [],\n }))\n })\n\n const breadcrumbItems = computed<BreadcrumbItem[]>(\n () => props.items ?? autoBreadcrumb.value\n )\n\n const handleSelect = (key: string) => {\n emit('select', key)\n }\n</script>\n\n<style lang=\"scss\">\n .c-breadcrumb {\n // 暴露 CSS 变量,方便主项目覆盖\n --c-breadcrumb-icon-size: 16px;\n --c-breadcrumb-gap: 4px;\n --c-breadcrumb-font-size: inherit;\n\n display: flex;\n align-items: center;\n height: 100%;\n\n &__trigger,\n &__link {\n display: inline-flex;\n align-items: center;\n gap: var(--c-breadcrumb-gap);\n font-size: var(--c-breadcrumb-font-size);\n line-height: 1;\n cursor: pointer;\n }\n\n // 确保 C_Icon 在面包屑中尺寸一致\n .c-icon {\n flex-shrink: 0;\n }\n\n // 修复分隔符 \"/\" 与文本/图标垂直居中对齐\n // 原因:Naive UI 分隔符默认 line-height (~1.4) 导致 \"/\" 基线偏低\n .n-breadcrumb-item {\n display: inline-flex !important;\n align-items: center;\n\n .n-breadcrumb-item__separator {\n display: inline-flex;\n align-items: center;\n line-height: 1;\n margin-top: -4px;\n }\n }\n }\n</style>\n","<!--\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\n *\n * 支持两种模式:\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\n * 2. 手动模式 — 传入 `items` 完全自定义\n *\n * @example 自动模式(零配置)\n * ```vue\n * <C_Breadcrumb\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 手动模式\n * ```vue\n * <C_Breadcrumb\n * :items=\"customBreadcrumbs\"\n * @select=\"handleSelect\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div\n class=\"c-breadcrumb flex-1 min-w-0\"\n :style=\"{\n '--c-breadcrumb-icon-size': `${iconSize}px`,\n '--c-breadcrumb-gap': `${Math.max(Math.round(iconSize / 4), 2)}px`,\n }\"\n >\n <NBreadcrumb>\n <NBreadcrumbItem\n v-for=\"(item, index) in breadcrumbItems\"\n :key=\"index\"\n >\n <NDropdown\n v-if=\"item.children?.length\"\n :options=\"item.children\"\n @select=\"handleSelect\"\n >\n <div class=\"c-breadcrumb__trigger\">\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n :size=\"iconSize\"\n />\n {{ item.label }}\n </div>\n </NDropdown>\n <span\n v-else\n class=\"c-breadcrumb__link\"\n @click=\"handleSelect(item.key)\"\n >\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n :size=\"iconSize\"\n />\n {{ item.label }}\n </span>\n </NBreadcrumbItem>\n </NBreadcrumb>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useRoute } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { BreadcrumbItem } from '../_shared'\n\n defineOptions({ name: 'C_Breadcrumb' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 手动传入面包屑数据\n * 不传时自动从 `route.matched` 生成(零配置)\n */\n items?: BreadcrumbItem[]\n /** 是否显示图标 */\n showIcon?: boolean\n /** 图标大小 (px),默认 16 */\n iconSize?: number\n /**\n * 标签文本格式化函数(用于 i18n)\n * 仅在自动模式下生效\n */\n labelFormatter?: (label: string) => string\n }>(),\n {\n showIcon: true,\n iconSize: 16,\n }\n )\n\n const emit = defineEmits<{\n /** 面包屑项被选中(返回路径 key) */\n select: [path: string]\n }>()\n\n const route = useRoute()\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n /** 从 route.matched 自动生成面包屑 */\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\n return route.matched\n .filter((record: any) => record.meta?.title)\n .map((record: any) => ({\n key: record.path,\n label: formatLabel((record.meta.title || '') as string),\n icon: record.meta.icon as string | undefined,\n children: record.children?.length\n ? record.children.map((child: any) => ({\n key: child.path,\n label: formatLabel((child.meta?.title || '') as string),\n ...(child.children?.length && {\n children: child.children.map((grandChild: any) => ({\n key: grandChild.path,\n label: formatLabel((grandChild.meta?.title || '') as string),\n })),\n }),\n }))\n : [],\n }))\n })\n\n const breadcrumbItems = computed<BreadcrumbItem[]>(\n () => props.items ?? autoBreadcrumb.value\n )\n\n const handleSelect = (key: string) => {\n emit('select', key)\n }\n</script>\n\n<style lang=\"scss\">\n .c-breadcrumb {\n // 暴露 CSS 变量,方便主项目覆盖\n --c-breadcrumb-icon-size: 16px;\n --c-breadcrumb-gap: 4px;\n --c-breadcrumb-font-size: inherit;\n\n display: flex;\n align-items: center;\n height: 100%;\n\n &__trigger,\n &__link {\n display: inline-flex;\n align-items: center;\n gap: var(--c-breadcrumb-gap);\n font-size: var(--c-breadcrumb-font-size);\n line-height: 1;\n cursor: pointer;\n }\n\n // 确保 C_Icon 在面包屑中尺寸一致\n .c-icon {\n flex-shrink: 0;\n }\n\n // 修复分隔符 \"/\" 与文本/图标垂直居中对齐\n // 原因:Naive UI 分隔符默认 line-height (~1.4) 导致 \"/\" 基线偏低\n .n-breadcrumb-item {\n display: inline-flex !important;\n align-items: center;\n\n .n-breadcrumb-item__separator {\n display: inline-flex;\n align-items: center;\n line-height: 1;\n margin-top: -4px;\n }\n }\n }\n</style>\n"],"mappings":"sqBA6EE,IAAM,EAAQ,EAuBR,EAAO,EAKP,EAAQ,GAAS,CAEjB,EAAe,GACnB,EAAM,eAAiB,EAAM,eAAe,EAAM,CAAG,EAGjD,EAAiB,MACd,EAAM,QACV,OAAQ,GAAgB,EAAO,MAAM,MAAK,CAC1C,IAAK,IAAiB,CACrB,IAAK,EAAO,KACZ,MAAO,EAAa,EAAO,KAAK,OAAS,GAAc,CACvD,KAAM,EAAO,KAAK,KAClB,SAAU,EAAO,UAAU,OACvB,EAAO,SAAS,IAAK,IAAgB,CACnC,IAAK,EAAM,KACX,MAAO,EAAa,EAAM,MAAM,OAAS,GAAc,CACvD,GAAI,EAAM,UAAU,QAAU,CAC5B,SAAU,EAAM,SAAS,IAAK,IAAqB,CACjD,IAAK,EAAW,KAChB,MAAO,EAAa,EAAW,MAAM,OAAS,GAAA,CAC/C,EAAE,CAAA,CAEN,EAAC,CACF,EAAA,CACL,EAAC,CACL,CAEK,EAAkB,MAChB,EAAM,OAAS,EAAe,MACtC,CAEM,EAAgB,GAAgB,CACpC,EAAK,SAAU,EAAG,2CChHpB,EAwCM,MAAA,CAvCJ,MAAM,8BACL,MAAK,EAAA,+BAAyCA,EAAAA,SAAQ,4BAAqC,KAAK,IAAI,KAAK,MAAMA,EAAAA,SAAQ,EAAA,CAAA,EAAA,CAAA,QAKxH,EAgCc,EAAA,KAAA,eA9B8B,EAAA,EAAA,GAAA,CAD1C,EA8BkB,EAAA,KAAA,EA7BQ,EAAA,OAAhB,EAAM,SADhB,EA8BkB,EAAA,CA5Bf,IAAK,EAAK,CAAA,eAeC,CAZJ,EAAK,UAAU,QAAA,GAAA,CADvB,EAaY,EAAA,OAXT,QAAS,EAAK,SACd,SAAQ,kBASH,CAPN,EAOM,MAPN,EAOM,CALIC,EAAAA,UAAAA,GAAAA,CADR,EAIE,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACX,KAAMD,EAAAA,kDACP,IACF,EAAG,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,CAAA,CAAA,8BAGjB,EAWO,OAAA,OATL,MAAM,qBACL,QAAK,GAAE,EAAa,EAAK,IAAA,GAGlBC,EAAAA,UAAAA,GAAAA,CADR,EAIE,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACX,KAAMD,EAAAA,kDACP,IACF,EAAG,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Captcha-B3yrtG7t.css","names":[],"sources":["../src/components/C_Captcha/index.vue?vue&type=style&index=0&scoped=a9889f51&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-captcha-modern[data-v-a9889f51] {\n width: 100%;\n font-family: inherit;\n}\n.captcha-trigger[data-v-a9889f51] {\n width: 100%;\n max-width: 280px;\n margin: 0 auto;\n padding: 12px 16px;\n cursor: pointer;\n user-select: none;\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n.captcha-trigger[data-v-a9889f51]:hover:not(.disabled) {\n transform: translateY(-1px);\n}\n.captcha-trigger.disabled[data-v-a9889f51] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.captcha-content[data-v-a9889f51] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n flex: 1;\n}\n.captcha-icon[data-v-a9889f51] {\n font-size: 18px;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n.captcha-text[data-v-a9889f51] {\n color: rgba(255, 255, 255, 0.8);\n font-size: 14px;\n font-weight: 500;\n letter-spacing: 0.3px;\n text-align: center;\n white-space: nowrap;\n}\n.refresh-button[data-v-a9889f51] {\n position: absolute;\n right: 12px;\n top: 50%;\n transform: translateY(-50%);\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.3s ease;\n color: rgba(255, 255, 255, 0.6);\n font-size: 14px;\n flex-shrink: 0;\n}\n.refresh-button[data-v-a9889f51]:hover {\n color: rgba(255, 255, 255, 0.9);\n transform: translateY(-50%) rotate(180deg);\n}\n.success-icon[data-v-a9889f51],\n.success-text[data-v-a9889f51] {\n color: #43c23a;\n}\n.error-icon[data-v-a9889f51],\n.error-text[data-v-a9889f51] {\n color: #f56c6c;\n}\n@keyframes shake-a9889f51 {\n0%, 100% {\n transform: translateX(0);\n}\n25% {\n transform: translateX(-4px);\n}\n75% {\n transform: translateX(4px);\n}\n}\n@media (max-width: 768px) {\n.captcha-trigger[data-v-a9889f51] {\n max-width: 240px;\n padding: 10px 14px;\n}\n.captcha-icon[data-v-a9889f51] {\n font-size: 16px;\n width: 18px;\n height: 18px;\n}\n.captcha-text[data-v-a9889f51] {\n font-size: 13px;\n}\n.refresh-button[data-v-a9889f51] {\n width: 24px;\n height: 24px;\n font-size: 12px;\n right: 10px;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Captcha-D4hDdWDu.css","names":[],"sources":["../src/components/C_Captcha/index.vue?vue&type=style&index=0&scoped=a9889f51&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-captcha-modern[data-v-a9889f51] {\n width: 100%;\n font-family: inherit;\n}\n.captcha-trigger[data-v-a9889f51] {\n width: 100%;\n max-width: 280px;\n margin: 0 auto;\n padding: 12px 16px;\n cursor: pointer;\n user-select: none;\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n.captcha-trigger[data-v-a9889f51]:hover:not(.disabled) {\n transform: translateY(-1px);\n}\n.captcha-trigger.disabled[data-v-a9889f51] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.captcha-content[data-v-a9889f51] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n flex: 1;\n}\n.captcha-icon[data-v-a9889f51] {\n font-size: 18px;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n.captcha-text[data-v-a9889f51] {\n color: rgba(255, 255, 255, 0.8);\n font-size: 14px;\n font-weight: 500;\n letter-spacing: 0.3px;\n text-align: center;\n white-space: nowrap;\n}\n.refresh-button[data-v-a9889f51] {\n position: absolute;\n right: 12px;\n top: 50%;\n transform: translateY(-50%);\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.3s ease;\n color: rgba(255, 255, 255, 0.6);\n font-size: 14px;\n flex-shrink: 0;\n}\n.refresh-button[data-v-a9889f51]:hover {\n color: rgba(255, 255, 255, 0.9);\n transform: translateY(-50%) rotate(180deg);\n}\n.success-icon[data-v-a9889f51],\n.success-text[data-v-a9889f51] {\n color: #43c23a;\n}\n.error-icon[data-v-a9889f51],\n.error-text[data-v-a9889f51] {\n color: #f56c6c;\n}\n@keyframes shake-a9889f51 {\n0%, 100% {\n transform: translateX(0);\n}\n25% {\n transform: translateX(-4px);\n}\n75% {\n transform: translateX(4px);\n}\n}\n@media (max-width: 768px) {\n.captcha-trigger[data-v-a9889f51] {\n max-width: 240px;\n padding: 10px 14px;\n}\n.captcha-icon[data-v-a9889f51] {\n font-size: 16px;\n width: 18px;\n height: 18px;\n}\n.captcha-text[data-v-a9889f51] {\n font-size: 13px;\n}\n.refresh-button[data-v-a9889f51] {\n width: 24px;\n height: 24px;\n font-size: 12px;\n right: 10px;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Captcha.js","names":["disabled","triggerText"],"sources":["../src/components/C_Captcha/index.vue","../src/components/C_Captcha/index.vue"],"sourcesContent":["<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-07-23\n * @Description: 基于 vue3-puzzle-vcode 封装的验证器组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-captcha-modern\">\n <div\n class=\"captcha-trigger\"\n @click=\"showCaptcha\"\n :class=\"{\n verified: isVerified,\n error: hasError,\n disabled: disabled,\n }\"\n >\n <div class=\"captcha-content\">\n <div class=\"captcha-icon\">\n <span v-if=\"!isVerified && !hasError\">🧩</span>\n <span v-if=\"isVerified\" class=\"success-icon\">✓</span>\n <span v-if=\"hasError\" class=\"error-icon\">⚠️</span>\n </div>\n <div class=\"captcha-text\">\n <span v-if=\"!isVerified && !hasError\">{{ triggerText }}</span>\n <span v-if=\"isVerified\" class=\"success-text\">验证成功</span>\n <span v-if=\"hasError\" class=\"error-text\">验证失败,请重试</span>\n </div>\n </div>\n <div\n v-if=\"isVerified || hasError\"\n class=\"refresh-button\"\n @click.stop=\"resetCaptcha\"\n title=\"重新验证\"\n >\n ⟲\n </div>\n </div>\n <PuzzleVcode\n :show=\"showModal\"\n :imgs=\"captchaImages\"\n @success=\"handleSuccess\"\n @close=\"handleClose\"\n @fail=\"handleFail\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, onBeforeUnmount } from \"vue\";\nimport PuzzleVcode from \"vue3-puzzle-vcode\";\n\ndefineOptions({ name: \"C_Captcha\" });\n\ninterface CaptchaProps {\n triggerText?: string;\n images?: string[];\n disabled?: boolean;\n theme?: \"light\" | \"dark\";\n}\n\ninterface CaptchaEmits {\n (e: \"success\", data: { token: string; timestamp: number }): void;\n (e: \"fail\", error: string): void;\n (e: \"change\", valid: boolean): void;\n (e: \"reset\"): void;\n}\n\nconst props = withDefaults(defineProps<CaptchaProps>(), {\n triggerText: \"点击进行人机验证\",\n images: () => [],\n disabled: false,\n theme: \"dark\",\n});\n\nconst emit = defineEmits<CaptchaEmits>();\n\nconst showModal = ref(false);\nconst isVerified = ref(false);\nconst hasError = ref(false);\nconst verificationToken = ref(\"\");\nlet errorTimer: ReturnType<typeof setTimeout> | null = null;\n\nconst captchaImages = computed(() =>\n props.images.length > 0 ? props.images : undefined,\n);\n\nconst showCaptcha = () => {\n if (props.disabled || isVerified.value) return;\n hasError.value = false;\n showModal.value = true;\n};\n\nconst handleSuccess = () => {\n isVerified.value = true;\n hasError.value = false;\n showModal.value = false;\n const timestamp = Date.now();\n const token = `puzzle_${timestamp}_${Math.random().toString(36).substring(2, 9)}`;\n verificationToken.value = token;\n emit(\"success\", { token, timestamp });\n emit(\"change\", true);\n};\n\nconst handleFail = () => {\n isVerified.value = false;\n hasError.value = true;\n showModal.value = false;\n if (errorTimer) clearTimeout(errorTimer);\n errorTimer = setTimeout(() => {\n hasError.value = false;\n errorTimer = null;\n }, 3000);\n emit(\"fail\", \"拼图验证失败\");\n emit(\"change\", false);\n};\n\nconst handleClose = () => {\n showModal.value = false;\n};\n\nconst resetCaptcha = () => {\n isVerified.value = false;\n hasError.value = false;\n showModal.value = false;\n verificationToken.value = \"\";\n if (errorTimer) {\n clearTimeout(errorTimer);\n errorTimer = null;\n }\n emit(\"reset\");\n emit(\"change\", false);\n};\n\ndefineExpose({\n validate: () => isVerified.value,\n getToken: () => verificationToken.value,\n getVerificationData: () => {\n if (!isVerified.value) return null;\n return {\n token: verificationToken.value,\n timestamp: Date.now(),\n type: \"puzzle-captcha\",\n };\n },\n reset: resetCaptcha,\n show: showCaptcha,\n});\n\nonBeforeUnmount(() => {\n if (errorTimer) clearTimeout(errorTimer);\n});\n</script>\n\n<style scoped lang=\"scss\">\n@use \"./index.scss\";\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-07-23\n * @Description: 基于 vue3-puzzle-vcode 封装的验证器组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-captcha-modern\">\n <div\n class=\"captcha-trigger\"\n @click=\"showCaptcha\"\n :class=\"{\n verified: isVerified,\n error: hasError,\n disabled: disabled,\n }\"\n >\n <div class=\"captcha-content\">\n <div class=\"captcha-icon\">\n <span v-if=\"!isVerified && !hasError\">🧩</span>\n <span v-if=\"isVerified\" class=\"success-icon\">✓</span>\n <span v-if=\"hasError\" class=\"error-icon\">⚠️</span>\n </div>\n <div class=\"captcha-text\">\n <span v-if=\"!isVerified && !hasError\">{{ triggerText }}</span>\n <span v-if=\"isVerified\" class=\"success-text\">验证成功</span>\n <span v-if=\"hasError\" class=\"error-text\">验证失败,请重试</span>\n </div>\n </div>\n <div\n v-if=\"isVerified || hasError\"\n class=\"refresh-button\"\n @click.stop=\"resetCaptcha\"\n title=\"重新验证\"\n >\n ⟲\n </div>\n </div>\n <PuzzleVcode\n :show=\"showModal\"\n :imgs=\"captchaImages\"\n @success=\"handleSuccess\"\n @close=\"handleClose\"\n @fail=\"handleFail\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, onBeforeUnmount } from \"vue\";\nimport PuzzleVcode from \"vue3-puzzle-vcode\";\n\ndefineOptions({ name: \"C_Captcha\" });\n\ninterface CaptchaProps {\n triggerText?: string;\n images?: string[];\n disabled?: boolean;\n theme?: \"light\" | \"dark\";\n}\n\ninterface CaptchaEmits {\n (e: \"success\", data: { token: string; timestamp: number }): void;\n (e: \"fail\", error: string): void;\n (e: \"change\", valid: boolean): void;\n (e: \"reset\"): void;\n}\n\nconst props = withDefaults(defineProps<CaptchaProps>(), {\n triggerText: \"点击进行人机验证\",\n images: () => [],\n disabled: false,\n theme: \"dark\",\n});\n\nconst emit = defineEmits<CaptchaEmits>();\n\nconst showModal = ref(false);\nconst isVerified = ref(false);\nconst hasError = ref(false);\nconst verificationToken = ref(\"\");\nlet errorTimer: ReturnType<typeof setTimeout> | null = null;\n\nconst captchaImages = computed(() =>\n props.images.length > 0 ? props.images : undefined,\n);\n\nconst showCaptcha = () => {\n if (props.disabled || isVerified.value) return;\n hasError.value = false;\n showModal.value = true;\n};\n\nconst handleSuccess = () => {\n isVerified.value = true;\n hasError.value = false;\n showModal.value = false;\n const timestamp = Date.now();\n const token = `puzzle_${timestamp}_${Math.random().toString(36).substring(2, 9)}`;\n verificationToken.value = token;\n emit(\"success\", { token, timestamp });\n emit(\"change\", true);\n};\n\nconst handleFail = () => {\n isVerified.value = false;\n hasError.value = true;\n showModal.value = false;\n if (errorTimer) clearTimeout(errorTimer);\n errorTimer = setTimeout(() => {\n hasError.value = false;\n errorTimer = null;\n }, 3000);\n emit(\"fail\", \"拼图验证失败\");\n emit(\"change\", false);\n};\n\nconst handleClose = () => {\n showModal.value = false;\n};\n\nconst resetCaptcha = () => {\n isVerified.value = false;\n hasError.value = false;\n showModal.value = false;\n verificationToken.value = \"\";\n if (errorTimer) {\n clearTimeout(errorTimer);\n errorTimer = null;\n }\n emit(\"reset\");\n emit(\"change\", false);\n};\n\ndefineExpose({\n validate: () => isVerified.value,\n getToken: () => verificationToken.value,\n getVerificationData: () => {\n if (!isVerified.value) return null;\n return {\n token: verificationToken.value,\n timestamp: Date.now(),\n type: \"puzzle-captcha\",\n };\n },\n reset: resetCaptcha,\n show: showCaptcha,\n});\n\nonBeforeUnmount(() => {\n if (errorTimer) clearTimeout(errorTimer);\n});\n</script>\n\n<style scoped lang=\"scss\">\n@use \"./index.scss\";\n</style>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Captcha2.js","names":["disabled","triggerText"],"sources":["../src/components/C_Captcha/index.vue","../src/components/C_Captcha/index.vue"],"sourcesContent":["<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-07-23\n * @Description: 基于 vue3-puzzle-vcode 封装的验证器组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-captcha-modern\">\n <div\n class=\"captcha-trigger\"\n @click=\"showCaptcha\"\n :class=\"{\n verified: isVerified,\n error: hasError,\n disabled: disabled,\n }\"\n >\n <div class=\"captcha-content\">\n <div class=\"captcha-icon\">\n <span v-if=\"!isVerified && !hasError\">🧩</span>\n <span v-if=\"isVerified\" class=\"success-icon\">✓</span>\n <span v-if=\"hasError\" class=\"error-icon\">⚠️</span>\n </div>\n <div class=\"captcha-text\">\n <span v-if=\"!isVerified && !hasError\">{{ triggerText }}</span>\n <span v-if=\"isVerified\" class=\"success-text\">验证成功</span>\n <span v-if=\"hasError\" class=\"error-text\">验证失败,请重试</span>\n </div>\n </div>\n <div\n v-if=\"isVerified || hasError\"\n class=\"refresh-button\"\n @click.stop=\"resetCaptcha\"\n title=\"重新验证\"\n >\n ⟲\n </div>\n </div>\n <PuzzleVcode\n :show=\"showModal\"\n :imgs=\"captchaImages\"\n @success=\"handleSuccess\"\n @close=\"handleClose\"\n @fail=\"handleFail\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, onBeforeUnmount } from \"vue\";\nimport PuzzleVcode from \"vue3-puzzle-vcode\";\n\ndefineOptions({ name: \"C_Captcha\" });\n\ninterface CaptchaProps {\n triggerText?: string;\n images?: string[];\n disabled?: boolean;\n theme?: \"light\" | \"dark\";\n}\n\ninterface CaptchaEmits {\n (e: \"success\", data: { token: string; timestamp: number }): void;\n (e: \"fail\", error: string): void;\n (e: \"change\", valid: boolean): void;\n (e: \"reset\"): void;\n}\n\nconst props = withDefaults(defineProps<CaptchaProps>(), {\n triggerText: \"点击进行人机验证\",\n images: () => [],\n disabled: false,\n theme: \"dark\",\n});\n\nconst emit = defineEmits<CaptchaEmits>();\n\nconst showModal = ref(false);\nconst isVerified = ref(false);\nconst hasError = ref(false);\nconst verificationToken = ref(\"\");\nlet errorTimer: ReturnType<typeof setTimeout> | null = null;\n\nconst captchaImages = computed(() =>\n props.images.length > 0 ? props.images : undefined,\n);\n\nconst showCaptcha = () => {\n if (props.disabled || isVerified.value) return;\n hasError.value = false;\n showModal.value = true;\n};\n\nconst handleSuccess = () => {\n isVerified.value = true;\n hasError.value = false;\n showModal.value = false;\n const timestamp = Date.now();\n const token = `puzzle_${timestamp}_${Math.random().toString(36).substring(2, 9)}`;\n verificationToken.value = token;\n emit(\"success\", { token, timestamp });\n emit(\"change\", true);\n};\n\nconst handleFail = () => {\n isVerified.value = false;\n hasError.value = true;\n showModal.value = false;\n if (errorTimer) clearTimeout(errorTimer);\n errorTimer = setTimeout(() => {\n hasError.value = false;\n errorTimer = null;\n }, 3000);\n emit(\"fail\", \"拼图验证失败\");\n emit(\"change\", false);\n};\n\nconst handleClose = () => {\n showModal.value = false;\n};\n\nconst resetCaptcha = () => {\n isVerified.value = false;\n hasError.value = false;\n showModal.value = false;\n verificationToken.value = \"\";\n if (errorTimer) {\n clearTimeout(errorTimer);\n errorTimer = null;\n }\n emit(\"reset\");\n emit(\"change\", false);\n};\n\ndefineExpose({\n validate: () => isVerified.value,\n getToken: () => verificationToken.value,\n getVerificationData: () => {\n if (!isVerified.value) return null;\n return {\n token: verificationToken.value,\n timestamp: Date.now(),\n type: \"puzzle-captcha\",\n };\n },\n reset: resetCaptcha,\n show: showCaptcha,\n});\n\nonBeforeUnmount(() => {\n if (errorTimer) clearTimeout(errorTimer);\n});\n</script>\n\n<style scoped lang=\"scss\">\n@use \"./index.scss\";\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-07-23\n * @Description: 基于 vue3-puzzle-vcode 封装的验证器组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-captcha-modern\">\n <div\n class=\"captcha-trigger\"\n @click=\"showCaptcha\"\n :class=\"{\n verified: isVerified,\n error: hasError,\n disabled: disabled,\n }\"\n >\n <div class=\"captcha-content\">\n <div class=\"captcha-icon\">\n <span v-if=\"!isVerified && !hasError\">🧩</span>\n <span v-if=\"isVerified\" class=\"success-icon\">✓</span>\n <span v-if=\"hasError\" class=\"error-icon\">⚠️</span>\n </div>\n <div class=\"captcha-text\">\n <span v-if=\"!isVerified && !hasError\">{{ triggerText }}</span>\n <span v-if=\"isVerified\" class=\"success-text\">验证成功</span>\n <span v-if=\"hasError\" class=\"error-text\">验证失败,请重试</span>\n </div>\n </div>\n <div\n v-if=\"isVerified || hasError\"\n class=\"refresh-button\"\n @click.stop=\"resetCaptcha\"\n title=\"重新验证\"\n >\n ⟲\n </div>\n </div>\n <PuzzleVcode\n :show=\"showModal\"\n :imgs=\"captchaImages\"\n @success=\"handleSuccess\"\n @close=\"handleClose\"\n @fail=\"handleFail\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, onBeforeUnmount } from \"vue\";\nimport PuzzleVcode from \"vue3-puzzle-vcode\";\n\ndefineOptions({ name: \"C_Captcha\" });\n\ninterface CaptchaProps {\n triggerText?: string;\n images?: string[];\n disabled?: boolean;\n theme?: \"light\" | \"dark\";\n}\n\ninterface CaptchaEmits {\n (e: \"success\", data: { token: string; timestamp: number }): void;\n (e: \"fail\", error: string): void;\n (e: \"change\", valid: boolean): void;\n (e: \"reset\"): void;\n}\n\nconst props = withDefaults(defineProps<CaptchaProps>(), {\n triggerText: \"点击进行人机验证\",\n images: () => [],\n disabled: false,\n theme: \"dark\",\n});\n\nconst emit = defineEmits<CaptchaEmits>();\n\nconst showModal = ref(false);\nconst isVerified = ref(false);\nconst hasError = ref(false);\nconst verificationToken = ref(\"\");\nlet errorTimer: ReturnType<typeof setTimeout> | null = null;\n\nconst captchaImages = computed(() =>\n props.images.length > 0 ? props.images : undefined,\n);\n\nconst showCaptcha = () => {\n if (props.disabled || isVerified.value) return;\n hasError.value = false;\n showModal.value = true;\n};\n\nconst handleSuccess = () => {\n isVerified.value = true;\n hasError.value = false;\n showModal.value = false;\n const timestamp = Date.now();\n const token = `puzzle_${timestamp}_${Math.random().toString(36).substring(2, 9)}`;\n verificationToken.value = token;\n emit(\"success\", { token, timestamp });\n emit(\"change\", true);\n};\n\nconst handleFail = () => {\n isVerified.value = false;\n hasError.value = true;\n showModal.value = false;\n if (errorTimer) clearTimeout(errorTimer);\n errorTimer = setTimeout(() => {\n hasError.value = false;\n errorTimer = null;\n }, 3000);\n emit(\"fail\", \"拼图验证失败\");\n emit(\"change\", false);\n};\n\nconst handleClose = () => {\n showModal.value = false;\n};\n\nconst resetCaptcha = () => {\n isVerified.value = false;\n hasError.value = false;\n showModal.value = false;\n verificationToken.value = \"\";\n if (errorTimer) {\n clearTimeout(errorTimer);\n errorTimer = null;\n }\n emit(\"reset\");\n emit(\"change\", false);\n};\n\ndefineExpose({\n validate: () => isVerified.value,\n getToken: () => verificationToken.value,\n getVerificationData: () => {\n if (!isVerified.value) return null;\n return {\n token: verificationToken.value,\n timestamp: Date.now(),\n type: \"puzzle-captcha\",\n };\n },\n reset: resetCaptcha,\n show: showCaptcha,\n});\n\nonBeforeUnmount(() => {\n if (errorTimer) clearTimeout(errorTimer);\n});\n</script>\n\n<style scoped lang=\"scss\">\n@use \"./index.scss\";\n</style>\n"],"mappings":"izBAqEA,IAAM,EAAQ,EAOR,EAAO,EAEP,EAAY,EAAI,GAAM,CACtB,EAAa,EAAI,GAAM,CACvB,EAAW,EAAI,GAAM,CACrB,EAAoB,EAAI,GAAG,CAC7B,EAAmD,KAEjD,EAAgB,MACpB,EAAM,OAAO,OAAS,EAAI,EAAM,OAAS,IAAA,GAC1C,CAEK,MAAoB,CACpB,EAAM,UAAY,EAAW,QACjC,EAAS,MAAQ,GACjB,EAAU,MAAQ,KAGd,MAAsB,CAC1B,EAAW,MAAQ,GACnB,EAAS,MAAQ,GACjB,EAAU,MAAQ,GAClB,IAAM,EAAY,KAAK,KAAK,CACtB,EAAQ,UAAU,EAAU,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,UAAU,EAAG,EAAE,GAC/E,EAAkB,MAAQ,EAC1B,EAAK,UAAW,CAAE,QAAO,YAAW,CAAC,CACrC,EAAK,SAAU,GAAK,EAGhB,MAAmB,CACvB,EAAW,MAAQ,GACnB,EAAS,MAAQ,GACjB,EAAU,MAAQ,GACd,GAAY,aAAa,EAAW,CACxC,EAAa,eAAiB,CAC5B,EAAS,MAAQ,GACjB,EAAa,MACZ,IAAK,CACR,EAAK,OAAQ,SAAS,CACtB,EAAK,SAAU,GAAM,EAGjB,MAAoB,CACxB,EAAU,MAAQ,IAGd,MAAqB,CACzB,EAAW,MAAQ,GACnB,EAAS,MAAQ,GACjB,EAAU,MAAQ,GAClB,EAAkB,MAAQ,GAC1B,AAEE,KADA,aAAa,EAAW,CACX,MAEf,EAAK,QAAQ,CACb,EAAK,SAAU,GAAM,SAGvB,EAAa,CACX,aAAgB,EAAW,MAC3B,aAAgB,EAAkB,MAClC,wBACO,EAAW,MACT,CACL,MAAO,EAAkB,MACzB,UAAW,KAAK,KAAK,CACrB,KAAM,iBACP,CAL6B,KAOhC,MAAO,EACP,KAAM,EACP,CAAC,CAEF,MAAsB,CAChB,GAAY,aAAa,EAAW,EACxC,aChJA,EAsCM,MAtCN,EAsCM,CArCJ,EA6BM,MAAA,CA5BJ,MAAK,EAAA,CAAC,kBAAiB,UAEK,EAAA,YAA2B,EAAA,eAA4BA,EAAAA,YADlF,QAAO,IAOR,EAWM,MAXN,EAWM,CAVJ,EAIM,MAJN,EAIM,EAHS,EAAA,OAAU,CAAK,EAAA,OAAA,GAAA,CAA5B,EAA+C,OAAA,EAAT,KAAE,EAAA,EAAA,OAAA,GAAA,CAC5B,EAAA,OAAA,GAAA,CAAZ,EAAqD,OAArD,EAA6C,IAAC,EAAA,EAAA,OAAA,GAAA,CAClC,EAAA,OAAA,GAAA,CAAZ,EAAkD,OAAlD,EAAyC,KAAE,EAAA,EAAA,OAAA,GAAA,GAE7C,EAIM,MAJN,EAIM,EAHS,EAAA,OAAU,CAAK,EAAA,OAAA,GAAA,CAA5B,EAA8D,OAAA,EAAA,EAArBC,EAAAA,YAAW,CAAA,EAAA,EAAA,EAAA,OAAA,GAAA,CACxC,EAAA,OAAA,GAAA,CAAZ,EAAwD,OAAxD,EAA6C,OAAI,EAAA,EAAA,OAAA,GAAA,CACrC,EAAA,OAAA,GAAA,CAAZ,EAAwD,OAAxD,EAAyC,WAAQ,EAAA,EAAA,OAAA,GAAA,KAI7C,EAAA,OAAc,EAAA,OAAA,GAAA,CADtB,EAOM,MAAA,OALJ,MAAM,iBACL,QAAK,EAAO,EAAY,CAAA,OAAA,CAAA,CACzB,MAAM,QACP,MAED,EAAA,EAAA,OAAA,GAAA,CAAA,CAAA,EAAA,CAEF,EAME,EAAA,EAAA,CAAA,CALC,KAAM,EAAA,MACN,KAAM,EAAA,MACN,UAAS,EACT,QAAO,EACP,OAAM"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Cascade-CmCrLVft.css","names":[],"sources":["../src/components/C_Cascade/index.vue?vue&type=style&index=0&scoped=cf9c290b&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.n-cascade-selector[data-v-cf9c290b] {\n display: flex;\n gap: 12px;\n}\n.n-cascade-selector .n-select-item[data-v-cf9c290b] {\n min-width: 140px;\n flex: 1;\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Cascade-U6A5AJJb.css","names":[],"sources":["../src/components/C_Cascade/index.vue?vue&type=style&index=0&scoped=cf9c290b&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.n-cascade-selector[data-v-cf9c290b] {\n display: flex;\n gap: 12px;\n}\n.n-cascade-selector .n-select-item[data-v-cf9c290b] {\n min-width: 140px;\n flex: 1;\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Cascade.js","names":["placeholders"],"sources":["../src/components/C_Cascade/index.vue","../src/components/C_Cascade/index.vue"],"sourcesContent":["<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-28\r\n * @Description: 级联选择组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"n-cascade-selector\">\r\n <NSelect\r\n v-for=\"(level, index) in levels\"\r\n :key=\"index\"\r\n v-model:value=\"selectedValues[index]\"\r\n :options=\"levelOptions[index]\"\r\n clearable\r\n :placeholder=\"placeholders[index]\"\r\n :disabled=\"index > 0 && !selectedValues[index - 1]\"\r\n @update:value=\"handleChange(index)\"\r\n class=\"n-select-item\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\n import { ref, computed, watch } from 'vue'\r\n import { NSelect } from 'naive-ui'\r\n import type { CascadeItem, CascadeValue } from './types'\r\n\r\n defineOptions({ name: 'C_Cascade' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n data: CascadeItem[]\r\n placeholders?: string[]\r\n modelValue?: CascadeValue\r\n }>(),\r\n {\r\n placeholders: () => ['请选择', '请选择', '请选择'],\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n 'update:modelValue': [value: CascadeValue]\r\n change: [value: CascadeValue]\r\n }>()\r\n\r\n const levels = [0, 1, 2]\r\n const selectedValues = ref<(string | number | null)[]>([null, null, null])\r\n\r\n const getLevelData = (level: number): CascadeItem[] => {\r\n if (level === 0) return props.data\r\n if (!selectedValues.value[level - 1]) return []\r\n const parentData = getLevelData(level - 1)\r\n return (\r\n parentData.find(x => x.value === selectedValues.value[level - 1])\r\n ?.children || []\r\n )\r\n }\r\n\r\n const levelOptions = computed(() =>\r\n levels.map(level =>\r\n getLevelData(level).map(item => ({\r\n label: item.label,\r\n value: item.value,\r\n }))\r\n )\r\n )\r\n\r\n const handleChange = (index: number) => {\r\n selectedValues.value.splice(\r\n index + 1,\r\n levels.length - index - 1,\r\n ...Array(levels.length - index - 1).fill(null)\r\n )\r\n emitValue()\r\n }\r\n\r\n const getSelectedItem = (index: number) => {\r\n const value = selectedValues.value[index]\r\n if (!value) return null\r\n const data = getLevelData(index)\r\n const item = data.find(i => i.value === value)\r\n return item ? { label: item.label, value: item.value } : null\r\n }\r\n\r\n const emitValue = () => {\r\n const result: CascadeValue = {\r\n primary: getSelectedItem(0),\r\n secondary: getSelectedItem(1),\r\n tertiary: getSelectedItem(2),\r\n }\r\n emit('update:modelValue', result)\r\n emit('change', result)\r\n }\r\n\r\n watch(\r\n () => props.modelValue,\r\n val => {\r\n selectedValues.value = [\r\n val?.primary?.value ?? null,\r\n val?.secondary?.value ?? null,\r\n val?.tertiary?.value ?? null,\r\n ]\r\n },\r\n { immediate: true, deep: true }\r\n )\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n .n-cascade-selector {\r\n display: flex;\r\n gap: 12px;\r\n .n-select-item {\r\n min-width: 140px;\r\n flex: 1;\r\n }\r\n }\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-28\r\n * @Description: 级联选择组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"n-cascade-selector\">\r\n <NSelect\r\n v-for=\"(level, index) in levels\"\r\n :key=\"index\"\r\n v-model:value=\"selectedValues[index]\"\r\n :options=\"levelOptions[index]\"\r\n clearable\r\n :placeholder=\"placeholders[index]\"\r\n :disabled=\"index > 0 && !selectedValues[index - 1]\"\r\n @update:value=\"handleChange(index)\"\r\n class=\"n-select-item\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\n import { ref, computed, watch } from 'vue'\r\n import { NSelect } from 'naive-ui'\r\n import type { CascadeItem, CascadeValue } from './types'\r\n\r\n defineOptions({ name: 'C_Cascade' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n data: CascadeItem[]\r\n placeholders?: string[]\r\n modelValue?: CascadeValue\r\n }>(),\r\n {\r\n placeholders: () => ['请选择', '请选择', '请选择'],\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n 'update:modelValue': [value: CascadeValue]\r\n change: [value: CascadeValue]\r\n }>()\r\n\r\n const levels = [0, 1, 2]\r\n const selectedValues = ref<(string | number | null)[]>([null, null, null])\r\n\r\n const getLevelData = (level: number): CascadeItem[] => {\r\n if (level === 0) return props.data\r\n if (!selectedValues.value[level - 1]) return []\r\n const parentData = getLevelData(level - 1)\r\n return (\r\n parentData.find(x => x.value === selectedValues.value[level - 1])\r\n ?.children || []\r\n )\r\n }\r\n\r\n const levelOptions = computed(() =>\r\n levels.map(level =>\r\n getLevelData(level).map(item => ({\r\n label: item.label,\r\n value: item.value,\r\n }))\r\n )\r\n )\r\n\r\n const handleChange = (index: number) => {\r\n selectedValues.value.splice(\r\n index + 1,\r\n levels.length - index - 1,\r\n ...Array(levels.length - index - 1).fill(null)\r\n )\r\n emitValue()\r\n }\r\n\r\n const getSelectedItem = (index: number) => {\r\n const value = selectedValues.value[index]\r\n if (!value) return null\r\n const data = getLevelData(index)\r\n const item = data.find(i => i.value === value)\r\n return item ? { label: item.label, value: item.value } : null\r\n }\r\n\r\n const emitValue = () => {\r\n const result: CascadeValue = {\r\n primary: getSelectedItem(0),\r\n secondary: getSelectedItem(1),\r\n tertiary: getSelectedItem(2),\r\n }\r\n emit('update:modelValue', result)\r\n emit('change', result)\r\n }\r\n\r\n watch(\r\n () => props.modelValue,\r\n val => {\r\n selectedValues.value = [\r\n val?.primary?.value ?? null,\r\n val?.secondary?.value ?? null,\r\n val?.tertiary?.value ?? null,\r\n ]\r\n },\r\n { immediate: true, deep: true }\r\n )\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n .n-cascade-selector {\r\n display: flex;\r\n gap: 12px;\r\n .n-select-item {\r\n min-width: 140px;\r\n flex: 1;\r\n }\r\n }\r\n</style>\r\n"],"mappings":"yUA8BE,IAAM,EAAQ,EAWR,EAAO,EAKP,EAAS,CAAC,EAAG,EAAG,EAAE,CAClB,GAAA,EAAA,EAAA,KAAiD,CAAC,KAAM,KAAM,KAAK,CAAC,CAEpE,EAAgB,GAChB,IAAU,EAAU,EAAM,KACzB,EAAe,MAAM,EAAQ,IACf,EAAa,EAAQ,EAAE,CAE7B,KAAK,GAAK,EAAE,QAAU,EAAe,MAAM,EAAQ,GAAG,EAC7D,UAJuC,EAAE,CAQ3C,GAAA,EAAA,EAAA,cACJ,EAAO,IAAI,GACT,EAAa,EAAM,CAAC,IAAI,IAAS,CAC/B,MAAO,EAAK,MACZ,MAAO,EAAK,MACb,EAAE,CACJ,CACF,CAEK,EAAgB,GAAkB,CACtC,EAAe,MAAM,OACnB,EAAQ,EACR,EAAO,OAAS,EAAQ,EACxB,GAAG,MAAM,EAAO,OAAS,EAAQ,EAAE,CAAC,KAAK,KAAK,CAC/C,CACD,GAAW,EAGP,EAAmB,GAAkB,CACzC,IAAM,EAAQ,EAAe,MAAM,GACnC,GAAI,CAAC,EAAO,OAAO,KAEnB,IAAM,EADO,EAAa,EAAM,CACd,KAAK,GAAK,EAAE,QAAU,EAAM,CAC9C,OAAO,EAAO,CAAE,MAAO,EAAK,MAAO,MAAO,EAAK,MAAO,CAAG,MAGrD,MAAkB,CACtB,IAAM,EAAuB,CAC3B,QAAS,EAAgB,EAAE,CAC3B,UAAW,EAAgB,EAAE,CAC7B,SAAU,EAAgB,EAAA,CAC3B,CACD,EAAK,oBAAqB,EAAO,CACjC,EAAK,SAAU,EAAO,SAGxB,EAAA,EAAA,WACQ,EAAM,WACZ,GAAO,CACL,EAAe,MAAQ,CACrB,GAAK,SAAS,OAAS,KACvB,GAAK,WAAW,OAAS,KACzB,GAAK,UAAU,OAAS,KACzB,EAEH,CAAE,UAAW,GAAM,KAAM,GAAM,CAChC,oDCrFK,MAZN,EAYM,GAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,oBADF,EAAA,SAAA,MAAA,EAAA,EAAA,YATyB,GAAjB,EAAO,kCASf,EAAA,QAAA,CAAA,CARC,IAAK,EACE,MAAO,EAAA,MAAe,wBAAf,EAAA,MAAe,GAAK,EAAA,GAKpB,EAAa,EAAK,CAAA,CAJhC,QAAS,EAAA,MAAa,GACvB,UAAA,GACC,YAAaA,EAAAA,aAAa,GAC1B,SAAU,EAAK,GAAA,CAAS,EAAA,MAAe,EAAK,GAE7C,MAAM"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Cascade2.js","names":["placeholders"],"sources":["../src/components/C_Cascade/index.vue","../src/components/C_Cascade/index.vue"],"sourcesContent":["<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-28\r\n * @Description: 级联选择组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"n-cascade-selector\">\r\n <NSelect\r\n v-for=\"(level, index) in levels\"\r\n :key=\"index\"\r\n v-model:value=\"selectedValues[index]\"\r\n :options=\"levelOptions[index]\"\r\n clearable\r\n :placeholder=\"placeholders[index]\"\r\n :disabled=\"index > 0 && !selectedValues[index - 1]\"\r\n @update:value=\"handleChange(index)\"\r\n class=\"n-select-item\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\n import { ref, computed, watch } from 'vue'\r\n import { NSelect } from 'naive-ui'\r\n import type { CascadeItem, CascadeValue } from './types'\r\n\r\n defineOptions({ name: 'C_Cascade' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n data: CascadeItem[]\r\n placeholders?: string[]\r\n modelValue?: CascadeValue\r\n }>(),\r\n {\r\n placeholders: () => ['请选择', '请选择', '请选择'],\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n 'update:modelValue': [value: CascadeValue]\r\n change: [value: CascadeValue]\r\n }>()\r\n\r\n const levels = [0, 1, 2]\r\n const selectedValues = ref<(string | number | null)[]>([null, null, null])\r\n\r\n const getLevelData = (level: number): CascadeItem[] => {\r\n if (level === 0) return props.data\r\n if (!selectedValues.value[level - 1]) return []\r\n const parentData = getLevelData(level - 1)\r\n return (\r\n parentData.find(x => x.value === selectedValues.value[level - 1])\r\n ?.children || []\r\n )\r\n }\r\n\r\n const levelOptions = computed(() =>\r\n levels.map(level =>\r\n getLevelData(level).map(item => ({\r\n label: item.label,\r\n value: item.value,\r\n }))\r\n )\r\n )\r\n\r\n const handleChange = (index: number) => {\r\n selectedValues.value.splice(\r\n index + 1,\r\n levels.length - index - 1,\r\n ...Array(levels.length - index - 1).fill(null)\r\n )\r\n emitValue()\r\n }\r\n\r\n const getSelectedItem = (index: number) => {\r\n const value = selectedValues.value[index]\r\n if (!value) return null\r\n const data = getLevelData(index)\r\n const item = data.find(i => i.value === value)\r\n return item ? { label: item.label, value: item.value } : null\r\n }\r\n\r\n const emitValue = () => {\r\n const result: CascadeValue = {\r\n primary: getSelectedItem(0),\r\n secondary: getSelectedItem(1),\r\n tertiary: getSelectedItem(2),\r\n }\r\n emit('update:modelValue', result)\r\n emit('change', result)\r\n }\r\n\r\n watch(\r\n () => props.modelValue,\r\n val => {\r\n selectedValues.value = [\r\n val?.primary?.value ?? null,\r\n val?.secondary?.value ?? null,\r\n val?.tertiary?.value ?? null,\r\n ]\r\n },\r\n { immediate: true, deep: true }\r\n )\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n .n-cascade-selector {\r\n display: flex;\r\n gap: 12px;\r\n .n-select-item {\r\n min-width: 140px;\r\n flex: 1;\r\n }\r\n }\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-28\r\n * @Description: 级联选择组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"n-cascade-selector\">\r\n <NSelect\r\n v-for=\"(level, index) in levels\"\r\n :key=\"index\"\r\n v-model:value=\"selectedValues[index]\"\r\n :options=\"levelOptions[index]\"\r\n clearable\r\n :placeholder=\"placeholders[index]\"\r\n :disabled=\"index > 0 && !selectedValues[index - 1]\"\r\n @update:value=\"handleChange(index)\"\r\n class=\"n-select-item\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\n import { ref, computed, watch } from 'vue'\r\n import { NSelect } from 'naive-ui'\r\n import type { CascadeItem, CascadeValue } from './types'\r\n\r\n defineOptions({ name: 'C_Cascade' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n data: CascadeItem[]\r\n placeholders?: string[]\r\n modelValue?: CascadeValue\r\n }>(),\r\n {\r\n placeholders: () => ['请选择', '请选择', '请选择'],\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n 'update:modelValue': [value: CascadeValue]\r\n change: [value: CascadeValue]\r\n }>()\r\n\r\n const levels = [0, 1, 2]\r\n const selectedValues = ref<(string | number | null)[]>([null, null, null])\r\n\r\n const getLevelData = (level: number): CascadeItem[] => {\r\n if (level === 0) return props.data\r\n if (!selectedValues.value[level - 1]) return []\r\n const parentData = getLevelData(level - 1)\r\n return (\r\n parentData.find(x => x.value === selectedValues.value[level - 1])\r\n ?.children || []\r\n )\r\n }\r\n\r\n const levelOptions = computed(() =>\r\n levels.map(level =>\r\n getLevelData(level).map(item => ({\r\n label: item.label,\r\n value: item.value,\r\n }))\r\n )\r\n )\r\n\r\n const handleChange = (index: number) => {\r\n selectedValues.value.splice(\r\n index + 1,\r\n levels.length - index - 1,\r\n ...Array(levels.length - index - 1).fill(null)\r\n )\r\n emitValue()\r\n }\r\n\r\n const getSelectedItem = (index: number) => {\r\n const value = selectedValues.value[index]\r\n if (!value) return null\r\n const data = getLevelData(index)\r\n const item = data.find(i => i.value === value)\r\n return item ? { label: item.label, value: item.value } : null\r\n }\r\n\r\n const emitValue = () => {\r\n const result: CascadeValue = {\r\n primary: getSelectedItem(0),\r\n secondary: getSelectedItem(1),\r\n tertiary: getSelectedItem(2),\r\n }\r\n emit('update:modelValue', result)\r\n emit('change', result)\r\n }\r\n\r\n watch(\r\n () => props.modelValue,\r\n val => {\r\n selectedValues.value = [\r\n val?.primary?.value ?? null,\r\n val?.secondary?.value ?? null,\r\n val?.tertiary?.value ?? null,\r\n ]\r\n },\r\n { immediate: true, deep: true }\r\n )\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n .n-cascade-selector {\r\n display: flex;\r\n gap: 12px;\r\n .n-select-item {\r\n min-width: 140px;\r\n flex: 1;\r\n }\r\n }\r\n</style>\r\n"],"mappings":"ucA8BE,IAAM,EAAQ,EAWR,EAAO,EAKP,EAAS,CAAC,EAAG,EAAG,EAAE,CAClB,EAAiB,EAAgC,CAAC,KAAM,KAAM,KAAK,CAAC,CAEpE,EAAgB,GAChB,IAAU,EAAU,EAAM,KACzB,EAAe,MAAM,EAAQ,IACf,EAAa,EAAQ,EAAE,CAE7B,KAAK,GAAK,EAAE,QAAU,EAAe,MAAM,EAAQ,GAAG,EAC7D,UAJuC,EAAE,CAQ3C,EAAe,MACnB,EAAO,IAAI,GACT,EAAa,EAAM,CAAC,IAAI,IAAS,CAC/B,MAAO,EAAK,MACZ,MAAO,EAAK,MACb,EAAE,CACJ,CACF,CAEK,EAAgB,GAAkB,CACtC,EAAe,MAAM,OACnB,EAAQ,EACR,EAAO,OAAS,EAAQ,EACxB,GAAG,MAAM,EAAO,OAAS,EAAQ,EAAE,CAAC,KAAK,KAAK,CAC/C,CACD,GAAW,EAGP,EAAmB,GAAkB,CACzC,IAAM,EAAQ,EAAe,MAAM,GACnC,GAAI,CAAC,EAAO,OAAO,KAEnB,IAAM,EADO,EAAa,EAAM,CACd,KAAK,GAAK,EAAE,QAAU,EAAM,CAC9C,OAAO,EAAO,CAAE,MAAO,EAAK,MAAO,MAAO,EAAK,MAAO,CAAG,MAGrD,MAAkB,CACtB,IAAM,EAAuB,CAC3B,QAAS,EAAgB,EAAE,CAC3B,UAAW,EAAgB,EAAE,CAC7B,SAAU,EAAgB,EAAA,CAC3B,CACD,EAAK,oBAAqB,EAAO,CACjC,EAAK,SAAU,EAAO,SAGxB,MACQ,EAAM,WACZ,GAAO,CACL,EAAe,MAAQ,CACrB,GAAK,SAAS,OAAS,KACvB,GAAK,WAAW,OAAS,KACzB,GAAK,UAAU,OAAS,KACzB,EAEH,CAAE,UAAW,GAAM,KAAM,GAAM,CAChC,aCjGD,EAYM,MAZN,EAYM,EAAA,GAAA,CAXJ,EAUE,EAAA,KAAA,EATyB,GAAjB,EAAO,IADjB,EAUE,EAAA,EAAA,CAAA,CARC,IAAK,EACE,MAAO,EAAA,MAAe,wBAAf,EAAA,MAAe,GAAK,EAAA,GAKpB,EAAa,EAAK,CAAA,CAJhC,QAAS,EAAA,MAAa,GACvB,UAAA,GACC,YAAaA,EAAAA,aAAa,GAC1B,SAAU,EAAK,GAAA,CAAS,EAAA,MAAe,EAAK,GAE7C,MAAM"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_Chat-DeUv6uuI.css","names":[],"sources":["../src/components/C_Chat/index.vue?vue&type=style&index=0&scoped=5b4bf8dc&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled *//* ─── CSS 变量(基于项目主题变量,自动适配明暗) ───────────────────────── */\n.c-chat[data-v-5b4bf8dc] {\n --chat-bg: var(--card-color);\n --chat-sidebar-bg: var(--body-color);\n --chat-sidebar-width: 280px;\n --chat-header-bg: var(--card-color);\n --chat-header-height: 56px;\n --chat-input-bg: var(--body-color);\n --chat-input-height: 56px;\n --chat-bubble-self: var(--primary-color);\n --chat-bubble-other: var(--body-color);\n --chat-bubble-system: transparent;\n --chat-text-primary: var(--app-text-primary);\n --chat-text-secondary: var(--text-color-3);\n --chat-text-self: #fff;\n --chat-border: var(--border-color);\n --chat-online-color: #22c55e;\n --chat-unread-bg: #ef4444;\n --chat-unread-color: #fff;\n --chat-failed-color: #ef4444;\n --chat-shadow: rgba(0, 0, 0, 0.06);\n --chat-shadow-light: rgba(0, 0, 0, 0.03);\n --chat-radius: 12px;\n --chat-avatar-size: 36px;\n --chat-scrollbar-thumb: var(--border-color);\n display: flex;\n width: 100%;\n height: 100%;\n min-height: 0;\n border-radius: var(--chat-radius);\n overflow: hidden;\n background: var(--chat-bg);\n color: var(--chat-text-primary);\n font-size: 14px;\n border: 1px solid var(--chat-border);\n box-shadow: 0 2px 12px var(--chat-shadow);\n}\n\n/* ─── 联系人侧栏 ──────────────────────────────────────── */\n.c-chat__sidebar[data-v-5b4bf8dc] {\n display: flex;\n flex-direction: column;\n width: var(--chat-sidebar-width);\n min-width: var(--chat-sidebar-width);\n background: var(--chat-sidebar-bg);\n border-right: 1px solid var(--chat-border);\n box-shadow: 2px 0 8px var(--chat-shadow-light);\n}\n.c-chat__sidebar-header[data-v-5b4bf8dc] {\n display: flex;\n align-items: center;\n gap: 8px;\n height: var(--chat-header-height);\n padding: 0 16px;\n border-bottom: 1px solid var(--chat-border);\n font-weight: 600;\n font-size: 15px;\n}\n.c-chat__search[data-v-5b4bf8dc] {\n padding: 8px 12px;\n}\n.c-chat__contact-list[data-v-5b4bf8dc] {\n flex: 1;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n.c-chat__contact-list[data-v-5b4bf8dc]::-webkit-scrollbar {\n width: 4px;\n}\n.c-chat__contact-list[data-v-5b4bf8dc]::-webkit-scrollbar-thumb {\n background: var(--chat-scrollbar-thumb);\n border-radius: 2px;\n}\n.c-chat__contact[data-v-5b4bf8dc] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px;\n cursor: pointer;\n transition: background 0.15s;\n}\n.c-chat__contact[data-v-5b4bf8dc]:hover {\n background: color-mix(in srgb, var(--primary-color) 6%, transparent);\n}\n.c-chat__contact.is-active[data-v-5b4bf8dc] {\n background: color-mix(in srgb, var(--primary-color) 12%, transparent);\n}\n.c-chat__contact-avatar[data-v-5b4bf8dc] {\n position: relative;\n flex-shrink: 0;\n}\n.c-chat__avatar[data-v-5b4bf8dc] {\n width: var(--chat-avatar-size);\n height: var(--chat-avatar-size);\n border-radius: 50%;\n object-fit: cover;\n background: color-mix(in srgb, var(--border-color) 50%, transparent);\n}\n.c-chat__avatar-fallback[data-v-5b4bf8dc] {\n width: var(--chat-avatar-size);\n height: var(--chat-avatar-size);\n border-radius: 50%;\n background: color-mix(in srgb, var(--primary-color) 25%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n font-weight: 600;\n color: var(--app-text-primary);\n}\n.c-chat__online-dot[data-v-5b4bf8dc] {\n position: absolute;\n bottom: 0;\n right: 0;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--chat-online-color);\n border: 2px solid var(--chat-sidebar-bg);\n}\n.c-chat__contact-info[data-v-5b4bf8dc] {\n flex: 1;\n min-width: 0;\n}\n.c-chat__contact-name[data-v-5b4bf8dc] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 13px;\n font-weight: 500;\n}\n.c-chat__contact-time[data-v-5b4bf8dc] {\n font-size: 11px;\n color: var(--chat-text-secondary);\n margin-left: 8px;\n flex-shrink: 0;\n}\n.c-chat__contact-last[data-v-5b4bf8dc] {\n font-size: 12px;\n color: var(--chat-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n margin-top: 2px;\n}\n.c-chat__unread[data-v-5b4bf8dc] {\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: 9px;\n background: var(--chat-unread-bg);\n color: var(--chat-unread-color);\n font-size: 11px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n/* ─── 主聊天区域 ──────────────────────────────────────── */\n.c-chat__main[data-v-5b4bf8dc] {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n.c-chat__header[data-v-5b4bf8dc] {\n display: flex;\n align-items: center;\n gap: 10px;\n height: var(--chat-header-height);\n padding: 0 20px;\n background: var(--chat-header-bg);\n border-bottom: 1px solid var(--chat-border);\n box-shadow: 0 1px 4px var(--chat-shadow-light);\n font-weight: 600;\n font-size: 15px;\n}\n.c-chat__header-status[data-v-5b4bf8dc] {\n font-size: 12px;\n font-weight: 400;\n color: var(--chat-text-secondary);\n}\n\n/* ─── 消息区域 ────────────────────────────────────────── */\n.c-chat__messages[data-v-5b4bf8dc] {\n flex: 1;\n overflow-y: auto;\n overscroll-behavior: contain;\n padding: 16px 20px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n background: color-mix(in srgb, var(--body-color) 50%, var(--card-color));\n}\n.c-chat__messages[data-v-5b4bf8dc]::-webkit-scrollbar {\n width: 4px;\n}\n.c-chat__messages[data-v-5b4bf8dc]::-webkit-scrollbar-thumb {\n background: var(--chat-scrollbar-thumb);\n border-radius: 2px;\n}\n.c-chat__load-more[data-v-5b4bf8dc] {\n text-align: center;\n padding: 8px;\n}\n.c-chat__empty[data-v-5b4bf8dc] {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--chat-text-secondary);\n font-size: 13px;\n}\n\n/* ─── 消息气泡 ────────────────────────────────────────── */\n.c-chat__msg[data-v-5b4bf8dc] {\n display: flex;\n gap: 8px;\n max-width: 75%;\n}\n.c-chat__msg.is-self[data-v-5b4bf8dc] {\n margin-left: auto;\n flex-direction: row-reverse;\n}\n.c-chat__msg.is-system[data-v-5b4bf8dc] {\n max-width: 100%;\n justify-content: center;\n align-self: center;\n}\n.c-chat__msg-avatar[data-v-5b4bf8dc] {\n flex-shrink: 0;\n margin-top: 2px;\n}\n.c-chat__msg-body[data-v-5b4bf8dc] {\n display: flex;\n flex-direction: column;\n}\n.c-chat__msg-username[data-v-5b4bf8dc] {\n font-size: 12px;\n color: var(--chat-text-secondary);\n margin-bottom: 2px;\n}\n.is-self .c-chat__msg-username[data-v-5b4bf8dc] {\n text-align: right;\n}\n.c-chat__bubble[data-v-5b4bf8dc] {\n padding: 10px 14px;\n border-radius: 16px;\n line-height: 1.5;\n word-break: break-word;\n white-space: pre-wrap;\n}\n.is-other .c-chat__bubble[data-v-5b4bf8dc] {\n background: var(--chat-bubble-other);\n border: 1px solid var(--chat-border);\n border-top-left-radius: 4px;\n}\n.is-self .c-chat__bubble[data-v-5b4bf8dc] {\n background: var(--chat-bubble-self);\n color: var(--chat-text-self);\n border-top-right-radius: 4px;\n}\n.is-system .c-chat__bubble[data-v-5b4bf8dc] {\n background: var(--chat-bubble-system);\n color: var(--chat-text-secondary);\n font-size: 12px;\n padding: 4px 12px;\n}\n.c-chat__msg-image[data-v-5b4bf8dc] {\n max-width: 240px;\n max-height: 180px;\n border-radius: 12px;\n cursor: pointer;\n object-fit: cover;\n}\n.is-self .c-chat__msg-image[data-v-5b4bf8dc] {\n border-top-right-radius: 4px;\n}\n.is-other .c-chat__msg-image[data-v-5b4bf8dc] {\n border-top-left-radius: 4px;\n}\n.c-chat__msg-file[data-v-5b4bf8dc] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n border-radius: 12px;\n cursor: pointer;\n}\n.is-other .c-chat__msg-file[data-v-5b4bf8dc] {\n background: var(--chat-bubble-other);\n border: 1px solid var(--chat-border);\n border-top-left-radius: 4px;\n}\n.is-self .c-chat__msg-file[data-v-5b4bf8dc] {\n background: var(--chat-bubble-self);\n color: var(--chat-text-self);\n border-top-right-radius: 4px;\n}\n.c-chat__file-info[data-v-5b4bf8dc] {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n.c-chat__file-name[data-v-5b4bf8dc] {\n font-size: 13px;\n font-weight: 500;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.c-chat__file-size[data-v-5b4bf8dc] {\n font-size: 11px;\n opacity: 0.6;\n}\n.c-chat__msg-time[data-v-5b4bf8dc] {\n font-size: 11px;\n color: var(--chat-text-secondary);\n margin-top: 4px;\n}\n.is-self .c-chat__msg-time[data-v-5b4bf8dc] {\n text-align: right;\n}\n.c-chat__msg-status[data-v-5b4bf8dc] {\n font-size: 11px;\n margin-top: 2px;\n text-align: right;\n}\n.c-chat__msg-status.is-sending[data-v-5b4bf8dc] {\n color: var(--chat-text-secondary);\n}\n.c-chat__msg-status.is-failed[data-v-5b4bf8dc] {\n color: var(--chat-failed-color);\n cursor: pointer;\n}\n\n/* ─── 时间分割线 ──────────────────────────────────────── */\n.c-chat__time-divider[data-v-5b4bf8dc] {\n text-align: center;\n font-size: 11px;\n color: var(--chat-text-secondary);\n padding: 4px 0;\n}\n\n/* ─── 输入区域 ────────────────────────────────────────── */\n.c-chat__input-area[data-v-5b4bf8dc] {\n display: flex;\n align-items: center;\n gap: 8px;\n min-height: var(--chat-input-height);\n padding: 10px 16px;\n background: var(--chat-input-bg);\n border-top: 1px solid var(--chat-border);\n}\n.c-chat__input-actions[data-v-5b4bf8dc] {\n display: flex;\n gap: 4px;\n flex-shrink: 0;\n}\n.c-chat__input-wrap[data-v-5b4bf8dc] {\n flex: 1;\n min-width: 0;\n}\n.c-chat__send-btn[data-v-5b4bf8dc] {\n flex-shrink: 0;\n}\n\n/* ─── 空态 ────────────────────────────────────────────── */\n.c-chat__no-chat[data-v-5b4bf8dc] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n color: var(--chat-text-secondary);\n}\n.c-chat__no-chat-icon[data-v-5b4bf8dc] {\n font-size: 48px;\n opacity: 0.3;\n}\n\n/* ─── 响应式 ──────────────────────────────────────────── */\n@media (max-width: 640px) {\n.c-chat[data-v-5b4bf8dc] {\n --chat-sidebar-width: 72px;\n}\n.c-chat__contact-info[data-v-5b4bf8dc],\n .c-chat__search[data-v-5b4bf8dc],\n .c-chat__unread[data-v-5b4bf8dc] {\n display: none;\n}\n.c-chat__contact[data-v-5b4bf8dc] {\n justify-content: center;\n padding: 10px 8px;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}