@robot-admin/naive-ui-components 0.6.27 → 0.6.29
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_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_Date.js.map +1 -1
- package/dist/C_Date2.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_GlobalSearch-B6wK3m3k.css.map → C_GlobalSearch-BNtNpLLD.css.map} +1 -1
- package/dist/{C_GlobalSearch-DoPjldFm.css.map → C_GlobalSearch-BlMeQ2D9.css.map} +1 -1
- package/dist/C_GlobalSearch.css +112 -112
- package/dist/C_GlobalSearch.js.map +1 -1
- package/dist/C_GlobalSearch2.js +1 -1
- package/dist/C_GlobalSearch2.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-BJMvVPWT.css.map → C_Signature-BDx4ctsE.css.map} +1 -1
- package/dist/{C_Signature-BJUHx2BF.css.map → C_Signature-Bz_uNbwv.css.map} +1 -1
- package/dist/C_Signature.css +15 -15
- 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.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-JIkRrAHT.css.map} +1 -1
- package/dist/{C_TagsView-Crm7yWg9.css.map → C_TagsView-mrh0FN4H.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-BKkimrZx.css.map +1 -0
- package/dist/C_Timeline-DTuSCX6p.css.map +1 -0
- package/dist/C_Timeline.css +8 -12
- package/dist/C_Timeline.js.map +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 +5 -5
- package/dist/constants3.d.ts +3 -3
- package/dist/constants4.d.ts +7 -7
- package/dist/data.d.ts +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.vue.d.ts +1 -1
- package/dist/index10.vue.d.ts +1 -1
- package/dist/index11.vue.d.ts +4 -4
- package/dist/index12.vue.d.ts +1 -1
- package/dist/index13.vue.d.ts +4 -4
- package/dist/index15.vue.d.ts +5 -5
- package/dist/index16.vue.d.ts +3 -3
- 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 +1 -1
- package/dist/index21.vue.d.ts +1 -1
- package/dist/index24.vue.d.ts +4 -4
- package/dist/index25.vue.d.ts +3 -3
- package/dist/index26.vue.d.ts +4 -4
- package/dist/index3.vue.d.ts +2 -2
- package/dist/index4.vue.d.ts +6 -6
- package/dist/index6.vue.d.ts +6 -6
- 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 +1557 -1561
- package/dist/useCollapsePanel.d.ts +4 -4
- package/dist/useCropperCore.d.ts +5 -5
- package/dist/useDraggableLayout.d.ts +10 -10
- package/dist/useDynamicFormState.d.ts +10 -10
- package/dist/useEdgeInteraction.d.ts +1 -1
- package/dist/useFullscreen.d.ts +2 -2
- package/dist/useInfiniteScroll.d.ts +5 -5
- package/dist/useModalEdit.d.ts +2 -2
- package/dist/useQRCode.d.ts +3 -3
- package/dist/useSignatureHistory.d.ts +3 -3
- package/dist/useSplitResize.d.ts +7 -7
- package/dist/useTimeSelection.d.ts +2 -2
- package/dist/useTreeOperations.d.ts +6 -6
- package/dist/useWorkflowValidation.d.ts +7 -7
- 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_Timeline-DUWB_nNy.css.map +0 -1
- package/dist/C_Timeline-DmV_M7Ua.css.map +0 -1
package/dist/C_Transfer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"C_Transfer.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_Transfer.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":"gIA+CA,MAAa,EAAiD,CAC5D,OAAQ,CAAC,OAAQ,OAAO,CACxB,WAAY,GACZ,kBAAmB,QACnB,cAAe,GACf,gBAAiB,OACjB,gBAAiB,SACjB,KAAM,SACP,wiCG4IC,IAAM,EAAQ,EAwBR,EAAO,EASP,GAAA,EAAA,EAAA,eAA8B,CAAE,GAAG,EAAwB,GAAG,EAAO,EAAC,CAItE,GAAA,EAAA,EAAA,cAAyB,CAC7B,IAAM,EAAI,IAAI,IACd,IAAK,IAAM,KAAQ,EAAM,KAAM,EAAE,IAAI,EAAK,IAAK,EAAI,CACnD,OAAO,GACR,CAEK,GAAA,EAAA,EAAA,cAA8B,IAAI,IAAI,EAAM,WAAW,CAAA,CAEvD,GAAA,EAAA,EAAA,cACJ,EAAM,KAAK,OAAO,GAAK,CAAC,EAAa,MAAM,IAAI,EAAE,IAAI,CAAA,CACvD,CAEM,GAAA,EAAA,EAAA,cAA4B,CAChC,IAAM,EAAyB,EAAC,CAChC,IAAK,IAAM,KAAO,EAAM,WAAY,CAClC,IAAM,EAAO,EAAQ,MAAM,IAAI,EAAG,CAC9B,GAAM,EAAO,KAAK,EAAI,CAE5B,OAAO,GACR,CAIK,GAAA,EAAA,EAAA,KAAkB,GAAE,CACpB,GAAA,EAAA,EAAA,KAAkB,GAAE,CAG1B,SAAS,EAAc,EAAe,EAA6B,CACjE,OAAO,EAAK,MAAM,aAAa,CAAC,SAAS,EAAM,aAAa,CAAA,CAG9D,IAAM,GAAA,EAAA,EAAA,cAA0B,EAAM,cAAgB,EAAa,CAE7D,GAAA,EAAA,EAAA,cAAoC,CACxC,IAAM,EAAI,EAAY,MAAM,MAAK,CACjC,OAAO,EACH,EAAW,MAAM,OAAO,GAAQ,EAAS,MAAM,EAAG,EAAK,CAAA,CACvD,EAAW,OAChB,CAEK,GAAA,EAAA,EAAA,cAAoC,CACxC,IAAM,EAAI,EAAY,MAAM,MAAK,CACjC,OAAO,EACH,EAAW,MAAM,OAAO,GAAQ,EAAS,MAAM,EAAG,EAAK,CAAA,CACvD,EAAW,OAChB,CAIK,GAAA,EAAA,EAAA,UAAyB,IAAI,IAAsB,CACnD,GAAA,EAAA,EAAA,UAAyB,IAAI,IAAsB,CAGzD,SAAS,EAAkB,EAAsB,CAC3C,EAAc,IAAI,EAAI,CAAE,EAAc,OAAO,EAAG,CAC/C,EAAc,IAAI,EAAG,CAI5B,SAAS,EAAkB,EAAsB,CAC3C,EAAc,IAAI,EAAI,CAAE,EAAc,OAAO,EAAG,CAC/C,EAAc,IAAI,EAAG,CAG5B,IAAM,GAAA,EAAA,EAAA,cAEF,EAAmB,MAAM,OAAS,GAClC,EAAmB,MAAM,MACvB,GAAK,EAAE,UAAY,EAAc,IAAI,EAAE,IAAG,CAC5C,CACJ,CACM,GAAA,EAAA,EAAA,cAEF,CAAC,EAAmB,OACpB,EAAmB,MAAM,KAAK,GAAK,EAAc,IAAI,EAAE,IAAI,CAAA,CAC/D,CACM,GAAA,EAAA,EAAA,cAEF,EAAmB,MAAM,OAAS,GAClC,EAAmB,MAAM,MACvB,GAAK,EAAE,UAAY,EAAc,IAAI,EAAE,IAAG,CAC5C,CACJ,CACM,GAAA,EAAA,EAAA,cAEF,CAAC,EAAmB,OACpB,EAAmB,MAAM,KAAK,GAAK,EAAc,IAAI,EAAE,IAAI,CAAA,CAC/D,CAGA,SAAS,EAAgB,EAAkB,CACzC,IAAK,IAAM,KAAQ,EAAmB,MAChC,EAAK,WACL,EAAS,EAAc,IAAI,EAAK,IAAG,CAClC,EAAc,OAAO,EAAK,IAAG,EAKtC,SAAS,EAAgB,EAAkB,CACzC,IAAK,IAAM,KAAQ,EAAmB,MAChC,EAAK,WACL,EAAS,EAAc,IAAI,EAAK,IAAG,CAClC,EAAc,OAAO,EAAK,IAAG,EAOtC,SAAS,GAAe,CACtB,IAAM,EAAO,CAAC,GAAG,EAAa,CACxB,EAAS,CAAC,GAAG,EAAM,WAAY,GAAG,EAAI,CAC5C,EAAK,oBAAqB,EAAM,CAChC,EAAK,SAAU,EAAQ,QAAS,EAAI,CACpC,EAAc,OAAM,CAItB,SAAS,GAAe,CACtB,IAAM,EAAO,CAAC,GAAG,EAAa,CACxB,EAAY,IAAI,IAAI,EAAI,CACxB,EAAS,EAAM,WAAW,OAAO,GAAK,CAAC,EAAU,IAAI,EAAE,CAAA,CAC7D,EAAK,oBAAqB,EAAM,CAChC,EAAK,SAAU,EAAQ,OAAQ,EAAI,CACnC,EAAc,OAAM,6FA9KhB,MAAA,CAlLJ,OAAA,EAAA,EAAA,gBAAK,CAAC,aAAY,CAAA,MACH,EAAA,MAAY,OAAI,CAAA,CAAA,CAAA,CAAA,0BAE/B,mDAAuD,0BA4EjD,MA3EN,EA2EM,0BAxDE,MAlBN,EAkBM,CAhBI,EAAA,MAAY,gBAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,aAMR,EAAA,OALT,QAAS,EAAA,MACT,cAAe,EAAA,MACf,mBAAgB,8BAEY,EAAA,EAAA,EAAA,kBAAA,EAAA,EAAA,iBAA1B,EAAA,MAAY,SAAM,GAAA,CAAA,EAAA,CAAA,CAAA,kFAOhB,OALP,GAAA,EAAA,EAAA,iBAIK,EAAA,MAAY,SAAM,GAAA,CAAA,EAAA,GAAA,EAAA,EAAA,oBAItB,OAFD,GAAA,EAAA,EAAA,iBACM,EAAc,KAAI,CAAG,OAAA,EAAA,EAAA,iBAAM,EAAA,MAAW,OAAM,CAAA,EAAA,CAAA,CAAA,CAK5C,EAAA,MAAY,aAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,oBAad,MAdN,EAcM,EAAA,EAAA,EAAA,aADK,EAAA,CARC,MAAO,EAAA,iCAAA,EAAW,MAAA,EAC1B,UAAA,GACA,KAAK,QACJ,YAAa,EAAA,MAAY,oBAEf,QAAA,EAAA,EAAA,aACoB,EAAA,EAAA,EAAA,aAAA,EAAA,EAAA,CAArB,KAAK,cAAa,CAAA,CAAA,CAAA,wEAMxB,EAAA,MAAmB,SAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,oBA8BrB,MA/BN,EA+BM,GAAA,EAAA,EAAA,WAAA,GAAA,EAAA,EAAA,EAAA,oBADO,EAAA,SAAA,MAAA,EAAA,EAAA,YAzBM,EAAA,MAAR,+CAwBD,MAAA,KAvBA,EAAK,IAGT,OAAA,EAAA,EAAA,gBAAK,CAAC,mBAAkB,eACe,EAAK,sBAAsC,EAAc,IAAI,EAAK,IAAG,IAI3G,QAAK,GAAA,CAAG,EAAK,UAAY,EAAkB,EAAK,IAAG,qBAQlD,EAAA,CALC,QAAS,EAAc,IAAI,EAAK,IAAG,CACnC,SAAU,EAAK,SAChB,KAAK,QACJ,mBAAc,GAAE,EAAkB,EAAK,IAAG,CAC1C,QAAK,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAN,GAAW,CAAA,OAAA,CAAA,oDAGL,EAAK,OAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,aAGX,EAAA,EAAA,OAFC,KAAM,EAAK,KACZ,MAAM,wGAEoD,OAA5D,GAAA,EAAA,EAAA,iBAAwC,EAAK,MAAK,CAAA,EAAA,+DASlD,MALN,GAAA,EAAA,EAAA,iBAIK,EAAA,MAAY,gBAAe,CAAA,EAAA,6BAIlC,8CAAkD,0BAkB5C,MAjBN,EAiBM,EAAA,EAAA,EAAA,oBATK,SAAA,CANP,OAAA,EAAA,EAAA,gBAAK,CAAC,kBAAiB,CAAA,cACE,EAAc,OAAI,EAAA,CAAA,CAAA,CAC1C,SAAU,EAAc,OAAI,EAC5B,QAAO,sBAE2B,EAAA,EAAA,CAA3B,KAAK,oBAAmB,CAAA,CAAA,CAAA,GAAA,EAAA,EAAA,EAAA,EAAA,oBASzB,SAAA,CANP,OAAA,EAAA,EAAA,gBAAK,CAAC,kBAAiB,CAAA,cACE,EAAc,OAAI,EAAA,CAAA,CAAA,CAC1C,SAAU,EAAc,OAAI,EAC5B,QAAO,sBAE0B,EAAA,EAAA,CAA1B,KAAK,mBAAkB,CAAA,CAAA,CAAA,GAAA,EAAA,CAAA,CAAA,0BAInC,mDAAuD,0BA4EjD,MA3EN,EA2EM,0BAxDE,MAlBN,EAkBM,CAhBI,EAAA,MAAY,gBAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,aAMR,EAAA,OALT,QAAS,EAAA,MACT,cAAe,EAAA,MACf,mBAAgB,8BAEY,EAAA,EAAA,EAAA,kBAAA,EAAA,EAAA,iBAA1B,EAAA,MAAY,SAAM,GAAA,CAAA,EAAA,CAAA,CAAA,kFAOhB,OALP,GAAA,EAAA,EAAA,iBAIK,EAAA,MAAY,SAAM,GAAA,CAAA,EAAA,GAAA,EAAA,EAAA,oBAItB,OAFD,GAAA,EAAA,EAAA,iBACM,EAAc,KAAI,CAAG,OAAA,EAAA,EAAA,iBAAM,EAAA,MAAW,OAAM,CAAA,EAAA,CAAA,CAAA,CAK5C,EAAA,MAAY,aAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,oBAad,MAdN,EAcM,EAAA,EAAA,EAAA,aADK,EAAA,CARC,MAAO,EAAA,iCAAA,EAAW,MAAA,EAC1B,UAAA,GACA,KAAK,QACJ,YAAa,EAAA,MAAY,oBAEf,QAAA,EAAA,EAAA,aACoB,EAAA,EAAA,EAAA,aAAA,EAAA,EAAA,CAArB,KAAK,cAAa,CAAA,CAAA,CAAA,wEAMxB,EAAA,MAAmB,SAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,oBA8BrB,MA/BN,EA+BM,GAAA,EAAA,EAAA,WAAA,GAAA,EAAA,EAAA,EAAA,oBADO,EAAA,SAAA,MAAA,EAAA,EAAA,YAzBM,EAAA,MAAR,+CAwBD,MAAA,KAvBA,EAAK,IAGT,OAAA,EAAA,EAAA,gBAAK,CAAC,mBAAkB,eACe,EAAK,sBAAsC,EAAc,IAAI,EAAK,IAAG,IAI3G,QAAK,GAAA,CAAG,EAAK,UAAY,EAAkB,EAAK,IAAG,qBAQlD,EAAA,CALC,QAAS,EAAc,IAAI,EAAK,IAAG,CACnC,SAAU,EAAK,SAChB,KAAK,QACJ,mBAAc,GAAE,EAAkB,EAAK,IAAG,CAC1C,QAAK,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAN,GAAW,CAAA,OAAA,CAAA,oDAGL,EAAK,OAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,aAGX,EAAA,EAAA,OAFC,KAAM,EAAK,KACZ,MAAM,wGAEoD,OAA5D,GAAA,EAAA,EAAA,iBAAwC,EAAK,MAAK,CAAA,EAAA,+DASlD,MALN,GAAA,EAAA,EAAA,iBAIK,EAAA,MAAY,gBAAe,CAAA,EAAA"}
|
package/dist/C_Transfer2.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./C_Icon2.js";import{t}from"./export-helper.js";import{NCheckbox as n,NInput as r}from"naive-ui";import{Fragment as i,computed as a,createBlock as o,createCommentVNode as s,createElementBlock as c,createElementVNode as l,createTextVNode as u,createVNode as d,defineComponent as f,normalizeClass as p,openBlock as m,reactive as h,ref as g,renderList as _,toDisplayString as v,withCtx as y,withModifiers as b}from"vue";const x={titles:[`可选列表`,`已选列表`],filterable:!1,filterPlaceholder:`搜索...`,showSelectAll:!0,sourceEmptyText:`暂无数据`,targetEmptyText:`未选择任何项`,size:`medium`},S={class:`c-transfer__panel`},ee={class:`c-transfer__header`},te={key:1,class:`c-transfer__title`},ne={class:`c-transfer__count`},re={key:0,class:`c-transfer__filter`},ie={key:1,class:`c-transfer__list`},ae=[`onClick`],C={class:`c-transfer__item-label`},w={key:2,class:`c-transfer__empty`},T={class:`c-transfer__actions`},E=[`disabled`],D=[`disabled`],O={class:`c-transfer__panel`},k={class:`c-transfer__header`},oe={key:1,class:`c-transfer__title`},se={class:`c-transfer__count`},ce={key:0,class:`c-transfer__filter`},A={key:1,class:`c-transfer__list`},j=[`onClick`],M={class:`c-transfer__item-label`},N={key:2,class:`c-transfer__empty`};var P=t(f({__name:`index`,props:{data:{},modelValue:{},titles:{default:()=>x.titles},filterable:{type:Boolean,default:x.filterable},filterPlaceholder:{default:x.filterPlaceholder},filterMethod:{},showSelectAll:{type:Boolean,default:x.showSelectAll},sourceEmptyText:{default:x.sourceEmptyText},targetEmptyText:{default:x.targetEmptyText},size:{default:x.size}},emits:[`update:modelValue`,`change`],setup(t,{emit:f}){let P=t,F=f,I=a(()=>({...x,...P})),L=a(()=>{let e=new Map;for(let t of P.data)e.set(t.key,t);return e}),R=a(()=>new Set(P.modelValue)),z=a(()=>P.data.filter(e=>!R.value.has(e.key))),B=a(()=>{let e=[];for(let t of P.modelValue){let n=L.value.get(t);n&&e.push(n)}return e}),V=g(``),H=g(``);function U(e,t){return t.label.toLowerCase().includes(e.toLowerCase())}let W=a(()=>P.filterMethod??U),G=a(()=>{let e=V.value.trim();return e?z.value.filter(t=>W.value(e,t)):z.value}),K=a(()=>{let e=H.value.trim();return e?B.value.filter(t=>W.value(e,t)):B.value}),q=h(new Set),J=h(new Set);function Y(e){q.has(e)?q.delete(e):q.add(e)}function X(e){J.has(e)?J.delete(e):J.add(e)}let Z=a(()=>G.value.length>0&&G.value.every(e=>e.disabled||q.has(e.key))),le=a(()=>!Z.value&&G.value.some(e=>q.has(e.key))),Q=a(()=>K.value.length>0&&K.value.every(e=>e.disabled||J.has(e.key))),$=a(()=>!Q.value&&K.value.some(e=>J.has(e.key)));function ue(e){for(let t of G.value)t.disabled||(e?q.add(t.key):q.delete(t.key))}function de(e){for(let t of K.value)t.disabled||(e?J.add(t.key):J.delete(t.key))}function fe(){let e=[...q],t=[...P.modelValue,...e];F(`update:modelValue`,t),F(`change`,t,`right`,e),q.clear()}function pe(){let e=[...J],t=new Set(e),n=P.modelValue.filter(e=>!t.has(e));F(`update:modelValue`,n),F(`change`,n,`left`,e),J.clear()}return(t,a)=>{let f=n,h=r;return m(),c(`div`,{class:p([`c-transfer`,[`is-${I.value.size}`]])},[s(` ================ Source Panel ================ `),l(`div`,S,[l(`div`,ee,[I.value.showSelectAll?(m(),o(f,{key:0,checked:Z.value,indeterminate:le.value,"onUpdate:checked":ue},{default:y(()=>[u(v(I.value.titles?.[0]),1)]),_:1},8,[`checked`,`indeterminate`])):(m(),c(`span`,te,v(I.value.titles?.[0]),1)),l(`span`,ne,v(q.size)+` / `+v(z.value.length),1)]),I.value.filterable?(m(),c(`div`,re,[d(h,{value:V.value,"onUpdate:value":a[0]||=e=>V.value=e,clearable:``,size:`small`,placeholder:I.value.filterPlaceholder},{prefix:y(()=>[d(e,{name:`mdi:magnify`})]),_:1},8,[`value`,`placeholder`])])):s(`v-if`,!0),G.value.length?(m(),c(`div`,ie,[(m(!0),c(i,null,_(G.value,t=>(m(),c(`div`,{key:t.key,class:p([`c-transfer__item`,{"is-disabled":t.disabled,"is-checked":q.has(t.key)}]),onClick:e=>!t.disabled&&Y(t.key)},[d(f,{checked:q.has(t.key),disabled:t.disabled,size:`small`,"onUpdate:checked":e=>Y(t.key),onClick:a[1]||=b(()=>{},[`stop`])},null,8,[`checked`,`disabled`,`onUpdate:checked`]),t.icon?(m(),o(e,{key:0,name:t.icon,class:`c-transfer__item-icon`},null,8,[`name`])):s(`v-if`,!0),l(`span`,C,v(t.label),1)],10,ae))),128))])):(m(),c(`div`,w,v(I.value.sourceEmptyText),1))]),s(` ================ Actions ================ `),l(`div`,T,[l(`button`,{class:p([`c-transfer__btn`,{"is-disabled":q.size===0}]),disabled:q.size===0,onClick:fe},[d(e,{name:`mdi:chevron-right`})],10,E),l(`button`,{class:p([`c-transfer__btn`,{"is-disabled":J.size===0}]),disabled:J.size===0,onClick:pe},[d(e,{name:`mdi:chevron-left`})],10,D)]),s(` ================ Target Panel ================ `),l(`div`,O,[l(`div`,k,[I.value.showSelectAll?(m(),o(f,{key:0,checked:Q.value,indeterminate:$.value,"onUpdate:checked":de},{default:y(()=>[u(v(I.value.titles?.[1]),1)]),_:1},8,[`checked`,`indeterminate`])):(m(),c(`span`,oe,v(I.value.titles?.[1]),1)),l(`span`,se,v(J.size)+` / `+v(B.value.length),1)]),I.value.filterable?(m(),c(`div`,ce,[d(h,{value:H.value,"onUpdate:value":a[2]||=e=>H.value=e,clearable:``,size:`small`,placeholder:I.value.filterPlaceholder},{prefix:y(()=>[d(e,{name:`mdi:magnify`})]),_:1},8,[`value`,`placeholder`])])):s(`v-if`,!0),K.value.length?(m(),c(`div`,A,[(m(!0),c(i,null,_(K.value,t=>(m(),c(`div`,{key:t.key,class:p([`c-transfer__item`,{"is-disabled":t.disabled,"is-checked":J.has(t.key)}]),onClick:e=>!t.disabled&&X(t.key)},[d(f,{checked:J.has(t.key),disabled:t.disabled,size:`small`,"onUpdate:checked":e=>X(t.key),onClick:a[3]||=b(()=>{},[`stop`])},null,8,[`checked`,`disabled`,`onUpdate:checked`]),t.icon?(m(),o(e,{key:0,name:t.icon,class:`c-transfer__item-icon`},null,8,[`name`])):s(`v-if`,!0),l(`span`,M,v(t.label),1)],10,j))),128))])):(m(),c(`div`,N,v(I.value.targetEmptyText),1))])],2)}}}),[[`__scopeId`,`data-v-
|
|
1
|
+
import{t as e}from"./C_Icon2.js";import{t}from"./export-helper.js";import{NCheckbox as n,NInput as r}from"naive-ui";import{Fragment as i,computed as a,createBlock as o,createCommentVNode as s,createElementBlock as c,createElementVNode as l,createTextVNode as u,createVNode as d,defineComponent as f,normalizeClass as p,openBlock as m,reactive as h,ref as g,renderList as _,toDisplayString as v,withCtx as y,withModifiers as b}from"vue";const x={titles:[`可选列表`,`已选列表`],filterable:!1,filterPlaceholder:`搜索...`,showSelectAll:!0,sourceEmptyText:`暂无数据`,targetEmptyText:`未选择任何项`,size:`medium`},S={class:`c-transfer__panel`},ee={class:`c-transfer__header`},te={key:1,class:`c-transfer__title`},ne={class:`c-transfer__count`},re={key:0,class:`c-transfer__filter`},ie={key:1,class:`c-transfer__list`},ae=[`onClick`],C={class:`c-transfer__item-label`},w={key:2,class:`c-transfer__empty`},T={class:`c-transfer__actions`},E=[`disabled`],D=[`disabled`],O={class:`c-transfer__panel`},k={class:`c-transfer__header`},oe={key:1,class:`c-transfer__title`},se={class:`c-transfer__count`},ce={key:0,class:`c-transfer__filter`},A={key:1,class:`c-transfer__list`},j=[`onClick`],M={class:`c-transfer__item-label`},N={key:2,class:`c-transfer__empty`};var P=t(f({__name:`index`,props:{data:{},modelValue:{},titles:{default:()=>x.titles},filterable:{type:Boolean,default:x.filterable},filterPlaceholder:{default:x.filterPlaceholder},filterMethod:{},showSelectAll:{type:Boolean,default:x.showSelectAll},sourceEmptyText:{default:x.sourceEmptyText},targetEmptyText:{default:x.targetEmptyText},size:{default:x.size}},emits:[`update:modelValue`,`change`],setup(t,{emit:f}){let P=t,F=f,I=a(()=>({...x,...P})),L=a(()=>{let e=new Map;for(let t of P.data)e.set(t.key,t);return e}),R=a(()=>new Set(P.modelValue)),z=a(()=>P.data.filter(e=>!R.value.has(e.key))),B=a(()=>{let e=[];for(let t of P.modelValue){let n=L.value.get(t);n&&e.push(n)}return e}),V=g(``),H=g(``);function U(e,t){return t.label.toLowerCase().includes(e.toLowerCase())}let W=a(()=>P.filterMethod??U),G=a(()=>{let e=V.value.trim();return e?z.value.filter(t=>W.value(e,t)):z.value}),K=a(()=>{let e=H.value.trim();return e?B.value.filter(t=>W.value(e,t)):B.value}),q=h(new Set),J=h(new Set);function Y(e){q.has(e)?q.delete(e):q.add(e)}function X(e){J.has(e)?J.delete(e):J.add(e)}let Z=a(()=>G.value.length>0&&G.value.every(e=>e.disabled||q.has(e.key))),le=a(()=>!Z.value&&G.value.some(e=>q.has(e.key))),Q=a(()=>K.value.length>0&&K.value.every(e=>e.disabled||J.has(e.key))),$=a(()=>!Q.value&&K.value.some(e=>J.has(e.key)));function ue(e){for(let t of G.value)t.disabled||(e?q.add(t.key):q.delete(t.key))}function de(e){for(let t of K.value)t.disabled||(e?J.add(t.key):J.delete(t.key))}function fe(){let e=[...q],t=[...P.modelValue,...e];F(`update:modelValue`,t),F(`change`,t,`right`,e),q.clear()}function pe(){let e=[...J],t=new Set(e),n=P.modelValue.filter(e=>!t.has(e));F(`update:modelValue`,n),F(`change`,n,`left`,e),J.clear()}return(t,a)=>{let f=n,h=r;return m(),c(`div`,{class:p([`c-transfer`,[`is-${I.value.size}`]])},[s(` ================ Source Panel ================ `),l(`div`,S,[l(`div`,ee,[I.value.showSelectAll?(m(),o(f,{key:0,checked:Z.value,indeterminate:le.value,"onUpdate:checked":ue},{default:y(()=>[u(v(I.value.titles?.[0]),1)]),_:1},8,[`checked`,`indeterminate`])):(m(),c(`span`,te,v(I.value.titles?.[0]),1)),l(`span`,ne,v(q.size)+` / `+v(z.value.length),1)]),I.value.filterable?(m(),c(`div`,re,[d(h,{value:V.value,"onUpdate:value":a[0]||=e=>V.value=e,clearable:``,size:`small`,placeholder:I.value.filterPlaceholder},{prefix:y(()=>[d(e,{name:`mdi:magnify`})]),_:1},8,[`value`,`placeholder`])])):s(`v-if`,!0),G.value.length?(m(),c(`div`,ie,[(m(!0),c(i,null,_(G.value,t=>(m(),c(`div`,{key:t.key,class:p([`c-transfer__item`,{"is-disabled":t.disabled,"is-checked":q.has(t.key)}]),onClick:e=>!t.disabled&&Y(t.key)},[d(f,{checked:q.has(t.key),disabled:t.disabled,size:`small`,"onUpdate:checked":e=>Y(t.key),onClick:a[1]||=b(()=>{},[`stop`])},null,8,[`checked`,`disabled`,`onUpdate:checked`]),t.icon?(m(),o(e,{key:0,name:t.icon,class:`c-transfer__item-icon`},null,8,[`name`])):s(`v-if`,!0),l(`span`,C,v(t.label),1)],10,ae))),128))])):(m(),c(`div`,w,v(I.value.sourceEmptyText),1))]),s(` ================ Actions ================ `),l(`div`,T,[l(`button`,{class:p([`c-transfer__btn`,{"is-disabled":q.size===0}]),disabled:q.size===0,onClick:fe},[d(e,{name:`mdi:chevron-right`})],10,E),l(`button`,{class:p([`c-transfer__btn`,{"is-disabled":J.size===0}]),disabled:J.size===0,onClick:pe},[d(e,{name:`mdi:chevron-left`})],10,D)]),s(` ================ Target Panel ================ `),l(`div`,O,[l(`div`,k,[I.value.showSelectAll?(m(),o(f,{key:0,checked:Q.value,indeterminate:$.value,"onUpdate:checked":de},{default:y(()=>[u(v(I.value.titles?.[1]),1)]),_:1},8,[`checked`,`indeterminate`])):(m(),c(`span`,oe,v(I.value.titles?.[1]),1)),l(`span`,se,v(J.size)+` / `+v(B.value.length),1)]),I.value.filterable?(m(),c(`div`,ce,[d(h,{value:H.value,"onUpdate:value":a[2]||=e=>H.value=e,clearable:``,size:`small`,placeholder:I.value.filterPlaceholder},{prefix:y(()=>[d(e,{name:`mdi:magnify`})]),_:1},8,[`value`,`placeholder`])])):s(`v-if`,!0),K.value.length?(m(),c(`div`,A,[(m(!0),c(i,null,_(K.value,t=>(m(),c(`div`,{key:t.key,class:p([`c-transfer__item`,{"is-disabled":t.disabled,"is-checked":J.has(t.key)}]),onClick:e=>!t.disabled&&X(t.key)},[d(f,{checked:J.has(t.key),disabled:t.disabled,size:`small`,"onUpdate:checked":e=>X(t.key),onClick:a[3]||=b(()=>{},[`stop`])},null,8,[`checked`,`disabled`,`onUpdate:checked`]),t.icon?(m(),o(e,{key:0,name:t.icon,class:`c-transfer__item-icon`},null,8,[`name`])):s(`v-if`,!0),l(`span`,M,v(t.label),1)],10,j))),128))])):(m(),c(`div`,N,v(I.value.targetEmptyText),1))])],2)}}}),[[`__scopeId`,`data-v-6de1c97f`]]);export{x as n,P as t};
|
|
2
2
|
//# sourceMappingURL=C_Transfer2.js.map
|
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"}
|