@robot-admin/naive-ui-components 0.4.3 → 0.4.5

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 (297) hide show
  1. package/dist/C_ActionBar.cjs +1 -5
  2. package/dist/C_ActionBar.js +1 -4
  3. package/dist/C_ActionBar.js.map +1 -0
  4. package/dist/C_ActionBar2.js +1 -195
  5. package/dist/C_ActionBar2.js.map +1 -1
  6. package/dist/{C_AntV-DGjscTWa.css.map → C_AntV-A3NXx_sF.css.map} +1 -1
  7. package/dist/C_AntV.cjs +1 -8
  8. package/dist/C_AntV.js +1 -4
  9. package/dist/C_AntV.js.map +1 -0
  10. package/dist/C_AntV2.js +4 -3149
  11. package/dist/C_AntV2.js.map +1 -1
  12. package/dist/C_Barcode.cjs +1 -4
  13. package/dist/C_Barcode.js +1 -3
  14. package/dist/C_Barcode.js.map +1 -0
  15. package/dist/C_Barcode2.js +1 -67
  16. package/dist/C_Barcode2.js.map +1 -1
  17. package/dist/C_Captcha.cjs +1 -4
  18. package/dist/C_Captcha.js +1 -3
  19. package/dist/C_Captcha.js.map +1 -0
  20. package/dist/C_Captcha2.js +1 -154
  21. package/dist/C_Captcha2.js.map +1 -1
  22. package/dist/C_Cascade.cjs +1 -4
  23. package/dist/C_Cascade.js +1 -3
  24. package/dist/C_Cascade.js.map +1 -0
  25. package/dist/C_Cascade2.js +1 -102
  26. package/dist/C_Cascade2.js.map +1 -1
  27. package/dist/C_City.cjs +1 -4
  28. package/dist/C_City.js +1 -3
  29. package/dist/C_City.js.map +1 -0
  30. package/dist/C_City2.js +1 -840
  31. package/dist/C_City2.js.map +1 -1
  32. package/dist/C_Code.cjs +1 -5
  33. package/dist/C_Code.js +1 -4
  34. package/dist/C_Code.js.map +1 -0
  35. package/dist/C_Code2.js +1 -346
  36. package/dist/C_Code2.js.map +1 -1
  37. package/dist/C_CollapsePanel.cjs +1 -6
  38. package/dist/C_CollapsePanel.js +1 -4
  39. package/dist/C_CollapsePanel.js.map +1 -0
  40. package/dist/C_CollapsePanel2.js +1 -318
  41. package/dist/C_CollapsePanel2.js.map +1 -1
  42. package/dist/C_Cron.cjs +1 -15
  43. package/dist/C_Cron.js +1 -4
  44. package/dist/C_Cron.js.map +1 -0
  45. package/dist/C_Cron2.js +1 -1209
  46. package/dist/C_Cron2.js.map +1 -1
  47. package/dist/C_Date.cjs +1 -4
  48. package/dist/C_Date.js +1 -3
  49. package/dist/C_Date.js.map +1 -0
  50. package/dist/C_Date2.js +1 -218
  51. package/dist/C_Date2.js.map +1 -1
  52. package/dist/C_Draggable.cjs +1 -5
  53. package/dist/C_Draggable.js +1 -3
  54. package/dist/C_Draggable.js.map +1 -0
  55. package/dist/C_Draggable2.js +1 -294
  56. package/dist/C_Draggable2.js.map +1 -1
  57. package/dist/C_Editor.cjs +1 -4
  58. package/dist/C_Editor.js +1 -3
  59. package/dist/C_Editor.js.map +1 -0
  60. package/dist/C_Editor2.js +1 -159
  61. package/dist/C_Editor2.js.map +1 -1
  62. package/dist/{C_FilePreview-B4XgTv-h.css.map → C_FilePreview-DHGlV4OR.css.map} +1 -1
  63. package/dist/C_FilePreview.cjs +1 -7
  64. package/dist/C_FilePreview.js +1 -4
  65. package/dist/C_FilePreview.js.map +1 -0
  66. package/dist/C_FilePreview2.js +1 -1031
  67. package/dist/C_FilePreview2.js.map +1 -1
  68. package/dist/{C_Form-DLb9FtAb.css.map → C_Form-CKHvXL3O.css.map} +1 -1
  69. package/dist/C_Form.cjs +1 -16
  70. package/dist/C_Form.js +1 -5
  71. package/dist/C_Form.js.map +1 -0
  72. package/dist/C_Form2.js +1 -2569
  73. package/dist/C_Form2.js.map +1 -1
  74. package/dist/{C_FormSearch-DlIEoh7X.css.map → C_FormSearch-DBNtnjaL.css.map} +1 -1
  75. package/dist/C_FormSearch.cjs +1 -6
  76. package/dist/C_FormSearch.js +1 -3
  77. package/dist/C_FormSearch.js.map +1 -0
  78. package/dist/C_FormSearch2.js +1 -355
  79. package/dist/C_FormSearch2.js.map +1 -1
  80. package/dist/C_FormulaEditor.cjs +1 -13
  81. package/dist/C_FormulaEditor.js +1 -4
  82. package/dist/C_FormulaEditor.js.map +1 -0
  83. package/dist/C_FormulaEditor2.js +1 -1432
  84. package/dist/C_FormulaEditor2.js.map +1 -1
  85. package/dist/C_FullCalendar-DLUdCtqf.css.map +1 -0
  86. package/dist/C_FullCalendar.cjs +1 -9
  87. package/dist/C_FullCalendar.js +1 -3
  88. package/dist/C_FullCalendar.js.map +1 -0
  89. package/dist/C_FullCalendar2.js +1 -376
  90. package/dist/C_FullCalendar2.js.map +1 -1
  91. package/dist/C_GlobalSearch.cjs +1 -5
  92. package/dist/C_GlobalSearch.js +1 -3
  93. package/dist/C_GlobalSearch.js.map +1 -0
  94. package/dist/C_GlobalSearch2.js +1 -335
  95. package/dist/C_GlobalSearch2.js.map +1 -1
  96. package/dist/C_Guide.cjs +1 -5
  97. package/dist/C_Guide.js +1 -4
  98. package/dist/C_Guide.js.map +1 -0
  99. package/dist/C_Guide2.js +1 -60
  100. package/dist/C_Guide2.js.map +1 -1
  101. package/dist/C_Icon.cjs +1 -4
  102. package/dist/C_Icon.js +1 -3
  103. package/dist/C_Icon.js.map +1 -0
  104. package/dist/C_Icon2.js +1 -285
  105. package/dist/C_Icon2.js.map +1 -1
  106. package/dist/C_ImageCropper.cjs +1 -6
  107. package/dist/C_ImageCropper.js +1 -4
  108. package/dist/C_ImageCropper.js.map +1 -0
  109. package/dist/C_ImageCropper2.js +1 -722
  110. package/dist/C_ImageCropper2.js.map +1 -1
  111. package/dist/C_Language.cjs +1 -5
  112. package/dist/C_Language.js +1 -4
  113. package/dist/C_Language.js.map +1 -0
  114. package/dist/C_Language2.js +1 -78
  115. package/dist/C_Language2.js.map +1 -1
  116. package/dist/C_Map.cjs +1 -7
  117. package/dist/C_Map.js +1 -3
  118. package/dist/C_Map.js.map +1 -0
  119. package/dist/C_Map2.js +1 -198
  120. package/dist/C_Map2.js.map +1 -1
  121. package/dist/{C_Markdown-C2uiBVSW.css.map → C_Markdown-BbTvdFCl.css.map} +1 -1
  122. package/dist/C_Markdown.cjs +1 -4
  123. package/dist/C_Markdown.js +1 -3
  124. package/dist/C_Markdown.js.map +1 -0
  125. package/dist/C_Markdown2.js +1 -185
  126. package/dist/C_Markdown2.js.map +1 -1
  127. package/dist/C_NotificationCenter.cjs +1 -20
  128. package/dist/C_NotificationCenter.js +1 -4
  129. package/dist/C_NotificationCenter.js.map +1 -0
  130. package/dist/C_NotificationCenter2.js +1 -1383
  131. package/dist/C_NotificationCenter2.js.map +1 -1
  132. package/dist/C_Progress.cjs +1 -4
  133. package/dist/C_Progress.js +1 -3
  134. package/dist/C_Progress.js.map +1 -0
  135. package/dist/C_Progress2.js +1 -102
  136. package/dist/C_Progress2.js.map +1 -1
  137. package/dist/C_QRCode.cjs +1 -5
  138. package/dist/C_QRCode.js +1 -3
  139. package/dist/C_QRCode.js.map +1 -0
  140. package/dist/C_QRCode2.js +1 -217
  141. package/dist/C_QRCode2.js.map +1 -1
  142. package/dist/C_Signature.cjs +1 -8
  143. package/dist/C_Signature.js +1 -4
  144. package/dist/C_Signature.js.map +1 -0
  145. package/dist/C_Signature2.js +1 -617
  146. package/dist/C_Signature2.js.map +1 -1
  147. package/dist/C_SplitPane.cjs +1 -6
  148. package/dist/C_SplitPane.js +1 -4
  149. package/dist/C_SplitPane.js.map +1 -0
  150. package/dist/C_SplitPane2.js +1 -355
  151. package/dist/C_SplitPane2.js.map +1 -1
  152. package/dist/C_Steps.cjs +1 -4
  153. package/dist/C_Steps.js +1 -3
  154. package/dist/C_Steps.js.map +1 -0
  155. package/dist/C_Steps2.js +1 -81
  156. package/dist/C_Steps2.js.map +1 -1
  157. package/dist/{C_Table-C55H4kPr.css.map → C_Table-tbUx-R4H.css.map} +1 -1
  158. package/dist/C_Table.cjs +1 -20
  159. package/dist/C_Table.js +1 -6
  160. package/dist/C_Table.js.map +1 -0
  161. package/dist/C_Table2.js +1 -3023
  162. package/dist/C_Table2.js.map +1 -1
  163. package/dist/C_Theme.cjs +1 -5
  164. package/dist/C_Theme.js +1 -4
  165. package/dist/C_Theme.js.map +1 -0
  166. package/dist/C_Theme2.js +1 -63
  167. package/dist/C_Theme2.js.map +1 -1
  168. package/dist/C_Time.cjs +1 -5
  169. package/dist/C_Time.js +1 -3
  170. package/dist/C_Time.js.map +1 -0
  171. package/dist/C_Time2.js +1 -198
  172. package/dist/C_Time2.js.map +1 -1
  173. package/dist/C_Tree.cjs +1 -7
  174. package/dist/C_Tree.js +1 -4
  175. package/dist/C_Tree.js.map +1 -0
  176. package/dist/C_Tree2.js +1 -440
  177. package/dist/C_Tree2.js.map +1 -1
  178. package/dist/C_Upload.cjs +1 -12
  179. package/dist/C_Upload.js +1 -4
  180. package/dist/C_Upload.js.map +1 -0
  181. package/dist/C_Upload2.js +2 -1365
  182. package/dist/C_Upload2.js.map +1 -1
  183. package/dist/C_VideoPlayer.cjs +1 -23
  184. package/dist/C_VideoPlayer.js +1 -3
  185. package/dist/C_VideoPlayer.js.map +1 -0
  186. package/dist/C_VideoPlayer2.js +3 -1932
  187. package/dist/C_VideoPlayer2.js.map +1 -1
  188. package/dist/C_VtableGantt.cjs +1 -6
  189. package/dist/C_VtableGantt.js +1 -4
  190. package/dist/C_VtableGantt.js.map +1 -0
  191. package/dist/C_VtableGantt2.js +1 -872
  192. package/dist/C_VtableGantt2.js.map +1 -1
  193. package/dist/C_WaterFall.cjs +1 -13
  194. package/dist/C_WaterFall.js +1 -3
  195. package/dist/C_WaterFall.js.map +1 -0
  196. package/dist/C_WaterFall2.js +1 -364
  197. package/dist/C_WaterFall2.js.map +1 -1
  198. package/dist/{C_WorkFlow-CSO86Cuc.css.map → C_WorkFlow-7F6op6qN.css.map} +1 -1
  199. package/dist/C_WorkFlow.cjs +1 -8
  200. package/dist/C_WorkFlow.js +1 -4
  201. package/dist/C_WorkFlow.js.map +1 -0
  202. package/dist/C_WorkFlow2.js +1 -1983
  203. package/dist/C_WorkFlow2.js.map +1 -1
  204. package/dist/chunk.js +1 -18
  205. package/dist/city.js +1 -4816
  206. package/dist/constants.d.ts +1 -1
  207. package/dist/constants2.d.ts +2 -2
  208. package/dist/constants4.d.ts +8 -8
  209. package/dist/constants5.d.ts +2 -2
  210. package/dist/data.d.ts +2 -2
  211. package/dist/export-helper.js +1 -9
  212. package/dist/index.cjs +2 -413
  213. package/dist/index.cjs.map +1 -0
  214. package/dist/index.d.cts +1 -2
  215. package/dist/index.d.ts +1 -2
  216. package/dist/index.js +1 -231
  217. package/dist/index.js.map +1 -1
  218. package/dist/index.vue.d.ts +2 -2
  219. package/dist/index10.vue.d.ts +6 -6
  220. package/dist/index12.vue.d.ts +4 -4
  221. package/dist/index13.vue.d.ts +1 -1
  222. package/dist/index14.vue.d.ts +1 -1
  223. package/dist/index15.vue.d.ts +1 -1
  224. package/dist/index16.vue.d.ts +2 -2
  225. package/dist/index2.vue.d.ts +2 -2
  226. package/dist/index3.vue.d.ts +4 -4
  227. package/dist/index5.vue.d.ts +1 -1
  228. package/dist/index7.vue.d.ts +4 -4
  229. package/dist/index8.vue.d.ts +3 -3
  230. package/dist/index9.vue.d.ts +1 -1
  231. package/dist/resolver.cjs +2 -89
  232. package/dist/resolver.cjs.map +1 -0
  233. package/dist/resolver.js +1 -86
  234. package/dist/resolver.js.map +1 -1
  235. package/dist/storage.js +1 -30
  236. package/dist/storage.js.map +1 -1
  237. package/dist/style.css +447 -447
  238. package/dist/useCalendarEvents.d.ts +1 -1
  239. package/dist/useCollapsePanel.d.ts +2 -2
  240. package/dist/useCropperCore.d.ts +3 -3
  241. package/dist/useDraggableLayout.d.ts +9 -9
  242. package/dist/useDynamicFormState.d.ts +5 -5
  243. package/dist/useEdgeInteraction.d.ts +4 -12
  244. package/dist/useInfiniteScroll.d.ts +2 -2
  245. package/dist/useModalEdit.d.ts +45 -21
  246. package/dist/useQRCode.d.ts +3 -3
  247. package/dist/useSearchState.d.ts +2 -2
  248. package/dist/useSignatureHistory.d.ts +2 -2
  249. package/dist/useSplitResize.d.ts +6 -6
  250. package/dist/useTimeSelection.d.ts +2 -2
  251. package/dist/useTreeOperations.d.ts +6 -6
  252. package/dist/useWorkflowValidation.d.ts +3 -3
  253. package/package.json +1 -2
  254. package/dist/C_FullCalendar-BULCIlVK.css.map +0 -1
  255. package/dist/constants.d.ts.map +0 -1
  256. package/dist/constants2.d.ts.map +0 -1
  257. package/dist/constants3.d.ts.map +0 -1
  258. package/dist/constants4.d.ts.map +0 -1
  259. package/dist/constants5.d.ts.map +0 -1
  260. package/dist/data.d.ts.map +0 -1
  261. package/dist/index.d.cts.map +0 -1
  262. package/dist/index.d.ts.map +0 -1
  263. package/dist/index.vue.d.ts.map +0 -1
  264. package/dist/index10.vue.d.ts.map +0 -1
  265. package/dist/index11.vue.d.ts.map +0 -1
  266. package/dist/index12.vue.d.ts.map +0 -1
  267. package/dist/index13.vue.d.ts.map +0 -1
  268. package/dist/index14.vue.d.ts.map +0 -1
  269. package/dist/index15.vue.d.ts.map +0 -1
  270. package/dist/index16.vue.d.ts.map +0 -1
  271. package/dist/index2.vue.d.ts.map +0 -1
  272. package/dist/index3.vue.d.ts.map +0 -1
  273. package/dist/index4.vue.d.ts.map +0 -1
  274. package/dist/index5.vue.d.ts.map +0 -1
  275. package/dist/index6.vue.d.ts.map +0 -1
  276. package/dist/index7.vue.d.ts.map +0 -1
  277. package/dist/index8.vue.d.ts.map +0 -1
  278. package/dist/index9.vue.d.ts.map +0 -1
  279. package/dist/resolver.d.cts.map +0 -1
  280. package/dist/resolver.d.ts.map +0 -1
  281. package/dist/useCalendarEvents.d.ts.map +0 -1
  282. package/dist/useCollapsePanel.d.ts.map +0 -1
  283. package/dist/useCropperCore.d.ts.map +0 -1
  284. package/dist/useDraggableLayout.d.ts.map +0 -1
  285. package/dist/useDynamicFormState.d.ts.map +0 -1
  286. package/dist/useEdgeInteraction.d.ts.map +0 -1
  287. package/dist/useFullscreen.d.ts.map +0 -1
  288. package/dist/useGlobalSearch.d.ts.map +0 -1
  289. package/dist/useInfiniteScroll.d.ts.map +0 -1
  290. package/dist/useModalEdit.d.ts.map +0 -1
  291. package/dist/useQRCode.d.ts.map +0 -1
  292. package/dist/useSearchState.d.ts.map +0 -1
  293. package/dist/useSignatureHistory.d.ts.map +0 -1
  294. package/dist/useSplitResize.d.ts.map +0 -1
  295. package/dist/useTimeSelection.d.ts.map +0 -1
  296. package/dist/useTreeOperations.d.ts.map +0 -1
  297. package/dist/useWorkflowValidation.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"C_City2.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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"}
1
+ {"version":3,"file":"C_City2.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":"ywIG2HA,IAAM,EAAW,EAAmD,KAAK,CAEzE,EAAU,SAAY,CACpB,IAAM,EAAM,MAAM,OAAO,aACzB,EAAS,MAAQ,EAAI,SAAW,GAChC,CAwBF,IAAM,EAAO,EAEP,EAAU,EAAI,GAAM,CACpB,EAAa,EAAyB,OAAO,CAC7C,EAAc,EAAI,GAAG,CAErB,EAAe,MAAe,CAClC,IAAM,EAA4B,EAAE,CAIpC,OAHA,OAAO,OAAO,EAAa,CAAC,QAAS,GAAU,CAC7C,EAAU,KAAK,GAAI,EAAyB,EAC5C,CACK,GACP,CAEI,EAAmB,MAAe,CACtC,GAAI,EAAS,OAAO,OAClB,OAAO,EAAS,MAAM,OAGxB,IAAM,EAAqC,EAAE,CACzC,EAAS,EACP,EAAU,IAAI,IACpB,EAAa,MAAM,QAAS,GAAa,CACvC,EAAS,KAAK,QAAS,GAAa,CAClC,EAAQ,IAAI,EAAS,EACrB,EACF,CACF,MAAM,KAAK,EAAQ,CAAC,QAAS,GAAa,CACxC,IAAM,EAAS,EAAS,GAAG,aAAa,CACnC,EAAO,KAAS,EAAO,GAAU,EAAE,EACxC,EAAO,GAAQ,KAAK,CAAE,GAAI,IAAU,KAAM,EAAU,MAAO,GAAI,CAAC,EAChE,CACF,IAAM,EAA2C,EAAE,CAQnD,OAPA,OAAO,KAAK,EAAO,CAChB,MAAM,CACN,QAAS,GAAW,CACnB,EAAa,GAAU,EAAO,GAAQ,MAAM,EAAG,IAC7C,EAAE,KAAK,cAAc,EAAE,KAAM,QAAQ,CACtC,EACD,CACG,GACP,CAEI,EAAU,MAAe,CAC7B,GAAI,EAAW,QAAU,OACvB,OAAO,OAAO,KAAK,EAAiB,MAAM,CAAC,MAAM,CAC5C,CACL,IAAM,EAAkB,IAAI,IAC5B,OAAO,KAAK,EAAa,CAAC,QAAS,GAAQ,CACrC,IAAQ,OAAS,IAAQ,MAC3B,EAAgB,IAAI,EAAI,EAE1B,CACF,IAAM,EAAS,MAAM,KAAK,EAAgB,CAAC,MAAM,CAEjD,OADA,EAAO,KAAK,MAAO,KAAK,CACjB,IAET,CAEI,EAAgB,MAA+B,CACnD,GAAI,EAAW,QAAU,OAAQ,CAC/B,IAAM,EAA0B,EAAE,CASlC,OARA,OAAO,OAAO,EAAiB,MAAM,CAAC,QAAS,GAAW,CACvD,EAAsB,QAAS,GAAS,CACvC,EAAQ,KAAK,CACX,MAAO,EAAK,KACZ,MAAO,EAAK,KACb,CAAC,EACF,EACF,CACK,OAEP,OAAO,EAAa,MAAM,QAAS,GACjC,EAAS,KAAK,IAAK,IAAU,CAC3B,MAAO,GAAG,EAAK,IAAI,EAAS,KAAK,GACjC,MAAO,EACR,EAAE,CACJ,EAEH,CAEI,EAAoB,GAA2B,CACnD,EAAK,oBAAqB,EAAS,CACnC,EAAK,SAAU,EAAS,CACxB,EAAQ,MAAQ,IAGZ,EAAsB,GAAwB,CAC9C,IACF,EAAiB,EAAM,CACvB,EAAY,MAAQ,KAIlB,EAAkB,GAAyB,CAC/C,IAAM,EAAU,SAAS,eAAe,UAAU,IAAS,CACvD,GACF,EAAQ,eAAe,CAAE,SAAU,SAAU,MAAO,QAAS,CAAC,oBAjPhE,EA2FW,EAAA,EAAA,CAAA,CA1FD,KAAM,EAAA,gCAAA,EAAO,MAAA,EACrB,UAAU,eACT,MAAO,IACR,QAAQ,QACP,aAAY,KAEF,QAAO,MAOT,CANP,EAMO,EAAA,OAAA,UAAA,CANe,MAAOA,EAAAA,WAAa,QAAS,EAAA,WAM5C,CALL,EAIM,MAJN,EAIM,CAHJ,EAES,OAFT,EAES,EADPA,EAAAA,YAAcC,EAAAA,YAAW,CAAA,EAAA,CAAA,CAAA,CAAA,CAAA,GAAA,CAAA,CAAA,eA+E3B,CAzEN,EAyEM,MAzEN,EAyEM,CAxEJ,EAcM,MAdN,EAcM,CAbJ,EAGc,EAAA,EAAA,CAAA,CAHO,MAAO,EAAA,iCAAA,EAAU,MAAA,EAAE,KAAK,wBACE,CAA7C,EAA6C,EAAA,EAAA,CAAA,CAA/B,MAAM,OAAM,CAAA,eAAI,AAAA,EAAA,KAAA,CAAA,EAAH,MAAG,GAAA,CAAA,CAAA,cAC9B,EAAiD,EAAA,EAAA,CAAA,CAAnC,MAAM,WAAU,CAAA,eAAI,AAAA,EAAA,KAAA,CAAA,EAAH,MAAG,GAAA,CAAA,CAAA,kCAEpC,EAQE,EAAA,EAAA,CAAA,CAPQ,MAAO,EAAA,kCAAA,EAAW,MAAA,EAMX,EAAA,CALf,MAAM,uBACL,QAAS,EAAA,MACV,WAAA,GACA,UAAA,GACA,YAAY,sCAKLC,EAAAA,aAAAA,GAAAA,CAAX,EASM,MATN,EASM,EAAA,EAAA,GAAA,CARJ,EAOO,EAAA,KAAA,EANY,EAAA,MAAV,QADT,EAOO,OAAA,CALJ,IAAK,EACN,MAAM,uBACL,QAAK,GAAE,EAAe,EAAM,IAE1B,EAAM,CAAA,EAAA,EAAA,yBAIb,EA4Ca,EAAA,EAAA,CAAA,CA5CD,MAAM,qBAAoB,CAAA,eAuBV,CAtBf,EAAA,QAAU,QAAA,GAAA,CAArB,EAoBM,MApBN,EAoBM,EAAA,EAAA,GAAA,CAnBJ,EAkBM,EAAA,KAAA,EAjBuB,EAAA,OAAnB,EAAQ,SADlB,EAkBM,MAAA,CAhBH,IAAK,EACL,GAAE,UAAY,IACf,MAAM,eAEN,EAAmD,MAAnD,EAAmD,EAAhB,EAAM,CAAG,IAAC,EAAA,CAC7C,EAUM,MAVN,EAUM,EAAA,EAAA,GAAA,CATJ,EAQO,EAAA,KAAA,EAPmB,GAAhB,EAAM,SADhB,EAQO,OAAA,CANJ,IAAG,GAAK,EAAM,GAAI,IACnB,MAAK,EAAA,CAAC,YAAW,CAAA,YACMF,EAAAA,aAAe,EAAK,KAAI,CAAA,CAAA,CAC9C,QAAK,GAAE,EAAiB,EAAK,KAAI,IAE/B,EAAK,KAAI,CAAA,GAAA,EAAA,iCAMpB,EAoBM,MApBN,EAoBM,EAAA,EAAA,GAAA,CAnBJ,EAkBM,EAAA,KAAA,EAjBe,EAAA,MAAZ,QADT,EAkBM,MAAA,CAhBH,IAAK,EAAS,GACd,GAAE,UAAY,EAAS,KACxB,MAAM,mBAEN,EAA4D,MAA5D,EAA4D,EAAvB,EAAS,KAAI,CAAG,IAAC,EAAA,CACtD,EAUM,MAVN,EAUM,EAAA,EAAA,GAAA,CATJ,EAQO,EAAA,KAAA,EAPmB,EAAS,MAAzB,EAAM,SADhB,EAQO,OAAA,CANJ,IAAG,GAAK,EAAS,GAAE,GAAI,IACxB,MAAK,EAAA,CAAC,YAAW,CAAA,YACMA,EAAAA,aAAe,EAAI,CAAA,CAAA,CACzC,QAAK,GAAE,EAAiB,EAAI,IAE1B,EAAI,CAAA,GAAA,EAAA"}
package/dist/C_Code.cjs CHANGED
@@ -1,5 +1 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- require('./C_Icon.js');
3
- const require_C_Code = require('./C_Code.js');
4
-
5
- exports.C_Code = require_C_Code.C_Code_default;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./C_Icon.js`);const e=require(`./C_Code.js`);exports.C_Code=e.t;
package/dist/C_Code.js CHANGED
@@ -1,4 +1 @@
1
- import "./C_Icon2.js";
2
- import { t as C_Code_default } from "./C_Code2.js";
3
-
4
- export { C_Code_default as C_Code };
1
+ import"./C_Icon2.js";import{t as e}from"./C_Code2.js";export{e as C_Code};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"C_Code.js","names":["showHeader","title","language","showFullscreen","code","showLineNumbers","wordWrap","trim"],"sources":["../src/components/C_Code/index.vue","../src/components/C_Code/index.vue","../src/components/C_Code/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-19\r\n * @Description: 全局代码高亮组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-code-wrapper\">\r\n <!-- 代码标题栏 -->\r\n <div v-if=\"showHeader\" class=\"c-code-header\">\r\n <div class=\"c-code-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <div class=\"c-code-actions\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"tiny\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n\r\n <NTooltip v-if=\"showFullscreen\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton size=\"tiny\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon\r\n :name=\"\r\n isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen'\r\n \"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n {{ isFullscreen ? \"退出全屏\" : \"全屏查看\" }}\r\n </NTooltip>\r\n </div>\r\n </div>\r\n\r\n <!-- 代码内容区域 -->\r\n <div class=\"c-code-content\">\r\n <div\r\n class=\"code-wrapper\"\r\n @mouseenter=\"showFloatingCopy = true\"\r\n @mouseleave=\"showFloatingCopy = false\"\r\n >\r\n <NCode\r\n :key=\"`code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n :style=\"codeStyle\"\r\n @click=\"emit('click', $event)\"\r\n />\r\n\r\n <!-- 悬浮复制按钮 -->\r\n <Transition name=\"fade\">\r\n <div v-if=\"showFloatingCopy && !showHeader\" class=\"floating-copy-btn\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"small\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n class=\"copy-floating\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n </div>\r\n </Transition>\r\n </div>\r\n\r\n <!-- 语言加载状态 -->\r\n <div v-if=\"languageLoading\" class=\"c-code-loading\">\r\n <NSpin size=\"small\" />\r\n <span class=\"ml-2\">正在加载 {{ language }} 语言包...</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 全屏模态框 -->\r\n <NModal\r\n v-model:show=\"isFullscreen\"\r\n :mask-closable=\"false\"\r\n :show-icon=\"false\"\r\n :bordered=\"false\"\r\n style=\"width: 100vw; height: 100vh; margin: 0; padding: 0\"\r\n >\r\n <div class=\"fullscreen-content\">\r\n <div class=\"fullscreen-header\">\r\n <div class=\"fullscreen-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <NButton size=\"small\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n <div class=\"fullscreen-body\">\r\n <NCode\r\n :key=\"`fullscreen-code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n />\r\n </div>\r\n </div>\r\n </NModal>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n onMounted,\r\n onUnmounted,\r\n nextTick,\r\n inject,\r\n} from \"vue\";\r\nimport C_Icon from \"../C_Icon/index.vue\";\r\n\r\n// 直接在组件内定义 HighlightManager 接口,避免导入错误\r\ninterface HighlightManager {\r\n getHljs?: () => any;\r\n loadLanguage?: (language: string) => Promise<void>;\r\n getLoadedLanguages?: () => string[];\r\n}\r\n\r\n// 组件名称定义\r\ndefineOptions({\r\n name: \"CCode\",\r\n});\r\n\r\ninterface Props {\r\n code: string;\r\n language?: string;\r\n title?: string;\r\n showHeader?: boolean;\r\n showLineNumbers?: boolean;\r\n wordWrap?: boolean;\r\n trim?: boolean;\r\n showFullscreen?: boolean;\r\n maxHeight?: string | number;\r\n autoLoadLanguage?: boolean;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n code: \"\",\r\n language: \"text\",\r\n showHeader: true,\r\n showLineNumbers: true,\r\n wordWrap: false,\r\n trim: true,\r\n showFullscreen: false,\r\n autoLoadLanguage: true,\r\n});\r\n\r\nconst emit = defineEmits<{\r\n copy: [code: string];\r\n click: [event: MouseEvent];\r\n fullscreen: [isFullscreen: boolean];\r\n}>();\r\n\r\n// 尝试获取 highlight 实例(如果用户安装了插件)\r\nconst highlightManager = inject<HighlightManager | null>(\r\n \"highlightManager\",\r\n null,\r\n);\r\n\r\nconst copying = ref(false);\r\nconst isFullscreen = ref(false);\r\nconst languageLoading = ref(false);\r\nconst showFloatingCopy = ref(false);\r\n\r\n// 安全地获取 hljs 实例\r\nconst hljs = computed(() => {\r\n try {\r\n return highlightManager?.getHljs?.() || null;\r\n } catch (error) {\r\n console.warn(\"Highlight.js not available:\", error);\r\n return null;\r\n }\r\n});\r\n\r\n// 语言图标映射(使用标准的 MDI 图标名称)\r\nconst languageIcon = computed(() => {\r\n const iconMap: Record<string, string> = {\r\n javascript: \"mdi:language-javascript\",\r\n typescript: \"mdi:language-typescript\",\r\n python: \"mdi:language-python\",\r\n html: \"mdi:language-html5\",\r\n css: \"mdi:language-css3\",\r\n vue: \"mdi:vuejs\",\r\n react: \"mdi:react\",\r\n json: \"mdi:code-json\",\r\n java: \"mdi:language-java\",\r\n cpp: \"mdi:language-cpp\",\r\n go: \"mdi:language-go\",\r\n rust: \"mdi:language-rust\",\r\n php: \"mdi:language-php\",\r\n csharp: \"mdi:language-csharp\",\r\n sql: \"mdi:database\",\r\n yaml: \"mdi:file-code\",\r\n xml: \"mdi:xml\",\r\n markdown: \"mdi:language-markdown\",\r\n bash: \"mdi:bash\",\r\n shell: \"mdi:console\",\r\n powershell: \"mdi:powershell\",\r\n swift: \"mdi:language-swift\",\r\n kotlin: \"mdi:language-kotlin\",\r\n ruby: \"mdi:language-ruby\",\r\n };\r\n return iconMap[props.language.toLowerCase()] || \"mdi:code-braces\";\r\n});\r\n\r\n// 代码样式\r\nconst codeStyle = computed(() => {\r\n if (!props.maxHeight) return {};\r\n return {\r\n maxHeight:\r\n typeof props.maxHeight === \"number\"\r\n ? `${props.maxHeight}px`\r\n : props.maxHeight,\r\n };\r\n});\r\n\r\n// 自动加载语言包\r\nwatch(\r\n () => props.language,\r\n async (newLanguage) => {\r\n if (!props.autoLoadLanguage || newLanguage === \"text\" || !highlightManager)\r\n return;\r\n\r\n const loadedLanguages = highlightManager.getLoadedLanguages?.() || [];\r\n if (loadedLanguages.includes(newLanguage)) return;\r\n\r\n languageLoading.value = true;\r\n try {\r\n await highlightManager.loadLanguage?.(newLanguage);\r\n await nextTick();\r\n } catch (error) {\r\n console.warn(`Failed to load language: ${newLanguage}`, error);\r\n } finally {\r\n languageLoading.value = false;\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n\r\n/**\r\n * 复制代码到剪贴板\r\n */\r\nasync function copyCode() {\r\n if (copying.value) return;\r\n copying.value = true;\r\n\r\n try {\r\n await navigator.clipboard.writeText(props.code);\r\n emit(\"copy\", props.code);\r\n } catch (error) {\r\n console.error(\"Copy failed:\", error);\r\n } finally {\r\n copying.value = false;\r\n }\r\n}\r\n\r\n/**\r\n * 切换全屏显示状态\r\n */\r\nfunction toggleFullscreen() {\r\n isFullscreen.value = !isFullscreen.value;\r\n emit(\"fullscreen\", isFullscreen.value);\r\n}\r\n\r\n/**\r\n * 获取编程语言的显示标题\r\n */\r\nfunction getLanguageTitle(lang: string): string {\r\n const titleMap: Record<string, string> = {\r\n javascript: \"JavaScript\",\r\n typescript: \"TypeScript\",\r\n python: \"Python\",\r\n java: \"Java\",\r\n cpp: \"C++\",\r\n csharp: \"C#\",\r\n php: \"PHP\",\r\n go: \"Go\",\r\n rust: \"Rust\",\r\n html: \"HTML\",\r\n css: \"CSS\",\r\n json: \"JSON\",\r\n bash: \"Bash\",\r\n shell: \"Shell\",\r\n yaml: \"YAML\",\r\n xml: \"XML\",\r\n markdown: \"Markdown\",\r\n sql: \"SQL\",\r\n powershell: \"PowerShell\",\r\n swift: \"Swift\",\r\n kotlin: \"Kotlin\",\r\n ruby: \"Ruby\",\r\n vue: \"Vue\",\r\n react: \"React\",\r\n };\r\n return titleMap[lang.toLowerCase()] || lang.toUpperCase();\r\n}\r\n\r\n/**\r\n * 处理ESC键退出全屏\r\n */\r\nfunction handleEscapeKey(event: KeyboardEvent) {\r\n if (event.key === \"Escape\" && isFullscreen.value) {\r\n toggleFullscreen();\r\n }\r\n}\r\n\r\nonMounted(() => {\r\n document.addEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\nonUnmounted(() => {\r\n document.removeEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\ndefineExpose({\r\n copyCode,\r\n toggleFullscreen,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.mr-2 {\r\n margin-right: 0.5rem;\r\n}\r\n\r\n.ml-2 {\r\n margin-left: 0.5rem;\r\n}\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-19\r\n * @Description: 全局代码高亮组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-code-wrapper\">\r\n <!-- 代码标题栏 -->\r\n <div v-if=\"showHeader\" class=\"c-code-header\">\r\n <div class=\"c-code-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <div class=\"c-code-actions\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"tiny\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n\r\n <NTooltip v-if=\"showFullscreen\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton size=\"tiny\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon\r\n :name=\"\r\n isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen'\r\n \"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n {{ isFullscreen ? \"退出全屏\" : \"全屏查看\" }}\r\n </NTooltip>\r\n </div>\r\n </div>\r\n\r\n <!-- 代码内容区域 -->\r\n <div class=\"c-code-content\">\r\n <div\r\n class=\"code-wrapper\"\r\n @mouseenter=\"showFloatingCopy = true\"\r\n @mouseleave=\"showFloatingCopy = false\"\r\n >\r\n <NCode\r\n :key=\"`code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n :style=\"codeStyle\"\r\n @click=\"emit('click', $event)\"\r\n />\r\n\r\n <!-- 悬浮复制按钮 -->\r\n <Transition name=\"fade\">\r\n <div v-if=\"showFloatingCopy && !showHeader\" class=\"floating-copy-btn\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"small\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n class=\"copy-floating\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n </div>\r\n </Transition>\r\n </div>\r\n\r\n <!-- 语言加载状态 -->\r\n <div v-if=\"languageLoading\" class=\"c-code-loading\">\r\n <NSpin size=\"small\" />\r\n <span class=\"ml-2\">正在加载 {{ language }} 语言包...</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 全屏模态框 -->\r\n <NModal\r\n v-model:show=\"isFullscreen\"\r\n :mask-closable=\"false\"\r\n :show-icon=\"false\"\r\n :bordered=\"false\"\r\n style=\"width: 100vw; height: 100vh; margin: 0; padding: 0\"\r\n >\r\n <div class=\"fullscreen-content\">\r\n <div class=\"fullscreen-header\">\r\n <div class=\"fullscreen-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <NButton size=\"small\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n <div class=\"fullscreen-body\">\r\n <NCode\r\n :key=\"`fullscreen-code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n />\r\n </div>\r\n </div>\r\n </NModal>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n onMounted,\r\n onUnmounted,\r\n nextTick,\r\n inject,\r\n} from \"vue\";\r\nimport C_Icon from \"../C_Icon/index.vue\";\r\n\r\n// 直接在组件内定义 HighlightManager 接口,避免导入错误\r\ninterface HighlightManager {\r\n getHljs?: () => any;\r\n loadLanguage?: (language: string) => Promise<void>;\r\n getLoadedLanguages?: () => string[];\r\n}\r\n\r\n// 组件名称定义\r\ndefineOptions({\r\n name: \"CCode\",\r\n});\r\n\r\ninterface Props {\r\n code: string;\r\n language?: string;\r\n title?: string;\r\n showHeader?: boolean;\r\n showLineNumbers?: boolean;\r\n wordWrap?: boolean;\r\n trim?: boolean;\r\n showFullscreen?: boolean;\r\n maxHeight?: string | number;\r\n autoLoadLanguage?: boolean;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n code: \"\",\r\n language: \"text\",\r\n showHeader: true,\r\n showLineNumbers: true,\r\n wordWrap: false,\r\n trim: true,\r\n showFullscreen: false,\r\n autoLoadLanguage: true,\r\n});\r\n\r\nconst emit = defineEmits<{\r\n copy: [code: string];\r\n click: [event: MouseEvent];\r\n fullscreen: [isFullscreen: boolean];\r\n}>();\r\n\r\n// 尝试获取 highlight 实例(如果用户安装了插件)\r\nconst highlightManager = inject<HighlightManager | null>(\r\n \"highlightManager\",\r\n null,\r\n);\r\n\r\nconst copying = ref(false);\r\nconst isFullscreen = ref(false);\r\nconst languageLoading = ref(false);\r\nconst showFloatingCopy = ref(false);\r\n\r\n// 安全地获取 hljs 实例\r\nconst hljs = computed(() => {\r\n try {\r\n return highlightManager?.getHljs?.() || null;\r\n } catch (error) {\r\n console.warn(\"Highlight.js not available:\", error);\r\n return null;\r\n }\r\n});\r\n\r\n// 语言图标映射(使用标准的 MDI 图标名称)\r\nconst languageIcon = computed(() => {\r\n const iconMap: Record<string, string> = {\r\n javascript: \"mdi:language-javascript\",\r\n typescript: \"mdi:language-typescript\",\r\n python: \"mdi:language-python\",\r\n html: \"mdi:language-html5\",\r\n css: \"mdi:language-css3\",\r\n vue: \"mdi:vuejs\",\r\n react: \"mdi:react\",\r\n json: \"mdi:code-json\",\r\n java: \"mdi:language-java\",\r\n cpp: \"mdi:language-cpp\",\r\n go: \"mdi:language-go\",\r\n rust: \"mdi:language-rust\",\r\n php: \"mdi:language-php\",\r\n csharp: \"mdi:language-csharp\",\r\n sql: \"mdi:database\",\r\n yaml: \"mdi:file-code\",\r\n xml: \"mdi:xml\",\r\n markdown: \"mdi:language-markdown\",\r\n bash: \"mdi:bash\",\r\n shell: \"mdi:console\",\r\n powershell: \"mdi:powershell\",\r\n swift: \"mdi:language-swift\",\r\n kotlin: \"mdi:language-kotlin\",\r\n ruby: \"mdi:language-ruby\",\r\n };\r\n return iconMap[props.language.toLowerCase()] || \"mdi:code-braces\";\r\n});\r\n\r\n// 代码样式\r\nconst codeStyle = computed(() => {\r\n if (!props.maxHeight) return {};\r\n return {\r\n maxHeight:\r\n typeof props.maxHeight === \"number\"\r\n ? `${props.maxHeight}px`\r\n : props.maxHeight,\r\n };\r\n});\r\n\r\n// 自动加载语言包\r\nwatch(\r\n () => props.language,\r\n async (newLanguage) => {\r\n if (!props.autoLoadLanguage || newLanguage === \"text\" || !highlightManager)\r\n return;\r\n\r\n const loadedLanguages = highlightManager.getLoadedLanguages?.() || [];\r\n if (loadedLanguages.includes(newLanguage)) return;\r\n\r\n languageLoading.value = true;\r\n try {\r\n await highlightManager.loadLanguage?.(newLanguage);\r\n await nextTick();\r\n } catch (error) {\r\n console.warn(`Failed to load language: ${newLanguage}`, error);\r\n } finally {\r\n languageLoading.value = false;\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n\r\n/**\r\n * 复制代码到剪贴板\r\n */\r\nasync function copyCode() {\r\n if (copying.value) return;\r\n copying.value = true;\r\n\r\n try {\r\n await navigator.clipboard.writeText(props.code);\r\n emit(\"copy\", props.code);\r\n } catch (error) {\r\n console.error(\"Copy failed:\", error);\r\n } finally {\r\n copying.value = false;\r\n }\r\n}\r\n\r\n/**\r\n * 切换全屏显示状态\r\n */\r\nfunction toggleFullscreen() {\r\n isFullscreen.value = !isFullscreen.value;\r\n emit(\"fullscreen\", isFullscreen.value);\r\n}\r\n\r\n/**\r\n * 获取编程语言的显示标题\r\n */\r\nfunction getLanguageTitle(lang: string): string {\r\n const titleMap: Record<string, string> = {\r\n javascript: \"JavaScript\",\r\n typescript: \"TypeScript\",\r\n python: \"Python\",\r\n java: \"Java\",\r\n cpp: \"C++\",\r\n csharp: \"C#\",\r\n php: \"PHP\",\r\n go: \"Go\",\r\n rust: \"Rust\",\r\n html: \"HTML\",\r\n css: \"CSS\",\r\n json: \"JSON\",\r\n bash: \"Bash\",\r\n shell: \"Shell\",\r\n yaml: \"YAML\",\r\n xml: \"XML\",\r\n markdown: \"Markdown\",\r\n sql: \"SQL\",\r\n powershell: \"PowerShell\",\r\n swift: \"Swift\",\r\n kotlin: \"Kotlin\",\r\n ruby: \"Ruby\",\r\n vue: \"Vue\",\r\n react: \"React\",\r\n };\r\n return titleMap[lang.toLowerCase()] || lang.toUpperCase();\r\n}\r\n\r\n/**\r\n * 处理ESC键退出全屏\r\n */\r\nfunction handleEscapeKey(event: KeyboardEvent) {\r\n if (event.key === \"Escape\" && isFullscreen.value) {\r\n toggleFullscreen();\r\n }\r\n}\r\n\r\nonMounted(() => {\r\n document.addEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\nonUnmounted(() => {\r\n document.removeEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\ndefineExpose({\r\n copyCode,\r\n toggleFullscreen,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.mr-2 {\r\n margin-right: 0.5rem;\r\n}\r\n\r\n.ml-2 {\r\n margin-left: 0.5rem;\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-19\r\n * @Description: 全局代码高亮组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-code-wrapper\">\r\n <!-- 代码标题栏 -->\r\n <div v-if=\"showHeader\" class=\"c-code-header\">\r\n <div class=\"c-code-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <div class=\"c-code-actions\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"tiny\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n\r\n <NTooltip v-if=\"showFullscreen\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton size=\"tiny\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon\r\n :name=\"\r\n isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen'\r\n \"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n {{ isFullscreen ? \"退出全屏\" : \"全屏查看\" }}\r\n </NTooltip>\r\n </div>\r\n </div>\r\n\r\n <!-- 代码内容区域 -->\r\n <div class=\"c-code-content\">\r\n <div\r\n class=\"code-wrapper\"\r\n @mouseenter=\"showFloatingCopy = true\"\r\n @mouseleave=\"showFloatingCopy = false\"\r\n >\r\n <NCode\r\n :key=\"`code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n :style=\"codeStyle\"\r\n @click=\"emit('click', $event)\"\r\n />\r\n\r\n <!-- 悬浮复制按钮 -->\r\n <Transition name=\"fade\">\r\n <div v-if=\"showFloatingCopy && !showHeader\" class=\"floating-copy-btn\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"small\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n class=\"copy-floating\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n </div>\r\n </Transition>\r\n </div>\r\n\r\n <!-- 语言加载状态 -->\r\n <div v-if=\"languageLoading\" class=\"c-code-loading\">\r\n <NSpin size=\"small\" />\r\n <span class=\"ml-2\">正在加载 {{ language }} 语言包...</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 全屏模态框 -->\r\n <NModal\r\n v-model:show=\"isFullscreen\"\r\n :mask-closable=\"false\"\r\n :show-icon=\"false\"\r\n :bordered=\"false\"\r\n style=\"width: 100vw; height: 100vh; margin: 0; padding: 0\"\r\n >\r\n <div class=\"fullscreen-content\">\r\n <div class=\"fullscreen-header\">\r\n <div class=\"fullscreen-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <NButton size=\"small\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n <div class=\"fullscreen-body\">\r\n <NCode\r\n :key=\"`fullscreen-code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n />\r\n </div>\r\n </div>\r\n </NModal>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n onMounted,\r\n onUnmounted,\r\n nextTick,\r\n inject,\r\n} from \"vue\";\r\nimport C_Icon from \"../C_Icon/index.vue\";\r\n\r\n// 直接在组件内定义 HighlightManager 接口,避免导入错误\r\ninterface HighlightManager {\r\n getHljs?: () => any;\r\n loadLanguage?: (language: string) => Promise<void>;\r\n getLoadedLanguages?: () => string[];\r\n}\r\n\r\n// 组件名称定义\r\ndefineOptions({\r\n name: \"CCode\",\r\n});\r\n\r\ninterface Props {\r\n code: string;\r\n language?: string;\r\n title?: string;\r\n showHeader?: boolean;\r\n showLineNumbers?: boolean;\r\n wordWrap?: boolean;\r\n trim?: boolean;\r\n showFullscreen?: boolean;\r\n maxHeight?: string | number;\r\n autoLoadLanguage?: boolean;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n code: \"\",\r\n language: \"text\",\r\n showHeader: true,\r\n showLineNumbers: true,\r\n wordWrap: false,\r\n trim: true,\r\n showFullscreen: false,\r\n autoLoadLanguage: true,\r\n});\r\n\r\nconst emit = defineEmits<{\r\n copy: [code: string];\r\n click: [event: MouseEvent];\r\n fullscreen: [isFullscreen: boolean];\r\n}>();\r\n\r\n// 尝试获取 highlight 实例(如果用户安装了插件)\r\nconst highlightManager = inject<HighlightManager | null>(\r\n \"highlightManager\",\r\n null,\r\n);\r\n\r\nconst copying = ref(false);\r\nconst isFullscreen = ref(false);\r\nconst languageLoading = ref(false);\r\nconst showFloatingCopy = ref(false);\r\n\r\n// 安全地获取 hljs 实例\r\nconst hljs = computed(() => {\r\n try {\r\n return highlightManager?.getHljs?.() || null;\r\n } catch (error) {\r\n console.warn(\"Highlight.js not available:\", error);\r\n return null;\r\n }\r\n});\r\n\r\n// 语言图标映射(使用标准的 MDI 图标名称)\r\nconst languageIcon = computed(() => {\r\n const iconMap: Record<string, string> = {\r\n javascript: \"mdi:language-javascript\",\r\n typescript: \"mdi:language-typescript\",\r\n python: \"mdi:language-python\",\r\n html: \"mdi:language-html5\",\r\n css: \"mdi:language-css3\",\r\n vue: \"mdi:vuejs\",\r\n react: \"mdi:react\",\r\n json: \"mdi:code-json\",\r\n java: \"mdi:language-java\",\r\n cpp: \"mdi:language-cpp\",\r\n go: \"mdi:language-go\",\r\n rust: \"mdi:language-rust\",\r\n php: \"mdi:language-php\",\r\n csharp: \"mdi:language-csharp\",\r\n sql: \"mdi:database\",\r\n yaml: \"mdi:file-code\",\r\n xml: \"mdi:xml\",\r\n markdown: \"mdi:language-markdown\",\r\n bash: \"mdi:bash\",\r\n shell: \"mdi:console\",\r\n powershell: \"mdi:powershell\",\r\n swift: \"mdi:language-swift\",\r\n kotlin: \"mdi:language-kotlin\",\r\n ruby: \"mdi:language-ruby\",\r\n };\r\n return iconMap[props.language.toLowerCase()] || \"mdi:code-braces\";\r\n});\r\n\r\n// 代码样式\r\nconst codeStyle = computed(() => {\r\n if (!props.maxHeight) return {};\r\n return {\r\n maxHeight:\r\n typeof props.maxHeight === \"number\"\r\n ? `${props.maxHeight}px`\r\n : props.maxHeight,\r\n };\r\n});\r\n\r\n// 自动加载语言包\r\nwatch(\r\n () => props.language,\r\n async (newLanguage) => {\r\n if (!props.autoLoadLanguage || newLanguage === \"text\" || !highlightManager)\r\n return;\r\n\r\n const loadedLanguages = highlightManager.getLoadedLanguages?.() || [];\r\n if (loadedLanguages.includes(newLanguage)) return;\r\n\r\n languageLoading.value = true;\r\n try {\r\n await highlightManager.loadLanguage?.(newLanguage);\r\n await nextTick();\r\n } catch (error) {\r\n console.warn(`Failed to load language: ${newLanguage}`, error);\r\n } finally {\r\n languageLoading.value = false;\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n\r\n/**\r\n * 复制代码到剪贴板\r\n */\r\nasync function copyCode() {\r\n if (copying.value) return;\r\n copying.value = true;\r\n\r\n try {\r\n await navigator.clipboard.writeText(props.code);\r\n emit(\"copy\", props.code);\r\n } catch (error) {\r\n console.error(\"Copy failed:\", error);\r\n } finally {\r\n copying.value = false;\r\n }\r\n}\r\n\r\n/**\r\n * 切换全屏显示状态\r\n */\r\nfunction toggleFullscreen() {\r\n isFullscreen.value = !isFullscreen.value;\r\n emit(\"fullscreen\", isFullscreen.value);\r\n}\r\n\r\n/**\r\n * 获取编程语言的显示标题\r\n */\r\nfunction getLanguageTitle(lang: string): string {\r\n const titleMap: Record<string, string> = {\r\n javascript: \"JavaScript\",\r\n typescript: \"TypeScript\",\r\n python: \"Python\",\r\n java: \"Java\",\r\n cpp: \"C++\",\r\n csharp: \"C#\",\r\n php: \"PHP\",\r\n go: \"Go\",\r\n rust: \"Rust\",\r\n html: \"HTML\",\r\n css: \"CSS\",\r\n json: \"JSON\",\r\n bash: \"Bash\",\r\n shell: \"Shell\",\r\n yaml: \"YAML\",\r\n xml: \"XML\",\r\n markdown: \"Markdown\",\r\n sql: \"SQL\",\r\n powershell: \"PowerShell\",\r\n swift: \"Swift\",\r\n kotlin: \"Kotlin\",\r\n ruby: \"Ruby\",\r\n vue: \"Vue\",\r\n react: \"React\",\r\n };\r\n return titleMap[lang.toLowerCase()] || lang.toUpperCase();\r\n}\r\n\r\n/**\r\n * 处理ESC键退出全屏\r\n */\r\nfunction handleEscapeKey(event: KeyboardEvent) {\r\n if (event.key === \"Escape\" && isFullscreen.value) {\r\n toggleFullscreen();\r\n }\r\n}\r\n\r\nonMounted(() => {\r\n document.addEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\nonUnmounted(() => {\r\n document.removeEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\ndefineExpose({\r\n copyCode,\r\n toggleFullscreen,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.mr-2 {\r\n margin-right: 0.5rem;\r\n}\r\n\r\n.ml-2 {\r\n margin-left: 0.5rem;\r\n}\r\n</style>\r\n"],"mappings":"m4BE2LA,IAAM,EAAQ,EAWR,EAAO,EAOP,GAAA,EAAA,EAAA,QACJ,mBACA,KACD,CAEK,GAAA,EAAA,EAAA,KAAc,GAAM,CACpB,GAAA,EAAA,EAAA,KAAmB,GAAM,CACzB,GAAA,EAAA,EAAA,KAAsB,GAAM,CAC5B,GAAA,EAAA,EAAA,KAAuB,GAAM,CAG7B,GAAA,EAAA,EAAA,cAAsB,CAC1B,GAAI,CACF,OAAO,GAAkB,WAAW,EAAI,WACjC,EAAO,CAEd,OADA,QAAQ,KAAK,8BAA+B,EAAM,CAC3C,OAET,CAGI,GAAA,EAAA,EAAA,eACoC,CACtC,WAAY,0BACZ,WAAY,0BACZ,OAAQ,sBACR,KAAM,qBACN,IAAK,oBACL,IAAK,YACL,MAAO,YACP,KAAM,gBACN,KAAM,oBACN,IAAK,mBACL,GAAI,kBACJ,KAAM,oBACN,IAAK,mBACL,OAAQ,sBACR,IAAK,eACL,KAAM,gBACN,IAAK,UACL,SAAU,wBACV,KAAM,WACN,MAAO,cACP,WAAY,iBACZ,MAAO,qBACP,OAAQ,sBACR,KAAM,oBACP,EACc,EAAM,SAAS,aAAa,GAAK,kBAChD,CAGI,GAAA,EAAA,EAAA,cACC,EAAM,UACJ,CACL,UACE,OAAO,EAAM,WAAc,SACvB,GAAG,EAAM,UAAU,IACnB,EAAM,UACb,CAN4B,EAAE,CAO/B,EAGF,EAAA,EAAA,WACQ,EAAM,SACZ,KAAO,IAAgB,CACjB,MAAC,EAAM,kBAAoB,IAAgB,QAAU,CAAC,IAI1D,EADwB,EAAiB,sBAAsB,EAAI,EAAE,EACjD,SAAS,EAAY,CAEzC,GAAgB,MAAQ,GACxB,GAAI,CACF,MAAM,EAAiB,eAAe,EAAY,CAClD,MAAA,EAAA,EAAA,WAAgB,OACT,EAAO,CACd,QAAQ,KAAK,4BAA4B,IAAe,EAAM,QACtD,CACR,EAAgB,MAAQ,MAG5B,CAAE,UAAW,GAAM,CACpB,CAKD,eAAe,GAAW,CACpB,MAAQ,MACZ,GAAQ,MAAQ,GAEhB,GAAI,CACF,MAAM,UAAU,UAAU,UAAU,EAAM,KAAK,CAC/C,EAAK,OAAQ,EAAM,KAAK,OACjB,EAAO,CACd,QAAQ,MAAM,eAAgB,EAAM,QAC5B,CACR,EAAQ,MAAQ,KAOpB,SAAS,GAAmB,CAC1B,EAAa,MAAQ,CAAC,EAAa,MACnC,EAAK,aAAc,EAAa,MAAM,CAMxC,SAAS,EAAiB,EAAsB,CA2B9C,MA1ByC,CACvC,WAAY,aACZ,WAAY,aACZ,OAAQ,SACR,KAAM,OACN,IAAK,MACL,OAAQ,KACR,IAAK,MACL,GAAI,KACJ,KAAM,OACN,KAAM,OACN,IAAK,MACL,KAAM,OACN,KAAM,OACN,MAAO,QACP,KAAM,OACN,IAAK,MACL,SAAU,WACV,IAAK,MACL,WAAY,aACZ,MAAO,QACP,OAAQ,SACR,KAAM,OACN,IAAK,MACL,MAAO,QACR,CACe,EAAK,aAAa,GAAK,EAAK,aAAa,CAM3D,SAAS,EAAgB,EAAsB,CACzC,EAAM,MAAQ,UAAY,EAAa,OACzC,GAAkB,QAItB,EAAA,EAAA,eAAgB,CACd,SAAS,iBAAiB,UAAW,EAAgB,EACrD,EAEF,EAAA,EAAA,iBAAkB,CAChB,SAAS,oBAAoB,UAAW,EAAgB,EACxD,CAEF,EAAa,CACX,WACA,mBACD,CAAC,sHA7NM,MA3IN,EA2IM,0BA1IJ,UAAc,CACHA,EAAAA,aAAAA,EAAAA,EAAAA,YAAAA,EAAAA,EAAAA,EAAAA,oBA6CL,MA7CN,EA6CM,EAAA,EAAA,EAAA,oBApCE,MARN,EAQM,CANI,EAAA,QAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,aAIN,EAAA,EAAA,OAHC,KAAM,EAAA,MACN,KAAM,GACP,MAAM,uFAE8C,OAAA,MAAA,EAAA,EAAA,iBAA7CC,EAAAA,OAAS,EAAiBC,EAAAA,SAAQ,CAAA,CAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,oBAoCvC,MAlCN,EAkCM,EAAA,EAAA,EAAA,aAhBO,EAAA,CAjBD,QAAQ,QAAO,CAAA,CACZ,SAAA,EAAA,EAAA,aAaC,EAAA,EAAA,EAAA,aAAA,EAAA,CAXR,KAAK,OACL,WAAA,GACC,QAAO,EACP,QAAS,EAAA,QAEC,MAAA,EAAA,EAAA,aAIP,EAAA,EAAA,EAAA,aAAA,EAAA,EAAA,CAFC,KAAM,EAAA,MAAO,cAAA,mBACb,QAAS,EAAA,oFAMpB,CAAA,AAAA,EAAA,MAAA,EAAA,EAAA,iBAFa,SAEb,GAAA,CAAA,CAAA,cAEgBC,EAAAA,iBAAAA,EAAAA,EAAAA,YAAAA,EAAAA,EAAAA,EAAAA,aAaL,EAAA,OAbqB,QAAQ,UAC3B,SAAA,EAAA,EAAA,aASC,EAAA,EAAA,EAAA,aAAA,EAAA,CARD,KAAK,OAAO,WAAA,GAAY,QAAO,IAC3B,MAAA,EAAA,EAAA,aAKP,EAAA,EAAA,EAAA,aAAA,EAAA,EAAA,CAHC,KAA4B,EAAA,MAAY,sBAAA,iBAAA,CAAA,KAAA,EAAA,CAAA,OAAA,CAAA,CAAA,CAAA,mCAOjD,EAAA,EAAA,EAAA,iBADW,KAAA,EAAA,EAAA,iBACR,EAAA,MAAY,OAAA,OAAA,CAAA,EAAA,CAAA,CAAA,8GAKrB,WAAe,0BAkDT,MAjDN,EAiDM,0BAPE,MAAA,CAxCJ,MAAM,eACL,aAAU,AAAA,EAAA,KAAA,GAAE,EAAA,MAAgB,GAC5B,aAAU,AAAA,EAAA,KAAA,GAAE,EAAA,MAAgB,0CAY3B,EAAA,CATC,IAAG,QAAUD,EAAAA,SAAQ,GAAIE,EAAAA,KAAK,SAC9B,KAAMA,EAAAA,KACN,SAAUF,EAAAA,SACV,KAAM,EAAA,MACN,oBAAmBG,EAAAA,gBACnB,YAAWC,EAAAA,SACX,KAAMC,EAAAA,KACN,OAAA,EAAA,EAAA,gBAAO,EAAA,MAAS,CAChB,QAAK,AAAA,EAAA,KAAA,GAAE,EAAI,QAAU,EAAM,8GAG9B,WAAe,mBAuBF,EAAA,WAAA,CAtBD,KAAK,OAAM,CAAA,2BAuBlB,CAtBQ,EAAA,OAAgB,CAAKP,EAAAA,aAAAA,EAAAA,EAAAA,YAAAA,EAAAA,EAAAA,EAAAA,oBAoB1B,MApBN,EAoBM,EAAA,EAAA,EAAA,aADO,EAAA,CAlBD,QAAQ,QAAO,CAAA,CACZ,SAAA,EAAA,EAAA,aAcC,EAAA,EAAA,EAAA,aAAA,EAAA,CAZR,KAAK,QACL,WAAA,GACC,QAAO,EACP,QAAS,EAAA,MACV,MAAM,kBAEK,MAAA,EAAA,EAAA,aAIP,EAAA,EAAA,EAAA,aAAA,EAAA,EAAA,CAFC,KAAM,EAAA,MAAO,cAAA,mBACb,QAAS,EAAA,oFAMpB,CAAA,AAAA,EAAA,MAAA,EAAA,EAAA,iBAFa,SAEb,GAAA,CAAA,CAAA,2FAKN,WAAe,CACJ,EAAA,QAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,oBAGL,MAHN,EAGM,EAAA,EAAA,EAAA,aAFkB,EAAA,CAAf,KAAK,QAAO,CAAA,EAAA,EAAA,EAAA,oBACiC,OAApD,EAAmB,SAAA,EAAA,EAAA,iBAAQE,EAAAA,SAAQ,CAAG,UAAO,EAAA,CAAA,CAAA,GAAA,EAAA,EAAA,oBAAA,OAAA,GAAA,4BAIjD,UAAc,mBAqCL,EAAA,CAnCC,KAAM,EAAA,gCAAA,EAAY,MAAA,EACzB,gBAAe,GACf,YAAW,GACX,SAAU,GACX,MAAA,CAAA,MAAA,QAAA,OAAA,QAAA,OAAA,IAAA,QAAA,IAAA,6BA8BM,EAAA,EAAA,EAAA,oBAAA,MA5BN,EA4BM,EAAA,EAAA,EAAA,oBAZE,MAfN,EAeM,EAAA,EAAA,EAAA,oBANE,MARN,EAQM,CANI,EAAA,QAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,aAIN,EAAA,EAAA,OAHC,KAAM,EAAA,MACN,KAAM,GACP,MAAM,uFAE8C,OAAA,MAAA,EAAA,EAAA,iBAA7CD,EAAAA,OAAS,EAAiBC,EAAAA,SAAQ,CAAA,CAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,aAMnC,EAAA,CAJD,KAAK,QAAQ,WAAA,GAAY,QAAO,IAC5B,MAAA,EAAA,EAAA,aACkB,EAAA,EAAA,EAAA,aAAA,EAAA,EAAA,CAAnB,KAAK,YAAW,CAAA,CAAA,CAAA,kCAcxB,MAVN,EAUM,GAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,aADF,EAAA,CAPC,IAAG,mBAAqBA,EAAAA,SAAQ,GAAIE,EAAAA,KAAK,SACzC,KAAMA,EAAAA,KACN,SAAUF,EAAAA,SACV,KAAM,EAAA,MACN,oBAAmBG,EAAAA,gBACnB,YAAWC,EAAAA,SACX,KAAMC,EAAAA"}
package/dist/C_Code2.js CHANGED
@@ -1,347 +1,2 @@
1
- import { t as C_Icon_default } from "./C_Icon2.js";
2
- import { t as export_helper_default } from "./export-helper.js";
3
- import { NButton, NCode, NModal, NSpin, NTooltip } from "naive-ui";
4
- import { Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, nextTick, normalizeStyle, onMounted, onUnmounted, openBlock, ref, toDisplayString, watch, withCtx } from "vue";
5
-
6
- //#region src/components/C_Code/index.vue?vue&type=script&setup=true&lang.ts
7
- const _hoisted_1 = { class: "c-code-wrapper" };
8
- const _hoisted_2 = {
9
- key: 0,
10
- class: "c-code-header"
11
- };
12
- const _hoisted_3 = { class: "c-code-title" };
13
- const _hoisted_4 = { class: "c-code-actions" };
14
- const _hoisted_5 = { class: "c-code-content" };
15
- const _hoisted_6 = {
16
- key: 0,
17
- class: "floating-copy-btn"
18
- };
19
- const _hoisted_7 = {
20
- key: 0,
21
- class: "c-code-loading"
22
- };
23
- const _hoisted_8 = { class: "ml-2" };
24
- const _hoisted_9 = { class: "fullscreen-content" };
25
- const _hoisted_10 = { class: "fullscreen-header" };
26
- const _hoisted_11 = { class: "fullscreen-title" };
27
- const _hoisted_12 = { class: "fullscreen-body" };
28
- var index_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
29
- name: "CCode",
30
- __name: "index",
31
- props: {
32
- code: { default: "" },
33
- language: { default: "text" },
34
- title: {},
35
- showHeader: {
36
- type: Boolean,
37
- default: true
38
- },
39
- showLineNumbers: {
40
- type: Boolean,
41
- default: true
42
- },
43
- wordWrap: {
44
- type: Boolean,
45
- default: false
46
- },
47
- trim: {
48
- type: Boolean,
49
- default: true
50
- },
51
- showFullscreen: {
52
- type: Boolean,
53
- default: false
54
- },
55
- maxHeight: {},
56
- autoLoadLanguage: {
57
- type: Boolean,
58
- default: true
59
- }
60
- },
61
- emits: [
62
- "copy",
63
- "click",
64
- "fullscreen"
65
- ],
66
- setup(__props, { expose: __expose, emit: __emit }) {
67
- const props = __props;
68
- const emit = __emit;
69
- const highlightManager = inject("highlightManager", null);
70
- const copying = ref(false);
71
- const isFullscreen = ref(false);
72
- const languageLoading = ref(false);
73
- const showFloatingCopy = ref(false);
74
- const hljs = computed(() => {
75
- try {
76
- return highlightManager?.getHljs?.() || null;
77
- } catch (error) {
78
- console.warn("Highlight.js not available:", error);
79
- return null;
80
- }
81
- });
82
- const languageIcon = computed(() => {
83
- return {
84
- javascript: "mdi:language-javascript",
85
- typescript: "mdi:language-typescript",
86
- python: "mdi:language-python",
87
- html: "mdi:language-html5",
88
- css: "mdi:language-css3",
89
- vue: "mdi:vuejs",
90
- react: "mdi:react",
91
- json: "mdi:code-json",
92
- java: "mdi:language-java",
93
- cpp: "mdi:language-cpp",
94
- go: "mdi:language-go",
95
- rust: "mdi:language-rust",
96
- php: "mdi:language-php",
97
- csharp: "mdi:language-csharp",
98
- sql: "mdi:database",
99
- yaml: "mdi:file-code",
100
- xml: "mdi:xml",
101
- markdown: "mdi:language-markdown",
102
- bash: "mdi:bash",
103
- shell: "mdi:console",
104
- powershell: "mdi:powershell",
105
- swift: "mdi:language-swift",
106
- kotlin: "mdi:language-kotlin",
107
- ruby: "mdi:language-ruby"
108
- }[props.language.toLowerCase()] || "mdi:code-braces";
109
- });
110
- const codeStyle = computed(() => {
111
- if (!props.maxHeight) return {};
112
- return { maxHeight: typeof props.maxHeight === "number" ? `${props.maxHeight}px` : props.maxHeight };
113
- });
114
- watch(() => props.language, async (newLanguage) => {
115
- if (!props.autoLoadLanguage || newLanguage === "text" || !highlightManager) return;
116
- if ((highlightManager.getLoadedLanguages?.() || []).includes(newLanguage)) return;
117
- languageLoading.value = true;
118
- try {
119
- await highlightManager.loadLanguage?.(newLanguage);
120
- await nextTick();
121
- } catch (error) {
122
- console.warn(`Failed to load language: ${newLanguage}`, error);
123
- } finally {
124
- languageLoading.value = false;
125
- }
126
- }, { immediate: true });
127
- /**
128
- * 复制代码到剪贴板
129
- */
130
- async function copyCode() {
131
- if (copying.value) return;
132
- copying.value = true;
133
- try {
134
- await navigator.clipboard.writeText(props.code);
135
- emit("copy", props.code);
136
- } catch (error) {
137
- console.error("Copy failed:", error);
138
- } finally {
139
- copying.value = false;
140
- }
141
- }
142
- /**
143
- * 切换全屏显示状态
144
- */
145
- function toggleFullscreen() {
146
- isFullscreen.value = !isFullscreen.value;
147
- emit("fullscreen", isFullscreen.value);
148
- }
149
- /**
150
- * 获取编程语言的显示标题
151
- */
152
- function getLanguageTitle(lang) {
153
- return {
154
- javascript: "JavaScript",
155
- typescript: "TypeScript",
156
- python: "Python",
157
- java: "Java",
158
- cpp: "C++",
159
- csharp: "C#",
160
- php: "PHP",
161
- go: "Go",
162
- rust: "Rust",
163
- html: "HTML",
164
- css: "CSS",
165
- json: "JSON",
166
- bash: "Bash",
167
- shell: "Shell",
168
- yaml: "YAML",
169
- xml: "XML",
170
- markdown: "Markdown",
171
- sql: "SQL",
172
- powershell: "PowerShell",
173
- swift: "Swift",
174
- kotlin: "Kotlin",
175
- ruby: "Ruby",
176
- vue: "Vue",
177
- react: "React"
178
- }[lang.toLowerCase()] || lang.toUpperCase();
179
- }
180
- /**
181
- * 处理ESC键退出全屏
182
- */
183
- function handleEscapeKey(event) {
184
- if (event.key === "Escape" && isFullscreen.value) toggleFullscreen();
185
- }
186
- onMounted(() => {
187
- document.addEventListener("keydown", handleEscapeKey);
188
- });
189
- onUnmounted(() => {
190
- document.removeEventListener("keydown", handleEscapeKey);
191
- });
192
- __expose({
193
- copyCode,
194
- toggleFullscreen
195
- });
196
- return (_ctx, _cache) => {
197
- const _component_NButton = NButton;
198
- const _component_NTooltip = NTooltip;
199
- const _component_NCode = NCode;
200
- const _component_NSpin = NSpin;
201
- const _component_NModal = NModal;
202
- return openBlock(), createElementBlock("div", _hoisted_1, [
203
- createCommentVNode(" 代码标题栏 "),
204
- _ctx.showHeader ? (openBlock(), createElementBlock("div", _hoisted_2, [createElementVNode("div", _hoisted_3, [languageIcon.value ? (openBlock(), createBlock(C_Icon_default, {
205
- key: 0,
206
- name: languageIcon.value,
207
- size: 16,
208
- class: "mr-2"
209
- }, null, 8, ["name"])) : createCommentVNode("v-if", true), createElementVNode("span", null, toDisplayString(_ctx.title || getLanguageTitle(_ctx.language)), 1)]), createElementVNode("div", _hoisted_4, [createVNode(_component_NTooltip, { trigger: "hover" }, {
210
- trigger: withCtx(() => [createVNode(_component_NButton, {
211
- size: "tiny",
212
- quaternary: "",
213
- onClick: copyCode,
214
- loading: copying.value
215
- }, {
216
- icon: withCtx(() => [createVNode(C_Icon_default, {
217
- name: copying.value ? "mdi:loading" : "mdi:content-copy",
218
- loading: copying.value
219
- }, null, 8, ["name", "loading"])]),
220
- _: 1
221
- }, 8, ["loading"])]),
222
- default: withCtx(() => [_cache[4] || (_cache[4] = createTextVNode(" 复制代码 ", -1))]),
223
- _: 1,
224
- __: [4]
225
- }), _ctx.showFullscreen ? (openBlock(), createBlock(_component_NTooltip, {
226
- key: 0,
227
- trigger: "hover"
228
- }, {
229
- trigger: withCtx(() => [createVNode(_component_NButton, {
230
- size: "tiny",
231
- quaternary: "",
232
- onClick: toggleFullscreen
233
- }, {
234
- icon: withCtx(() => [createVNode(C_Icon_default, { name: isFullscreen.value ? "mdi:fullscreen-exit" : "mdi:fullscreen" }, null, 8, ["name"])]),
235
- _: 1
236
- })]),
237
- default: withCtx(() => [createTextVNode(" " + toDisplayString(isFullscreen.value ? "退出全屏" : "全屏查看"), 1)]),
238
- _: 1
239
- })) : createCommentVNode("v-if", true)])])) : createCommentVNode("v-if", true),
240
- createCommentVNode(" 代码内容区域 "),
241
- createElementVNode("div", _hoisted_5, [
242
- createElementVNode("div", {
243
- class: "code-wrapper",
244
- onMouseenter: _cache[1] || (_cache[1] = ($event) => showFloatingCopy.value = true),
245
- onMouseleave: _cache[2] || (_cache[2] = ($event) => showFloatingCopy.value = false)
246
- }, [
247
- (openBlock(), createBlock(_component_NCode, {
248
- key: `code-${_ctx.language}-${_ctx.code.length}`,
249
- code: _ctx.code,
250
- language: _ctx.language,
251
- hljs: hljs.value,
252
- "show-line-numbers": _ctx.showLineNumbers,
253
- "word-wrap": _ctx.wordWrap,
254
- trim: _ctx.trim,
255
- style: normalizeStyle(codeStyle.value),
256
- onClick: _cache[0] || (_cache[0] = ($event) => emit("click", $event))
257
- }, null, 8, [
258
- "code",
259
- "language",
260
- "hljs",
261
- "show-line-numbers",
262
- "word-wrap",
263
- "trim",
264
- "style"
265
- ])),
266
- createCommentVNode(" 悬浮复制按钮 "),
267
- createVNode(Transition, { name: "fade" }, {
268
- default: withCtx(() => [showFloatingCopy.value && !_ctx.showHeader ? (openBlock(), createElementBlock("div", _hoisted_6, [createVNode(_component_NTooltip, { trigger: "hover" }, {
269
- trigger: withCtx(() => [createVNode(_component_NButton, {
270
- size: "small",
271
- quaternary: "",
272
- onClick: copyCode,
273
- loading: copying.value,
274
- class: "copy-floating"
275
- }, {
276
- icon: withCtx(() => [createVNode(C_Icon_default, {
277
- name: copying.value ? "mdi:loading" : "mdi:content-copy",
278
- loading: copying.value
279
- }, null, 8, ["name", "loading"])]),
280
- _: 1
281
- }, 8, ["loading"])]),
282
- default: withCtx(() => [_cache[5] || (_cache[5] = createTextVNode(" 复制代码 ", -1))]),
283
- _: 1,
284
- __: [5]
285
- })])) : createCommentVNode("v-if", true)]),
286
- _: 1
287
- })
288
- ], 32),
289
- createCommentVNode(" 语言加载状态 "),
290
- languageLoading.value ? (openBlock(), createElementBlock("div", _hoisted_7, [createVNode(_component_NSpin, { size: "small" }), createElementVNode("span", _hoisted_8, "正在加载 " + toDisplayString(_ctx.language) + " 语言包...", 1)])) : createCommentVNode("v-if", true)
291
- ]),
292
- createCommentVNode(" 全屏模态框 "),
293
- createVNode(_component_NModal, {
294
- show: isFullscreen.value,
295
- "onUpdate:show": _cache[3] || (_cache[3] = ($event) => isFullscreen.value = $event),
296
- "mask-closable": false,
297
- "show-icon": false,
298
- bordered: false,
299
- style: {
300
- "width": "100vw",
301
- "height": "100vh",
302
- "margin": "0",
303
- "padding": "0"
304
- }
305
- }, {
306
- default: withCtx(() => [createElementVNode("div", _hoisted_9, [createElementVNode("div", _hoisted_10, [createElementVNode("div", _hoisted_11, [languageIcon.value ? (openBlock(), createBlock(C_Icon_default, {
307
- key: 0,
308
- name: languageIcon.value,
309
- size: 16,
310
- class: "mr-2"
311
- }, null, 8, ["name"])) : createCommentVNode("v-if", true), createElementVNode("span", null, toDisplayString(_ctx.title || getLanguageTitle(_ctx.language)), 1)]), createVNode(_component_NButton, {
312
- size: "small",
313
- quaternary: "",
314
- onClick: toggleFullscreen
315
- }, {
316
- icon: withCtx(() => [createVNode(C_Icon_default, { name: "mdi:close" })]),
317
- _: 1
318
- })]), createElementVNode("div", _hoisted_12, [(openBlock(), createBlock(_component_NCode, {
319
- key: `fullscreen-code-${_ctx.language}-${_ctx.code.length}`,
320
- code: _ctx.code,
321
- language: _ctx.language,
322
- hljs: hljs.value,
323
- "show-line-numbers": _ctx.showLineNumbers,
324
- "word-wrap": _ctx.wordWrap,
325
- trim: _ctx.trim
326
- }, null, 8, [
327
- "code",
328
- "language",
329
- "hljs",
330
- "show-line-numbers",
331
- "word-wrap",
332
- "trim"
333
- ]))])])]),
334
- _: 1
335
- }, 8, ["show"])
336
- ]);
337
- };
338
- }
339
- });
340
-
341
- //#endregion
342
- //#region src/components/C_Code/index.vue
343
- var C_Code_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-2f676c83"]]);
344
-
345
- //#endregion
346
- export { C_Code_default as t };
1
+ import{t as e}from"./C_Icon2.js";import{t}from"./export-helper.js";import{NButton as n,NCode as r,NModal as i,NSpin as a,NTooltip as o}from"naive-ui";import{Transition as s,computed as c,createBlock as l,createCommentVNode as u,createElementBlock as d,createElementVNode as f,createTextVNode as p,createVNode as m,defineComponent as h,inject as g,nextTick as _,normalizeStyle as v,onMounted as y,onUnmounted as b,openBlock as x,ref as S,toDisplayString as C,watch as w,withCtx as T}from"vue";const E={class:`c-code-wrapper`},D={key:0,class:`c-code-header`},O={class:`c-code-title`},k={class:`c-code-actions`},A={class:`c-code-content`},j={key:0,class:`floating-copy-btn`},M={key:0,class:`c-code-loading`},N={class:`ml-2`},P={class:`fullscreen-content`},F={class:`fullscreen-header`},I={class:`fullscreen-title`},L={class:`fullscreen-body`};var R=t(h({name:`CCode`,__name:`index`,props:{code:{default:``},language:{default:`text`},title:{},showHeader:{type:Boolean,default:!0},showLineNumbers:{type:Boolean,default:!0},wordWrap:{type:Boolean,default:!1},trim:{type:Boolean,default:!0},showFullscreen:{type:Boolean,default:!1},maxHeight:{},autoLoadLanguage:{type:Boolean,default:!0}},emits:[`copy`,`click`,`fullscreen`],setup(t,{expose:h,emit:R}){let z=t,B=R,V=g(`highlightManager`,null),H=S(!1),U=S(!1),W=S(!1),G=S(!1),K=c(()=>{try{return V?.getHljs?.()||null}catch(e){return console.warn(`Highlight.js not available:`,e),null}}),q=c(()=>({javascript:`mdi:language-javascript`,typescript:`mdi:language-typescript`,python:`mdi:language-python`,html:`mdi:language-html5`,css:`mdi:language-css3`,vue:`mdi:vuejs`,react:`mdi:react`,json:`mdi:code-json`,java:`mdi:language-java`,cpp:`mdi:language-cpp`,go:`mdi:language-go`,rust:`mdi:language-rust`,php:`mdi:language-php`,csharp:`mdi:language-csharp`,sql:`mdi:database`,yaml:`mdi:file-code`,xml:`mdi:xml`,markdown:`mdi:language-markdown`,bash:`mdi:bash`,shell:`mdi:console`,powershell:`mdi:powershell`,swift:`mdi:language-swift`,kotlin:`mdi:language-kotlin`,ruby:`mdi:language-ruby`})[z.language.toLowerCase()]||`mdi:code-braces`),J=c(()=>z.maxHeight?{maxHeight:typeof z.maxHeight==`number`?`${z.maxHeight}px`:z.maxHeight}:{});w(()=>z.language,async e=>{if(!(!z.autoLoadLanguage||e===`text`||!V)&&!(V.getLoadedLanguages?.()||[]).includes(e)){W.value=!0;try{await V.loadLanguage?.(e),await _()}catch(t){console.warn(`Failed to load language: ${e}`,t)}finally{W.value=!1}}},{immediate:!0});async function Y(){if(!H.value){H.value=!0;try{await navigator.clipboard.writeText(z.code),B(`copy`,z.code)}catch(e){console.error(`Copy failed:`,e)}finally{H.value=!1}}}function X(){U.value=!U.value,B(`fullscreen`,U.value)}function Z(e){return{javascript:`JavaScript`,typescript:`TypeScript`,python:`Python`,java:`Java`,cpp:`C++`,csharp:`C#`,php:`PHP`,go:`Go`,rust:`Rust`,html:`HTML`,css:`CSS`,json:`JSON`,bash:`Bash`,shell:`Shell`,yaml:`YAML`,xml:`XML`,markdown:`Markdown`,sql:`SQL`,powershell:`PowerShell`,swift:`Swift`,kotlin:`Kotlin`,ruby:`Ruby`,vue:`Vue`,react:`React`}[e.toLowerCase()]||e.toUpperCase()}function Q(e){e.key===`Escape`&&U.value&&X()}return y(()=>{document.addEventListener(`keydown`,Q)}),b(()=>{document.removeEventListener(`keydown`,Q)}),h({copyCode:Y,toggleFullscreen:X}),(t,c)=>{let h=n,g=o,_=r,y=a,b=i;return x(),d(`div`,E,[u(` 代码标题栏 `),t.showHeader?(x(),d(`div`,D,[f(`div`,O,[q.value?(x(),l(e,{key:0,name:q.value,size:16,class:`mr-2`},null,8,[`name`])):u(`v-if`,!0),f(`span`,null,C(t.title||Z(t.language)),1)]),f(`div`,k,[m(g,{trigger:`hover`},{trigger:T(()=>[m(h,{size:`tiny`,quaternary:``,onClick:Y,loading:H.value},{icon:T(()=>[m(e,{name:H.value?`mdi:loading`:`mdi:content-copy`,loading:H.value},null,8,[`name`,`loading`])]),_:1},8,[`loading`])]),default:T(()=>[c[4]||=p(` 复制代码 `,-1)]),_:1,__:[4]}),t.showFullscreen?(x(),l(g,{key:0,trigger:`hover`},{trigger:T(()=>[m(h,{size:`tiny`,quaternary:``,onClick:X},{icon:T(()=>[m(e,{name:U.value?`mdi:fullscreen-exit`:`mdi:fullscreen`},null,8,[`name`])]),_:1})]),default:T(()=>[p(` `+C(U.value?`退出全屏`:`全屏查看`),1)]),_:1})):u(`v-if`,!0)])])):u(`v-if`,!0),u(` 代码内容区域 `),f(`div`,A,[f(`div`,{class:`code-wrapper`,onMouseenter:c[1]||=e=>G.value=!0,onMouseleave:c[2]||=e=>G.value=!1},[(x(),l(_,{key:`code-${t.language}-${t.code.length}`,code:t.code,language:t.language,hljs:K.value,"show-line-numbers":t.showLineNumbers,"word-wrap":t.wordWrap,trim:t.trim,style:v(J.value),onClick:c[0]||=e=>B(`click`,e)},null,8,[`code`,`language`,`hljs`,`show-line-numbers`,`word-wrap`,`trim`,`style`])),u(` 悬浮复制按钮 `),m(s,{name:`fade`},{default:T(()=>[G.value&&!t.showHeader?(x(),d(`div`,j,[m(g,{trigger:`hover`},{trigger:T(()=>[m(h,{size:`small`,quaternary:``,onClick:Y,loading:H.value,class:`copy-floating`},{icon:T(()=>[m(e,{name:H.value?`mdi:loading`:`mdi:content-copy`,loading:H.value},null,8,[`name`,`loading`])]),_:1},8,[`loading`])]),default:T(()=>[c[5]||=p(` 复制代码 `,-1)]),_:1,__:[5]})])):u(`v-if`,!0)]),_:1})],32),u(` 语言加载状态 `),W.value?(x(),d(`div`,M,[m(y,{size:`small`}),f(`span`,N,`正在加载 `+C(t.language)+` 语言包...`,1)])):u(`v-if`,!0)]),u(` 全屏模态框 `),m(b,{show:U.value,"onUpdate:show":c[3]||=e=>U.value=e,"mask-closable":!1,"show-icon":!1,bordered:!1,style:{width:`100vw`,height:`100vh`,margin:`0`,padding:`0`}},{default:T(()=>[f(`div`,P,[f(`div`,F,[f(`div`,I,[q.value?(x(),l(e,{key:0,name:q.value,size:16,class:`mr-2`},null,8,[`name`])):u(`v-if`,!0),f(`span`,null,C(t.title||Z(t.language)),1)]),m(h,{size:`small`,quaternary:``,onClick:X},{icon:T(()=>[m(e,{name:`mdi:close`})]),_:1})]),f(`div`,L,[(x(),l(_,{key:`fullscreen-code-${t.language}-${t.code.length}`,code:t.code,language:t.language,hljs:K.value,"show-line-numbers":t.showLineNumbers,"word-wrap":t.wordWrap,trim:t.trim},null,8,[`code`,`language`,`hljs`,`show-line-numbers`,`word-wrap`,`trim`]))])])]),_:1},8,[`show`])])}}}),[[`__scopeId`,`data-v-2f676c83`]]);export{R as t};
347
2
  //# sourceMappingURL=C_Code2.js.map