@robot-admin/naive-ui-components 0.6.10 → 0.6.12

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 (350) hide show
  1. package/CHANGELOG.md +48 -48
  2. package/README.md +36 -20
  3. package/dist/C_ActionBar-jZPsiXtT.css.map +1 -0
  4. package/dist/C_ActionBar-nnfbZCea.css.map +1 -0
  5. package/dist/C_ActionBar.css +47 -47
  6. package/dist/C_ActionBar.js.map +1 -1
  7. package/dist/C_ActionBar2.js +1 -1
  8. package/dist/C_ActionBar2.js.map +1 -1
  9. package/dist/{C_AntV-BY8jqqmi.css.map → C_AntV-A3NXx_sF.css.map} +1 -1
  10. package/dist/{C_AntV-BbkHc-_a.css.map → C_AntV-D2Zp2qQF.css.map} +1 -1
  11. package/dist/C_AntV.css +78 -78
  12. package/dist/C_AntV.js.map +1 -1
  13. package/dist/C_AntV2.js +2 -2
  14. package/dist/C_AntV2.js.map +1 -1
  15. package/dist/C_AudioPlayer-CXl4yP_m.css.map +1 -0
  16. package/dist/C_AudioPlayer-CcBtt71m.css.map +1 -0
  17. package/dist/C_AudioPlayer.cjs +1 -0
  18. package/dist/C_AudioPlayer.css +283 -0
  19. package/dist/C_AudioPlayer.d.cts +2 -0
  20. package/dist/C_AudioPlayer.d.ts +2 -0
  21. package/dist/C_AudioPlayer.js +1 -0
  22. package/dist/C_AudioPlayer.js.map +1 -0
  23. package/dist/C_AudioPlayer2.js +2 -0
  24. package/dist/C_AudioPlayer2.js.map +1 -0
  25. package/dist/C_AvatarGroup-CtPhfH9h.css.map +1 -0
  26. package/dist/C_AvatarGroup-M1dNPH3E.css.map +1 -0
  27. package/dist/C_AvatarGroup.cjs +1 -0
  28. package/dist/C_AvatarGroup.css +107 -0
  29. package/dist/C_AvatarGroup.d.cts +2 -0
  30. package/dist/C_AvatarGroup.d.ts +2 -0
  31. package/dist/C_AvatarGroup.js +1 -0
  32. package/dist/C_AvatarGroup.js.map +1 -0
  33. package/dist/C_AvatarGroup2.js +2 -0
  34. package/dist/C_AvatarGroup2.js.map +1 -0
  35. package/dist/{C_Barcode-DYDRDe2O.css.map → C_Barcode-DjTmDkbQ.css.map} +1 -1
  36. package/dist/{C_Barcode-muJdc_O4.css.map → C_Barcode-Do1Ozsmh.css.map} +1 -1
  37. package/dist/C_Barcode.css +7 -7
  38. package/dist/C_Barcode.d.cts +1 -1
  39. package/dist/C_Barcode.d.ts +1 -1
  40. package/dist/C_Barcode.js.map +1 -1
  41. package/dist/C_Barcode2.js +1 -1
  42. package/dist/C_Barcode2.js.map +1 -1
  43. package/dist/C_Breadcrumb.d.cts +1 -1
  44. package/dist/C_Breadcrumb.d.ts +1 -1
  45. package/dist/C_Breadcrumb.js.map +1 -1
  46. package/dist/C_Breadcrumb2.js.map +1 -1
  47. package/dist/{C_Captcha-9_hk6VmY.css.map → C_Captcha-CZmAETly.css.map} +1 -1
  48. package/dist/{C_Captcha-MtASlJ0f.css.map → C_Captcha-Ccq3DMrR.css.map} +1 -1
  49. package/dist/C_Captcha.css +19 -19
  50. package/dist/C_Captcha.d.cts +1 -1
  51. package/dist/C_Captcha.d.ts +1 -1
  52. package/dist/C_Captcha.js.map +1 -1
  53. package/dist/C_Captcha2.js +1 -1
  54. package/dist/C_Captcha2.js.map +1 -1
  55. package/dist/C_Cascade.d.cts +1 -1
  56. package/dist/C_Cascade.d.ts +1 -1
  57. package/dist/C_Chat-CcdWgVE8.css.map +1 -0
  58. package/dist/C_Chat-Cx0woEEe.css.map +1 -0
  59. package/dist/C_Chat.cjs +1 -0
  60. package/dist/C_Chat.css +395 -0
  61. package/dist/C_Chat.d.cts +2 -0
  62. package/dist/C_Chat.d.ts +2 -0
  63. package/dist/C_Chat.js +1 -0
  64. package/dist/C_Chat.js.map +1 -0
  65. package/dist/C_Chat2.js +2 -0
  66. package/dist/C_Chat2.js.map +1 -0
  67. package/dist/{C_City-Bam08Qv3.css.map → C_City-Cv5BESaN.css.map} +1 -1
  68. package/dist/{C_City-Bb3W-KaN.css.map → C_City-DNKv4MpH.css.map} +1 -1
  69. package/dist/C_City.css +29 -29
  70. package/dist/C_City.d.cts +1 -1
  71. package/dist/C_City.d.ts +1 -1
  72. package/dist/C_City.js.map +1 -1
  73. package/dist/C_City2.js +1 -1
  74. package/dist/C_City2.js.map +1 -1
  75. package/dist/C_Code-DPZlNSxL.css.map +1 -0
  76. package/dist/C_Code-avZjAnHD.css.map +1 -0
  77. package/dist/C_Code.css +3 -3
  78. package/dist/C_Code.d.cts +1 -1
  79. package/dist/C_Code.d.ts +1 -1
  80. package/dist/C_Code.js.map +1 -1
  81. package/dist/C_Code2.js +1 -1
  82. package/dist/C_Code2.js.map +1 -1
  83. package/dist/{C_CollapsePanel-BCmTFuDz.css.map → C_CollapsePanel-BpofCDyl.css.map} +1 -1
  84. package/dist/{C_CollapsePanel-Bxnh4Zy-.css.map → C_CollapsePanel-Fap-lv_5.css.map} +1 -1
  85. package/dist/C_CollapsePanel.css +35 -35
  86. package/dist/C_CollapsePanel.js.map +1 -1
  87. package/dist/C_CollapsePanel2.js +1 -1
  88. package/dist/C_CollapsePanel2.js.map +1 -1
  89. package/dist/C_ContextMenu-CCsCeI39.css.map +1 -0
  90. package/dist/C_ContextMenu-DOTBeltf.css.map +1 -0
  91. package/dist/C_ContextMenu.cjs +1 -0
  92. package/dist/C_ContextMenu.css +115 -0
  93. package/dist/C_ContextMenu.d.cts +2 -0
  94. package/dist/C_ContextMenu.d.ts +2 -0
  95. package/dist/C_ContextMenu.js +1 -0
  96. package/dist/C_ContextMenu.js.map +1 -0
  97. package/dist/C_ContextMenu2.js +2 -0
  98. package/dist/C_ContextMenu2.js.map +1 -0
  99. package/dist/{C_Cron-Civir5-I.css.map → C_Cron-C0-8b5af.css.map} +1 -1
  100. package/dist/{C_Cron-iw3qpMsy.css.map → C_Cron-DqHQ92LN.css.map} +1 -1
  101. package/dist/C_Cron.css +37 -37
  102. package/dist/C_Cron.js.map +1 -1
  103. package/dist/C_Cron2.js +1 -1
  104. package/dist/C_Cron2.js.map +1 -1
  105. package/dist/C_Date.d.cts +1 -1
  106. package/dist/C_Date.d.ts +1 -1
  107. package/dist/{C_Draggable-BCjIvIwT.css.map → C_Draggable-Bq6o0qXn.css.map} +1 -1
  108. package/dist/{C_Draggable-DwkSlCOS.css.map → C_Draggable-r6nhx3yM.css.map} +1 -1
  109. package/dist/C_Draggable.css +46 -46
  110. package/dist/C_Draggable.js.map +1 -1
  111. package/dist/C_Draggable2.js +1 -1
  112. package/dist/C_Draggable2.js.map +1 -1
  113. package/dist/C_Editor.d.cts +1 -1
  114. package/dist/C_Editor.d.ts +1 -1
  115. package/dist/{C_FilePreview-BhcbiXsV.css.map → C_FilePreview-BVarVvnD.css.map} +1 -1
  116. package/dist/{C_FilePreview-DJudcIbW.css.map → C_FilePreview-DHGlV4OR.css.map} +1 -1
  117. package/dist/C_FilePreview.css +91 -91
  118. package/dist/C_FilePreview.js.map +1 -1
  119. package/dist/C_FilePreview2.js +1 -1
  120. package/dist/C_FilePreview2.js.map +1 -1
  121. package/dist/{C_Form-BtcAPIRi.css.map → C_Form-BciQUPpS.css.map} +1 -1
  122. package/dist/{C_Form-D_gwQ7hC.css.map → C_Form-CKHvXL3O.css.map} +1 -1
  123. package/dist/C_Form.css +222 -222
  124. package/dist/C_Form.js.map +1 -1
  125. package/dist/C_Form2.js +1 -1
  126. package/dist/C_Form2.js.map +1 -1
  127. package/dist/{C_FormSearch-Bi4XYjjk.css.map → C_FormSearch-Bmd2CmG6.css.map} +1 -1
  128. package/dist/{C_FormSearch-DUGOoX0u.css.map → C_FormSearch-DBNtnjaL.css.map} +1 -1
  129. package/dist/C_FormSearch.css +45 -45
  130. package/dist/C_FormSearch.js.map +1 -1
  131. package/dist/C_FormSearch2.js +1 -1
  132. package/dist/C_FormSearch2.js.map +1 -1
  133. package/dist/{C_FormulaEditor-CJk3SSUJ.css.map → C_FormulaEditor-3WG2vtQl.css.map} +1 -1
  134. package/dist/{C_FormulaEditor-Cy7mjfVO.css.map → C_FormulaEditor-Cm0CokN5.css.map} +1 -1
  135. package/dist/C_FormulaEditor.css +78 -78
  136. package/dist/C_FormulaEditor.js.map +1 -1
  137. package/dist/C_FormulaEditor2.js +1 -1
  138. package/dist/C_FormulaEditor2.js.map +1 -1
  139. package/dist/C_FullCalendar-1U4VlG7X.css.map +1 -0
  140. package/dist/C_FullCalendar-DLUdCtqf.css.map +1 -0
  141. package/dist/C_FullCalendar.css +3 -3
  142. package/dist/C_FullCalendar.js.map +1 -1
  143. package/dist/C_FullCalendar2.js +1 -1
  144. package/dist/C_FullCalendar2.js.map +1 -1
  145. package/dist/C_Guide.d.cts +1 -1
  146. package/dist/C_Guide.d.ts +1 -1
  147. package/dist/C_Icon.d.cts +1 -1
  148. package/dist/C_Icon.d.ts +1 -1
  149. package/dist/C_Icon.js.map +1 -1
  150. package/dist/C_Icon2.js.map +1 -1
  151. package/dist/{C_ImageCropper-BqZpkZfU.css.map → C_ImageCropper-D3L2-J_J.css.map} +1 -1
  152. package/dist/{C_ImageCropper-CylwGuzS.css.map → C_ImageCropper-DrmUlaLi.css.map} +1 -1
  153. package/dist/C_ImageCropper.css +10 -10
  154. package/dist/C_ImageCropper.js.map +1 -1
  155. package/dist/C_ImageCropper2.js +1 -1
  156. package/dist/C_ImageCropper2.js.map +1 -1
  157. package/dist/C_Language.d.cts +1 -1
  158. package/dist/C_Language.d.ts +1 -1
  159. package/dist/C_Login-Cst_W1XC.css.map +1 -0
  160. package/dist/C_Login-DWZ_6K1x.css.map +1 -0
  161. package/dist/C_Login.cjs +1 -0
  162. package/dist/C_Login.css +515 -0
  163. package/dist/C_Login.d.cts +2 -0
  164. package/dist/C_Login.d.ts +2 -0
  165. package/dist/C_Login.js +1 -0
  166. package/dist/C_Login.js.map +1 -0
  167. package/dist/C_Login2.js +2 -0
  168. package/dist/C_Login2.js.map +1 -0
  169. package/dist/{C_Map-DNnBcY70.css.map → C_Map-BCJmDnJT.css.map} +1 -1
  170. package/dist/{C_Map-LEXIx6zM.css.map → C_Map-WUMXSAfy.css.map} +1 -1
  171. package/dist/C_Map.css +4 -4
  172. package/dist/C_Map.js.map +1 -1
  173. package/dist/C_Map2.js +1 -1
  174. package/dist/C_Map2.js.map +1 -1
  175. package/dist/{C_Markdown-Kujzx1qG.css.map → C_Markdown-7iBnLDtL.css.map} +1 -1
  176. package/dist/{C_Markdown-YCAmBbNF.css.map → C_Markdown-BbTvdFCl.css.map} +1 -1
  177. package/dist/C_Markdown.css +33 -33
  178. package/dist/C_Markdown.d.cts +1 -1
  179. package/dist/C_Markdown.d.ts +1 -1
  180. package/dist/C_Markdown.js.map +1 -1
  181. package/dist/C_Markdown2.js +1 -1
  182. package/dist/C_Markdown2.js.map +1 -1
  183. package/dist/C_Menu.d.cts +1 -1
  184. package/dist/C_Menu.d.ts +1 -1
  185. package/dist/C_Progress.d.cts +1 -1
  186. package/dist/C_Progress.d.ts +1 -1
  187. package/dist/{C_QRCode-CFNTda_1.css.map → C_QRCode-DMxSyogU.css.map} +1 -1
  188. package/dist/{C_QRCode-CmWr9lK-.css.map → C_QRCode-G7fiAkm4.css.map} +1 -1
  189. package/dist/C_QRCode.css +9 -9
  190. package/dist/C_QRCode.js.map +1 -1
  191. package/dist/C_QRCode2.js +1 -1
  192. package/dist/C_QRCode2.js.map +1 -1
  193. package/dist/{C_Signature-PN4GOLRX.css.map → C_Signature-B5fFz6RD.css.map} +1 -1
  194. package/dist/{C_Signature-fWQqGIZ-.css.map → C_Signature-es-ZNPzr.css.map} +1 -1
  195. package/dist/C_Signature.css +14 -14
  196. package/dist/C_Signature.js.map +1 -1
  197. package/dist/C_Signature2.js +1 -1
  198. package/dist/C_Signature2.js.map +1 -1
  199. package/dist/{C_SplitPane-BOwr1C7K.css.map → C_SplitPane-Br2eK8IG.css.map} +1 -1
  200. package/dist/{C_SplitPane-N-a_D-7F.css.map → C_SplitPane-DuyzUhKB.css.map} +1 -1
  201. package/dist/C_SplitPane.css +31 -31
  202. package/dist/C_SplitPane.js.map +1 -1
  203. package/dist/C_SplitPane2.js +1 -1
  204. package/dist/C_SplitPane2.js.map +1 -1
  205. package/dist/C_Steps-B1Wf3E73.css.map +1 -0
  206. package/dist/C_Steps-C8cOf_tu.css.map +1 -0
  207. package/dist/C_Steps.css +41 -41
  208. package/dist/C_Steps.d.cts +1 -1
  209. package/dist/C_Steps.d.ts +1 -1
  210. package/dist/C_Steps.js.map +1 -1
  211. package/dist/C_Steps2.js +1 -1
  212. package/dist/C_Steps2.js.map +1 -1
  213. package/dist/{C_Table-BV1wUw6f.css.map → C_Table-vZlcgH6x.css.map} +1 -1
  214. package/dist/{C_Table-LlJ-JEdm.css.map → C_Table-wzAO43l0.css.map} +1 -1
  215. package/dist/C_Table.css +93 -93
  216. package/dist/C_Table.js.map +1 -1
  217. package/dist/C_Table2.js +1 -1
  218. package/dist/C_Table2.js.map +1 -1
  219. package/dist/{C_TagsView-DX3LecK1.css.map → C_TagsView-BsutyC3U.css.map} +1 -1
  220. package/dist/{C_TagsView-DqOTa8bL.css.map → C_TagsView-Crm7yWg9.css.map} +1 -1
  221. package/dist/C_TagsView.css +36 -36
  222. package/dist/C_TagsView.d.cts +1 -1
  223. package/dist/C_TagsView.d.ts +1 -1
  224. package/dist/C_TagsView.js.map +1 -1
  225. package/dist/C_TagsView2.js +1 -1
  226. package/dist/C_TagsView2.js.map +1 -1
  227. package/dist/C_Theme.d.cts +1 -1
  228. package/dist/C_Theme.d.ts +1 -1
  229. package/dist/C_Time-Bd_e1YDN.css.map +1 -0
  230. package/dist/C_Time-CKb_fwdm.css.map +1 -0
  231. package/dist/C_Time.css +5 -5
  232. package/dist/C_Time.js.map +1 -1
  233. package/dist/C_Time2.js +1 -1
  234. package/dist/C_Time2.js.map +1 -1
  235. package/dist/C_Timeline-CGTFTMX0.css.map +1 -0
  236. package/dist/C_Timeline-DxKyvKer.css.map +1 -0
  237. package/dist/C_Timeline.cjs +1 -0
  238. package/dist/C_Timeline.css +271 -0
  239. package/dist/C_Timeline.d.cts +2 -0
  240. package/dist/C_Timeline.d.ts +2 -0
  241. package/dist/C_Timeline.js +1 -0
  242. package/dist/C_Timeline.js.map +1 -0
  243. package/dist/C_Timeline2.js +2 -0
  244. package/dist/C_Timeline2.js.map +1 -0
  245. package/dist/C_Transfer-CCEqnC-v.css.map +1 -0
  246. package/dist/C_Transfer-CStPm4NP.css.map +1 -0
  247. package/dist/C_Transfer.cjs +1 -0
  248. package/dist/C_Transfer.css +193 -0
  249. package/dist/C_Transfer.d.cts +2 -0
  250. package/dist/C_Transfer.d.ts +2 -0
  251. package/dist/C_Transfer.js +1 -0
  252. package/dist/C_Transfer.js.map +1 -0
  253. package/dist/C_Transfer2.js +2 -0
  254. package/dist/C_Transfer2.js.map +1 -0
  255. package/dist/{C_Tree-D98nX9qT.css.map → C_Tree-B-Le4hjQ.css.map} +1 -1
  256. package/dist/{C_Tree-ulfm7b-P.css.map → C_Tree-DnGc_MPb.css.map} +1 -1
  257. package/dist/C_Tree.css +33 -33
  258. package/dist/C_Tree.js.map +1 -1
  259. package/dist/C_Tree2.js +1 -1
  260. package/dist/C_Tree2.js.map +1 -1
  261. package/dist/{C_VideoPlayer-4J96tKfK.css.map → C_VideoPlayer-Cdgv4Zq0.css.map} +1 -1
  262. package/dist/{C_VideoPlayer-D35seHkm.css.map → C_VideoPlayer-rm0MODUv.css.map} +1 -1
  263. package/dist/C_VideoPlayer.css +13 -13
  264. package/dist/C_VideoPlayer.js.map +1 -1
  265. package/dist/C_VideoPlayer2.js +1 -1
  266. package/dist/C_VideoPlayer2.js.map +1 -1
  267. package/dist/{C_VtableGantt-C2lxFmWg.css.map → C_VtableGantt-BpY-Rng3.css.map} +1 -1
  268. package/dist/{C_VtableGantt-Dwnkw_WL.css.map → C_VtableGantt-DB87-jdS.css.map} +1 -1
  269. package/dist/C_VtableGantt.css +15 -15
  270. package/dist/C_VtableGantt.d.cts +1 -1
  271. package/dist/C_VtableGantt.d.ts +1 -1
  272. package/dist/C_VtableGantt.js.map +1 -1
  273. package/dist/C_VtableGantt2.js +1 -1
  274. package/dist/C_VtableGantt2.js.map +1 -1
  275. package/dist/{C_WorkFlow-2hJfvvTK.css.map → C_WorkFlow-7F6op6qN.css.map} +1 -1
  276. package/dist/{C_WorkFlow-DHoNjl8-.css.map → C_WorkFlow-Cy3HWA6A.css.map} +1 -1
  277. package/dist/C_WorkFlow.css +108 -108
  278. package/dist/C_WorkFlow.js.map +1 -1
  279. package/dist/C_WorkFlow2.js +1 -1
  280. package/dist/C_WorkFlow2.js.map +1 -1
  281. package/dist/constants.d.ts +4 -4
  282. package/dist/constants2.d.ts +4 -4
  283. package/dist/constants3.d.ts +5 -5
  284. package/dist/constants4.d.ts +2 -2
  285. package/dist/data.d.ts +1 -1
  286. package/dist/index.cjs +1 -1
  287. package/dist/index.cjs.map +1 -1
  288. package/dist/index.d.cts +48 -41
  289. package/dist/index.d.ts +48 -41
  290. package/dist/index.js +1 -1
  291. package/dist/index.js.map +1 -1
  292. package/dist/index10.vue.d.ts +41 -23
  293. package/dist/index11.vue.d.ts +80 -63
  294. package/dist/index12.vue.d.ts +48 -20
  295. package/dist/index13.vue.d.ts +23 -63
  296. package/dist/index14.vue.d.ts +21 -58
  297. package/dist/index15.vue.d.ts +65 -48
  298. package/dist/index16.vue.d.ts +16 -21
  299. package/dist/index17.vue.d.ts +106 -77
  300. package/dist/index18.vue.d.ts +74 -11
  301. package/dist/index19.vue.d.ts +58 -653
  302. package/dist/index2.vue.d.ts +75 -30
  303. package/dist/index20.vue.d.ts +55 -0
  304. package/dist/index21.vue.d.ts +33 -0
  305. package/dist/index22.vue.d.ts +88 -0
  306. package/dist/index23.vue.d.ts +18 -0
  307. package/dist/index24.vue.d.ts +112 -0
  308. package/dist/index25.vue.d.ts +81 -0
  309. package/dist/index26.vue.d.ts +662 -0
  310. package/dist/index3.vue.d.ts +77 -19
  311. package/dist/index4.vue.d.ts +29 -36
  312. package/dist/index5.vue.d.ts +19 -20
  313. package/dist/index6.vue.d.ts +36 -26
  314. package/dist/index7.vue.d.ts +23 -40
  315. package/dist/index8.vue.d.ts +117 -45
  316. package/dist/index9.vue.d.ts +19 -25
  317. package/dist/menuAdapter.js.map +1 -1
  318. package/dist/resolver.cjs +1 -1
  319. package/dist/resolver.cjs.map +1 -1
  320. package/dist/resolver.d.cts +1 -1
  321. package/dist/resolver.d.ts +1 -1
  322. package/dist/resolver.js +1 -1
  323. package/dist/resolver.js.map +1 -1
  324. package/dist/style.css +3121 -1228
  325. package/dist/useCollapsePanel.d.ts +2 -2
  326. package/dist/useCropperCore.d.ts +5 -5
  327. package/dist/useDraggableLayout.d.ts +2 -2
  328. package/dist/useDynamicFormState.d.ts +4 -4
  329. package/dist/useEdgeInteraction.d.ts +2 -2
  330. package/dist/useFullscreen.d.ts +2 -2
  331. package/dist/useInfiniteScroll.d.ts +3 -3
  332. package/dist/useModalEdit.d.ts +2 -2
  333. package/dist/useQRCode.d.ts +1 -1
  334. package/dist/useSearchState.d.ts +2 -2
  335. package/dist/useSignatureHistory.d.ts +2 -2
  336. package/dist/useSplitResize.d.ts +6 -6
  337. package/dist/useTimeSelection.d.ts +1 -1
  338. package/dist/useTreeOperations.d.ts +2 -2
  339. package/dist/useWorkflowValidation.d.ts +3 -3
  340. package/package.json +2 -2
  341. package/dist/C_ActionBar-DYG0Yf3A.css.map +0 -1
  342. package/dist/C_ActionBar-MAjRhqcI.css.map +0 -1
  343. package/dist/C_Code-BFi5xxnM.css.map +0 -1
  344. package/dist/C_Code-CZ7yfnri.css.map +0 -1
  345. package/dist/C_FullCalendar-BQA0OjGB.css.map +0 -1
  346. package/dist/C_FullCalendar-ChJUosAi.css.map +0 -1
  347. package/dist/C_Steps-DLW2vbqC.css.map +0 -1
  348. package/dist/C_Steps-DwMRHbG2.css.map +0 -1
  349. package/dist/C_Time-BzyQVvTS.css.map +0 -1
  350. package/dist/C_Time-DmpKLQHc.css.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"C_Chat2.js","names":[],"sources":["../src/components/C_Chat/types.ts","../src/components/C_Chat/index.vue","../src/components/C_Chat/index.vue","../src/components/C_Chat/index.vue"],"sourcesContent":["/**\r\n * @Description: C_Chat 通用聊天组件 - 类型定义\r\n */\r\n\r\n// ================= 消息类型 =================\r\n\r\n/** 消息类型 */\r\nexport type MessageType = 'text' | 'image' | 'file' | 'system'\r\n\r\n/** 消息发送方 */\r\nexport type MessageSender = 'self' | 'other' | 'system'\r\n\r\n/** 消息状态 */\r\nexport type MessageStatus = 'sending' | 'sent' | 'failed'\r\n\r\n/** 单条消息 */\r\nexport interface ChatMessage {\r\n /** 唯一标识 */\r\n id: string\r\n /** 消息内容(文本 / 图片 URL / 文件信息 JSON) */\r\n content: string\r\n /** 消息类型 */\r\n type: MessageType\r\n /** 发送方 */\r\n sender: MessageSender\r\n /** 发送时间(ISO 字符串 或 时间戳) */\r\n timestamp: string | number\r\n /** 发送者头像 URL */\r\n avatar?: string\r\n /** 发送者用户名 */\r\n username?: string\r\n /** 消息状态 */\r\n status?: MessageStatus\r\n /** 文件名(type = 'file' 时使用) */\r\n fileName?: string\r\n /** 文件大小(字节) */\r\n fileSize?: number\r\n}\r\n\r\n// ================= 联系人 =================\r\n\r\n/** 联系人 / 会话 */\r\nexport interface ChatContact {\r\n /** 唯一标识 */\r\n id: string\r\n /** 显示名称 */\r\n name: string\r\n /** 头像 URL */\r\n avatar?: string\r\n /** 最后一条消息摘要 */\r\n lastMessage?: string\r\n /** 最后消息时间 */\r\n lastTime?: string | number\r\n /** 未读数 */\r\n unread?: number\r\n /** 是否在线 */\r\n online?: boolean\r\n}\r\n\r\n// ================= 组件 Props =================\r\n\r\nexport interface ChatProps {\r\n /** 联系人列表 */\r\n contacts?: ChatContact[]\r\n /** 消息列表 */\r\n messages?: ChatMessage[]\r\n /** 当前选中的联系人 ID */\r\n currentContactId?: string\r\n /** 输入框占位符 */\r\n placeholder?: string\r\n /** 是否显示联系人侧栏 */\r\n showContacts?: boolean\r\n /** 是否显示消息时间戳 */\r\n showTimestamp?: boolean\r\n /** 当前用户头像 */\r\n selfAvatar?: string\r\n /** 当前用户名 */\r\n selfName?: string\r\n /** 是否显示发送按钮 */\r\n showSendBtn?: boolean\r\n /** 是否正在加载历史消息 */\r\n loadingHistory?: boolean\r\n /** 标题(无联系人模式时显示) */\r\n title?: string\r\n}\r\n\r\n// ================= 默认值 =================\r\n\r\nexport const DEFAULT_CHAT_PROPS: Required<\r\n Pick<\r\n ChatProps,\r\n | 'placeholder'\r\n | 'showContacts'\r\n | 'showTimestamp'\r\n | 'showSendBtn'\r\n | 'loadingHistory'\r\n | 'title'\r\n >\r\n> = {\r\n placeholder: '输入消息...',\r\n showContacts: true,\r\n showTimestamp: true,\r\n showSendBtn: true,\r\n loadingHistory: false,\r\n title: '聊天',\r\n}\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: C_Chat — 通用聊天组件\r\n *\r\n * 支持联系人列表 + 消息气泡 + 输入框,可用于 IM、客服、AI 对话等场景。\r\n * 通过 CSS 变量可完全自定义主题风格。\r\n *\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\r\n-->\r\n<template>\r\n <div class=\"c-chat\">\r\n <!-- ===== 联系人侧栏 ===== -->\r\n <aside\r\n v-if=\"mergedProps.showContacts && contactList.length\"\r\n class=\"c-chat__sidebar\"\r\n >\r\n <div class=\"c-chat__sidebar-header\">\r\n <C_Icon\r\n name=\"mdi:chat-outline\"\r\n :size=\"20\"\r\n />\r\n 会话\r\n </div>\r\n\r\n <!-- 搜索框 -->\r\n <div class=\"c-chat__search\">\r\n <NInput\r\n v-model:value=\"searchKeyword\"\r\n placeholder=\"搜索联系人\"\r\n clearable\r\n size=\"small\"\r\n >\r\n <template #prefix>\r\n <C_Icon\r\n name=\"mdi:magnify\"\r\n :size=\"16\"\r\n />\r\n </template>\r\n </NInput>\r\n </div>\r\n\r\n <!-- 联系人列表 -->\r\n <div class=\"c-chat__contact-list\">\r\n <div\r\n v-for=\"contact in filteredContacts\"\r\n :key=\"contact.id\"\r\n class=\"c-chat__contact\"\r\n :class=\"{ 'is-active': contact.id === currentId }\"\r\n @click=\"handleSelectContact(contact.id)\"\r\n >\r\n <div class=\"c-chat__contact-avatar\">\r\n <img\r\n v-if=\"contact.avatar\"\r\n :src=\"contact.avatar\"\r\n :alt=\"contact.name\"\r\n class=\"c-chat__avatar\"\r\n />\r\n <div\r\n v-else\r\n class=\"c-chat__avatar-fallback\"\r\n >\r\n {{ contact.name.charAt(0) }}\r\n </div>\r\n <span\r\n v-if=\"contact.online\"\r\n class=\"c-chat__online-dot\"\r\n />\r\n </div>\r\n\r\n <div class=\"c-chat__contact-info\">\r\n <div class=\"c-chat__contact-name\">\r\n <span>{{ contact.name }}</span>\r\n <span\r\n v-if=\"contact.lastTime\"\r\n class=\"c-chat__contact-time\"\r\n >\r\n {{ formatContactTime(contact.lastTime) }}\r\n </span>\r\n </div>\r\n <div\r\n v-if=\"contact.lastMessage\"\r\n class=\"c-chat__contact-last\"\r\n >\r\n {{ contact.lastMessage }}\r\n </div>\r\n </div>\r\n\r\n <span\r\n v-if=\"contact.unread\"\r\n class=\"c-chat__unread\"\r\n >\r\n {{ contact.unread > 99 ? '99+' : contact.unread }}\r\n </span>\r\n </div>\r\n </div>\r\n </aside>\r\n\r\n <!-- ===== 主聊天区域 ===== -->\r\n <div\r\n v-if=\"currentContact || !mergedProps.showContacts\"\r\n class=\"c-chat__main\"\r\n >\r\n <!-- 顶栏 -->\r\n <div class=\"c-chat__header\">\r\n <slot name=\"header\">\r\n <span>{{ currentContact?.name || mergedProps.title }}</span>\r\n <span\r\n v-if=\"currentContact?.online\"\r\n class=\"c-chat__header-status\"\r\n >\r\n 在线\r\n </span>\r\n </slot>\r\n </div>\r\n\r\n <!-- 消息列表 -->\r\n <div\r\n ref=\"messagesRef\"\r\n class=\"c-chat__messages\"\r\n @scroll=\"handleMessagesScroll\"\r\n >\r\n <!-- 加载更多 -->\r\n <div\r\n v-if=\"mergedProps.loadingHistory\"\r\n class=\"c-chat__load-more\"\r\n >\r\n <NSpin size=\"small\" />\r\n </div>\r\n\r\n <!-- 消息为空 -->\r\n <div\r\n v-if=\"!messageList.length && !mergedProps.loadingHistory\"\r\n class=\"c-chat__empty\"\r\n >\r\n 暂无消息,发送第一条吧\r\n </div>\r\n\r\n <!-- 消息列表 -->\r\n <template\r\n v-for=\"(msg, idx) in messageList\"\r\n :key=\"msg.id\"\r\n >\r\n <!-- 时间分割 -->\r\n <div\r\n v-if=\"shouldShowTimeDivider(msg, idx)\"\r\n class=\"c-chat__time-divider\"\r\n >\r\n {{ formatMessageTime(msg.timestamp) }}\r\n </div>\r\n\r\n <!-- 消息条目 -->\r\n <div\r\n class=\"c-chat__msg\"\r\n :class=\"`is-${msg.sender}`\"\r\n >\r\n <!-- 头像 -->\r\n <div\r\n v-if=\"msg.sender !== 'system'\"\r\n class=\"c-chat__msg-avatar\"\r\n >\r\n <img\r\n v-if=\"getAvatar(msg)\"\r\n :src=\"getAvatar(msg)\"\r\n class=\"c-chat__avatar\"\r\n />\r\n <div\r\n v-else\r\n class=\"c-chat__avatar-fallback\"\r\n >\r\n {{ getUsername(msg).charAt(0) }}\r\n </div>\r\n </div>\r\n\r\n <!-- 消息体 -->\r\n <div class=\"c-chat__msg-body\">\r\n <span\r\n v-if=\"msg.sender === 'other' && msg.username\"\r\n class=\"c-chat__msg-username\"\r\n >\r\n {{ msg.username }}\r\n </span>\r\n\r\n <!-- 文本消息 -->\r\n <div\r\n v-if=\"msg.type === 'text' || msg.type === 'system'\"\r\n class=\"c-chat__bubble\"\r\n >\r\n {{ msg.content }}\r\n </div>\r\n\r\n <!-- 图片消息 -->\r\n <img\r\n v-else-if=\"msg.type === 'image'\"\r\n :src=\"msg.content\"\r\n class=\"c-chat__msg-image\"\r\n @click=\"emit('image-preview', msg.content)\"\r\n />\r\n\r\n <!-- 文件消息 -->\r\n <div\r\n v-else-if=\"msg.type === 'file'\"\r\n class=\"c-chat__msg-file\"\r\n @click=\"emit('file-click', msg)\"\r\n >\r\n <C_Icon\r\n name=\"mdi:file-document-outline\"\r\n :size=\"28\"\r\n />\r\n <div class=\"c-chat__file-info\">\r\n <span class=\"c-chat__file-name\">{{\r\n msg.fileName || '文件'\r\n }}</span>\r\n <span\r\n v-if=\"msg.fileSize\"\r\n class=\"c-chat__file-size\"\r\n >\r\n {{ formatFileSize(msg.fileSize) }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <!-- 时间戳 -->\r\n <span\r\n v-if=\"mergedProps.showTimestamp && msg.sender !== 'system'\"\r\n class=\"c-chat__msg-time\"\r\n >\r\n {{ formatTime(msg.timestamp) }}\r\n </span>\r\n\r\n <!-- 发送状态 -->\r\n <span\r\n v-if=\"msg.sender === 'self' && msg.status\"\r\n class=\"c-chat__msg-status\"\r\n :class=\"`is-${msg.status}`\"\r\n @click=\"msg.status === 'failed' && emit('resend', msg)\"\r\n >\r\n {{\r\n msg.status === 'sending'\r\n ? '发送中...'\r\n : msg.status === 'failed'\r\n ? '发送失败,点击重试'\r\n : ''\r\n }}\r\n </span>\r\n </div>\r\n </div>\r\n </template>\r\n </div>\r\n\r\n <!-- 输入区域 -->\r\n <div class=\"c-chat__input-area\">\r\n <div class=\"c-chat__input-actions\">\r\n <slot name=\"input-actions\">\r\n <NButton\r\n quaternary\r\n circle\r\n size=\"small\"\r\n @click=\"emit('emoji-click')\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n name=\"mdi:emoticon-happy-outline\"\r\n :size=\"18\"\r\n />\r\n </template>\r\n </NButton>\r\n <NButton\r\n quaternary\r\n circle\r\n size=\"small\"\r\n @click=\"emit('file-upload')\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n name=\"mdi:paperclip\"\r\n :size=\"18\"\r\n />\r\n </template>\r\n </NButton>\r\n </slot>\r\n </div>\r\n\r\n <div class=\"c-chat__input-wrap\">\r\n <NInput\r\n v-model:value=\"inputText\"\r\n :placeholder=\"mergedProps.placeholder\"\r\n type=\"textarea\"\r\n :autosize=\"{ minRows: 1, maxRows: 4 }\"\r\n @keydown.enter.exact.prevent=\"handleSend\"\r\n />\r\n </div>\r\n\r\n <NButton\r\n v-if=\"mergedProps.showSendBtn\"\r\n type=\"primary\"\r\n :disabled=\"!inputText.trim()\"\r\n class=\"c-chat__send-btn\"\r\n @click=\"handleSend\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n name=\"mdi:send\"\r\n :size=\"16\"\r\n />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <!-- ===== 未选中联系人空态 ===== -->\r\n <div\r\n v-else\r\n class=\"c-chat__main c-chat__no-chat\"\r\n >\r\n <C_Icon\r\n name=\"mdi:chat-processing-outline\"\r\n :size=\"48\"\r\n class=\"c-chat__no-chat-icon\"\r\n />\r\n <span>选择一个会话开始聊天</span>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed, nextTick, ref, watch } from 'vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import {\r\n DEFAULT_CHAT_PROPS,\r\n type ChatProps,\r\n type ChatMessage,\r\n type ChatContact,\r\n } from './types'\r\n\r\n // ===== Props & Emits =====\r\n const props = withDefaults(defineProps<ChatProps>(), {\r\n contacts: () => [],\r\n messages: () => [],\r\n currentContactId: '',\r\n placeholder: DEFAULT_CHAT_PROPS.placeholder,\r\n showContacts: DEFAULT_CHAT_PROPS.showContacts,\r\n showTimestamp: DEFAULT_CHAT_PROPS.showTimestamp,\r\n showSendBtn: DEFAULT_CHAT_PROPS.showSendBtn,\r\n loadingHistory: DEFAULT_CHAT_PROPS.loadingHistory,\r\n title: DEFAULT_CHAT_PROPS.title,\r\n selfAvatar: '',\r\n selfName: '',\r\n })\r\n\r\n const emit = defineEmits<{\r\n (e: 'send', content: string): void\r\n (e: 'select-contact', id: string): void\r\n (e: 'load-more'): void\r\n (e: 'image-preview', url: string): void\r\n (e: 'file-click', msg: ChatMessage): void\r\n (e: 'file-upload'): void\r\n (e: 'emoji-click'): void\r\n (e: 'resend', msg: ChatMessage): void\r\n }>()\r\n\r\n // ===== 合并 Props =====\r\n const mergedProps = computed(() => ({\r\n ...DEFAULT_CHAT_PROPS,\r\n ...props,\r\n }))\r\n\r\n // ===== 内部状态 =====\r\n const inputText = ref('')\r\n const searchKeyword = ref('')\r\n const messagesRef = ref<HTMLElement | null>(null)\r\n\r\n // ===== 计算属性 =====\r\n const contactList = computed<ChatContact[]>(() => props.contacts ?? [])\r\n const messageList = computed<ChatMessage[]>(() => props.messages ?? [])\r\n\r\n const currentId = computed(\r\n () => props.currentContactId || contactList.value[0]?.id || ''\r\n )\r\n\r\n const currentContact = computed(() =>\r\n contactList.value.find(c => c.id === currentId.value)\r\n )\r\n\r\n const filteredContacts = computed(() => {\r\n if (!searchKeyword.value) return contactList.value\r\n const kw = searchKeyword.value.toLowerCase()\r\n return contactList.value.filter(c => c.name.toLowerCase().includes(kw))\r\n })\r\n\r\n // ===== 方法 =====\r\n const handleSelectContact = (id: string) => {\r\n emit('select-contact', id)\r\n }\r\n\r\n const handleSend = () => {\r\n const text = inputText.value.trim()\r\n if (!text) return\r\n emit('send', text)\r\n inputText.value = ''\r\n }\r\n\r\n /** 自动滚动到底部 */\r\n const scrollToBottom = () => {\r\n nextTick(() => {\r\n if (messagesRef.value) {\r\n messagesRef.value.scrollTop = messagesRef.value.scrollHeight\r\n }\r\n })\r\n }\r\n\r\n // 消息变化时自动滚动\r\n watch(\r\n () => props.messages?.length,\r\n () => scrollToBottom(),\r\n { flush: 'post' }\r\n )\r\n\r\n /** 滚动到顶部时触发加载更多 */\r\n const handleMessagesScroll = () => {\r\n if (\r\n messagesRef.value &&\r\n messagesRef.value.scrollTop === 0 &&\r\n !mergedProps.value.loadingHistory\r\n ) {\r\n emit('load-more')\r\n }\r\n }\r\n\r\n // ===== 工具函数 =====\r\n const getAvatar = (msg: ChatMessage) => {\r\n if (msg.sender === 'self') return props.selfAvatar || msg.avatar\r\n return msg.avatar\r\n }\r\n\r\n const getUsername = (msg: ChatMessage) => {\r\n if (msg.sender === 'self') return props.selfName || msg.username || 'Me'\r\n return msg.username || '未知'\r\n }\r\n\r\n const formatTime = (ts: string | number) => {\r\n const d = new Date(ts)\r\n return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`\r\n }\r\n\r\n const formatMessageTime = (ts: string | number) => {\r\n const d = new Date(ts)\r\n const now = new Date()\r\n const isToday =\r\n d.getFullYear() === now.getFullYear() &&\r\n d.getMonth() === now.getMonth() &&\r\n d.getDate() === now.getDate()\r\n if (isToday) return formatTime(ts)\r\n return `${d.getMonth() + 1}/${d.getDate()} ${formatTime(ts)}`\r\n }\r\n\r\n const formatContactTime = (ts: string | number) => {\r\n const d = new Date(ts)\r\n const now = new Date()\r\n const diff = now.getTime() - d.getTime()\r\n if (diff < 60_000) return '刚刚'\r\n if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}分钟前`\r\n if (diff < 86_400_000) return formatTime(ts)\r\n return `${d.getMonth() + 1}/${d.getDate()}`\r\n }\r\n\r\n const formatFileSize = (bytes: number) => {\r\n if (bytes < 1024) return `${bytes}B`\r\n if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)}KB`\r\n return `${(bytes / 1048576).toFixed(1)}MB`\r\n }\r\n\r\n /** 是否显示时间分割线(两条消息间超过 5 分钟) */\r\n const shouldShowTimeDivider = (msg: ChatMessage, idx: number) => {\r\n if (idx === 0) return true\r\n const prev = messageList.value[idx - 1]\r\n const curr = new Date(msg.timestamp).getTime()\r\n const prevTime = new Date(prev.timestamp).getTime()\r\n return curr - prevTime > 5 * 60 * 1000\r\n }\r\n\r\n // ===== 暴露方法 =====\r\n defineExpose({ scrollToBottom })\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: C_Chat — 通用聊天组件\r\n *\r\n * 支持联系人列表 + 消息气泡 + 输入框,可用于 IM、客服、AI 对话等场景。\r\n * 通过 CSS 变量可完全自定义主题风格。\r\n *\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\r\n-->\r\n<template>\r\n <div class=\"c-chat\">\r\n <!-- ===== 联系人侧栏 ===== -->\r\n <aside\r\n v-if=\"mergedProps.showContacts && contactList.length\"\r\n class=\"c-chat__sidebar\"\r\n >\r\n <div class=\"c-chat__sidebar-header\">\r\n <C_Icon\r\n name=\"mdi:chat-outline\"\r\n :size=\"20\"\r\n />\r\n 会话\r\n </div>\r\n\r\n <!-- 搜索框 -->\r\n <div class=\"c-chat__search\">\r\n <NInput\r\n v-model:value=\"searchKeyword\"\r\n placeholder=\"搜索联系人\"\r\n clearable\r\n size=\"small\"\r\n >\r\n <template #prefix>\r\n <C_Icon\r\n name=\"mdi:magnify\"\r\n :size=\"16\"\r\n />\r\n </template>\r\n </NInput>\r\n </div>\r\n\r\n <!-- 联系人列表 -->\r\n <div class=\"c-chat__contact-list\">\r\n <div\r\n v-for=\"contact in filteredContacts\"\r\n :key=\"contact.id\"\r\n class=\"c-chat__contact\"\r\n :class=\"{ 'is-active': contact.id === currentId }\"\r\n @click=\"handleSelectContact(contact.id)\"\r\n >\r\n <div class=\"c-chat__contact-avatar\">\r\n <img\r\n v-if=\"contact.avatar\"\r\n :src=\"contact.avatar\"\r\n :alt=\"contact.name\"\r\n class=\"c-chat__avatar\"\r\n />\r\n <div\r\n v-else\r\n class=\"c-chat__avatar-fallback\"\r\n >\r\n {{ contact.name.charAt(0) }}\r\n </div>\r\n <span\r\n v-if=\"contact.online\"\r\n class=\"c-chat__online-dot\"\r\n />\r\n </div>\r\n\r\n <div class=\"c-chat__contact-info\">\r\n <div class=\"c-chat__contact-name\">\r\n <span>{{ contact.name }}</span>\r\n <span\r\n v-if=\"contact.lastTime\"\r\n class=\"c-chat__contact-time\"\r\n >\r\n {{ formatContactTime(contact.lastTime) }}\r\n </span>\r\n </div>\r\n <div\r\n v-if=\"contact.lastMessage\"\r\n class=\"c-chat__contact-last\"\r\n >\r\n {{ contact.lastMessage }}\r\n </div>\r\n </div>\r\n\r\n <span\r\n v-if=\"contact.unread\"\r\n class=\"c-chat__unread\"\r\n >\r\n {{ contact.unread > 99 ? '99+' : contact.unread }}\r\n </span>\r\n </div>\r\n </div>\r\n </aside>\r\n\r\n <!-- ===== 主聊天区域 ===== -->\r\n <div\r\n v-if=\"currentContact || !mergedProps.showContacts\"\r\n class=\"c-chat__main\"\r\n >\r\n <!-- 顶栏 -->\r\n <div class=\"c-chat__header\">\r\n <slot name=\"header\">\r\n <span>{{ currentContact?.name || mergedProps.title }}</span>\r\n <span\r\n v-if=\"currentContact?.online\"\r\n class=\"c-chat__header-status\"\r\n >\r\n 在线\r\n </span>\r\n </slot>\r\n </div>\r\n\r\n <!-- 消息列表 -->\r\n <div\r\n ref=\"messagesRef\"\r\n class=\"c-chat__messages\"\r\n @scroll=\"handleMessagesScroll\"\r\n >\r\n <!-- 加载更多 -->\r\n <div\r\n v-if=\"mergedProps.loadingHistory\"\r\n class=\"c-chat__load-more\"\r\n >\r\n <NSpin size=\"small\" />\r\n </div>\r\n\r\n <!-- 消息为空 -->\r\n <div\r\n v-if=\"!messageList.length && !mergedProps.loadingHistory\"\r\n class=\"c-chat__empty\"\r\n >\r\n 暂无消息,发送第一条吧\r\n </div>\r\n\r\n <!-- 消息列表 -->\r\n <template\r\n v-for=\"(msg, idx) in messageList\"\r\n :key=\"msg.id\"\r\n >\r\n <!-- 时间分割 -->\r\n <div\r\n v-if=\"shouldShowTimeDivider(msg, idx)\"\r\n class=\"c-chat__time-divider\"\r\n >\r\n {{ formatMessageTime(msg.timestamp) }}\r\n </div>\r\n\r\n <!-- 消息条目 -->\r\n <div\r\n class=\"c-chat__msg\"\r\n :class=\"`is-${msg.sender}`\"\r\n >\r\n <!-- 头像 -->\r\n <div\r\n v-if=\"msg.sender !== 'system'\"\r\n class=\"c-chat__msg-avatar\"\r\n >\r\n <img\r\n v-if=\"getAvatar(msg)\"\r\n :src=\"getAvatar(msg)\"\r\n class=\"c-chat__avatar\"\r\n />\r\n <div\r\n v-else\r\n class=\"c-chat__avatar-fallback\"\r\n >\r\n {{ getUsername(msg).charAt(0) }}\r\n </div>\r\n </div>\r\n\r\n <!-- 消息体 -->\r\n <div class=\"c-chat__msg-body\">\r\n <span\r\n v-if=\"msg.sender === 'other' && msg.username\"\r\n class=\"c-chat__msg-username\"\r\n >\r\n {{ msg.username }}\r\n </span>\r\n\r\n <!-- 文本消息 -->\r\n <div\r\n v-if=\"msg.type === 'text' || msg.type === 'system'\"\r\n class=\"c-chat__bubble\"\r\n >\r\n {{ msg.content }}\r\n </div>\r\n\r\n <!-- 图片消息 -->\r\n <img\r\n v-else-if=\"msg.type === 'image'\"\r\n :src=\"msg.content\"\r\n class=\"c-chat__msg-image\"\r\n @click=\"emit('image-preview', msg.content)\"\r\n />\r\n\r\n <!-- 文件消息 -->\r\n <div\r\n v-else-if=\"msg.type === 'file'\"\r\n class=\"c-chat__msg-file\"\r\n @click=\"emit('file-click', msg)\"\r\n >\r\n <C_Icon\r\n name=\"mdi:file-document-outline\"\r\n :size=\"28\"\r\n />\r\n <div class=\"c-chat__file-info\">\r\n <span class=\"c-chat__file-name\">{{\r\n msg.fileName || '文件'\r\n }}</span>\r\n <span\r\n v-if=\"msg.fileSize\"\r\n class=\"c-chat__file-size\"\r\n >\r\n {{ formatFileSize(msg.fileSize) }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <!-- 时间戳 -->\r\n <span\r\n v-if=\"mergedProps.showTimestamp && msg.sender !== 'system'\"\r\n class=\"c-chat__msg-time\"\r\n >\r\n {{ formatTime(msg.timestamp) }}\r\n </span>\r\n\r\n <!-- 发送状态 -->\r\n <span\r\n v-if=\"msg.sender === 'self' && msg.status\"\r\n class=\"c-chat__msg-status\"\r\n :class=\"`is-${msg.status}`\"\r\n @click=\"msg.status === 'failed' && emit('resend', msg)\"\r\n >\r\n {{\r\n msg.status === 'sending'\r\n ? '发送中...'\r\n : msg.status === 'failed'\r\n ? '发送失败,点击重试'\r\n : ''\r\n }}\r\n </span>\r\n </div>\r\n </div>\r\n </template>\r\n </div>\r\n\r\n <!-- 输入区域 -->\r\n <div class=\"c-chat__input-area\">\r\n <div class=\"c-chat__input-actions\">\r\n <slot name=\"input-actions\">\r\n <NButton\r\n quaternary\r\n circle\r\n size=\"small\"\r\n @click=\"emit('emoji-click')\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n name=\"mdi:emoticon-happy-outline\"\r\n :size=\"18\"\r\n />\r\n </template>\r\n </NButton>\r\n <NButton\r\n quaternary\r\n circle\r\n size=\"small\"\r\n @click=\"emit('file-upload')\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n name=\"mdi:paperclip\"\r\n :size=\"18\"\r\n />\r\n </template>\r\n </NButton>\r\n </slot>\r\n </div>\r\n\r\n <div class=\"c-chat__input-wrap\">\r\n <NInput\r\n v-model:value=\"inputText\"\r\n :placeholder=\"mergedProps.placeholder\"\r\n type=\"textarea\"\r\n :autosize=\"{ minRows: 1, maxRows: 4 }\"\r\n @keydown.enter.exact.prevent=\"handleSend\"\r\n />\r\n </div>\r\n\r\n <NButton\r\n v-if=\"mergedProps.showSendBtn\"\r\n type=\"primary\"\r\n :disabled=\"!inputText.trim()\"\r\n class=\"c-chat__send-btn\"\r\n @click=\"handleSend\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n name=\"mdi:send\"\r\n :size=\"16\"\r\n />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <!-- ===== 未选中联系人空态 ===== -->\r\n <div\r\n v-else\r\n class=\"c-chat__main c-chat__no-chat\"\r\n >\r\n <C_Icon\r\n name=\"mdi:chat-processing-outline\"\r\n :size=\"48\"\r\n class=\"c-chat__no-chat-icon\"\r\n />\r\n <span>选择一个会话开始聊天</span>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed, nextTick, ref, watch } from 'vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import {\r\n DEFAULT_CHAT_PROPS,\r\n type ChatProps,\r\n type ChatMessage,\r\n type ChatContact,\r\n } from './types'\r\n\r\n // ===== Props & Emits =====\r\n const props = withDefaults(defineProps<ChatProps>(), {\r\n contacts: () => [],\r\n messages: () => [],\r\n currentContactId: '',\r\n placeholder: DEFAULT_CHAT_PROPS.placeholder,\r\n showContacts: DEFAULT_CHAT_PROPS.showContacts,\r\n showTimestamp: DEFAULT_CHAT_PROPS.showTimestamp,\r\n showSendBtn: DEFAULT_CHAT_PROPS.showSendBtn,\r\n loadingHistory: DEFAULT_CHAT_PROPS.loadingHistory,\r\n title: DEFAULT_CHAT_PROPS.title,\r\n selfAvatar: '',\r\n selfName: '',\r\n })\r\n\r\n const emit = defineEmits<{\r\n (e: 'send', content: string): void\r\n (e: 'select-contact', id: string): void\r\n (e: 'load-more'): void\r\n (e: 'image-preview', url: string): void\r\n (e: 'file-click', msg: ChatMessage): void\r\n (e: 'file-upload'): void\r\n (e: 'emoji-click'): void\r\n (e: 'resend', msg: ChatMessage): void\r\n }>()\r\n\r\n // ===== 合并 Props =====\r\n const mergedProps = computed(() => ({\r\n ...DEFAULT_CHAT_PROPS,\r\n ...props,\r\n }))\r\n\r\n // ===== 内部状态 =====\r\n const inputText = ref('')\r\n const searchKeyword = ref('')\r\n const messagesRef = ref<HTMLElement | null>(null)\r\n\r\n // ===== 计算属性 =====\r\n const contactList = computed<ChatContact[]>(() => props.contacts ?? [])\r\n const messageList = computed<ChatMessage[]>(() => props.messages ?? [])\r\n\r\n const currentId = computed(\r\n () => props.currentContactId || contactList.value[0]?.id || ''\r\n )\r\n\r\n const currentContact = computed(() =>\r\n contactList.value.find(c => c.id === currentId.value)\r\n )\r\n\r\n const filteredContacts = computed(() => {\r\n if (!searchKeyword.value) return contactList.value\r\n const kw = searchKeyword.value.toLowerCase()\r\n return contactList.value.filter(c => c.name.toLowerCase().includes(kw))\r\n })\r\n\r\n // ===== 方法 =====\r\n const handleSelectContact = (id: string) => {\r\n emit('select-contact', id)\r\n }\r\n\r\n const handleSend = () => {\r\n const text = inputText.value.trim()\r\n if (!text) return\r\n emit('send', text)\r\n inputText.value = ''\r\n }\r\n\r\n /** 自动滚动到底部 */\r\n const scrollToBottom = () => {\r\n nextTick(() => {\r\n if (messagesRef.value) {\r\n messagesRef.value.scrollTop = messagesRef.value.scrollHeight\r\n }\r\n })\r\n }\r\n\r\n // 消息变化时自动滚动\r\n watch(\r\n () => props.messages?.length,\r\n () => scrollToBottom(),\r\n { flush: 'post' }\r\n )\r\n\r\n /** 滚动到顶部时触发加载更多 */\r\n const handleMessagesScroll = () => {\r\n if (\r\n messagesRef.value &&\r\n messagesRef.value.scrollTop === 0 &&\r\n !mergedProps.value.loadingHistory\r\n ) {\r\n emit('load-more')\r\n }\r\n }\r\n\r\n // ===== 工具函数 =====\r\n const getAvatar = (msg: ChatMessage) => {\r\n if (msg.sender === 'self') return props.selfAvatar || msg.avatar\r\n return msg.avatar\r\n }\r\n\r\n const getUsername = (msg: ChatMessage) => {\r\n if (msg.sender === 'self') return props.selfName || msg.username || 'Me'\r\n return msg.username || '未知'\r\n }\r\n\r\n const formatTime = (ts: string | number) => {\r\n const d = new Date(ts)\r\n return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`\r\n }\r\n\r\n const formatMessageTime = (ts: string | number) => {\r\n const d = new Date(ts)\r\n const now = new Date()\r\n const isToday =\r\n d.getFullYear() === now.getFullYear() &&\r\n d.getMonth() === now.getMonth() &&\r\n d.getDate() === now.getDate()\r\n if (isToday) return formatTime(ts)\r\n return `${d.getMonth() + 1}/${d.getDate()} ${formatTime(ts)}`\r\n }\r\n\r\n const formatContactTime = (ts: string | number) => {\r\n const d = new Date(ts)\r\n const now = new Date()\r\n const diff = now.getTime() - d.getTime()\r\n if (diff < 60_000) return '刚刚'\r\n if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}分钟前`\r\n if (diff < 86_400_000) return formatTime(ts)\r\n return `${d.getMonth() + 1}/${d.getDate()}`\r\n }\r\n\r\n const formatFileSize = (bytes: number) => {\r\n if (bytes < 1024) return `${bytes}B`\r\n if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)}KB`\r\n return `${(bytes / 1048576).toFixed(1)}MB`\r\n }\r\n\r\n /** 是否显示时间分割线(两条消息间超过 5 分钟) */\r\n const shouldShowTimeDivider = (msg: ChatMessage, idx: number) => {\r\n if (idx === 0) return true\r\n const prev = messageList.value[idx - 1]\r\n const curr = new Date(msg.timestamp).getTime()\r\n const prevTime = new Date(prev.timestamp).getTime()\r\n return curr - prevTime > 5 * 60 * 1000\r\n }\r\n\r\n // ===== 暴露方法 =====\r\n defineExpose({ scrollToBottom })\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n","<!--\r\n * @Description: C_Chat — 通用聊天组件\r\n *\r\n * 支持联系人列表 + 消息气泡 + 输入框,可用于 IM、客服、AI 对话等场景。\r\n * 通过 CSS 变量可完全自定义主题风格。\r\n *\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\r\n-->\r\n<template>\r\n <div class=\"c-chat\">\r\n <!-- ===== 联系人侧栏 ===== -->\r\n <aside\r\n v-if=\"mergedProps.showContacts && contactList.length\"\r\n class=\"c-chat__sidebar\"\r\n >\r\n <div class=\"c-chat__sidebar-header\">\r\n <C_Icon\r\n name=\"mdi:chat-outline\"\r\n :size=\"20\"\r\n />\r\n 会话\r\n </div>\r\n\r\n <!-- 搜索框 -->\r\n <div class=\"c-chat__search\">\r\n <NInput\r\n v-model:value=\"searchKeyword\"\r\n placeholder=\"搜索联系人\"\r\n clearable\r\n size=\"small\"\r\n >\r\n <template #prefix>\r\n <C_Icon\r\n name=\"mdi:magnify\"\r\n :size=\"16\"\r\n />\r\n </template>\r\n </NInput>\r\n </div>\r\n\r\n <!-- 联系人列表 -->\r\n <div class=\"c-chat__contact-list\">\r\n <div\r\n v-for=\"contact in filteredContacts\"\r\n :key=\"contact.id\"\r\n class=\"c-chat__contact\"\r\n :class=\"{ 'is-active': contact.id === currentId }\"\r\n @click=\"handleSelectContact(contact.id)\"\r\n >\r\n <div class=\"c-chat__contact-avatar\">\r\n <img\r\n v-if=\"contact.avatar\"\r\n :src=\"contact.avatar\"\r\n :alt=\"contact.name\"\r\n class=\"c-chat__avatar\"\r\n />\r\n <div\r\n v-else\r\n class=\"c-chat__avatar-fallback\"\r\n >\r\n {{ contact.name.charAt(0) }}\r\n </div>\r\n <span\r\n v-if=\"contact.online\"\r\n class=\"c-chat__online-dot\"\r\n />\r\n </div>\r\n\r\n <div class=\"c-chat__contact-info\">\r\n <div class=\"c-chat__contact-name\">\r\n <span>{{ contact.name }}</span>\r\n <span\r\n v-if=\"contact.lastTime\"\r\n class=\"c-chat__contact-time\"\r\n >\r\n {{ formatContactTime(contact.lastTime) }}\r\n </span>\r\n </div>\r\n <div\r\n v-if=\"contact.lastMessage\"\r\n class=\"c-chat__contact-last\"\r\n >\r\n {{ contact.lastMessage }}\r\n </div>\r\n </div>\r\n\r\n <span\r\n v-if=\"contact.unread\"\r\n class=\"c-chat__unread\"\r\n >\r\n {{ contact.unread > 99 ? '99+' : contact.unread }}\r\n </span>\r\n </div>\r\n </div>\r\n </aside>\r\n\r\n <!-- ===== 主聊天区域 ===== -->\r\n <div\r\n v-if=\"currentContact || !mergedProps.showContacts\"\r\n class=\"c-chat__main\"\r\n >\r\n <!-- 顶栏 -->\r\n <div class=\"c-chat__header\">\r\n <slot name=\"header\">\r\n <span>{{ currentContact?.name || mergedProps.title }}</span>\r\n <span\r\n v-if=\"currentContact?.online\"\r\n class=\"c-chat__header-status\"\r\n >\r\n 在线\r\n </span>\r\n </slot>\r\n </div>\r\n\r\n <!-- 消息列表 -->\r\n <div\r\n ref=\"messagesRef\"\r\n class=\"c-chat__messages\"\r\n @scroll=\"handleMessagesScroll\"\r\n >\r\n <!-- 加载更多 -->\r\n <div\r\n v-if=\"mergedProps.loadingHistory\"\r\n class=\"c-chat__load-more\"\r\n >\r\n <NSpin size=\"small\" />\r\n </div>\r\n\r\n <!-- 消息为空 -->\r\n <div\r\n v-if=\"!messageList.length && !mergedProps.loadingHistory\"\r\n class=\"c-chat__empty\"\r\n >\r\n 暂无消息,发送第一条吧\r\n </div>\r\n\r\n <!-- 消息列表 -->\r\n <template\r\n v-for=\"(msg, idx) in messageList\"\r\n :key=\"msg.id\"\r\n >\r\n <!-- 时间分割 -->\r\n <div\r\n v-if=\"shouldShowTimeDivider(msg, idx)\"\r\n class=\"c-chat__time-divider\"\r\n >\r\n {{ formatMessageTime(msg.timestamp) }}\r\n </div>\r\n\r\n <!-- 消息条目 -->\r\n <div\r\n class=\"c-chat__msg\"\r\n :class=\"`is-${msg.sender}`\"\r\n >\r\n <!-- 头像 -->\r\n <div\r\n v-if=\"msg.sender !== 'system'\"\r\n class=\"c-chat__msg-avatar\"\r\n >\r\n <img\r\n v-if=\"getAvatar(msg)\"\r\n :src=\"getAvatar(msg)\"\r\n class=\"c-chat__avatar\"\r\n />\r\n <div\r\n v-else\r\n class=\"c-chat__avatar-fallback\"\r\n >\r\n {{ getUsername(msg).charAt(0) }}\r\n </div>\r\n </div>\r\n\r\n <!-- 消息体 -->\r\n <div class=\"c-chat__msg-body\">\r\n <span\r\n v-if=\"msg.sender === 'other' && msg.username\"\r\n class=\"c-chat__msg-username\"\r\n >\r\n {{ msg.username }}\r\n </span>\r\n\r\n <!-- 文本消息 -->\r\n <div\r\n v-if=\"msg.type === 'text' || msg.type === 'system'\"\r\n class=\"c-chat__bubble\"\r\n >\r\n {{ msg.content }}\r\n </div>\r\n\r\n <!-- 图片消息 -->\r\n <img\r\n v-else-if=\"msg.type === 'image'\"\r\n :src=\"msg.content\"\r\n class=\"c-chat__msg-image\"\r\n @click=\"emit('image-preview', msg.content)\"\r\n />\r\n\r\n <!-- 文件消息 -->\r\n <div\r\n v-else-if=\"msg.type === 'file'\"\r\n class=\"c-chat__msg-file\"\r\n @click=\"emit('file-click', msg)\"\r\n >\r\n <C_Icon\r\n name=\"mdi:file-document-outline\"\r\n :size=\"28\"\r\n />\r\n <div class=\"c-chat__file-info\">\r\n <span class=\"c-chat__file-name\">{{\r\n msg.fileName || '文件'\r\n }}</span>\r\n <span\r\n v-if=\"msg.fileSize\"\r\n class=\"c-chat__file-size\"\r\n >\r\n {{ formatFileSize(msg.fileSize) }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <!-- 时间戳 -->\r\n <span\r\n v-if=\"mergedProps.showTimestamp && msg.sender !== 'system'\"\r\n class=\"c-chat__msg-time\"\r\n >\r\n {{ formatTime(msg.timestamp) }}\r\n </span>\r\n\r\n <!-- 发送状态 -->\r\n <span\r\n v-if=\"msg.sender === 'self' && msg.status\"\r\n class=\"c-chat__msg-status\"\r\n :class=\"`is-${msg.status}`\"\r\n @click=\"msg.status === 'failed' && emit('resend', msg)\"\r\n >\r\n {{\r\n msg.status === 'sending'\r\n ? '发送中...'\r\n : msg.status === 'failed'\r\n ? '发送失败,点击重试'\r\n : ''\r\n }}\r\n </span>\r\n </div>\r\n </div>\r\n </template>\r\n </div>\r\n\r\n <!-- 输入区域 -->\r\n <div class=\"c-chat__input-area\">\r\n <div class=\"c-chat__input-actions\">\r\n <slot name=\"input-actions\">\r\n <NButton\r\n quaternary\r\n circle\r\n size=\"small\"\r\n @click=\"emit('emoji-click')\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n name=\"mdi:emoticon-happy-outline\"\r\n :size=\"18\"\r\n />\r\n </template>\r\n </NButton>\r\n <NButton\r\n quaternary\r\n circle\r\n size=\"small\"\r\n @click=\"emit('file-upload')\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n name=\"mdi:paperclip\"\r\n :size=\"18\"\r\n />\r\n </template>\r\n </NButton>\r\n </slot>\r\n </div>\r\n\r\n <div class=\"c-chat__input-wrap\">\r\n <NInput\r\n v-model:value=\"inputText\"\r\n :placeholder=\"mergedProps.placeholder\"\r\n type=\"textarea\"\r\n :autosize=\"{ minRows: 1, maxRows: 4 }\"\r\n @keydown.enter.exact.prevent=\"handleSend\"\r\n />\r\n </div>\r\n\r\n <NButton\r\n v-if=\"mergedProps.showSendBtn\"\r\n type=\"primary\"\r\n :disabled=\"!inputText.trim()\"\r\n class=\"c-chat__send-btn\"\r\n @click=\"handleSend\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n name=\"mdi:send\"\r\n :size=\"16\"\r\n />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <!-- ===== 未选中联系人空态 ===== -->\r\n <div\r\n v-else\r\n class=\"c-chat__main c-chat__no-chat\"\r\n >\r\n <C_Icon\r\n name=\"mdi:chat-processing-outline\"\r\n :size=\"48\"\r\n class=\"c-chat__no-chat-icon\"\r\n />\r\n <span>选择一个会话开始聊天</span>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed, nextTick, ref, watch } from 'vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import {\r\n DEFAULT_CHAT_PROPS,\r\n type ChatProps,\r\n type ChatMessage,\r\n type ChatContact,\r\n } from './types'\r\n\r\n // ===== Props & Emits =====\r\n const props = withDefaults(defineProps<ChatProps>(), {\r\n contacts: () => [],\r\n messages: () => [],\r\n currentContactId: '',\r\n placeholder: DEFAULT_CHAT_PROPS.placeholder,\r\n showContacts: DEFAULT_CHAT_PROPS.showContacts,\r\n showTimestamp: DEFAULT_CHAT_PROPS.showTimestamp,\r\n showSendBtn: DEFAULT_CHAT_PROPS.showSendBtn,\r\n loadingHistory: DEFAULT_CHAT_PROPS.loadingHistory,\r\n title: DEFAULT_CHAT_PROPS.title,\r\n selfAvatar: '',\r\n selfName: '',\r\n })\r\n\r\n const emit = defineEmits<{\r\n (e: 'send', content: string): void\r\n (e: 'select-contact', id: string): void\r\n (e: 'load-more'): void\r\n (e: 'image-preview', url: string): void\r\n (e: 'file-click', msg: ChatMessage): void\r\n (e: 'file-upload'): void\r\n (e: 'emoji-click'): void\r\n (e: 'resend', msg: ChatMessage): void\r\n }>()\r\n\r\n // ===== 合并 Props =====\r\n const mergedProps = computed(() => ({\r\n ...DEFAULT_CHAT_PROPS,\r\n ...props,\r\n }))\r\n\r\n // ===== 内部状态 =====\r\n const inputText = ref('')\r\n const searchKeyword = ref('')\r\n const messagesRef = ref<HTMLElement | null>(null)\r\n\r\n // ===== 计算属性 =====\r\n const contactList = computed<ChatContact[]>(() => props.contacts ?? [])\r\n const messageList = computed<ChatMessage[]>(() => props.messages ?? [])\r\n\r\n const currentId = computed(\r\n () => props.currentContactId || contactList.value[0]?.id || ''\r\n )\r\n\r\n const currentContact = computed(() =>\r\n contactList.value.find(c => c.id === currentId.value)\r\n )\r\n\r\n const filteredContacts = computed(() => {\r\n if (!searchKeyword.value) return contactList.value\r\n const kw = searchKeyword.value.toLowerCase()\r\n return contactList.value.filter(c => c.name.toLowerCase().includes(kw))\r\n })\r\n\r\n // ===== 方法 =====\r\n const handleSelectContact = (id: string) => {\r\n emit('select-contact', id)\r\n }\r\n\r\n const handleSend = () => {\r\n const text = inputText.value.trim()\r\n if (!text) return\r\n emit('send', text)\r\n inputText.value = ''\r\n }\r\n\r\n /** 自动滚动到底部 */\r\n const scrollToBottom = () => {\r\n nextTick(() => {\r\n if (messagesRef.value) {\r\n messagesRef.value.scrollTop = messagesRef.value.scrollHeight\r\n }\r\n })\r\n }\r\n\r\n // 消息变化时自动滚动\r\n watch(\r\n () => props.messages?.length,\r\n () => scrollToBottom(),\r\n { flush: 'post' }\r\n )\r\n\r\n /** 滚动到顶部时触发加载更多 */\r\n const handleMessagesScroll = () => {\r\n if (\r\n messagesRef.value &&\r\n messagesRef.value.scrollTop === 0 &&\r\n !mergedProps.value.loadingHistory\r\n ) {\r\n emit('load-more')\r\n }\r\n }\r\n\r\n // ===== 工具函数 =====\r\n const getAvatar = (msg: ChatMessage) => {\r\n if (msg.sender === 'self') return props.selfAvatar || msg.avatar\r\n return msg.avatar\r\n }\r\n\r\n const getUsername = (msg: ChatMessage) => {\r\n if (msg.sender === 'self') return props.selfName || msg.username || 'Me'\r\n return msg.username || '未知'\r\n }\r\n\r\n const formatTime = (ts: string | number) => {\r\n const d = new Date(ts)\r\n return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`\r\n }\r\n\r\n const formatMessageTime = (ts: string | number) => {\r\n const d = new Date(ts)\r\n const now = new Date()\r\n const isToday =\r\n d.getFullYear() === now.getFullYear() &&\r\n d.getMonth() === now.getMonth() &&\r\n d.getDate() === now.getDate()\r\n if (isToday) return formatTime(ts)\r\n return `${d.getMonth() + 1}/${d.getDate()} ${formatTime(ts)}`\r\n }\r\n\r\n const formatContactTime = (ts: string | number) => {\r\n const d = new Date(ts)\r\n const now = new Date()\r\n const diff = now.getTime() - d.getTime()\r\n if (diff < 60_000) return '刚刚'\r\n if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}分钟前`\r\n if (diff < 86_400_000) return formatTime(ts)\r\n return `${d.getMonth() + 1}/${d.getDate()}`\r\n }\r\n\r\n const formatFileSize = (bytes: number) => {\r\n if (bytes < 1024) return `${bytes}B`\r\n if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)}KB`\r\n return `${(bytes / 1048576).toFixed(1)}MB`\r\n }\r\n\r\n /** 是否显示时间分割线(两条消息间超过 5 分钟) */\r\n const shouldShowTimeDivider = (msg: ChatMessage, idx: number) => {\r\n if (idx === 0) return true\r\n const prev = messageList.value[idx - 1]\r\n const curr = new Date(msg.timestamp).getTime()\r\n const prevTime = new Date(prev.timestamp).getTime()\r\n return curr - prevTime > 5 * 60 * 1000\r\n }\r\n\r\n // ===== 暴露方法 =====\r\n defineExpose({ scrollToBottom })\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n"],"mappings":"4eAwFA,MAAa,EAUT,CACF,YAAa,UACb,aAAc,GACd,cAAe,GACf,YAAa,GACb,eAAgB,GAChB,MAAO,KACR,uuDGqOC,IAAM,EAAQ,EAcR,EAAO,EAYP,EAAc,OAAgB,CAClC,GAAG,EACH,GAAG,EACJ,EAAE,CAGG,EAAY,EAAI,GAAG,CACnB,EAAgB,EAAI,GAAG,CACvB,EAAc,EAAwB,KAAK,CAG3C,EAAc,MAA8B,EAAM,UAAY,EAAE,CAAC,CACjE,EAAc,MAA8B,EAAM,UAAY,EAAE,CAAC,CAEjE,EAAY,MACV,EAAM,kBAAoB,EAAY,MAAM,IAAI,IAAM,GAC7D,CAEK,EAAiB,MACrB,EAAY,MAAM,KAAK,GAAK,EAAE,KAAO,EAAU,MAAM,CACtD,CAEK,GAAmB,MAAe,CACtC,GAAI,CAAC,EAAc,MAAO,OAAO,EAAY,MAC7C,IAAM,EAAK,EAAc,MAAM,aAAa,CAC5C,OAAO,EAAY,MAAM,OAAO,GAAK,EAAE,KAAK,aAAa,CAAC,SAAS,EAAG,CAAC,EACvE,CAGI,GAAuB,GAAe,CAC1C,EAAK,iBAAkB,EAAG,EAGtB,MAAmB,CACvB,IAAM,EAAO,EAAU,MAAM,MAAM,CAC9B,IACL,EAAK,OAAQ,EAAK,CAClB,EAAU,MAAQ,KAId,MAAuB,CAC3B,MAAe,CACT,EAAY,QACd,EAAY,MAAM,UAAY,EAAY,MAAM,eAElD,EAIJ,OACQ,EAAM,UAAU,WAChB,GAAgB,CACtB,CAAE,MAAO,OAAQ,CAClB,CAGD,IAAM,OAA6B,CAE/B,EAAY,OACZ,EAAY,MAAM,YAAc,GAChC,CAAC,EAAY,MAAM,gBAEnB,EAAK,YAAY,EAKf,EAAa,GACb,EAAI,SAAW,QAAe,EAAM,YACjC,EAAI,OAGP,GAAe,GACf,EAAI,SAAW,OAAe,EAAM,UAAY,EAAI,UAAY,KAC7D,EAAI,UAAY,KAGnB,EAAc,GAAwB,CAC1C,IAAM,EAAI,IAAI,KAAK,EAAG,CACtB,MAAO,GAAG,EAAE,UAAU,CAAC,UAAU,CAAC,SAAS,EAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,UAAU,CAAC,SAAS,EAAG,IAAI,IAG5F,GAAqB,GAAwB,CACjD,IAAM,EAAI,IAAI,KAAK,EAAG,CAChB,EAAM,IAAI,KAMhB,OAJE,EAAE,aAAa,GAAK,EAAI,aAAa,EACrC,EAAE,UAAU,GAAK,EAAI,UAAU,EAC/B,EAAE,SAAS,GAAK,EAAI,SAAS,CACX,EAAW,EAAG,CAC3B,GAAG,EAAE,UAAU,CAAG,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,EAAW,EAAG,IAGvD,GAAqB,GAAwB,CACjD,IAAM,EAAI,IAAI,KAAK,EAAG,CAEhB,EADM,IAAI,MAAM,CACL,SAAS,CAAG,EAAE,SAAS,CAIxC,OAHI,EAAO,IAAe,KACtB,EAAO,KAAkB,GAAG,KAAK,MAAM,EAAO,IAAO,CAAC,KACtD,EAAO,MAAmB,EAAW,EAAG,CACrC,GAAG,EAAE,UAAU,CAAG,EAAE,GAAG,EAAE,SAAS,IAGrC,GAAkB,GAClB,EAAQ,KAAa,GAAG,EAAM,GAC9B,EAAQ,QAAgB,IAAI,EAAQ,MAAM,QAAQ,EAAE,CAAC,IAClD,IAAI,EAAQ,SAAS,QAAQ,EAAE,CAAC,IAInC,IAAyB,EAAkB,IAAgB,CAC/D,GAAI,IAAQ,EAAG,MAAO,GACtB,IAAM,EAAO,EAAY,MAAM,EAAM,GAGrC,OAFa,IAAI,KAAK,EAAI,UAAU,CAAC,SAAS,CAC7B,IAAI,KAAK,EAAK,UAAU,CAAC,SAAS,CAC1B,IAAS,YAIpC,EAAa,CAAE,iBAAgB,CAAC,sCAvdhC,EAuTM,MAvTN,GAuTM,CAtTJ,EAAA,sBAA0B,CAElB,EAAA,MAAY,cAAgB,EAAA,MAAY,QAAA,GAAA,CADhD,EAmFQ,QAnFR,GAmFQ,CA/EN,EAMM,MANN,GAMM,CALJ,EAGE,EAAA,CAFA,KAAK,mBACJ,KAAM,cACP,OAEJ,GAAA,CAAA,CAAA,CAEA,EAAA,QAAY,CACZ,EAcM,MAdN,GAcM,CAbJ,EAYS,EAAA,CAXC,MAAO,EAAA,iCAAA,EAAa,MAAA,EAC5B,YAAY,QACZ,UAAA,GACA,KAAK,UAEM,OAAM,MAIb,CAHF,EAGE,EAAA,CAFA,KAAK,cACJ,KAAM,2BAMf,EAAA,UAAc,CACd,EAoDM,MApDN,GAoDM,EAAA,EAAA,GAAA,CAnDJ,EAkDM,EAAA,KAAA,EAjDc,GAAA,MAAX,QADT,EAkDM,MAAA,CAhDH,IAAK,EAAQ,GACd,MAAK,EAAA,CAAC,kBAAiB,CAAA,YACA,EAAQ,KAAO,EAAA,MAAS,CAAA,CAAA,CAC9C,QAAK,GAAE,GAAoB,EAAQ,GAAE,GAEtC,EAiBM,MAjBN,GAiBM,CAfI,EAAQ,QAAA,GAAA,CADhB,EAKE,MAAA,OAHC,IAAK,EAAQ,OACb,IAAK,EAAQ,KACd,MAAM,mCAER,EAKM,MALN,GAKM,EADD,EAAQ,KAAK,OAAM,EAAA,CAAA,CAAA,EAAA,EAGhB,EAAQ,QAAA,GAAA,CADhB,EAGE,OAHF,GAGE,EAAA,EAAA,OAAA,GAAA,CAAA,CAAA,CAGJ,EAgBM,MAhBN,EAgBM,CAfJ,EAQM,MARN,EAQM,CAPJ,EAA+B,OAAA,KAAA,EAAtB,EAAQ,KAAI,CAAA,EAAA,CAEb,EAAQ,UAAA,GAAA,CADhB,EAKO,OALP,EAKO,EADF,GAAkB,EAAQ,SAAQ,CAAA,CAAA,EAAA,EAAA,EAAA,OAAA,GAAA,CAAA,CAAA,CAIjC,EAAQ,aAAA,GAAA,CADhB,EAKM,MALN,EAKM,EADD,EAAQ,YAAW,CAAA,EAAA,EAAA,EAAA,OAAA,GAAA,CAAA,CAAA,CAKlB,EAAQ,QAAA,GAAA,CADhB,EAKO,OALP,EAKO,EADF,EAAQ,OAAM,GAAA,MAAgB,EAAQ,OAAM,CAAA,EAAA,EAAA,EAAA,OAAA,GAAA,mCAMvD,EAAA,sBAA0B,CAElB,EAAA,OAAc,CAAK,EAAA,MAAY,cAAA,GAAA,CADvC,EAiNM,MAjNN,EAiNM,CA7MJ,EAAA,OAAW,CACX,EAUM,MAVN,EAUM,CATJ,EAQO,EAAA,OAAA,SAAA,EAAA,KAAA,CAPL,EAA4D,OAAA,KAAA,EAAnD,EAAA,OAAgB,MAAQ,EAAA,MAAY,MAAK,CAAA,EAAA,CAE1C,EAAA,OAAgB,QAAA,GAAA,CADxB,EAKO,OALP,EAGC,OAED,EAAA,EAAA,OAAA,GAAA,CAAA,CAAA,GAAA,CAAA,CAAA,CAIJ,EAAA,SAAa,CACb,EAmIM,MAAA,SAlIA,cAAJ,IAAI,EACJ,MAAM,mBACL,SAAQ,KAET,EAAA,SAAa,CAEL,EAAA,MAAY,gBAAA,GAAA,CADpB,EAKM,MALN,EAKM,CADJ,EAAsB,EAAA,CAAf,KAAK,QAAO,CAAA,CAAA,CAAA,EAAA,EAAA,OAAA,GAAA,CAGrB,EAAA,SAAa,EAEJ,EAAA,MAAY,QAAM,CAAK,EAAA,MAAY,gBAAA,GAAA,CAD5C,EAKM,MALN,EAGC,gBAED,EAAA,EAAA,OAAA,GAAA,CAEA,EAAA,SAAa,QACb,EA4GW,EAAA,KAAA,EA3GY,EAAA,OAAb,EAAK,kBACP,EAAI,GAAA,CAAA,CAEV,EAAA,SAAa,CAEL,GAAsB,EAAK,EAAG,EAAA,GAAA,CADtC,EAKM,MALN,EAKM,EADD,GAAkB,EAAI,UAAS,CAAA,CAAA,EAAA,EAAA,EAAA,OAAA,GAAA,CAGpC,EAAA,SAAa,CACb,EA8FM,MAAA,CA7FJ,MAAK,EAAA,CAAC,cAAa,MACL,EAAI,SAAM,CAAA,CAAA,CAAA,CAExB,EAAA,OAAW,CAEH,EAAI,SAAM,SAYY,EAAA,OAAA,GAAA,EAZZ,GAAA,CADlB,EAeM,MAfN,EAeM,CAVI,EAAU,EAAG,EAAA,GAAA,CADrB,EAIE,MAAA,OAFC,IAAK,EAAU,EAAG,CACnB,MAAM,kCAER,EAKM,MALN,EAKM,EADD,GAAY,EAAG,CAAE,OAAM,EAAA,CAAA,CAAA,EAAA,EAAA,CAAA,EAI9B,EAAA,QAAY,CACZ,EAsEM,MAtEN,EAsEM,CApEI,EAAI,SAAM,SAAgB,EAAI,UAAA,GAAA,CADtC,EAKO,OALP,EAKO,EADF,EAAI,SAAQ,CAAA,EAAA,EAAA,EAAA,OAAA,GAAA,CAGjB,EAAA,SAAa,CAEL,EAAI,OAAI,QAAe,EAAI,OAAI,UAAA,GAAA,CADvC,EAKM,MALN,EAKM,EADD,EAAI,QAAO,CAAA,EAAA,EAKH,EAAI,OAAI,SAAA,GAAA,CADrB,EAKE,EAAA,CAAA,IAAA,EAAA,CAAA,CANF,EAAA,SAAa,CACb,EAKE,MAAA,CAHC,IAAK,EAAI,QACV,MAAM,oBACL,QAAK,GAAE,EAAI,gBAAkB,EAAI,QAAO,oBAK9B,EAAI,OAAI,QAAA,GAAA,CADrB,EAoBM,EAAA,CAAA,IAAA,EAAA,CAAA,CArBN,EAAA,SAAa,CACb,EAoBM,MAAA,CAlBJ,MAAM,mBACL,QAAK,GAAE,EAAI,aAAe,EAAG,GAE9B,EAGE,EAAA,CAFA,KAAK,4BACJ,KAAM,KAET,EAUM,MAVN,GAUM,CATJ,EAES,OAFT,GAES,EADP,EAAI,UAAQ,KAAA,CAAA,EAAA,CAGN,EAAI,UAAA,GAAA,CADZ,EAKO,OALP,GAKO,EADF,GAAe,EAAI,SAAQ,CAAA,CAAA,EAAA,EAAA,EAAA,OAAA,GAAA,CAAA,CAAA,CAAA,CAAA,EAAA,EAAA,CAAA,CAAA,KAAA,EAAA,EAAA,OAAA,GAAA,CAKpC,EAAA,QAAY,CAEJ,EAAA,MAAY,eAAiB,EAAI,SAAM,UAAA,GAAA,CAD/C,EAKO,OALP,GAKO,EADF,EAAW,EAAI,UAAS,CAAA,CAAA,EAAA,EAAA,EAAA,OAAA,GAAA,CAG7B,EAAA,SAAa,CAEL,EAAI,SAAM,QAAe,EAAI,QAAA,GAAA,CADrC,EAaO,OAAA,OAXL,MAAK,EAAA,CAAC,qBAAoB,MACZ,EAAI,SAAM,CAAA,CACvB,QAAK,GAAE,EAAI,SAAM,UAAiB,EAAI,SAAW,EAAG,IAGnD,EAAI,SAAM,UAAA,SAAsE,EAAI,SAAM,SAAA,YAAA,GAAA,CAAA,GAAA,GAAA,EAAA,EAAA,OAAA,GAAA,0BAYtG,EAAA,SAAa,CACb,EAwDM,MAxDN,GAwDM,CAvDJ,EA6BM,MA7BN,GA6BM,CA5BJ,EA2BO,EAAA,OAAA,gBAAA,EAAA,KAAA,CA1BL,EAYU,EAAA,CAXR,WAAA,GACA,OAAA,GACA,KAAK,QACJ,QAAK,AAAA,EAAA,KAAA,GAAE,EAAI,cAAA,GAED,KAAI,MAIX,CAHF,EAGE,EAAA,CAFA,KAAK,6BACJ,KAAM,aAIb,EAYU,EAAA,CAXR,WAAA,GACA,OAAA,GACA,KAAK,QACJ,QAAK,AAAA,EAAA,KAAA,GAAE,EAAI,cAAA,GAED,KAAI,MAIX,CAHF,EAGE,EAAA,CAFA,KAAK,gBACJ,KAAM,oBAOjB,EAQM,MARN,GAQM,CAPJ,EAME,EAAA,CALQ,MAAO,EAAA,iCAAA,EAAS,MAAA,EACvB,YAAa,EAAA,MAAY,YAC1B,KAAK,WACJ,SAAU,CAAA,QAAA,EAAA,QAAA,EAA0B,CACpC,UAAO,GAAA,GAAsB,EAAU,CAAA,QAAA,UAAA,CAAA,CAAA,CAAA,QAAA,CAAA,gDAKpC,EAAA,MAAY,aAAA,GAAA,CADpB,GAaU,EAAA,OAXR,KAAK,UACJ,SAAQ,CAAG,EAAA,MAAU,MAAI,CAC1B,MAAM,mBACL,QAAO,IAEG,KAAI,MAIX,CAHF,EAGE,EAAA,CAFA,KAAK,WACJ,KAAM,oDAQjB,EAUM,EAAA,CAAA,IAAA,EAAA,CAAA,CAXN,EAAA,yBAA6B,CAC7B,EAUM,MAVN,GAUM,CANJ,EAIE,EAAA,CAHA,KAAK,8BACJ,KAAM,GACP,MAAM,gCAER,EAAuB,OAAA,KAAjB,aAAU,GAAA,CAAA,CAAA,CAAA,CAAA,KAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"C_City-Bam08Qv3.css","names":[],"sources":["../src/components/C_City/index.vue?vue&type=style&index=0&scoped=00b4ba0b&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.city-selector-trigger[data-v-00b4ba0b] {\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n min-width: 200px;\n height: 34px;\n padding: 0 12px;\n border: 1px solid #e0e0e6;\n border-radius: 3px;\n cursor: pointer;\n transition: all 0.3s;\n}\n.city-selector-trigger[data-v-00b4ba0b]:hover {\n border-color: var(--c-primary, #1890ff);\n}\n.city-selector-trigger__text[data-v-00b4ba0b] {\n flex: 1;\n font-size: 14px;\n}\n.city-selector-trigger__icon[data-v-00b4ba0b] {\n margin-left: 8px;\n color: #999;\n font-size: 16px;\n transition: transform 0.3s;\n}\n.city-selector-trigger__icon.is-active[data-v-00b4ba0b] {\n transform: rotate(180deg);\n}\n.city-selector-content[data-v-00b4ba0b] {\n display: flex;\n flex-direction: column;\n height: 500px;\n padding: 0;\n}\n.city-selector-header[data-v-00b4ba0b] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border-bottom: 1px solid rgba(247, 247, 247, 0.514);\n flex-shrink: 0;\n}\n.city-selector-header .city-selector-search[data-v-00b4ba0b] {\n flex: 1;\n}\n.city-selector-letters[data-v-00b4ba0b] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin: 0 12px 10px;\n padding: 8px;\n flex-shrink: 0;\n}\n.city-selector-letters .city-selector-letter[data-v-00b4ba0b] {\n padding: 2px 6px;\n cursor: pointer;\n color: var(--c-primary, #1890ff);\n font-size: 13px;\n font-weight: 500;\n transition: all 0.3s;\n min-width: 24px;\n text-align: center;\n}\n.city-selector-letters .city-selector-letter[data-v-00b4ba0b]:hover {\n background-color: var(--c-primary, #1890ff);\n color: #fff;\n border-radius: 2px;\n}\n.city-selector-body[data-v-00b4ba0b] {\n flex: 1;\n margin: 12px;\n margin-top: 8px;\n overflow: hidden;\n}\n.city-list[data-v-00b4ba0b],\n.province-list[data-v-00b4ba0b] {\n padding: 0 8px;\n}\n.city-group[data-v-00b4ba0b],\n.province-group[data-v-00b4ba0b] {\n display: flex;\n margin-bottom: 12px;\n padding-bottom: 12px;\n border-bottom: 1px solid rgba(247, 247, 247, 0.514);\n}\n.city-group[data-v-00b4ba0b]:last-child,\n.province-group[data-v-00b4ba0b]:last-child {\n border-bottom: none;\n margin-bottom: 0;\n padding-bottom: 0;\n}\n.city-group__letter[data-v-00b4ba0b], .city-group__name[data-v-00b4ba0b],\n.province-group__letter[data-v-00b4ba0b],\n.province-group__name[data-v-00b4ba0b] {\n flex-shrink: 0;\n width: 70px;\n font-weight: 500;\n font-size: 14px;\n line-height: 28px;\n}\n.city-group__cities[data-v-00b4ba0b],\n.province-group__cities[data-v-00b4ba0b] {\n flex: 1;\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n align-items: flex-start;\n}\n.city-item[data-v-00b4ba0b] {\n padding: 3px 10px;\n cursor: pointer;\n font-size: 13px;\n border-radius: 2px;\n transition: all 0.3s;\n line-height: 20px;\n}\n.city-item[data-v-00b4ba0b]:hover {\n background-color: #f0f0f0;\n color: var(--c-primary, #1890ff);\n}\n.city-item.is-active[data-v-00b4ba0b] {\n background-color: var(--c-primary, #1890ff);\n color: #fff;\n}\n[data-v-00b4ba0b] .n-scrollbar .n-scrollbar-rail {\n right: 2px;\n}\n[data-v-00b4ba0b] .n-scrollbar .n-scrollbar-content {\n padding-right: 12px;\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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
1
+ {"version":3,"file":"C_City-Cv5BESaN.css","names":[],"sources":["../src/components/C_City/index.vue?vue&type=style&index=0&scoped=06a335f4&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.city-selector-trigger[data-v-06a335f4] {\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n min-width: 200px;\n height: 34px;\n padding: 0 12px;\n border: 1px solid #e0e0e6;\n border-radius: 3px;\n cursor: pointer;\n transition: all 0.3s;\n}\n.city-selector-trigger[data-v-06a335f4]:hover {\n border-color: var(--c-primary, #1890ff);\n}\n.city-selector-trigger__text[data-v-06a335f4] {\n flex: 1;\n font-size: 14px;\n}\n.city-selector-trigger__icon[data-v-06a335f4] {\n margin-left: 8px;\n color: #999;\n font-size: 16px;\n transition: transform 0.3s;\n}\n.city-selector-trigger__icon.is-active[data-v-06a335f4] {\n transform: rotate(180deg);\n}\n.city-selector-content[data-v-06a335f4] {\n display: flex;\n flex-direction: column;\n height: 500px;\n padding: 0;\n}\n.city-selector-header[data-v-06a335f4] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border-bottom: 1px solid rgba(247, 247, 247, 0.514);\n flex-shrink: 0;\n}\n.city-selector-header .city-selector-search[data-v-06a335f4] {\n flex: 1;\n}\n.city-selector-letters[data-v-06a335f4] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin: 0 12px 10px;\n padding: 8px;\n flex-shrink: 0;\n}\n.city-selector-letters .city-selector-letter[data-v-06a335f4] {\n padding: 2px 6px;\n cursor: pointer;\n color: var(--c-primary, #1890ff);\n font-size: 13px;\n font-weight: 500;\n transition: all 0.3s;\n min-width: 24px;\n text-align: center;\n}\n.city-selector-letters .city-selector-letter[data-v-06a335f4]:hover {\n background-color: var(--c-primary, #1890ff);\n color: #fff;\n border-radius: 2px;\n}\n.city-selector-body[data-v-06a335f4] {\n flex: 1;\n margin: 12px;\n margin-top: 8px;\n overflow: hidden;\n}\n.city-list[data-v-06a335f4],\n.province-list[data-v-06a335f4] {\n padding: 0 8px;\n}\n.city-group[data-v-06a335f4],\n.province-group[data-v-06a335f4] {\n display: flex;\n margin-bottom: 12px;\n padding-bottom: 12px;\n border-bottom: 1px solid rgba(247, 247, 247, 0.514);\n}\n.city-group[data-v-06a335f4]:last-child,\n.province-group[data-v-06a335f4]:last-child {\n border-bottom: none;\n margin-bottom: 0;\n padding-bottom: 0;\n}\n.city-group__letter[data-v-06a335f4], .city-group__name[data-v-06a335f4],\n.province-group__letter[data-v-06a335f4],\n.province-group__name[data-v-06a335f4] {\n flex-shrink: 0;\n width: 70px;\n font-weight: 500;\n font-size: 14px;\n line-height: 28px;\n}\n.city-group__cities[data-v-06a335f4],\n.province-group__cities[data-v-06a335f4] {\n flex: 1;\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n align-items: flex-start;\n}\n.city-item[data-v-06a335f4] {\n padding: 3px 10px;\n cursor: pointer;\n font-size: 13px;\n border-radius: 2px;\n transition: all 0.3s;\n line-height: 20px;\n}\n.city-item[data-v-06a335f4]:hover {\n background-color: #f0f0f0;\n color: var(--c-primary, #1890ff);\n}\n.city-item.is-active[data-v-06a335f4] {\n background-color: var(--c-primary, #1890ff);\n color: #fff;\n}\n[data-v-06a335f4] .n-scrollbar .n-scrollbar-rail {\n right: 2px;\n}\n[data-v-06a335f4] .n-scrollbar .n-scrollbar-content {\n padding-right: 12px;\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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1 +1 @@
1
- {"version":3,"file":"C_City-Bb3W-KaN.css","names":[],"sources":["../src/components/C_City/index.vue?vue&type=style&index=0&scoped=00b4ba0b&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.city-selector-trigger[data-v-00b4ba0b] {\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n min-width: 200px;\n height: 34px;\n padding: 0 12px;\n border: 1px solid #e0e0e6;\n border-radius: 3px;\n cursor: pointer;\n transition: all 0.3s;\n}\n.city-selector-trigger[data-v-00b4ba0b]:hover {\n border-color: var(--c-primary, #1890ff);\n}\n.city-selector-trigger__text[data-v-00b4ba0b] {\n flex: 1;\n font-size: 14px;\n}\n.city-selector-trigger__icon[data-v-00b4ba0b] {\n margin-left: 8px;\n color: #999;\n font-size: 16px;\n transition: transform 0.3s;\n}\n.city-selector-trigger__icon.is-active[data-v-00b4ba0b] {\n transform: rotate(180deg);\n}\n.city-selector-content[data-v-00b4ba0b] {\n display: flex;\n flex-direction: column;\n height: 500px;\n padding: 0;\n}\n.city-selector-header[data-v-00b4ba0b] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border-bottom: 1px solid rgba(247, 247, 247, 0.514);\n flex-shrink: 0;\n}\n.city-selector-header .city-selector-search[data-v-00b4ba0b] {\n flex: 1;\n}\n.city-selector-letters[data-v-00b4ba0b] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin: 0 12px 10px;\n padding: 8px;\n flex-shrink: 0;\n}\n.city-selector-letters .city-selector-letter[data-v-00b4ba0b] {\n padding: 2px 6px;\n cursor: pointer;\n color: var(--c-primary, #1890ff);\n font-size: 13px;\n font-weight: 500;\n transition: all 0.3s;\n min-width: 24px;\n text-align: center;\n}\n.city-selector-letters .city-selector-letter[data-v-00b4ba0b]:hover {\n background-color: var(--c-primary, #1890ff);\n color: #fff;\n border-radius: 2px;\n}\n.city-selector-body[data-v-00b4ba0b] {\n flex: 1;\n margin: 12px;\n margin-top: 8px;\n overflow: hidden;\n}\n.city-list[data-v-00b4ba0b],\n.province-list[data-v-00b4ba0b] {\n padding: 0 8px;\n}\n.city-group[data-v-00b4ba0b],\n.province-group[data-v-00b4ba0b] {\n display: flex;\n margin-bottom: 12px;\n padding-bottom: 12px;\n border-bottom: 1px solid rgba(247, 247, 247, 0.514);\n}\n.city-group[data-v-00b4ba0b]:last-child,\n.province-group[data-v-00b4ba0b]:last-child {\n border-bottom: none;\n margin-bottom: 0;\n padding-bottom: 0;\n}\n.city-group__letter[data-v-00b4ba0b], .city-group__name[data-v-00b4ba0b],\n.province-group__letter[data-v-00b4ba0b],\n.province-group__name[data-v-00b4ba0b] {\n flex-shrink: 0;\n width: 70px;\n font-weight: 500;\n font-size: 14px;\n line-height: 28px;\n}\n.city-group__cities[data-v-00b4ba0b],\n.province-group__cities[data-v-00b4ba0b] {\n flex: 1;\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n align-items: flex-start;\n}\n.city-item[data-v-00b4ba0b] {\n padding: 3px 10px;\n cursor: pointer;\n font-size: 13px;\n border-radius: 2px;\n transition: all 0.3s;\n line-height: 20px;\n}\n.city-item[data-v-00b4ba0b]:hover {\n background-color: #f0f0f0;\n color: var(--c-primary, #1890ff);\n}\n.city-item.is-active[data-v-00b4ba0b] {\n background-color: var(--c-primary, #1890ff);\n color: #fff;\n}\n[data-v-00b4ba0b] .n-scrollbar .n-scrollbar-rail {\n right: 2px;\n}\n[data-v-00b4ba0b] .n-scrollbar .n-scrollbar-content {\n padding-right: 12px;\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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
1
+ {"version":3,"file":"C_City-DNKv4MpH.css","names":[],"sources":["../src/components/C_City/index.vue?vue&type=style&index=0&scoped=06a335f4&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.city-selector-trigger[data-v-06a335f4] {\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n min-width: 200px;\n height: 34px;\n padding: 0 12px;\n border: 1px solid #e0e0e6;\n border-radius: 3px;\n cursor: pointer;\n transition: all 0.3s;\n}\n.city-selector-trigger[data-v-06a335f4]:hover {\n border-color: var(--c-primary, #1890ff);\n}\n.city-selector-trigger__text[data-v-06a335f4] {\n flex: 1;\n font-size: 14px;\n}\n.city-selector-trigger__icon[data-v-06a335f4] {\n margin-left: 8px;\n color: #999;\n font-size: 16px;\n transition: transform 0.3s;\n}\n.city-selector-trigger__icon.is-active[data-v-06a335f4] {\n transform: rotate(180deg);\n}\n.city-selector-content[data-v-06a335f4] {\n display: flex;\n flex-direction: column;\n height: 500px;\n padding: 0;\n}\n.city-selector-header[data-v-06a335f4] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border-bottom: 1px solid rgba(247, 247, 247, 0.514);\n flex-shrink: 0;\n}\n.city-selector-header .city-selector-search[data-v-06a335f4] {\n flex: 1;\n}\n.city-selector-letters[data-v-06a335f4] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin: 0 12px 10px;\n padding: 8px;\n flex-shrink: 0;\n}\n.city-selector-letters .city-selector-letter[data-v-06a335f4] {\n padding: 2px 6px;\n cursor: pointer;\n color: var(--c-primary, #1890ff);\n font-size: 13px;\n font-weight: 500;\n transition: all 0.3s;\n min-width: 24px;\n text-align: center;\n}\n.city-selector-letters .city-selector-letter[data-v-06a335f4]:hover {\n background-color: var(--c-primary, #1890ff);\n color: #fff;\n border-radius: 2px;\n}\n.city-selector-body[data-v-06a335f4] {\n flex: 1;\n margin: 12px;\n margin-top: 8px;\n overflow: hidden;\n}\n.city-list[data-v-06a335f4],\n.province-list[data-v-06a335f4] {\n padding: 0 8px;\n}\n.city-group[data-v-06a335f4],\n.province-group[data-v-06a335f4] {\n display: flex;\n margin-bottom: 12px;\n padding-bottom: 12px;\n border-bottom: 1px solid rgba(247, 247, 247, 0.514);\n}\n.city-group[data-v-06a335f4]:last-child,\n.province-group[data-v-06a335f4]:last-child {\n border-bottom: none;\n margin-bottom: 0;\n padding-bottom: 0;\n}\n.city-group__letter[data-v-06a335f4], .city-group__name[data-v-06a335f4],\n.province-group__letter[data-v-06a335f4],\n.province-group__name[data-v-06a335f4] {\n flex-shrink: 0;\n width: 70px;\n font-weight: 500;\n font-size: 14px;\n line-height: 28px;\n}\n.city-group__cities[data-v-06a335f4],\n.province-group__cities[data-v-06a335f4] {\n flex: 1;\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n align-items: flex-start;\n}\n.city-item[data-v-06a335f4] {\n padding: 3px 10px;\n cursor: pointer;\n font-size: 13px;\n border-radius: 2px;\n transition: all 0.3s;\n line-height: 20px;\n}\n.city-item[data-v-06a335f4]:hover {\n background-color: #f0f0f0;\n color: var(--c-primary, #1890ff);\n}\n.city-item.is-active[data-v-06a335f4] {\n background-color: var(--c-primary, #1890ff);\n color: #fff;\n}\n[data-v-06a335f4] .n-scrollbar .n-scrollbar-rail {\n right: 2px;\n}\n[data-v-06a335f4] .n-scrollbar .n-scrollbar-content {\n padding-right: 12px;\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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
package/dist/C_City.css CHANGED
@@ -1,4 +1,4 @@
1
- /* unplugin-vue-components disabled */.city-selector-trigger[data-v-00b4ba0b] {
1
+ /* unplugin-vue-components disabled */.city-selector-trigger[data-v-06a335f4] {
2
2
  display: inline-flex;
3
3
  align-items: center;
4
4
  justify-content: space-between;
@@ -10,29 +10,29 @@
10
10
  cursor: pointer;
11
11
  transition: all 0.3s;
12
12
  }
13
- .city-selector-trigger[data-v-00b4ba0b]:hover {
13
+ .city-selector-trigger[data-v-06a335f4]:hover {
14
14
  border-color: var(--c-primary, #1890ff);
15
15
  }
16
- .city-selector-trigger__text[data-v-00b4ba0b] {
16
+ .city-selector-trigger__text[data-v-06a335f4] {
17
17
  flex: 1;
18
18
  font-size: 14px;
19
19
  }
20
- .city-selector-trigger__icon[data-v-00b4ba0b] {
20
+ .city-selector-trigger__icon[data-v-06a335f4] {
21
21
  margin-left: 8px;
22
22
  color: #999;
23
23
  font-size: 16px;
24
24
  transition: transform 0.3s;
25
25
  }
26
- .city-selector-trigger__icon.is-active[data-v-00b4ba0b] {
26
+ .city-selector-trigger__icon.is-active[data-v-06a335f4] {
27
27
  transform: rotate(180deg);
28
28
  }
29
- .city-selector-content[data-v-00b4ba0b] {
29
+ .city-selector-content[data-v-06a335f4] {
30
30
  display: flex;
31
31
  flex-direction: column;
32
32
  height: 500px;
33
33
  padding: 0;
34
34
  }
35
- .city-selector-header[data-v-00b4ba0b] {
35
+ .city-selector-header[data-v-06a335f4] {
36
36
  display: flex;
37
37
  align-items: center;
38
38
  gap: 12px;
@@ -40,10 +40,10 @@
40
40
  border-bottom: 1px solid rgba(247, 247, 247, 0.514);
41
41
  flex-shrink: 0;
42
42
  }
43
- .city-selector-header .city-selector-search[data-v-00b4ba0b] {
43
+ .city-selector-header .city-selector-search[data-v-06a335f4] {
44
44
  flex: 1;
45
45
  }
46
- .city-selector-letters[data-v-00b4ba0b] {
46
+ .city-selector-letters[data-v-06a335f4] {
47
47
  display: flex;
48
48
  flex-wrap: wrap;
49
49
  gap: 6px;
@@ -51,7 +51,7 @@
51
51
  padding: 8px;
52
52
  flex-shrink: 0;
53
53
  }
54
- .city-selector-letters .city-selector-letter[data-v-00b4ba0b] {
54
+ .city-selector-letters .city-selector-letter[data-v-06a335f4] {
55
55
  padding: 2px 6px;
56
56
  cursor: pointer;
57
57
  color: var(--c-primary, #1890ff);
@@ -61,52 +61,52 @@
61
61
  min-width: 24px;
62
62
  text-align: center;
63
63
  }
64
- .city-selector-letters .city-selector-letter[data-v-00b4ba0b]:hover {
64
+ .city-selector-letters .city-selector-letter[data-v-06a335f4]:hover {
65
65
  background-color: var(--c-primary, #1890ff);
66
66
  color: #fff;
67
67
  border-radius: 2px;
68
68
  }
69
- .city-selector-body[data-v-00b4ba0b] {
69
+ .city-selector-body[data-v-06a335f4] {
70
70
  flex: 1;
71
71
  margin: 12px;
72
72
  margin-top: 8px;
73
73
  overflow: hidden;
74
74
  }
75
- .city-list[data-v-00b4ba0b],
76
- .province-list[data-v-00b4ba0b] {
75
+ .city-list[data-v-06a335f4],
76
+ .province-list[data-v-06a335f4] {
77
77
  padding: 0 8px;
78
78
  }
79
- .city-group[data-v-00b4ba0b],
80
- .province-group[data-v-00b4ba0b] {
79
+ .city-group[data-v-06a335f4],
80
+ .province-group[data-v-06a335f4] {
81
81
  display: flex;
82
82
  margin-bottom: 12px;
83
83
  padding-bottom: 12px;
84
84
  border-bottom: 1px solid rgba(247, 247, 247, 0.514);
85
85
  }
86
- .city-group[data-v-00b4ba0b]:last-child,
87
- .province-group[data-v-00b4ba0b]:last-child {
86
+ .city-group[data-v-06a335f4]:last-child,
87
+ .province-group[data-v-06a335f4]:last-child {
88
88
  border-bottom: none;
89
89
  margin-bottom: 0;
90
90
  padding-bottom: 0;
91
91
  }
92
- .city-group__letter[data-v-00b4ba0b], .city-group__name[data-v-00b4ba0b],
93
- .province-group__letter[data-v-00b4ba0b],
94
- .province-group__name[data-v-00b4ba0b] {
92
+ .city-group__letter[data-v-06a335f4], .city-group__name[data-v-06a335f4],
93
+ .province-group__letter[data-v-06a335f4],
94
+ .province-group__name[data-v-06a335f4] {
95
95
  flex-shrink: 0;
96
96
  width: 70px;
97
97
  font-weight: 500;
98
98
  font-size: 14px;
99
99
  line-height: 28px;
100
100
  }
101
- .city-group__cities[data-v-00b4ba0b],
102
- .province-group__cities[data-v-00b4ba0b] {
101
+ .city-group__cities[data-v-06a335f4],
102
+ .province-group__cities[data-v-06a335f4] {
103
103
  flex: 1;
104
104
  display: flex;
105
105
  flex-wrap: wrap;
106
106
  gap: 6px;
107
107
  align-items: flex-start;
108
108
  }
109
- .city-item[data-v-00b4ba0b] {
109
+ .city-item[data-v-06a335f4] {
110
110
  padding: 3px 10px;
111
111
  cursor: pointer;
112
112
  font-size: 13px;
@@ -114,19 +114,19 @@
114
114
  transition: all 0.3s;
115
115
  line-height: 20px;
116
116
  }
117
- .city-item[data-v-00b4ba0b]:hover {
117
+ .city-item[data-v-06a335f4]:hover {
118
118
  background-color: #f0f0f0;
119
119
  color: var(--c-primary, #1890ff);
120
120
  }
121
- .city-item.is-active[data-v-00b4ba0b] {
121
+ .city-item.is-active[data-v-06a335f4] {
122
122
  background-color: var(--c-primary, #1890ff);
123
123
  color: #fff;
124
124
  }
125
- [data-v-00b4ba0b] .n-scrollbar .n-scrollbar-rail {
125
+ [data-v-06a335f4] .n-scrollbar .n-scrollbar-rail {
126
126
  right: 2px;
127
127
  }
128
- [data-v-00b4ba0b] .n-scrollbar .n-scrollbar-content {
128
+ [data-v-06a335f4] .n-scrollbar .n-scrollbar-content {
129
129
  padding-right: 12px;
130
130
  }
131
131
 
132
- /*# sourceMappingURL=C_City-Bam08Qv3.css.map*/
132
+ /*# sourceMappingURL=C_City-Cv5BESaN.css.map*/
package/dist/C_City.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { t as _default } from "./index6.vue.js";
1
+ import { t as _default } from "./index9.vue.js";
2
2
  export { _default as C_City };
package/dist/C_City.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { t as _default } from "./index6.vue.js";
1
+ import { t as _default } from "./index9.vue.js";
2
2
  export { _default as C_City };
@@ -1 +1 @@
1
- {"version":3,"file":"C_City.js","names":["modelValue","placeholder","showLetters"],"sources":["../src/components/C_City/province.json","../src/components/C_City/index.vue","../src/components/C_City/index.vue","../src/components/C_City/index.vue"],"sourcesContent":["","/* unplugin-vue-components disabled */<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-05-30\n * @Description: 城市选择器组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <NPopover\n v-model:show=\"visible\"\n placement=\"bottom-start\"\n :width=\"430\"\n trigger=\"click\"\n :show-arrow=\"false\"\n >\n <template #trigger>\n <slot name=\"trigger\" :value=\"modelValue\" :visible=\"visible\">\n <div class=\"city-selector-trigger\">\n <span class=\"city-selector-trigger__text\">{{\n modelValue || placeholder\n }}</span>\n </div>\n </slot>\n </template>\n\n <div class=\"city-selector-content\">\n <div class=\"city-selector-header\">\n <NRadioGroup v-model:value=\"radioValue\" size=\"small\">\n <NRadioButton value=\"city\">按城市</NRadioButton>\n <NRadioButton value=\"province\">按省份</NRadioButton>\n </NRadioGroup>\n <NSelect\n v-model:value=\"searchValue\"\n class=\"city-selector-search\"\n :options=\"searchOptions\"\n filterable\n clearable\n placeholder=\"搜索城市\"\n @update:value=\"handleSearchSelect\"\n />\n </div>\n\n <div v-if=\"showLetters\" class=\"city-selector-letters\">\n <span\n v-for=\"letter in letters\"\n :key=\"letter\"\n class=\"city-selector-letter\"\n @click=\"scrollToLetter(letter)\"\n >\n {{ letter }}\n </span>\n </div>\n\n <NScrollbar class=\"city-selector-body\">\n <div v-if=\"radioValue === 'city'\" class=\"city-list\">\n <div\n v-for=\"(cities, letter) in cityDataByLetter\"\n :key=\"letter\"\n :id=\"`letter-${letter}`\"\n class=\"city-group\"\n >\n <div class=\"city-group__letter\">{{ letter }}:</div>\n <div class=\"city-group__cities\">\n <span\n v-for=\"(city, index) in cities\"\n :key=\"`${letter}-${index}`\"\n class=\"city-item\"\n :class=\"{ 'is-active': modelValue === city.name }\"\n @click=\"handleCitySelect(city.name)\"\n >\n {{ city.name }}\n </span>\n </div>\n </div>\n </div>\n\n <div v-else class=\"province-list\">\n <div\n v-for=\"province in allProvinces\"\n :key=\"province.id\"\n :id=\"`letter-${province.id}`\"\n class=\"province-group\"\n >\n <div class=\"province-group__name\">{{ province.name }}:</div>\n <div class=\"province-group__cities\">\n <span\n v-for=\"(city, index) in province.data\"\n :key=\"`${province.id}-${index}`\"\n class=\"city-item\"\n :class=\"{ 'is-active': modelValue === city }\"\n @click=\"handleCitySelect(city)\"\n >\n {{ city }}\n </span>\n </div>\n </div>\n </div>\n </NScrollbar>\n </div>\n </NPopover>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, onMounted } from \"vue\";\nimport {\n NPopover,\n NRadioGroup,\n NRadioButton,\n NSelect,\n NScrollbar,\n} from \"naive-ui\";\nimport type { SelectOption } from \"naive-ui\";\nimport provinceData from \"./province.json\";\n\ndefineOptions({ name: \"C_City\" });\n\ninterface CityItem {\n id: number;\n spell: string;\n name: string;\n}\n\n// 城市数据懒加载(83KB JSON 仅在组件挂载时加载)\nconst cityData = ref<{ cities: Record<string, CityItem[]> } | null>(null);\n\nonMounted(async () => {\n const mod = await import(\"./city.json\");\n cityData.value = mod.default ?? mod;\n});\n\ninterface ProvinceItem {\n id?: string;\n name: string;\n data: string[];\n}\n\ninterface Props {\n modelValue?: string;\n placeholder?: string;\n showLetters?: boolean;\n}\n\ninterface Emits {\n (e: \"update:modelValue\", value: string): void;\n (e: \"change\", value: string): void;\n}\n\nwithDefaults(defineProps<Props>(), {\n placeholder: \"请选择城市\",\n showLetters: true,\n});\n\nconst emit = defineEmits<Emits>();\n\nconst visible = ref(false);\nconst radioValue = ref<\"city\" | \"province\">(\"city\");\nconst searchValue = ref(\"\");\n\nconst allProvinces = computed(() => {\n const provinces: ProvinceItem[] = [];\n Object.values(provinceData).forEach((group) => {\n provinces.push(...(group as ProvinceItem[]));\n });\n return provinces;\n});\n\nconst cityDataByLetter = computed(() => {\n if (cityData.value?.cities) {\n return cityData.value.cities;\n }\n // 数据未加载时,从省份数据生成城市索引作为 fallback\n const cities: Record<string, CityItem[]> = {};\n let cityId = 1;\n const citySet = new Set<string>();\n allProvinces.value.forEach((province) => {\n province.data.forEach((cityName) => {\n citySet.add(cityName);\n });\n });\n Array.from(citySet).forEach((cityName) => {\n const letter = cityName[0].toUpperCase();\n if (!cities[letter]) cities[letter] = [];\n cities[letter].push({ id: cityId++, name: cityName, spell: \"\" });\n });\n const sortedCities: Record<string, CityItem[]> = {};\n Object.keys(cities)\n .sort()\n .forEach((letter) => {\n sortedCities[letter] = cities[letter].sort((a, b) =>\n a.name.localeCompare(b.name, \"zh-CN\"),\n );\n });\n return sortedCities;\n});\n\nconst letters = computed(() => {\n if (radioValue.value === \"city\") {\n return Object.keys(cityDataByLetter.value).sort();\n } else {\n const provinceLetters = new Set<string>();\n Object.keys(provinceData).forEach((key) => {\n if (key !== \"直辖市\" && key !== \"港澳\") {\n provinceLetters.add(key);\n }\n });\n const result = Array.from(provinceLetters).sort();\n result.push(\"直辖市\", \"港澳\");\n return result;\n }\n});\n\nconst searchOptions = computed((): SelectOption[] => {\n if (radioValue.value === \"city\") {\n const options: SelectOption[] = [];\n Object.values(cityDataByLetter.value).forEach((cities) => {\n (cities as CityItem[]).forEach((city) => {\n options.push({\n label: city.name,\n value: city.name,\n });\n });\n });\n return options;\n } else {\n return allProvinces.value.flatMap((province) =>\n province.data.map((city) => ({\n label: `${city} (${province.name})`,\n value: city,\n })),\n );\n }\n});\n\nconst handleCitySelect = (cityName: string): void => {\n emit(\"update:modelValue\", cityName);\n emit(\"change\", cityName);\n visible.value = false;\n};\n\nconst handleSearchSelect = (value: string): void => {\n if (value) {\n handleCitySelect(value);\n searchValue.value = \"\";\n }\n};\n\nconst scrollToLetter = (letter: string): void => {\n const element = document.getElementById(`letter-${letter}`);\n if (element) {\n element.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n }\n};\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n","/* unplugin-vue-components disabled */<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-05-30\n * @Description: 城市选择器组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <NPopover\n v-model:show=\"visible\"\n placement=\"bottom-start\"\n :width=\"430\"\n trigger=\"click\"\n :show-arrow=\"false\"\n >\n <template #trigger>\n <slot name=\"trigger\" :value=\"modelValue\" :visible=\"visible\">\n <div class=\"city-selector-trigger\">\n <span class=\"city-selector-trigger__text\">{{\n modelValue || placeholder\n }}</span>\n </div>\n </slot>\n </template>\n\n <div class=\"city-selector-content\">\n <div class=\"city-selector-header\">\n <NRadioGroup v-model:value=\"radioValue\" size=\"small\">\n <NRadioButton value=\"city\">按城市</NRadioButton>\n <NRadioButton value=\"province\">按省份</NRadioButton>\n </NRadioGroup>\n <NSelect\n v-model:value=\"searchValue\"\n class=\"city-selector-search\"\n :options=\"searchOptions\"\n filterable\n clearable\n placeholder=\"搜索城市\"\n @update:value=\"handleSearchSelect\"\n />\n </div>\n\n <div v-if=\"showLetters\" class=\"city-selector-letters\">\n <span\n v-for=\"letter in letters\"\n :key=\"letter\"\n class=\"city-selector-letter\"\n @click=\"scrollToLetter(letter)\"\n >\n {{ letter }}\n </span>\n </div>\n\n <NScrollbar class=\"city-selector-body\">\n <div v-if=\"radioValue === 'city'\" class=\"city-list\">\n <div\n v-for=\"(cities, letter) in cityDataByLetter\"\n :key=\"letter\"\n :id=\"`letter-${letter}`\"\n class=\"city-group\"\n >\n <div class=\"city-group__letter\">{{ letter }}:</div>\n <div class=\"city-group__cities\">\n <span\n v-for=\"(city, index) in cities\"\n :key=\"`${letter}-${index}`\"\n class=\"city-item\"\n :class=\"{ 'is-active': modelValue === city.name }\"\n @click=\"handleCitySelect(city.name)\"\n >\n {{ city.name }}\n </span>\n </div>\n </div>\n </div>\n\n <div v-else class=\"province-list\">\n <div\n v-for=\"province in allProvinces\"\n :key=\"province.id\"\n :id=\"`letter-${province.id}`\"\n class=\"province-group\"\n >\n <div class=\"province-group__name\">{{ province.name }}:</div>\n <div class=\"province-group__cities\">\n <span\n v-for=\"(city, index) in province.data\"\n :key=\"`${province.id}-${index}`\"\n class=\"city-item\"\n :class=\"{ 'is-active': modelValue === city }\"\n @click=\"handleCitySelect(city)\"\n >\n {{ city }}\n </span>\n </div>\n </div>\n </div>\n </NScrollbar>\n </div>\n </NPopover>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, onMounted } from \"vue\";\nimport {\n NPopover,\n NRadioGroup,\n NRadioButton,\n NSelect,\n NScrollbar,\n} from \"naive-ui\";\nimport type { SelectOption } from \"naive-ui\";\nimport provinceData from \"./province.json\";\n\ndefineOptions({ name: \"C_City\" });\n\ninterface CityItem {\n id: number;\n spell: string;\n name: string;\n}\n\n// 城市数据懒加载(83KB JSON 仅在组件挂载时加载)\nconst cityData = ref<{ cities: Record<string, CityItem[]> } | null>(null);\n\nonMounted(async () => {\n const mod = await import(\"./city.json\");\n cityData.value = mod.default ?? mod;\n});\n\ninterface ProvinceItem {\n id?: string;\n name: string;\n data: string[];\n}\n\ninterface Props {\n modelValue?: string;\n placeholder?: string;\n showLetters?: boolean;\n}\n\ninterface Emits {\n (e: \"update:modelValue\", value: string): void;\n (e: \"change\", value: string): void;\n}\n\nwithDefaults(defineProps<Props>(), {\n placeholder: \"请选择城市\",\n showLetters: true,\n});\n\nconst emit = defineEmits<Emits>();\n\nconst visible = ref(false);\nconst radioValue = ref<\"city\" | \"province\">(\"city\");\nconst searchValue = ref(\"\");\n\nconst allProvinces = computed(() => {\n const provinces: ProvinceItem[] = [];\n Object.values(provinceData).forEach((group) => {\n provinces.push(...(group as ProvinceItem[]));\n });\n return provinces;\n});\n\nconst cityDataByLetter = computed(() => {\n if (cityData.value?.cities) {\n return cityData.value.cities;\n }\n // 数据未加载时,从省份数据生成城市索引作为 fallback\n const cities: Record<string, CityItem[]> = {};\n let cityId = 1;\n const citySet = new Set<string>();\n allProvinces.value.forEach((province) => {\n province.data.forEach((cityName) => {\n citySet.add(cityName);\n });\n });\n Array.from(citySet).forEach((cityName) => {\n const letter = cityName[0].toUpperCase();\n if (!cities[letter]) cities[letter] = [];\n cities[letter].push({ id: cityId++, name: cityName, spell: \"\" });\n });\n const sortedCities: Record<string, CityItem[]> = {};\n Object.keys(cities)\n .sort()\n .forEach((letter) => {\n sortedCities[letter] = cities[letter].sort((a, b) =>\n a.name.localeCompare(b.name, \"zh-CN\"),\n );\n });\n return sortedCities;\n});\n\nconst letters = computed(() => {\n if (radioValue.value === \"city\") {\n return Object.keys(cityDataByLetter.value).sort();\n } else {\n const provinceLetters = new Set<string>();\n Object.keys(provinceData).forEach((key) => {\n if (key !== \"直辖市\" && key !== \"港澳\") {\n provinceLetters.add(key);\n }\n });\n const result = Array.from(provinceLetters).sort();\n result.push(\"直辖市\", \"港澳\");\n return result;\n }\n});\n\nconst searchOptions = computed((): SelectOption[] => {\n if (radioValue.value === \"city\") {\n const options: SelectOption[] = [];\n Object.values(cityDataByLetter.value).forEach((cities) => {\n (cities as CityItem[]).forEach((city) => {\n options.push({\n label: city.name,\n value: city.name,\n });\n });\n });\n return options;\n } else {\n return allProvinces.value.flatMap((province) =>\n province.data.map((city) => ({\n label: `${city} (${province.name})`,\n value: city,\n })),\n );\n }\n});\n\nconst handleCitySelect = (cityName: string): void => {\n emit(\"update:modelValue\", cityName);\n emit(\"change\", cityName);\n visible.value = false;\n};\n\nconst handleSearchSelect = (value: string): void => {\n if (value) {\n handleCitySelect(value);\n searchValue.value = \"\";\n }\n};\n\nconst scrollToLetter = (letter: string): void => {\n const element = document.getElementById(`letter-${letter}`);\n if (element) {\n element.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n }\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-05-30\n * @Description: 城市选择器组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <NPopover\n v-model:show=\"visible\"\n placement=\"bottom-start\"\n :width=\"430\"\n trigger=\"click\"\n :show-arrow=\"false\"\n >\n <template #trigger>\n <slot name=\"trigger\" :value=\"modelValue\" :visible=\"visible\">\n <div class=\"city-selector-trigger\">\n <span class=\"city-selector-trigger__text\">{{\n modelValue || placeholder\n }}</span>\n </div>\n </slot>\n </template>\n\n <div class=\"city-selector-content\">\n <div class=\"city-selector-header\">\n <NRadioGroup v-model:value=\"radioValue\" size=\"small\">\n <NRadioButton value=\"city\">按城市</NRadioButton>\n <NRadioButton value=\"province\">按省份</NRadioButton>\n </NRadioGroup>\n <NSelect\n v-model:value=\"searchValue\"\n class=\"city-selector-search\"\n :options=\"searchOptions\"\n filterable\n clearable\n placeholder=\"搜索城市\"\n @update:value=\"handleSearchSelect\"\n />\n </div>\n\n <div v-if=\"showLetters\" class=\"city-selector-letters\">\n <span\n v-for=\"letter in letters\"\n :key=\"letter\"\n class=\"city-selector-letter\"\n @click=\"scrollToLetter(letter)\"\n >\n {{ letter }}\n </span>\n </div>\n\n <NScrollbar class=\"city-selector-body\">\n <div v-if=\"radioValue === 'city'\" class=\"city-list\">\n <div\n v-for=\"(cities, letter) in cityDataByLetter\"\n :key=\"letter\"\n :id=\"`letter-${letter}`\"\n class=\"city-group\"\n >\n <div class=\"city-group__letter\">{{ letter }}:</div>\n <div class=\"city-group__cities\">\n <span\n v-for=\"(city, index) in cities\"\n :key=\"`${letter}-${index}`\"\n class=\"city-item\"\n :class=\"{ 'is-active': modelValue === city.name }\"\n @click=\"handleCitySelect(city.name)\"\n >\n {{ city.name }}\n </span>\n </div>\n </div>\n </div>\n\n <div v-else class=\"province-list\">\n <div\n v-for=\"province in allProvinces\"\n :key=\"province.id\"\n :id=\"`letter-${province.id}`\"\n class=\"province-group\"\n >\n <div class=\"province-group__name\">{{ province.name }}:</div>\n <div class=\"province-group__cities\">\n <span\n v-for=\"(city, index) in province.data\"\n :key=\"`${province.id}-${index}`\"\n class=\"city-item\"\n :class=\"{ 'is-active': modelValue === city }\"\n @click=\"handleCitySelect(city)\"\n >\n {{ city }}\n </span>\n </div>\n </div>\n </div>\n </NScrollbar>\n </div>\n </NPopover>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, onMounted } from \"vue\";\nimport {\n NPopover,\n NRadioGroup,\n NRadioButton,\n NSelect,\n NScrollbar,\n} from \"naive-ui\";\nimport type { SelectOption } from \"naive-ui\";\nimport provinceData from \"./province.json\";\n\ndefineOptions({ name: \"C_City\" });\n\ninterface CityItem {\n id: number;\n spell: string;\n name: string;\n}\n\n// 城市数据懒加载(83KB JSON 仅在组件挂载时加载)\nconst cityData = ref<{ cities: Record<string, CityItem[]> } | null>(null);\n\nonMounted(async () => {\n const mod = await import(\"./city.json\");\n cityData.value = mod.default ?? mod;\n});\n\ninterface ProvinceItem {\n id?: string;\n name: string;\n data: string[];\n}\n\ninterface Props {\n modelValue?: string;\n placeholder?: string;\n showLetters?: boolean;\n}\n\ninterface Emits {\n (e: \"update:modelValue\", value: string): void;\n (e: \"change\", value: string): void;\n}\n\nwithDefaults(defineProps<Props>(), {\n placeholder: \"请选择城市\",\n showLetters: true,\n});\n\nconst emit = defineEmits<Emits>();\n\nconst visible = ref(false);\nconst radioValue = ref<\"city\" | \"province\">(\"city\");\nconst searchValue = ref(\"\");\n\nconst allProvinces = computed(() => {\n const provinces: ProvinceItem[] = [];\n Object.values(provinceData).forEach((group) => {\n provinces.push(...(group as ProvinceItem[]));\n });\n return provinces;\n});\n\nconst cityDataByLetter = computed(() => {\n if (cityData.value?.cities) {\n return cityData.value.cities;\n }\n // 数据未加载时,从省份数据生成城市索引作为 fallback\n const cities: Record<string, CityItem[]> = {};\n let cityId = 1;\n const citySet = new Set<string>();\n allProvinces.value.forEach((province) => {\n province.data.forEach((cityName) => {\n citySet.add(cityName);\n });\n });\n Array.from(citySet).forEach((cityName) => {\n const letter = cityName[0].toUpperCase();\n if (!cities[letter]) cities[letter] = [];\n cities[letter].push({ id: cityId++, name: cityName, spell: \"\" });\n });\n const sortedCities: Record<string, CityItem[]> = {};\n Object.keys(cities)\n .sort()\n .forEach((letter) => {\n sortedCities[letter] = cities[letter].sort((a, b) =>\n a.name.localeCompare(b.name, \"zh-CN\"),\n );\n });\n return sortedCities;\n});\n\nconst letters = computed(() => {\n if (radioValue.value === \"city\") {\n return Object.keys(cityDataByLetter.value).sort();\n } else {\n const provinceLetters = new Set<string>();\n Object.keys(provinceData).forEach((key) => {\n if (key !== \"直辖市\" && key !== \"港澳\") {\n provinceLetters.add(key);\n }\n });\n const result = Array.from(provinceLetters).sort();\n result.push(\"直辖市\", \"港澳\");\n return result;\n }\n});\n\nconst searchOptions = computed((): SelectOption[] => {\n if (radioValue.value === \"city\") {\n const options: SelectOption[] = [];\n Object.values(cityDataByLetter.value).forEach((cities) => {\n (cities as CityItem[]).forEach((city) => {\n options.push({\n label: city.name,\n value: city.name,\n });\n });\n });\n return options;\n } else {\n return allProvinces.value.flatMap((province) =>\n province.data.map((city) => ({\n label: `${city} (${province.name})`,\n value: city,\n })),\n );\n }\n});\n\nconst handleCitySelect = (cityName: string): void => {\n emit(\"update:modelValue\", cityName);\n emit(\"change\", cityName);\n visible.value = false;\n};\n\nconst handleSearchSelect = (value: string): void => {\n if (value) {\n handleCitySelect(value);\n searchValue.value = \"\";\n }\n};\n\nconst scrollToLetter = (letter: string): void => {\n const element = document.getElementById(`letter-${letter}`);\n if (element) {\n element.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n }\n};\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"C_City.js","names":["modelValue","placeholder","showLetters"],"sources":["../src/components/C_City/province.json","../src/components/C_City/index.vue","../src/components/C_City/index.vue","../src/components/C_City/index.vue"],"sourcesContent":["","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-30\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 <NPopover\r\n v-model:show=\"visible\"\r\n placement=\"bottom-start\"\r\n :width=\"430\"\r\n trigger=\"click\"\r\n :show-arrow=\"false\"\r\n >\r\n <template #trigger>\r\n <slot name=\"trigger\" :value=\"modelValue\" :visible=\"visible\">\r\n <div class=\"city-selector-trigger\">\r\n <span class=\"city-selector-trigger__text\">{{\r\n modelValue || placeholder\r\n }}</span>\r\n </div>\r\n </slot>\r\n </template>\r\n\r\n <div class=\"city-selector-content\">\r\n <div class=\"city-selector-header\">\r\n <NRadioGroup v-model:value=\"radioValue\" size=\"small\">\r\n <NRadioButton value=\"city\">按城市</NRadioButton>\r\n <NRadioButton value=\"province\">按省份</NRadioButton>\r\n </NRadioGroup>\r\n <NSelect\r\n v-model:value=\"searchValue\"\r\n class=\"city-selector-search\"\r\n :options=\"searchOptions\"\r\n filterable\r\n clearable\r\n placeholder=\"搜索城市\"\r\n @update:value=\"handleSearchSelect\"\r\n />\r\n </div>\r\n\r\n <div v-if=\"showLetters\" class=\"city-selector-letters\">\r\n <span\r\n v-for=\"letter in letters\"\r\n :key=\"letter\"\r\n class=\"city-selector-letter\"\r\n @click=\"scrollToLetter(letter)\"\r\n >\r\n {{ letter }}\r\n </span>\r\n </div>\r\n\r\n <NScrollbar class=\"city-selector-body\">\r\n <div v-if=\"radioValue === 'city'\" class=\"city-list\">\r\n <div\r\n v-for=\"(cities, letter) in cityDataByLetter\"\r\n :key=\"letter\"\r\n :id=\"`letter-${letter}`\"\r\n class=\"city-group\"\r\n >\r\n <div class=\"city-group__letter\">{{ letter }}:</div>\r\n <div class=\"city-group__cities\">\r\n <span\r\n v-for=\"(city, index) in cities\"\r\n :key=\"`${letter}-${index}`\"\r\n class=\"city-item\"\r\n :class=\"{ 'is-active': modelValue === city.name }\"\r\n @click=\"handleCitySelect(city.name)\"\r\n >\r\n {{ city.name }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div v-else class=\"province-list\">\r\n <div\r\n v-for=\"province in allProvinces\"\r\n :key=\"province.id\"\r\n :id=\"`letter-${province.id}`\"\r\n class=\"province-group\"\r\n >\r\n <div class=\"province-group__name\">{{ province.name }}:</div>\r\n <div class=\"province-group__cities\">\r\n <span\r\n v-for=\"(city, index) in province.data\"\r\n :key=\"`${province.id}-${index}`\"\r\n class=\"city-item\"\r\n :class=\"{ 'is-active': modelValue === city }\"\r\n @click=\"handleCitySelect(city)\"\r\n >\r\n {{ city }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n </NScrollbar>\r\n </div>\r\n </NPopover>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, onMounted } from \"vue\";\r\nimport {\r\n NPopover,\r\n NRadioGroup,\r\n NRadioButton,\r\n NSelect,\r\n NScrollbar,\r\n} from \"naive-ui\";\r\nimport type { SelectOption } from \"naive-ui\";\r\nimport provinceData from \"./province.json\";\r\n\r\ndefineOptions({ name: \"C_City\" });\r\n\r\ninterface CityItem {\r\n id: number;\r\n spell: string;\r\n name: string;\r\n}\r\n\r\n// 城市数据懒加载(83KB JSON 仅在组件挂载时加载)\r\nconst cityData = ref<{ cities: Record<string, CityItem[]> } | null>(null);\r\n\r\nonMounted(async () => {\r\n const mod = await import(\"./city.json\");\r\n cityData.value = mod.default ?? mod;\r\n});\r\n\r\ninterface ProvinceItem {\r\n id?: string;\r\n name: string;\r\n data: string[];\r\n}\r\n\r\ninterface Props {\r\n modelValue?: string;\r\n placeholder?: string;\r\n showLetters?: boolean;\r\n}\r\n\r\ninterface Emits {\r\n (e: \"update:modelValue\", value: string): void;\r\n (e: \"change\", value: string): void;\r\n}\r\n\r\nwithDefaults(defineProps<Props>(), {\r\n placeholder: \"请选择城市\",\r\n showLetters: true,\r\n});\r\n\r\nconst emit = defineEmits<Emits>();\r\n\r\nconst visible = ref(false);\r\nconst radioValue = ref<\"city\" | \"province\">(\"city\");\r\nconst searchValue = ref(\"\");\r\n\r\nconst allProvinces = computed(() => {\r\n const provinces: ProvinceItem[] = [];\r\n Object.values(provinceData).forEach((group) => {\r\n provinces.push(...(group as ProvinceItem[]));\r\n });\r\n return provinces;\r\n});\r\n\r\nconst cityDataByLetter = computed(() => {\r\n if (cityData.value?.cities) {\r\n return cityData.value.cities;\r\n }\r\n // 数据未加载时,从省份数据生成城市索引作为 fallback\r\n const cities: Record<string, CityItem[]> = {};\r\n let cityId = 1;\r\n const citySet = new Set<string>();\r\n allProvinces.value.forEach((province) => {\r\n province.data.forEach((cityName) => {\r\n citySet.add(cityName);\r\n });\r\n });\r\n Array.from(citySet).forEach((cityName) => {\r\n const letter = cityName[0].toUpperCase();\r\n if (!cities[letter]) cities[letter] = [];\r\n cities[letter].push({ id: cityId++, name: cityName, spell: \"\" });\r\n });\r\n const sortedCities: Record<string, CityItem[]> = {};\r\n Object.keys(cities)\r\n .sort()\r\n .forEach((letter) => {\r\n sortedCities[letter] = cities[letter].sort((a, b) =>\r\n a.name.localeCompare(b.name, \"zh-CN\"),\r\n );\r\n });\r\n return sortedCities;\r\n});\r\n\r\nconst letters = computed(() => {\r\n if (radioValue.value === \"city\") {\r\n return Object.keys(cityDataByLetter.value).sort();\r\n } else {\r\n const provinceLetters = new Set<string>();\r\n Object.keys(provinceData).forEach((key) => {\r\n if (key !== \"直辖市\" && key !== \"港澳\") {\r\n provinceLetters.add(key);\r\n }\r\n });\r\n const result = Array.from(provinceLetters).sort();\r\n result.push(\"直辖市\", \"港澳\");\r\n return result;\r\n }\r\n});\r\n\r\nconst searchOptions = computed((): SelectOption[] => {\r\n if (radioValue.value === \"city\") {\r\n const options: SelectOption[] = [];\r\n Object.values(cityDataByLetter.value).forEach((cities) => {\r\n (cities as CityItem[]).forEach((city) => {\r\n options.push({\r\n label: city.name,\r\n value: city.name,\r\n });\r\n });\r\n });\r\n return options;\r\n } else {\r\n return allProvinces.value.flatMap((province) =>\r\n province.data.map((city) => ({\r\n label: `${city} (${province.name})`,\r\n value: city,\r\n })),\r\n );\r\n }\r\n});\r\n\r\nconst handleCitySelect = (cityName: string): void => {\r\n emit(\"update:modelValue\", cityName);\r\n emit(\"change\", cityName);\r\n visible.value = false;\r\n};\r\n\r\nconst handleSearchSelect = (value: string): void => {\r\n if (value) {\r\n handleCitySelect(value);\r\n searchValue.value = \"\";\r\n }\r\n};\r\n\r\nconst scrollToLetter = (letter: string): void => {\r\n const element = document.getElementById(`letter-${letter}`);\r\n if (element) {\r\n element.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\r\n }\r\n};\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-30\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 <NPopover\r\n v-model:show=\"visible\"\r\n placement=\"bottom-start\"\r\n :width=\"430\"\r\n trigger=\"click\"\r\n :show-arrow=\"false\"\r\n >\r\n <template #trigger>\r\n <slot name=\"trigger\" :value=\"modelValue\" :visible=\"visible\">\r\n <div class=\"city-selector-trigger\">\r\n <span class=\"city-selector-trigger__text\">{{\r\n modelValue || placeholder\r\n }}</span>\r\n </div>\r\n </slot>\r\n </template>\r\n\r\n <div class=\"city-selector-content\">\r\n <div class=\"city-selector-header\">\r\n <NRadioGroup v-model:value=\"radioValue\" size=\"small\">\r\n <NRadioButton value=\"city\">按城市</NRadioButton>\r\n <NRadioButton value=\"province\">按省份</NRadioButton>\r\n </NRadioGroup>\r\n <NSelect\r\n v-model:value=\"searchValue\"\r\n class=\"city-selector-search\"\r\n :options=\"searchOptions\"\r\n filterable\r\n clearable\r\n placeholder=\"搜索城市\"\r\n @update:value=\"handleSearchSelect\"\r\n />\r\n </div>\r\n\r\n <div v-if=\"showLetters\" class=\"city-selector-letters\">\r\n <span\r\n v-for=\"letter in letters\"\r\n :key=\"letter\"\r\n class=\"city-selector-letter\"\r\n @click=\"scrollToLetter(letter)\"\r\n >\r\n {{ letter }}\r\n </span>\r\n </div>\r\n\r\n <NScrollbar class=\"city-selector-body\">\r\n <div v-if=\"radioValue === 'city'\" class=\"city-list\">\r\n <div\r\n v-for=\"(cities, letter) in cityDataByLetter\"\r\n :key=\"letter\"\r\n :id=\"`letter-${letter}`\"\r\n class=\"city-group\"\r\n >\r\n <div class=\"city-group__letter\">{{ letter }}:</div>\r\n <div class=\"city-group__cities\">\r\n <span\r\n v-for=\"(city, index) in cities\"\r\n :key=\"`${letter}-${index}`\"\r\n class=\"city-item\"\r\n :class=\"{ 'is-active': modelValue === city.name }\"\r\n @click=\"handleCitySelect(city.name)\"\r\n >\r\n {{ city.name }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div v-else class=\"province-list\">\r\n <div\r\n v-for=\"province in allProvinces\"\r\n :key=\"province.id\"\r\n :id=\"`letter-${province.id}`\"\r\n class=\"province-group\"\r\n >\r\n <div class=\"province-group__name\">{{ province.name }}:</div>\r\n <div class=\"province-group__cities\">\r\n <span\r\n v-for=\"(city, index) in province.data\"\r\n :key=\"`${province.id}-${index}`\"\r\n class=\"city-item\"\r\n :class=\"{ 'is-active': modelValue === city }\"\r\n @click=\"handleCitySelect(city)\"\r\n >\r\n {{ city }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n </NScrollbar>\r\n </div>\r\n </NPopover>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, onMounted } from \"vue\";\r\nimport {\r\n NPopover,\r\n NRadioGroup,\r\n NRadioButton,\r\n NSelect,\r\n NScrollbar,\r\n} from \"naive-ui\";\r\nimport type { SelectOption } from \"naive-ui\";\r\nimport provinceData from \"./province.json\";\r\n\r\ndefineOptions({ name: \"C_City\" });\r\n\r\ninterface CityItem {\r\n id: number;\r\n spell: string;\r\n name: string;\r\n}\r\n\r\n// 城市数据懒加载(83KB JSON 仅在组件挂载时加载)\r\nconst cityData = ref<{ cities: Record<string, CityItem[]> } | null>(null);\r\n\r\nonMounted(async () => {\r\n const mod = await import(\"./city.json\");\r\n cityData.value = mod.default ?? mod;\r\n});\r\n\r\ninterface ProvinceItem {\r\n id?: string;\r\n name: string;\r\n data: string[];\r\n}\r\n\r\ninterface Props {\r\n modelValue?: string;\r\n placeholder?: string;\r\n showLetters?: boolean;\r\n}\r\n\r\ninterface Emits {\r\n (e: \"update:modelValue\", value: string): void;\r\n (e: \"change\", value: string): void;\r\n}\r\n\r\nwithDefaults(defineProps<Props>(), {\r\n placeholder: \"请选择城市\",\r\n showLetters: true,\r\n});\r\n\r\nconst emit = defineEmits<Emits>();\r\n\r\nconst visible = ref(false);\r\nconst radioValue = ref<\"city\" | \"province\">(\"city\");\r\nconst searchValue = ref(\"\");\r\n\r\nconst allProvinces = computed(() => {\r\n const provinces: ProvinceItem[] = [];\r\n Object.values(provinceData).forEach((group) => {\r\n provinces.push(...(group as ProvinceItem[]));\r\n });\r\n return provinces;\r\n});\r\n\r\nconst cityDataByLetter = computed(() => {\r\n if (cityData.value?.cities) {\r\n return cityData.value.cities;\r\n }\r\n // 数据未加载时,从省份数据生成城市索引作为 fallback\r\n const cities: Record<string, CityItem[]> = {};\r\n let cityId = 1;\r\n const citySet = new Set<string>();\r\n allProvinces.value.forEach((province) => {\r\n province.data.forEach((cityName) => {\r\n citySet.add(cityName);\r\n });\r\n });\r\n Array.from(citySet).forEach((cityName) => {\r\n const letter = cityName[0].toUpperCase();\r\n if (!cities[letter]) cities[letter] = [];\r\n cities[letter].push({ id: cityId++, name: cityName, spell: \"\" });\r\n });\r\n const sortedCities: Record<string, CityItem[]> = {};\r\n Object.keys(cities)\r\n .sort()\r\n .forEach((letter) => {\r\n sortedCities[letter] = cities[letter].sort((a, b) =>\r\n a.name.localeCompare(b.name, \"zh-CN\"),\r\n );\r\n });\r\n return sortedCities;\r\n});\r\n\r\nconst letters = computed(() => {\r\n if (radioValue.value === \"city\") {\r\n return Object.keys(cityDataByLetter.value).sort();\r\n } else {\r\n const provinceLetters = new Set<string>();\r\n Object.keys(provinceData).forEach((key) => {\r\n if (key !== \"直辖市\" && key !== \"港澳\") {\r\n provinceLetters.add(key);\r\n }\r\n });\r\n const result = Array.from(provinceLetters).sort();\r\n result.push(\"直辖市\", \"港澳\");\r\n return result;\r\n }\r\n});\r\n\r\nconst searchOptions = computed((): SelectOption[] => {\r\n if (radioValue.value === \"city\") {\r\n const options: SelectOption[] = [];\r\n Object.values(cityDataByLetter.value).forEach((cities) => {\r\n (cities as CityItem[]).forEach((city) => {\r\n options.push({\r\n label: city.name,\r\n value: city.name,\r\n });\r\n });\r\n });\r\n return options;\r\n } else {\r\n return allProvinces.value.flatMap((province) =>\r\n province.data.map((city) => ({\r\n label: `${city} (${province.name})`,\r\n value: city,\r\n })),\r\n );\r\n }\r\n});\r\n\r\nconst handleCitySelect = (cityName: string): void => {\r\n emit(\"update:modelValue\", cityName);\r\n emit(\"change\", cityName);\r\n visible.value = false;\r\n};\r\n\r\nconst handleSearchSelect = (value: string): void => {\r\n if (value) {\r\n handleCitySelect(value);\r\n searchValue.value = \"\";\r\n }\r\n};\r\n\r\nconst scrollToLetter = (letter: string): void => {\r\n const element = document.getElementById(`letter-${letter}`);\r\n if (element) {\r\n element.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\r\n }\r\n};\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-30\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 <NPopover\r\n v-model:show=\"visible\"\r\n placement=\"bottom-start\"\r\n :width=\"430\"\r\n trigger=\"click\"\r\n :show-arrow=\"false\"\r\n >\r\n <template #trigger>\r\n <slot name=\"trigger\" :value=\"modelValue\" :visible=\"visible\">\r\n <div class=\"city-selector-trigger\">\r\n <span class=\"city-selector-trigger__text\">{{\r\n modelValue || placeholder\r\n }}</span>\r\n </div>\r\n </slot>\r\n </template>\r\n\r\n <div class=\"city-selector-content\">\r\n <div class=\"city-selector-header\">\r\n <NRadioGroup v-model:value=\"radioValue\" size=\"small\">\r\n <NRadioButton value=\"city\">按城市</NRadioButton>\r\n <NRadioButton value=\"province\">按省份</NRadioButton>\r\n </NRadioGroup>\r\n <NSelect\r\n v-model:value=\"searchValue\"\r\n class=\"city-selector-search\"\r\n :options=\"searchOptions\"\r\n filterable\r\n clearable\r\n placeholder=\"搜索城市\"\r\n @update:value=\"handleSearchSelect\"\r\n />\r\n </div>\r\n\r\n <div v-if=\"showLetters\" class=\"city-selector-letters\">\r\n <span\r\n v-for=\"letter in letters\"\r\n :key=\"letter\"\r\n class=\"city-selector-letter\"\r\n @click=\"scrollToLetter(letter)\"\r\n >\r\n {{ letter }}\r\n </span>\r\n </div>\r\n\r\n <NScrollbar class=\"city-selector-body\">\r\n <div v-if=\"radioValue === 'city'\" class=\"city-list\">\r\n <div\r\n v-for=\"(cities, letter) in cityDataByLetter\"\r\n :key=\"letter\"\r\n :id=\"`letter-${letter}`\"\r\n class=\"city-group\"\r\n >\r\n <div class=\"city-group__letter\">{{ letter }}:</div>\r\n <div class=\"city-group__cities\">\r\n <span\r\n v-for=\"(city, index) in cities\"\r\n :key=\"`${letter}-${index}`\"\r\n class=\"city-item\"\r\n :class=\"{ 'is-active': modelValue === city.name }\"\r\n @click=\"handleCitySelect(city.name)\"\r\n >\r\n {{ city.name }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div v-else class=\"province-list\">\r\n <div\r\n v-for=\"province in allProvinces\"\r\n :key=\"province.id\"\r\n :id=\"`letter-${province.id}`\"\r\n class=\"province-group\"\r\n >\r\n <div class=\"province-group__name\">{{ province.name }}:</div>\r\n <div class=\"province-group__cities\">\r\n <span\r\n v-for=\"(city, index) in province.data\"\r\n :key=\"`${province.id}-${index}`\"\r\n class=\"city-item\"\r\n :class=\"{ 'is-active': modelValue === city }\"\r\n @click=\"handleCitySelect(city)\"\r\n >\r\n {{ city }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n </NScrollbar>\r\n </div>\r\n </NPopover>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, onMounted } from \"vue\";\r\nimport {\r\n NPopover,\r\n NRadioGroup,\r\n NRadioButton,\r\n NSelect,\r\n NScrollbar,\r\n} from \"naive-ui\";\r\nimport type { SelectOption } from \"naive-ui\";\r\nimport provinceData from \"./province.json\";\r\n\r\ndefineOptions({ name: \"C_City\" });\r\n\r\ninterface CityItem {\r\n id: number;\r\n spell: string;\r\n name: string;\r\n}\r\n\r\n// 城市数据懒加载(83KB JSON 仅在组件挂载时加载)\r\nconst cityData = ref<{ cities: Record<string, CityItem[]> } | null>(null);\r\n\r\nonMounted(async () => {\r\n const mod = await import(\"./city.json\");\r\n cityData.value = mod.default ?? mod;\r\n});\r\n\r\ninterface ProvinceItem {\r\n id?: string;\r\n name: string;\r\n data: string[];\r\n}\r\n\r\ninterface Props {\r\n modelValue?: string;\r\n placeholder?: string;\r\n showLetters?: boolean;\r\n}\r\n\r\ninterface Emits {\r\n (e: \"update:modelValue\", value: string): void;\r\n (e: \"change\", value: string): void;\r\n}\r\n\r\nwithDefaults(defineProps<Props>(), {\r\n placeholder: \"请选择城市\",\r\n showLetters: true,\r\n});\r\n\r\nconst emit = defineEmits<Emits>();\r\n\r\nconst visible = ref(false);\r\nconst radioValue = ref<\"city\" | \"province\">(\"city\");\r\nconst searchValue = ref(\"\");\r\n\r\nconst allProvinces = computed(() => {\r\n const provinces: ProvinceItem[] = [];\r\n Object.values(provinceData).forEach((group) => {\r\n provinces.push(...(group as ProvinceItem[]));\r\n });\r\n return provinces;\r\n});\r\n\r\nconst cityDataByLetter = computed(() => {\r\n if (cityData.value?.cities) {\r\n return cityData.value.cities;\r\n }\r\n // 数据未加载时,从省份数据生成城市索引作为 fallback\r\n const cities: Record<string, CityItem[]> = {};\r\n let cityId = 1;\r\n const citySet = new Set<string>();\r\n allProvinces.value.forEach((province) => {\r\n province.data.forEach((cityName) => {\r\n citySet.add(cityName);\r\n });\r\n });\r\n Array.from(citySet).forEach((cityName) => {\r\n const letter = cityName[0].toUpperCase();\r\n if (!cities[letter]) cities[letter] = [];\r\n cities[letter].push({ id: cityId++, name: cityName, spell: \"\" });\r\n });\r\n const sortedCities: Record<string, CityItem[]> = {};\r\n Object.keys(cities)\r\n .sort()\r\n .forEach((letter) => {\r\n sortedCities[letter] = cities[letter].sort((a, b) =>\r\n a.name.localeCompare(b.name, \"zh-CN\"),\r\n );\r\n });\r\n return sortedCities;\r\n});\r\n\r\nconst letters = computed(() => {\r\n if (radioValue.value === \"city\") {\r\n return Object.keys(cityDataByLetter.value).sort();\r\n } else {\r\n const provinceLetters = new Set<string>();\r\n Object.keys(provinceData).forEach((key) => {\r\n if (key !== \"直辖市\" && key !== \"港澳\") {\r\n provinceLetters.add(key);\r\n }\r\n });\r\n const result = Array.from(provinceLetters).sort();\r\n result.push(\"直辖市\", \"港澳\");\r\n return result;\r\n }\r\n});\r\n\r\nconst searchOptions = computed((): SelectOption[] => {\r\n if (radioValue.value === \"city\") {\r\n const options: SelectOption[] = [];\r\n Object.values(cityDataByLetter.value).forEach((cities) => {\r\n (cities as CityItem[]).forEach((city) => {\r\n options.push({\r\n label: city.name,\r\n value: city.name,\r\n });\r\n });\r\n });\r\n return options;\r\n } else {\r\n return allProvinces.value.flatMap((province) =>\r\n province.data.map((city) => ({\r\n label: `${city} (${province.name})`,\r\n value: city,\r\n })),\r\n );\r\n }\r\n});\r\n\r\nconst handleCitySelect = (cityName: string): void => {\r\n emit(\"update:modelValue\", cityName);\r\n emit(\"change\", cityName);\r\n visible.value = false;\r\n};\r\n\r\nconst handleSearchSelect = (value: string): void => {\r\n if (value) {\r\n handleCitySelect(value);\r\n searchValue.value = \"\";\r\n }\r\n};\r\n\r\nconst scrollToLetter = (letter: string): void => {\r\n const element = document.getElementById(`letter-${letter}`);\r\n if (element) {\r\n element.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\r\n }\r\n};\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n"],"mappings":"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"}
package/dist/C_City2.js CHANGED
@@ -1,2 +1,2 @@
1
- import{t as e}from"./export-helper.js";import{NPopover as t,NRadioButton as n,NRadioGroup as r,NScrollbar as i,NSelect as a}from"naive-ui";import{Fragment as o,computed as s,createBlock as c,createCommentVNode as l,createElementBlock as u,createElementVNode as d,createTextVNode as f,createVNode as p,defineComponent as m,normalizeClass as h,onMounted as g,openBlock as _,ref as v,renderList as y,renderSlot as b,toDisplayString as x,unref as S,withCtx as C}from"vue";var w={A:[{name:`安徽`,id:`A`,data:[`合肥`,`芜湖`,`蚌埠`,`淮南`,`马鞍山`,`淮北`,`铜陵`,`安庆`,`黄山`,`滁州`,`阜阳`,`宿州`,`六安`,`亳州`,`池州`,`宣城`]}],C:[{name:`重庆`,id:`C`,data:`万州.涪陵.渝中.大渡口.江北.沙坪坝.九龙坡.南岸.北碚.綦江.大足.渝北.巴南.黔江.长寿.江津.合川.永川.南川.璧山.铜梁.潼南.荣昌.开州.梁平.武隆.城口县.丰都县.垫江县.忠县.云阳县.奉节县.巫山县.巫溪县.石柱土家族自治县.秀山土家族苗族自治县.酉阳土家族苗族自治县.彭水苗族土家族自治县`.split(`.`)}],F:[{name:`福建`,id:`F`,data:[`福州`,`厦门`,`莆田`,`三明`,`泉州`,`漳州`,`南平`,`龙岩`,`宁德`]}],G:[{name:`广东`,id:`G`,data:[`广州`,`韶关`,`深圳`,`珠海`,`汕头`,`佛山`,`江门`,`湛江`,`茂名`,`肇庆`,`惠州`,`梅州`,`汕尾`,`河源`,`阳江`,`清远`,`东莞`,`中山`,`潮州`,`揭阳`,`云浮`]},{name:`广西`,data:[`南宁`,`柳州`,`桂林`,`梧州`,`北海`,`防城港`,`钦州`,`贵港`,`玉林`,`百色`,`贺州`,`河池`,`来宾`,`崇左`]},{name:`贵州`,data:[`贵阳`,`六盘水`,`遵义`,`安顺`,`毕节`,`铜仁`,`黔西南布依族苗族自治州`,`黔东南苗族侗族自治州`,`黔南布依族苗族自治州`]},{name:`甘肃`,data:[`兰州`,`嘉峪关`,`金昌`,`白银`,`天水`,`武威`,`张掖`,`平凉`,`酒泉`,`庆阳`,`定西`,`陇南`,`临夏回族自治州`,`甘南藏族自治州`]}],H:[{name:`河北`,id:`H`,data:[`石家庄`,`唐山`,`秦皇岛`,`邯郸`,`邢台`,`保定`,`张家口`,`承德`,`沧州`,`廊坊`,`衡水`]},{name:`黑龙江`,data:[`哈尔滨`,`齐齐哈尔`,`鸡西`,`鹤岗`,`双鸭山`,`大庆`,`伊春`,`佳木斯`,`七台河`,`牡丹江`,`黑河`,`绥化`,`大兴安岭地`]},{name:`河南`,data:[`郑州`,`开封`,`洛阳`,`平顶山`,`安阳`,`鹤壁`,`新乡`,`焦作`,`濮阳`,`许昌`,`漯河`,`三门峡`,`南阳`,`商丘`,`信阳`,`周口`,`驻马店`,`济源`]},{name:`湖北`,data:[`武汉`,`黄石`,`十堰`,`宜昌`,`襄阳`,`鄂州`,`荆门`,`孝感`,`荆州`,`黄冈`,`咸宁`,`随州`,`恩施土家族苗族自治州`,`仙桃`,`潜江`,`天门`,`神农架林`]},{name:`湖南`,data:[`长沙`,`株洲`,`湘潭`,`衡阳`,`邵阳`,`岳阳`,`常德`,`张家界`,`益阳`,`郴州`,`永州`,`怀化`,`娄底`,`湘西土家族苗族自治州`]},{name:`海南`,data:[`海口`,`三亚`,`三沙`,`儋州`,`五指山`,`琼海`,`文昌`,`万宁`,`东方`,`定安县`,`屯昌县`,`澄迈县`,`临高县`,`白沙黎族自治县`,`昌江黎族自治县`,`乐东黎族自治县`,`陵水黎族自治县`,`保亭黎族苗族自治县`,`琼中黎族苗族自治县`]}],J:[{name:`吉林`,id:`J`,data:[`长春`,`吉林`,`四平`,`辽源`,`通化`,`白山`,`松原`,`白城`,`延边朝鲜族自治州`]},{name:`江苏`,data:[`南京`,`无锡`,`徐州`,`常州`,`苏州`,`南通`,`连云港`,`淮安`,`盐城`,`扬州`,`镇江`,`泰州`,`宿迁`]},{name:`江西`,data:[`南昌`,`景德镇`,`萍乡`,`九江`,`新余`,`鹰潭`,`赣州`,`吉安`,`宜春`,`抚州`,`上饶`]}],L:[{name:`辽宁`,id:`L`,data:[`沈阳`,`大连`,`鞍山`,`抚顺`,`本溪`,`丹东`,`锦州`,`营口`,`阜新`,`辽阳`,`盘锦`,`铁岭`,`朝阳`,`葫芦岛`]}],N:[{name:`内蒙古`,id:`N`,data:[`呼和浩特`,`包头`,`乌海`,`赤峰`,`通辽`,`鄂尔多斯`,`呼伦贝尔`,`巴彦淖尔`,`乌兰察布`,`兴安盟`,`锡林郭勒盟`,`阿拉善盟`]},{name:`宁夏`,data:[`银川`,`石嘴山`,`吴忠`,`固原`,`中卫`]}],Q:[{name:`青海`,id:`Q`,data:[`西宁`,`海东`,`海北藏族自治州`,`黄南藏族自治州`,`海南藏族自治州`,`果洛藏族自治州`,`玉树藏族自治州`,`海西蒙古族藏族自治州`]}],S:[{name:`山西`,id:`S`,data:[`太原`,`大同`,`阳泉`,`长治`,`晋城`,`朔州`,`晋中`,`运城`,`忻州`,`临汾`,`吕梁`]},{name:`上海`,data:[`黄浦`,`徐汇`,`长宁`,`静安`,`普陀`,`虹口`,`杨浦`,`闵行`,`宝山`,`嘉定`,`浦东新`,`金山`,`松江`,`青浦`,`奉贤`,`崇明`]},{name:`山东`,data:[`济南`,`青岛`,`淄博`,`枣庄`,`东营`,`烟台`,`潍坊`,`济宁`,`泰安`,`威海`,`日照`,`临沂`,`德州`,`聊城`,`滨州`,`菏泽`]},{name:`四川`,data:[`成都`,`自贡`,`攀枝花`,`泸州`,`德阳`,`绵阳`,`广元`,`遂宁`,`内江`,`乐山`,`南充`,`眉山`,`宜宾`,`广安`,`达州`,`雅安`,`巴中`,`资阳`,`阿坝藏族羌族自治州`,`甘孜藏族自治州`,`凉山彝族自治州`]},{name:`陕西`,data:[`西安`,`铜川`,`宝鸡`,`咸阳`,`渭南`,`延安`,`汉中`,`榆林`,`安康`,`商洛`]}],T:[{name:`天津`,id:`T`,data:[`和平`,`河东`,`河西`,`南开`,`河北`,`红桥`,`东丽`,`西青`,`津南`,`北辰`,`武清`,`宝坻`,`滨海新`,`宁河`,`静海`,`蓟州`]}],X:[{name:`西藏`,id:`X`,data:[`拉萨`,`日喀则`,`昌都`,`林芝`,`山南`,`那曲`,`阿里地`]},{name:`新疆`,data:[`乌鲁木齐`,`克拉玛依`,`吐鲁番`,`哈密`,`昌吉回族自治州`,`博尔塔拉蒙古自治州`,`巴音郭楞蒙古自治州`,`阿克苏地`,`克孜勒苏柯尔克孜自治州`,`喀什地`,`和田地`,`伊犁哈萨克自治州`,`塔城地`,`阿勒泰地`,`石河子`,`阿拉尔`,`图木舒克`,`五家渠`,`北屯`,`铁门关`,`双河`,`可克达拉`,`昆玉`,`胡杨河`]}],Y:[{name:`云南`,id:`Y`,data:[`昆明`,`曲靖`,`玉溪`,`保山`,`昭通`,`丽江`,`普洱`,`临沧`,`楚雄彝族自治州`,`红河哈尼族彝族自治州`,`文山壮族苗族自治州`,`西双版纳傣族自治州`,`大理白族自治州`,`德宏傣族景颇族自治州`,`怒江傈僳族自治州`,`迪庆藏族自治州`]}],Z:[{name:`浙江`,id:`Z`,data:[`杭州`,`宁波`,`温州`,`嘉兴`,`湖州`,`绍兴`,`金华`,`衢州`,`舟山`,`台州`,`丽水`]}],直辖市:[{name:`直辖市`,id:`直辖市`,data:[`北京`,`天津`,`上海`,`重庆`]}],港澳:[{name:`港澳`,id:`港澳`,data:[`香港`,`澳门`]}]};const T={class:`city-selector-trigger`},E={class:`city-selector-trigger__text`},D={class:`city-selector-content`},O={class:`city-selector-header`},k={key:0,class:`city-selector-letters`},A=[`onClick`],j={key:0,class:`city-list`},M=[`id`],N={class:`city-group__letter`},P={class:`city-group__cities`},F=[`onClick`],I={key:1,class:`province-list`},L=[`id`],R={class:`province-group__name`},z={class:`province-group__cities`},B=[`onClick`];var V=e(m({name:`C_City`,__name:`index`,props:{modelValue:{},placeholder:{default:`请选择城市`},showLetters:{type:Boolean,default:!0}},emits:[`update:modelValue`,`change`],setup(e,{emit:m}){let V=v(null);g(async()=>{let e=await import(`./city.js`);V.value=e.default??e});let H=m,U=v(!1),W=v(`city`),G=v(``),K=s(()=>{let e=[];return Object.values(w).forEach(t=>{e.push(...t)}),e}),q=s(()=>{if(V.value?.cities)return V.value.cities;let e={},t=1,n=new Set;K.value.forEach(e=>{e.data.forEach(e=>{n.add(e)})}),Array.from(n).forEach(n=>{let r=n[0].toUpperCase();e[r]||(e[r]=[]),e[r].push({id:t++,name:n,spell:``})});let r={};return Object.keys(e).sort().forEach(t=>{r[t]=e[t].sort((e,t)=>e.name.localeCompare(t.name,`zh-CN`))}),r}),J=s(()=>{if(W.value===`city`)return Object.keys(q.value).sort();{let e=new Set;Object.keys(w).forEach(t=>{t!==`直辖市`&&t!==`港澳`&&e.add(t)});let t=Array.from(e).sort();return t.push(`直辖市`,`港澳`),t}}),Y=s(()=>{if(W.value===`city`){let e=[];return Object.values(q.value).forEach(t=>{t.forEach(t=>{e.push({label:t.name,value:t.name})})}),e}else return K.value.flatMap(e=>e.data.map(t=>({label:`${t} (${e.name})`,value:t})))}),X=e=>{H(`update:modelValue`,e),H(`change`,e),U.value=!1},Z=e=>{e&&(X(e),G.value=``)},Q=e=>{let t=document.getElementById(`letter-${e}`);t&&t.scrollIntoView({behavior:`smooth`,block:`start`})};return(e,s)=>(_(),c(S(t),{show:U.value,"onUpdate:show":s[2]||=e=>U.value=e,placement:`bottom-start`,width:430,trigger:`click`,"show-arrow":!1},{trigger:C(()=>[b(e.$slots,`trigger`,{value:e.modelValue,visible:U.value},()=>[d(`div`,T,[d(`span`,E,x(e.modelValue||e.placeholder),1)])],!0)]),default:C(()=>[d(`div`,D,[d(`div`,O,[p(S(r),{value:W.value,"onUpdate:value":s[0]||=e=>W.value=e,size:`small`},{default:C(()=>[p(S(n),{value:`city`},{default:C(()=>s[3]||=[f(`按城市`,-1)]),_:1,__:[3]}),p(S(n),{value:`province`},{default:C(()=>s[4]||=[f(`按省份`,-1)]),_:1,__:[4]})]),_:1},8,[`value`]),p(S(a),{value:G.value,"onUpdate:value":[s[1]||=e=>G.value=e,Z],class:`city-selector-search`,options:Y.value,filterable:``,clearable:``,placeholder:`搜索城市`},null,8,[`value`,`options`])]),e.showLetters?(_(),u(`div`,k,[(_(!0),u(o,null,y(J.value,e=>(_(),u(`span`,{key:e,class:`city-selector-letter`,onClick:t=>Q(e)},x(e),9,A))),128))])):l(`v-if`,!0),p(S(i),{class:`city-selector-body`},{default:C(()=>[W.value===`city`?(_(),u(`div`,j,[(_(!0),u(o,null,y(q.value,(t,n)=>(_(),u(`div`,{key:n,id:`letter-${n}`,class:`city-group`},[d(`div`,N,x(n)+`:`,1),d(`div`,P,[(_(!0),u(o,null,y(t,(t,r)=>(_(),u(`span`,{key:`${n}-${r}`,class:h([`city-item`,{"is-active":e.modelValue===t.name}]),onClick:e=>X(t.name)},x(t.name),11,F))),128))])],8,M))),128))])):(_(),u(`div`,I,[(_(!0),u(o,null,y(K.value,t=>(_(),u(`div`,{key:t.id,id:`letter-${t.id}`,class:`province-group`},[d(`div`,R,x(t.name)+`:`,1),d(`div`,z,[(_(!0),u(o,null,y(t.data,(n,r)=>(_(),u(`span`,{key:`${t.id}-${r}`,class:h([`city-item`,{"is-active":e.modelValue===n}]),onClick:e=>X(n)},x(n),11,B))),128))])],8,L))),128))]))]),_:1})])]),_:3},8,[`show`]))}}),[[`__scopeId`,`data-v-00b4ba0b`]]);export{V as t};
1
+ import{t as e}from"./export-helper.js";import{NPopover as t,NRadioButton as n,NRadioGroup as r,NScrollbar as i,NSelect as a}from"naive-ui";import{Fragment as o,computed as s,createBlock as c,createCommentVNode as l,createElementBlock as u,createElementVNode as d,createTextVNode as f,createVNode as p,defineComponent as m,normalizeClass as h,onMounted as g,openBlock as _,ref as v,renderList as y,renderSlot as b,toDisplayString as x,unref as S,withCtx as C}from"vue";var w={A:[{name:`安徽`,id:`A`,data:[`合肥`,`芜湖`,`蚌埠`,`淮南`,`马鞍山`,`淮北`,`铜陵`,`安庆`,`黄山`,`滁州`,`阜阳`,`宿州`,`六安`,`亳州`,`池州`,`宣城`]}],C:[{name:`重庆`,id:`C`,data:`万州.涪陵.渝中.大渡口.江北.沙坪坝.九龙坡.南岸.北碚.綦江.大足.渝北.巴南.黔江.长寿.江津.合川.永川.南川.璧山.铜梁.潼南.荣昌.开州.梁平.武隆.城口县.丰都县.垫江县.忠县.云阳县.奉节县.巫山县.巫溪县.石柱土家族自治县.秀山土家族苗族自治县.酉阳土家族苗族自治县.彭水苗族土家族自治县`.split(`.`)}],F:[{name:`福建`,id:`F`,data:[`福州`,`厦门`,`莆田`,`三明`,`泉州`,`漳州`,`南平`,`龙岩`,`宁德`]}],G:[{name:`广东`,id:`G`,data:[`广州`,`韶关`,`深圳`,`珠海`,`汕头`,`佛山`,`江门`,`湛江`,`茂名`,`肇庆`,`惠州`,`梅州`,`汕尾`,`河源`,`阳江`,`清远`,`东莞`,`中山`,`潮州`,`揭阳`,`云浮`]},{name:`广西`,data:[`南宁`,`柳州`,`桂林`,`梧州`,`北海`,`防城港`,`钦州`,`贵港`,`玉林`,`百色`,`贺州`,`河池`,`来宾`,`崇左`]},{name:`贵州`,data:[`贵阳`,`六盘水`,`遵义`,`安顺`,`毕节`,`铜仁`,`黔西南布依族苗族自治州`,`黔东南苗族侗族自治州`,`黔南布依族苗族自治州`]},{name:`甘肃`,data:[`兰州`,`嘉峪关`,`金昌`,`白银`,`天水`,`武威`,`张掖`,`平凉`,`酒泉`,`庆阳`,`定西`,`陇南`,`临夏回族自治州`,`甘南藏族自治州`]}],H:[{name:`河北`,id:`H`,data:[`石家庄`,`唐山`,`秦皇岛`,`邯郸`,`邢台`,`保定`,`张家口`,`承德`,`沧州`,`廊坊`,`衡水`]},{name:`黑龙江`,data:[`哈尔滨`,`齐齐哈尔`,`鸡西`,`鹤岗`,`双鸭山`,`大庆`,`伊春`,`佳木斯`,`七台河`,`牡丹江`,`黑河`,`绥化`,`大兴安岭地`]},{name:`河南`,data:[`郑州`,`开封`,`洛阳`,`平顶山`,`安阳`,`鹤壁`,`新乡`,`焦作`,`濮阳`,`许昌`,`漯河`,`三门峡`,`南阳`,`商丘`,`信阳`,`周口`,`驻马店`,`济源`]},{name:`湖北`,data:[`武汉`,`黄石`,`十堰`,`宜昌`,`襄阳`,`鄂州`,`荆门`,`孝感`,`荆州`,`黄冈`,`咸宁`,`随州`,`恩施土家族苗族自治州`,`仙桃`,`潜江`,`天门`,`神农架林`]},{name:`湖南`,data:[`长沙`,`株洲`,`湘潭`,`衡阳`,`邵阳`,`岳阳`,`常德`,`张家界`,`益阳`,`郴州`,`永州`,`怀化`,`娄底`,`湘西土家族苗族自治州`]},{name:`海南`,data:[`海口`,`三亚`,`三沙`,`儋州`,`五指山`,`琼海`,`文昌`,`万宁`,`东方`,`定安县`,`屯昌县`,`澄迈县`,`临高县`,`白沙黎族自治县`,`昌江黎族自治县`,`乐东黎族自治县`,`陵水黎族自治县`,`保亭黎族苗族自治县`,`琼中黎族苗族自治县`]}],J:[{name:`吉林`,id:`J`,data:[`长春`,`吉林`,`四平`,`辽源`,`通化`,`白山`,`松原`,`白城`,`延边朝鲜族自治州`]},{name:`江苏`,data:[`南京`,`无锡`,`徐州`,`常州`,`苏州`,`南通`,`连云港`,`淮安`,`盐城`,`扬州`,`镇江`,`泰州`,`宿迁`]},{name:`江西`,data:[`南昌`,`景德镇`,`萍乡`,`九江`,`新余`,`鹰潭`,`赣州`,`吉安`,`宜春`,`抚州`,`上饶`]}],L:[{name:`辽宁`,id:`L`,data:[`沈阳`,`大连`,`鞍山`,`抚顺`,`本溪`,`丹东`,`锦州`,`营口`,`阜新`,`辽阳`,`盘锦`,`铁岭`,`朝阳`,`葫芦岛`]}],N:[{name:`内蒙古`,id:`N`,data:[`呼和浩特`,`包头`,`乌海`,`赤峰`,`通辽`,`鄂尔多斯`,`呼伦贝尔`,`巴彦淖尔`,`乌兰察布`,`兴安盟`,`锡林郭勒盟`,`阿拉善盟`]},{name:`宁夏`,data:[`银川`,`石嘴山`,`吴忠`,`固原`,`中卫`]}],Q:[{name:`青海`,id:`Q`,data:[`西宁`,`海东`,`海北藏族自治州`,`黄南藏族自治州`,`海南藏族自治州`,`果洛藏族自治州`,`玉树藏族自治州`,`海西蒙古族藏族自治州`]}],S:[{name:`山西`,id:`S`,data:[`太原`,`大同`,`阳泉`,`长治`,`晋城`,`朔州`,`晋中`,`运城`,`忻州`,`临汾`,`吕梁`]},{name:`上海`,data:[`黄浦`,`徐汇`,`长宁`,`静安`,`普陀`,`虹口`,`杨浦`,`闵行`,`宝山`,`嘉定`,`浦东新`,`金山`,`松江`,`青浦`,`奉贤`,`崇明`]},{name:`山东`,data:[`济南`,`青岛`,`淄博`,`枣庄`,`东营`,`烟台`,`潍坊`,`济宁`,`泰安`,`威海`,`日照`,`临沂`,`德州`,`聊城`,`滨州`,`菏泽`]},{name:`四川`,data:[`成都`,`自贡`,`攀枝花`,`泸州`,`德阳`,`绵阳`,`广元`,`遂宁`,`内江`,`乐山`,`南充`,`眉山`,`宜宾`,`广安`,`达州`,`雅安`,`巴中`,`资阳`,`阿坝藏族羌族自治州`,`甘孜藏族自治州`,`凉山彝族自治州`]},{name:`陕西`,data:[`西安`,`铜川`,`宝鸡`,`咸阳`,`渭南`,`延安`,`汉中`,`榆林`,`安康`,`商洛`]}],T:[{name:`天津`,id:`T`,data:[`和平`,`河东`,`河西`,`南开`,`河北`,`红桥`,`东丽`,`西青`,`津南`,`北辰`,`武清`,`宝坻`,`滨海新`,`宁河`,`静海`,`蓟州`]}],X:[{name:`西藏`,id:`X`,data:[`拉萨`,`日喀则`,`昌都`,`林芝`,`山南`,`那曲`,`阿里地`]},{name:`新疆`,data:[`乌鲁木齐`,`克拉玛依`,`吐鲁番`,`哈密`,`昌吉回族自治州`,`博尔塔拉蒙古自治州`,`巴音郭楞蒙古自治州`,`阿克苏地`,`克孜勒苏柯尔克孜自治州`,`喀什地`,`和田地`,`伊犁哈萨克自治州`,`塔城地`,`阿勒泰地`,`石河子`,`阿拉尔`,`图木舒克`,`五家渠`,`北屯`,`铁门关`,`双河`,`可克达拉`,`昆玉`,`胡杨河`]}],Y:[{name:`云南`,id:`Y`,data:[`昆明`,`曲靖`,`玉溪`,`保山`,`昭通`,`丽江`,`普洱`,`临沧`,`楚雄彝族自治州`,`红河哈尼族彝族自治州`,`文山壮族苗族自治州`,`西双版纳傣族自治州`,`大理白族自治州`,`德宏傣族景颇族自治州`,`怒江傈僳族自治州`,`迪庆藏族自治州`]}],Z:[{name:`浙江`,id:`Z`,data:[`杭州`,`宁波`,`温州`,`嘉兴`,`湖州`,`绍兴`,`金华`,`衢州`,`舟山`,`台州`,`丽水`]}],直辖市:[{name:`直辖市`,id:`直辖市`,data:[`北京`,`天津`,`上海`,`重庆`]}],港澳:[{name:`港澳`,id:`港澳`,data:[`香港`,`澳门`]}]};const T={class:`city-selector-trigger`},E={class:`city-selector-trigger__text`},D={class:`city-selector-content`},O={class:`city-selector-header`},k={key:0,class:`city-selector-letters`},A=[`onClick`],j={key:0,class:`city-list`},M=[`id`],N={class:`city-group__letter`},P={class:`city-group__cities`},F=[`onClick`],I={key:1,class:`province-list`},L=[`id`],R={class:`province-group__name`},z={class:`province-group__cities`},B=[`onClick`];var V=e(m({name:`C_City`,__name:`index`,props:{modelValue:{},placeholder:{default:`请选择城市`},showLetters:{type:Boolean,default:!0}},emits:[`update:modelValue`,`change`],setup(e,{emit:m}){let V=v(null);g(async()=>{let e=await import(`./city.js`);V.value=e.default??e});let H=m,U=v(!1),W=v(`city`),G=v(``),K=s(()=>{let e=[];return Object.values(w).forEach(t=>{e.push(...t)}),e}),q=s(()=>{if(V.value?.cities)return V.value.cities;let e={},t=1,n=new Set;K.value.forEach(e=>{e.data.forEach(e=>{n.add(e)})}),Array.from(n).forEach(n=>{let r=n[0].toUpperCase();e[r]||(e[r]=[]),e[r].push({id:t++,name:n,spell:``})});let r={};return Object.keys(e).sort().forEach(t=>{r[t]=e[t].sort((e,t)=>e.name.localeCompare(t.name,`zh-CN`))}),r}),J=s(()=>{if(W.value===`city`)return Object.keys(q.value).sort();{let e=new Set;Object.keys(w).forEach(t=>{t!==`直辖市`&&t!==`港澳`&&e.add(t)});let t=Array.from(e).sort();return t.push(`直辖市`,`港澳`),t}}),Y=s(()=>{if(W.value===`city`){let e=[];return Object.values(q.value).forEach(t=>{t.forEach(t=>{e.push({label:t.name,value:t.name})})}),e}else return K.value.flatMap(e=>e.data.map(t=>({label:`${t} (${e.name})`,value:t})))}),X=e=>{H(`update:modelValue`,e),H(`change`,e),U.value=!1},Z=e=>{e&&(X(e),G.value=``)},Q=e=>{let t=document.getElementById(`letter-${e}`);t&&t.scrollIntoView({behavior:`smooth`,block:`start`})};return(e,s)=>(_(),c(S(t),{show:U.value,"onUpdate:show":s[2]||=e=>U.value=e,placement:`bottom-start`,width:430,trigger:`click`,"show-arrow":!1},{trigger:C(()=>[b(e.$slots,`trigger`,{value:e.modelValue,visible:U.value},()=>[d(`div`,T,[d(`span`,E,x(e.modelValue||e.placeholder),1)])],!0)]),default:C(()=>[d(`div`,D,[d(`div`,O,[p(S(r),{value:W.value,"onUpdate:value":s[0]||=e=>W.value=e,size:`small`},{default:C(()=>[p(S(n),{value:`city`},{default:C(()=>s[3]||=[f(`按城市`,-1)]),_:1,__:[3]}),p(S(n),{value:`province`},{default:C(()=>s[4]||=[f(`按省份`,-1)]),_:1,__:[4]})]),_:1},8,[`value`]),p(S(a),{value:G.value,"onUpdate:value":[s[1]||=e=>G.value=e,Z],class:`city-selector-search`,options:Y.value,filterable:``,clearable:``,placeholder:`搜索城市`},null,8,[`value`,`options`])]),e.showLetters?(_(),u(`div`,k,[(_(!0),u(o,null,y(J.value,e=>(_(),u(`span`,{key:e,class:`city-selector-letter`,onClick:t=>Q(e)},x(e),9,A))),128))])):l(`v-if`,!0),p(S(i),{class:`city-selector-body`},{default:C(()=>[W.value===`city`?(_(),u(`div`,j,[(_(!0),u(o,null,y(q.value,(t,n)=>(_(),u(`div`,{key:n,id:`letter-${n}`,class:`city-group`},[d(`div`,N,x(n)+`:`,1),d(`div`,P,[(_(!0),u(o,null,y(t,(t,r)=>(_(),u(`span`,{key:`${n}-${r}`,class:h([`city-item`,{"is-active":e.modelValue===t.name}]),onClick:e=>X(t.name)},x(t.name),11,F))),128))])],8,M))),128))])):(_(),u(`div`,I,[(_(!0),u(o,null,y(K.value,t=>(_(),u(`div`,{key:t.id,id:`letter-${t.id}`,class:`province-group`},[d(`div`,R,x(t.name)+`:`,1),d(`div`,z,[(_(!0),u(o,null,y(t.data,(n,r)=>(_(),u(`span`,{key:`${t.id}-${r}`,class:h([`city-item`,{"is-active":e.modelValue===n}]),onClick:e=>X(n)},x(n),11,B))),128))])],8,L))),128))]))]),_:1})])]),_:3},8,[`show`]))}}),[[`__scopeId`,`data-v-06a335f4`]]);export{V as t};
2
2
  //# sourceMappingURL=C_City2.js.map