@robot-admin/naive-ui-components 0.6.14 → 0.6.15
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.
- package/CHANGELOG.md +48 -48
- package/README.md +277 -277
- package/dist/C_ActionBar-DYG0Yf3A.css.map +1 -0
- package/dist/C_ActionBar-MAjRhqcI.css.map +1 -0
- package/dist/C_ActionBar.css +47 -47
- package/dist/C_ActionBar.js.map +1 -1
- package/dist/C_ActionBar2.js +1 -1
- package/dist/C_ActionBar2.js.map +1 -1
- package/dist/{C_AntV-A3NXx_sF.css.map → C_AntV-BY8jqqmi.css.map} +1 -1
- package/dist/{C_AntV-D2Zp2qQF.css.map → C_AntV-BbkHc-_a.css.map} +1 -1
- package/dist/C_AntV.css +78 -78
- package/dist/C_AntV.js.map +1 -1
- package/dist/C_AntV2.js +2 -2
- package/dist/C_AntV2.js.map +1 -1
- package/dist/{C_AudioPlayer-CXl4yP_m.css.map → C_AudioPlayer-DKRIGhi7.css.map} +1 -1
- package/dist/{C_AudioPlayer-CcBtt71m.css.map → C_AudioPlayer-DlB6eNsa.css.map} +1 -1
- package/dist/C_AudioPlayer.css +47 -47
- package/dist/C_AudioPlayer.js.map +1 -1
- package/dist/C_AudioPlayer2.js +1 -1
- package/dist/C_AudioPlayer2.js.map +1 -1
- package/dist/{C_AvatarGroup-CtPhfH9h.css.map → C_AvatarGroup-D02zci-i.css.map} +1 -1
- package/dist/{C_AvatarGroup-M1dNPH3E.css.map → C_AvatarGroup-DatHaUlY.css.map} +1 -1
- package/dist/C_AvatarGroup.css +21 -21
- package/dist/C_AvatarGroup.js.map +1 -1
- package/dist/C_AvatarGroup2.js +1 -1
- package/dist/C_AvatarGroup2.js.map +1 -1
- package/dist/{C_Barcode-DjTmDkbQ.css.map → C_Barcode-DYDRDe2O.css.map} +1 -1
- package/dist/{C_Barcode-Do1Ozsmh.css.map → C_Barcode-muJdc_O4.css.map} +1 -1
- package/dist/C_Barcode.css +7 -7
- package/dist/C_Barcode.js.map +1 -1
- package/dist/C_Barcode2.js +1 -1
- package/dist/C_Barcode2.js.map +1 -1
- package/dist/C_Breadcrumb.js.map +1 -1
- package/dist/C_Breadcrumb2.js.map +1 -1
- package/dist/{C_Captcha-CZmAETly.css.map → C_Captcha-9_hk6VmY.css.map} +1 -1
- package/dist/{C_Captcha-Ccq3DMrR.css.map → C_Captcha-MtASlJ0f.css.map} +1 -1
- package/dist/C_Captcha.css +19 -19
- package/dist/C_Captcha.js.map +1 -1
- package/dist/C_Captcha2.js +1 -1
- package/dist/C_Captcha2.js.map +1 -1
- package/dist/{C_Chat-CcdWgVE8.css.map → C_Chat-CcjZdAwu.css.map} +1 -1
- package/dist/{C_Chat-Cx0woEEe.css.map → C_Chat-Diq7tyWU.css.map} +1 -1
- package/dist/C_Chat.css +65 -65
- package/dist/C_Chat.js.map +1 -1
- package/dist/C_Chat2.js +1 -1
- package/dist/C_Chat2.js.map +1 -1
- package/dist/{C_City-Cv5BESaN.css.map → C_City-Bam08Qv3.css.map} +1 -1
- package/dist/{C_City-DNKv4MpH.css.map → C_City-Bb3W-KaN.css.map} +1 -1
- package/dist/C_City.css +29 -29
- package/dist/C_City.js.map +1 -1
- package/dist/C_City2.js +1 -1
- package/dist/C_City2.js.map +1 -1
- package/dist/C_Code-BFi5xxnM.css.map +1 -0
- package/dist/C_Code-CZ7yfnri.css.map +1 -0
- package/dist/C_Code.css +3 -3
- package/dist/C_Code.js.map +1 -1
- package/dist/C_Code2.js +1 -1
- package/dist/C_Code2.js.map +1 -1
- package/dist/{C_CollapsePanel-Fap-lv_5.css.map → C_CollapsePanel-BCmTFuDz.css.map} +1 -1
- package/dist/{C_CollapsePanel-BpofCDyl.css.map → C_CollapsePanel-Bxnh4Zy-.css.map} +1 -1
- package/dist/C_CollapsePanel.css +35 -35
- package/dist/C_CollapsePanel.js.map +1 -1
- package/dist/C_CollapsePanel2.js +1 -1
- package/dist/C_CollapsePanel2.js.map +1 -1
- package/dist/{C_ContextMenu-CCsCeI39.css.map → C_ContextMenu-B3Cgz_wJ.css.map} +1 -1
- package/dist/{C_ContextMenu-DOTBeltf.css.map → C_ContextMenu-u1LixF3_.css.map} +1 -1
- package/dist/C_ContextMenu.css +17 -17
- package/dist/C_ContextMenu.js.map +1 -1
- package/dist/C_ContextMenu2.js +1 -1
- package/dist/C_ContextMenu2.js.map +1 -1
- package/dist/{C_Cron-C0-8b5af.css.map → C_Cron-Civir5-I.css.map} +1 -1
- package/dist/{C_Cron-DqHQ92LN.css.map → C_Cron-iw3qpMsy.css.map} +1 -1
- package/dist/C_Cron.css +37 -37
- package/dist/C_Cron.js.map +1 -1
- package/dist/C_Cron2.js +1 -1
- package/dist/C_Cron2.js.map +1 -1
- package/dist/{C_Draggable-Bq6o0qXn.css.map → C_Draggable-BCjIvIwT.css.map} +1 -1
- package/dist/{C_Draggable-r6nhx3yM.css.map → C_Draggable-DwkSlCOS.css.map} +1 -1
- package/dist/C_Draggable.css +46 -46
- package/dist/C_Draggable.js.map +1 -1
- package/dist/C_Draggable2.js +1 -1
- package/dist/C_Draggable2.js.map +1 -1
- package/dist/{C_FilePreview-BVarVvnD.css.map → C_FilePreview-BhcbiXsV.css.map} +1 -1
- package/dist/{C_FilePreview-DHGlV4OR.css.map → C_FilePreview-DJudcIbW.css.map} +1 -1
- package/dist/C_FilePreview.css +91 -91
- package/dist/C_FilePreview.js.map +1 -1
- package/dist/C_FilePreview2.js +1 -1
- package/dist/C_FilePreview2.js.map +1 -1
- package/dist/{C_Form-BciQUPpS.css.map → C_Form-BtcAPIRi.css.map} +1 -1
- package/dist/{C_Form-CKHvXL3O.css.map → C_Form-D_gwQ7hC.css.map} +1 -1
- package/dist/C_Form.css +222 -222
- package/dist/C_Form.js.map +1 -1
- package/dist/C_Form2.js +1 -1
- package/dist/C_Form2.js.map +1 -1
- package/dist/{C_FormSearch-Bmd2CmG6.css.map → C_FormSearch-Bi4XYjjk.css.map} +1 -1
- package/dist/{C_FormSearch-DBNtnjaL.css.map → C_FormSearch-DUGOoX0u.css.map} +1 -1
- package/dist/C_FormSearch.css +45 -45
- package/dist/C_FormSearch.js.map +1 -1
- package/dist/C_FormSearch2.js +1 -1
- package/dist/C_FormSearch2.js.map +1 -1
- package/dist/{C_FormulaEditor-3WG2vtQl.css.map → C_FormulaEditor-CJk3SSUJ.css.map} +1 -1
- package/dist/{C_FormulaEditor-Cm0CokN5.css.map → C_FormulaEditor-Cy7mjfVO.css.map} +1 -1
- package/dist/C_FormulaEditor.css +78 -78
- package/dist/C_FormulaEditor.js.map +1 -1
- package/dist/C_FormulaEditor2.js +1 -1
- package/dist/C_FormulaEditor2.js.map +1 -1
- package/dist/C_FullCalendar-BQA0OjGB.css.map +1 -0
- package/dist/C_FullCalendar-ChJUosAi.css.map +1 -0
- package/dist/C_FullCalendar.css +3 -3
- package/dist/C_FullCalendar.js.map +1 -1
- package/dist/C_FullCalendar2.js +1 -1
- package/dist/C_FullCalendar2.js.map +1 -1
- package/dist/C_Icon.js.map +1 -1
- package/dist/C_Icon2.js.map +1 -1
- package/dist/{C_ImageCropper-D3L2-J_J.css.map → C_ImageCropper-BqZpkZfU.css.map} +1 -1
- package/dist/{C_ImageCropper-DrmUlaLi.css.map → C_ImageCropper-CylwGuzS.css.map} +1 -1
- package/dist/C_ImageCropper.css +10 -10
- package/dist/C_ImageCropper.js.map +1 -1
- package/dist/C_ImageCropper2.js +1 -1
- package/dist/C_ImageCropper2.js.map +1 -1
- package/dist/C_Language.js.map +1 -1
- package/dist/C_Language2.js.map +1 -1
- package/dist/{C_Login-Cst_W1XC.css.map → C_Login-BkdnkBKX.css.map} +1 -1
- package/dist/{C_Login-DWZ_6K1x.css.map → C_Login-VkMM6OJ2.css.map} +1 -1
- package/dist/C_Login.css +72 -72
- package/dist/C_Login.js.map +1 -1
- package/dist/C_Login2.js +1 -1
- package/dist/C_Login2.js.map +1 -1
- package/dist/{C_Map-BCJmDnJT.css.map → C_Map-DNnBcY70.css.map} +1 -1
- package/dist/{C_Map-WUMXSAfy.css.map → C_Map-LEXIx6zM.css.map} +1 -1
- package/dist/C_Map.css +4 -4
- package/dist/C_Map.js.map +1 -1
- package/dist/C_Map2.js +1 -1
- package/dist/C_Map2.js.map +1 -1
- package/dist/{C_Markdown-7iBnLDtL.css.map → C_Markdown-Kujzx1qG.css.map} +1 -1
- package/dist/{C_Markdown-BbTvdFCl.css.map → C_Markdown-YCAmBbNF.css.map} +1 -1
- package/dist/C_Markdown.css +33 -33
- package/dist/C_Markdown.js.map +1 -1
- package/dist/C_Markdown2.js +1 -1
- package/dist/C_Markdown2.js.map +1 -1
- package/dist/{C_QRCode-DMxSyogU.css.map → C_QRCode-CFNTda_1.css.map} +1 -1
- package/dist/{C_QRCode-G7fiAkm4.css.map → C_QRCode-CmWr9lK-.css.map} +1 -1
- package/dist/C_QRCode.css +9 -9
- package/dist/C_QRCode.js.map +1 -1
- package/dist/C_QRCode2.js +1 -1
- package/dist/C_QRCode2.js.map +1 -1
- package/dist/{C_Signature-B5fFz6RD.css.map → C_Signature-PN4GOLRX.css.map} +1 -1
- package/dist/{C_Signature-es-ZNPzr.css.map → C_Signature-fWQqGIZ-.css.map} +1 -1
- package/dist/C_Signature.css +14 -14
- package/dist/C_Signature.js.map +1 -1
- package/dist/C_Signature2.js +1 -1
- package/dist/C_Signature2.js.map +1 -1
- package/dist/{C_SplitPane-Br2eK8IG.css.map → C_SplitPane-BOwr1C7K.css.map} +1 -1
- package/dist/{C_SplitPane-DuyzUhKB.css.map → C_SplitPane-N-a_D-7F.css.map} +1 -1
- package/dist/C_SplitPane.css +31 -31
- package/dist/C_SplitPane.js.map +1 -1
- package/dist/C_SplitPane2.js +1 -1
- package/dist/C_SplitPane2.js.map +1 -1
- package/dist/C_Steps-Bk1krgiR.css.map +1 -0
- package/dist/C_Steps-Dn2CMpy1.css.map +1 -0
- package/dist/C_Steps.css +38 -38
- package/dist/C_Steps.js.map +1 -1
- package/dist/C_Steps2.js +1 -1
- package/dist/C_Steps2.js.map +1 -1
- package/dist/{C_Table-vZlcgH6x.css.map → C_Table-BV1wUw6f.css.map} +1 -1
- package/dist/{C_Table-wzAO43l0.css.map → C_Table-LlJ-JEdm.css.map} +1 -1
- package/dist/C_Table.css +93 -93
- package/dist/C_Table.d.cts +2 -2
- package/dist/C_Table.d.ts +2 -2
- package/dist/C_Table.js.map +1 -1
- package/dist/C_Table2.js +1 -1
- package/dist/C_Table2.js.map +1 -1
- package/dist/{C_TagsView-BsutyC3U.css.map → C_TagsView-DX3LecK1.css.map} +1 -1
- package/dist/{C_TagsView-Crm7yWg9.css.map → C_TagsView-DqOTa8bL.css.map} +1 -1
- package/dist/C_TagsView.css +36 -36
- package/dist/C_TagsView.js.map +1 -1
- package/dist/C_TagsView2.js +1 -1
- package/dist/C_TagsView2.js.map +1 -1
- package/dist/C_Time-BzyQVvTS.css.map +1 -0
- package/dist/C_Time-DmpKLQHc.css.map +1 -0
- package/dist/C_Time.css +5 -5
- package/dist/C_Time.js.map +1 -1
- package/dist/C_Time2.js +1 -1
- package/dist/C_Time2.js.map +1 -1
- package/dist/{C_Timeline-CGTFTMX0.css.map → C_Timeline-BIvw3f41.css.map} +1 -1
- package/dist/{C_Timeline-DxKyvKer.css.map → C_Timeline-VsDfCv4Q.css.map} +1 -1
- package/dist/C_Timeline.css +42 -42
- package/dist/C_Timeline.js.map +1 -1
- package/dist/C_Timeline2.js +1 -1
- package/dist/C_Timeline2.js.map +1 -1
- package/dist/{C_Transfer-CCEqnC-v.css.map → C_Transfer-BOB6OBqS.css.map} +1 -1
- package/dist/{C_Transfer-CStPm4NP.css.map → C_Transfer-Dpq7ezWC.css.map} +1 -1
- package/dist/C_Transfer.css +31 -31
- package/dist/C_Transfer.js.map +1 -1
- package/dist/C_Transfer2.js +1 -1
- package/dist/C_Transfer2.js.map +1 -1
- package/dist/{C_Tree-B-Le4hjQ.css.map → C_Tree-D98nX9qT.css.map} +1 -1
- package/dist/{C_Tree-DnGc_MPb.css.map → C_Tree-ulfm7b-P.css.map} +1 -1
- package/dist/C_Tree.css +33 -33
- package/dist/C_Tree.js.map +1 -1
- package/dist/C_Tree2.js +1 -1
- package/dist/C_Tree2.js.map +1 -1
- package/dist/{C_VideoPlayer-Cdgv4Zq0.css.map → C_VideoPlayer-4J96tKfK.css.map} +1 -1
- package/dist/{C_VideoPlayer-rm0MODUv.css.map → C_VideoPlayer-D35seHkm.css.map} +1 -1
- package/dist/C_VideoPlayer.css +13 -13
- package/dist/C_VideoPlayer.js.map +1 -1
- package/dist/C_VideoPlayer2.js +1 -1
- package/dist/C_VideoPlayer2.js.map +1 -1
- package/dist/{C_VtableGantt-BpY-Rng3.css.map → C_VtableGantt-C2lxFmWg.css.map} +1 -1
- package/dist/{C_VtableGantt-DB87-jdS.css.map → C_VtableGantt-Dwnkw_WL.css.map} +1 -1
- package/dist/C_VtableGantt.css +15 -15
- package/dist/C_VtableGantt.js.map +1 -1
- package/dist/C_VtableGantt2.js +1 -1
- package/dist/C_VtableGantt2.js.map +1 -1
- package/dist/{C_WorkFlow-7F6op6qN.css.map → C_WorkFlow-2hJfvvTK.css.map} +1 -1
- package/dist/{C_WorkFlow-Cy3HWA6A.css.map → C_WorkFlow-DHoNjl8-.css.map} +1 -1
- package/dist/C_WorkFlow.css +108 -108
- package/dist/C_WorkFlow.js.map +1 -1
- package/dist/C_WorkFlow2.js +1 -1
- package/dist/C_WorkFlow2.js.map +1 -1
- package/dist/constants.d.ts +4 -4
- package/dist/constants2.d.ts +4 -4
- package/dist/constants3.d.ts +3 -3
- package/dist/constants4.d.ts +4 -4
- package/dist/constants5.d.ts +2 -2
- package/dist/data.d.ts +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.vue.d.ts +8 -8
- package/dist/index10.vue.d.ts +3 -3
- package/dist/index11.vue.d.ts +3 -3
- package/dist/index12.vue.d.ts +4 -4
- package/dist/index13.vue.d.ts +2 -2
- package/dist/index15.vue.d.ts +7 -7
- package/dist/index16.vue.d.ts +2 -2
- package/dist/index17.vue.d.ts +1 -1
- package/dist/index18.vue.d.ts +7 -7
- package/dist/index2.vue.d.ts +5 -5
- package/dist/index20.vue.d.ts +2 -2
- package/dist/index21.vue.d.ts +2 -2
- package/dist/index22.vue.d.ts +1 -1
- package/dist/index24.vue.d.ts +4 -4
- package/dist/index25.vue.d.ts +2 -2
- package/dist/index26.vue.d.ts +1 -1
- package/dist/index3.vue.d.ts +3 -3
- package/dist/index4.vue.d.ts +7 -7
- package/dist/index7.vue.d.ts +2 -2
- package/dist/index8.vue.d.ts +3 -3
- package/dist/index9.vue.d.ts +2 -2
- package/dist/menuAdapter.js.map +1 -1
- package/dist/resolver.cjs.map +1 -1
- package/dist/resolver.js.map +1 -1
- package/dist/style.css +1512 -1512
- package/dist/useCalendarEvents.d.ts +1 -1
- package/dist/useCollapsePanel.d.ts +4 -4
- package/dist/useCropperCore.d.ts +3 -3
- package/dist/useDraggableLayout.d.ts +10 -10
- package/dist/useDynamicFormState.d.ts +8 -8
- package/dist/useEdgeInteraction.d.ts +1 -1
- package/dist/useInfiniteScroll.d.ts +2 -2
- package/dist/useModalEdit.d.ts +3 -3
- package/dist/useQRCode.d.ts +3 -3
- package/dist/useSearchState.d.ts +2 -2
- package/dist/useSignatureHistory.d.ts +1 -1
- package/dist/useSplitResize.d.ts +6 -6
- package/dist/useTimeSelection.d.ts +3 -3
- package/dist/useTreeOperations.d.ts +5 -5
- package/dist/useWorkflowValidation.d.ts +4 -4
- package/package.json +1 -1
- package/dist/C_ActionBar-jZPsiXtT.css.map +0 -1
- package/dist/C_ActionBar-nnfbZCea.css.map +0 -1
- package/dist/C_Code-DPZlNSxL.css.map +0 -1
- package/dist/C_Code-avZjAnHD.css.map +0 -1
- package/dist/C_FullCalendar-1U4VlG7X.css.map +0 -1
- package/dist/C_FullCalendar-DLUdCtqf.css.map +0 -1
- package/dist/C_Steps-BHYNJI1l.css.map +0 -1
- package/dist/C_Steps-DY7vl-4I.css.map +0 -1
- package/dist/C_Time-Bd_e1YDN.css.map +0 -1
- package/dist/C_Time-CKb_fwdm.css.map +0 -1
package/dist/C_Transfer2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"C_Transfer2.js","names":[],"sources":["../src/components/C_Transfer/types.ts","../src/components/C_Transfer/index.vue","../src/components/C_Transfer/index.vue","../src/components/C_Transfer/index.vue"],"sourcesContent":["/**\r\n * @Description: 穿梭框组件类型定义\r\n */\r\n\r\n/** 穿梭框数据项 */\r\nexport interface TransferItem {\r\n /** 唯一标识 */\r\n key: string | number\r\n /** 显示标签 */\r\n label: string\r\n /** 是否禁用 */\r\n disabled?: boolean\r\n /** 分组名称 */\r\n group?: string\r\n /** 描述信息(用于详细模式) */\r\n description?: string\r\n /** 图标(Iconify) */\r\n icon?: string\r\n /** 额外数据 */\r\n extra?: Record<string, unknown>\r\n}\r\n\r\n/** 组件 Props */\r\nexport interface TransferProps {\r\n /** 数据源 */\r\n data: TransferItem[]\r\n /** 右侧已选 key 列表 (v-model) */\r\n modelValue: Array<string | number>\r\n /** 左右栏标题 */\r\n titles?: [string, string]\r\n /** 是否可搜索 */\r\n filterable?: boolean\r\n /** 搜索占位符 */\r\n filterPlaceholder?: string\r\n /** 自定义筛选函数 */\r\n filterMethod?: (query: string, item: TransferItem) => boolean\r\n /** 是否显示全选复选框 */\r\n showSelectAll?: boolean\r\n /** 左侧空状态描述 */\r\n sourceEmptyText?: string\r\n /** 右侧空状态描述 */\r\n targetEmptyText?: string\r\n /** 尺寸 */\r\n size?: 'small' | 'medium' | 'large'\r\n}\r\n\r\n/** 默认 Props */\r\nexport const DEFAULT_TRANSFER_PROPS: Partial<TransferProps> = {\r\n titles: ['可选列表', '已选列表'],\r\n filterable: false,\r\n filterPlaceholder: '搜索...',\r\n showSelectAll: true,\r\n sourceEmptyText: '暂无数据',\r\n targetEmptyText: '未选择任何项',\r\n size: 'medium',\r\n}\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: 穿梭框组件\r\n-->\r\n<template>\r\n <div\r\n class=\"c-transfer\"\r\n :class=\"[`is-${mergedProps.size}`]\"\r\n >\r\n <!-- ================ Source Panel ================ -->\r\n <div class=\"c-transfer__panel\">\r\n <div class=\"c-transfer__header\">\r\n <NCheckbox\r\n v-if=\"mergedProps.showSelectAll\"\r\n :checked=\"isSourceAllChecked\"\r\n :indeterminate=\"isSourceIndeterminate\"\r\n @update:checked=\"toggleSourceAll\"\r\n >\r\n {{ mergedProps.titles?.[0] }}\r\n </NCheckbox>\r\n <span\r\n v-else\r\n class=\"c-transfer__title\"\r\n >\r\n {{ mergedProps.titles?.[0] }}\r\n </span>\r\n <span class=\"c-transfer__count\"\r\n >{{ sourceChecked.size }} / {{ sourceList.length }}</span\r\n >\r\n </div>\r\n\r\n <div\r\n v-if=\"mergedProps.filterable\"\r\n class=\"c-transfer__filter\"\r\n >\r\n <NInput\r\n v-model:value=\"sourceQuery\"\r\n clearable\r\n size=\"small\"\r\n :placeholder=\"mergedProps.filterPlaceholder\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" />\r\n </template>\r\n </NInput>\r\n </div>\r\n\r\n <div\r\n v-if=\"filteredSourceList.length\"\r\n class=\"c-transfer__list\"\r\n >\r\n <template\r\n v-for=\"item in filteredSourceList\"\r\n :key=\"item.key\"\r\n >\r\n <div\r\n class=\"c-transfer__item\"\r\n :class=\"{\r\n 'is-disabled': item.disabled,\r\n 'is-checked': sourceChecked.has(item.key),\r\n }\"\r\n @click=\"!item.disabled && toggleSourceCheck(item.key)\"\r\n >\r\n <NCheckbox\r\n :checked=\"sourceChecked.has(item.key)\"\r\n :disabled=\"item.disabled\"\r\n size=\"small\"\r\n @update:checked=\"toggleSourceCheck(item.key)\"\r\n @click.stop\r\n />\r\n <C_Icon\r\n v-if=\"item.icon\"\r\n :name=\"item.icon\"\r\n class=\"c-transfer__item-icon\"\r\n />\r\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n <div\r\n v-else\r\n class=\"c-transfer__empty\"\r\n >\r\n {{ mergedProps.sourceEmptyText }}\r\n </div>\r\n </div>\r\n\r\n <!-- ================ Actions ================ -->\r\n <div class=\"c-transfer__actions\">\r\n <button\r\n class=\"c-transfer__btn\"\r\n :class=\"{ 'is-disabled': sourceChecked.size === 0 }\"\r\n :disabled=\"sourceChecked.size === 0\"\r\n @click=\"moveToTarget\"\r\n >\r\n <C_Icon name=\"mdi:chevron-right\" />\r\n </button>\r\n <button\r\n class=\"c-transfer__btn\"\r\n :class=\"{ 'is-disabled': targetChecked.size === 0 }\"\r\n :disabled=\"targetChecked.size === 0\"\r\n @click=\"moveToSource\"\r\n >\r\n <C_Icon name=\"mdi:chevron-left\" />\r\n </button>\r\n </div>\r\n\r\n <!-- ================ Target Panel ================ -->\r\n <div class=\"c-transfer__panel\">\r\n <div class=\"c-transfer__header\">\r\n <NCheckbox\r\n v-if=\"mergedProps.showSelectAll\"\r\n :checked=\"isTargetAllChecked\"\r\n :indeterminate=\"isTargetIndeterminate\"\r\n @update:checked=\"toggleTargetAll\"\r\n >\r\n {{ mergedProps.titles?.[1] }}\r\n </NCheckbox>\r\n <span\r\n v-else\r\n class=\"c-transfer__title\"\r\n >\r\n {{ mergedProps.titles?.[1] }}\r\n </span>\r\n <span class=\"c-transfer__count\"\r\n >{{ targetChecked.size }} / {{ targetList.length }}</span\r\n >\r\n </div>\r\n\r\n <div\r\n v-if=\"mergedProps.filterable\"\r\n class=\"c-transfer__filter\"\r\n >\r\n <NInput\r\n v-model:value=\"targetQuery\"\r\n clearable\r\n size=\"small\"\r\n :placeholder=\"mergedProps.filterPlaceholder\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" />\r\n </template>\r\n </NInput>\r\n </div>\r\n\r\n <div\r\n v-if=\"filteredTargetList.length\"\r\n class=\"c-transfer__list\"\r\n >\r\n <template\r\n v-for=\"item in filteredTargetList\"\r\n :key=\"item.key\"\r\n >\r\n <div\r\n class=\"c-transfer__item\"\r\n :class=\"{\r\n 'is-disabled': item.disabled,\r\n 'is-checked': targetChecked.has(item.key),\r\n }\"\r\n @click=\"!item.disabled && toggleTargetCheck(item.key)\"\r\n >\r\n <NCheckbox\r\n :checked=\"targetChecked.has(item.key)\"\r\n :disabled=\"item.disabled\"\r\n size=\"small\"\r\n @update:checked=\"toggleTargetCheck(item.key)\"\r\n @click.stop\r\n />\r\n <C_Icon\r\n v-if=\"item.icon\"\r\n :name=\"item.icon\"\r\n class=\"c-transfer__item-icon\"\r\n />\r\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n <div\r\n v-else\r\n class=\"c-transfer__empty\"\r\n >\r\n {{ mergedProps.targetEmptyText }}\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed, reactive, ref } from 'vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import {\r\n DEFAULT_TRANSFER_PROPS,\r\n type TransferItem,\r\n type TransferProps,\r\n } from './types'\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n data: TransferProps['data']\r\n modelValue: TransferProps['modelValue']\r\n titles?: TransferProps['titles']\r\n filterable?: boolean\r\n filterPlaceholder?: string\r\n filterMethod?: TransferProps['filterMethod']\r\n showSelectAll?: boolean\r\n sourceEmptyText?: string\r\n targetEmptyText?: string\r\n size?: TransferProps['size']\r\n }>(),\r\n {\r\n titles: () => DEFAULT_TRANSFER_PROPS.titles as [string, string],\r\n filterable: DEFAULT_TRANSFER_PROPS.filterable,\r\n filterPlaceholder: DEFAULT_TRANSFER_PROPS.filterPlaceholder,\r\n showSelectAll: DEFAULT_TRANSFER_PROPS.showSelectAll,\r\n sourceEmptyText: DEFAULT_TRANSFER_PROPS.sourceEmptyText,\r\n targetEmptyText: DEFAULT_TRANSFER_PROPS.targetEmptyText,\r\n size: DEFAULT_TRANSFER_PROPS.size,\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n 'update:modelValue': [value: Array<string | number>]\r\n change: [\r\n targetKeys: Array<string | number>,\r\n direction: 'left' | 'right',\r\n moveKeys: Array<string | number>,\r\n ]\r\n }>()\r\n\r\n const mergedProps = computed(() => ({ ...DEFAULT_TRANSFER_PROPS, ...props }))\r\n\r\n // ==================== 数据列表 ====================\r\n\r\n const dataMap = computed(() => {\r\n const m = new Map<string | number, TransferItem>()\r\n for (const item of props.data) m.set(item.key, item)\r\n return m\r\n })\r\n\r\n const targetKeySet = computed(() => new Set(props.modelValue))\r\n\r\n const sourceList = computed(() =>\r\n props.data.filter(d => !targetKeySet.value.has(d.key))\r\n )\r\n\r\n const targetList = computed(() => {\r\n const result: TransferItem[] = []\r\n for (const key of props.modelValue) {\r\n const item = dataMap.value.get(key)\r\n if (item) result.push(item)\r\n }\r\n return result\r\n })\r\n\r\n // ==================== 搜索 ====================\r\n\r\n const sourceQuery = ref('')\r\n const targetQuery = ref('')\r\n\r\n /** 默认过滤方法 */\r\n function defaultFilter(query: string, item: TransferItem): boolean {\r\n return item.label.toLowerCase().includes(query.toLowerCase())\r\n }\r\n\r\n const filterFn = computed(() => props.filterMethod ?? defaultFilter)\r\n\r\n const filteredSourceList = computed(() => {\r\n const q = sourceQuery.value.trim()\r\n return q\r\n ? sourceList.value.filter(item => filterFn.value(q, item))\r\n : sourceList.value\r\n })\r\n\r\n const filteredTargetList = computed(() => {\r\n const q = targetQuery.value.trim()\r\n return q\r\n ? targetList.value.filter(item => filterFn.value(q, item))\r\n : targetList.value\r\n })\r\n\r\n // ==================== 选中态 ====================\r\n\r\n const sourceChecked = reactive(new Set<string | number>())\r\n const targetChecked = reactive(new Set<string | number>())\r\n\r\n /** 切换源列表项选中状态 */\r\n function toggleSourceCheck(key: string | number) {\r\n if (sourceChecked.has(key)) sourceChecked.delete(key)\r\n else sourceChecked.add(key)\r\n }\r\n\r\n /** 切换目标列表项选中状态 */\r\n function toggleTargetCheck(key: string | number) {\r\n if (targetChecked.has(key)) targetChecked.delete(key)\r\n else targetChecked.add(key)\r\n }\r\n\r\n const isSourceAllChecked = computed(\r\n () =>\r\n filteredSourceList.value.length > 0 &&\r\n filteredSourceList.value.every(\r\n i => i.disabled || sourceChecked.has(i.key)\r\n )\r\n )\r\n const isSourceIndeterminate = computed(\r\n () =>\r\n !isSourceAllChecked.value &&\r\n filteredSourceList.value.some(i => sourceChecked.has(i.key))\r\n )\r\n const isTargetAllChecked = computed(\r\n () =>\r\n filteredTargetList.value.length > 0 &&\r\n filteredTargetList.value.every(\r\n i => i.disabled || targetChecked.has(i.key)\r\n )\r\n )\r\n const isTargetIndeterminate = computed(\r\n () =>\r\n !isTargetAllChecked.value &&\r\n filteredTargetList.value.some(i => targetChecked.has(i.key))\r\n )\r\n\r\n /** 切换源列表全选状态 */\r\n function toggleSourceAll(checked: boolean) {\r\n for (const item of filteredSourceList.value) {\r\n if (item.disabled) continue\r\n if (checked) sourceChecked.add(item.key)\r\n else sourceChecked.delete(item.key)\r\n }\r\n }\r\n\r\n /** 切换目标列表全选状态 */\r\n function toggleTargetAll(checked: boolean) {\r\n for (const item of filteredTargetList.value) {\r\n if (item.disabled) continue\r\n if (checked) targetChecked.add(item.key)\r\n else targetChecked.delete(item.key)\r\n }\r\n }\r\n\r\n // ==================== 穿梭操作 ====================\r\n\r\n /** 将选中项移至目标列表 */\r\n function moveToTarget() {\r\n const keys = [...sourceChecked]\r\n const newVal = [...props.modelValue, ...keys]\r\n emit('update:modelValue', newVal)\r\n emit('change', newVal, 'right', keys)\r\n sourceChecked.clear()\r\n }\r\n\r\n /** 将选中项移回源列表 */\r\n function moveToSource() {\r\n const keys = [...targetChecked]\r\n const removeSet = new Set(keys)\r\n const newVal = props.modelValue.filter(k => !removeSet.has(k))\r\n emit('update:modelValue', newVal)\r\n emit('change', newVal, 'left', keys)\r\n targetChecked.clear()\r\n }\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: 穿梭框组件\r\n-->\r\n<template>\r\n <div\r\n class=\"c-transfer\"\r\n :class=\"[`is-${mergedProps.size}`]\"\r\n >\r\n <!-- ================ Source Panel ================ -->\r\n <div class=\"c-transfer__panel\">\r\n <div class=\"c-transfer__header\">\r\n <NCheckbox\r\n v-if=\"mergedProps.showSelectAll\"\r\n :checked=\"isSourceAllChecked\"\r\n :indeterminate=\"isSourceIndeterminate\"\r\n @update:checked=\"toggleSourceAll\"\r\n >\r\n {{ mergedProps.titles?.[0] }}\r\n </NCheckbox>\r\n <span\r\n v-else\r\n class=\"c-transfer__title\"\r\n >\r\n {{ mergedProps.titles?.[0] }}\r\n </span>\r\n <span class=\"c-transfer__count\"\r\n >{{ sourceChecked.size }} / {{ sourceList.length }}</span\r\n >\r\n </div>\r\n\r\n <div\r\n v-if=\"mergedProps.filterable\"\r\n class=\"c-transfer__filter\"\r\n >\r\n <NInput\r\n v-model:value=\"sourceQuery\"\r\n clearable\r\n size=\"small\"\r\n :placeholder=\"mergedProps.filterPlaceholder\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" />\r\n </template>\r\n </NInput>\r\n </div>\r\n\r\n <div\r\n v-if=\"filteredSourceList.length\"\r\n class=\"c-transfer__list\"\r\n >\r\n <template\r\n v-for=\"item in filteredSourceList\"\r\n :key=\"item.key\"\r\n >\r\n <div\r\n class=\"c-transfer__item\"\r\n :class=\"{\r\n 'is-disabled': item.disabled,\r\n 'is-checked': sourceChecked.has(item.key),\r\n }\"\r\n @click=\"!item.disabled && toggleSourceCheck(item.key)\"\r\n >\r\n <NCheckbox\r\n :checked=\"sourceChecked.has(item.key)\"\r\n :disabled=\"item.disabled\"\r\n size=\"small\"\r\n @update:checked=\"toggleSourceCheck(item.key)\"\r\n @click.stop\r\n />\r\n <C_Icon\r\n v-if=\"item.icon\"\r\n :name=\"item.icon\"\r\n class=\"c-transfer__item-icon\"\r\n />\r\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n <div\r\n v-else\r\n class=\"c-transfer__empty\"\r\n >\r\n {{ mergedProps.sourceEmptyText }}\r\n </div>\r\n </div>\r\n\r\n <!-- ================ Actions ================ -->\r\n <div class=\"c-transfer__actions\">\r\n <button\r\n class=\"c-transfer__btn\"\r\n :class=\"{ 'is-disabled': sourceChecked.size === 0 }\"\r\n :disabled=\"sourceChecked.size === 0\"\r\n @click=\"moveToTarget\"\r\n >\r\n <C_Icon name=\"mdi:chevron-right\" />\r\n </button>\r\n <button\r\n class=\"c-transfer__btn\"\r\n :class=\"{ 'is-disabled': targetChecked.size === 0 }\"\r\n :disabled=\"targetChecked.size === 0\"\r\n @click=\"moveToSource\"\r\n >\r\n <C_Icon name=\"mdi:chevron-left\" />\r\n </button>\r\n </div>\r\n\r\n <!-- ================ Target Panel ================ -->\r\n <div class=\"c-transfer__panel\">\r\n <div class=\"c-transfer__header\">\r\n <NCheckbox\r\n v-if=\"mergedProps.showSelectAll\"\r\n :checked=\"isTargetAllChecked\"\r\n :indeterminate=\"isTargetIndeterminate\"\r\n @update:checked=\"toggleTargetAll\"\r\n >\r\n {{ mergedProps.titles?.[1] }}\r\n </NCheckbox>\r\n <span\r\n v-else\r\n class=\"c-transfer__title\"\r\n >\r\n {{ mergedProps.titles?.[1] }}\r\n </span>\r\n <span class=\"c-transfer__count\"\r\n >{{ targetChecked.size }} / {{ targetList.length }}</span\r\n >\r\n </div>\r\n\r\n <div\r\n v-if=\"mergedProps.filterable\"\r\n class=\"c-transfer__filter\"\r\n >\r\n <NInput\r\n v-model:value=\"targetQuery\"\r\n clearable\r\n size=\"small\"\r\n :placeholder=\"mergedProps.filterPlaceholder\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" />\r\n </template>\r\n </NInput>\r\n </div>\r\n\r\n <div\r\n v-if=\"filteredTargetList.length\"\r\n class=\"c-transfer__list\"\r\n >\r\n <template\r\n v-for=\"item in filteredTargetList\"\r\n :key=\"item.key\"\r\n >\r\n <div\r\n class=\"c-transfer__item\"\r\n :class=\"{\r\n 'is-disabled': item.disabled,\r\n 'is-checked': targetChecked.has(item.key),\r\n }\"\r\n @click=\"!item.disabled && toggleTargetCheck(item.key)\"\r\n >\r\n <NCheckbox\r\n :checked=\"targetChecked.has(item.key)\"\r\n :disabled=\"item.disabled\"\r\n size=\"small\"\r\n @update:checked=\"toggleTargetCheck(item.key)\"\r\n @click.stop\r\n />\r\n <C_Icon\r\n v-if=\"item.icon\"\r\n :name=\"item.icon\"\r\n class=\"c-transfer__item-icon\"\r\n />\r\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n <div\r\n v-else\r\n class=\"c-transfer__empty\"\r\n >\r\n {{ mergedProps.targetEmptyText }}\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed, reactive, ref } from 'vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import {\r\n DEFAULT_TRANSFER_PROPS,\r\n type TransferItem,\r\n type TransferProps,\r\n } from './types'\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n data: TransferProps['data']\r\n modelValue: TransferProps['modelValue']\r\n titles?: TransferProps['titles']\r\n filterable?: boolean\r\n filterPlaceholder?: string\r\n filterMethod?: TransferProps['filterMethod']\r\n showSelectAll?: boolean\r\n sourceEmptyText?: string\r\n targetEmptyText?: string\r\n size?: TransferProps['size']\r\n }>(),\r\n {\r\n titles: () => DEFAULT_TRANSFER_PROPS.titles as [string, string],\r\n filterable: DEFAULT_TRANSFER_PROPS.filterable,\r\n filterPlaceholder: DEFAULT_TRANSFER_PROPS.filterPlaceholder,\r\n showSelectAll: DEFAULT_TRANSFER_PROPS.showSelectAll,\r\n sourceEmptyText: DEFAULT_TRANSFER_PROPS.sourceEmptyText,\r\n targetEmptyText: DEFAULT_TRANSFER_PROPS.targetEmptyText,\r\n size: DEFAULT_TRANSFER_PROPS.size,\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n 'update:modelValue': [value: Array<string | number>]\r\n change: [\r\n targetKeys: Array<string | number>,\r\n direction: 'left' | 'right',\r\n moveKeys: Array<string | number>,\r\n ]\r\n }>()\r\n\r\n const mergedProps = computed(() => ({ ...DEFAULT_TRANSFER_PROPS, ...props }))\r\n\r\n // ==================== 数据列表 ====================\r\n\r\n const dataMap = computed(() => {\r\n const m = new Map<string | number, TransferItem>()\r\n for (const item of props.data) m.set(item.key, item)\r\n return m\r\n })\r\n\r\n const targetKeySet = computed(() => new Set(props.modelValue))\r\n\r\n const sourceList = computed(() =>\r\n props.data.filter(d => !targetKeySet.value.has(d.key))\r\n )\r\n\r\n const targetList = computed(() => {\r\n const result: TransferItem[] = []\r\n for (const key of props.modelValue) {\r\n const item = dataMap.value.get(key)\r\n if (item) result.push(item)\r\n }\r\n return result\r\n })\r\n\r\n // ==================== 搜索 ====================\r\n\r\n const sourceQuery = ref('')\r\n const targetQuery = ref('')\r\n\r\n /** 默认过滤方法 */\r\n function defaultFilter(query: string, item: TransferItem): boolean {\r\n return item.label.toLowerCase().includes(query.toLowerCase())\r\n }\r\n\r\n const filterFn = computed(() => props.filterMethod ?? defaultFilter)\r\n\r\n const filteredSourceList = computed(() => {\r\n const q = sourceQuery.value.trim()\r\n return q\r\n ? sourceList.value.filter(item => filterFn.value(q, item))\r\n : sourceList.value\r\n })\r\n\r\n const filteredTargetList = computed(() => {\r\n const q = targetQuery.value.trim()\r\n return q\r\n ? targetList.value.filter(item => filterFn.value(q, item))\r\n : targetList.value\r\n })\r\n\r\n // ==================== 选中态 ====================\r\n\r\n const sourceChecked = reactive(new Set<string | number>())\r\n const targetChecked = reactive(new Set<string | number>())\r\n\r\n /** 切换源列表项选中状态 */\r\n function toggleSourceCheck(key: string | number) {\r\n if (sourceChecked.has(key)) sourceChecked.delete(key)\r\n else sourceChecked.add(key)\r\n }\r\n\r\n /** 切换目标列表项选中状态 */\r\n function toggleTargetCheck(key: string | number) {\r\n if (targetChecked.has(key)) targetChecked.delete(key)\r\n else targetChecked.add(key)\r\n }\r\n\r\n const isSourceAllChecked = computed(\r\n () =>\r\n filteredSourceList.value.length > 0 &&\r\n filteredSourceList.value.every(\r\n i => i.disabled || sourceChecked.has(i.key)\r\n )\r\n )\r\n const isSourceIndeterminate = computed(\r\n () =>\r\n !isSourceAllChecked.value &&\r\n filteredSourceList.value.some(i => sourceChecked.has(i.key))\r\n )\r\n const isTargetAllChecked = computed(\r\n () =>\r\n filteredTargetList.value.length > 0 &&\r\n filteredTargetList.value.every(\r\n i => i.disabled || targetChecked.has(i.key)\r\n )\r\n )\r\n const isTargetIndeterminate = computed(\r\n () =>\r\n !isTargetAllChecked.value &&\r\n filteredTargetList.value.some(i => targetChecked.has(i.key))\r\n )\r\n\r\n /** 切换源列表全选状态 */\r\n function toggleSourceAll(checked: boolean) {\r\n for (const item of filteredSourceList.value) {\r\n if (item.disabled) continue\r\n if (checked) sourceChecked.add(item.key)\r\n else sourceChecked.delete(item.key)\r\n }\r\n }\r\n\r\n /** 切换目标列表全选状态 */\r\n function toggleTargetAll(checked: boolean) {\r\n for (const item of filteredTargetList.value) {\r\n if (item.disabled) continue\r\n if (checked) targetChecked.add(item.key)\r\n else targetChecked.delete(item.key)\r\n }\r\n }\r\n\r\n // ==================== 穿梭操作 ====================\r\n\r\n /** 将选中项移至目标列表 */\r\n function moveToTarget() {\r\n const keys = [...sourceChecked]\r\n const newVal = [...props.modelValue, ...keys]\r\n emit('update:modelValue', newVal)\r\n emit('change', newVal, 'right', keys)\r\n sourceChecked.clear()\r\n }\r\n\r\n /** 将选中项移回源列表 */\r\n function moveToSource() {\r\n const keys = [...targetChecked]\r\n const removeSet = new Set(keys)\r\n const newVal = props.modelValue.filter(k => !removeSet.has(k))\r\n emit('update:modelValue', newVal)\r\n emit('change', newVal, 'left', keys)\r\n targetChecked.clear()\r\n }\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n","<!--\r\n * @Description: 穿梭框组件\r\n-->\r\n<template>\r\n <div\r\n class=\"c-transfer\"\r\n :class=\"[`is-${mergedProps.size}`]\"\r\n >\r\n <!-- ================ Source Panel ================ -->\r\n <div class=\"c-transfer__panel\">\r\n <div class=\"c-transfer__header\">\r\n <NCheckbox\r\n v-if=\"mergedProps.showSelectAll\"\r\n :checked=\"isSourceAllChecked\"\r\n :indeterminate=\"isSourceIndeterminate\"\r\n @update:checked=\"toggleSourceAll\"\r\n >\r\n {{ mergedProps.titles?.[0] }}\r\n </NCheckbox>\r\n <span\r\n v-else\r\n class=\"c-transfer__title\"\r\n >\r\n {{ mergedProps.titles?.[0] }}\r\n </span>\r\n <span class=\"c-transfer__count\"\r\n >{{ sourceChecked.size }} / {{ sourceList.length }}</span\r\n >\r\n </div>\r\n\r\n <div\r\n v-if=\"mergedProps.filterable\"\r\n class=\"c-transfer__filter\"\r\n >\r\n <NInput\r\n v-model:value=\"sourceQuery\"\r\n clearable\r\n size=\"small\"\r\n :placeholder=\"mergedProps.filterPlaceholder\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" />\r\n </template>\r\n </NInput>\r\n </div>\r\n\r\n <div\r\n v-if=\"filteredSourceList.length\"\r\n class=\"c-transfer__list\"\r\n >\r\n <template\r\n v-for=\"item in filteredSourceList\"\r\n :key=\"item.key\"\r\n >\r\n <div\r\n class=\"c-transfer__item\"\r\n :class=\"{\r\n 'is-disabled': item.disabled,\r\n 'is-checked': sourceChecked.has(item.key),\r\n }\"\r\n @click=\"!item.disabled && toggleSourceCheck(item.key)\"\r\n >\r\n <NCheckbox\r\n :checked=\"sourceChecked.has(item.key)\"\r\n :disabled=\"item.disabled\"\r\n size=\"small\"\r\n @update:checked=\"toggleSourceCheck(item.key)\"\r\n @click.stop\r\n />\r\n <C_Icon\r\n v-if=\"item.icon\"\r\n :name=\"item.icon\"\r\n class=\"c-transfer__item-icon\"\r\n />\r\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n <div\r\n v-else\r\n class=\"c-transfer__empty\"\r\n >\r\n {{ mergedProps.sourceEmptyText }}\r\n </div>\r\n </div>\r\n\r\n <!-- ================ Actions ================ -->\r\n <div class=\"c-transfer__actions\">\r\n <button\r\n class=\"c-transfer__btn\"\r\n :class=\"{ 'is-disabled': sourceChecked.size === 0 }\"\r\n :disabled=\"sourceChecked.size === 0\"\r\n @click=\"moveToTarget\"\r\n >\r\n <C_Icon name=\"mdi:chevron-right\" />\r\n </button>\r\n <button\r\n class=\"c-transfer__btn\"\r\n :class=\"{ 'is-disabled': targetChecked.size === 0 }\"\r\n :disabled=\"targetChecked.size === 0\"\r\n @click=\"moveToSource\"\r\n >\r\n <C_Icon name=\"mdi:chevron-left\" />\r\n </button>\r\n </div>\r\n\r\n <!-- ================ Target Panel ================ -->\r\n <div class=\"c-transfer__panel\">\r\n <div class=\"c-transfer__header\">\r\n <NCheckbox\r\n v-if=\"mergedProps.showSelectAll\"\r\n :checked=\"isTargetAllChecked\"\r\n :indeterminate=\"isTargetIndeterminate\"\r\n @update:checked=\"toggleTargetAll\"\r\n >\r\n {{ mergedProps.titles?.[1] }}\r\n </NCheckbox>\r\n <span\r\n v-else\r\n class=\"c-transfer__title\"\r\n >\r\n {{ mergedProps.titles?.[1] }}\r\n </span>\r\n <span class=\"c-transfer__count\"\r\n >{{ targetChecked.size }} / {{ targetList.length }}</span\r\n >\r\n </div>\r\n\r\n <div\r\n v-if=\"mergedProps.filterable\"\r\n class=\"c-transfer__filter\"\r\n >\r\n <NInput\r\n v-model:value=\"targetQuery\"\r\n clearable\r\n size=\"small\"\r\n :placeholder=\"mergedProps.filterPlaceholder\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" />\r\n </template>\r\n </NInput>\r\n </div>\r\n\r\n <div\r\n v-if=\"filteredTargetList.length\"\r\n class=\"c-transfer__list\"\r\n >\r\n <template\r\n v-for=\"item in filteredTargetList\"\r\n :key=\"item.key\"\r\n >\r\n <div\r\n class=\"c-transfer__item\"\r\n :class=\"{\r\n 'is-disabled': item.disabled,\r\n 'is-checked': targetChecked.has(item.key),\r\n }\"\r\n @click=\"!item.disabled && toggleTargetCheck(item.key)\"\r\n >\r\n <NCheckbox\r\n :checked=\"targetChecked.has(item.key)\"\r\n :disabled=\"item.disabled\"\r\n size=\"small\"\r\n @update:checked=\"toggleTargetCheck(item.key)\"\r\n @click.stop\r\n />\r\n <C_Icon\r\n v-if=\"item.icon\"\r\n :name=\"item.icon\"\r\n class=\"c-transfer__item-icon\"\r\n />\r\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n <div\r\n v-else\r\n class=\"c-transfer__empty\"\r\n >\r\n {{ mergedProps.targetEmptyText }}\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed, reactive, ref } from 'vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import {\r\n DEFAULT_TRANSFER_PROPS,\r\n type TransferItem,\r\n type TransferProps,\r\n } from './types'\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n data: TransferProps['data']\r\n modelValue: TransferProps['modelValue']\r\n titles?: TransferProps['titles']\r\n filterable?: boolean\r\n filterPlaceholder?: string\r\n filterMethod?: TransferProps['filterMethod']\r\n showSelectAll?: boolean\r\n sourceEmptyText?: string\r\n targetEmptyText?: string\r\n size?: TransferProps['size']\r\n }>(),\r\n {\r\n titles: () => DEFAULT_TRANSFER_PROPS.titles as [string, string],\r\n filterable: DEFAULT_TRANSFER_PROPS.filterable,\r\n filterPlaceholder: DEFAULT_TRANSFER_PROPS.filterPlaceholder,\r\n showSelectAll: DEFAULT_TRANSFER_PROPS.showSelectAll,\r\n sourceEmptyText: DEFAULT_TRANSFER_PROPS.sourceEmptyText,\r\n targetEmptyText: DEFAULT_TRANSFER_PROPS.targetEmptyText,\r\n size: DEFAULT_TRANSFER_PROPS.size,\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n 'update:modelValue': [value: Array<string | number>]\r\n change: [\r\n targetKeys: Array<string | number>,\r\n direction: 'left' | 'right',\r\n moveKeys: Array<string | number>,\r\n ]\r\n }>()\r\n\r\n const mergedProps = computed(() => ({ ...DEFAULT_TRANSFER_PROPS, ...props }))\r\n\r\n // ==================== 数据列表 ====================\r\n\r\n const dataMap = computed(() => {\r\n const m = new Map<string | number, TransferItem>()\r\n for (const item of props.data) m.set(item.key, item)\r\n return m\r\n })\r\n\r\n const targetKeySet = computed(() => new Set(props.modelValue))\r\n\r\n const sourceList = computed(() =>\r\n props.data.filter(d => !targetKeySet.value.has(d.key))\r\n )\r\n\r\n const targetList = computed(() => {\r\n const result: TransferItem[] = []\r\n for (const key of props.modelValue) {\r\n const item = dataMap.value.get(key)\r\n if (item) result.push(item)\r\n }\r\n return result\r\n })\r\n\r\n // ==================== 搜索 ====================\r\n\r\n const sourceQuery = ref('')\r\n const targetQuery = ref('')\r\n\r\n /** 默认过滤方法 */\r\n function defaultFilter(query: string, item: TransferItem): boolean {\r\n return item.label.toLowerCase().includes(query.toLowerCase())\r\n }\r\n\r\n const filterFn = computed(() => props.filterMethod ?? defaultFilter)\r\n\r\n const filteredSourceList = computed(() => {\r\n const q = sourceQuery.value.trim()\r\n return q\r\n ? sourceList.value.filter(item => filterFn.value(q, item))\r\n : sourceList.value\r\n })\r\n\r\n const filteredTargetList = computed(() => {\r\n const q = targetQuery.value.trim()\r\n return q\r\n ? targetList.value.filter(item => filterFn.value(q, item))\r\n : targetList.value\r\n })\r\n\r\n // ==================== 选中态 ====================\r\n\r\n const sourceChecked = reactive(new Set<string | number>())\r\n const targetChecked = reactive(new Set<string | number>())\r\n\r\n /** 切换源列表项选中状态 */\r\n function toggleSourceCheck(key: string | number) {\r\n if (sourceChecked.has(key)) sourceChecked.delete(key)\r\n else sourceChecked.add(key)\r\n }\r\n\r\n /** 切换目标列表项选中状态 */\r\n function toggleTargetCheck(key: string | number) {\r\n if (targetChecked.has(key)) targetChecked.delete(key)\r\n else targetChecked.add(key)\r\n }\r\n\r\n const isSourceAllChecked = computed(\r\n () =>\r\n filteredSourceList.value.length > 0 &&\r\n filteredSourceList.value.every(\r\n i => i.disabled || sourceChecked.has(i.key)\r\n )\r\n )\r\n const isSourceIndeterminate = computed(\r\n () =>\r\n !isSourceAllChecked.value &&\r\n filteredSourceList.value.some(i => sourceChecked.has(i.key))\r\n )\r\n const isTargetAllChecked = computed(\r\n () =>\r\n filteredTargetList.value.length > 0 &&\r\n filteredTargetList.value.every(\r\n i => i.disabled || targetChecked.has(i.key)\r\n )\r\n )\r\n const isTargetIndeterminate = computed(\r\n () =>\r\n !isTargetAllChecked.value &&\r\n filteredTargetList.value.some(i => targetChecked.has(i.key))\r\n )\r\n\r\n /** 切换源列表全选状态 */\r\n function toggleSourceAll(checked: boolean) {\r\n for (const item of filteredSourceList.value) {\r\n if (item.disabled) continue\r\n if (checked) sourceChecked.add(item.key)\r\n else sourceChecked.delete(item.key)\r\n }\r\n }\r\n\r\n /** 切换目标列表全选状态 */\r\n function toggleTargetAll(checked: boolean) {\r\n for (const item of filteredTargetList.value) {\r\n if (item.disabled) continue\r\n if (checked) targetChecked.add(item.key)\r\n else targetChecked.delete(item.key)\r\n }\r\n }\r\n\r\n // ==================== 穿梭操作 ====================\r\n\r\n /** 将选中项移至目标列表 */\r\n function moveToTarget() {\r\n const keys = [...sourceChecked]\r\n const newVal = [...props.modelValue, ...keys]\r\n emit('update:modelValue', newVal)\r\n emit('change', newVal, 'right', keys)\r\n sourceChecked.clear()\r\n }\r\n\r\n /** 将选中项移回源列表 */\r\n function moveToSource() {\r\n const keys = [...targetChecked]\r\n const removeSet = new Set(keys)\r\n const newVal = props.modelValue.filter(k => !removeSet.has(k))\r\n emit('update:modelValue', newVal)\r\n emit('change', newVal, 'left', keys)\r\n targetChecked.clear()\r\n }\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"C_Transfer2.js","names":[],"sources":["../src/components/C_Transfer/types.ts","../src/components/C_Transfer/index.vue","../src/components/C_Transfer/index.vue","../src/components/C_Transfer/index.vue"],"sourcesContent":["/**\n * @Description: 穿梭框组件类型定义\n */\n\n/** 穿梭框数据项 */\nexport interface TransferItem {\n /** 唯一标识 */\n key: string | number\n /** 显示标签 */\n label: string\n /** 是否禁用 */\n disabled?: boolean\n /** 分组名称 */\n group?: string\n /** 描述信息(用于详细模式) */\n description?: string\n /** 图标(Iconify) */\n icon?: string\n /** 额外数据 */\n extra?: Record<string, unknown>\n}\n\n/** 组件 Props */\nexport interface TransferProps {\n /** 数据源 */\n data: TransferItem[]\n /** 右侧已选 key 列表 (v-model) */\n modelValue: Array<string | number>\n /** 左右栏标题 */\n titles?: [string, string]\n /** 是否可搜索 */\n filterable?: boolean\n /** 搜索占位符 */\n filterPlaceholder?: string\n /** 自定义筛选函数 */\n filterMethod?: (query: string, item: TransferItem) => boolean\n /** 是否显示全选复选框 */\n showSelectAll?: boolean\n /** 左侧空状态描述 */\n sourceEmptyText?: string\n /** 右侧空状态描述 */\n targetEmptyText?: string\n /** 尺寸 */\n size?: 'small' | 'medium' | 'large'\n}\n\n/** 默认 Props */\nexport const DEFAULT_TRANSFER_PROPS: Partial<TransferProps> = {\n titles: ['可选列表', '已选列表'],\n filterable: false,\n filterPlaceholder: '搜索...',\n showSelectAll: true,\n sourceEmptyText: '暂无数据',\n targetEmptyText: '未选择任何项',\n size: 'medium',\n}\n","/* unplugin-vue-components disabled */<!--\n * @Description: 穿梭框组件\n-->\n<template>\n <div\n class=\"c-transfer\"\n :class=\"[`is-${mergedProps.size}`]\"\n >\n <!-- ================ Source Panel ================ -->\n <div class=\"c-transfer__panel\">\n <div class=\"c-transfer__header\">\n <NCheckbox\n v-if=\"mergedProps.showSelectAll\"\n :checked=\"isSourceAllChecked\"\n :indeterminate=\"isSourceIndeterminate\"\n @update:checked=\"toggleSourceAll\"\n >\n {{ mergedProps.titles?.[0] }}\n </NCheckbox>\n <span\n v-else\n class=\"c-transfer__title\"\n >\n {{ mergedProps.titles?.[0] }}\n </span>\n <span class=\"c-transfer__count\"\n >{{ sourceChecked.size }} / {{ sourceList.length }}</span\n >\n </div>\n\n <div\n v-if=\"mergedProps.filterable\"\n class=\"c-transfer__filter\"\n >\n <NInput\n v-model:value=\"sourceQuery\"\n clearable\n size=\"small\"\n :placeholder=\"mergedProps.filterPlaceholder\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n </div>\n\n <div\n v-if=\"filteredSourceList.length\"\n class=\"c-transfer__list\"\n >\n <template\n v-for=\"item in filteredSourceList\"\n :key=\"item.key\"\n >\n <div\n class=\"c-transfer__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-checked': sourceChecked.has(item.key),\n }\"\n @click=\"!item.disabled && toggleSourceCheck(item.key)\"\n >\n <NCheckbox\n :checked=\"sourceChecked.has(item.key)\"\n :disabled=\"item.disabled\"\n size=\"small\"\n @update:checked=\"toggleSourceCheck(item.key)\"\n @click.stop\n />\n <C_Icon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n class=\"c-transfer__item-icon\"\n />\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\n </div>\n </template>\n </div>\n <div\n v-else\n class=\"c-transfer__empty\"\n >\n {{ mergedProps.sourceEmptyText }}\n </div>\n </div>\n\n <!-- ================ Actions ================ -->\n <div class=\"c-transfer__actions\">\n <button\n class=\"c-transfer__btn\"\n :class=\"{ 'is-disabled': sourceChecked.size === 0 }\"\n :disabled=\"sourceChecked.size === 0\"\n @click=\"moveToTarget\"\n >\n <C_Icon name=\"mdi:chevron-right\" />\n </button>\n <button\n class=\"c-transfer__btn\"\n :class=\"{ 'is-disabled': targetChecked.size === 0 }\"\n :disabled=\"targetChecked.size === 0\"\n @click=\"moveToSource\"\n >\n <C_Icon name=\"mdi:chevron-left\" />\n </button>\n </div>\n\n <!-- ================ Target Panel ================ -->\n <div class=\"c-transfer__panel\">\n <div class=\"c-transfer__header\">\n <NCheckbox\n v-if=\"mergedProps.showSelectAll\"\n :checked=\"isTargetAllChecked\"\n :indeterminate=\"isTargetIndeterminate\"\n @update:checked=\"toggleTargetAll\"\n >\n {{ mergedProps.titles?.[1] }}\n </NCheckbox>\n <span\n v-else\n class=\"c-transfer__title\"\n >\n {{ mergedProps.titles?.[1] }}\n </span>\n <span class=\"c-transfer__count\"\n >{{ targetChecked.size }} / {{ targetList.length }}</span\n >\n </div>\n\n <div\n v-if=\"mergedProps.filterable\"\n class=\"c-transfer__filter\"\n >\n <NInput\n v-model:value=\"targetQuery\"\n clearable\n size=\"small\"\n :placeholder=\"mergedProps.filterPlaceholder\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n </div>\n\n <div\n v-if=\"filteredTargetList.length\"\n class=\"c-transfer__list\"\n >\n <template\n v-for=\"item in filteredTargetList\"\n :key=\"item.key\"\n >\n <div\n class=\"c-transfer__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-checked': targetChecked.has(item.key),\n }\"\n @click=\"!item.disabled && toggleTargetCheck(item.key)\"\n >\n <NCheckbox\n :checked=\"targetChecked.has(item.key)\"\n :disabled=\"item.disabled\"\n size=\"small\"\n @update:checked=\"toggleTargetCheck(item.key)\"\n @click.stop\n />\n <C_Icon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n class=\"c-transfer__item-icon\"\n />\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\n </div>\n </template>\n </div>\n <div\n v-else\n class=\"c-transfer__empty\"\n >\n {{ mergedProps.targetEmptyText }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, reactive, ref } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_TRANSFER_PROPS,\n type TransferItem,\n type TransferProps,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n data: TransferProps['data']\n modelValue: TransferProps['modelValue']\n titles?: TransferProps['titles']\n filterable?: boolean\n filterPlaceholder?: string\n filterMethod?: TransferProps['filterMethod']\n showSelectAll?: boolean\n sourceEmptyText?: string\n targetEmptyText?: string\n size?: TransferProps['size']\n }>(),\n {\n titles: () => DEFAULT_TRANSFER_PROPS.titles as [string, string],\n filterable: DEFAULT_TRANSFER_PROPS.filterable,\n filterPlaceholder: DEFAULT_TRANSFER_PROPS.filterPlaceholder,\n showSelectAll: DEFAULT_TRANSFER_PROPS.showSelectAll,\n sourceEmptyText: DEFAULT_TRANSFER_PROPS.sourceEmptyText,\n targetEmptyText: DEFAULT_TRANSFER_PROPS.targetEmptyText,\n size: DEFAULT_TRANSFER_PROPS.size,\n }\n )\n\n const emit = defineEmits<{\n 'update:modelValue': [value: Array<string | number>]\n change: [\n targetKeys: Array<string | number>,\n direction: 'left' | 'right',\n moveKeys: Array<string | number>,\n ]\n }>()\n\n const mergedProps = computed(() => ({ ...DEFAULT_TRANSFER_PROPS, ...props }))\n\n // ==================== 数据列表 ====================\n\n const dataMap = computed(() => {\n const m = new Map<string | number, TransferItem>()\n for (const item of props.data) m.set(item.key, item)\n return m\n })\n\n const targetKeySet = computed(() => new Set(props.modelValue))\n\n const sourceList = computed(() =>\n props.data.filter(d => !targetKeySet.value.has(d.key))\n )\n\n const targetList = computed(() => {\n const result: TransferItem[] = []\n for (const key of props.modelValue) {\n const item = dataMap.value.get(key)\n if (item) result.push(item)\n }\n return result\n })\n\n // ==================== 搜索 ====================\n\n const sourceQuery = ref('')\n const targetQuery = ref('')\n\n /** 默认过滤方法 */\n function defaultFilter(query: string, item: TransferItem): boolean {\n return item.label.toLowerCase().includes(query.toLowerCase())\n }\n\n const filterFn = computed(() => props.filterMethod ?? defaultFilter)\n\n const filteredSourceList = computed(() => {\n const q = sourceQuery.value.trim()\n return q\n ? sourceList.value.filter(item => filterFn.value(q, item))\n : sourceList.value\n })\n\n const filteredTargetList = computed(() => {\n const q = targetQuery.value.trim()\n return q\n ? targetList.value.filter(item => filterFn.value(q, item))\n : targetList.value\n })\n\n // ==================== 选中态 ====================\n\n const sourceChecked = reactive(new Set<string | number>())\n const targetChecked = reactive(new Set<string | number>())\n\n /** 切换源列表项选中状态 */\n function toggleSourceCheck(key: string | number) {\n if (sourceChecked.has(key)) sourceChecked.delete(key)\n else sourceChecked.add(key)\n }\n\n /** 切换目标列表项选中状态 */\n function toggleTargetCheck(key: string | number) {\n if (targetChecked.has(key)) targetChecked.delete(key)\n else targetChecked.add(key)\n }\n\n const isSourceAllChecked = computed(\n () =>\n filteredSourceList.value.length > 0 &&\n filteredSourceList.value.every(\n i => i.disabled || sourceChecked.has(i.key)\n )\n )\n const isSourceIndeterminate = computed(\n () =>\n !isSourceAllChecked.value &&\n filteredSourceList.value.some(i => sourceChecked.has(i.key))\n )\n const isTargetAllChecked = computed(\n () =>\n filteredTargetList.value.length > 0 &&\n filteredTargetList.value.every(\n i => i.disabled || targetChecked.has(i.key)\n )\n )\n const isTargetIndeterminate = computed(\n () =>\n !isTargetAllChecked.value &&\n filteredTargetList.value.some(i => targetChecked.has(i.key))\n )\n\n /** 切换源列表全选状态 */\n function toggleSourceAll(checked: boolean) {\n for (const item of filteredSourceList.value) {\n if (item.disabled) continue\n if (checked) sourceChecked.add(item.key)\n else sourceChecked.delete(item.key)\n }\n }\n\n /** 切换目标列表全选状态 */\n function toggleTargetAll(checked: boolean) {\n for (const item of filteredTargetList.value) {\n if (item.disabled) continue\n if (checked) targetChecked.add(item.key)\n else targetChecked.delete(item.key)\n }\n }\n\n // ==================== 穿梭操作 ====================\n\n /** 将选中项移至目标列表 */\n function moveToTarget() {\n const keys = [...sourceChecked]\n const newVal = [...props.modelValue, ...keys]\n emit('update:modelValue', newVal)\n emit('change', newVal, 'right', keys)\n sourceChecked.clear()\n }\n\n /** 将选中项移回源列表 */\n function moveToSource() {\n const keys = [...targetChecked]\n const removeSet = new Set(keys)\n const newVal = props.modelValue.filter(k => !removeSet.has(k))\n emit('update:modelValue', newVal)\n emit('change', newVal, 'left', keys)\n targetChecked.clear()\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","/* unplugin-vue-components disabled */<!--\n * @Description: 穿梭框组件\n-->\n<template>\n <div\n class=\"c-transfer\"\n :class=\"[`is-${mergedProps.size}`]\"\n >\n <!-- ================ Source Panel ================ -->\n <div class=\"c-transfer__panel\">\n <div class=\"c-transfer__header\">\n <NCheckbox\n v-if=\"mergedProps.showSelectAll\"\n :checked=\"isSourceAllChecked\"\n :indeterminate=\"isSourceIndeterminate\"\n @update:checked=\"toggleSourceAll\"\n >\n {{ mergedProps.titles?.[0] }}\n </NCheckbox>\n <span\n v-else\n class=\"c-transfer__title\"\n >\n {{ mergedProps.titles?.[0] }}\n </span>\n <span class=\"c-transfer__count\"\n >{{ sourceChecked.size }} / {{ sourceList.length }}</span\n >\n </div>\n\n <div\n v-if=\"mergedProps.filterable\"\n class=\"c-transfer__filter\"\n >\n <NInput\n v-model:value=\"sourceQuery\"\n clearable\n size=\"small\"\n :placeholder=\"mergedProps.filterPlaceholder\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n </div>\n\n <div\n v-if=\"filteredSourceList.length\"\n class=\"c-transfer__list\"\n >\n <template\n v-for=\"item in filteredSourceList\"\n :key=\"item.key\"\n >\n <div\n class=\"c-transfer__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-checked': sourceChecked.has(item.key),\n }\"\n @click=\"!item.disabled && toggleSourceCheck(item.key)\"\n >\n <NCheckbox\n :checked=\"sourceChecked.has(item.key)\"\n :disabled=\"item.disabled\"\n size=\"small\"\n @update:checked=\"toggleSourceCheck(item.key)\"\n @click.stop\n />\n <C_Icon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n class=\"c-transfer__item-icon\"\n />\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\n </div>\n </template>\n </div>\n <div\n v-else\n class=\"c-transfer__empty\"\n >\n {{ mergedProps.sourceEmptyText }}\n </div>\n </div>\n\n <!-- ================ Actions ================ -->\n <div class=\"c-transfer__actions\">\n <button\n class=\"c-transfer__btn\"\n :class=\"{ 'is-disabled': sourceChecked.size === 0 }\"\n :disabled=\"sourceChecked.size === 0\"\n @click=\"moveToTarget\"\n >\n <C_Icon name=\"mdi:chevron-right\" />\n </button>\n <button\n class=\"c-transfer__btn\"\n :class=\"{ 'is-disabled': targetChecked.size === 0 }\"\n :disabled=\"targetChecked.size === 0\"\n @click=\"moveToSource\"\n >\n <C_Icon name=\"mdi:chevron-left\" />\n </button>\n </div>\n\n <!-- ================ Target Panel ================ -->\n <div class=\"c-transfer__panel\">\n <div class=\"c-transfer__header\">\n <NCheckbox\n v-if=\"mergedProps.showSelectAll\"\n :checked=\"isTargetAllChecked\"\n :indeterminate=\"isTargetIndeterminate\"\n @update:checked=\"toggleTargetAll\"\n >\n {{ mergedProps.titles?.[1] }}\n </NCheckbox>\n <span\n v-else\n class=\"c-transfer__title\"\n >\n {{ mergedProps.titles?.[1] }}\n </span>\n <span class=\"c-transfer__count\"\n >{{ targetChecked.size }} / {{ targetList.length }}</span\n >\n </div>\n\n <div\n v-if=\"mergedProps.filterable\"\n class=\"c-transfer__filter\"\n >\n <NInput\n v-model:value=\"targetQuery\"\n clearable\n size=\"small\"\n :placeholder=\"mergedProps.filterPlaceholder\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n </div>\n\n <div\n v-if=\"filteredTargetList.length\"\n class=\"c-transfer__list\"\n >\n <template\n v-for=\"item in filteredTargetList\"\n :key=\"item.key\"\n >\n <div\n class=\"c-transfer__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-checked': targetChecked.has(item.key),\n }\"\n @click=\"!item.disabled && toggleTargetCheck(item.key)\"\n >\n <NCheckbox\n :checked=\"targetChecked.has(item.key)\"\n :disabled=\"item.disabled\"\n size=\"small\"\n @update:checked=\"toggleTargetCheck(item.key)\"\n @click.stop\n />\n <C_Icon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n class=\"c-transfer__item-icon\"\n />\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\n </div>\n </template>\n </div>\n <div\n v-else\n class=\"c-transfer__empty\"\n >\n {{ mergedProps.targetEmptyText }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, reactive, ref } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_TRANSFER_PROPS,\n type TransferItem,\n type TransferProps,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n data: TransferProps['data']\n modelValue: TransferProps['modelValue']\n titles?: TransferProps['titles']\n filterable?: boolean\n filterPlaceholder?: string\n filterMethod?: TransferProps['filterMethod']\n showSelectAll?: boolean\n sourceEmptyText?: string\n targetEmptyText?: string\n size?: TransferProps['size']\n }>(),\n {\n titles: () => DEFAULT_TRANSFER_PROPS.titles as [string, string],\n filterable: DEFAULT_TRANSFER_PROPS.filterable,\n filterPlaceholder: DEFAULT_TRANSFER_PROPS.filterPlaceholder,\n showSelectAll: DEFAULT_TRANSFER_PROPS.showSelectAll,\n sourceEmptyText: DEFAULT_TRANSFER_PROPS.sourceEmptyText,\n targetEmptyText: DEFAULT_TRANSFER_PROPS.targetEmptyText,\n size: DEFAULT_TRANSFER_PROPS.size,\n }\n )\n\n const emit = defineEmits<{\n 'update:modelValue': [value: Array<string | number>]\n change: [\n targetKeys: Array<string | number>,\n direction: 'left' | 'right',\n moveKeys: Array<string | number>,\n ]\n }>()\n\n const mergedProps = computed(() => ({ ...DEFAULT_TRANSFER_PROPS, ...props }))\n\n // ==================== 数据列表 ====================\n\n const dataMap = computed(() => {\n const m = new Map<string | number, TransferItem>()\n for (const item of props.data) m.set(item.key, item)\n return m\n })\n\n const targetKeySet = computed(() => new Set(props.modelValue))\n\n const sourceList = computed(() =>\n props.data.filter(d => !targetKeySet.value.has(d.key))\n )\n\n const targetList = computed(() => {\n const result: TransferItem[] = []\n for (const key of props.modelValue) {\n const item = dataMap.value.get(key)\n if (item) result.push(item)\n }\n return result\n })\n\n // ==================== 搜索 ====================\n\n const sourceQuery = ref('')\n const targetQuery = ref('')\n\n /** 默认过滤方法 */\n function defaultFilter(query: string, item: TransferItem): boolean {\n return item.label.toLowerCase().includes(query.toLowerCase())\n }\n\n const filterFn = computed(() => props.filterMethod ?? defaultFilter)\n\n const filteredSourceList = computed(() => {\n const q = sourceQuery.value.trim()\n return q\n ? sourceList.value.filter(item => filterFn.value(q, item))\n : sourceList.value\n })\n\n const filteredTargetList = computed(() => {\n const q = targetQuery.value.trim()\n return q\n ? targetList.value.filter(item => filterFn.value(q, item))\n : targetList.value\n })\n\n // ==================== 选中态 ====================\n\n const sourceChecked = reactive(new Set<string | number>())\n const targetChecked = reactive(new Set<string | number>())\n\n /** 切换源列表项选中状态 */\n function toggleSourceCheck(key: string | number) {\n if (sourceChecked.has(key)) sourceChecked.delete(key)\n else sourceChecked.add(key)\n }\n\n /** 切换目标列表项选中状态 */\n function toggleTargetCheck(key: string | number) {\n if (targetChecked.has(key)) targetChecked.delete(key)\n else targetChecked.add(key)\n }\n\n const isSourceAllChecked = computed(\n () =>\n filteredSourceList.value.length > 0 &&\n filteredSourceList.value.every(\n i => i.disabled || sourceChecked.has(i.key)\n )\n )\n const isSourceIndeterminate = computed(\n () =>\n !isSourceAllChecked.value &&\n filteredSourceList.value.some(i => sourceChecked.has(i.key))\n )\n const isTargetAllChecked = computed(\n () =>\n filteredTargetList.value.length > 0 &&\n filteredTargetList.value.every(\n i => i.disabled || targetChecked.has(i.key)\n )\n )\n const isTargetIndeterminate = computed(\n () =>\n !isTargetAllChecked.value &&\n filteredTargetList.value.some(i => targetChecked.has(i.key))\n )\n\n /** 切换源列表全选状态 */\n function toggleSourceAll(checked: boolean) {\n for (const item of filteredSourceList.value) {\n if (item.disabled) continue\n if (checked) sourceChecked.add(item.key)\n else sourceChecked.delete(item.key)\n }\n }\n\n /** 切换目标列表全选状态 */\n function toggleTargetAll(checked: boolean) {\n for (const item of filteredTargetList.value) {\n if (item.disabled) continue\n if (checked) targetChecked.add(item.key)\n else targetChecked.delete(item.key)\n }\n }\n\n // ==================== 穿梭操作 ====================\n\n /** 将选中项移至目标列表 */\n function moveToTarget() {\n const keys = [...sourceChecked]\n const newVal = [...props.modelValue, ...keys]\n emit('update:modelValue', newVal)\n emit('change', newVal, 'right', keys)\n sourceChecked.clear()\n }\n\n /** 将选中项移回源列表 */\n function moveToSource() {\n const keys = [...targetChecked]\n const removeSet = new Set(keys)\n const newVal = props.modelValue.filter(k => !removeSet.has(k))\n emit('update:modelValue', newVal)\n emit('change', newVal, 'left', keys)\n targetChecked.clear()\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Description: 穿梭框组件\n-->\n<template>\n <div\n class=\"c-transfer\"\n :class=\"[`is-${mergedProps.size}`]\"\n >\n <!-- ================ Source Panel ================ -->\n <div class=\"c-transfer__panel\">\n <div class=\"c-transfer__header\">\n <NCheckbox\n v-if=\"mergedProps.showSelectAll\"\n :checked=\"isSourceAllChecked\"\n :indeterminate=\"isSourceIndeterminate\"\n @update:checked=\"toggleSourceAll\"\n >\n {{ mergedProps.titles?.[0] }}\n </NCheckbox>\n <span\n v-else\n class=\"c-transfer__title\"\n >\n {{ mergedProps.titles?.[0] }}\n </span>\n <span class=\"c-transfer__count\"\n >{{ sourceChecked.size }} / {{ sourceList.length }}</span\n >\n </div>\n\n <div\n v-if=\"mergedProps.filterable\"\n class=\"c-transfer__filter\"\n >\n <NInput\n v-model:value=\"sourceQuery\"\n clearable\n size=\"small\"\n :placeholder=\"mergedProps.filterPlaceholder\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n </div>\n\n <div\n v-if=\"filteredSourceList.length\"\n class=\"c-transfer__list\"\n >\n <template\n v-for=\"item in filteredSourceList\"\n :key=\"item.key\"\n >\n <div\n class=\"c-transfer__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-checked': sourceChecked.has(item.key),\n }\"\n @click=\"!item.disabled && toggleSourceCheck(item.key)\"\n >\n <NCheckbox\n :checked=\"sourceChecked.has(item.key)\"\n :disabled=\"item.disabled\"\n size=\"small\"\n @update:checked=\"toggleSourceCheck(item.key)\"\n @click.stop\n />\n <C_Icon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n class=\"c-transfer__item-icon\"\n />\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\n </div>\n </template>\n </div>\n <div\n v-else\n class=\"c-transfer__empty\"\n >\n {{ mergedProps.sourceEmptyText }}\n </div>\n </div>\n\n <!-- ================ Actions ================ -->\n <div class=\"c-transfer__actions\">\n <button\n class=\"c-transfer__btn\"\n :class=\"{ 'is-disabled': sourceChecked.size === 0 }\"\n :disabled=\"sourceChecked.size === 0\"\n @click=\"moveToTarget\"\n >\n <C_Icon name=\"mdi:chevron-right\" />\n </button>\n <button\n class=\"c-transfer__btn\"\n :class=\"{ 'is-disabled': targetChecked.size === 0 }\"\n :disabled=\"targetChecked.size === 0\"\n @click=\"moveToSource\"\n >\n <C_Icon name=\"mdi:chevron-left\" />\n </button>\n </div>\n\n <!-- ================ Target Panel ================ -->\n <div class=\"c-transfer__panel\">\n <div class=\"c-transfer__header\">\n <NCheckbox\n v-if=\"mergedProps.showSelectAll\"\n :checked=\"isTargetAllChecked\"\n :indeterminate=\"isTargetIndeterminate\"\n @update:checked=\"toggleTargetAll\"\n >\n {{ mergedProps.titles?.[1] }}\n </NCheckbox>\n <span\n v-else\n class=\"c-transfer__title\"\n >\n {{ mergedProps.titles?.[1] }}\n </span>\n <span class=\"c-transfer__count\"\n >{{ targetChecked.size }} / {{ targetList.length }}</span\n >\n </div>\n\n <div\n v-if=\"mergedProps.filterable\"\n class=\"c-transfer__filter\"\n >\n <NInput\n v-model:value=\"targetQuery\"\n clearable\n size=\"small\"\n :placeholder=\"mergedProps.filterPlaceholder\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n </div>\n\n <div\n v-if=\"filteredTargetList.length\"\n class=\"c-transfer__list\"\n >\n <template\n v-for=\"item in filteredTargetList\"\n :key=\"item.key\"\n >\n <div\n class=\"c-transfer__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-checked': targetChecked.has(item.key),\n }\"\n @click=\"!item.disabled && toggleTargetCheck(item.key)\"\n >\n <NCheckbox\n :checked=\"targetChecked.has(item.key)\"\n :disabled=\"item.disabled\"\n size=\"small\"\n @update:checked=\"toggleTargetCheck(item.key)\"\n @click.stop\n />\n <C_Icon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n class=\"c-transfer__item-icon\"\n />\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\n </div>\n </template>\n </div>\n <div\n v-else\n class=\"c-transfer__empty\"\n >\n {{ mergedProps.targetEmptyText }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, reactive, ref } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_TRANSFER_PROPS,\n type TransferItem,\n type TransferProps,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n data: TransferProps['data']\n modelValue: TransferProps['modelValue']\n titles?: TransferProps['titles']\n filterable?: boolean\n filterPlaceholder?: string\n filterMethod?: TransferProps['filterMethod']\n showSelectAll?: boolean\n sourceEmptyText?: string\n targetEmptyText?: string\n size?: TransferProps['size']\n }>(),\n {\n titles: () => DEFAULT_TRANSFER_PROPS.titles as [string, string],\n filterable: DEFAULT_TRANSFER_PROPS.filterable,\n filterPlaceholder: DEFAULT_TRANSFER_PROPS.filterPlaceholder,\n showSelectAll: DEFAULT_TRANSFER_PROPS.showSelectAll,\n sourceEmptyText: DEFAULT_TRANSFER_PROPS.sourceEmptyText,\n targetEmptyText: DEFAULT_TRANSFER_PROPS.targetEmptyText,\n size: DEFAULT_TRANSFER_PROPS.size,\n }\n )\n\n const emit = defineEmits<{\n 'update:modelValue': [value: Array<string | number>]\n change: [\n targetKeys: Array<string | number>,\n direction: 'left' | 'right',\n moveKeys: Array<string | number>,\n ]\n }>()\n\n const mergedProps = computed(() => ({ ...DEFAULT_TRANSFER_PROPS, ...props }))\n\n // ==================== 数据列表 ====================\n\n const dataMap = computed(() => {\n const m = new Map<string | number, TransferItem>()\n for (const item of props.data) m.set(item.key, item)\n return m\n })\n\n const targetKeySet = computed(() => new Set(props.modelValue))\n\n const sourceList = computed(() =>\n props.data.filter(d => !targetKeySet.value.has(d.key))\n )\n\n const targetList = computed(() => {\n const result: TransferItem[] = []\n for (const key of props.modelValue) {\n const item = dataMap.value.get(key)\n if (item) result.push(item)\n }\n return result\n })\n\n // ==================== 搜索 ====================\n\n const sourceQuery = ref('')\n const targetQuery = ref('')\n\n /** 默认过滤方法 */\n function defaultFilter(query: string, item: TransferItem): boolean {\n return item.label.toLowerCase().includes(query.toLowerCase())\n }\n\n const filterFn = computed(() => props.filterMethod ?? defaultFilter)\n\n const filteredSourceList = computed(() => {\n const q = sourceQuery.value.trim()\n return q\n ? sourceList.value.filter(item => filterFn.value(q, item))\n : sourceList.value\n })\n\n const filteredTargetList = computed(() => {\n const q = targetQuery.value.trim()\n return q\n ? targetList.value.filter(item => filterFn.value(q, item))\n : targetList.value\n })\n\n // ==================== 选中态 ====================\n\n const sourceChecked = reactive(new Set<string | number>())\n const targetChecked = reactive(new Set<string | number>())\n\n /** 切换源列表项选中状态 */\n function toggleSourceCheck(key: string | number) {\n if (sourceChecked.has(key)) sourceChecked.delete(key)\n else sourceChecked.add(key)\n }\n\n /** 切换目标列表项选中状态 */\n function toggleTargetCheck(key: string | number) {\n if (targetChecked.has(key)) targetChecked.delete(key)\n else targetChecked.add(key)\n }\n\n const isSourceAllChecked = computed(\n () =>\n filteredSourceList.value.length > 0 &&\n filteredSourceList.value.every(\n i => i.disabled || sourceChecked.has(i.key)\n )\n )\n const isSourceIndeterminate = computed(\n () =>\n !isSourceAllChecked.value &&\n filteredSourceList.value.some(i => sourceChecked.has(i.key))\n )\n const isTargetAllChecked = computed(\n () =>\n filteredTargetList.value.length > 0 &&\n filteredTargetList.value.every(\n i => i.disabled || targetChecked.has(i.key)\n )\n )\n const isTargetIndeterminate = computed(\n () =>\n !isTargetAllChecked.value &&\n filteredTargetList.value.some(i => targetChecked.has(i.key))\n )\n\n /** 切换源列表全选状态 */\n function toggleSourceAll(checked: boolean) {\n for (const item of filteredSourceList.value) {\n if (item.disabled) continue\n if (checked) sourceChecked.add(item.key)\n else sourceChecked.delete(item.key)\n }\n }\n\n /** 切换目标列表全选状态 */\n function toggleTargetAll(checked: boolean) {\n for (const item of filteredTargetList.value) {\n if (item.disabled) continue\n if (checked) targetChecked.add(item.key)\n else targetChecked.delete(item.key)\n }\n }\n\n // ==================== 穿梭操作 ====================\n\n /** 将选中项移至目标列表 */\n function moveToTarget() {\n const keys = [...sourceChecked]\n const newVal = [...props.modelValue, ...keys]\n emit('update:modelValue', newVal)\n emit('change', newVal, 'right', keys)\n sourceChecked.clear()\n }\n\n /** 将选中项移回源列表 */\n function moveToSource() {\n const keys = [...targetChecked]\n const removeSet = new Set(keys)\n const newVal = props.modelValue.filter(k => !removeSet.has(k))\n emit('update:modelValue', newVal)\n emit('change', newVal, 'left', keys)\n targetChecked.clear()\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"C_Tree-
|
|
1
|
+
{"version":3,"file":"C_Tree-D98nX9qT.css","names":[],"sources":["../src/components/C_Tree/index.vue?vue&type=style&index=0&scoped=56828099&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-tree[data-v-56828099] {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n.c-tree .c-tree-toolbar[data-v-56828099] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: #fafafa;\n border-radius: 6px;\n margin-bottom: 12px;\n flex-shrink: 0;\n}\n.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-56828099] {\n width: 260px;\n}\n.c-tree .c-tree-toolbar .toolbar-right[data-v-56828099] {\n display: flex;\n gap: 8px;\n}\n.c-tree .c-tree-container[data-v-56828099] {\n flex: 1;\n min-height: 0;\n border: 1px solid #e0e0e6;\n border-radius: 6px;\n padding: 12px;\n overflow-y: auto;\n scrollbar-width: thin;\n scrollbar-color: rgba(0, 0, 0, 0.12) transparent;\n}\n.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar {\n width: 6px;\n}\n.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.12);\n border-radius: 3px;\n transition: background 0.2s;\n}\n.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.25);\n}\n.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar-track {\n background: transparent;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node {\n margin-bottom: 4px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-wrapper {\n padding: 2px 0;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content {\n padding: 12px 8px;\n border-radius: 6px;\n transition: all 0.2s ease;\n align-items: center;\n min-height: 44px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content:hover {\n background-color: #f0f9ff;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__prefix {\n margin-right: 0;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__text {\n flex: 1;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__suffix {\n margin-left: auto;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-switcher {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-right: 8px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-switcher .n-tree-node-switcher__icon {\n font-size: 14px;\n transition: transform 0.2s ease;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node.n-tree-node--selected .n-tree-node-content {\n background-color: #e6f3ff;\n border: 1px solid #1890ff;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node.n-tree-node--disabled .n-tree-node-content {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node-indent {\n width: 24px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree-node:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .tree-actions {\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .tree-actions:hover {\n opacity: 1 !important;\n}\n.c-tree[data-v-56828099] .n-tree-node-content:hover .tree-actions,\n.c-tree[data-v-56828099] .n-tree-node:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree[data-v-56828099] .tree-actions {\n opacity: 0;\n transition: opacity 0.2s ease-in-out;\n}\n\n/* responsive */\n@media (max-width: 768px) {\n.c-tree .c-tree-toolbar[data-v-56828099] {\n flex-direction: column;\n gap: 12px;\n}\n.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-56828099] {\n width: 100%;\n}\n.c-tree .c-tree-toolbar .toolbar-right[data-v-56828099] {\n width: 100%;\n justify-content: center;\n}\n.c-tree[data-v-56828099] .tree-actions {\n opacity: 1 !important;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"C_Tree-
|
|
1
|
+
{"version":3,"file":"C_Tree-ulfm7b-P.css","names":[],"sources":["../src/components/C_Tree/index.vue?vue&type=style&index=0&scoped=56828099&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-tree[data-v-56828099] {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n.c-tree .c-tree-toolbar[data-v-56828099] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: #fafafa;\n border-radius: 6px;\n margin-bottom: 12px;\n flex-shrink: 0;\n}\n.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-56828099] {\n width: 260px;\n}\n.c-tree .c-tree-toolbar .toolbar-right[data-v-56828099] {\n display: flex;\n gap: 8px;\n}\n.c-tree .c-tree-container[data-v-56828099] {\n flex: 1;\n min-height: 0;\n border: 1px solid #e0e0e6;\n border-radius: 6px;\n padding: 12px;\n overflow-y: auto;\n scrollbar-width: thin;\n scrollbar-color: rgba(0, 0, 0, 0.12) transparent;\n}\n.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar {\n width: 6px;\n}\n.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.12);\n border-radius: 3px;\n transition: background 0.2s;\n}\n.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.25);\n}\n.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar-track {\n background: transparent;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node {\n margin-bottom: 4px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-wrapper {\n padding: 2px 0;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content {\n padding: 12px 8px;\n border-radius: 6px;\n transition: all 0.2s ease;\n align-items: center;\n min-height: 44px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content:hover {\n background-color: #f0f9ff;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__prefix {\n margin-right: 0;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__text {\n flex: 1;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__suffix {\n margin-left: auto;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-switcher {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-right: 8px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-switcher .n-tree-node-switcher__icon {\n font-size: 14px;\n transition: transform 0.2s ease;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node.n-tree-node--selected .n-tree-node-content {\n background-color: #e6f3ff;\n border: 1px solid #1890ff;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node.n-tree-node--disabled .n-tree-node-content {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node-indent {\n width: 24px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree-node:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .tree-actions {\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n.c-tree .c-tree-container .tree-instance[data-v-56828099] .tree-actions:hover {\n opacity: 1 !important;\n}\n.c-tree[data-v-56828099] .n-tree-node-content:hover .tree-actions,\n.c-tree[data-v-56828099] .n-tree-node:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree[data-v-56828099] .tree-actions {\n opacity: 0;\n transition: opacity 0.2s ease-in-out;\n}\n\n/* responsive */\n@media (max-width: 768px) {\n.c-tree .c-tree-toolbar[data-v-56828099] {\n flex-direction: column;\n gap: 12px;\n}\n.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-56828099] {\n width: 100%;\n}\n.c-tree .c-tree-toolbar .toolbar-right[data-v-56828099] {\n width: 100%;\n justify-content: center;\n}\n.c-tree[data-v-56828099] .tree-actions {\n opacity: 1 !important;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
package/dist/C_Tree.css
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/* unplugin-vue-components disabled */.c-tree[data-v-
|
|
1
|
+
/* unplugin-vue-components disabled */.c-tree[data-v-56828099] {
|
|
2
2
|
display: flex;
|
|
3
3
|
flex-direction: column;
|
|
4
4
|
height: 100%;
|
|
5
5
|
}
|
|
6
|
-
.c-tree .c-tree-toolbar[data-v-
|
|
6
|
+
.c-tree .c-tree-toolbar[data-v-56828099] {
|
|
7
7
|
display: flex;
|
|
8
8
|
justify-content: space-between;
|
|
9
9
|
align-items: center;
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
margin-bottom: 12px;
|
|
14
14
|
flex-shrink: 0;
|
|
15
15
|
}
|
|
16
|
-
.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-
|
|
16
|
+
.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-56828099] {
|
|
17
17
|
width: 260px;
|
|
18
18
|
}
|
|
19
|
-
.c-tree .c-tree-toolbar .toolbar-right[data-v-
|
|
19
|
+
.c-tree .c-tree-toolbar .toolbar-right[data-v-56828099] {
|
|
20
20
|
display: flex;
|
|
21
21
|
gap: 8px;
|
|
22
22
|
}
|
|
23
|
-
.c-tree .c-tree-container[data-v-
|
|
23
|
+
.c-tree .c-tree-container[data-v-56828099] {
|
|
24
24
|
flex: 1;
|
|
25
25
|
min-height: 0;
|
|
26
26
|
border: 1px solid #e0e0e6;
|
|
@@ -30,56 +30,56 @@
|
|
|
30
30
|
scrollbar-width: thin;
|
|
31
31
|
scrollbar-color: rgba(0, 0, 0, 0.12) transparent;
|
|
32
32
|
}
|
|
33
|
-
.c-tree .c-tree-container[data-v-
|
|
33
|
+
.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar {
|
|
34
34
|
width: 6px;
|
|
35
35
|
}
|
|
36
|
-
.c-tree .c-tree-container[data-v-
|
|
36
|
+
.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar-thumb {
|
|
37
37
|
background: rgba(0, 0, 0, 0.12);
|
|
38
38
|
border-radius: 3px;
|
|
39
39
|
transition: background 0.2s;
|
|
40
40
|
}
|
|
41
|
-
.c-tree .c-tree-container[data-v-
|
|
41
|
+
.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar-thumb:hover {
|
|
42
42
|
background: rgba(0, 0, 0, 0.25);
|
|
43
43
|
}
|
|
44
|
-
.c-tree .c-tree-container[data-v-
|
|
44
|
+
.c-tree .c-tree-container[data-v-56828099]::-webkit-scrollbar-track {
|
|
45
45
|
background: transparent;
|
|
46
46
|
}
|
|
47
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
47
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node {
|
|
48
48
|
margin-bottom: 4px;
|
|
49
49
|
}
|
|
50
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
50
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-wrapper {
|
|
51
51
|
padding: 2px 0;
|
|
52
52
|
}
|
|
53
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
53
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content {
|
|
54
54
|
padding: 12px 8px;
|
|
55
55
|
border-radius: 6px;
|
|
56
56
|
transition: all 0.2s ease;
|
|
57
57
|
align-items: center;
|
|
58
58
|
min-height: 44px;
|
|
59
59
|
}
|
|
60
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
60
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content:hover {
|
|
61
61
|
background-color: #f0f9ff;
|
|
62
62
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
63
63
|
}
|
|
64
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
64
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content:hover .tree-actions {
|
|
65
65
|
opacity: 1 !important;
|
|
66
66
|
}
|
|
67
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
67
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__prefix {
|
|
68
68
|
margin-right: 0;
|
|
69
69
|
display: flex;
|
|
70
70
|
align-items: center;
|
|
71
71
|
}
|
|
72
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
72
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__text {
|
|
73
73
|
flex: 1;
|
|
74
74
|
display: flex;
|
|
75
75
|
align-items: center;
|
|
76
76
|
}
|
|
77
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
77
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__suffix {
|
|
78
78
|
margin-left: auto;
|
|
79
79
|
display: flex;
|
|
80
80
|
align-items: center;
|
|
81
81
|
}
|
|
82
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
82
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-switcher {
|
|
83
83
|
width: 24px;
|
|
84
84
|
height: 24px;
|
|
85
85
|
display: flex;
|
|
@@ -87,56 +87,56 @@
|
|
|
87
87
|
justify-content: center;
|
|
88
88
|
margin-right: 8px;
|
|
89
89
|
}
|
|
90
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
90
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node .n-tree-node-switcher .n-tree-node-switcher__icon {
|
|
91
91
|
font-size: 14px;
|
|
92
92
|
transition: transform 0.2s ease;
|
|
93
93
|
}
|
|
94
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
94
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node.n-tree-node--selected .n-tree-node-content {
|
|
95
95
|
background-color: #e6f3ff;
|
|
96
96
|
border: 1px solid #1890ff;
|
|
97
97
|
}
|
|
98
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
98
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node.n-tree-node--disabled .n-tree-node-content {
|
|
99
99
|
opacity: 0.6;
|
|
100
100
|
cursor: not-allowed;
|
|
101
101
|
}
|
|
102
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
102
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree .n-tree-node-indent {
|
|
103
103
|
width: 24px;
|
|
104
104
|
}
|
|
105
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
105
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .n-tree-node:hover .tree-actions {
|
|
106
106
|
opacity: 1 !important;
|
|
107
107
|
}
|
|
108
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
108
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .tree-actions {
|
|
109
109
|
opacity: 0;
|
|
110
110
|
transition: opacity 0.2s ease;
|
|
111
111
|
}
|
|
112
|
-
.c-tree .c-tree-container .tree-instance[data-v-
|
|
112
|
+
.c-tree .c-tree-container .tree-instance[data-v-56828099] .tree-actions:hover {
|
|
113
113
|
opacity: 1 !important;
|
|
114
114
|
}
|
|
115
|
-
.c-tree[data-v-
|
|
116
|
-
.c-tree[data-v-
|
|
115
|
+
.c-tree[data-v-56828099] .n-tree-node-content:hover .tree-actions,
|
|
116
|
+
.c-tree[data-v-56828099] .n-tree-node:hover .tree-actions {
|
|
117
117
|
opacity: 1 !important;
|
|
118
118
|
}
|
|
119
|
-
.c-tree[data-v-
|
|
119
|
+
.c-tree[data-v-56828099] .tree-actions {
|
|
120
120
|
opacity: 0;
|
|
121
121
|
transition: opacity 0.2s ease-in-out;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/* responsive */
|
|
125
125
|
@media (max-width: 768px) {
|
|
126
|
-
.c-tree .c-tree-toolbar[data-v-
|
|
126
|
+
.c-tree .c-tree-toolbar[data-v-56828099] {
|
|
127
127
|
flex-direction: column;
|
|
128
128
|
gap: 12px;
|
|
129
129
|
}
|
|
130
|
-
.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-
|
|
130
|
+
.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-56828099] {
|
|
131
131
|
width: 100%;
|
|
132
132
|
}
|
|
133
|
-
.c-tree .c-tree-toolbar .toolbar-right[data-v-
|
|
133
|
+
.c-tree .c-tree-toolbar .toolbar-right[data-v-56828099] {
|
|
134
134
|
width: 100%;
|
|
135
135
|
justify-content: center;
|
|
136
136
|
}
|
|
137
|
-
.c-tree[data-v-
|
|
137
|
+
.c-tree[data-v-56828099] .tree-actions {
|
|
138
138
|
opacity: 1 !important;
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
/*# sourceMappingURL=C_Tree-
|
|
142
|
+
/*# sourceMappingURL=C_Tree-D98nX9qT.css.map*/
|
package/dist/C_Tree.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"C_Tree.js","names":["C_Icon","NTag","NPopconfirm","NButton","NSpace","showToolbar","searchable","searchPlaceholder","addable","addText","refreshable","draggable","showLine","keyField","labelField","childrenField"],"sources":["../src/components/C_Tree/data.ts","../src/components/C_Tree/composables/useTreeOperations.ts","../src/components/C_Tree/index.vue","../src/components/C_Tree/index.vue","../src/components/C_Tree/index.vue"],"sourcesContent":["import type { TreeMode, TreeProps } from \"./types\";\r\n\r\nexport const presetConfigs: Record<TreeMode, Partial<TreeProps>> = {\r\n menu: {\r\n draggable: true,\r\n showLine: true,\r\n iconConfig: {\r\n typeMap: {\r\n directory: \"mdi:folder\",\r\n menu: \"mdi:file-document\",\r\n button: \"mdi:button-cursor\",\r\n },\r\n },\r\n actions: [\r\n { key: \"add\", text: \"新增子菜单\", icon: \"mdi:plus\", type: \"primary\" },\r\n { key: \"edit\", text: \"编辑\", icon: \"mdi:pencil\", type: \"info\" },\r\n {\r\n key: \"delete\",\r\n text: \"删除\",\r\n icon: \"mdi:delete\",\r\n type: \"error\",\r\n confirm: \"确认删除该菜单吗?\",\r\n },\r\n ],\r\n },\r\n file: {\r\n draggable: false,\r\n showLine: false,\r\n iconConfig: {\r\n typeMap: {\r\n folder: \"mdi:folder\",\r\n file: \"mdi:file-document\",\r\n image: \"mdi:file-image\",\r\n video: \"mdi:file-video\",\r\n audio: \"mdi:file-music\",\r\n },\r\n },\r\n actions: [\r\n { key: \"open\", text: \"打开\", icon: \"mdi:folder-open\", type: \"primary\" },\r\n { key: \"rename\", text: \"重命名\", icon: \"mdi:rename-box\", type: \"info\" },\r\n {\r\n key: \"delete\",\r\n text: \"删除\",\r\n icon: \"mdi:delete\",\r\n type: \"error\",\r\n confirm: \"确认删除该文件吗?\",\r\n },\r\n ],\r\n },\r\n org: {\r\n draggable: false,\r\n showLine: true,\r\n iconConfig: {\r\n typeMap: {\r\n department: \"mdi:domain\",\r\n user: \"mdi:account\",\r\n manager: \"mdi:account-tie\",\r\n },\r\n },\r\n actions: [\r\n { key: \"add\", text: \"新增下级\", icon: \"mdi:plus\", type: \"primary\" },\r\n { key: \"edit\", text: \"编辑\", icon: \"mdi:pencil\", type: \"info\" },\r\n ],\r\n },\r\n custom: {},\r\n};\r\n","import { ref, computed, h } from \"vue\";\r\nimport { NTag, NButton, NPopconfirm, NSpace } from \"naive-ui\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport { presetConfigs } from \"../data\";\r\nimport type {\r\n TreeOption,\r\n TreeNodeData,\r\n TreeProps,\r\n TreeExpose,\r\n DropInfo,\r\n} from \"../types\";\r\n\r\ntype EmitFn = {\r\n (\r\n event: \"node-select\",\r\n node: TreeNodeData | null,\r\n selectedKeys: (string | number)[],\r\n ): void;\r\n (event: \"node-action\", action: string, node: TreeNodeData): void;\r\n (event: \"node-drop\", info: DropInfo): void;\r\n (event: \"add\", parentNode?: TreeNodeData): void;\r\n (event: \"refresh\"): void;\r\n};\r\n\r\nexport function useTreeOperations(props: TreeProps, emit: EmitFn) {\r\n const internalSearchPattern = ref(\"\");\r\n const expandedKeys = ref<(string | number)[]>(\r\n props.defaultExpandedKeys ?? [],\r\n );\r\n const selectedKeys = ref<(string | number)[]>(\r\n props.defaultSelectedKeys ?? [],\r\n );\r\n const isAllExpanded = ref(props.defaultExpandAll ?? false);\r\n\r\n const keyField = computed(() => props.keyField ?? \"id\");\r\n const labelField = computed(() => props.labelField ?? \"name\");\r\n const childrenField = computed(() => props.childrenField ?? \"children\");\r\n const iconField = computed(() => props.iconField ?? \"icon\");\r\n\r\n const mergedConfig = computed(() => {\r\n const preset = presetConfigs[props.mode ?? \"custom\"] || {};\r\n return {\r\n ...preset,\r\n ...props,\r\n iconConfig: { ...preset.iconConfig, ...props.iconConfig },\r\n actions: props.actions?.length ? props.actions : preset.actions || [],\r\n };\r\n });\r\n\r\n const treeData = computed((): TreeOption[] => props.data as TreeOption[]);\r\n\r\n const currentSearchPattern = computed(\r\n () => props.searchPattern || internalSearchPattern.value,\r\n );\r\n\r\n const selectedNode = computed((): TreeNodeData | null => {\r\n if (selectedKeys.value.length === 0) return null;\r\n const findNode = (\r\n nodes: TreeNodeData[],\r\n id: string | number,\r\n ): TreeNodeData | null => {\r\n for (const node of nodes) {\r\n if (node[keyField.value] === id) return node;\r\n if (node[childrenField.value]) {\r\n const found = findNode(node[childrenField.value], id);\r\n if (found) return found;\r\n }\r\n }\r\n return null;\r\n };\r\n return findNode(props.data, selectedKeys.value[0]);\r\n });\r\n\r\n const getAllKeys = (nodes: TreeNodeData[]): (string | number)[] => {\r\n const keys: (string | number)[] = [];\r\n nodes.forEach((node) => {\r\n keys.push(node[keyField.value]);\r\n if (node[childrenField.value]) {\r\n keys.push(...getAllKeys(node[childrenField.value]));\r\n }\r\n });\r\n return keys;\r\n };\r\n\r\n const getNodeIcon = (node: TreeNodeData): string => {\r\n const config = mergedConfig.value.iconConfig!;\r\n if (node[iconField.value]) return node[iconField.value];\r\n if (node.type && config.typeMap?.[node.type])\r\n return config.typeMap[node.type];\r\n return config.default || \"mdi:circle-outline\";\r\n };\r\n\r\n const getNodeIconColor = (node: TreeNodeData): string => {\r\n const config = mergedConfig.value.iconConfig!;\r\n if (node.type && config.colorMap?.[node.type])\r\n return config.colorMap[node.type];\r\n return \"currentColor\";\r\n };\r\n\r\n const renderPrefix = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n return h(C_Icon, {\r\n name: getNodeIcon(node),\r\n size: 18,\r\n color: getNodeIconColor(node),\r\n class: \"mr-3 flex-shrink-0\",\r\n });\r\n };\r\n\r\n const renderLabel = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n const statusTags =\r\n props.statusConfigs\r\n ?.map((config) => {\r\n const value = node[config.field];\r\n const statusConfig = config.values[value];\r\n if (!statusConfig?.text) return null;\r\n return h(\r\n NTag,\r\n {\r\n type: statusConfig.type,\r\n size: \"small\",\r\n class: \"ml-2\",\r\n style: { fontSize: \"12px\" },\r\n },\r\n { default: () => statusConfig.text },\r\n );\r\n })\r\n .filter(Boolean) || [];\r\n\r\n return h(\r\n \"div\",\r\n {\r\n class: \"flex items-center flex-1 min-w-0 py-1\",\r\n style: { lineHeight: \"1.5\" },\r\n },\r\n [\r\n h(\r\n \"span\",\r\n { class: \"mr-3 font-medium text-sm\" },\r\n node[labelField.value],\r\n ),\r\n ...statusTags,\r\n ],\r\n );\r\n };\r\n\r\n const renderSuffix = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n const actions = mergedConfig.value.actions || [];\r\n\r\n const actionButtons = actions\r\n .filter((action) => !action.show || action.show(node))\r\n .map((action) => {\r\n const buttonProps = {\r\n size: \"tiny\" as const,\r\n type: action.type || (\"default\" as const),\r\n secondary: true,\r\n style: { padding: \"4px 6px\", minWidth: \"24px\", height: \"24px\" },\r\n onClick: (e: Event) => {\r\n e.stopPropagation();\r\n handleNodeAction(action.key, node);\r\n },\r\n };\r\n const iconEl = h(C_Icon, {\r\n name: action.icon,\r\n size: 12,\r\n title: action.text,\r\n });\r\n\r\n if (action.confirm) {\r\n return h(\r\n NPopconfirm,\r\n { onPositiveClick: () => handleNodeAction(action.key, node) },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n ...buttonProps,\r\n onClick: (e: Event) => e.stopPropagation(),\r\n },\r\n { icon: () => iconEl },\r\n ),\r\n default: () => action.confirm,\r\n },\r\n );\r\n }\r\n return h(NButton, buttonProps, { icon: () => iconEl });\r\n });\r\n\r\n return h(\r\n \"div\",\r\n {\r\n class: \"tree-actions\",\r\n style: {\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n marginLeft: \"8px\",\r\n opacity: \"0\",\r\n transition: \"opacity 0.2s ease\",\r\n },\r\n },\r\n [h(NSpace, { size: 4 }, { default: () => actionButtons })],\r\n );\r\n };\r\n\r\n const toggleExpandAll = (): void => {\r\n if (isAllExpanded.value) {\r\n expandedKeys.value = [];\r\n isAllExpanded.value = false;\r\n } else {\r\n expandedKeys.value = getAllKeys(props.data);\r\n isAllExpanded.value = true;\r\n }\r\n };\r\n\r\n const handleExpandedKeysChange = (keys: (string | number)[]): void => {\r\n expandedKeys.value = keys;\r\n isAllExpanded.value = keys.length === getAllKeys(props.data).length;\r\n };\r\n\r\n const handleSelectedKeysChange = (keys: (string | number)[]): void => {\r\n selectedKeys.value = keys;\r\n emit(\"node-select\", selectedNode.value, keys);\r\n };\r\n\r\n const handleDrop = (info: DropInfo): void => emit(\"node-drop\", info);\r\n\r\n const handleNodeAction = (action: string, node: TreeNodeData): void => {\r\n if (action === \"add\") {\r\n emit(\"add\", node);\r\n } else {\r\n emit(\"node-action\", action, node);\r\n }\r\n };\r\n\r\n const handleAdd = (): void => emit(\"add\");\r\n const handleRefresh = (): void => emit(\"refresh\");\r\n\r\n const expose: TreeExpose = {\r\n expandAll: () => {\r\n expandedKeys.value = getAllKeys(props.data);\r\n isAllExpanded.value = true;\r\n },\r\n collapseAll: () => {\r\n expandedKeys.value = [];\r\n isAllExpanded.value = false;\r\n },\r\n selectNode: (key: string | number) => {\r\n selectedKeys.value = [key];\r\n },\r\n getSelectedNode: () => selectedNode.value,\r\n expandedKeys,\r\n selectedKeys,\r\n };\r\n\r\n return {\r\n internalSearchPattern,\r\n expandedKeys,\r\n selectedKeys,\r\n isAllExpanded,\r\n treeData,\r\n currentSearchPattern,\r\n renderPrefix,\r\n renderLabel,\r\n renderSuffix,\r\n toggleExpandAll,\r\n handleExpandedKeysChange,\r\n handleSelectedKeysChange,\r\n handleDrop,\r\n handleAdd,\r\n handleRefresh,\r\n expose,\r\n };\r\n}\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-27\r\n * @Description: 树型组件 — 薄 UI 壳,逻辑由 useTreeOperations 驱动\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-tree\">\r\n <div v-if=\"showToolbar\" class=\"c-tree-toolbar\">\r\n <div class=\"toolbar-left\">\r\n <NInput\r\n v-if=\"searchable\"\r\n v-model:value=\"internalSearchPattern\"\r\n :placeholder=\"searchPlaceholder\"\r\n clearable\r\n class=\"search-input\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\r\n </template>\r\n </NInput>\r\n </div>\r\n <div class=\"toolbar-right\">\r\n <slot name=\"toolbar-actions\">\r\n <NButton v-if=\"addable\" type=\"primary\" @click=\"handleAdd()\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n {{ addText }}\r\n </NButton>\r\n <NButton @click=\"toggleExpandAll\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:file-tree\" :size=\"16\" />\r\n </template>\r\n {{ isAllExpanded ? \"收起全部\" : \"展开全部\" }}\r\n </NButton>\r\n <NButton v-if=\"refreshable\" @click=\"handleRefresh\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:refresh\" :size=\"16\" />\r\n </template>\r\n 刷新\r\n </NButton>\r\n </slot>\r\n </div>\r\n </div>\r\n\r\n <div class=\"c-tree-container\">\r\n <NTree\r\n :data=\"treeData\"\r\n :pattern=\"currentSearchPattern\"\r\n :expanded-keys=\"expandedKeys\"\r\n :selected-keys=\"selectedKeys\"\r\n :draggable=\"draggable\"\r\n :show-line=\"showLine\"\r\n :render-prefix=\"renderPrefix\"\r\n :render-suffix=\"renderSuffix\"\r\n :render-label=\"renderLabel\"\r\n :key-field=\"keyField\"\r\n :label-field=\"labelField\"\r\n :children-field=\"childrenField\"\r\n @update:expanded-keys=\"handleExpandedKeysChange\"\r\n @update:selected-keys=\"handleSelectedKeysChange\"\r\n @drop=\"handleDrop\"\r\n class=\"tree-instance\"\r\n />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { NInput, NButton, NTree } from \"naive-ui\";\r\nimport C_Icon from \"../C_Icon/index.vue\";\r\nimport { useTreeOperations } from \"./composables/useTreeOperations\";\r\nimport type { TreeProps, TreeEmits, TreeExpose } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_Tree\" });\r\n\r\nconst props = withDefaults(defineProps<TreeProps>(), {\r\n mode: \"custom\",\r\n keyField: \"id\",\r\n labelField: \"name\",\r\n childrenField: \"children\",\r\n searchPattern: \"\",\r\n searchable: true,\r\n searchPlaceholder: \"搜索...\",\r\n draggable: false,\r\n showLine: true,\r\n showToolbar: true,\r\n addable: true,\r\n addText: \"新增\",\r\n refreshable: true,\r\n iconField: \"icon\",\r\n iconConfig: () => ({\r\n default: \"mdi:circle-outline\",\r\n typeMap: {},\r\n colorMap: {},\r\n }),\r\n statusConfigs: () => [],\r\n actions: () => [],\r\n defaultExpandAll: false,\r\n defaultExpandedKeys: () => [],\r\n defaultSelectedKeys: () => [],\r\n});\r\n\r\nconst emit = defineEmits<TreeEmits>();\r\n\r\nconst {\r\n internalSearchPattern,\r\n expandedKeys,\r\n selectedKeys,\r\n isAllExpanded,\r\n treeData,\r\n currentSearchPattern,\r\n renderPrefix,\r\n renderLabel,\r\n renderSuffix,\r\n toggleExpandAll,\r\n handleExpandedKeysChange,\r\n handleSelectedKeysChange,\r\n handleDrop,\r\n handleAdd,\r\n handleRefresh,\r\n expose,\r\n} = useTreeOperations(props, emit);\r\n\r\ndefineExpose<TreeExpose>(expose);\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-27\r\n * @Description: 树型组件 — 薄 UI 壳,逻辑由 useTreeOperations 驱动\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-tree\">\r\n <div v-if=\"showToolbar\" class=\"c-tree-toolbar\">\r\n <div class=\"toolbar-left\">\r\n <NInput\r\n v-if=\"searchable\"\r\n v-model:value=\"internalSearchPattern\"\r\n :placeholder=\"searchPlaceholder\"\r\n clearable\r\n class=\"search-input\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\r\n </template>\r\n </NInput>\r\n </div>\r\n <div class=\"toolbar-right\">\r\n <slot name=\"toolbar-actions\">\r\n <NButton v-if=\"addable\" type=\"primary\" @click=\"handleAdd()\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n {{ addText }}\r\n </NButton>\r\n <NButton @click=\"toggleExpandAll\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:file-tree\" :size=\"16\" />\r\n </template>\r\n {{ isAllExpanded ? \"收起全部\" : \"展开全部\" }}\r\n </NButton>\r\n <NButton v-if=\"refreshable\" @click=\"handleRefresh\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:refresh\" :size=\"16\" />\r\n </template>\r\n 刷新\r\n </NButton>\r\n </slot>\r\n </div>\r\n </div>\r\n\r\n <div class=\"c-tree-container\">\r\n <NTree\r\n :data=\"treeData\"\r\n :pattern=\"currentSearchPattern\"\r\n :expanded-keys=\"expandedKeys\"\r\n :selected-keys=\"selectedKeys\"\r\n :draggable=\"draggable\"\r\n :show-line=\"showLine\"\r\n :render-prefix=\"renderPrefix\"\r\n :render-suffix=\"renderSuffix\"\r\n :render-label=\"renderLabel\"\r\n :key-field=\"keyField\"\r\n :label-field=\"labelField\"\r\n :children-field=\"childrenField\"\r\n @update:expanded-keys=\"handleExpandedKeysChange\"\r\n @update:selected-keys=\"handleSelectedKeysChange\"\r\n @drop=\"handleDrop\"\r\n class=\"tree-instance\"\r\n />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { NInput, NButton, NTree } from \"naive-ui\";\r\nimport C_Icon from \"../C_Icon/index.vue\";\r\nimport { useTreeOperations } from \"./composables/useTreeOperations\";\r\nimport type { TreeProps, TreeEmits, TreeExpose } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_Tree\" });\r\n\r\nconst props = withDefaults(defineProps<TreeProps>(), {\r\n mode: \"custom\",\r\n keyField: \"id\",\r\n labelField: \"name\",\r\n childrenField: \"children\",\r\n searchPattern: \"\",\r\n searchable: true,\r\n searchPlaceholder: \"搜索...\",\r\n draggable: false,\r\n showLine: true,\r\n showToolbar: true,\r\n addable: true,\r\n addText: \"新增\",\r\n refreshable: true,\r\n iconField: \"icon\",\r\n iconConfig: () => ({\r\n default: \"mdi:circle-outline\",\r\n typeMap: {},\r\n colorMap: {},\r\n }),\r\n statusConfigs: () => [],\r\n actions: () => [],\r\n defaultExpandAll: false,\r\n defaultExpandedKeys: () => [],\r\n defaultSelectedKeys: () => [],\r\n});\r\n\r\nconst emit = defineEmits<TreeEmits>();\r\n\r\nconst {\r\n internalSearchPattern,\r\n expandedKeys,\r\n selectedKeys,\r\n isAllExpanded,\r\n treeData,\r\n currentSearchPattern,\r\n renderPrefix,\r\n renderLabel,\r\n renderSuffix,\r\n toggleExpandAll,\r\n handleExpandedKeysChange,\r\n handleSelectedKeysChange,\r\n handleDrop,\r\n handleAdd,\r\n handleRefresh,\r\n expose,\r\n} = useTreeOperations(props, emit);\r\n\r\ndefineExpose<TreeExpose>(expose);\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-27\r\n * @Description: 树型组件 — 薄 UI 壳,逻辑由 useTreeOperations 驱动\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-tree\">\r\n <div v-if=\"showToolbar\" class=\"c-tree-toolbar\">\r\n <div class=\"toolbar-left\">\r\n <NInput\r\n v-if=\"searchable\"\r\n v-model:value=\"internalSearchPattern\"\r\n :placeholder=\"searchPlaceholder\"\r\n clearable\r\n class=\"search-input\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\r\n </template>\r\n </NInput>\r\n </div>\r\n <div class=\"toolbar-right\">\r\n <slot name=\"toolbar-actions\">\r\n <NButton v-if=\"addable\" type=\"primary\" @click=\"handleAdd()\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n {{ addText }}\r\n </NButton>\r\n <NButton @click=\"toggleExpandAll\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:file-tree\" :size=\"16\" />\r\n </template>\r\n {{ isAllExpanded ? \"收起全部\" : \"展开全部\" }}\r\n </NButton>\r\n <NButton v-if=\"refreshable\" @click=\"handleRefresh\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:refresh\" :size=\"16\" />\r\n </template>\r\n 刷新\r\n </NButton>\r\n </slot>\r\n </div>\r\n </div>\r\n\r\n <div class=\"c-tree-container\">\r\n <NTree\r\n :data=\"treeData\"\r\n :pattern=\"currentSearchPattern\"\r\n :expanded-keys=\"expandedKeys\"\r\n :selected-keys=\"selectedKeys\"\r\n :draggable=\"draggable\"\r\n :show-line=\"showLine\"\r\n :render-prefix=\"renderPrefix\"\r\n :render-suffix=\"renderSuffix\"\r\n :render-label=\"renderLabel\"\r\n :key-field=\"keyField\"\r\n :label-field=\"labelField\"\r\n :children-field=\"childrenField\"\r\n @update:expanded-keys=\"handleExpandedKeysChange\"\r\n @update:selected-keys=\"handleSelectedKeysChange\"\r\n @drop=\"handleDrop\"\r\n class=\"tree-instance\"\r\n />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { NInput, NButton, NTree } from \"naive-ui\";\r\nimport C_Icon from \"../C_Icon/index.vue\";\r\nimport { useTreeOperations } from \"./composables/useTreeOperations\";\r\nimport type { TreeProps, TreeEmits, TreeExpose } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_Tree\" });\r\n\r\nconst props = withDefaults(defineProps<TreeProps>(), {\r\n mode: \"custom\",\r\n keyField: \"id\",\r\n labelField: \"name\",\r\n childrenField: \"children\",\r\n searchPattern: \"\",\r\n searchable: true,\r\n searchPlaceholder: \"搜索...\",\r\n draggable: false,\r\n showLine: true,\r\n showToolbar: true,\r\n addable: true,\r\n addText: \"新增\",\r\n refreshable: true,\r\n iconField: \"icon\",\r\n iconConfig: () => ({\r\n default: \"mdi:circle-outline\",\r\n typeMap: {},\r\n colorMap: {},\r\n }),\r\n statusConfigs: () => [],\r\n actions: () => [],\r\n defaultExpandAll: false,\r\n defaultExpandedKeys: () => [],\r\n defaultSelectedKeys: () => [],\r\n});\r\n\r\nconst emit = defineEmits<TreeEmits>();\r\n\r\nconst {\r\n internalSearchPattern,\r\n expandedKeys,\r\n selectedKeys,\r\n isAllExpanded,\r\n treeData,\r\n currentSearchPattern,\r\n renderPrefix,\r\n renderLabel,\r\n renderSuffix,\r\n toggleExpandAll,\r\n handleExpandedKeysChange,\r\n handleSelectedKeysChange,\r\n handleDrop,\r\n handleAdd,\r\n handleRefresh,\r\n expose,\r\n} = useTreeOperations(props, emit);\r\n\r\ndefineExpose<TreeExpose>(expose);\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"C_Tree.js","names":["C_Icon","NTag","NPopconfirm","NButton","NSpace","showToolbar","searchable","searchPlaceholder","addable","addText","refreshable","draggable","showLine","keyField","labelField","childrenField"],"sources":["../src/components/C_Tree/data.ts","../src/components/C_Tree/composables/useTreeOperations.ts","../src/components/C_Tree/index.vue","../src/components/C_Tree/index.vue","../src/components/C_Tree/index.vue"],"sourcesContent":["import type { TreeMode, TreeProps } from \"./types\";\r\n\r\nexport const presetConfigs: Record<TreeMode, Partial<TreeProps>> = {\r\n menu: {\r\n draggable: true,\r\n showLine: true,\r\n iconConfig: {\r\n typeMap: {\r\n directory: \"mdi:folder\",\r\n menu: \"mdi:file-document\",\r\n button: \"mdi:button-cursor\",\r\n },\r\n },\r\n actions: [\r\n { key: \"add\", text: \"新增子菜单\", icon: \"mdi:plus\", type: \"primary\" },\r\n { key: \"edit\", text: \"编辑\", icon: \"mdi:pencil\", type: \"info\" },\r\n {\r\n key: \"delete\",\r\n text: \"删除\",\r\n icon: \"mdi:delete\",\r\n type: \"error\",\r\n confirm: \"确认删除该菜单吗?\",\r\n },\r\n ],\r\n },\r\n file: {\r\n draggable: false,\r\n showLine: false,\r\n iconConfig: {\r\n typeMap: {\r\n folder: \"mdi:folder\",\r\n file: \"mdi:file-document\",\r\n image: \"mdi:file-image\",\r\n video: \"mdi:file-video\",\r\n audio: \"mdi:file-music\",\r\n },\r\n },\r\n actions: [\r\n { key: \"open\", text: \"打开\", icon: \"mdi:folder-open\", type: \"primary\" },\r\n { key: \"rename\", text: \"重命名\", icon: \"mdi:rename-box\", type: \"info\" },\r\n {\r\n key: \"delete\",\r\n text: \"删除\",\r\n icon: \"mdi:delete\",\r\n type: \"error\",\r\n confirm: \"确认删除该文件吗?\",\r\n },\r\n ],\r\n },\r\n org: {\r\n draggable: false,\r\n showLine: true,\r\n iconConfig: {\r\n typeMap: {\r\n department: \"mdi:domain\",\r\n user: \"mdi:account\",\r\n manager: \"mdi:account-tie\",\r\n },\r\n },\r\n actions: [\r\n { key: \"add\", text: \"新增下级\", icon: \"mdi:plus\", type: \"primary\" },\r\n { key: \"edit\", text: \"编辑\", icon: \"mdi:pencil\", type: \"info\" },\r\n ],\r\n },\r\n custom: {},\r\n};\r\n","import { ref, computed, h } from \"vue\";\r\nimport { NTag, NButton, NPopconfirm, NSpace } from \"naive-ui\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport { presetConfigs } from \"../data\";\r\nimport type {\r\n TreeOption,\r\n TreeNodeData,\r\n TreeProps,\r\n TreeExpose,\r\n DropInfo,\r\n} from \"../types\";\r\n\r\ntype EmitFn = {\r\n (\r\n event: \"node-select\",\r\n node: TreeNodeData | null,\r\n selectedKeys: (string | number)[],\r\n ): void;\r\n (event: \"node-action\", action: string, node: TreeNodeData): void;\r\n (event: \"node-drop\", info: DropInfo): void;\r\n (event: \"add\", parentNode?: TreeNodeData): void;\r\n (event: \"refresh\"): void;\r\n};\r\n\r\nexport function useTreeOperations(props: TreeProps, emit: EmitFn) {\r\n const internalSearchPattern = ref(\"\");\r\n const expandedKeys = ref<(string | number)[]>(\r\n props.defaultExpandedKeys ?? [],\r\n );\r\n const selectedKeys = ref<(string | number)[]>(\r\n props.defaultSelectedKeys ?? [],\r\n );\r\n const isAllExpanded = ref(props.defaultExpandAll ?? false);\r\n\r\n const keyField = computed(() => props.keyField ?? \"id\");\r\n const labelField = computed(() => props.labelField ?? \"name\");\r\n const childrenField = computed(() => props.childrenField ?? \"children\");\r\n const iconField = computed(() => props.iconField ?? \"icon\");\r\n\r\n const mergedConfig = computed(() => {\r\n const preset = presetConfigs[props.mode ?? \"custom\"] || {};\r\n return {\r\n ...preset,\r\n ...props,\r\n iconConfig: { ...preset.iconConfig, ...props.iconConfig },\r\n actions: props.actions?.length ? props.actions : preset.actions || [],\r\n };\r\n });\r\n\r\n const treeData = computed((): TreeOption[] => props.data as TreeOption[]);\r\n\r\n const currentSearchPattern = computed(\r\n () => props.searchPattern || internalSearchPattern.value,\r\n );\r\n\r\n const selectedNode = computed((): TreeNodeData | null => {\r\n if (selectedKeys.value.length === 0) return null;\r\n const findNode = (\r\n nodes: TreeNodeData[],\r\n id: string | number,\r\n ): TreeNodeData | null => {\r\n for (const node of nodes) {\r\n if (node[keyField.value] === id) return node;\r\n if (node[childrenField.value]) {\r\n const found = findNode(node[childrenField.value], id);\r\n if (found) return found;\r\n }\r\n }\r\n return null;\r\n };\r\n return findNode(props.data, selectedKeys.value[0]);\r\n });\r\n\r\n const getAllKeys = (nodes: TreeNodeData[]): (string | number)[] => {\r\n const keys: (string | number)[] = [];\r\n nodes.forEach((node) => {\r\n keys.push(node[keyField.value]);\r\n if (node[childrenField.value]) {\r\n keys.push(...getAllKeys(node[childrenField.value]));\r\n }\r\n });\r\n return keys;\r\n };\r\n\r\n const getNodeIcon = (node: TreeNodeData): string => {\r\n const config = mergedConfig.value.iconConfig!;\r\n if (node[iconField.value]) return node[iconField.value];\r\n if (node.type && config.typeMap?.[node.type])\r\n return config.typeMap[node.type];\r\n return config.default || \"mdi:circle-outline\";\r\n };\r\n\r\n const getNodeIconColor = (node: TreeNodeData): string => {\r\n const config = mergedConfig.value.iconConfig!;\r\n if (node.type && config.colorMap?.[node.type])\r\n return config.colorMap[node.type];\r\n return \"currentColor\";\r\n };\r\n\r\n const renderPrefix = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n return h(C_Icon, {\r\n name: getNodeIcon(node),\r\n size: 18,\r\n color: getNodeIconColor(node),\r\n class: \"mr-3 flex-shrink-0\",\r\n });\r\n };\r\n\r\n const renderLabel = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n const statusTags =\r\n props.statusConfigs\r\n ?.map((config) => {\r\n const value = node[config.field];\r\n const statusConfig = config.values[value];\r\n if (!statusConfig?.text) return null;\r\n return h(\r\n NTag,\r\n {\r\n type: statusConfig.type,\r\n size: \"small\",\r\n class: \"ml-2\",\r\n style: { fontSize: \"12px\" },\r\n },\r\n { default: () => statusConfig.text },\r\n );\r\n })\r\n .filter(Boolean) || [];\r\n\r\n return h(\r\n \"div\",\r\n {\r\n class: \"flex items-center flex-1 min-w-0 py-1\",\r\n style: { lineHeight: \"1.5\" },\r\n },\r\n [\r\n h(\r\n \"span\",\r\n { class: \"mr-3 font-medium text-sm\" },\r\n node[labelField.value],\r\n ),\r\n ...statusTags,\r\n ],\r\n );\r\n };\r\n\r\n const renderSuffix = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n const actions = mergedConfig.value.actions || [];\r\n\r\n const actionButtons = actions\r\n .filter((action) => !action.show || action.show(node))\r\n .map((action) => {\r\n const buttonProps = {\r\n size: \"tiny\" as const,\r\n type: action.type || (\"default\" as const),\r\n secondary: true,\r\n style: { padding: \"4px 6px\", minWidth: \"24px\", height: \"24px\" },\r\n onClick: (e: Event) => {\r\n e.stopPropagation();\r\n handleNodeAction(action.key, node);\r\n },\r\n };\r\n const iconEl = h(C_Icon, {\r\n name: action.icon,\r\n size: 12,\r\n title: action.text,\r\n });\r\n\r\n if (action.confirm) {\r\n return h(\r\n NPopconfirm,\r\n { onPositiveClick: () => handleNodeAction(action.key, node) },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n ...buttonProps,\r\n onClick: (e: Event) => e.stopPropagation(),\r\n },\r\n { icon: () => iconEl },\r\n ),\r\n default: () => action.confirm,\r\n },\r\n );\r\n }\r\n return h(NButton, buttonProps, { icon: () => iconEl });\r\n });\r\n\r\n return h(\r\n \"div\",\r\n {\r\n class: \"tree-actions\",\r\n style: {\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n marginLeft: \"8px\",\r\n opacity: \"0\",\r\n transition: \"opacity 0.2s ease\",\r\n },\r\n },\r\n [h(NSpace, { size: 4 }, { default: () => actionButtons })],\r\n );\r\n };\r\n\r\n const toggleExpandAll = (): void => {\r\n if (isAllExpanded.value) {\r\n expandedKeys.value = [];\r\n isAllExpanded.value = false;\r\n } else {\r\n expandedKeys.value = getAllKeys(props.data);\r\n isAllExpanded.value = true;\r\n }\r\n };\r\n\r\n const handleExpandedKeysChange = (keys: (string | number)[]): void => {\r\n expandedKeys.value = keys;\r\n isAllExpanded.value = keys.length === getAllKeys(props.data).length;\r\n };\r\n\r\n const handleSelectedKeysChange = (keys: (string | number)[]): void => {\r\n selectedKeys.value = keys;\r\n emit(\"node-select\", selectedNode.value, keys);\r\n };\r\n\r\n const handleDrop = (info: DropInfo): void => emit(\"node-drop\", info);\r\n\r\n const handleNodeAction = (action: string, node: TreeNodeData): void => {\r\n if (action === \"add\") {\r\n emit(\"add\", node);\r\n } else {\r\n emit(\"node-action\", action, node);\r\n }\r\n };\r\n\r\n const handleAdd = (): void => emit(\"add\");\r\n const handleRefresh = (): void => emit(\"refresh\");\r\n\r\n const expose: TreeExpose = {\r\n expandAll: () => {\r\n expandedKeys.value = getAllKeys(props.data);\r\n isAllExpanded.value = true;\r\n },\r\n collapseAll: () => {\r\n expandedKeys.value = [];\r\n isAllExpanded.value = false;\r\n },\r\n selectNode: (key: string | number) => {\r\n selectedKeys.value = [key];\r\n },\r\n getSelectedNode: () => selectedNode.value,\r\n expandedKeys,\r\n selectedKeys,\r\n };\r\n\r\n return {\r\n internalSearchPattern,\r\n expandedKeys,\r\n selectedKeys,\r\n isAllExpanded,\r\n treeData,\r\n currentSearchPattern,\r\n renderPrefix,\r\n renderLabel,\r\n renderSuffix,\r\n toggleExpandAll,\r\n handleExpandedKeysChange,\r\n handleSelectedKeysChange,\r\n handleDrop,\r\n handleAdd,\r\n handleRefresh,\r\n expose,\r\n };\r\n}\r\n","/* unplugin-vue-components disabled */<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-27\n * @Description: 树型组件 — 薄 UI 壳,逻辑由 useTreeOperations 驱动\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-tree\">\n <div v-if=\"showToolbar\" class=\"c-tree-toolbar\">\n <div class=\"toolbar-left\">\n <NInput\n v-if=\"searchable\"\n v-model:value=\"internalSearchPattern\"\n :placeholder=\"searchPlaceholder\"\n clearable\n class=\"search-input\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\n </template>\n </NInput>\n </div>\n <div class=\"toolbar-right\">\n <slot name=\"toolbar-actions\">\n <NButton v-if=\"addable\" type=\"primary\" @click=\"handleAdd()\">\n <template #icon>\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\n </template>\n {{ addText }}\n </NButton>\n <NButton @click=\"toggleExpandAll\">\n <template #icon>\n <C_Icon name=\"mdi:file-tree\" :size=\"16\" />\n </template>\n {{ isAllExpanded ? \"收起全部\" : \"展开全部\" }}\n </NButton>\n <NButton v-if=\"refreshable\" @click=\"handleRefresh\">\n <template #icon>\n <C_Icon name=\"mdi:refresh\" :size=\"16\" />\n </template>\n 刷新\n </NButton>\n </slot>\n </div>\n </div>\n\n <div class=\"c-tree-container\">\n <NTree\n :data=\"treeData\"\n :pattern=\"currentSearchPattern\"\n :expanded-keys=\"expandedKeys\"\n :selected-keys=\"selectedKeys\"\n :draggable=\"draggable\"\n :show-line=\"showLine\"\n :render-prefix=\"renderPrefix\"\n :render-suffix=\"renderSuffix\"\n :render-label=\"renderLabel\"\n :key-field=\"keyField\"\n :label-field=\"labelField\"\n :children-field=\"childrenField\"\n @update:expanded-keys=\"handleExpandedKeysChange\"\n @update:selected-keys=\"handleSelectedKeysChange\"\n @drop=\"handleDrop\"\n class=\"tree-instance\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { NInput, NButton, NTree } from \"naive-ui\";\nimport C_Icon from \"../C_Icon/index.vue\";\nimport { useTreeOperations } from \"./composables/useTreeOperations\";\nimport type { TreeProps, TreeEmits, TreeExpose } from \"./types\";\n\ndefineOptions({ name: \"C_Tree\" });\n\nconst props = withDefaults(defineProps<TreeProps>(), {\n mode: \"custom\",\n keyField: \"id\",\n labelField: \"name\",\n childrenField: \"children\",\n searchPattern: \"\",\n searchable: true,\n searchPlaceholder: \"搜索...\",\n draggable: false,\n showLine: true,\n showToolbar: true,\n addable: true,\n addText: \"新增\",\n refreshable: true,\n iconField: \"icon\",\n iconConfig: () => ({\n default: \"mdi:circle-outline\",\n typeMap: {},\n colorMap: {},\n }),\n statusConfigs: () => [],\n actions: () => [],\n defaultExpandAll: false,\n defaultExpandedKeys: () => [],\n defaultSelectedKeys: () => [],\n});\n\nconst emit = defineEmits<TreeEmits>();\n\nconst {\n internalSearchPattern,\n expandedKeys,\n selectedKeys,\n isAllExpanded,\n treeData,\n currentSearchPattern,\n renderPrefix,\n renderLabel,\n renderSuffix,\n toggleExpandAll,\n handleExpandedKeysChange,\n handleSelectedKeysChange,\n handleDrop,\n handleAdd,\n handleRefresh,\n expose,\n} = useTreeOperations(props, emit);\n\ndefineExpose<TreeExpose>(expose);\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n","/* unplugin-vue-components disabled */<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-27\n * @Description: 树型组件 — 薄 UI 壳,逻辑由 useTreeOperations 驱动\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-tree\">\n <div v-if=\"showToolbar\" class=\"c-tree-toolbar\">\n <div class=\"toolbar-left\">\n <NInput\n v-if=\"searchable\"\n v-model:value=\"internalSearchPattern\"\n :placeholder=\"searchPlaceholder\"\n clearable\n class=\"search-input\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\n </template>\n </NInput>\n </div>\n <div class=\"toolbar-right\">\n <slot name=\"toolbar-actions\">\n <NButton v-if=\"addable\" type=\"primary\" @click=\"handleAdd()\">\n <template #icon>\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\n </template>\n {{ addText }}\n </NButton>\n <NButton @click=\"toggleExpandAll\">\n <template #icon>\n <C_Icon name=\"mdi:file-tree\" :size=\"16\" />\n </template>\n {{ isAllExpanded ? \"收起全部\" : \"展开全部\" }}\n </NButton>\n <NButton v-if=\"refreshable\" @click=\"handleRefresh\">\n <template #icon>\n <C_Icon name=\"mdi:refresh\" :size=\"16\" />\n </template>\n 刷新\n </NButton>\n </slot>\n </div>\n </div>\n\n <div class=\"c-tree-container\">\n <NTree\n :data=\"treeData\"\n :pattern=\"currentSearchPattern\"\n :expanded-keys=\"expandedKeys\"\n :selected-keys=\"selectedKeys\"\n :draggable=\"draggable\"\n :show-line=\"showLine\"\n :render-prefix=\"renderPrefix\"\n :render-suffix=\"renderSuffix\"\n :render-label=\"renderLabel\"\n :key-field=\"keyField\"\n :label-field=\"labelField\"\n :children-field=\"childrenField\"\n @update:expanded-keys=\"handleExpandedKeysChange\"\n @update:selected-keys=\"handleSelectedKeysChange\"\n @drop=\"handleDrop\"\n class=\"tree-instance\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { NInput, NButton, NTree } from \"naive-ui\";\nimport C_Icon from \"../C_Icon/index.vue\";\nimport { useTreeOperations } from \"./composables/useTreeOperations\";\nimport type { TreeProps, TreeEmits, TreeExpose } from \"./types\";\n\ndefineOptions({ name: \"C_Tree\" });\n\nconst props = withDefaults(defineProps<TreeProps>(), {\n mode: \"custom\",\n keyField: \"id\",\n labelField: \"name\",\n childrenField: \"children\",\n searchPattern: \"\",\n searchable: true,\n searchPlaceholder: \"搜索...\",\n draggable: false,\n showLine: true,\n showToolbar: true,\n addable: true,\n addText: \"新增\",\n refreshable: true,\n iconField: \"icon\",\n iconConfig: () => ({\n default: \"mdi:circle-outline\",\n typeMap: {},\n colorMap: {},\n }),\n statusConfigs: () => [],\n actions: () => [],\n defaultExpandAll: false,\n defaultExpandedKeys: () => [],\n defaultSelectedKeys: () => [],\n});\n\nconst emit = defineEmits<TreeEmits>();\n\nconst {\n internalSearchPattern,\n expandedKeys,\n selectedKeys,\n isAllExpanded,\n treeData,\n currentSearchPattern,\n renderPrefix,\n renderLabel,\n renderSuffix,\n toggleExpandAll,\n handleExpandedKeysChange,\n handleSelectedKeysChange,\n handleDrop,\n handleAdd,\n handleRefresh,\n expose,\n} = useTreeOperations(props, emit);\n\ndefineExpose<TreeExpose>(expose);\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-27\n * @Description: 树型组件 — 薄 UI 壳,逻辑由 useTreeOperations 驱动\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-tree\">\n <div v-if=\"showToolbar\" class=\"c-tree-toolbar\">\n <div class=\"toolbar-left\">\n <NInput\n v-if=\"searchable\"\n v-model:value=\"internalSearchPattern\"\n :placeholder=\"searchPlaceholder\"\n clearable\n class=\"search-input\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\n </template>\n </NInput>\n </div>\n <div class=\"toolbar-right\">\n <slot name=\"toolbar-actions\">\n <NButton v-if=\"addable\" type=\"primary\" @click=\"handleAdd()\">\n <template #icon>\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\n </template>\n {{ addText }}\n </NButton>\n <NButton @click=\"toggleExpandAll\">\n <template #icon>\n <C_Icon name=\"mdi:file-tree\" :size=\"16\" />\n </template>\n {{ isAllExpanded ? \"收起全部\" : \"展开全部\" }}\n </NButton>\n <NButton v-if=\"refreshable\" @click=\"handleRefresh\">\n <template #icon>\n <C_Icon name=\"mdi:refresh\" :size=\"16\" />\n </template>\n 刷新\n </NButton>\n </slot>\n </div>\n </div>\n\n <div class=\"c-tree-container\">\n <NTree\n :data=\"treeData\"\n :pattern=\"currentSearchPattern\"\n :expanded-keys=\"expandedKeys\"\n :selected-keys=\"selectedKeys\"\n :draggable=\"draggable\"\n :show-line=\"showLine\"\n :render-prefix=\"renderPrefix\"\n :render-suffix=\"renderSuffix\"\n :render-label=\"renderLabel\"\n :key-field=\"keyField\"\n :label-field=\"labelField\"\n :children-field=\"childrenField\"\n @update:expanded-keys=\"handleExpandedKeysChange\"\n @update:selected-keys=\"handleSelectedKeysChange\"\n @drop=\"handleDrop\"\n class=\"tree-instance\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { NInput, NButton, NTree } from \"naive-ui\";\nimport C_Icon from \"../C_Icon/index.vue\";\nimport { useTreeOperations } from \"./composables/useTreeOperations\";\nimport type { TreeProps, TreeEmits, TreeExpose } from \"./types\";\n\ndefineOptions({ name: \"C_Tree\" });\n\nconst props = withDefaults(defineProps<TreeProps>(), {\n mode: \"custom\",\n keyField: \"id\",\n labelField: \"name\",\n childrenField: \"children\",\n searchPattern: \"\",\n searchable: true,\n searchPlaceholder: \"搜索...\",\n draggable: false,\n showLine: true,\n showToolbar: true,\n addable: true,\n addText: \"新增\",\n refreshable: true,\n iconField: \"icon\",\n iconConfig: () => ({\n default: \"mdi:circle-outline\",\n typeMap: {},\n colorMap: {},\n }),\n statusConfigs: () => [],\n actions: () => [],\n defaultExpandAll: false,\n defaultExpandedKeys: () => [],\n defaultSelectedKeys: () => [],\n});\n\nconst emit = defineEmits<TreeEmits>();\n\nconst {\n internalSearchPattern,\n expandedKeys,\n selectedKeys,\n isAllExpanded,\n treeData,\n currentSearchPattern,\n renderPrefix,\n renderLabel,\n renderSuffix,\n toggleExpandAll,\n handleExpandedKeysChange,\n handleSelectedKeysChange,\n handleDrop,\n handleAdd,\n handleRefresh,\n expose,\n} = useTreeOperations(props, emit);\n\ndefineExpose<TreeExpose>(expose);\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n"],"mappings":"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"}
|