@robot-admin/naive-ui-components 0.3.1 → 0.3.3

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 (188) hide show
  1. package/dist/C_ActionBar-nnfbZCea.css.map +1 -0
  2. package/dist/C_ActionBar2.js +2 -2
  3. package/dist/C_ActionBar2.js.map +1 -1
  4. package/dist/C_AntV-DGjscTWa.css.map +1 -0
  5. package/dist/C_AntV2.js +6 -6
  6. package/dist/C_AntV2.js.map +1 -1
  7. package/dist/C_Barcode-DjTmDkbQ.css.map +1 -0
  8. package/dist/C_Barcode2.js +1 -1
  9. package/dist/C_Barcode2.js.map +1 -1
  10. package/dist/C_Captcha-Ccq3DMrR.css.map +1 -0
  11. package/dist/C_Captcha2.js +1 -1
  12. package/dist/C_Captcha2.js.map +1 -1
  13. package/dist/C_Cascade-IUUHIh7r.css.map +1 -0
  14. package/dist/C_Cascade2.js +2 -2
  15. package/dist/C_Cascade2.js.map +1 -1
  16. package/dist/C_City-Cv5BESaN.css.map +1 -0
  17. package/dist/C_City2.js +2 -2
  18. package/dist/C_City2.js.map +1 -1
  19. package/dist/C_Code-DPZlNSxL.css.map +1 -0
  20. package/dist/C_Code2.js +8 -7
  21. package/dist/C_Code2.js.map +1 -1
  22. package/dist/C_CollapsePanel-Fap-lv_5.css.map +1 -0
  23. package/dist/C_CollapsePanel2.js +1 -1
  24. package/dist/C_CollapsePanel2.js.map +1 -1
  25. package/dist/C_Cron-C0-8b5af.css.map +1 -0
  26. package/dist/C_Cron2.js +15 -14
  27. package/dist/C_Cron2.js.map +1 -1
  28. package/dist/C_Date2.js +1 -1
  29. package/dist/C_Date2.js.map +1 -1
  30. package/dist/C_Draggable-Bq6o0qXn.css.map +1 -0
  31. package/dist/C_Draggable2.js +1 -1
  32. package/dist/C_Draggable2.js.map +1 -1
  33. package/dist/C_Editor-OlxIF9-5.css.map +1 -0
  34. package/dist/C_Editor2.js +1 -1
  35. package/dist/C_Editor2.js.map +1 -1
  36. package/dist/C_FilePreview-B4XgTv-h.css.map +1 -0
  37. package/dist/C_FilePreview.cjs +1 -0
  38. package/dist/C_FilePreview.js +1 -0
  39. package/dist/C_FilePreview2.js +10 -9
  40. package/dist/C_FilePreview2.js.map +1 -1
  41. package/dist/C_Form-Cr9oX037.css.map +1 -0
  42. package/dist/C_Form.cjs +1 -0
  43. package/dist/C_Form.js +1 -0
  44. package/dist/C_Form2.js +69 -72
  45. package/dist/C_Form2.js.map +1 -1
  46. package/dist/C_FormSearch-DlIEoh7X.css.map +1 -0
  47. package/dist/C_FormSearch2.js +2 -2
  48. package/dist/C_FormSearch2.js.map +1 -1
  49. package/dist/C_FormulaEditor-Cm0CokN5.css.map +1 -0
  50. package/dist/C_FormulaEditor2.js +6 -6
  51. package/dist/C_FormulaEditor2.js.map +1 -1
  52. package/dist/C_FullCalendar-BULCIlVK.css.map +1 -0
  53. package/dist/C_FullCalendar2.js +2 -2
  54. package/dist/C_FullCalendar2.js.map +1 -1
  55. package/dist/C_Guide2.js +1 -1
  56. package/dist/C_Guide2.js.map +1 -1
  57. package/dist/C_Icon2.js.map +1 -1
  58. package/dist/C_ImageCropper-DrmUlaLi.css.map +1 -0
  59. package/dist/C_ImageCropper2.js +4 -4
  60. package/dist/C_ImageCropper2.js.map +1 -1
  61. package/dist/C_Language2.js +1 -1
  62. package/dist/C_Language2.js.map +1 -1
  63. package/dist/C_Map-WUMXSAfy.css.map +1 -0
  64. package/dist/C_Map2.js +2 -2
  65. package/dist/C_Map2.js.map +1 -1
  66. package/dist/C_Markdown-Dmv8yaM4.css.map +1 -0
  67. package/dist/C_Markdown2.js +4 -4
  68. package/dist/C_Markdown2.js.map +1 -1
  69. package/dist/C_NotificationCenter-DbgBiyqB.css.map +1 -0
  70. package/dist/C_NotificationCenter2.js +21 -20
  71. package/dist/C_NotificationCenter2.js.map +1 -1
  72. package/dist/C_Progress2.js +1 -1
  73. package/dist/C_Progress2.js.map +1 -1
  74. package/dist/C_QRCode-G7fiAkm4.css.map +1 -0
  75. package/dist/C_QRCode2.js +1 -1
  76. package/dist/C_QRCode2.js.map +1 -1
  77. package/dist/C_Signature-es-ZNPzr.css.map +1 -0
  78. package/dist/C_Signature2.js +2 -2
  79. package/dist/C_Signature2.js.map +1 -1
  80. package/dist/C_SplitPane-Br2eK8IG.css.map +1 -0
  81. package/dist/C_SplitPane2.js +1 -1
  82. package/dist/C_SplitPane2.js.map +1 -1
  83. package/dist/C_Steps-P9Qj9iDd.css.map +1 -0
  84. package/dist/C_Steps2.js +1 -1
  85. package/dist/C_Steps2.js.map +1 -1
  86. package/dist/C_Table-DAwAxr72.css.map +1 -0
  87. package/dist/C_Table.cjs +1 -0
  88. package/dist/C_Table.js +1 -0
  89. package/dist/C_Table2.js +3 -3
  90. package/dist/C_Table2.js.map +1 -1
  91. package/dist/C_Theme2.js +1 -1
  92. package/dist/C_Theme2.js.map +1 -1
  93. package/dist/C_Time-Bd_e1YDN.css.map +1 -0
  94. package/dist/C_Time2.js +2 -2
  95. package/dist/C_Time2.js.map +1 -1
  96. package/dist/C_Tree-DnGc_MPb.css.map +1 -0
  97. package/dist/C_Tree2.js +2 -2
  98. package/dist/C_Tree2.js.map +1 -1
  99. package/dist/C_Upload-i8LB_29U.css.map +1 -0
  100. package/dist/C_Upload2.js +5 -5
  101. package/dist/C_Upload2.js.map +1 -1
  102. package/dist/C_VideoPlayer-rm0MODUv.css.map +1 -0
  103. package/dist/C_VideoPlayer2.js +21 -20
  104. package/dist/C_VideoPlayer2.js.map +1 -1
  105. package/dist/C_VtableGantt-BpY-Rng3.css.map +1 -0
  106. package/dist/C_VtableGantt2.js +2 -2
  107. package/dist/C_VtableGantt2.js.map +1 -1
  108. package/dist/C_WaterFall-HWB-gPON.css.map +1 -0
  109. package/dist/C_WaterFall2.js +2 -2
  110. package/dist/C_WaterFall2.js.map +1 -1
  111. package/dist/C_WorkFlow-CSO86Cuc.css.map +1 -0
  112. package/dist/C_WorkFlow2.js +6 -6
  113. package/dist/C_WorkFlow2.js.map +1 -1
  114. package/dist/constants.d.ts +4 -4
  115. package/dist/constants2.d.ts +4 -4
  116. package/dist/constants3.d.ts +4 -4
  117. package/dist/constants4.d.ts +5 -5
  118. package/dist/constants5.d.ts +2 -2
  119. package/dist/data.d.ts +1 -1
  120. package/dist/index.js.map +1 -1
  121. package/dist/index.vue.d.ts +1 -1
  122. package/dist/index10.vue.d.ts +7 -7
  123. package/dist/index11.vue.d.ts +2 -2
  124. package/dist/index12.vue.d.ts +5 -5
  125. package/dist/index12.vue.d.ts.map +1 -1
  126. package/dist/index13.vue.d.ts +1 -1
  127. package/dist/index14.vue.d.ts +1 -1
  128. package/dist/index16.vue.d.ts +1 -1
  129. package/dist/index2.vue.d.ts +6 -6
  130. package/dist/index3.vue.d.ts +2 -2
  131. package/dist/index4.vue.d.ts +2 -2
  132. package/dist/index5.vue.d.ts +2 -2
  133. package/dist/index6.vue.d.ts +1 -1
  134. package/dist/index8.vue.d.ts +3 -3
  135. package/dist/resolver.js.map +1 -1
  136. package/dist/style.css +1555 -1555
  137. package/dist/useCalendarEvents.d.ts +2 -2
  138. package/dist/useCollapsePanel.d.ts +2 -2
  139. package/dist/useCropperCore.d.ts +3 -3
  140. package/dist/useDraggableLayout.d.ts +8 -8
  141. package/dist/useDynamicFormState.d.ts +111 -111
  142. package/dist/useDynamicFormState.d.ts.map +1 -1
  143. package/dist/useEdgeInteraction.d.ts +2 -2
  144. package/dist/useInfiniteScroll.d.ts +1 -1
  145. package/dist/useModalEdit.d.ts +4 -4
  146. package/dist/useModalEdit.d.ts.map +1 -1
  147. package/dist/useQRCode.d.ts +4 -4
  148. package/dist/useQRCode.d.ts.map +1 -1
  149. package/dist/useSearchState.d.ts +2 -2
  150. package/dist/useSignatureHistory.d.ts +4 -4
  151. package/dist/useSplitResize.d.ts +6 -6
  152. package/dist/useSplitResize.d.ts.map +1 -1
  153. package/dist/useTimeSelection.d.ts +2 -2
  154. package/dist/useTreeOperations.d.ts +6 -6
  155. package/dist/useWorkflowValidation.d.ts +5 -5
  156. package/package.json +2 -1
  157. package/dist/C_ActionBar-DWN-woTc.css.map +0 -1
  158. package/dist/C_AntV-AFKyK6hH.css.map +0 -1
  159. package/dist/C_Barcode-P_EFj8dC.css.map +0 -1
  160. package/dist/C_Captcha-C-ef41xw.css.map +0 -1
  161. package/dist/C_Cascade-D9kNsjsV.css.map +0 -1
  162. package/dist/C_City-BCQ4ipiK.css.map +0 -1
  163. package/dist/C_Code-C9kvvEmO.css.map +0 -1
  164. package/dist/C_CollapsePanel-BUJHuYcU.css.map +0 -1
  165. package/dist/C_Cron-yx2Ob4Jl.css.map +0 -1
  166. package/dist/C_Draggable-C483syRC.css.map +0 -1
  167. package/dist/C_Editor-Bp0SyIEw.css.map +0 -1
  168. package/dist/C_FilePreview-CPqvhoCy.css.map +0 -1
  169. package/dist/C_Form-Jx7PY3sT.css.map +0 -1
  170. package/dist/C_FormSearch-DvRgxlRn.css.map +0 -1
  171. package/dist/C_FormulaEditor-DtGkt4T_.css.map +0 -1
  172. package/dist/C_FullCalendar-BF7H0YIx.css.map +0 -1
  173. package/dist/C_ImageCropper-BVJfUufl.css.map +0 -1
  174. package/dist/C_Map-DpzeuWdX.css.map +0 -1
  175. package/dist/C_Markdown-BEjxknqd.css.map +0 -1
  176. package/dist/C_NotificationCenter-0l3TY2Gn.css.map +0 -1
  177. package/dist/C_QRCode-DbdiAIPg.css.map +0 -1
  178. package/dist/C_Signature-zhHCbra9.css.map +0 -1
  179. package/dist/C_SplitPane-C6sBsfKY.css.map +0 -1
  180. package/dist/C_Steps-CODHN5Hs.css.map +0 -1
  181. package/dist/C_Table-DSNsntmT.css.map +0 -1
  182. package/dist/C_Time-BvZLYraL.css.map +0 -1
  183. package/dist/C_Tree-0GDv--jX.css.map +0 -1
  184. package/dist/C_Upload-BXd3YYLx.css.map +0 -1
  185. package/dist/C_VideoPlayer-DYG3RL0Q.css.map +0 -1
  186. package/dist/C_VtableGantt-fhItIiHE.css.map +0 -1
  187. package/dist/C_WaterFall-8sQDFXKg.css.map +0 -1
  188. package/dist/C_WorkFlow-J-dyIuh9.css.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"C_FullCalendar-BULCIlVK.css","names":[],"sources":["../src/components/C_FullCalendar/index.vue?vue&type=style&index=0&scoped=f993af8f&lang.css"],"sourcesContent":["/* unplugin-vue-components disabled */\n.c-full-calendar[data-v-f993af8f] {\r\n width: 100%;\n}\r\n"],"mappings":"AAAA;AACA;AACA;AACA"}
@@ -1,6 +1,6 @@
1
1
  import { t as export_helper_default } from "./export-helper.js";
2
- import { Fragment, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, isRef, normalizeStyle, openBlock, ref, renderList, unref, watch, withCtx } from "vue";
3
2
  import { NButton, NDatePicker, NInput, NModal, useMessage } from "naive-ui";
3
+ import { Fragment, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, isRef, normalizeStyle, openBlock, ref, renderList, unref, watch, withCtx } from "vue";
4
4
  import FullCalendar from "@fullcalendar/vue3";
5
5
  import dayGridPlugin from "@fullcalendar/daygrid";
6
6
  import interactionPlugin from "@fullcalendar/interaction";
@@ -370,7 +370,7 @@ var index_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCo
370
370
 
371
371
  //#endregion
372
372
  //#region src/components/C_FullCalendar/index.vue
373
- var C_FullCalendar_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-c70fa420"]]);
373
+ var C_FullCalendar_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-f993af8f"]]);
374
374
 
375
375
  //#endregion
376
376
  export { EVENT_COLORS as a, DEFAULT_EDIT_FORM as i, useCalendarEvents as n, HEADER_TOOLBAR as o, BUTTON_TEXT as r, C_FullCalendar_default as t };
@@ -1 +1 @@
1
- {"version":3,"file":"C_FullCalendar2.js","names":[],"sources":["../src/components/C_FullCalendar/data.ts","../src/components/C_FullCalendar/composables/useCalendarEvents.ts","../src/components/C_FullCalendar/index.vue","../src/components/C_FullCalendar/index.vue","../src/components/C_FullCalendar/index.vue"],"sourcesContent":["import type { CalendarEditForm } from \"./types\";\r\n\r\nexport const EVENT_COLORS = [\r\n \"#3f86ff\",\r\n \"#ff6b6b\",\r\n \"#67c23a\",\r\n \"#e6a23c\",\r\n \"#9c27b0\",\r\n \"#00bcd4\",\r\n \"#ff5722\",\r\n];\r\n\r\nexport const DEFAULT_EDIT_FORM: CalendarEditForm = {\r\n id: \"\",\r\n title: \"\",\r\n date: Date.now(),\r\n startTime: \"09:00\",\r\n endTime: \"10:00\",\r\n color: \"#3f86ff\",\r\n};\r\n\r\nexport const HEADER_TOOLBAR = {\r\n left: \"prev,next today\",\r\n center: \"title\",\r\n right: \"dayGridMonth,dayGridWeek,dayGridDay,listWeek\",\r\n};\r\n\r\nexport const BUTTON_TEXT = {\r\n today: \"今天\",\r\n month: \"月\",\r\n week: \"周\",\r\n day: \"日\",\r\n list: \"列表\",\r\n};\r\n","import { ref, watch } from \"vue\";\r\nimport { useMessage } from \"naive-ui\";\r\nimport type { Ref } from \"vue\";\r\nimport type { CalendarEvent, CalendarProps, CalendarEditForm } from \"../types\";\r\nimport {\r\n EVENT_COLORS,\r\n DEFAULT_EDIT_FORM,\r\n HEADER_TOOLBAR,\r\n BUTTON_TEXT,\r\n} from \"../data\";\r\nimport dayGridPlugin from \"@fullcalendar/daygrid\";\r\nimport interactionPlugin from \"@fullcalendar/interaction\";\r\nimport listPlugin from \"@fullcalendar/list\";\r\nimport type { CalendarOptions } from \"@fullcalendar/core\";\r\nimport zhCn from \"@fullcalendar/core/locales/zh-cn\";\r\n\r\ntype EmitFn = {\r\n (event: \"update:events\", events: CalendarEvent[]): void;\r\n (event: \"event-added\", eventData: CalendarEvent): void;\r\n (event: \"event-updated\", eventData: Partial<CalendarEvent>): void;\r\n (event: \"event-deleted\", eventData: { id: string; title: string }): void;\r\n (event: \"event-dropped\", eventData: Partial<CalendarEvent>): void;\r\n};\r\n\r\nexport function useCalendarEvents(props: CalendarProps, emit: EmitFn) {\r\n const message = useMessage();\r\n const calendarRef = ref();\r\n\r\n const internalEvents = ref<CalendarEvent[]>([...(props.events ?? [])]);\r\n\r\n const showActionDialog = ref(false);\r\n const showEditModal = ref(false);\r\n const isEditing = ref(false);\r\n const selectedEvent = ref<any>(null);\r\n const editForm = ref<CalendarEditForm>({ ...DEFAULT_EDIT_FORM });\r\n\r\n const addEventToArray = (event: CalendarEvent) => {\r\n internalEvents.value = [...internalEvents.value, event];\r\n emit(\"update:events\", internalEvents.value);\r\n };\r\n\r\n const updateEventInArray = (eventData: Partial<CalendarEvent>) => {\r\n const index = internalEvents.value.findIndex((e) => e.id === eventData.id);\r\n if (index !== -1) {\r\n internalEvents.value = internalEvents.value.map((event, i) =>\r\n i === index ? { ...event, ...eventData } : event,\r\n );\r\n emit(\"update:events\", internalEvents.value);\r\n }\r\n };\r\n\r\n const removeEventFromArray = (eventId: string) => {\r\n internalEvents.value = internalEvents.value.filter((e) => e.id !== eventId);\r\n emit(\"update:events\", internalEvents.value);\r\n };\r\n\r\n function handleEventClick(info: any) {\r\n if (!props.editable) return;\r\n info.jsEvent.preventDefault();\r\n selectedEvent.value = info.event;\r\n if (props.showEditDialog) showActionDialog.value = true;\r\n }\r\n\r\n function handleDateClick(info: any) {\r\n if (!props.editable || !props.showAddDialog) return;\r\n openAddModal(new Date(info.dateStr));\r\n }\r\n\r\n function handleEventDrop(info: any) {\r\n const payload = {\r\n id: info.event.id,\r\n start: info.event.start,\r\n end: info.event.end,\r\n };\r\n updateEventInArray(payload);\r\n emit(\"event-dropped\", payload);\r\n message.success(`事件 \"${info.event.title}\" 时间已更新`);\r\n }\r\n\r\n function handleEventResize(info: any) {\r\n const payload = {\r\n id: info.event.id,\r\n start: info.event.start,\r\n end: info.event.end,\r\n };\r\n updateEventInArray(payload);\r\n emit(\"event-updated\", payload);\r\n }\r\n\r\n function openAddModal(date: Date) {\r\n isEditing.value = false;\r\n editForm.value = {\r\n ...DEFAULT_EDIT_FORM,\r\n date: date.getTime(),\r\n color: EVENT_COLORS[Math.floor(Math.random() * EVENT_COLORS.length)],\r\n };\r\n showEditModal.value = true;\r\n }\r\n\r\n function openEditModal() {\r\n if (!selectedEvent.value) return;\r\n isEditing.value = true;\r\n const event = selectedEvent.value;\r\n const startDate = new Date(event.start);\r\n const endDate = event.end\r\n ? new Date(event.end)\r\n : new Date(event.start.getTime() + 3600000);\r\n\r\n editForm.value = {\r\n id: event.id,\r\n title: event.title,\r\n date: startDate.getTime(),\r\n startTime: `${String(startDate.getHours()).padStart(2, \"0\")}:${String(startDate.getMinutes()).padStart(2, \"0\")}`,\r\n endTime: `${String(endDate.getHours()).padStart(2, \"0\")}:${String(endDate.getMinutes()).padStart(2, \"0\")}`,\r\n color: event.backgroundColor || \"#3f86ff\",\r\n };\r\n showActionDialog.value = false;\r\n showEditModal.value = true;\r\n }\r\n\r\n function saveEvent() {\r\n if (!editForm.value.title.trim()) {\r\n message.error(\"请输入事件标题\");\r\n return false;\r\n }\r\n if (editForm.value.startTime >= editForm.value.endTime) {\r\n message.error(\"结束时间必须晚于开始时间\");\r\n return false;\r\n }\r\n\r\n const dateStr = new Date(editForm.value.date).toISOString().split(\"T\")[0];\r\n const eventData: CalendarEvent = {\r\n id: isEditing.value ? editForm.value.id : Date.now().toString(),\r\n title: editForm.value.title,\r\n start: new Date(`${dateStr}T${editForm.value.startTime}:00`),\r\n end: new Date(`${dateStr}T${editForm.value.endTime}:00`),\r\n color: editForm.value.color,\r\n };\r\n\r\n if (isEditing.value) {\r\n updateEventInArray(eventData);\r\n emit(\"event-updated\", eventData);\r\n message.success(\"事件已更新\");\r\n } else {\r\n addEventToArray(eventData);\r\n emit(\"event-added\", eventData);\r\n message.success(\"事件已添加\");\r\n }\r\n\r\n showEditModal.value = false;\r\n return true;\r\n }\r\n\r\n function deleteEvent() {\r\n if (!selectedEvent.value) return;\r\n const { id, title } = selectedEvent.value;\r\n removeEventFromArray(id);\r\n emit(\"event-deleted\", { id, title });\r\n showActionDialog.value = false;\r\n message.success(`已删除事件: ${title}`);\r\n }\r\n\r\n const calendarOptions: Ref<CalendarOptions> = ref({\r\n plugins: [dayGridPlugin, interactionPlugin, listPlugin],\r\n locale: zhCn,\r\n initialView: props.initialView ?? \"dayGridMonth\",\r\n events: internalEvents.value as any,\r\n headerToolbar: HEADER_TOOLBAR,\r\n buttonText: BUTTON_TEXT,\r\n editable: props.editable ?? true,\r\n eventClick: handleEventClick,\r\n dateClick: handleDateClick,\r\n eventDrop: handleEventDrop,\r\n eventResize: handleEventResize,\r\n });\r\n\r\n watch(\r\n () => props.events,\r\n (newEvents) => {\r\n internalEvents.value = [...(newEvents ?? [])];\r\n },\r\n { deep: true },\r\n );\r\n\r\n watch(\r\n internalEvents,\r\n (newEvents) => {\r\n calendarOptions.value.events = newEvents as any;\r\n },\r\n { deep: true },\r\n );\r\n\r\n return {\r\n calendarRef,\r\n calendarOptions,\r\n showActionDialog,\r\n showEditModal,\r\n isEditing,\r\n selectedEvent,\r\n editForm,\r\n eventColors: EVENT_COLORS,\r\n openEditModal,\r\n saveEvent,\r\n deleteEvent,\r\n expose: {\r\n getApi: () => calendarRef.value?.getApi(),\r\n addEvent: addEventToArray,\r\n updateEvent: updateEventInArray,\r\n deleteEvent: removeEventFromArray,\r\n getEvents: () => internalEvents.value,\r\n },\r\n };\r\n}\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-19\r\n * @Description: 全局日历组件 — 薄 UI 壳,逻辑由 useCalendarEvents 驱动\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-full-calendar\">\r\n <FullCalendar ref=\"calendarRef\" :options=\"calendarOptions\" />\r\n\r\n <NModal\r\n v-if=\"showActionDialog\"\r\n v-model:show=\"showActionDialog\"\r\n preset=\"dialog\"\r\n :title=\"`事件操作 - ${selectedEvent?.title}`\"\r\n style=\"width: 400px\"\r\n >\r\n <div class=\"ml-10% mt-20px\">\r\n <NButton\r\n type=\"primary\"\r\n @click=\"openEditModal\"\r\n style=\"margin-right: 12px\"\r\n size=\"small\"\r\n >\r\n 编辑\r\n </NButton>\r\n <NButton type=\"error\" @click=\"deleteEvent\" size=\"small\"> 删除 </NButton>\r\n </div>\r\n </NModal>\r\n\r\n <NModal\r\n v-model:show=\"showEditModal\"\r\n preset=\"dialog\"\r\n :title=\"isEditing ? '编辑事件' : '添加事件'\"\r\n positive-text=\"保存\"\r\n negative-text=\"取消\"\r\n @positive-click=\"saveEvent\"\r\n >\r\n <div style=\"padding: 20px\">\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件标题</label\r\n >\r\n <NInput v-model:value=\"editForm.title\" placeholder=\"请输入事件标题\" />\r\n </div>\r\n\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件日期</label\r\n >\r\n <NDatePicker\r\n v-model:value=\"editForm.date\"\r\n type=\"date\"\r\n format=\"yyyy-MM-dd\"\r\n style=\"width: 100%\"\r\n />\r\n </div>\r\n\r\n <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 16px\">\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >开始时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.startTime\"\r\n type=\"time\"\r\n placeholder=\"09:00\"\r\n />\r\n </div>\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >结束时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.endTime\"\r\n type=\"time\"\r\n placeholder=\"10:00\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div style=\"margin-top: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件颜色</label\r\n >\r\n <div style=\"display: flex; gap: 8px\">\r\n <div\r\n v-for=\"color in eventColors\"\r\n :key=\"color\"\r\n :style=\"{\r\n width: '30px',\r\n height: '30px',\r\n backgroundColor: color,\r\n borderRadius: '50%',\r\n cursor: 'pointer',\r\n border:\r\n editForm.color === color\r\n ? '3px solid #000'\r\n : '2px solid #ddd',\r\n }\"\r\n @click=\"editForm.color = color\"\r\n />\r\n </div>\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 { NModal, NButton, NInput, NDatePicker } from \"naive-ui\";\r\nimport FullCalendar from \"@fullcalendar/vue3\";\r\nimport { useCalendarEvents } from \"./composables/useCalendarEvents\";\r\nimport type { CalendarProps, CalendarEmits, CalendarExpose } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_FullCalendar\" });\r\n\r\nconst props = withDefaults(defineProps<CalendarProps>(), {\r\n events: () => [],\r\n initialView: \"dayGridMonth\",\r\n editable: true,\r\n showAddDialog: true,\r\n showEditDialog: true,\r\n});\r\n\r\nconst emit = defineEmits<CalendarEmits>();\r\n\r\nconst {\r\n calendarRef,\r\n calendarOptions,\r\n showActionDialog,\r\n showEditModal,\r\n isEditing,\r\n selectedEvent,\r\n editForm,\r\n eventColors,\r\n openEditModal,\r\n saveEvent,\r\n deleteEvent,\r\n expose,\r\n} = useCalendarEvents(props, emit);\r\n\r\ndefineExpose<CalendarExpose>(expose);\r\n</script>\r\n\r\n<style scoped>\r\n.c-full-calendar {\r\n width: 100%;\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-19\r\n * @Description: 全局日历组件 — 薄 UI 壳,逻辑由 useCalendarEvents 驱动\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-full-calendar\">\r\n <FullCalendar ref=\"calendarRef\" :options=\"calendarOptions\" />\r\n\r\n <NModal\r\n v-if=\"showActionDialog\"\r\n v-model:show=\"showActionDialog\"\r\n preset=\"dialog\"\r\n :title=\"`事件操作 - ${selectedEvent?.title}`\"\r\n style=\"width: 400px\"\r\n >\r\n <div class=\"ml-10% mt-20px\">\r\n <NButton\r\n type=\"primary\"\r\n @click=\"openEditModal\"\r\n style=\"margin-right: 12px\"\r\n size=\"small\"\r\n >\r\n 编辑\r\n </NButton>\r\n <NButton type=\"error\" @click=\"deleteEvent\" size=\"small\"> 删除 </NButton>\r\n </div>\r\n </NModal>\r\n\r\n <NModal\r\n v-model:show=\"showEditModal\"\r\n preset=\"dialog\"\r\n :title=\"isEditing ? '编辑事件' : '添加事件'\"\r\n positive-text=\"保存\"\r\n negative-text=\"取消\"\r\n @positive-click=\"saveEvent\"\r\n >\r\n <div style=\"padding: 20px\">\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件标题</label\r\n >\r\n <NInput v-model:value=\"editForm.title\" placeholder=\"请输入事件标题\" />\r\n </div>\r\n\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件日期</label\r\n >\r\n <NDatePicker\r\n v-model:value=\"editForm.date\"\r\n type=\"date\"\r\n format=\"yyyy-MM-dd\"\r\n style=\"width: 100%\"\r\n />\r\n </div>\r\n\r\n <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 16px\">\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >开始时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.startTime\"\r\n type=\"time\"\r\n placeholder=\"09:00\"\r\n />\r\n </div>\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >结束时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.endTime\"\r\n type=\"time\"\r\n placeholder=\"10:00\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div style=\"margin-top: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件颜色</label\r\n >\r\n <div style=\"display: flex; gap: 8px\">\r\n <div\r\n v-for=\"color in eventColors\"\r\n :key=\"color\"\r\n :style=\"{\r\n width: '30px',\r\n height: '30px',\r\n backgroundColor: color,\r\n borderRadius: '50%',\r\n cursor: 'pointer',\r\n border:\r\n editForm.color === color\r\n ? '3px solid #000'\r\n : '2px solid #ddd',\r\n }\"\r\n @click=\"editForm.color = color\"\r\n />\r\n </div>\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 { NModal, NButton, NInput, NDatePicker } from \"naive-ui\";\r\nimport FullCalendar from \"@fullcalendar/vue3\";\r\nimport { useCalendarEvents } from \"./composables/useCalendarEvents\";\r\nimport type { CalendarProps, CalendarEmits, CalendarExpose } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_FullCalendar\" });\r\n\r\nconst props = withDefaults(defineProps<CalendarProps>(), {\r\n events: () => [],\r\n initialView: \"dayGridMonth\",\r\n editable: true,\r\n showAddDialog: true,\r\n showEditDialog: true,\r\n});\r\n\r\nconst emit = defineEmits<CalendarEmits>();\r\n\r\nconst {\r\n calendarRef,\r\n calendarOptions,\r\n showActionDialog,\r\n showEditModal,\r\n isEditing,\r\n selectedEvent,\r\n editForm,\r\n eventColors,\r\n openEditModal,\r\n saveEvent,\r\n deleteEvent,\r\n expose,\r\n} = useCalendarEvents(props, emit);\r\n\r\ndefineExpose<CalendarExpose>(expose);\r\n</script>\r\n\r\n<style scoped>\r\n.c-full-calendar {\r\n width: 100%;\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-19\r\n * @Description: 全局日历组件 — 薄 UI 壳,逻辑由 useCalendarEvents 驱动\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-full-calendar\">\r\n <FullCalendar ref=\"calendarRef\" :options=\"calendarOptions\" />\r\n\r\n <NModal\r\n v-if=\"showActionDialog\"\r\n v-model:show=\"showActionDialog\"\r\n preset=\"dialog\"\r\n :title=\"`事件操作 - ${selectedEvent?.title}`\"\r\n style=\"width: 400px\"\r\n >\r\n <div class=\"ml-10% mt-20px\">\r\n <NButton\r\n type=\"primary\"\r\n @click=\"openEditModal\"\r\n style=\"margin-right: 12px\"\r\n size=\"small\"\r\n >\r\n 编辑\r\n </NButton>\r\n <NButton type=\"error\" @click=\"deleteEvent\" size=\"small\"> 删除 </NButton>\r\n </div>\r\n </NModal>\r\n\r\n <NModal\r\n v-model:show=\"showEditModal\"\r\n preset=\"dialog\"\r\n :title=\"isEditing ? '编辑事件' : '添加事件'\"\r\n positive-text=\"保存\"\r\n negative-text=\"取消\"\r\n @positive-click=\"saveEvent\"\r\n >\r\n <div style=\"padding: 20px\">\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件标题</label\r\n >\r\n <NInput v-model:value=\"editForm.title\" placeholder=\"请输入事件标题\" />\r\n </div>\r\n\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件日期</label\r\n >\r\n <NDatePicker\r\n v-model:value=\"editForm.date\"\r\n type=\"date\"\r\n format=\"yyyy-MM-dd\"\r\n style=\"width: 100%\"\r\n />\r\n </div>\r\n\r\n <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 16px\">\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >开始时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.startTime\"\r\n type=\"time\"\r\n placeholder=\"09:00\"\r\n />\r\n </div>\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >结束时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.endTime\"\r\n type=\"time\"\r\n placeholder=\"10:00\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div style=\"margin-top: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件颜色</label\r\n >\r\n <div style=\"display: flex; gap: 8px\">\r\n <div\r\n v-for=\"color in eventColors\"\r\n :key=\"color\"\r\n :style=\"{\r\n width: '30px',\r\n height: '30px',\r\n backgroundColor: color,\r\n borderRadius: '50%',\r\n cursor: 'pointer',\r\n border:\r\n editForm.color === color\r\n ? '3px solid #000'\r\n : '2px solid #ddd',\r\n }\"\r\n @click=\"editForm.color = color\"\r\n />\r\n </div>\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 { NModal, NButton, NInput, NDatePicker } from \"naive-ui\";\r\nimport FullCalendar from \"@fullcalendar/vue3\";\r\nimport { useCalendarEvents } from \"./composables/useCalendarEvents\";\r\nimport type { CalendarProps, CalendarEmits, CalendarExpose } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_FullCalendar\" });\r\n\r\nconst props = withDefaults(defineProps<CalendarProps>(), {\r\n events: () => [],\r\n initialView: \"dayGridMonth\",\r\n editable: true,\r\n showAddDialog: true,\r\n showEditDialog: true,\r\n});\r\n\r\nconst emit = defineEmits<CalendarEmits>();\r\n\r\nconst {\r\n calendarRef,\r\n calendarOptions,\r\n showActionDialog,\r\n showEditModal,\r\n isEditing,\r\n selectedEvent,\r\n editForm,\r\n eventColors,\r\n openEditModal,\r\n saveEvent,\r\n deleteEvent,\r\n expose,\r\n} = useCalendarEvents(props, emit);\r\n\r\ndefineExpose<CalendarExpose>(expose);\r\n</script>\r\n\r\n<style scoped>\r\n.c-full-calendar {\r\n width: 100%;\r\n}\r\n</style>\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"C_FullCalendar2.js","names":[],"sources":["../src/components/C_FullCalendar/data.ts","../src/components/C_FullCalendar/composables/useCalendarEvents.ts","../src/components/C_FullCalendar/index.vue","../src/components/C_FullCalendar/index.vue","../src/components/C_FullCalendar/index.vue"],"sourcesContent":["import type { CalendarEditForm } from \"./types\";\r\n\r\nexport const EVENT_COLORS = [\r\n \"#3f86ff\",\r\n \"#ff6b6b\",\r\n \"#67c23a\",\r\n \"#e6a23c\",\r\n \"#9c27b0\",\r\n \"#00bcd4\",\r\n \"#ff5722\",\r\n];\r\n\r\nexport const DEFAULT_EDIT_FORM: CalendarEditForm = {\r\n id: \"\",\r\n title: \"\",\r\n date: Date.now(),\r\n startTime: \"09:00\",\r\n endTime: \"10:00\",\r\n color: \"#3f86ff\",\r\n};\r\n\r\nexport const HEADER_TOOLBAR = {\r\n left: \"prev,next today\",\r\n center: \"title\",\r\n right: \"dayGridMonth,dayGridWeek,dayGridDay,listWeek\",\r\n};\r\n\r\nexport const BUTTON_TEXT = {\r\n today: \"今天\",\r\n month: \"月\",\r\n week: \"周\",\r\n day: \"日\",\r\n list: \"列表\",\r\n};\r\n","import { ref, watch } from \"vue\";\r\nimport { useMessage } from \"naive-ui\";\r\nimport type { Ref } from \"vue\";\r\nimport type { CalendarEvent, CalendarProps, CalendarEditForm } from \"../types\";\r\nimport {\r\n EVENT_COLORS,\r\n DEFAULT_EDIT_FORM,\r\n HEADER_TOOLBAR,\r\n BUTTON_TEXT,\r\n} from \"../data\";\r\nimport dayGridPlugin from \"@fullcalendar/daygrid\";\r\nimport interactionPlugin from \"@fullcalendar/interaction\";\r\nimport listPlugin from \"@fullcalendar/list\";\r\nimport type { CalendarOptions } from \"@fullcalendar/core\";\r\nimport zhCn from \"@fullcalendar/core/locales/zh-cn\";\r\n\r\ntype EmitFn = {\r\n (event: \"update:events\", events: CalendarEvent[]): void;\r\n (event: \"event-added\", eventData: CalendarEvent): void;\r\n (event: \"event-updated\", eventData: Partial<CalendarEvent>): void;\r\n (event: \"event-deleted\", eventData: { id: string; title: string }): void;\r\n (event: \"event-dropped\", eventData: Partial<CalendarEvent>): void;\r\n};\r\n\r\nexport function useCalendarEvents(props: CalendarProps, emit: EmitFn) {\r\n const message = useMessage();\r\n const calendarRef = ref();\r\n\r\n const internalEvents = ref<CalendarEvent[]>([...(props.events ?? [])]);\r\n\r\n const showActionDialog = ref(false);\r\n const showEditModal = ref(false);\r\n const isEditing = ref(false);\r\n const selectedEvent = ref<any>(null);\r\n const editForm = ref<CalendarEditForm>({ ...DEFAULT_EDIT_FORM });\r\n\r\n const addEventToArray = (event: CalendarEvent) => {\r\n internalEvents.value = [...internalEvents.value, event];\r\n emit(\"update:events\", internalEvents.value);\r\n };\r\n\r\n const updateEventInArray = (eventData: Partial<CalendarEvent>) => {\r\n const index = internalEvents.value.findIndex((e) => e.id === eventData.id);\r\n if (index !== -1) {\r\n internalEvents.value = internalEvents.value.map((event, i) =>\r\n i === index ? { ...event, ...eventData } : event,\r\n );\r\n emit(\"update:events\", internalEvents.value);\r\n }\r\n };\r\n\r\n const removeEventFromArray = (eventId: string) => {\r\n internalEvents.value = internalEvents.value.filter((e) => e.id !== eventId);\r\n emit(\"update:events\", internalEvents.value);\r\n };\r\n\r\n function handleEventClick(info: any) {\r\n if (!props.editable) return;\r\n info.jsEvent.preventDefault();\r\n selectedEvent.value = info.event;\r\n if (props.showEditDialog) showActionDialog.value = true;\r\n }\r\n\r\n function handleDateClick(info: any) {\r\n if (!props.editable || !props.showAddDialog) return;\r\n openAddModal(new Date(info.dateStr));\r\n }\r\n\r\n function handleEventDrop(info: any) {\r\n const payload = {\r\n id: info.event.id,\r\n start: info.event.start,\r\n end: info.event.end,\r\n };\r\n updateEventInArray(payload);\r\n emit(\"event-dropped\", payload);\r\n message.success(`事件 \"${info.event.title}\" 时间已更新`);\r\n }\r\n\r\n function handleEventResize(info: any) {\r\n const payload = {\r\n id: info.event.id,\r\n start: info.event.start,\r\n end: info.event.end,\r\n };\r\n updateEventInArray(payload);\r\n emit(\"event-updated\", payload);\r\n }\r\n\r\n function openAddModal(date: Date) {\r\n isEditing.value = false;\r\n editForm.value = {\r\n ...DEFAULT_EDIT_FORM,\r\n date: date.getTime(),\r\n color: EVENT_COLORS[Math.floor(Math.random() * EVENT_COLORS.length)],\r\n };\r\n showEditModal.value = true;\r\n }\r\n\r\n function openEditModal() {\r\n if (!selectedEvent.value) return;\r\n isEditing.value = true;\r\n const event = selectedEvent.value;\r\n const startDate = new Date(event.start);\r\n const endDate = event.end\r\n ? new Date(event.end)\r\n : new Date(event.start.getTime() + 3600000);\r\n\r\n editForm.value = {\r\n id: event.id,\r\n title: event.title,\r\n date: startDate.getTime(),\r\n startTime: `${String(startDate.getHours()).padStart(2, \"0\")}:${String(startDate.getMinutes()).padStart(2, \"0\")}`,\r\n endTime: `${String(endDate.getHours()).padStart(2, \"0\")}:${String(endDate.getMinutes()).padStart(2, \"0\")}`,\r\n color: event.backgroundColor || \"#3f86ff\",\r\n };\r\n showActionDialog.value = false;\r\n showEditModal.value = true;\r\n }\r\n\r\n function saveEvent() {\r\n if (!editForm.value.title.trim()) {\r\n message.error(\"请输入事件标题\");\r\n return false;\r\n }\r\n if (editForm.value.startTime >= editForm.value.endTime) {\r\n message.error(\"结束时间必须晚于开始时间\");\r\n return false;\r\n }\r\n\r\n const dateStr = new Date(editForm.value.date).toISOString().split(\"T\")[0];\r\n const eventData: CalendarEvent = {\r\n id: isEditing.value ? editForm.value.id : Date.now().toString(),\r\n title: editForm.value.title,\r\n start: new Date(`${dateStr}T${editForm.value.startTime}:00`),\r\n end: new Date(`${dateStr}T${editForm.value.endTime}:00`),\r\n color: editForm.value.color,\r\n };\r\n\r\n if (isEditing.value) {\r\n updateEventInArray(eventData);\r\n emit(\"event-updated\", eventData);\r\n message.success(\"事件已更新\");\r\n } else {\r\n addEventToArray(eventData);\r\n emit(\"event-added\", eventData);\r\n message.success(\"事件已添加\");\r\n }\r\n\r\n showEditModal.value = false;\r\n return true;\r\n }\r\n\r\n function deleteEvent() {\r\n if (!selectedEvent.value) return;\r\n const { id, title } = selectedEvent.value;\r\n removeEventFromArray(id);\r\n emit(\"event-deleted\", { id, title });\r\n showActionDialog.value = false;\r\n message.success(`已删除事件: ${title}`);\r\n }\r\n\r\n const calendarOptions: Ref<CalendarOptions> = ref({\r\n plugins: [dayGridPlugin, interactionPlugin, listPlugin],\r\n locale: zhCn,\r\n initialView: props.initialView ?? \"dayGridMonth\",\r\n events: internalEvents.value as any,\r\n headerToolbar: HEADER_TOOLBAR,\r\n buttonText: BUTTON_TEXT,\r\n editable: props.editable ?? true,\r\n eventClick: handleEventClick,\r\n dateClick: handleDateClick,\r\n eventDrop: handleEventDrop,\r\n eventResize: handleEventResize,\r\n });\r\n\r\n watch(\r\n () => props.events,\r\n (newEvents) => {\r\n internalEvents.value = [...(newEvents ?? [])];\r\n },\r\n { deep: true },\r\n );\r\n\r\n watch(\r\n internalEvents,\r\n (newEvents) => {\r\n calendarOptions.value.events = newEvents as any;\r\n },\r\n { deep: true },\r\n );\r\n\r\n return {\r\n calendarRef,\r\n calendarOptions,\r\n showActionDialog,\r\n showEditModal,\r\n isEditing,\r\n selectedEvent,\r\n editForm,\r\n eventColors: EVENT_COLORS,\r\n openEditModal,\r\n saveEvent,\r\n deleteEvent,\r\n expose: {\r\n getApi: () => calendarRef.value?.getApi(),\r\n addEvent: addEventToArray,\r\n updateEvent: updateEventInArray,\r\n deleteEvent: removeEventFromArray,\r\n getEvents: () => internalEvents.value,\r\n },\r\n };\r\n}\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-19\r\n * @Description: 全局日历组件 — 薄 UI 壳,逻辑由 useCalendarEvents 驱动\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-full-calendar\">\r\n <FullCalendar ref=\"calendarRef\" :options=\"calendarOptions\" />\r\n\r\n <NModal\r\n v-if=\"showActionDialog\"\r\n v-model:show=\"showActionDialog\"\r\n preset=\"dialog\"\r\n :title=\"`事件操作 - ${selectedEvent?.title}`\"\r\n style=\"width: 400px\"\r\n >\r\n <div class=\"ml-10% mt-20px\">\r\n <NButton\r\n type=\"primary\"\r\n @click=\"openEditModal\"\r\n style=\"margin-right: 12px\"\r\n size=\"small\"\r\n >\r\n 编辑\r\n </NButton>\r\n <NButton type=\"error\" @click=\"deleteEvent\" size=\"small\"> 删除 </NButton>\r\n </div>\r\n </NModal>\r\n\r\n <NModal\r\n v-model:show=\"showEditModal\"\r\n preset=\"dialog\"\r\n :title=\"isEditing ? '编辑事件' : '添加事件'\"\r\n positive-text=\"保存\"\r\n negative-text=\"取消\"\r\n @positive-click=\"saveEvent\"\r\n >\r\n <div style=\"padding: 20px\">\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件标题</label\r\n >\r\n <NInput v-model:value=\"editForm.title\" placeholder=\"请输入事件标题\" />\r\n </div>\r\n\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件日期</label\r\n >\r\n <NDatePicker\r\n v-model:value=\"editForm.date\"\r\n type=\"date\"\r\n format=\"yyyy-MM-dd\"\r\n style=\"width: 100%\"\r\n />\r\n </div>\r\n\r\n <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 16px\">\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >开始时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.startTime\"\r\n type=\"time\"\r\n placeholder=\"09:00\"\r\n />\r\n </div>\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >结束时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.endTime\"\r\n type=\"time\"\r\n placeholder=\"10:00\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div style=\"margin-top: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件颜色</label\r\n >\r\n <div style=\"display: flex; gap: 8px\">\r\n <div\r\n v-for=\"color in eventColors\"\r\n :key=\"color\"\r\n :style=\"{\r\n width: '30px',\r\n height: '30px',\r\n backgroundColor: color,\r\n borderRadius: '50%',\r\n cursor: 'pointer',\r\n border:\r\n editForm.color === color\r\n ? '3px solid #000'\r\n : '2px solid #ddd',\r\n }\"\r\n @click=\"editForm.color = color\"\r\n />\r\n </div>\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 { NModal, NButton, NInput, NDatePicker } from \"naive-ui\";\r\nimport FullCalendar from \"@fullcalendar/vue3\";\r\nimport { useCalendarEvents } from \"./composables/useCalendarEvents\";\r\nimport type { CalendarProps, CalendarEmits, CalendarExpose } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_FullCalendar\" });\r\n\r\nconst props = withDefaults(defineProps<CalendarProps>(), {\r\n events: () => [],\r\n initialView: \"dayGridMonth\",\r\n editable: true,\r\n showAddDialog: true,\r\n showEditDialog: true,\r\n});\r\n\r\nconst emit = defineEmits<CalendarEmits>();\r\n\r\nconst {\r\n calendarRef,\r\n calendarOptions,\r\n showActionDialog,\r\n showEditModal,\r\n isEditing,\r\n selectedEvent,\r\n editForm,\r\n eventColors,\r\n openEditModal,\r\n saveEvent,\r\n deleteEvent,\r\n expose,\r\n} = useCalendarEvents(props, emit);\r\n\r\ndefineExpose<CalendarExpose>(expose);\r\n</script>\r\n\r\n<style scoped>\r\n.c-full-calendar {\r\n width: 100%;\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: 全局日历组件 — 薄 UI 壳,逻辑由 useCalendarEvents 驱动\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-full-calendar\">\r\n <FullCalendar ref=\"calendarRef\" :options=\"calendarOptions\" />\r\n\r\n <NModal\r\n v-if=\"showActionDialog\"\r\n v-model:show=\"showActionDialog\"\r\n preset=\"dialog\"\r\n :title=\"`事件操作 - ${selectedEvent?.title}`\"\r\n style=\"width: 400px\"\r\n >\r\n <div class=\"ml-10% mt-20px\">\r\n <NButton\r\n type=\"primary\"\r\n @click=\"openEditModal\"\r\n style=\"margin-right: 12px\"\r\n size=\"small\"\r\n >\r\n 编辑\r\n </NButton>\r\n <NButton type=\"error\" @click=\"deleteEvent\" size=\"small\"> 删除 </NButton>\r\n </div>\r\n </NModal>\r\n\r\n <NModal\r\n v-model:show=\"showEditModal\"\r\n preset=\"dialog\"\r\n :title=\"isEditing ? '编辑事件' : '添加事件'\"\r\n positive-text=\"保存\"\r\n negative-text=\"取消\"\r\n @positive-click=\"saveEvent\"\r\n >\r\n <div style=\"padding: 20px\">\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件标题</label\r\n >\r\n <NInput v-model:value=\"editForm.title\" placeholder=\"请输入事件标题\" />\r\n </div>\r\n\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件日期</label\r\n >\r\n <NDatePicker\r\n v-model:value=\"editForm.date\"\r\n type=\"date\"\r\n format=\"yyyy-MM-dd\"\r\n style=\"width: 100%\"\r\n />\r\n </div>\r\n\r\n <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 16px\">\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >开始时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.startTime\"\r\n type=\"time\"\r\n placeholder=\"09:00\"\r\n />\r\n </div>\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >结束时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.endTime\"\r\n type=\"time\"\r\n placeholder=\"10:00\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div style=\"margin-top: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件颜色</label\r\n >\r\n <div style=\"display: flex; gap: 8px\">\r\n <div\r\n v-for=\"color in eventColors\"\r\n :key=\"color\"\r\n :style=\"{\r\n width: '30px',\r\n height: '30px',\r\n backgroundColor: color,\r\n borderRadius: '50%',\r\n cursor: 'pointer',\r\n border:\r\n editForm.color === color\r\n ? '3px solid #000'\r\n : '2px solid #ddd',\r\n }\"\r\n @click=\"editForm.color = color\"\r\n />\r\n </div>\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 { NModal, NButton, NInput, NDatePicker } from \"naive-ui\";\r\nimport FullCalendar from \"@fullcalendar/vue3\";\r\nimport { useCalendarEvents } from \"./composables/useCalendarEvents\";\r\nimport type { CalendarProps, CalendarEmits, CalendarExpose } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_FullCalendar\" });\r\n\r\nconst props = withDefaults(defineProps<CalendarProps>(), {\r\n events: () => [],\r\n initialView: \"dayGridMonth\",\r\n editable: true,\r\n showAddDialog: true,\r\n showEditDialog: true,\r\n});\r\n\r\nconst emit = defineEmits<CalendarEmits>();\r\n\r\nconst {\r\n calendarRef,\r\n calendarOptions,\r\n showActionDialog,\r\n showEditModal,\r\n isEditing,\r\n selectedEvent,\r\n editForm,\r\n eventColors,\r\n openEditModal,\r\n saveEvent,\r\n deleteEvent,\r\n expose,\r\n} = useCalendarEvents(props, emit);\r\n\r\ndefineExpose<CalendarExpose>(expose);\r\n</script>\r\n\r\n<style scoped>\r\n.c-full-calendar {\r\n width: 100%;\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-19\r\n * @Description: 全局日历组件 — 薄 UI 壳,逻辑由 useCalendarEvents 驱动\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-full-calendar\">\r\n <FullCalendar ref=\"calendarRef\" :options=\"calendarOptions\" />\r\n\r\n <NModal\r\n v-if=\"showActionDialog\"\r\n v-model:show=\"showActionDialog\"\r\n preset=\"dialog\"\r\n :title=\"`事件操作 - ${selectedEvent?.title}`\"\r\n style=\"width: 400px\"\r\n >\r\n <div class=\"ml-10% mt-20px\">\r\n <NButton\r\n type=\"primary\"\r\n @click=\"openEditModal\"\r\n style=\"margin-right: 12px\"\r\n size=\"small\"\r\n >\r\n 编辑\r\n </NButton>\r\n <NButton type=\"error\" @click=\"deleteEvent\" size=\"small\"> 删除 </NButton>\r\n </div>\r\n </NModal>\r\n\r\n <NModal\r\n v-model:show=\"showEditModal\"\r\n preset=\"dialog\"\r\n :title=\"isEditing ? '编辑事件' : '添加事件'\"\r\n positive-text=\"保存\"\r\n negative-text=\"取消\"\r\n @positive-click=\"saveEvent\"\r\n >\r\n <div style=\"padding: 20px\">\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件标题</label\r\n >\r\n <NInput v-model:value=\"editForm.title\" placeholder=\"请输入事件标题\" />\r\n </div>\r\n\r\n <div style=\"margin-bottom: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件日期</label\r\n >\r\n <NDatePicker\r\n v-model:value=\"editForm.date\"\r\n type=\"date\"\r\n format=\"yyyy-MM-dd\"\r\n style=\"width: 100%\"\r\n />\r\n </div>\r\n\r\n <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 16px\">\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >开始时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.startTime\"\r\n type=\"time\"\r\n placeholder=\"09:00\"\r\n />\r\n </div>\r\n <div>\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >结束时间</label\r\n >\r\n <NInput\r\n v-model:value=\"editForm.endTime\"\r\n type=\"time\"\r\n placeholder=\"10:00\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div style=\"margin-top: 16px\">\r\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500\"\r\n >事件颜色</label\r\n >\r\n <div style=\"display: flex; gap: 8px\">\r\n <div\r\n v-for=\"color in eventColors\"\r\n :key=\"color\"\r\n :style=\"{\r\n width: '30px',\r\n height: '30px',\r\n backgroundColor: color,\r\n borderRadius: '50%',\r\n cursor: 'pointer',\r\n border:\r\n editForm.color === color\r\n ? '3px solid #000'\r\n : '2px solid #ddd',\r\n }\"\r\n @click=\"editForm.color = color\"\r\n />\r\n </div>\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 { NModal, NButton, NInput, NDatePicker } from \"naive-ui\";\r\nimport FullCalendar from \"@fullcalendar/vue3\";\r\nimport { useCalendarEvents } from \"./composables/useCalendarEvents\";\r\nimport type { CalendarProps, CalendarEmits, CalendarExpose } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_FullCalendar\" });\r\n\r\nconst props = withDefaults(defineProps<CalendarProps>(), {\r\n events: () => [],\r\n initialView: \"dayGridMonth\",\r\n editable: true,\r\n showAddDialog: true,\r\n showEditDialog: true,\r\n});\r\n\r\nconst emit = defineEmits<CalendarEmits>();\r\n\r\nconst {\r\n calendarRef,\r\n calendarOptions,\r\n showActionDialog,\r\n showEditModal,\r\n isEditing,\r\n selectedEvent,\r\n editForm,\r\n eventColors,\r\n openEditModal,\r\n saveEvent,\r\n deleteEvent,\r\n expose,\r\n} = useCalendarEvents(props, emit);\r\n\r\ndefineExpose<CalendarExpose>(expose);\r\n</script>\r\n\r\n<style scoped>\r\n.c-full-calendar {\r\n width: 100%;\r\n}\r\n</style>\r\n"],"mappings":"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"}
package/dist/C_Guide2.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createBlock, createElementVNode, createVNode, defineComponent, openBlock, unref, withCtx } from "vue";
2
1
  import { NButton, NTooltip } from "naive-ui";
2
+ import { createBlock, createElementVNode, createVNode, defineComponent, openBlock, unref, withCtx } from "vue";
3
3
  import { driver } from "driver.js";
4
4
  import "driver.js/dist/driver.css";
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"C_Guide2.js","names":[],"sources":["../src/components/C_Guide/index.vue","../src/components/C_Guide/index.vue","../src/components/C_Guide/index.vue"],"sourcesContent":["<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 用户引导组件(基于 driver.js)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NTooltip placement=\"bottom\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton text @click=\"startGuide\">\r\n <div class=\"i-mdi:sign-routes\" />\r\n </NButton>\r\n </template>\r\n <span>功能引导</span>\r\n </NTooltip>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { NTooltip, NButton } from \"naive-ui\";\r\nimport { driver } from \"driver.js\";\r\nimport \"driver.js/dist/driver.css\";\r\n\r\ndefineOptions({ name: \"C_Guide\" });\r\n\r\nexport interface GuideStep {\r\n element: string;\r\n popover: {\r\n title: string;\r\n description: string;\r\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\r\n };\r\n}\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n steps?: GuideStep[];\r\n doneBtnText?: string;\r\n nextBtnText?: string;\r\n prevBtnText?: string;\r\n }>(),\r\n {\r\n steps: () => [],\r\n doneBtnText: \"完成\",\r\n nextBtnText: \"下一步\",\r\n prevBtnText: \"上一步\",\r\n },\r\n);\r\n\r\nconst startGuide = () => {\r\n const driverObj = driver({\r\n popoverClass: \"driverjs-theme\",\r\n animate: true,\r\n showProgress: true,\r\n doneBtnText: props.doneBtnText,\r\n nextBtnText: props.nextBtnText,\r\n prevBtnText: props.prevBtnText,\r\n });\r\n\r\n if (props.steps.length > 0) {\r\n driverObj.setSteps(props.steps);\r\n }\r\n driverObj.drive();\r\n};\r\n\r\ndefineExpose({ startGuide });\r\n</script>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 用户引导组件(基于 driver.js)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NTooltip placement=\"bottom\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton text @click=\"startGuide\">\r\n <div class=\"i-mdi:sign-routes\" />\r\n </NButton>\r\n </template>\r\n <span>功能引导</span>\r\n </NTooltip>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { NTooltip, NButton } from \"naive-ui\";\r\nimport { driver } from \"driver.js\";\r\nimport \"driver.js/dist/driver.css\";\r\n\r\ndefineOptions({ name: \"C_Guide\" });\r\n\r\nexport interface GuideStep {\r\n element: string;\r\n popover: {\r\n title: string;\r\n description: string;\r\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\r\n };\r\n}\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n steps?: GuideStep[];\r\n doneBtnText?: string;\r\n nextBtnText?: string;\r\n prevBtnText?: string;\r\n }>(),\r\n {\r\n steps: () => [],\r\n doneBtnText: \"完成\",\r\n nextBtnText: \"下一步\",\r\n prevBtnText: \"上一步\",\r\n },\r\n);\r\n\r\nconst startGuide = () => {\r\n const driverObj = driver({\r\n popoverClass: \"driverjs-theme\",\r\n animate: true,\r\n showProgress: true,\r\n doneBtnText: props.doneBtnText,\r\n nextBtnText: props.nextBtnText,\r\n prevBtnText: props.prevBtnText,\r\n });\r\n\r\n if (props.steps.length > 0) {\r\n driverObj.setSteps(props.steps);\r\n }\r\n driverObj.drive();\r\n};\r\n\r\ndefineExpose({ startGuide });\r\n</script>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 用户引导组件(基于 driver.js)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NTooltip placement=\"bottom\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton text @click=\"startGuide\">\r\n <div class=\"i-mdi:sign-routes\" />\r\n </NButton>\r\n </template>\r\n <span>功能引导</span>\r\n </NTooltip>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { NTooltip, NButton } from \"naive-ui\";\r\nimport { driver } from \"driver.js\";\r\nimport \"driver.js/dist/driver.css\";\r\n\r\ndefineOptions({ name: \"C_Guide\" });\r\n\r\nexport interface GuideStep {\r\n element: string;\r\n popover: {\r\n title: string;\r\n description: string;\r\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\r\n };\r\n}\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n steps?: GuideStep[];\r\n doneBtnText?: string;\r\n nextBtnText?: string;\r\n prevBtnText?: string;\r\n }>(),\r\n {\r\n steps: () => [],\r\n doneBtnText: \"完成\",\r\n nextBtnText: \"下一步\",\r\n prevBtnText: \"上一步\",\r\n },\r\n);\r\n\r\nconst startGuide = () => {\r\n const driverObj = driver({\r\n popoverClass: \"driverjs-theme\",\r\n animate: true,\r\n showProgress: true,\r\n doneBtnText: props.doneBtnText,\r\n nextBtnText: props.nextBtnText,\r\n prevBtnText: props.prevBtnText,\r\n });\r\n\r\n if (props.steps.length > 0) {\r\n driverObj.setSteps(props.steps);\r\n }\r\n driverObj.drive();\r\n};\r\n\r\ndefineExpose({ startGuide });\r\n</script>\r\n"],"mappings":";;;;;;;;;;;;;;;;ECkCA,MAAM,QAAQ;EAed,MAAM,mBAAmB;GACvB,MAAM,YAAY,OAAO;IACvB,cAAc;IACd,SAAS;IACT,cAAc;IACd,aAAa,MAAM;IACnB,aAAa,MAAM;IACnB,aAAa,MAAM;IACpB,CAAC;AAEF,OAAI,MAAM,MAAM,SAAS,EACvB,WAAU,SAAS,MAAM,MAAM;AAEjC,aAAU,OAAO;;AAGnB,WAAa,EAAE,YAAY,CAAC;;uBAzD1B,YAOW,MAAA,SAAA,EAAA;IAPD,WAAU;IAAS,SAAQ;;IACxB,SAAO,cAGN,CAFV,YAEU,MAAA,QAAA,EAAA;KAFD,MAAA;KAAM,SAAO;;4BACa,OAAA,OAAA,OAAA,KAAA,CAAjC,mBAAiC,OAAA,EAA5B,OAAM,qBAAmB,EAAA,MAAA,GAAA;;;;2BAGjB,2BAAjB,mBAAiB,QAAA,MAAX,QAAI,GAAA"}
1
+ {"version":3,"file":"C_Guide2.js","names":[],"sources":["../src/components/C_Guide/index.vue","../src/components/C_Guide/index.vue","../src/components/C_Guide/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 用户引导组件(基于 driver.js)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NTooltip placement=\"bottom\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton text @click=\"startGuide\">\r\n <div class=\"i-mdi:sign-routes\" />\r\n </NButton>\r\n </template>\r\n <span>功能引导</span>\r\n </NTooltip>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { NTooltip, NButton } from \"naive-ui\";\r\nimport { driver } from \"driver.js\";\r\nimport \"driver.js/dist/driver.css\";\r\n\r\ndefineOptions({ name: \"C_Guide\" });\r\n\r\nexport interface GuideStep {\r\n element: string;\r\n popover: {\r\n title: string;\r\n description: string;\r\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\r\n };\r\n}\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n steps?: GuideStep[];\r\n doneBtnText?: string;\r\n nextBtnText?: string;\r\n prevBtnText?: string;\r\n }>(),\r\n {\r\n steps: () => [],\r\n doneBtnText: \"完成\",\r\n nextBtnText: \"下一步\",\r\n prevBtnText: \"上一步\",\r\n },\r\n);\r\n\r\nconst startGuide = () => {\r\n const driverObj = driver({\r\n popoverClass: \"driverjs-theme\",\r\n animate: true,\r\n showProgress: true,\r\n doneBtnText: props.doneBtnText,\r\n nextBtnText: props.nextBtnText,\r\n prevBtnText: props.prevBtnText,\r\n });\r\n\r\n if (props.steps.length > 0) {\r\n driverObj.setSteps(props.steps);\r\n }\r\n driverObj.drive();\r\n};\r\n\r\ndefineExpose({ startGuide });\r\n</script>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 用户引导组件(基于 driver.js)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NTooltip placement=\"bottom\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton text @click=\"startGuide\">\r\n <div class=\"i-mdi:sign-routes\" />\r\n </NButton>\r\n </template>\r\n <span>功能引导</span>\r\n </NTooltip>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { NTooltip, NButton } from \"naive-ui\";\r\nimport { driver } from \"driver.js\";\r\nimport \"driver.js/dist/driver.css\";\r\n\r\ndefineOptions({ name: \"C_Guide\" });\r\n\r\nexport interface GuideStep {\r\n element: string;\r\n popover: {\r\n title: string;\r\n description: string;\r\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\r\n };\r\n}\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n steps?: GuideStep[];\r\n doneBtnText?: string;\r\n nextBtnText?: string;\r\n prevBtnText?: string;\r\n }>(),\r\n {\r\n steps: () => [],\r\n doneBtnText: \"完成\",\r\n nextBtnText: \"下一步\",\r\n prevBtnText: \"上一步\",\r\n },\r\n);\r\n\r\nconst startGuide = () => {\r\n const driverObj = driver({\r\n popoverClass: \"driverjs-theme\",\r\n animate: true,\r\n showProgress: true,\r\n doneBtnText: props.doneBtnText,\r\n nextBtnText: props.nextBtnText,\r\n prevBtnText: props.prevBtnText,\r\n });\r\n\r\n if (props.steps.length > 0) {\r\n driverObj.setSteps(props.steps);\r\n }\r\n driverObj.drive();\r\n};\r\n\r\ndefineExpose({ startGuide });\r\n</script>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 用户引导组件(基于 driver.js)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NTooltip placement=\"bottom\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton text @click=\"startGuide\">\r\n <div class=\"i-mdi:sign-routes\" />\r\n </NButton>\r\n </template>\r\n <span>功能引导</span>\r\n </NTooltip>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { NTooltip, NButton } from \"naive-ui\";\r\nimport { driver } from \"driver.js\";\r\nimport \"driver.js/dist/driver.css\";\r\n\r\ndefineOptions({ name: \"C_Guide\" });\r\n\r\nexport interface GuideStep {\r\n element: string;\r\n popover: {\r\n title: string;\r\n description: string;\r\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\r\n };\r\n}\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n steps?: GuideStep[];\r\n doneBtnText?: string;\r\n nextBtnText?: string;\r\n prevBtnText?: string;\r\n }>(),\r\n {\r\n steps: () => [],\r\n doneBtnText: \"完成\",\r\n nextBtnText: \"下一步\",\r\n prevBtnText: \"上一步\",\r\n },\r\n);\r\n\r\nconst startGuide = () => {\r\n const driverObj = driver({\r\n popoverClass: \"driverjs-theme\",\r\n animate: true,\r\n showProgress: true,\r\n doneBtnText: props.doneBtnText,\r\n nextBtnText: props.nextBtnText,\r\n prevBtnText: props.prevBtnText,\r\n });\r\n\r\n if (props.steps.length > 0) {\r\n driverObj.setSteps(props.steps);\r\n }\r\n driverObj.drive();\r\n};\r\n\r\ndefineExpose({ startGuide });\r\n</script>\r\n"],"mappings":";;;;;;;;;;;;;;;;ECkCA,MAAM,QAAQ;EAed,MAAM,mBAAmB;GACvB,MAAM,YAAY,OAAO;IACvB,cAAc;IACd,SAAS;IACT,cAAc;IACd,aAAa,MAAM;IACnB,aAAa,MAAM;IACnB,aAAa,MAAM;IACpB,CAAC;AAEF,OAAI,MAAM,MAAM,SAAS,EACvB,WAAU,SAAS,MAAM,MAAM;AAEjC,aAAU,OAAO;;AAGnB,WAAa,EAAE,YAAY,CAAC;;uBAzD1B,YAOW,MAAA,SAAA,EAAA;IAPD,WAAU;IAAS,SAAQ;;IACxB,SAAO,cAGN,CAFV,YAEU,MAAA,QAAA,EAAA;KAFD,MAAA;KAAM,SAAO;;4BACa,OAAA,OAAA,OAAA,KAAA,CAAjC,mBAAiC,OAAA,EAA5B,OAAM,qBAAmB,EAAA,MAAA,GAAA;;;;2BAGjB,2BAAjB,mBAAiB,QAAA,MAAX,QAAI,GAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"C_Icon2.js","names":["type","clickable","loading","title","ariaLabel","name","size","color","componentProps","svgPath","viewBox","alt","fallbackIcon"],"sources":["../src/hooks/useImage/index.ts","../src/components/C_Icon/index.vue","../src/components/C_Icon/index.vue","../src/components/C_Icon/index.vue"],"sourcesContent":["/**\r\n * @description: 图片路径解析函数(组件库版本)\r\n * 在组件库中,图片路径由宿主应用管理。\r\n * 如果传入的是完整 URL(http/https/data:)则直接返回;\r\n * 否则可通过 imageResolver 配置自定义解析逻辑。\r\n * @param {string} imagePath 图片路径或完整 URL\r\n * @param {Function} resolver 可选的自定义解析器\r\n * @return {Promise<string>} 处理后图片路径\r\n */\r\nexport const useImage = async (\r\n imagePath: string,\r\n resolver?: (path: string) => string | Promise<string>,\r\n): Promise<string> => {\r\n if (!imagePath) return \"\";\r\n\r\n // 完整 URL 直接返回\r\n if (\r\n imagePath.startsWith(\"http://\") ||\r\n imagePath.startsWith(\"https://\") ||\r\n imagePath.startsWith(\"data:\") ||\r\n imagePath.startsWith(\"blob:\") ||\r\n imagePath.startsWith(\"/\")\r\n ) {\r\n return imagePath;\r\n }\r\n\r\n // 如果提供了自定义解析器,使用自定义解析器\r\n if (resolver) {\r\n try {\r\n return await resolver(imagePath);\r\n } catch (e) {\r\n console.error(`[useImage] 自定义解析器处理失败: ${imagePath}`, e);\r\n return \"\";\r\n }\r\n }\r\n\r\n // 默认:直接返回路径(宿主应用应保证路径可访问)\r\n return imagePath;\r\n};\r\n\r\n/**\r\n * @description: 同步版本的图片路径处理函数\r\n * @param {string} imagePath 图片路径或完整 URL\r\n * @return {string} 图片URL\r\n */\r\nexport const useImageSync = (imagePath: string): string => {\r\n if (!imagePath) return \"\";\r\n\r\n // 完整 URL 直接返回\r\n if (\r\n imagePath.startsWith(\"http://\") ||\r\n imagePath.startsWith(\"https://\") ||\r\n imagePath.startsWith(\"data:\") ||\r\n imagePath.startsWith(\"blob:\") ||\r\n imagePath.startsWith(\"/\")\r\n ) {\r\n return imagePath;\r\n }\r\n\r\n return imagePath;\r\n};\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-14\r\n * @Description: 支持多种图标使用方式,默认使用Iconify图标,统一错误处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"iconRef\"\r\n class=\"c-icon\"\r\n :class=\"[\r\n `c-icon--${type}`,\r\n {\r\n 'c-icon--clickable': clickable,\r\n 'c-icon--loading': loading,\r\n 'c-icon--error': hasError,\r\n },\r\n ]\"\r\n :style=\"rootStyle\"\r\n :title=\"title || iconDisplayName\"\r\n :aria-label=\"ariaLabel || iconDisplayName\"\r\n :role=\"clickable ? 'button' : 'img'\"\r\n :tabindex=\"clickable ? 0 : -1\"\r\n @click=\"handleClick\"\r\n @keydown=\"handleKeydown\"\r\n >\r\n <!-- 方式1:Iconify图标(默认方式) -->\r\n <Icon\r\n v-if=\"\r\n (type === 'iconify' || !type) && !hasError && typeof name === 'string'\r\n \"\r\n :icon=\"name\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('iconify', 'Iconify图标加载失败')\"\r\n />\r\n\r\n <!-- 方式2:UnoCSS图标 -->\r\n <div\r\n v-else-if=\"type === 'unocss' && !hasError && typeof name === 'string'\"\r\n :class=\"[name, iconClasses]\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('unocss', 'UnoCSS图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式3:组件挂载方式 -->\r\n <component\r\n v-else-if=\"type === 'component' && resolvedComponent && !hasError\"\r\n :is=\"resolvedComponent\"\r\n :style=\"iconStyle\"\r\n v-bind=\"componentProps\"\r\n @error=\"() => handleError('component', '组件图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式4:SVG路径方式 -->\r\n <svg\r\n v-else-if=\"type === 'svg' && svgPath && !hasError\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :viewBox=\"viewBox\"\r\n :style=\"iconStyle\"\r\n :fill=\"color\"\r\n @error=\"() => handleError('svg', 'SVG图标渲染失败')\"\r\n >\r\n <path :d=\"svgPath\" :fill=\"color\" />\r\n </svg>\r\n\r\n <!-- 方式5:图片方式 - 修复异步加载 -->\r\n <img\r\n v-else-if=\"type === 'image' && imageSrc && !hasError && !imageLoading\"\r\n :src=\"imageSrc\"\r\n :alt=\"alt || iconDisplayName\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n style=\"display: inline-block; vertical-align: middle\"\r\n @error=\"() => handleError('image', '图片图标加载失败')\"\r\n @load=\"handleImageLoad\"\r\n />\r\n\r\n <!-- 图片加载状态 -->\r\n <div\r\n v-if=\"(loading || imageLoading) && !hasError\"\r\n class=\"c-icon__loading\"\r\n :style=\"iconStyle\"\r\n >\r\n <div class=\"c-icon__spinner\" />\r\n </div>\r\n\r\n <!-- 统一错误状态显示 -->\r\n <div\r\n v-else-if=\"hasError\"\r\n class=\"c-icon__error\"\r\n :style=\"iconStyle\"\r\n :title=\"`图标解析失败: ${iconDisplayName} (${errorMessage})${\r\n fallbackIcon ? ` (fallback: ${fallbackIcon})` : ''\r\n }`\"\r\n >\r\n ⚠️\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { Icon } from \"@iconify/vue\";\r\nimport { useImage } from \"../../hooks/useImage\";\r\nimport { computed, nextTick, onMounted, readonly, ref, watch } from \"vue\";\r\n\r\nexport interface IconProps {\r\n /** 图标名称/路径/组件实例 */\r\n name: string | any;\r\n /** 图标类型 */\r\n type?: \"iconify\" | \"unocss\" | \"component\" | \"svg\" | \"image\";\r\n /** 图标颜色 */\r\n color?: string;\r\n /** 图标大小(px) */\r\n size?: number | string;\r\n /** SVG路径数据(仅type=svg时使用) */\r\n svgPath?: string;\r\n /** SVG viewBox(仅type=svg时使用) */\r\n viewBox?: string;\r\n /** 图片alt属性(仅type=image时使用) */\r\n alt?: string;\r\n /** 是否可点击 */\r\n clickable?: boolean;\r\n /** 加载状态 */\r\n loading?: boolean;\r\n /** 错误时的回退图标 */\r\n fallbackIcon?: string;\r\n /** 工具提示 */\r\n title?: string;\r\n /** 无障碍标签 */\r\n ariaLabel?: string;\r\n /** 自定义样式类 */\r\n customClass?: string;\r\n /** 旋转角度 */\r\n rotate?: number;\r\n /** 是否翻转 */\r\n flip?: \"horizontal\" | \"vertical\" | \"both\";\r\n /** 传递给组件的额外属性(仅type=component时使用) */\r\n componentProps?: Record<string, any>;\r\n}\r\n\r\nconst props = withDefaults(defineProps<IconProps>(), {\r\n name: undefined,\r\n type: \"iconify\",\r\n color: \"currentColor\",\r\n size: 18,\r\n svgPath: \"\",\r\n viewBox: \"0 0 24 24\",\r\n alt: \"\",\r\n clickable: false,\r\n loading: false,\r\n fallbackIcon: \"\",\r\n title: \"\",\r\n ariaLabel: \"\",\r\n customClass: \"\",\r\n rotate: 0,\r\n flip: undefined,\r\n componentProps: () => ({}),\r\n});\r\n\r\nconst emit = defineEmits<{\r\n click: [event: MouseEvent];\r\n error: [type: string, error?: any];\r\n load: [];\r\n}>();\r\n\r\nconst iconRef = ref<HTMLElement>();\r\nconst hasError = ref(false);\r\nconst errorMessage = ref(\"\");\r\nconst imageSrc = ref<string>(\"\"); // 存储异步加载的图片URL\r\nconst imageLoading = ref(false); // 图片加载状态\r\n\r\n// 计算根元素样式\r\nconst rootStyle = computed(() => ({\r\n fontSize: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n color: props.color,\r\n cursor: props.clickable ? \"pointer\" : \"default\",\r\n}));\r\n\r\n// 计算图标样式\r\nconst iconStyle = computed(() => {\r\n const style: Record<string, any> = {\r\n display: \"inline-flex\",\r\n justifyContent: \"center\",\r\n alignItems: \"center\",\r\n width: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n height: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n };\r\n\r\n // 旋转\r\n if (props.rotate) {\r\n style.transform = `rotate(${props.rotate}deg)`;\r\n }\r\n\r\n // 翻转\r\n if (props.flip) {\r\n const scaleX =\r\n props.flip === \"horizontal\" || props.flip === \"both\" ? -1 : 1;\r\n const scaleY = props.flip === \"vertical\" || props.flip === \"both\" ? -1 : 1;\r\n style.transform = `${\r\n style.transform || \"\"\r\n } scaleX(${scaleX}) scaleY(${scaleY})`.trim();\r\n }\r\n\r\n return style;\r\n});\r\n\r\n// 获取图标名称用于显示\r\nconst iconDisplayName = computed(() => {\r\n if (typeof props.name === \"string\") {\r\n return props.name;\r\n }\r\n if (props.name && props.name.__name) {\r\n return props.name.__name;\r\n }\r\n if (props.name && props.name.name) {\r\n return props.name.name;\r\n }\r\n return \"Component Icon\";\r\n});\r\n\r\n// 图标类名\r\nconst iconClasses = computed(() => [\r\n props.customClass,\r\n {\r\n \"c-icon__rotatable\": props.rotate !== 0,\r\n \"c-icon__flippable\": !!props.flip,\r\n },\r\n]);\r\n\r\n// 解析组件名称(纯计算属性,无副作用)\r\nconst resolvedComponent = computed(() => {\r\n if (props.type !== \"component\" || !props.name) return null;\r\n return props.name;\r\n});\r\n\r\n// 统一错误处理函数\r\nconst handleError = (type: string, message: string, originalError?: any) => {\r\n console.warn(`[C_Icon] ${message}:`, iconDisplayName.value, originalError);\r\n hasError.value = true;\r\n errorMessage.value = message;\r\n imageLoading.value = false; // 错误时停止加载状态\r\n emit(\"error\", type, originalError);\r\n\r\n // 如果有回退图标,可以在这里尝试使用回退方案\r\n if (props.fallbackIcon && !hasError.value) {\r\n // 这里可以实现回退逻辑\r\n // 比如尝试使用fallbackIcon作为iconify图标\r\n }\r\n};\r\n\r\n// 处理点击事件\r\nconst handleClick = (event: MouseEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n emit(\"click\", event);\r\n};\r\n\r\n// 处理键盘事件\r\nconst handleKeydown = (event: KeyboardEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n if (event.key === \"Enter\" || event.key === \" \") {\r\n event.preventDefault();\r\n emit(\"click\", event as any);\r\n }\r\n};\r\n\r\n// 处理图片加载成功\r\nconst handleImageLoad = () => {\r\n imageLoading.value = false;\r\n emit(\"load\");\r\n};\r\n\r\n// 异步加载图片 - 修复关键函数\r\nconst loadImageSrc = async () => {\r\n if (props.type !== \"image\" || typeof props.name !== \"string\") {\r\n imageSrc.value = \"\";\r\n return;\r\n }\r\n\r\n // 如果是完整URL,直接设置\r\n if (\r\n props.name.startsWith(\"http://\") ||\r\n props.name.startsWith(\"https://\") ||\r\n props.name.startsWith(\"//\") ||\r\n props.name.startsWith(\"/\")\r\n ) {\r\n imageSrc.value = props.name;\r\n return;\r\n }\r\n\r\n // 其他情况都当作本地图片处理\r\n try {\r\n imageLoading.value = true;\r\n const imageUrl = await useImage(props.name);\r\n if (imageUrl) {\r\n imageSrc.value = imageUrl;\r\n } else {\r\n handleError(\"image\", \"图片路径解析失败\");\r\n }\r\n } catch (error) {\r\n handleError(\"image\", \"图片加载失败\", error);\r\n } finally {\r\n imageLoading.value = false;\r\n }\r\n};\r\n\r\n// 验证规则映射\r\nconst validationRules = {\r\n iconify: (): string | null => {\r\n if (!props.name.includes(\":\")) {\r\n console.warn(\r\n '[C_Icon] Iconify图标名称格式应为 \"prefix:name\",如 \"mdi:home\"',\r\n );\r\n }\r\n return null;\r\n },\r\n unocss: (): string | null => {\r\n if (typeof props.name !== \"string\" || !props.name.startsWith(\"i-\")) {\r\n console.warn('[C_Icon] UnoCSS图标名称应以 \"i-\" 开头');\r\n }\r\n return null;\r\n },\r\n component: (): string | null =>\r\n !resolvedComponent.value ? \"无法解析组件\" : null,\r\n svg: (): string | null => (!props.svgPath ? \"SVG路径不能为空\" : null),\r\n image: (): string | null => {\r\n // 对于image类型,异步验证在loadImageSrc中处理\r\n return null;\r\n },\r\n};\r\n\r\n// 验证配置\r\nconst validateProps = () => {\r\n hasError.value = false;\r\n errorMessage.value = \"\";\r\n\r\n // 基础验证\r\n if (!props.name) {\r\n return handleError(\"validation\", \"图标名称不能为空\");\r\n }\r\n\r\n // 类型验证\r\n const validator = validationRules[props.type];\r\n if (!validator) {\r\n return handleError(\"type\", \"不支持的图标类型\");\r\n }\r\n\r\n const errorMsg = validator();\r\n if (errorMsg) {\r\n handleError(props.type, errorMsg);\r\n }\r\n\r\n // 如果是图片类型,异步加载图片\r\n if (props.type === \"image\") {\r\n loadImageSrc();\r\n }\r\n};\r\n\r\n// 监听关键属性变化\r\nwatch(\r\n () => [props.name, props.type, props.svgPath],\r\n () => {\r\n nextTick(validateProps);\r\n },\r\n { immediate: true },\r\n);\r\n\r\n// 组件挂载后验证\r\nonMounted(() => {\r\n validateProps();\r\n // 非image类型在这里触发load事件\r\n if (!hasError.value && props.type !== \"image\") {\r\n emit(\"load\");\r\n }\r\n});\r\n\r\n// 暴露方法给父组件\r\ndefineExpose({\r\n /** 重新验证配置 */\r\n validate: validateProps,\r\n /** 是否有错误 */\r\n hasError: readonly(hasError),\r\n /** 错误信息 */\r\n errorMessage: readonly(errorMessage),\r\n /** DOM引用 */\r\n el: iconRef,\r\n});\r\n</script>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-14\r\n * @Description: 支持多种图标使用方式,默认使用Iconify图标,统一错误处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"iconRef\"\r\n class=\"c-icon\"\r\n :class=\"[\r\n `c-icon--${type}`,\r\n {\r\n 'c-icon--clickable': clickable,\r\n 'c-icon--loading': loading,\r\n 'c-icon--error': hasError,\r\n },\r\n ]\"\r\n :style=\"rootStyle\"\r\n :title=\"title || iconDisplayName\"\r\n :aria-label=\"ariaLabel || iconDisplayName\"\r\n :role=\"clickable ? 'button' : 'img'\"\r\n :tabindex=\"clickable ? 0 : -1\"\r\n @click=\"handleClick\"\r\n @keydown=\"handleKeydown\"\r\n >\r\n <!-- 方式1:Iconify图标(默认方式) -->\r\n <Icon\r\n v-if=\"\r\n (type === 'iconify' || !type) && !hasError && typeof name === 'string'\r\n \"\r\n :icon=\"name\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('iconify', 'Iconify图标加载失败')\"\r\n />\r\n\r\n <!-- 方式2:UnoCSS图标 -->\r\n <div\r\n v-else-if=\"type === 'unocss' && !hasError && typeof name === 'string'\"\r\n :class=\"[name, iconClasses]\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('unocss', 'UnoCSS图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式3:组件挂载方式 -->\r\n <component\r\n v-else-if=\"type === 'component' && resolvedComponent && !hasError\"\r\n :is=\"resolvedComponent\"\r\n :style=\"iconStyle\"\r\n v-bind=\"componentProps\"\r\n @error=\"() => handleError('component', '组件图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式4:SVG路径方式 -->\r\n <svg\r\n v-else-if=\"type === 'svg' && svgPath && !hasError\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :viewBox=\"viewBox\"\r\n :style=\"iconStyle\"\r\n :fill=\"color\"\r\n @error=\"() => handleError('svg', 'SVG图标渲染失败')\"\r\n >\r\n <path :d=\"svgPath\" :fill=\"color\" />\r\n </svg>\r\n\r\n <!-- 方式5:图片方式 - 修复异步加载 -->\r\n <img\r\n v-else-if=\"type === 'image' && imageSrc && !hasError && !imageLoading\"\r\n :src=\"imageSrc\"\r\n :alt=\"alt || iconDisplayName\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n style=\"display: inline-block; vertical-align: middle\"\r\n @error=\"() => handleError('image', '图片图标加载失败')\"\r\n @load=\"handleImageLoad\"\r\n />\r\n\r\n <!-- 图片加载状态 -->\r\n <div\r\n v-if=\"(loading || imageLoading) && !hasError\"\r\n class=\"c-icon__loading\"\r\n :style=\"iconStyle\"\r\n >\r\n <div class=\"c-icon__spinner\" />\r\n </div>\r\n\r\n <!-- 统一错误状态显示 -->\r\n <div\r\n v-else-if=\"hasError\"\r\n class=\"c-icon__error\"\r\n :style=\"iconStyle\"\r\n :title=\"`图标解析失败: ${iconDisplayName} (${errorMessage})${\r\n fallbackIcon ? ` (fallback: ${fallbackIcon})` : ''\r\n }`\"\r\n >\r\n ⚠️\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { Icon } from \"@iconify/vue\";\r\nimport { useImage } from \"../../hooks/useImage\";\r\nimport { computed, nextTick, onMounted, readonly, ref, watch } from \"vue\";\r\n\r\nexport interface IconProps {\r\n /** 图标名称/路径/组件实例 */\r\n name: string | any;\r\n /** 图标类型 */\r\n type?: \"iconify\" | \"unocss\" | \"component\" | \"svg\" | \"image\";\r\n /** 图标颜色 */\r\n color?: string;\r\n /** 图标大小(px) */\r\n size?: number | string;\r\n /** SVG路径数据(仅type=svg时使用) */\r\n svgPath?: string;\r\n /** SVG viewBox(仅type=svg时使用) */\r\n viewBox?: string;\r\n /** 图片alt属性(仅type=image时使用) */\r\n alt?: string;\r\n /** 是否可点击 */\r\n clickable?: boolean;\r\n /** 加载状态 */\r\n loading?: boolean;\r\n /** 错误时的回退图标 */\r\n fallbackIcon?: string;\r\n /** 工具提示 */\r\n title?: string;\r\n /** 无障碍标签 */\r\n ariaLabel?: string;\r\n /** 自定义样式类 */\r\n customClass?: string;\r\n /** 旋转角度 */\r\n rotate?: number;\r\n /** 是否翻转 */\r\n flip?: \"horizontal\" | \"vertical\" | \"both\";\r\n /** 传递给组件的额外属性(仅type=component时使用) */\r\n componentProps?: Record<string, any>;\r\n}\r\n\r\nconst props = withDefaults(defineProps<IconProps>(), {\r\n name: undefined,\r\n type: \"iconify\",\r\n color: \"currentColor\",\r\n size: 18,\r\n svgPath: \"\",\r\n viewBox: \"0 0 24 24\",\r\n alt: \"\",\r\n clickable: false,\r\n loading: false,\r\n fallbackIcon: \"\",\r\n title: \"\",\r\n ariaLabel: \"\",\r\n customClass: \"\",\r\n rotate: 0,\r\n flip: undefined,\r\n componentProps: () => ({}),\r\n});\r\n\r\nconst emit = defineEmits<{\r\n click: [event: MouseEvent];\r\n error: [type: string, error?: any];\r\n load: [];\r\n}>();\r\n\r\nconst iconRef = ref<HTMLElement>();\r\nconst hasError = ref(false);\r\nconst errorMessage = ref(\"\");\r\nconst imageSrc = ref<string>(\"\"); // 存储异步加载的图片URL\r\nconst imageLoading = ref(false); // 图片加载状态\r\n\r\n// 计算根元素样式\r\nconst rootStyle = computed(() => ({\r\n fontSize: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n color: props.color,\r\n cursor: props.clickable ? \"pointer\" : \"default\",\r\n}));\r\n\r\n// 计算图标样式\r\nconst iconStyle = computed(() => {\r\n const style: Record<string, any> = {\r\n display: \"inline-flex\",\r\n justifyContent: \"center\",\r\n alignItems: \"center\",\r\n width: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n height: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n };\r\n\r\n // 旋转\r\n if (props.rotate) {\r\n style.transform = `rotate(${props.rotate}deg)`;\r\n }\r\n\r\n // 翻转\r\n if (props.flip) {\r\n const scaleX =\r\n props.flip === \"horizontal\" || props.flip === \"both\" ? -1 : 1;\r\n const scaleY = props.flip === \"vertical\" || props.flip === \"both\" ? -1 : 1;\r\n style.transform = `${\r\n style.transform || \"\"\r\n } scaleX(${scaleX}) scaleY(${scaleY})`.trim();\r\n }\r\n\r\n return style;\r\n});\r\n\r\n// 获取图标名称用于显示\r\nconst iconDisplayName = computed(() => {\r\n if (typeof props.name === \"string\") {\r\n return props.name;\r\n }\r\n if (props.name && props.name.__name) {\r\n return props.name.__name;\r\n }\r\n if (props.name && props.name.name) {\r\n return props.name.name;\r\n }\r\n return \"Component Icon\";\r\n});\r\n\r\n// 图标类名\r\nconst iconClasses = computed(() => [\r\n props.customClass,\r\n {\r\n \"c-icon__rotatable\": props.rotate !== 0,\r\n \"c-icon__flippable\": !!props.flip,\r\n },\r\n]);\r\n\r\n// 解析组件名称(纯计算属性,无副作用)\r\nconst resolvedComponent = computed(() => {\r\n if (props.type !== \"component\" || !props.name) return null;\r\n return props.name;\r\n});\r\n\r\n// 统一错误处理函数\r\nconst handleError = (type: string, message: string, originalError?: any) => {\r\n console.warn(`[C_Icon] ${message}:`, iconDisplayName.value, originalError);\r\n hasError.value = true;\r\n errorMessage.value = message;\r\n imageLoading.value = false; // 错误时停止加载状态\r\n emit(\"error\", type, originalError);\r\n\r\n // 如果有回退图标,可以在这里尝试使用回退方案\r\n if (props.fallbackIcon && !hasError.value) {\r\n // 这里可以实现回退逻辑\r\n // 比如尝试使用fallbackIcon作为iconify图标\r\n }\r\n};\r\n\r\n// 处理点击事件\r\nconst handleClick = (event: MouseEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n emit(\"click\", event);\r\n};\r\n\r\n// 处理键盘事件\r\nconst handleKeydown = (event: KeyboardEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n if (event.key === \"Enter\" || event.key === \" \") {\r\n event.preventDefault();\r\n emit(\"click\", event as any);\r\n }\r\n};\r\n\r\n// 处理图片加载成功\r\nconst handleImageLoad = () => {\r\n imageLoading.value = false;\r\n emit(\"load\");\r\n};\r\n\r\n// 异步加载图片 - 修复关键函数\r\nconst loadImageSrc = async () => {\r\n if (props.type !== \"image\" || typeof props.name !== \"string\") {\r\n imageSrc.value = \"\";\r\n return;\r\n }\r\n\r\n // 如果是完整URL,直接设置\r\n if (\r\n props.name.startsWith(\"http://\") ||\r\n props.name.startsWith(\"https://\") ||\r\n props.name.startsWith(\"//\") ||\r\n props.name.startsWith(\"/\")\r\n ) {\r\n imageSrc.value = props.name;\r\n return;\r\n }\r\n\r\n // 其他情况都当作本地图片处理\r\n try {\r\n imageLoading.value = true;\r\n const imageUrl = await useImage(props.name);\r\n if (imageUrl) {\r\n imageSrc.value = imageUrl;\r\n } else {\r\n handleError(\"image\", \"图片路径解析失败\");\r\n }\r\n } catch (error) {\r\n handleError(\"image\", \"图片加载失败\", error);\r\n } finally {\r\n imageLoading.value = false;\r\n }\r\n};\r\n\r\n// 验证规则映射\r\nconst validationRules = {\r\n iconify: (): string | null => {\r\n if (!props.name.includes(\":\")) {\r\n console.warn(\r\n '[C_Icon] Iconify图标名称格式应为 \"prefix:name\",如 \"mdi:home\"',\r\n );\r\n }\r\n return null;\r\n },\r\n unocss: (): string | null => {\r\n if (typeof props.name !== \"string\" || !props.name.startsWith(\"i-\")) {\r\n console.warn('[C_Icon] UnoCSS图标名称应以 \"i-\" 开头');\r\n }\r\n return null;\r\n },\r\n component: (): string | null =>\r\n !resolvedComponent.value ? \"无法解析组件\" : null,\r\n svg: (): string | null => (!props.svgPath ? \"SVG路径不能为空\" : null),\r\n image: (): string | null => {\r\n // 对于image类型,异步验证在loadImageSrc中处理\r\n return null;\r\n },\r\n};\r\n\r\n// 验证配置\r\nconst validateProps = () => {\r\n hasError.value = false;\r\n errorMessage.value = \"\";\r\n\r\n // 基础验证\r\n if (!props.name) {\r\n return handleError(\"validation\", \"图标名称不能为空\");\r\n }\r\n\r\n // 类型验证\r\n const validator = validationRules[props.type];\r\n if (!validator) {\r\n return handleError(\"type\", \"不支持的图标类型\");\r\n }\r\n\r\n const errorMsg = validator();\r\n if (errorMsg) {\r\n handleError(props.type, errorMsg);\r\n }\r\n\r\n // 如果是图片类型,异步加载图片\r\n if (props.type === \"image\") {\r\n loadImageSrc();\r\n }\r\n};\r\n\r\n// 监听关键属性变化\r\nwatch(\r\n () => [props.name, props.type, props.svgPath],\r\n () => {\r\n nextTick(validateProps);\r\n },\r\n { immediate: true },\r\n);\r\n\r\n// 组件挂载后验证\r\nonMounted(() => {\r\n validateProps();\r\n // 非image类型在这里触发load事件\r\n if (!hasError.value && props.type !== \"image\") {\r\n emit(\"load\");\r\n }\r\n});\r\n\r\n// 暴露方法给父组件\r\ndefineExpose({\r\n /** 重新验证配置 */\r\n validate: validateProps,\r\n /** 是否有错误 */\r\n hasError: readonly(hasError),\r\n /** 错误信息 */\r\n errorMessage: readonly(errorMessage),\r\n /** DOM引用 */\r\n el: iconRef,\r\n});\r\n</script>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-14\r\n * @Description: 支持多种图标使用方式,默认使用Iconify图标,统一错误处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"iconRef\"\r\n class=\"c-icon\"\r\n :class=\"[\r\n `c-icon--${type}`,\r\n {\r\n 'c-icon--clickable': clickable,\r\n 'c-icon--loading': loading,\r\n 'c-icon--error': hasError,\r\n },\r\n ]\"\r\n :style=\"rootStyle\"\r\n :title=\"title || iconDisplayName\"\r\n :aria-label=\"ariaLabel || iconDisplayName\"\r\n :role=\"clickable ? 'button' : 'img'\"\r\n :tabindex=\"clickable ? 0 : -1\"\r\n @click=\"handleClick\"\r\n @keydown=\"handleKeydown\"\r\n >\r\n <!-- 方式1:Iconify图标(默认方式) -->\r\n <Icon\r\n v-if=\"\r\n (type === 'iconify' || !type) && !hasError && typeof name === 'string'\r\n \"\r\n :icon=\"name\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('iconify', 'Iconify图标加载失败')\"\r\n />\r\n\r\n <!-- 方式2:UnoCSS图标 -->\r\n <div\r\n v-else-if=\"type === 'unocss' && !hasError && typeof name === 'string'\"\r\n :class=\"[name, iconClasses]\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('unocss', 'UnoCSS图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式3:组件挂载方式 -->\r\n <component\r\n v-else-if=\"type === 'component' && resolvedComponent && !hasError\"\r\n :is=\"resolvedComponent\"\r\n :style=\"iconStyle\"\r\n v-bind=\"componentProps\"\r\n @error=\"() => handleError('component', '组件图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式4:SVG路径方式 -->\r\n <svg\r\n v-else-if=\"type === 'svg' && svgPath && !hasError\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :viewBox=\"viewBox\"\r\n :style=\"iconStyle\"\r\n :fill=\"color\"\r\n @error=\"() => handleError('svg', 'SVG图标渲染失败')\"\r\n >\r\n <path :d=\"svgPath\" :fill=\"color\" />\r\n </svg>\r\n\r\n <!-- 方式5:图片方式 - 修复异步加载 -->\r\n <img\r\n v-else-if=\"type === 'image' && imageSrc && !hasError && !imageLoading\"\r\n :src=\"imageSrc\"\r\n :alt=\"alt || iconDisplayName\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n style=\"display: inline-block; vertical-align: middle\"\r\n @error=\"() => handleError('image', '图片图标加载失败')\"\r\n @load=\"handleImageLoad\"\r\n />\r\n\r\n <!-- 图片加载状态 -->\r\n <div\r\n v-if=\"(loading || imageLoading) && !hasError\"\r\n class=\"c-icon__loading\"\r\n :style=\"iconStyle\"\r\n >\r\n <div class=\"c-icon__spinner\" />\r\n </div>\r\n\r\n <!-- 统一错误状态显示 -->\r\n <div\r\n v-else-if=\"hasError\"\r\n class=\"c-icon__error\"\r\n :style=\"iconStyle\"\r\n :title=\"`图标解析失败: ${iconDisplayName} (${errorMessage})${\r\n fallbackIcon ? ` (fallback: ${fallbackIcon})` : ''\r\n }`\"\r\n >\r\n ⚠️\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { Icon } from \"@iconify/vue\";\r\nimport { useImage } from \"../../hooks/useImage\";\r\nimport { computed, nextTick, onMounted, readonly, ref, watch } from \"vue\";\r\n\r\nexport interface IconProps {\r\n /** 图标名称/路径/组件实例 */\r\n name: string | any;\r\n /** 图标类型 */\r\n type?: \"iconify\" | \"unocss\" | \"component\" | \"svg\" | \"image\";\r\n /** 图标颜色 */\r\n color?: string;\r\n /** 图标大小(px) */\r\n size?: number | string;\r\n /** SVG路径数据(仅type=svg时使用) */\r\n svgPath?: string;\r\n /** SVG viewBox(仅type=svg时使用) */\r\n viewBox?: string;\r\n /** 图片alt属性(仅type=image时使用) */\r\n alt?: string;\r\n /** 是否可点击 */\r\n clickable?: boolean;\r\n /** 加载状态 */\r\n loading?: boolean;\r\n /** 错误时的回退图标 */\r\n fallbackIcon?: string;\r\n /** 工具提示 */\r\n title?: string;\r\n /** 无障碍标签 */\r\n ariaLabel?: string;\r\n /** 自定义样式类 */\r\n customClass?: string;\r\n /** 旋转角度 */\r\n rotate?: number;\r\n /** 是否翻转 */\r\n flip?: \"horizontal\" | \"vertical\" | \"both\";\r\n /** 传递给组件的额外属性(仅type=component时使用) */\r\n componentProps?: Record<string, any>;\r\n}\r\n\r\nconst props = withDefaults(defineProps<IconProps>(), {\r\n name: undefined,\r\n type: \"iconify\",\r\n color: \"currentColor\",\r\n size: 18,\r\n svgPath: \"\",\r\n viewBox: \"0 0 24 24\",\r\n alt: \"\",\r\n clickable: false,\r\n loading: false,\r\n fallbackIcon: \"\",\r\n title: \"\",\r\n ariaLabel: \"\",\r\n customClass: \"\",\r\n rotate: 0,\r\n flip: undefined,\r\n componentProps: () => ({}),\r\n});\r\n\r\nconst emit = defineEmits<{\r\n click: [event: MouseEvent];\r\n error: [type: string, error?: any];\r\n load: [];\r\n}>();\r\n\r\nconst iconRef = ref<HTMLElement>();\r\nconst hasError = ref(false);\r\nconst errorMessage = ref(\"\");\r\nconst imageSrc = ref<string>(\"\"); // 存储异步加载的图片URL\r\nconst imageLoading = ref(false); // 图片加载状态\r\n\r\n// 计算根元素样式\r\nconst rootStyle = computed(() => ({\r\n fontSize: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n color: props.color,\r\n cursor: props.clickable ? \"pointer\" : \"default\",\r\n}));\r\n\r\n// 计算图标样式\r\nconst iconStyle = computed(() => {\r\n const style: Record<string, any> = {\r\n display: \"inline-flex\",\r\n justifyContent: \"center\",\r\n alignItems: \"center\",\r\n width: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n height: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n };\r\n\r\n // 旋转\r\n if (props.rotate) {\r\n style.transform = `rotate(${props.rotate}deg)`;\r\n }\r\n\r\n // 翻转\r\n if (props.flip) {\r\n const scaleX =\r\n props.flip === \"horizontal\" || props.flip === \"both\" ? -1 : 1;\r\n const scaleY = props.flip === \"vertical\" || props.flip === \"both\" ? -1 : 1;\r\n style.transform = `${\r\n style.transform || \"\"\r\n } scaleX(${scaleX}) scaleY(${scaleY})`.trim();\r\n }\r\n\r\n return style;\r\n});\r\n\r\n// 获取图标名称用于显示\r\nconst iconDisplayName = computed(() => {\r\n if (typeof props.name === \"string\") {\r\n return props.name;\r\n }\r\n if (props.name && props.name.__name) {\r\n return props.name.__name;\r\n }\r\n if (props.name && props.name.name) {\r\n return props.name.name;\r\n }\r\n return \"Component Icon\";\r\n});\r\n\r\n// 图标类名\r\nconst iconClasses = computed(() => [\r\n props.customClass,\r\n {\r\n \"c-icon__rotatable\": props.rotate !== 0,\r\n \"c-icon__flippable\": !!props.flip,\r\n },\r\n]);\r\n\r\n// 解析组件名称(纯计算属性,无副作用)\r\nconst resolvedComponent = computed(() => {\r\n if (props.type !== \"component\" || !props.name) return null;\r\n return props.name;\r\n});\r\n\r\n// 统一错误处理函数\r\nconst handleError = (type: string, message: string, originalError?: any) => {\r\n console.warn(`[C_Icon] ${message}:`, iconDisplayName.value, originalError);\r\n hasError.value = true;\r\n errorMessage.value = message;\r\n imageLoading.value = false; // 错误时停止加载状态\r\n emit(\"error\", type, originalError);\r\n\r\n // 如果有回退图标,可以在这里尝试使用回退方案\r\n if (props.fallbackIcon && !hasError.value) {\r\n // 这里可以实现回退逻辑\r\n // 比如尝试使用fallbackIcon作为iconify图标\r\n }\r\n};\r\n\r\n// 处理点击事件\r\nconst handleClick = (event: MouseEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n emit(\"click\", event);\r\n};\r\n\r\n// 处理键盘事件\r\nconst handleKeydown = (event: KeyboardEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n if (event.key === \"Enter\" || event.key === \" \") {\r\n event.preventDefault();\r\n emit(\"click\", event as any);\r\n }\r\n};\r\n\r\n// 处理图片加载成功\r\nconst handleImageLoad = () => {\r\n imageLoading.value = false;\r\n emit(\"load\");\r\n};\r\n\r\n// 异步加载图片 - 修复关键函数\r\nconst loadImageSrc = async () => {\r\n if (props.type !== \"image\" || typeof props.name !== \"string\") {\r\n imageSrc.value = \"\";\r\n return;\r\n }\r\n\r\n // 如果是完整URL,直接设置\r\n if (\r\n props.name.startsWith(\"http://\") ||\r\n props.name.startsWith(\"https://\") ||\r\n props.name.startsWith(\"//\") ||\r\n props.name.startsWith(\"/\")\r\n ) {\r\n imageSrc.value = props.name;\r\n return;\r\n }\r\n\r\n // 其他情况都当作本地图片处理\r\n try {\r\n imageLoading.value = true;\r\n const imageUrl = await useImage(props.name);\r\n if (imageUrl) {\r\n imageSrc.value = imageUrl;\r\n } else {\r\n handleError(\"image\", \"图片路径解析失败\");\r\n }\r\n } catch (error) {\r\n handleError(\"image\", \"图片加载失败\", error);\r\n } finally {\r\n imageLoading.value = false;\r\n }\r\n};\r\n\r\n// 验证规则映射\r\nconst validationRules = {\r\n iconify: (): string | null => {\r\n if (!props.name.includes(\":\")) {\r\n console.warn(\r\n '[C_Icon] Iconify图标名称格式应为 \"prefix:name\",如 \"mdi:home\"',\r\n );\r\n }\r\n return null;\r\n },\r\n unocss: (): string | null => {\r\n if (typeof props.name !== \"string\" || !props.name.startsWith(\"i-\")) {\r\n console.warn('[C_Icon] UnoCSS图标名称应以 \"i-\" 开头');\r\n }\r\n return null;\r\n },\r\n component: (): string | null =>\r\n !resolvedComponent.value ? \"无法解析组件\" : null,\r\n svg: (): string | null => (!props.svgPath ? \"SVG路径不能为空\" : null),\r\n image: (): string | null => {\r\n // 对于image类型,异步验证在loadImageSrc中处理\r\n return null;\r\n },\r\n};\r\n\r\n// 验证配置\r\nconst validateProps = () => {\r\n hasError.value = false;\r\n errorMessage.value = \"\";\r\n\r\n // 基础验证\r\n if (!props.name) {\r\n return handleError(\"validation\", \"图标名称不能为空\");\r\n }\r\n\r\n // 类型验证\r\n const validator = validationRules[props.type];\r\n if (!validator) {\r\n return handleError(\"type\", \"不支持的图标类型\");\r\n }\r\n\r\n const errorMsg = validator();\r\n if (errorMsg) {\r\n handleError(props.type, errorMsg);\r\n }\r\n\r\n // 如果是图片类型,异步加载图片\r\n if (props.type === \"image\") {\r\n loadImageSrc();\r\n }\r\n};\r\n\r\n// 监听关键属性变化\r\nwatch(\r\n () => [props.name, props.type, props.svgPath],\r\n () => {\r\n nextTick(validateProps);\r\n },\r\n { immediate: true },\r\n);\r\n\r\n// 组件挂载后验证\r\nonMounted(() => {\r\n validateProps();\r\n // 非image类型在这里触发load事件\r\n if (!hasError.value && props.type !== \"image\") {\r\n emit(\"load\");\r\n }\r\n});\r\n\r\n// 暴露方法给父组件\r\ndefineExpose({\r\n /** 重新验证配置 */\r\n validate: validateProps,\r\n /** 是否有错误 */\r\n hasError: readonly(hasError),\r\n /** 错误信息 */\r\n errorMessage: readonly(errorMessage),\r\n /** DOM引用 */\r\n el: iconRef,\r\n});\r\n</script>\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"C_Icon2.js","names":["type","clickable","loading","title","ariaLabel","name","size","color","componentProps","svgPath","viewBox","alt","fallbackIcon"],"sources":["../src/hooks/useImage/index.ts","../src/components/C_Icon/index.vue","../src/components/C_Icon/index.vue","../src/components/C_Icon/index.vue"],"sourcesContent":["/**\r\n * @description: 图片路径解析函数(组件库版本)\r\n * 在组件库中,图片路径由宿主应用管理。\r\n * 如果传入的是完整 URL(http/https/data:)则直接返回;\r\n * 否则可通过 imageResolver 配置自定义解析逻辑。\r\n * @param {string} imagePath 图片路径或完整 URL\r\n * @param {Function} resolver 可选的自定义解析器\r\n * @return {Promise<string>} 处理后图片路径\r\n */\r\nexport const useImage = async (\r\n imagePath: string,\r\n resolver?: (path: string) => string | Promise<string>,\r\n): Promise<string> => {\r\n if (!imagePath) return \"\";\r\n\r\n // 完整 URL 直接返回\r\n if (\r\n imagePath.startsWith(\"http://\") ||\r\n imagePath.startsWith(\"https://\") ||\r\n imagePath.startsWith(\"data:\") ||\r\n imagePath.startsWith(\"blob:\") ||\r\n imagePath.startsWith(\"/\")\r\n ) {\r\n return imagePath;\r\n }\r\n\r\n // 如果提供了自定义解析器,使用自定义解析器\r\n if (resolver) {\r\n try {\r\n return await resolver(imagePath);\r\n } catch (e) {\r\n console.error(`[useImage] 自定义解析器处理失败: ${imagePath}`, e);\r\n return \"\";\r\n }\r\n }\r\n\r\n // 默认:直接返回路径(宿主应用应保证路径可访问)\r\n return imagePath;\r\n};\r\n\r\n/**\r\n * @description: 同步版本的图片路径处理函数\r\n * @param {string} imagePath 图片路径或完整 URL\r\n * @return {string} 图片URL\r\n */\r\nexport const useImageSync = (imagePath: string): string => {\r\n if (!imagePath) return \"\";\r\n\r\n // 完整 URL 直接返回\r\n if (\r\n imagePath.startsWith(\"http://\") ||\r\n imagePath.startsWith(\"https://\") ||\r\n imagePath.startsWith(\"data:\") ||\r\n imagePath.startsWith(\"blob:\") ||\r\n imagePath.startsWith(\"/\")\r\n ) {\r\n return imagePath;\r\n }\r\n\r\n return imagePath;\r\n};\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-14\r\n * @Description: 支持多种图标使用方式,默认使用Iconify图标,统一错误处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"iconRef\"\r\n class=\"c-icon\"\r\n :class=\"[\r\n `c-icon--${type}`,\r\n {\r\n 'c-icon--clickable': clickable,\r\n 'c-icon--loading': loading,\r\n 'c-icon--error': hasError,\r\n },\r\n ]\"\r\n :style=\"rootStyle\"\r\n :title=\"title || iconDisplayName\"\r\n :aria-label=\"ariaLabel || iconDisplayName\"\r\n :role=\"clickable ? 'button' : 'img'\"\r\n :tabindex=\"clickable ? 0 : -1\"\r\n @click=\"handleClick\"\r\n @keydown=\"handleKeydown\"\r\n >\r\n <!-- 方式1:Iconify图标(默认方式) -->\r\n <Icon\r\n v-if=\"\r\n (type === 'iconify' || !type) && !hasError && typeof name === 'string'\r\n \"\r\n :icon=\"name\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('iconify', 'Iconify图标加载失败')\"\r\n />\r\n\r\n <!-- 方式2:UnoCSS图标 -->\r\n <div\r\n v-else-if=\"type === 'unocss' && !hasError && typeof name === 'string'\"\r\n :class=\"[name, iconClasses]\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('unocss', 'UnoCSS图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式3:组件挂载方式 -->\r\n <component\r\n v-else-if=\"type === 'component' && resolvedComponent && !hasError\"\r\n :is=\"resolvedComponent\"\r\n :style=\"iconStyle\"\r\n v-bind=\"componentProps\"\r\n @error=\"() => handleError('component', '组件图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式4:SVG路径方式 -->\r\n <svg\r\n v-else-if=\"type === 'svg' && svgPath && !hasError\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :viewBox=\"viewBox\"\r\n :style=\"iconStyle\"\r\n :fill=\"color\"\r\n @error=\"() => handleError('svg', 'SVG图标渲染失败')\"\r\n >\r\n <path :d=\"svgPath\" :fill=\"color\" />\r\n </svg>\r\n\r\n <!-- 方式5:图片方式 - 修复异步加载 -->\r\n <img\r\n v-else-if=\"type === 'image' && imageSrc && !hasError && !imageLoading\"\r\n :src=\"imageSrc\"\r\n :alt=\"alt || iconDisplayName\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n style=\"display: inline-block; vertical-align: middle\"\r\n @error=\"() => handleError('image', '图片图标加载失败')\"\r\n @load=\"handleImageLoad\"\r\n />\r\n\r\n <!-- 图片加载状态 -->\r\n <div\r\n v-if=\"(loading || imageLoading) && !hasError\"\r\n class=\"c-icon__loading\"\r\n :style=\"iconStyle\"\r\n >\r\n <div class=\"c-icon__spinner\" />\r\n </div>\r\n\r\n <!-- 统一错误状态显示 -->\r\n <div\r\n v-else-if=\"hasError\"\r\n class=\"c-icon__error\"\r\n :style=\"iconStyle\"\r\n :title=\"`图标解析失败: ${iconDisplayName} (${errorMessage})${\r\n fallbackIcon ? ` (fallback: ${fallbackIcon})` : ''\r\n }`\"\r\n >\r\n ⚠️\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { Icon } from \"@iconify/vue\";\r\nimport { useImage } from \"../../hooks/useImage\";\r\nimport { computed, nextTick, onMounted, readonly, ref, watch } from \"vue\";\r\n\r\nexport interface IconProps {\r\n /** 图标名称/路径/组件实例 */\r\n name: string | any;\r\n /** 图标类型 */\r\n type?: \"iconify\" | \"unocss\" | \"component\" | \"svg\" | \"image\";\r\n /** 图标颜色 */\r\n color?: string;\r\n /** 图标大小(px) */\r\n size?: number | string;\r\n /** SVG路径数据(仅type=svg时使用) */\r\n svgPath?: string;\r\n /** SVG viewBox(仅type=svg时使用) */\r\n viewBox?: string;\r\n /** 图片alt属性(仅type=image时使用) */\r\n alt?: string;\r\n /** 是否可点击 */\r\n clickable?: boolean;\r\n /** 加载状态 */\r\n loading?: boolean;\r\n /** 错误时的回退图标 */\r\n fallbackIcon?: string;\r\n /** 工具提示 */\r\n title?: string;\r\n /** 无障碍标签 */\r\n ariaLabel?: string;\r\n /** 自定义样式类 */\r\n customClass?: string;\r\n /** 旋转角度 */\r\n rotate?: number;\r\n /** 是否翻转 */\r\n flip?: \"horizontal\" | \"vertical\" | \"both\";\r\n /** 传递给组件的额外属性(仅type=component时使用) */\r\n componentProps?: Record<string, any>;\r\n}\r\n\r\nconst props = withDefaults(defineProps<IconProps>(), {\r\n name: undefined,\r\n type: \"iconify\",\r\n color: \"currentColor\",\r\n size: 18,\r\n svgPath: \"\",\r\n viewBox: \"0 0 24 24\",\r\n alt: \"\",\r\n clickable: false,\r\n loading: false,\r\n fallbackIcon: \"\",\r\n title: \"\",\r\n ariaLabel: \"\",\r\n customClass: \"\",\r\n rotate: 0,\r\n flip: undefined,\r\n componentProps: () => ({}),\r\n});\r\n\r\nconst emit = defineEmits<{\r\n click: [event: MouseEvent];\r\n error: [type: string, error?: any];\r\n load: [];\r\n}>();\r\n\r\nconst iconRef = ref<HTMLElement>();\r\nconst hasError = ref(false);\r\nconst errorMessage = ref(\"\");\r\nconst imageSrc = ref<string>(\"\"); // 存储异步加载的图片URL\r\nconst imageLoading = ref(false); // 图片加载状态\r\n\r\n// 计算根元素样式\r\nconst rootStyle = computed(() => ({\r\n fontSize: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n color: props.color,\r\n cursor: props.clickable ? \"pointer\" : \"default\",\r\n}));\r\n\r\n// 计算图标样式\r\nconst iconStyle = computed(() => {\r\n const style: Record<string, any> = {\r\n display: \"inline-flex\",\r\n justifyContent: \"center\",\r\n alignItems: \"center\",\r\n width: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n height: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n };\r\n\r\n // 旋转\r\n if (props.rotate) {\r\n style.transform = `rotate(${props.rotate}deg)`;\r\n }\r\n\r\n // 翻转\r\n if (props.flip) {\r\n const scaleX =\r\n props.flip === \"horizontal\" || props.flip === \"both\" ? -1 : 1;\r\n const scaleY = props.flip === \"vertical\" || props.flip === \"both\" ? -1 : 1;\r\n style.transform = `${\r\n style.transform || \"\"\r\n } scaleX(${scaleX}) scaleY(${scaleY})`.trim();\r\n }\r\n\r\n return style;\r\n});\r\n\r\n// 获取图标名称用于显示\r\nconst iconDisplayName = computed(() => {\r\n if (typeof props.name === \"string\") {\r\n return props.name;\r\n }\r\n if (props.name && props.name.__name) {\r\n return props.name.__name;\r\n }\r\n if (props.name && props.name.name) {\r\n return props.name.name;\r\n }\r\n return \"Component Icon\";\r\n});\r\n\r\n// 图标类名\r\nconst iconClasses = computed(() => [\r\n props.customClass,\r\n {\r\n \"c-icon__rotatable\": props.rotate !== 0,\r\n \"c-icon__flippable\": !!props.flip,\r\n },\r\n]);\r\n\r\n// 解析组件名称(纯计算属性,无副作用)\r\nconst resolvedComponent = computed(() => {\r\n if (props.type !== \"component\" || !props.name) return null;\r\n return props.name;\r\n});\r\n\r\n// 统一错误处理函数\r\nconst handleError = (type: string, message: string, originalError?: any) => {\r\n console.warn(`[C_Icon] ${message}:`, iconDisplayName.value, originalError);\r\n hasError.value = true;\r\n errorMessage.value = message;\r\n imageLoading.value = false; // 错误时停止加载状态\r\n emit(\"error\", type, originalError);\r\n\r\n // 如果有回退图标,可以在这里尝试使用回退方案\r\n if (props.fallbackIcon && !hasError.value) {\r\n // 这里可以实现回退逻辑\r\n // 比如尝试使用fallbackIcon作为iconify图标\r\n }\r\n};\r\n\r\n// 处理点击事件\r\nconst handleClick = (event: MouseEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n emit(\"click\", event);\r\n};\r\n\r\n// 处理键盘事件\r\nconst handleKeydown = (event: KeyboardEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n if (event.key === \"Enter\" || event.key === \" \") {\r\n event.preventDefault();\r\n emit(\"click\", event as any);\r\n }\r\n};\r\n\r\n// 处理图片加载成功\r\nconst handleImageLoad = () => {\r\n imageLoading.value = false;\r\n emit(\"load\");\r\n};\r\n\r\n// 异步加载图片 - 修复关键函数\r\nconst loadImageSrc = async () => {\r\n if (props.type !== \"image\" || typeof props.name !== \"string\") {\r\n imageSrc.value = \"\";\r\n return;\r\n }\r\n\r\n // 如果是完整URL,直接设置\r\n if (\r\n props.name.startsWith(\"http://\") ||\r\n props.name.startsWith(\"https://\") ||\r\n props.name.startsWith(\"//\") ||\r\n props.name.startsWith(\"/\")\r\n ) {\r\n imageSrc.value = props.name;\r\n return;\r\n }\r\n\r\n // 其他情况都当作本地图片处理\r\n try {\r\n imageLoading.value = true;\r\n const imageUrl = await useImage(props.name);\r\n if (imageUrl) {\r\n imageSrc.value = imageUrl;\r\n } else {\r\n handleError(\"image\", \"图片路径解析失败\");\r\n }\r\n } catch (error) {\r\n handleError(\"image\", \"图片加载失败\", error);\r\n } finally {\r\n imageLoading.value = false;\r\n }\r\n};\r\n\r\n// 验证规则映射\r\nconst validationRules = {\r\n iconify: (): string | null => {\r\n if (!props.name.includes(\":\")) {\r\n console.warn(\r\n '[C_Icon] Iconify图标名称格式应为 \"prefix:name\",如 \"mdi:home\"',\r\n );\r\n }\r\n return null;\r\n },\r\n unocss: (): string | null => {\r\n if (typeof props.name !== \"string\" || !props.name.startsWith(\"i-\")) {\r\n console.warn('[C_Icon] UnoCSS图标名称应以 \"i-\" 开头');\r\n }\r\n return null;\r\n },\r\n component: (): string | null =>\r\n !resolvedComponent.value ? \"无法解析组件\" : null,\r\n svg: (): string | null => (!props.svgPath ? \"SVG路径不能为空\" : null),\r\n image: (): string | null => {\r\n // 对于image类型,异步验证在loadImageSrc中处理\r\n return null;\r\n },\r\n};\r\n\r\n// 验证配置\r\nconst validateProps = () => {\r\n hasError.value = false;\r\n errorMessage.value = \"\";\r\n\r\n // 基础验证\r\n if (!props.name) {\r\n return handleError(\"validation\", \"图标名称不能为空\");\r\n }\r\n\r\n // 类型验证\r\n const validator = validationRules[props.type];\r\n if (!validator) {\r\n return handleError(\"type\", \"不支持的图标类型\");\r\n }\r\n\r\n const errorMsg = validator();\r\n if (errorMsg) {\r\n handleError(props.type, errorMsg);\r\n }\r\n\r\n // 如果是图片类型,异步加载图片\r\n if (props.type === \"image\") {\r\n loadImageSrc();\r\n }\r\n};\r\n\r\n// 监听关键属性变化\r\nwatch(\r\n () => [props.name, props.type, props.svgPath],\r\n () => {\r\n nextTick(validateProps);\r\n },\r\n { immediate: true },\r\n);\r\n\r\n// 组件挂载后验证\r\nonMounted(() => {\r\n validateProps();\r\n // 非image类型在这里触发load事件\r\n if (!hasError.value && props.type !== \"image\") {\r\n emit(\"load\");\r\n }\r\n});\r\n\r\n// 暴露方法给父组件\r\ndefineExpose({\r\n /** 重新验证配置 */\r\n validate: validateProps,\r\n /** 是否有错误 */\r\n hasError: readonly(hasError),\r\n /** 错误信息 */\r\n errorMessage: readonly(errorMessage),\r\n /** DOM引用 */\r\n el: iconRef,\r\n});\r\n</script>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-14\r\n * @Description: 支持多种图标使用方式,默认使用Iconify图标,统一错误处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"iconRef\"\r\n class=\"c-icon\"\r\n :class=\"[\r\n `c-icon--${type}`,\r\n {\r\n 'c-icon--clickable': clickable,\r\n 'c-icon--loading': loading,\r\n 'c-icon--error': hasError,\r\n },\r\n ]\"\r\n :style=\"rootStyle\"\r\n :title=\"title || iconDisplayName\"\r\n :aria-label=\"ariaLabel || iconDisplayName\"\r\n :role=\"clickable ? 'button' : 'img'\"\r\n :tabindex=\"clickable ? 0 : -1\"\r\n @click=\"handleClick\"\r\n @keydown=\"handleKeydown\"\r\n >\r\n <!-- 方式1:Iconify图标(默认方式) -->\r\n <Icon\r\n v-if=\"\r\n (type === 'iconify' || !type) && !hasError && typeof name === 'string'\r\n \"\r\n :icon=\"name\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('iconify', 'Iconify图标加载失败')\"\r\n />\r\n\r\n <!-- 方式2:UnoCSS图标 -->\r\n <div\r\n v-else-if=\"type === 'unocss' && !hasError && typeof name === 'string'\"\r\n :class=\"[name, iconClasses]\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('unocss', 'UnoCSS图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式3:组件挂载方式 -->\r\n <component\r\n v-else-if=\"type === 'component' && resolvedComponent && !hasError\"\r\n :is=\"resolvedComponent\"\r\n :style=\"iconStyle\"\r\n v-bind=\"componentProps\"\r\n @error=\"() => handleError('component', '组件图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式4:SVG路径方式 -->\r\n <svg\r\n v-else-if=\"type === 'svg' && svgPath && !hasError\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :viewBox=\"viewBox\"\r\n :style=\"iconStyle\"\r\n :fill=\"color\"\r\n @error=\"() => handleError('svg', 'SVG图标渲染失败')\"\r\n >\r\n <path :d=\"svgPath\" :fill=\"color\" />\r\n </svg>\r\n\r\n <!-- 方式5:图片方式 - 修复异步加载 -->\r\n <img\r\n v-else-if=\"type === 'image' && imageSrc && !hasError && !imageLoading\"\r\n :src=\"imageSrc\"\r\n :alt=\"alt || iconDisplayName\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n style=\"display: inline-block; vertical-align: middle\"\r\n @error=\"() => handleError('image', '图片图标加载失败')\"\r\n @load=\"handleImageLoad\"\r\n />\r\n\r\n <!-- 图片加载状态 -->\r\n <div\r\n v-if=\"(loading || imageLoading) && !hasError\"\r\n class=\"c-icon__loading\"\r\n :style=\"iconStyle\"\r\n >\r\n <div class=\"c-icon__spinner\" />\r\n </div>\r\n\r\n <!-- 统一错误状态显示 -->\r\n <div\r\n v-else-if=\"hasError\"\r\n class=\"c-icon__error\"\r\n :style=\"iconStyle\"\r\n :title=\"`图标解析失败: ${iconDisplayName} (${errorMessage})${\r\n fallbackIcon ? ` (fallback: ${fallbackIcon})` : ''\r\n }`\"\r\n >\r\n ⚠️\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { Icon } from \"@iconify/vue\";\r\nimport { useImage } from \"../../hooks/useImage\";\r\nimport { computed, nextTick, onMounted, readonly, ref, watch } from \"vue\";\r\n\r\nexport interface IconProps {\r\n /** 图标名称/路径/组件实例 */\r\n name: string | any;\r\n /** 图标类型 */\r\n type?: \"iconify\" | \"unocss\" | \"component\" | \"svg\" | \"image\";\r\n /** 图标颜色 */\r\n color?: string;\r\n /** 图标大小(px) */\r\n size?: number | string;\r\n /** SVG路径数据(仅type=svg时使用) */\r\n svgPath?: string;\r\n /** SVG viewBox(仅type=svg时使用) */\r\n viewBox?: string;\r\n /** 图片alt属性(仅type=image时使用) */\r\n alt?: string;\r\n /** 是否可点击 */\r\n clickable?: boolean;\r\n /** 加载状态 */\r\n loading?: boolean;\r\n /** 错误时的回退图标 */\r\n fallbackIcon?: string;\r\n /** 工具提示 */\r\n title?: string;\r\n /** 无障碍标签 */\r\n ariaLabel?: string;\r\n /** 自定义样式类 */\r\n customClass?: string;\r\n /** 旋转角度 */\r\n rotate?: number;\r\n /** 是否翻转 */\r\n flip?: \"horizontal\" | \"vertical\" | \"both\";\r\n /** 传递给组件的额外属性(仅type=component时使用) */\r\n componentProps?: Record<string, any>;\r\n}\r\n\r\nconst props = withDefaults(defineProps<IconProps>(), {\r\n name: undefined,\r\n type: \"iconify\",\r\n color: \"currentColor\",\r\n size: 18,\r\n svgPath: \"\",\r\n viewBox: \"0 0 24 24\",\r\n alt: \"\",\r\n clickable: false,\r\n loading: false,\r\n fallbackIcon: \"\",\r\n title: \"\",\r\n ariaLabel: \"\",\r\n customClass: \"\",\r\n rotate: 0,\r\n flip: undefined,\r\n componentProps: () => ({}),\r\n});\r\n\r\nconst emit = defineEmits<{\r\n click: [event: MouseEvent];\r\n error: [type: string, error?: any];\r\n load: [];\r\n}>();\r\n\r\nconst iconRef = ref<HTMLElement>();\r\nconst hasError = ref(false);\r\nconst errorMessage = ref(\"\");\r\nconst imageSrc = ref<string>(\"\"); // 存储异步加载的图片URL\r\nconst imageLoading = ref(false); // 图片加载状态\r\n\r\n// 计算根元素样式\r\nconst rootStyle = computed(() => ({\r\n fontSize: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n color: props.color,\r\n cursor: props.clickable ? \"pointer\" : \"default\",\r\n}));\r\n\r\n// 计算图标样式\r\nconst iconStyle = computed(() => {\r\n const style: Record<string, any> = {\r\n display: \"inline-flex\",\r\n justifyContent: \"center\",\r\n alignItems: \"center\",\r\n width: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n height: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n };\r\n\r\n // 旋转\r\n if (props.rotate) {\r\n style.transform = `rotate(${props.rotate}deg)`;\r\n }\r\n\r\n // 翻转\r\n if (props.flip) {\r\n const scaleX =\r\n props.flip === \"horizontal\" || props.flip === \"both\" ? -1 : 1;\r\n const scaleY = props.flip === \"vertical\" || props.flip === \"both\" ? -1 : 1;\r\n style.transform = `${\r\n style.transform || \"\"\r\n } scaleX(${scaleX}) scaleY(${scaleY})`.trim();\r\n }\r\n\r\n return style;\r\n});\r\n\r\n// 获取图标名称用于显示\r\nconst iconDisplayName = computed(() => {\r\n if (typeof props.name === \"string\") {\r\n return props.name;\r\n }\r\n if (props.name && props.name.__name) {\r\n return props.name.__name;\r\n }\r\n if (props.name && props.name.name) {\r\n return props.name.name;\r\n }\r\n return \"Component Icon\";\r\n});\r\n\r\n// 图标类名\r\nconst iconClasses = computed(() => [\r\n props.customClass,\r\n {\r\n \"c-icon__rotatable\": props.rotate !== 0,\r\n \"c-icon__flippable\": !!props.flip,\r\n },\r\n]);\r\n\r\n// 解析组件名称(纯计算属性,无副作用)\r\nconst resolvedComponent = computed(() => {\r\n if (props.type !== \"component\" || !props.name) return null;\r\n return props.name;\r\n});\r\n\r\n// 统一错误处理函数\r\nconst handleError = (type: string, message: string, originalError?: any) => {\r\n console.warn(`[C_Icon] ${message}:`, iconDisplayName.value, originalError);\r\n hasError.value = true;\r\n errorMessage.value = message;\r\n imageLoading.value = false; // 错误时停止加载状态\r\n emit(\"error\", type, originalError);\r\n\r\n // 如果有回退图标,可以在这里尝试使用回退方案\r\n if (props.fallbackIcon && !hasError.value) {\r\n // 这里可以实现回退逻辑\r\n // 比如尝试使用fallbackIcon作为iconify图标\r\n }\r\n};\r\n\r\n// 处理点击事件\r\nconst handleClick = (event: MouseEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n emit(\"click\", event);\r\n};\r\n\r\n// 处理键盘事件\r\nconst handleKeydown = (event: KeyboardEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n if (event.key === \"Enter\" || event.key === \" \") {\r\n event.preventDefault();\r\n emit(\"click\", event as any);\r\n }\r\n};\r\n\r\n// 处理图片加载成功\r\nconst handleImageLoad = () => {\r\n imageLoading.value = false;\r\n emit(\"load\");\r\n};\r\n\r\n// 异步加载图片 - 修复关键函数\r\nconst loadImageSrc = async () => {\r\n if (props.type !== \"image\" || typeof props.name !== \"string\") {\r\n imageSrc.value = \"\";\r\n return;\r\n }\r\n\r\n // 如果是完整URL,直接设置\r\n if (\r\n props.name.startsWith(\"http://\") ||\r\n props.name.startsWith(\"https://\") ||\r\n props.name.startsWith(\"//\") ||\r\n props.name.startsWith(\"/\")\r\n ) {\r\n imageSrc.value = props.name;\r\n return;\r\n }\r\n\r\n // 其他情况都当作本地图片处理\r\n try {\r\n imageLoading.value = true;\r\n const imageUrl = await useImage(props.name);\r\n if (imageUrl) {\r\n imageSrc.value = imageUrl;\r\n } else {\r\n handleError(\"image\", \"图片路径解析失败\");\r\n }\r\n } catch (error) {\r\n handleError(\"image\", \"图片加载失败\", error);\r\n } finally {\r\n imageLoading.value = false;\r\n }\r\n};\r\n\r\n// 验证规则映射\r\nconst validationRules = {\r\n iconify: (): string | null => {\r\n if (!props.name.includes(\":\")) {\r\n console.warn(\r\n '[C_Icon] Iconify图标名称格式应为 \"prefix:name\",如 \"mdi:home\"',\r\n );\r\n }\r\n return null;\r\n },\r\n unocss: (): string | null => {\r\n if (typeof props.name !== \"string\" || !props.name.startsWith(\"i-\")) {\r\n console.warn('[C_Icon] UnoCSS图标名称应以 \"i-\" 开头');\r\n }\r\n return null;\r\n },\r\n component: (): string | null =>\r\n !resolvedComponent.value ? \"无法解析组件\" : null,\r\n svg: (): string | null => (!props.svgPath ? \"SVG路径不能为空\" : null),\r\n image: (): string | null => {\r\n // 对于image类型,异步验证在loadImageSrc中处理\r\n return null;\r\n },\r\n};\r\n\r\n// 验证配置\r\nconst validateProps = () => {\r\n hasError.value = false;\r\n errorMessage.value = \"\";\r\n\r\n // 基础验证\r\n if (!props.name) {\r\n return handleError(\"validation\", \"图标名称不能为空\");\r\n }\r\n\r\n // 类型验证\r\n const validator = validationRules[props.type];\r\n if (!validator) {\r\n return handleError(\"type\", \"不支持的图标类型\");\r\n }\r\n\r\n const errorMsg = validator();\r\n if (errorMsg) {\r\n handleError(props.type, errorMsg);\r\n }\r\n\r\n // 如果是图片类型,异步加载图片\r\n if (props.type === \"image\") {\r\n loadImageSrc();\r\n }\r\n};\r\n\r\n// 监听关键属性变化\r\nwatch(\r\n () => [props.name, props.type, props.svgPath],\r\n () => {\r\n nextTick(validateProps);\r\n },\r\n { immediate: true },\r\n);\r\n\r\n// 组件挂载后验证\r\nonMounted(() => {\r\n validateProps();\r\n // 非image类型在这里触发load事件\r\n if (!hasError.value && props.type !== \"image\") {\r\n emit(\"load\");\r\n }\r\n});\r\n\r\n// 暴露方法给父组件\r\ndefineExpose({\r\n /** 重新验证配置 */\r\n validate: validateProps,\r\n /** 是否有错误 */\r\n hasError: readonly(hasError),\r\n /** 错误信息 */\r\n errorMessage: readonly(errorMessage),\r\n /** DOM引用 */\r\n el: iconRef,\r\n});\r\n</script>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-14\r\n * @Description: 支持多种图标使用方式,默认使用Iconify图标,统一错误处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"iconRef\"\r\n class=\"c-icon\"\r\n :class=\"[\r\n `c-icon--${type}`,\r\n {\r\n 'c-icon--clickable': clickable,\r\n 'c-icon--loading': loading,\r\n 'c-icon--error': hasError,\r\n },\r\n ]\"\r\n :style=\"rootStyle\"\r\n :title=\"title || iconDisplayName\"\r\n :aria-label=\"ariaLabel || iconDisplayName\"\r\n :role=\"clickable ? 'button' : 'img'\"\r\n :tabindex=\"clickable ? 0 : -1\"\r\n @click=\"handleClick\"\r\n @keydown=\"handleKeydown\"\r\n >\r\n <!-- 方式1:Iconify图标(默认方式) -->\r\n <Icon\r\n v-if=\"\r\n (type === 'iconify' || !type) && !hasError && typeof name === 'string'\r\n \"\r\n :icon=\"name\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('iconify', 'Iconify图标加载失败')\"\r\n />\r\n\r\n <!-- 方式2:UnoCSS图标 -->\r\n <div\r\n v-else-if=\"type === 'unocss' && !hasError && typeof name === 'string'\"\r\n :class=\"[name, iconClasses]\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('unocss', 'UnoCSS图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式3:组件挂载方式 -->\r\n <component\r\n v-else-if=\"type === 'component' && resolvedComponent && !hasError\"\r\n :is=\"resolvedComponent\"\r\n :style=\"iconStyle\"\r\n v-bind=\"componentProps\"\r\n @error=\"() => handleError('component', '组件图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式4:SVG路径方式 -->\r\n <svg\r\n v-else-if=\"type === 'svg' && svgPath && !hasError\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :viewBox=\"viewBox\"\r\n :style=\"iconStyle\"\r\n :fill=\"color\"\r\n @error=\"() => handleError('svg', 'SVG图标渲染失败')\"\r\n >\r\n <path :d=\"svgPath\" :fill=\"color\" />\r\n </svg>\r\n\r\n <!-- 方式5:图片方式 - 修复异步加载 -->\r\n <img\r\n v-else-if=\"type === 'image' && imageSrc && !hasError && !imageLoading\"\r\n :src=\"imageSrc\"\r\n :alt=\"alt || iconDisplayName\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n style=\"display: inline-block; vertical-align: middle\"\r\n @error=\"() => handleError('image', '图片图标加载失败')\"\r\n @load=\"handleImageLoad\"\r\n />\r\n\r\n <!-- 图片加载状态 -->\r\n <div\r\n v-if=\"(loading || imageLoading) && !hasError\"\r\n class=\"c-icon__loading\"\r\n :style=\"iconStyle\"\r\n >\r\n <div class=\"c-icon__spinner\" />\r\n </div>\r\n\r\n <!-- 统一错误状态显示 -->\r\n <div\r\n v-else-if=\"hasError\"\r\n class=\"c-icon__error\"\r\n :style=\"iconStyle\"\r\n :title=\"`图标解析失败: ${iconDisplayName} (${errorMessage})${\r\n fallbackIcon ? ` (fallback: ${fallbackIcon})` : ''\r\n }`\"\r\n >\r\n ⚠️\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { Icon } from \"@iconify/vue\";\r\nimport { useImage } from \"../../hooks/useImage\";\r\nimport { computed, nextTick, onMounted, readonly, ref, watch } from \"vue\";\r\n\r\nexport interface IconProps {\r\n /** 图标名称/路径/组件实例 */\r\n name: string | any;\r\n /** 图标类型 */\r\n type?: \"iconify\" | \"unocss\" | \"component\" | \"svg\" | \"image\";\r\n /** 图标颜色 */\r\n color?: string;\r\n /** 图标大小(px) */\r\n size?: number | string;\r\n /** SVG路径数据(仅type=svg时使用) */\r\n svgPath?: string;\r\n /** SVG viewBox(仅type=svg时使用) */\r\n viewBox?: string;\r\n /** 图片alt属性(仅type=image时使用) */\r\n alt?: string;\r\n /** 是否可点击 */\r\n clickable?: boolean;\r\n /** 加载状态 */\r\n loading?: boolean;\r\n /** 错误时的回退图标 */\r\n fallbackIcon?: string;\r\n /** 工具提示 */\r\n title?: string;\r\n /** 无障碍标签 */\r\n ariaLabel?: string;\r\n /** 自定义样式类 */\r\n customClass?: string;\r\n /** 旋转角度 */\r\n rotate?: number;\r\n /** 是否翻转 */\r\n flip?: \"horizontal\" | \"vertical\" | \"both\";\r\n /** 传递给组件的额外属性(仅type=component时使用) */\r\n componentProps?: Record<string, any>;\r\n}\r\n\r\nconst props = withDefaults(defineProps<IconProps>(), {\r\n name: undefined,\r\n type: \"iconify\",\r\n color: \"currentColor\",\r\n size: 18,\r\n svgPath: \"\",\r\n viewBox: \"0 0 24 24\",\r\n alt: \"\",\r\n clickable: false,\r\n loading: false,\r\n fallbackIcon: \"\",\r\n title: \"\",\r\n ariaLabel: \"\",\r\n customClass: \"\",\r\n rotate: 0,\r\n flip: undefined,\r\n componentProps: () => ({}),\r\n});\r\n\r\nconst emit = defineEmits<{\r\n click: [event: MouseEvent];\r\n error: [type: string, error?: any];\r\n load: [];\r\n}>();\r\n\r\nconst iconRef = ref<HTMLElement>();\r\nconst hasError = ref(false);\r\nconst errorMessage = ref(\"\");\r\nconst imageSrc = ref<string>(\"\"); // 存储异步加载的图片URL\r\nconst imageLoading = ref(false); // 图片加载状态\r\n\r\n// 计算根元素样式\r\nconst rootStyle = computed(() => ({\r\n fontSize: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n color: props.color,\r\n cursor: props.clickable ? \"pointer\" : \"default\",\r\n}));\r\n\r\n// 计算图标样式\r\nconst iconStyle = computed(() => {\r\n const style: Record<string, any> = {\r\n display: \"inline-flex\",\r\n justifyContent: \"center\",\r\n alignItems: \"center\",\r\n width: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n height: typeof props.size === \"number\" ? `${props.size}px` : props.size,\r\n };\r\n\r\n // 旋转\r\n if (props.rotate) {\r\n style.transform = `rotate(${props.rotate}deg)`;\r\n }\r\n\r\n // 翻转\r\n if (props.flip) {\r\n const scaleX =\r\n props.flip === \"horizontal\" || props.flip === \"both\" ? -1 : 1;\r\n const scaleY = props.flip === \"vertical\" || props.flip === \"both\" ? -1 : 1;\r\n style.transform = `${\r\n style.transform || \"\"\r\n } scaleX(${scaleX}) scaleY(${scaleY})`.trim();\r\n }\r\n\r\n return style;\r\n});\r\n\r\n// 获取图标名称用于显示\r\nconst iconDisplayName = computed(() => {\r\n if (typeof props.name === \"string\") {\r\n return props.name;\r\n }\r\n if (props.name && props.name.__name) {\r\n return props.name.__name;\r\n }\r\n if (props.name && props.name.name) {\r\n return props.name.name;\r\n }\r\n return \"Component Icon\";\r\n});\r\n\r\n// 图标类名\r\nconst iconClasses = computed(() => [\r\n props.customClass,\r\n {\r\n \"c-icon__rotatable\": props.rotate !== 0,\r\n \"c-icon__flippable\": !!props.flip,\r\n },\r\n]);\r\n\r\n// 解析组件名称(纯计算属性,无副作用)\r\nconst resolvedComponent = computed(() => {\r\n if (props.type !== \"component\" || !props.name) return null;\r\n return props.name;\r\n});\r\n\r\n// 统一错误处理函数\r\nconst handleError = (type: string, message: string, originalError?: any) => {\r\n console.warn(`[C_Icon] ${message}:`, iconDisplayName.value, originalError);\r\n hasError.value = true;\r\n errorMessage.value = message;\r\n imageLoading.value = false; // 错误时停止加载状态\r\n emit(\"error\", type, originalError);\r\n\r\n // 如果有回退图标,可以在这里尝试使用回退方案\r\n if (props.fallbackIcon && !hasError.value) {\r\n // 这里可以实现回退逻辑\r\n // 比如尝试使用fallbackIcon作为iconify图标\r\n }\r\n};\r\n\r\n// 处理点击事件\r\nconst handleClick = (event: MouseEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n emit(\"click\", event);\r\n};\r\n\r\n// 处理键盘事件\r\nconst handleKeydown = (event: KeyboardEvent) => {\r\n if (!props.clickable || props.loading) return;\r\n if (event.key === \"Enter\" || event.key === \" \") {\r\n event.preventDefault();\r\n emit(\"click\", event as any);\r\n }\r\n};\r\n\r\n// 处理图片加载成功\r\nconst handleImageLoad = () => {\r\n imageLoading.value = false;\r\n emit(\"load\");\r\n};\r\n\r\n// 异步加载图片 - 修复关键函数\r\nconst loadImageSrc = async () => {\r\n if (props.type !== \"image\" || typeof props.name !== \"string\") {\r\n imageSrc.value = \"\";\r\n return;\r\n }\r\n\r\n // 如果是完整URL,直接设置\r\n if (\r\n props.name.startsWith(\"http://\") ||\r\n props.name.startsWith(\"https://\") ||\r\n props.name.startsWith(\"//\") ||\r\n props.name.startsWith(\"/\")\r\n ) {\r\n imageSrc.value = props.name;\r\n return;\r\n }\r\n\r\n // 其他情况都当作本地图片处理\r\n try {\r\n imageLoading.value = true;\r\n const imageUrl = await useImage(props.name);\r\n if (imageUrl) {\r\n imageSrc.value = imageUrl;\r\n } else {\r\n handleError(\"image\", \"图片路径解析失败\");\r\n }\r\n } catch (error) {\r\n handleError(\"image\", \"图片加载失败\", error);\r\n } finally {\r\n imageLoading.value = false;\r\n }\r\n};\r\n\r\n// 验证规则映射\r\nconst validationRules = {\r\n iconify: (): string | null => {\r\n if (!props.name.includes(\":\")) {\r\n console.warn(\r\n '[C_Icon] Iconify图标名称格式应为 \"prefix:name\",如 \"mdi:home\"',\r\n );\r\n }\r\n return null;\r\n },\r\n unocss: (): string | null => {\r\n if (typeof props.name !== \"string\" || !props.name.startsWith(\"i-\")) {\r\n console.warn('[C_Icon] UnoCSS图标名称应以 \"i-\" 开头');\r\n }\r\n return null;\r\n },\r\n component: (): string | null =>\r\n !resolvedComponent.value ? \"无法解析组件\" : null,\r\n svg: (): string | null => (!props.svgPath ? \"SVG路径不能为空\" : null),\r\n image: (): string | null => {\r\n // 对于image类型,异步验证在loadImageSrc中处理\r\n return null;\r\n },\r\n};\r\n\r\n// 验证配置\r\nconst validateProps = () => {\r\n hasError.value = false;\r\n errorMessage.value = \"\";\r\n\r\n // 基础验证\r\n if (!props.name) {\r\n return handleError(\"validation\", \"图标名称不能为空\");\r\n }\r\n\r\n // 类型验证\r\n const validator = validationRules[props.type];\r\n if (!validator) {\r\n return handleError(\"type\", \"不支持的图标类型\");\r\n }\r\n\r\n const errorMsg = validator();\r\n if (errorMsg) {\r\n handleError(props.type, errorMsg);\r\n }\r\n\r\n // 如果是图片类型,异步加载图片\r\n if (props.type === \"image\") {\r\n loadImageSrc();\r\n }\r\n};\r\n\r\n// 监听关键属性变化\r\nwatch(\r\n () => [props.name, props.type, props.svgPath],\r\n () => {\r\n nextTick(validateProps);\r\n },\r\n { immediate: true },\r\n);\r\n\r\n// 组件挂载后验证\r\nonMounted(() => {\r\n validateProps();\r\n // 非image类型在这里触发load事件\r\n if (!hasError.value && props.type !== \"image\") {\r\n emit(\"load\");\r\n }\r\n});\r\n\r\n// 暴露方法给父组件\r\ndefineExpose({\r\n /** 重新验证配置 */\r\n validate: validateProps,\r\n /** 是否有错误 */\r\n hasError: readonly(hasError),\r\n /** 错误信息 */\r\n errorMessage: readonly(errorMessage),\r\n /** DOM引用 */\r\n el: iconRef,\r\n});\r\n</script>\r\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"C_ImageCropper-DrmUlaLi.css","names":[],"sources":["../src/components/C_ImageCropper/components/CropperToolbar.vue?vue&type=style&index=0&scoped=ac310c55&lang.scss","../src/components/C_ImageCropper/components/CropperPreview.vue?vue&type=style&index=0&scoped=11f355b4&lang.scss","../src/components/C_ImageCropper/index.vue?vue&type=style&index=0&scoped=b25a5839&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.cropper-toolbar[data-v-ac310c55] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n align-items: center;\n padding: 8px 0;\n}","/* unplugin-vue-components disabled */.cropper-preview[data-v-11f355b4] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n.cropper-preview .preview-label[data-v-11f355b4] {\n display: flex;\n gap: 6px;\n align-items: center;\n font-size: 13px;\n font-weight: 500;\n color: var(--text-color-2);\n}\n.cropper-preview .preview-main[data-v-11f355b4] {\n width: 100%;\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 6px;\n background: linear-gradient(45deg, #f0f0f0 25%, transparent 25%), linear-gradient(-45deg, #f0f0f0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #f0f0f0 75%), linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n background-size: 16px 16px;\n}\n.cropper-preview .preview-main--circular[data-v-11f355b4] {\n border-radius: 50%;\n}\n.cropper-preview .preview-main__viewport img[data-v-11f355b4] {\n display: block;\n max-width: none !important;\n}\n.cropper-preview .preview-empty[data-v-11f355b4] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n min-height: 120px;\n}\n.cropper-preview .preview-thumbs[data-v-11f355b4] {\n display: flex;\n gap: 12px;\n align-items: flex-end;\n justify-content: center;\n}\n.cropper-preview .preview-thumb[data-v-11f355b4] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n align-items: center;\n}\n.cropper-preview .preview-thumb__box[data-v-11f355b4] {\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 4px;\n background: var(--body-color);\n}\n.cropper-preview .preview-thumb__box--circular[data-v-11f355b4] {\n border-radius: 50%;\n}\n.cropper-preview .preview-thumb__viewport img[data-v-11f355b4] {\n display: block;\n max-width: none !important;\n}\n.cropper-preview .preview-thumb__label[data-v-11f355b4] {\n font-size: 11px;\n color: var(--text-color-3);\n}","/* unplugin-vue-components disabled *//* C_ImageCropper styles */\n.c-image-cropper[data-v-b25a5839] {\n width: 100%;\n}\n.c-image-cropper__body[data-v-b25a5839] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.c-image-cropper__workspace[data-v-b25a5839] {\n display: flex;\n gap: 16px;\n align-items: stretch;\n}\n.c-image-cropper__canvas[data-v-b25a5839] {\n position: relative;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 6px;\n background: var(--body-color);\n}\n.c-image-cropper__canvas[data-v-b25a5839] .vue-cropper {\n width: 100% !important;\n height: 100% !important;\n}\n.c-image-cropper__preview-panel[data-v-b25a5839] {\n flex: 0 1 280px;\n min-width: 200px;\n padding: 16px;\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 6px;\n background: var(--card-color);\n}\n.c-image-cropper__placeholder[data-v-b25a5839] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--text-color-3);\n font-size: 14px;\n}\n@media (width <= 768px) {\n.c-image-cropper__workspace[data-v-b25a5839] {\n flex-direction: column;\n}\n.c-image-cropper__preview-panel[data-v-b25a5839] {\n max-width: none;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACjEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1,7 +1,7 @@
1
1
  import { t as C_Icon_default } from "./C_Icon2.js";
2
2
  import { t as export_helper_default } from "./export-helper.js";
3
- import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, toDisplayString, unref, watch, withCtx } from "vue";
4
3
  import { NButton, NButtonGroup, NDivider, NModal, NSpace, NTooltip } from "naive-ui";
4
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, toDisplayString, unref, watch, withCtx } from "vue";
5
5
  import { VueCropper } from "vue-cropper";
6
6
 
7
7
  //#region src/components/C_ImageCropper/composables/useCropperCore.ts
@@ -247,7 +247,7 @@ var CropperToolbar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */
247
247
 
248
248
  //#endregion
249
249
  //#region src/components/C_ImageCropper/components/CropperToolbar.vue
250
- var CropperToolbar_default = /* @__PURE__ */ export_helper_default(CropperToolbar_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-499ffd01"]]);
250
+ var CropperToolbar_default = /* @__PURE__ */ export_helper_default(CropperToolbar_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-ac310c55"]]);
251
251
 
252
252
  //#endregion
253
253
  //#region src/components/C_ImageCropper/components/CropperPreview.vue?vue&type=script&setup=true&lang.ts
@@ -369,7 +369,7 @@ var CropperPreview_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */
369
369
 
370
370
  //#endregion
371
371
  //#region src/components/C_ImageCropper/components/CropperPreview.vue
372
- var CropperPreview_default = /* @__PURE__ */ export_helper_default(CropperPreview_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-cefefdc0"]]);
372
+ var CropperPreview_default = /* @__PURE__ */ export_helper_default(CropperPreview_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-11f355b4"]]);
373
373
 
374
374
  //#endregion
375
375
  //#region src/components/C_ImageCropper/index.vue?vue&type=script&setup=true&lang.ts
@@ -716,7 +716,7 @@ var index_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCo
716
716
 
717
717
  //#endregion
718
718
  //#region src/components/C_ImageCropper/index.vue
719
- var C_ImageCropper_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-692ad231"]]);
719
+ var C_ImageCropper_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-b25a5839"]]);
720
720
 
721
721
  //#endregion
722
722
  export { useCropperCore as n, C_ImageCropper_default as t };