@robot-admin/naive-ui-components 0.6.28 → 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.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 +1 -1
- package/dist/constants2.d.ts +2 -2
- package/dist/constants3.d.ts +2 -2
- package/dist/constants4.d.ts +3 -3
- package/dist/constants5.d.ts +2 -2
- package/dist/data.d.ts +1 -1
- 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 +3 -3
- package/dist/index11.vue.d.ts +4 -4
- package/dist/index13.vue.d.ts +2 -2
- package/dist/index15.vue.d.ts +3 -3
- package/dist/index17.vue.d.ts +1 -1
- package/dist/index18.vue.d.ts +4 -4
- package/dist/index2.vue.d.ts +2 -2
- package/dist/index20.vue.d.ts +1 -1
- package/dist/index21.vue.d.ts +1 -1
- package/dist/index22.vue.d.ts +1 -1
- package/dist/index24.vue.d.ts +2 -2
- package/dist/index26.vue.d.ts +3 -3
- package/dist/index3.vue.d.ts +3 -3
- package/dist/index4.vue.d.ts +6 -6
- package/dist/index8.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 +1547 -1547
- package/dist/useCalendarEvents.d.ts +1 -1
- package/dist/useCollapsePanel.d.ts +3 -3
- package/dist/useCropperCore.d.ts +5 -5
- package/dist/useDraggableLayout.d.ts +9 -9
- package/dist/useDynamicFormState.d.ts +2 -2
- package/dist/useEdgeInteraction.d.ts +1 -1
- package/dist/useFullscreen.d.ts +2 -2
- package/dist/useInfiniteScroll.d.ts +1 -1
- package/dist/useQRCode.d.ts +3 -3
- package/dist/useSignatureHistory.d.ts +1 -1
- package/dist/useSplitResize.d.ts +5 -5
- package/dist/useTimeSelection.d.ts +2 -2
- package/dist/useTreeOperations.d.ts +1 -1
- 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_Code2.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./C_Icon2.js";import{t}from"./export-helper.js";import{NButton as n,NCode as r,NModal as i,NSpin as a,NTooltip as o}from"naive-ui";import{Transition as s,computed as c,createBlock as l,createCommentVNode as u,createElementBlock as d,createElementVNode as f,createTextVNode as p,createVNode as m,defineComponent as h,inject as g,nextTick as _,normalizeStyle as v,onMounted as y,onUnmounted as b,openBlock as x,ref as S,toDisplayString as C,watch as w,withCtx as T}from"vue";const E={class:`c-code-wrapper`},D={key:0,class:`c-code-header`},O={class:`c-code-title`},k={class:`c-code-actions`},A={class:`c-code-content`},j={key:0,class:`floating-copy-btn`},M={key:0,class:`c-code-loading`},N={class:`ml-2`},P={class:`fullscreen-content`},F={class:`fullscreen-header`},I={class:`fullscreen-title`},L={class:`fullscreen-body`};var R=t(h({name:`CCode`,__name:`index`,props:{code:{default:``},language:{default:`text`},title:{},showHeader:{type:Boolean,default:!0},showLineNumbers:{type:Boolean,default:!0},wordWrap:{type:Boolean,default:!1},trim:{type:Boolean,default:!0},showFullscreen:{type:Boolean,default:!1},maxHeight:{},autoLoadLanguage:{type:Boolean,default:!0}},emits:[`copy`,`click`,`fullscreen`],setup(t,{expose:h,emit:R}){let z=t,B=R,V=g(`highlightManager`,null),H=S(!1),U=S(!1),W=S(!1),G=S(!1),K=c(()=>{try{return V?.getHljs?.()||null}catch(e){return console.warn(`Highlight.js not available:`,e),null}}),q=c(()=>({javascript:`mdi:language-javascript`,typescript:`mdi:language-typescript`,python:`mdi:language-python`,html:`mdi:language-html5`,css:`mdi:language-css3`,vue:`mdi:vuejs`,react:`mdi:react`,json:`mdi:code-json`,java:`mdi:language-java`,cpp:`mdi:language-cpp`,go:`mdi:language-go`,rust:`mdi:language-rust`,php:`mdi:language-php`,csharp:`mdi:language-csharp`,sql:`mdi:database`,yaml:`mdi:file-code`,xml:`mdi:xml`,markdown:`mdi:language-markdown`,bash:`mdi:bash`,shell:`mdi:console`,powershell:`mdi:powershell`,swift:`mdi:language-swift`,kotlin:`mdi:language-kotlin`,ruby:`mdi:language-ruby`})[z.language.toLowerCase()]||`mdi:code-braces`),J=c(()=>z.maxHeight?{maxHeight:typeof z.maxHeight==`number`?`${z.maxHeight}px`:z.maxHeight}:{});w(()=>z.language,async e=>{if(!(!z.autoLoadLanguage||e===`text`||!V)&&!(V.getLoadedLanguages?.()||[]).includes(e)){W.value=!0;try{await V.loadLanguage?.(e),await _()}catch(t){console.warn(`Failed to load language: ${e}`,t)}finally{W.value=!1}}},{immediate:!0});async function Y(){if(!H.value){H.value=!0;try{await navigator.clipboard.writeText(z.code),B(`copy`,z.code)}catch(e){console.error(`Copy failed:`,e)}finally{H.value=!1}}}function X(){U.value=!U.value,B(`fullscreen`,U.value)}function Z(e){return{javascript:`JavaScript`,typescript:`TypeScript`,python:`Python`,java:`Java`,cpp:`C++`,csharp:`C#`,php:`PHP`,go:`Go`,rust:`Rust`,html:`HTML`,css:`CSS`,json:`JSON`,bash:`Bash`,shell:`Shell`,yaml:`YAML`,xml:`XML`,markdown:`Markdown`,sql:`SQL`,powershell:`PowerShell`,swift:`Swift`,kotlin:`Kotlin`,ruby:`Ruby`,vue:`Vue`,react:`React`}[e.toLowerCase()]||e.toUpperCase()}function Q(e){e.key===`Escape`&&U.value&&X()}return y(()=>{document.addEventListener(`keydown`,Q)}),b(()=>{document.removeEventListener(`keydown`,Q)}),h({copyCode:Y,toggleFullscreen:X}),(t,c)=>{let h=n,g=o,_=r,y=a,b=i;return x(),d(`div`,E,[u(` 代码标题栏 `),t.showHeader?(x(),d(`div`,D,[f(`div`,O,[q.value?(x(),l(e,{key:0,name:q.value,size:16,class:`mr-2`},null,8,[`name`])):u(`v-if`,!0),f(`span`,null,C(t.title||Z(t.language)),1)]),f(`div`,k,[m(g,{trigger:`hover`},{trigger:T(()=>[m(h,{size:`tiny`,quaternary:``,onClick:Y,loading:H.value},{icon:T(()=>[m(e,{name:H.value?`mdi:loading`:`mdi:content-copy`,loading:H.value},null,8,[`name`,`loading`])]),_:1},8,[`loading`])]),default:T(()=>[c[4]||=p(` 复制代码 `,-1)]),_:1,__:[4]}),t.showFullscreen?(x(),l(g,{key:0,trigger:`hover`},{trigger:T(()=>[m(h,{size:`tiny`,quaternary:``,onClick:X},{icon:T(()=>[m(e,{name:U.value?`mdi:fullscreen-exit`:`mdi:fullscreen`},null,8,[`name`])]),_:1})]),default:T(()=>[p(` `+C(U.value?`退出全屏`:`全屏查看`),1)]),_:1})):u(`v-if`,!0)])])):u(`v-if`,!0),u(` 代码内容区域 `),f(`div`,A,[f(`div`,{class:`code-wrapper`,onMouseenter:c[1]||=e=>G.value=!0,onMouseleave:c[2]||=e=>G.value=!1},[(x(),l(_,{key:`code-${t.language}-${t.code.length}`,code:t.code,language:t.language,hljs:K.value,"show-line-numbers":t.showLineNumbers,"word-wrap":t.wordWrap,trim:t.trim,style:v(J.value),onClick:c[0]||=e=>B(`click`,e)},null,8,[`code`,`language`,`hljs`,`show-line-numbers`,`word-wrap`,`trim`,`style`])),u(` 悬浮复制按钮 `),m(s,{name:`fade`},{default:T(()=>[G.value&&!t.showHeader?(x(),d(`div`,j,[m(g,{trigger:`hover`},{trigger:T(()=>[m(h,{size:`small`,quaternary:``,onClick:Y,loading:H.value,class:`copy-floating`},{icon:T(()=>[m(e,{name:H.value?`mdi:loading`:`mdi:content-copy`,loading:H.value},null,8,[`name`,`loading`])]),_:1},8,[`loading`])]),default:T(()=>[c[5]||=p(` 复制代码 `,-1)]),_:1,__:[5]})])):u(`v-if`,!0)]),_:1})],32),u(` 语言加载状态 `),W.value?(x(),d(`div`,M,[m(y,{size:`small`}),f(`span`,N,`正在加载 `+C(t.language)+` 语言包...`,1)])):u(`v-if`,!0)]),u(` 全屏模态框 `),m(b,{show:U.value,"onUpdate:show":c[3]||=e=>U.value=e,"mask-closable":!1,"show-icon":!1,bordered:!1,style:{width:`100vw`,height:`100vh`,margin:`0`,padding:`0`}},{default:T(()=>[f(`div`,P,[f(`div`,F,[f(`div`,I,[q.value?(x(),l(e,{key:0,name:q.value,size:16,class:`mr-2`},null,8,[`name`])):u(`v-if`,!0),f(`span`,null,C(t.title||Z(t.language)),1)]),m(h,{size:`small`,quaternary:``,onClick:X},{icon:T(()=>[m(e,{name:`mdi:close`})]),_:1})]),f(`div`,L,[(x(),l(_,{key:`fullscreen-code-${t.language}-${t.code.length}`,code:t.code,language:t.language,hljs:K.value,"show-line-numbers":t.showLineNumbers,"word-wrap":t.wordWrap,trim:t.trim},null,8,[`code`,`language`,`hljs`,`show-line-numbers`,`word-wrap`,`trim`]))])])]),_:1},8,[`show`])])}}}),[[`__scopeId`,`data-v-
|
|
1
|
+
import{t as e}from"./C_Icon2.js";import{t}from"./export-helper.js";import{NButton as n,NCode as r,NModal as i,NSpin as a,NTooltip as o}from"naive-ui";import{Transition as s,computed as c,createBlock as l,createCommentVNode as u,createElementBlock as d,createElementVNode as f,createTextVNode as p,createVNode as m,defineComponent as h,inject as g,nextTick as _,normalizeStyle as v,onMounted as y,onUnmounted as b,openBlock as x,ref as S,toDisplayString as C,watch as w,withCtx as T}from"vue";const E={class:`c-code-wrapper`},D={key:0,class:`c-code-header`},O={class:`c-code-title`},k={class:`c-code-actions`},A={class:`c-code-content`},j={key:0,class:`floating-copy-btn`},M={key:0,class:`c-code-loading`},N={class:`ml-2`},P={class:`fullscreen-content`},F={class:`fullscreen-header`},I={class:`fullscreen-title`},L={class:`fullscreen-body`};var R=t(h({name:`CCode`,__name:`index`,props:{code:{default:``},language:{default:`text`},title:{},showHeader:{type:Boolean,default:!0},showLineNumbers:{type:Boolean,default:!0},wordWrap:{type:Boolean,default:!1},trim:{type:Boolean,default:!0},showFullscreen:{type:Boolean,default:!1},maxHeight:{},autoLoadLanguage:{type:Boolean,default:!0}},emits:[`copy`,`click`,`fullscreen`],setup(t,{expose:h,emit:R}){let z=t,B=R,V=g(`highlightManager`,null),H=S(!1),U=S(!1),W=S(!1),G=S(!1),K=c(()=>{try{return V?.getHljs?.()||null}catch(e){return console.warn(`Highlight.js not available:`,e),null}}),q=c(()=>({javascript:`mdi:language-javascript`,typescript:`mdi:language-typescript`,python:`mdi:language-python`,html:`mdi:language-html5`,css:`mdi:language-css3`,vue:`mdi:vuejs`,react:`mdi:react`,json:`mdi:code-json`,java:`mdi:language-java`,cpp:`mdi:language-cpp`,go:`mdi:language-go`,rust:`mdi:language-rust`,php:`mdi:language-php`,csharp:`mdi:language-csharp`,sql:`mdi:database`,yaml:`mdi:file-code`,xml:`mdi:xml`,markdown:`mdi:language-markdown`,bash:`mdi:bash`,shell:`mdi:console`,powershell:`mdi:powershell`,swift:`mdi:language-swift`,kotlin:`mdi:language-kotlin`,ruby:`mdi:language-ruby`})[z.language.toLowerCase()]||`mdi:code-braces`),J=c(()=>z.maxHeight?{maxHeight:typeof z.maxHeight==`number`?`${z.maxHeight}px`:z.maxHeight}:{});w(()=>z.language,async e=>{if(!(!z.autoLoadLanguage||e===`text`||!V)&&!(V.getLoadedLanguages?.()||[]).includes(e)){W.value=!0;try{await V.loadLanguage?.(e),await _()}catch(t){console.warn(`Failed to load language: ${e}`,t)}finally{W.value=!1}}},{immediate:!0});async function Y(){if(!H.value){H.value=!0;try{await navigator.clipboard.writeText(z.code),B(`copy`,z.code)}catch(e){console.error(`Copy failed:`,e)}finally{H.value=!1}}}function X(){U.value=!U.value,B(`fullscreen`,U.value)}function Z(e){return{javascript:`JavaScript`,typescript:`TypeScript`,python:`Python`,java:`Java`,cpp:`C++`,csharp:`C#`,php:`PHP`,go:`Go`,rust:`Rust`,html:`HTML`,css:`CSS`,json:`JSON`,bash:`Bash`,shell:`Shell`,yaml:`YAML`,xml:`XML`,markdown:`Markdown`,sql:`SQL`,powershell:`PowerShell`,swift:`Swift`,kotlin:`Kotlin`,ruby:`Ruby`,vue:`Vue`,react:`React`}[e.toLowerCase()]||e.toUpperCase()}function Q(e){e.key===`Escape`&&U.value&&X()}return y(()=>{document.addEventListener(`keydown`,Q)}),b(()=>{document.removeEventListener(`keydown`,Q)}),h({copyCode:Y,toggleFullscreen:X}),(t,c)=>{let h=n,g=o,_=r,y=a,b=i;return x(),d(`div`,E,[u(` 代码标题栏 `),t.showHeader?(x(),d(`div`,D,[f(`div`,O,[q.value?(x(),l(e,{key:0,name:q.value,size:16,class:`mr-2`},null,8,[`name`])):u(`v-if`,!0),f(`span`,null,C(t.title||Z(t.language)),1)]),f(`div`,k,[m(g,{trigger:`hover`},{trigger:T(()=>[m(h,{size:`tiny`,quaternary:``,onClick:Y,loading:H.value},{icon:T(()=>[m(e,{name:H.value?`mdi:loading`:`mdi:content-copy`,loading:H.value},null,8,[`name`,`loading`])]),_:1},8,[`loading`])]),default:T(()=>[c[4]||=p(` 复制代码 `,-1)]),_:1,__:[4]}),t.showFullscreen?(x(),l(g,{key:0,trigger:`hover`},{trigger:T(()=>[m(h,{size:`tiny`,quaternary:``,onClick:X},{icon:T(()=>[m(e,{name:U.value?`mdi:fullscreen-exit`:`mdi:fullscreen`},null,8,[`name`])]),_:1})]),default:T(()=>[p(` `+C(U.value?`退出全屏`:`全屏查看`),1)]),_:1})):u(`v-if`,!0)])])):u(`v-if`,!0),u(` 代码内容区域 `),f(`div`,A,[f(`div`,{class:`code-wrapper`,onMouseenter:c[1]||=e=>G.value=!0,onMouseleave:c[2]||=e=>G.value=!1},[(x(),l(_,{key:`code-${t.language}-${t.code.length}`,code:t.code,language:t.language,hljs:K.value,"show-line-numbers":t.showLineNumbers,"word-wrap":t.wordWrap,trim:t.trim,style:v(J.value),onClick:c[0]||=e=>B(`click`,e)},null,8,[`code`,`language`,`hljs`,`show-line-numbers`,`word-wrap`,`trim`,`style`])),u(` 悬浮复制按钮 `),m(s,{name:`fade`},{default:T(()=>[G.value&&!t.showHeader?(x(),d(`div`,j,[m(g,{trigger:`hover`},{trigger:T(()=>[m(h,{size:`small`,quaternary:``,onClick:Y,loading:H.value,class:`copy-floating`},{icon:T(()=>[m(e,{name:H.value?`mdi:loading`:`mdi:content-copy`,loading:H.value},null,8,[`name`,`loading`])]),_:1},8,[`loading`])]),default:T(()=>[c[5]||=p(` 复制代码 `,-1)]),_:1,__:[5]})])):u(`v-if`,!0)]),_:1})],32),u(` 语言加载状态 `),W.value?(x(),d(`div`,M,[m(y,{size:`small`}),f(`span`,N,`正在加载 `+C(t.language)+` 语言包...`,1)])):u(`v-if`,!0)]),u(` 全屏模态框 `),m(b,{show:U.value,"onUpdate:show":c[3]||=e=>U.value=e,"mask-closable":!1,"show-icon":!1,bordered:!1,style:{width:`100vw`,height:`100vh`,margin:`0`,padding:`0`}},{default:T(()=>[f(`div`,P,[f(`div`,F,[f(`div`,I,[q.value?(x(),l(e,{key:0,name:q.value,size:16,class:`mr-2`},null,8,[`name`])):u(`v-if`,!0),f(`span`,null,C(t.title||Z(t.language)),1)]),m(h,{size:`small`,quaternary:``,onClick:X},{icon:T(()=>[m(e,{name:`mdi:close`})]),_:1})]),f(`div`,L,[(x(),l(_,{key:`fullscreen-code-${t.language}-${t.code.length}`,code:t.code,language:t.language,hljs:K.value,"show-line-numbers":t.showLineNumbers,"word-wrap":t.wordWrap,trim:t.trim},null,8,[`code`,`language`,`hljs`,`show-line-numbers`,`word-wrap`,`trim`]))])])]),_:1},8,[`show`])])}}}),[[`__scopeId`,`data-v-bc1cd0d6`]]);export{R as t};
|
|
2
2
|
//# sourceMappingURL=C_Code2.js.map
|
package/dist/C_Code2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"C_Code2.js","names":["showHeader","title","language","showFullscreen","code","showLineNumbers","wordWrap","trim"],"sources":["../src/components/C_Code/index.vue","../src/components/C_Code/index.vue","../src/components/C_Code/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-19\r\n * @Description: 全局代码高亮组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-code-wrapper\">\r\n <!-- 代码标题栏 -->\r\n <div v-if=\"showHeader\" class=\"c-code-header\">\r\n <div class=\"c-code-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <div class=\"c-code-actions\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"tiny\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n\r\n <NTooltip v-if=\"showFullscreen\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton size=\"tiny\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon\r\n :name=\"\r\n isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen'\r\n \"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n {{ isFullscreen ? \"退出全屏\" : \"全屏查看\" }}\r\n </NTooltip>\r\n </div>\r\n </div>\r\n\r\n <!-- 代码内容区域 -->\r\n <div class=\"c-code-content\">\r\n <div\r\n class=\"code-wrapper\"\r\n @mouseenter=\"showFloatingCopy = true\"\r\n @mouseleave=\"showFloatingCopy = false\"\r\n >\r\n <NCode\r\n :key=\"`code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n :style=\"codeStyle\"\r\n @click=\"emit('click', $event)\"\r\n />\r\n\r\n <!-- 悬浮复制按钮 -->\r\n <Transition name=\"fade\">\r\n <div v-if=\"showFloatingCopy && !showHeader\" class=\"floating-copy-btn\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"small\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n class=\"copy-floating\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n </div>\r\n </Transition>\r\n </div>\r\n\r\n <!-- 语言加载状态 -->\r\n <div v-if=\"languageLoading\" class=\"c-code-loading\">\r\n <NSpin size=\"small\" />\r\n <span class=\"ml-2\">正在加载 {{ language }} 语言包...</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 全屏模态框 -->\r\n <NModal\r\n v-model:show=\"isFullscreen\"\r\n :mask-closable=\"false\"\r\n :show-icon=\"false\"\r\n :bordered=\"false\"\r\n style=\"width: 100vw; height: 100vh; margin: 0; padding: 0\"\r\n >\r\n <div class=\"fullscreen-content\">\r\n <div class=\"fullscreen-header\">\r\n <div class=\"fullscreen-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <NButton size=\"small\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n <div class=\"fullscreen-body\">\r\n <NCode\r\n :key=\"`fullscreen-code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n />\r\n </div>\r\n </div>\r\n </NModal>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n onMounted,\r\n onUnmounted,\r\n nextTick,\r\n inject,\r\n} from \"vue\";\r\nimport C_Icon from \"../C_Icon/index.vue\";\r\n\r\n// 直接在组件内定义 HighlightManager 接口,避免导入错误\r\ninterface HighlightManager {\r\n getHljs?: () => any;\r\n loadLanguage?: (language: string) => Promise<void>;\r\n getLoadedLanguages?: () => string[];\r\n}\r\n\r\n// 组件名称定义\r\ndefineOptions({\r\n name: \"CCode\",\r\n});\r\n\r\ninterface Props {\r\n code: string;\r\n language?: string;\r\n title?: string;\r\n showHeader?: boolean;\r\n showLineNumbers?: boolean;\r\n wordWrap?: boolean;\r\n trim?: boolean;\r\n showFullscreen?: boolean;\r\n maxHeight?: string | number;\r\n autoLoadLanguage?: boolean;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n code: \"\",\r\n language: \"text\",\r\n showHeader: true,\r\n showLineNumbers: true,\r\n wordWrap: false,\r\n trim: true,\r\n showFullscreen: false,\r\n autoLoadLanguage: true,\r\n});\r\n\r\nconst emit = defineEmits<{\r\n copy: [code: string];\r\n click: [event: MouseEvent];\r\n fullscreen: [isFullscreen: boolean];\r\n}>();\r\n\r\n// 尝试获取 highlight 实例(如果用户安装了插件)\r\nconst highlightManager = inject<HighlightManager | null>(\r\n \"highlightManager\",\r\n null,\r\n);\r\n\r\nconst copying = ref(false);\r\nconst isFullscreen = ref(false);\r\nconst languageLoading = ref(false);\r\nconst showFloatingCopy = ref(false);\r\n\r\n// 安全地获取 hljs 实例\r\nconst hljs = computed(() => {\r\n try {\r\n return highlightManager?.getHljs?.() || null;\r\n } catch (error) {\r\n console.warn(\"Highlight.js not available:\", error);\r\n return null;\r\n }\r\n});\r\n\r\n// 语言图标映射(使用标准的 MDI 图标名称)\r\nconst languageIcon = computed(() => {\r\n const iconMap: Record<string, string> = {\r\n javascript: \"mdi:language-javascript\",\r\n typescript: \"mdi:language-typescript\",\r\n python: \"mdi:language-python\",\r\n html: \"mdi:language-html5\",\r\n css: \"mdi:language-css3\",\r\n vue: \"mdi:vuejs\",\r\n react: \"mdi:react\",\r\n json: \"mdi:code-json\",\r\n java: \"mdi:language-java\",\r\n cpp: \"mdi:language-cpp\",\r\n go: \"mdi:language-go\",\r\n rust: \"mdi:language-rust\",\r\n php: \"mdi:language-php\",\r\n csharp: \"mdi:language-csharp\",\r\n sql: \"mdi:database\",\r\n yaml: \"mdi:file-code\",\r\n xml: \"mdi:xml\",\r\n markdown: \"mdi:language-markdown\",\r\n bash: \"mdi:bash\",\r\n shell: \"mdi:console\",\r\n powershell: \"mdi:powershell\",\r\n swift: \"mdi:language-swift\",\r\n kotlin: \"mdi:language-kotlin\",\r\n ruby: \"mdi:language-ruby\",\r\n };\r\n return iconMap[props.language.toLowerCase()] || \"mdi:code-braces\";\r\n});\r\n\r\n// 代码样式\r\nconst codeStyle = computed(() => {\r\n if (!props.maxHeight) return {};\r\n return {\r\n maxHeight:\r\n typeof props.maxHeight === \"number\"\r\n ? `${props.maxHeight}px`\r\n : props.maxHeight,\r\n };\r\n});\r\n\r\n// 自动加载语言包\r\nwatch(\r\n () => props.language,\r\n async (newLanguage) => {\r\n if (!props.autoLoadLanguage || newLanguage === \"text\" || !highlightManager)\r\n return;\r\n\r\n const loadedLanguages = highlightManager.getLoadedLanguages?.() || [];\r\n if (loadedLanguages.includes(newLanguage)) return;\r\n\r\n languageLoading.value = true;\r\n try {\r\n await highlightManager.loadLanguage?.(newLanguage);\r\n await nextTick();\r\n } catch (error) {\r\n console.warn(`Failed to load language: ${newLanguage}`, error);\r\n } finally {\r\n languageLoading.value = false;\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n\r\n/**\r\n * 复制代码到剪贴板\r\n */\r\nasync function copyCode() {\r\n if (copying.value) return;\r\n copying.value = true;\r\n\r\n try {\r\n await navigator.clipboard.writeText(props.code);\r\n emit(\"copy\", props.code);\r\n } catch (error) {\r\n console.error(\"Copy failed:\", error);\r\n } finally {\r\n copying.value = false;\r\n }\r\n}\r\n\r\n/**\r\n * 切换全屏显示状态\r\n */\r\nfunction toggleFullscreen() {\r\n isFullscreen.value = !isFullscreen.value;\r\n emit(\"fullscreen\", isFullscreen.value);\r\n}\r\n\r\n/**\r\n * 获取编程语言的显示标题\r\n */\r\nfunction getLanguageTitle(lang: string): string {\r\n const titleMap: Record<string, string> = {\r\n javascript: \"JavaScript\",\r\n typescript: \"TypeScript\",\r\n python: \"Python\",\r\n java: \"Java\",\r\n cpp: \"C++\",\r\n csharp: \"C#\",\r\n php: \"PHP\",\r\n go: \"Go\",\r\n rust: \"Rust\",\r\n html: \"HTML\",\r\n css: \"CSS\",\r\n json: \"JSON\",\r\n bash: \"Bash\",\r\n shell: \"Shell\",\r\n yaml: \"YAML\",\r\n xml: \"XML\",\r\n markdown: \"Markdown\",\r\n sql: \"SQL\",\r\n powershell: \"PowerShell\",\r\n swift: \"Swift\",\r\n kotlin: \"Kotlin\",\r\n ruby: \"Ruby\",\r\n vue: \"Vue\",\r\n react: \"React\",\r\n };\r\n return titleMap[lang.toLowerCase()] || lang.toUpperCase();\r\n}\r\n\r\n/**\r\n * 处理ESC键退出全屏\r\n */\r\nfunction handleEscapeKey(event: KeyboardEvent) {\r\n if (event.key === \"Escape\" && isFullscreen.value) {\r\n toggleFullscreen();\r\n }\r\n}\r\n\r\nonMounted(() => {\r\n document.addEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\nonUnmounted(() => {\r\n document.removeEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\ndefineExpose({\r\n copyCode,\r\n toggleFullscreen,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.mr-2 {\r\n margin-right: 0.5rem;\r\n}\r\n\r\n.ml-2 {\r\n margin-left: 0.5rem;\r\n}\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-19\r\n * @Description: 全局代码高亮组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-code-wrapper\">\r\n <!-- 代码标题栏 -->\r\n <div v-if=\"showHeader\" class=\"c-code-header\">\r\n <div class=\"c-code-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <div class=\"c-code-actions\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"tiny\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n\r\n <NTooltip v-if=\"showFullscreen\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton size=\"tiny\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon\r\n :name=\"\r\n isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen'\r\n \"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n {{ isFullscreen ? \"退出全屏\" : \"全屏查看\" }}\r\n </NTooltip>\r\n </div>\r\n </div>\r\n\r\n <!-- 代码内容区域 -->\r\n <div class=\"c-code-content\">\r\n <div\r\n class=\"code-wrapper\"\r\n @mouseenter=\"showFloatingCopy = true\"\r\n @mouseleave=\"showFloatingCopy = false\"\r\n >\r\n <NCode\r\n :key=\"`code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n :style=\"codeStyle\"\r\n @click=\"emit('click', $event)\"\r\n />\r\n\r\n <!-- 悬浮复制按钮 -->\r\n <Transition name=\"fade\">\r\n <div v-if=\"showFloatingCopy && !showHeader\" class=\"floating-copy-btn\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"small\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n class=\"copy-floating\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n </div>\r\n </Transition>\r\n </div>\r\n\r\n <!-- 语言加载状态 -->\r\n <div v-if=\"languageLoading\" class=\"c-code-loading\">\r\n <NSpin size=\"small\" />\r\n <span class=\"ml-2\">正在加载 {{ language }} 语言包...</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 全屏模态框 -->\r\n <NModal\r\n v-model:show=\"isFullscreen\"\r\n :mask-closable=\"false\"\r\n :show-icon=\"false\"\r\n :bordered=\"false\"\r\n style=\"width: 100vw; height: 100vh; margin: 0; padding: 0\"\r\n >\r\n <div class=\"fullscreen-content\">\r\n <div class=\"fullscreen-header\">\r\n <div class=\"fullscreen-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <NButton size=\"small\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n <div class=\"fullscreen-body\">\r\n <NCode\r\n :key=\"`fullscreen-code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n />\r\n </div>\r\n </div>\r\n </NModal>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n onMounted,\r\n onUnmounted,\r\n nextTick,\r\n inject,\r\n} from \"vue\";\r\nimport C_Icon from \"../C_Icon/index.vue\";\r\n\r\n// 直接在组件内定义 HighlightManager 接口,避免导入错误\r\ninterface HighlightManager {\r\n getHljs?: () => any;\r\n loadLanguage?: (language: string) => Promise<void>;\r\n getLoadedLanguages?: () => string[];\r\n}\r\n\r\n// 组件名称定义\r\ndefineOptions({\r\n name: \"CCode\",\r\n});\r\n\r\ninterface Props {\r\n code: string;\r\n language?: string;\r\n title?: string;\r\n showHeader?: boolean;\r\n showLineNumbers?: boolean;\r\n wordWrap?: boolean;\r\n trim?: boolean;\r\n showFullscreen?: boolean;\r\n maxHeight?: string | number;\r\n autoLoadLanguage?: boolean;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n code: \"\",\r\n language: \"text\",\r\n showHeader: true,\r\n showLineNumbers: true,\r\n wordWrap: false,\r\n trim: true,\r\n showFullscreen: false,\r\n autoLoadLanguage: true,\r\n});\r\n\r\nconst emit = defineEmits<{\r\n copy: [code: string];\r\n click: [event: MouseEvent];\r\n fullscreen: [isFullscreen: boolean];\r\n}>();\r\n\r\n// 尝试获取 highlight 实例(如果用户安装了插件)\r\nconst highlightManager = inject<HighlightManager | null>(\r\n \"highlightManager\",\r\n null,\r\n);\r\n\r\nconst copying = ref(false);\r\nconst isFullscreen = ref(false);\r\nconst languageLoading = ref(false);\r\nconst showFloatingCopy = ref(false);\r\n\r\n// 安全地获取 hljs 实例\r\nconst hljs = computed(() => {\r\n try {\r\n return highlightManager?.getHljs?.() || null;\r\n } catch (error) {\r\n console.warn(\"Highlight.js not available:\", error);\r\n return null;\r\n }\r\n});\r\n\r\n// 语言图标映射(使用标准的 MDI 图标名称)\r\nconst languageIcon = computed(() => {\r\n const iconMap: Record<string, string> = {\r\n javascript: \"mdi:language-javascript\",\r\n typescript: \"mdi:language-typescript\",\r\n python: \"mdi:language-python\",\r\n html: \"mdi:language-html5\",\r\n css: \"mdi:language-css3\",\r\n vue: \"mdi:vuejs\",\r\n react: \"mdi:react\",\r\n json: \"mdi:code-json\",\r\n java: \"mdi:language-java\",\r\n cpp: \"mdi:language-cpp\",\r\n go: \"mdi:language-go\",\r\n rust: \"mdi:language-rust\",\r\n php: \"mdi:language-php\",\r\n csharp: \"mdi:language-csharp\",\r\n sql: \"mdi:database\",\r\n yaml: \"mdi:file-code\",\r\n xml: \"mdi:xml\",\r\n markdown: \"mdi:language-markdown\",\r\n bash: \"mdi:bash\",\r\n shell: \"mdi:console\",\r\n powershell: \"mdi:powershell\",\r\n swift: \"mdi:language-swift\",\r\n kotlin: \"mdi:language-kotlin\",\r\n ruby: \"mdi:language-ruby\",\r\n };\r\n return iconMap[props.language.toLowerCase()] || \"mdi:code-braces\";\r\n});\r\n\r\n// 代码样式\r\nconst codeStyle = computed(() => {\r\n if (!props.maxHeight) return {};\r\n return {\r\n maxHeight:\r\n typeof props.maxHeight === \"number\"\r\n ? `${props.maxHeight}px`\r\n : props.maxHeight,\r\n };\r\n});\r\n\r\n// 自动加载语言包\r\nwatch(\r\n () => props.language,\r\n async (newLanguage) => {\r\n if (!props.autoLoadLanguage || newLanguage === \"text\" || !highlightManager)\r\n return;\r\n\r\n const loadedLanguages = highlightManager.getLoadedLanguages?.() || [];\r\n if (loadedLanguages.includes(newLanguage)) return;\r\n\r\n languageLoading.value = true;\r\n try {\r\n await highlightManager.loadLanguage?.(newLanguage);\r\n await nextTick();\r\n } catch (error) {\r\n console.warn(`Failed to load language: ${newLanguage}`, error);\r\n } finally {\r\n languageLoading.value = false;\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n\r\n/**\r\n * 复制代码到剪贴板\r\n */\r\nasync function copyCode() {\r\n if (copying.value) return;\r\n copying.value = true;\r\n\r\n try {\r\n await navigator.clipboard.writeText(props.code);\r\n emit(\"copy\", props.code);\r\n } catch (error) {\r\n console.error(\"Copy failed:\", error);\r\n } finally {\r\n copying.value = false;\r\n }\r\n}\r\n\r\n/**\r\n * 切换全屏显示状态\r\n */\r\nfunction toggleFullscreen() {\r\n isFullscreen.value = !isFullscreen.value;\r\n emit(\"fullscreen\", isFullscreen.value);\r\n}\r\n\r\n/**\r\n * 获取编程语言的显示标题\r\n */\r\nfunction getLanguageTitle(lang: string): string {\r\n const titleMap: Record<string, string> = {\r\n javascript: \"JavaScript\",\r\n typescript: \"TypeScript\",\r\n python: \"Python\",\r\n java: \"Java\",\r\n cpp: \"C++\",\r\n csharp: \"C#\",\r\n php: \"PHP\",\r\n go: \"Go\",\r\n rust: \"Rust\",\r\n html: \"HTML\",\r\n css: \"CSS\",\r\n json: \"JSON\",\r\n bash: \"Bash\",\r\n shell: \"Shell\",\r\n yaml: \"YAML\",\r\n xml: \"XML\",\r\n markdown: \"Markdown\",\r\n sql: \"SQL\",\r\n powershell: \"PowerShell\",\r\n swift: \"Swift\",\r\n kotlin: \"Kotlin\",\r\n ruby: \"Ruby\",\r\n vue: \"Vue\",\r\n react: \"React\",\r\n };\r\n return titleMap[lang.toLowerCase()] || lang.toUpperCase();\r\n}\r\n\r\n/**\r\n * 处理ESC键退出全屏\r\n */\r\nfunction handleEscapeKey(event: KeyboardEvent) {\r\n if (event.key === \"Escape\" && isFullscreen.value) {\r\n toggleFullscreen();\r\n }\r\n}\r\n\r\nonMounted(() => {\r\n document.addEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\nonUnmounted(() => {\r\n document.removeEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\ndefineExpose({\r\n copyCode,\r\n toggleFullscreen,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.mr-2 {\r\n margin-right: 0.5rem;\r\n}\r\n\r\n.ml-2 {\r\n margin-left: 0.5rem;\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-19\r\n * @Description: 全局代码高亮组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-code-wrapper\">\r\n <!-- 代码标题栏 -->\r\n <div v-if=\"showHeader\" class=\"c-code-header\">\r\n <div class=\"c-code-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <div class=\"c-code-actions\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"tiny\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n\r\n <NTooltip v-if=\"showFullscreen\" trigger=\"hover\">\r\n <template #trigger>\r\n <NButton size=\"tiny\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon\r\n :name=\"\r\n isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen'\r\n \"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n {{ isFullscreen ? \"退出全屏\" : \"全屏查看\" }}\r\n </NTooltip>\r\n </div>\r\n </div>\r\n\r\n <!-- 代码内容区域 -->\r\n <div class=\"c-code-content\">\r\n <div\r\n class=\"code-wrapper\"\r\n @mouseenter=\"showFloatingCopy = true\"\r\n @mouseleave=\"showFloatingCopy = false\"\r\n >\r\n <NCode\r\n :key=\"`code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n :style=\"codeStyle\"\r\n @click=\"emit('click', $event)\"\r\n />\r\n\r\n <!-- 悬浮复制按钮 -->\r\n <Transition name=\"fade\">\r\n <div v-if=\"showFloatingCopy && !showHeader\" class=\"floating-copy-btn\">\r\n <NTooltip trigger=\"hover\">\r\n <template #trigger>\r\n <NButton\r\n size=\"small\"\r\n quaternary\r\n @click=\"copyCode\"\r\n :loading=\"copying\"\r\n class=\"copy-floating\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\r\n :loading=\"copying\"\r\n />\r\n </template>\r\n </NButton>\r\n </template>\r\n 复制代码\r\n </NTooltip>\r\n </div>\r\n </Transition>\r\n </div>\r\n\r\n <!-- 语言加载状态 -->\r\n <div v-if=\"languageLoading\" class=\"c-code-loading\">\r\n <NSpin size=\"small\" />\r\n <span class=\"ml-2\">正在加载 {{ language }} 语言包...</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 全屏模态框 -->\r\n <NModal\r\n v-model:show=\"isFullscreen\"\r\n :mask-closable=\"false\"\r\n :show-icon=\"false\"\r\n :bordered=\"false\"\r\n style=\"width: 100vw; height: 100vh; margin: 0; padding: 0\"\r\n >\r\n <div class=\"fullscreen-content\">\r\n <div class=\"fullscreen-header\">\r\n <div class=\"fullscreen-title\">\r\n <C_Icon\r\n v-if=\"languageIcon\"\r\n :name=\"languageIcon\"\r\n :size=\"16\"\r\n class=\"mr-2\"\r\n />\r\n <span>{{ title || getLanguageTitle(language) }}</span>\r\n </div>\r\n <NButton size=\"small\" quaternary @click=\"toggleFullscreen\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n <div class=\"fullscreen-body\">\r\n <NCode\r\n :key=\"`fullscreen-code-${language}-${code.length}`\"\r\n :code=\"code\"\r\n :language=\"language\"\r\n :hljs=\"hljs\"\r\n :show-line-numbers=\"showLineNumbers\"\r\n :word-wrap=\"wordWrap\"\r\n :trim=\"trim\"\r\n />\r\n </div>\r\n </div>\r\n </NModal>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n onMounted,\r\n onUnmounted,\r\n nextTick,\r\n inject,\r\n} from \"vue\";\r\nimport C_Icon from \"../C_Icon/index.vue\";\r\n\r\n// 直接在组件内定义 HighlightManager 接口,避免导入错误\r\ninterface HighlightManager {\r\n getHljs?: () => any;\r\n loadLanguage?: (language: string) => Promise<void>;\r\n getLoadedLanguages?: () => string[];\r\n}\r\n\r\n// 组件名称定义\r\ndefineOptions({\r\n name: \"CCode\",\r\n});\r\n\r\ninterface Props {\r\n code: string;\r\n language?: string;\r\n title?: string;\r\n showHeader?: boolean;\r\n showLineNumbers?: boolean;\r\n wordWrap?: boolean;\r\n trim?: boolean;\r\n showFullscreen?: boolean;\r\n maxHeight?: string | number;\r\n autoLoadLanguage?: boolean;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n code: \"\",\r\n language: \"text\",\r\n showHeader: true,\r\n showLineNumbers: true,\r\n wordWrap: false,\r\n trim: true,\r\n showFullscreen: false,\r\n autoLoadLanguage: true,\r\n});\r\n\r\nconst emit = defineEmits<{\r\n copy: [code: string];\r\n click: [event: MouseEvent];\r\n fullscreen: [isFullscreen: boolean];\r\n}>();\r\n\r\n// 尝试获取 highlight 实例(如果用户安装了插件)\r\nconst highlightManager = inject<HighlightManager | null>(\r\n \"highlightManager\",\r\n null,\r\n);\r\n\r\nconst copying = ref(false);\r\nconst isFullscreen = ref(false);\r\nconst languageLoading = ref(false);\r\nconst showFloatingCopy = ref(false);\r\n\r\n// 安全地获取 hljs 实例\r\nconst hljs = computed(() => {\r\n try {\r\n return highlightManager?.getHljs?.() || null;\r\n } catch (error) {\r\n console.warn(\"Highlight.js not available:\", error);\r\n return null;\r\n }\r\n});\r\n\r\n// 语言图标映射(使用标准的 MDI 图标名称)\r\nconst languageIcon = computed(() => {\r\n const iconMap: Record<string, string> = {\r\n javascript: \"mdi:language-javascript\",\r\n typescript: \"mdi:language-typescript\",\r\n python: \"mdi:language-python\",\r\n html: \"mdi:language-html5\",\r\n css: \"mdi:language-css3\",\r\n vue: \"mdi:vuejs\",\r\n react: \"mdi:react\",\r\n json: \"mdi:code-json\",\r\n java: \"mdi:language-java\",\r\n cpp: \"mdi:language-cpp\",\r\n go: \"mdi:language-go\",\r\n rust: \"mdi:language-rust\",\r\n php: \"mdi:language-php\",\r\n csharp: \"mdi:language-csharp\",\r\n sql: \"mdi:database\",\r\n yaml: \"mdi:file-code\",\r\n xml: \"mdi:xml\",\r\n markdown: \"mdi:language-markdown\",\r\n bash: \"mdi:bash\",\r\n shell: \"mdi:console\",\r\n powershell: \"mdi:powershell\",\r\n swift: \"mdi:language-swift\",\r\n kotlin: \"mdi:language-kotlin\",\r\n ruby: \"mdi:language-ruby\",\r\n };\r\n return iconMap[props.language.toLowerCase()] || \"mdi:code-braces\";\r\n});\r\n\r\n// 代码样式\r\nconst codeStyle = computed(() => {\r\n if (!props.maxHeight) return {};\r\n return {\r\n maxHeight:\r\n typeof props.maxHeight === \"number\"\r\n ? `${props.maxHeight}px`\r\n : props.maxHeight,\r\n };\r\n});\r\n\r\n// 自动加载语言包\r\nwatch(\r\n () => props.language,\r\n async (newLanguage) => {\r\n if (!props.autoLoadLanguage || newLanguage === \"text\" || !highlightManager)\r\n return;\r\n\r\n const loadedLanguages = highlightManager.getLoadedLanguages?.() || [];\r\n if (loadedLanguages.includes(newLanguage)) return;\r\n\r\n languageLoading.value = true;\r\n try {\r\n await highlightManager.loadLanguage?.(newLanguage);\r\n await nextTick();\r\n } catch (error) {\r\n console.warn(`Failed to load language: ${newLanguage}`, error);\r\n } finally {\r\n languageLoading.value = false;\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n\r\n/**\r\n * 复制代码到剪贴板\r\n */\r\nasync function copyCode() {\r\n if (copying.value) return;\r\n copying.value = true;\r\n\r\n try {\r\n await navigator.clipboard.writeText(props.code);\r\n emit(\"copy\", props.code);\r\n } catch (error) {\r\n console.error(\"Copy failed:\", error);\r\n } finally {\r\n copying.value = false;\r\n }\r\n}\r\n\r\n/**\r\n * 切换全屏显示状态\r\n */\r\nfunction toggleFullscreen() {\r\n isFullscreen.value = !isFullscreen.value;\r\n emit(\"fullscreen\", isFullscreen.value);\r\n}\r\n\r\n/**\r\n * 获取编程语言的显示标题\r\n */\r\nfunction getLanguageTitle(lang: string): string {\r\n const titleMap: Record<string, string> = {\r\n javascript: \"JavaScript\",\r\n typescript: \"TypeScript\",\r\n python: \"Python\",\r\n java: \"Java\",\r\n cpp: \"C++\",\r\n csharp: \"C#\",\r\n php: \"PHP\",\r\n go: \"Go\",\r\n rust: \"Rust\",\r\n html: \"HTML\",\r\n css: \"CSS\",\r\n json: \"JSON\",\r\n bash: \"Bash\",\r\n shell: \"Shell\",\r\n yaml: \"YAML\",\r\n xml: \"XML\",\r\n markdown: \"Markdown\",\r\n sql: \"SQL\",\r\n powershell: \"PowerShell\",\r\n swift: \"Swift\",\r\n kotlin: \"Kotlin\",\r\n ruby: \"Ruby\",\r\n vue: \"Vue\",\r\n react: \"React\",\r\n };\r\n return titleMap[lang.toLowerCase()] || lang.toUpperCase();\r\n}\r\n\r\n/**\r\n * 处理ESC键退出全屏\r\n */\r\nfunction handleEscapeKey(event: KeyboardEvent) {\r\n if (event.key === \"Escape\" && isFullscreen.value) {\r\n toggleFullscreen();\r\n }\r\n}\r\n\r\nonMounted(() => {\r\n document.addEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\nonUnmounted(() => {\r\n document.removeEventListener(\"keydown\", handleEscapeKey);\r\n});\r\n\r\ndefineExpose({\r\n copyCode,\r\n toggleFullscreen,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.mr-2 {\r\n margin-right: 0.5rem;\r\n}\r\n\r\n.ml-2 {\r\n margin-left: 0.5rem;\r\n}\r\n</style>\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"C_Code2.js","names":["showHeader","title","language","showFullscreen","code","showLineNumbers","wordWrap","trim"],"sources":["../src/components/C_Code/index.vue","../src/components/C_Code/index.vue","../src/components/C_Code/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-19\n * @Description: 全局代码高亮组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-code-wrapper\">\n <!-- 代码标题栏 -->\n <div v-if=\"showHeader\" class=\"c-code-header\">\n <div class=\"c-code-title\">\n <C_Icon\n v-if=\"languageIcon\"\n :name=\"languageIcon\"\n :size=\"16\"\n class=\"mr-2\"\n />\n <span>{{ title || getLanguageTitle(language) }}</span>\n </div>\n <div class=\"c-code-actions\">\n <NTooltip trigger=\"hover\">\n <template #trigger>\n <NButton\n size=\"tiny\"\n quaternary\n @click=\"copyCode\"\n :loading=\"copying\"\n >\n <template #icon>\n <C_Icon\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\n :loading=\"copying\"\n />\n </template>\n </NButton>\n </template>\n 复制代码\n </NTooltip>\n\n <NTooltip v-if=\"showFullscreen\" trigger=\"hover\">\n <template #trigger>\n <NButton size=\"tiny\" quaternary @click=\"toggleFullscreen\">\n <template #icon>\n <C_Icon\n :name=\"\n isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen'\n \"\n />\n </template>\n </NButton>\n </template>\n {{ isFullscreen ? \"退出全屏\" : \"全屏查看\" }}\n </NTooltip>\n </div>\n </div>\n\n <!-- 代码内容区域 -->\n <div class=\"c-code-content\">\n <div\n class=\"code-wrapper\"\n @mouseenter=\"showFloatingCopy = true\"\n @mouseleave=\"showFloatingCopy = false\"\n >\n <NCode\n :key=\"`code-${language}-${code.length}`\"\n :code=\"code\"\n :language=\"language\"\n :hljs=\"hljs\"\n :show-line-numbers=\"showLineNumbers\"\n :word-wrap=\"wordWrap\"\n :trim=\"trim\"\n :style=\"codeStyle\"\n @click=\"emit('click', $event)\"\n />\n\n <!-- 悬浮复制按钮 -->\n <Transition name=\"fade\">\n <div v-if=\"showFloatingCopy && !showHeader\" class=\"floating-copy-btn\">\n <NTooltip trigger=\"hover\">\n <template #trigger>\n <NButton\n size=\"small\"\n quaternary\n @click=\"copyCode\"\n :loading=\"copying\"\n class=\"copy-floating\"\n >\n <template #icon>\n <C_Icon\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\n :loading=\"copying\"\n />\n </template>\n </NButton>\n </template>\n 复制代码\n </NTooltip>\n </div>\n </Transition>\n </div>\n\n <!-- 语言加载状态 -->\n <div v-if=\"languageLoading\" class=\"c-code-loading\">\n <NSpin size=\"small\" />\n <span class=\"ml-2\">正在加载 {{ language }} 语言包...</span>\n </div>\n </div>\n\n <!-- 全屏模态框 -->\n <NModal\n v-model:show=\"isFullscreen\"\n :mask-closable=\"false\"\n :show-icon=\"false\"\n :bordered=\"false\"\n style=\"width: 100vw; height: 100vh; margin: 0; padding: 0\"\n >\n <div class=\"fullscreen-content\">\n <div class=\"fullscreen-header\">\n <div class=\"fullscreen-title\">\n <C_Icon\n v-if=\"languageIcon\"\n :name=\"languageIcon\"\n :size=\"16\"\n class=\"mr-2\"\n />\n <span>{{ title || getLanguageTitle(language) }}</span>\n </div>\n <NButton size=\"small\" quaternary @click=\"toggleFullscreen\">\n <template #icon>\n <C_Icon name=\"mdi:close\" />\n </template>\n </NButton>\n </div>\n <div class=\"fullscreen-body\">\n <NCode\n :key=\"`fullscreen-code-${language}-${code.length}`\"\n :code=\"code\"\n :language=\"language\"\n :hljs=\"hljs\"\n :show-line-numbers=\"showLineNumbers\"\n :word-wrap=\"wordWrap\"\n :trim=\"trim\"\n />\n </div>\n </div>\n </NModal>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n ref,\n computed,\n watch,\n onMounted,\n onUnmounted,\n nextTick,\n inject,\n} from \"vue\";\nimport C_Icon from \"../C_Icon/index.vue\";\n\n// 直接在组件内定义 HighlightManager 接口,避免导入错误\ninterface HighlightManager {\n getHljs?: () => any;\n loadLanguage?: (language: string) => Promise<void>;\n getLoadedLanguages?: () => string[];\n}\n\n// 组件名称定义\ndefineOptions({\n name: \"CCode\",\n});\n\ninterface Props {\n code: string;\n language?: string;\n title?: string;\n showHeader?: boolean;\n showLineNumbers?: boolean;\n wordWrap?: boolean;\n trim?: boolean;\n showFullscreen?: boolean;\n maxHeight?: string | number;\n autoLoadLanguage?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n code: \"\",\n language: \"text\",\n showHeader: true,\n showLineNumbers: true,\n wordWrap: false,\n trim: true,\n showFullscreen: false,\n autoLoadLanguage: true,\n});\n\nconst emit = defineEmits<{\n copy: [code: string];\n click: [event: MouseEvent];\n fullscreen: [isFullscreen: boolean];\n}>();\n\n// 尝试获取 highlight 实例(如果用户安装了插件)\nconst highlightManager = inject<HighlightManager | null>(\n \"highlightManager\",\n null,\n);\n\nconst copying = ref(false);\nconst isFullscreen = ref(false);\nconst languageLoading = ref(false);\nconst showFloatingCopy = ref(false);\n\n// 安全地获取 hljs 实例\nconst hljs = computed(() => {\n try {\n return highlightManager?.getHljs?.() || null;\n } catch (error) {\n console.warn(\"Highlight.js not available:\", error);\n return null;\n }\n});\n\n// 语言图标映射(使用标准的 MDI 图标名称)\nconst languageIcon = computed(() => {\n const iconMap: Record<string, string> = {\n javascript: \"mdi:language-javascript\",\n typescript: \"mdi:language-typescript\",\n python: \"mdi:language-python\",\n html: \"mdi:language-html5\",\n css: \"mdi:language-css3\",\n vue: \"mdi:vuejs\",\n react: \"mdi:react\",\n json: \"mdi:code-json\",\n java: \"mdi:language-java\",\n cpp: \"mdi:language-cpp\",\n go: \"mdi:language-go\",\n rust: \"mdi:language-rust\",\n php: \"mdi:language-php\",\n csharp: \"mdi:language-csharp\",\n sql: \"mdi:database\",\n yaml: \"mdi:file-code\",\n xml: \"mdi:xml\",\n markdown: \"mdi:language-markdown\",\n bash: \"mdi:bash\",\n shell: \"mdi:console\",\n powershell: \"mdi:powershell\",\n swift: \"mdi:language-swift\",\n kotlin: \"mdi:language-kotlin\",\n ruby: \"mdi:language-ruby\",\n };\n return iconMap[props.language.toLowerCase()] || \"mdi:code-braces\";\n});\n\n// 代码样式\nconst codeStyle = computed(() => {\n if (!props.maxHeight) return {};\n return {\n maxHeight:\n typeof props.maxHeight === \"number\"\n ? `${props.maxHeight}px`\n : props.maxHeight,\n };\n});\n\n// 自动加载语言包\nwatch(\n () => props.language,\n async (newLanguage) => {\n if (!props.autoLoadLanguage || newLanguage === \"text\" || !highlightManager)\n return;\n\n const loadedLanguages = highlightManager.getLoadedLanguages?.() || [];\n if (loadedLanguages.includes(newLanguage)) return;\n\n languageLoading.value = true;\n try {\n await highlightManager.loadLanguage?.(newLanguage);\n await nextTick();\n } catch (error) {\n console.warn(`Failed to load language: ${newLanguage}`, error);\n } finally {\n languageLoading.value = false;\n }\n },\n { immediate: true },\n);\n\n/**\n * 复制代码到剪贴板\n */\nasync function copyCode() {\n if (copying.value) return;\n copying.value = true;\n\n try {\n await navigator.clipboard.writeText(props.code);\n emit(\"copy\", props.code);\n } catch (error) {\n console.error(\"Copy failed:\", error);\n } finally {\n copying.value = false;\n }\n}\n\n/**\n * 切换全屏显示状态\n */\nfunction toggleFullscreen() {\n isFullscreen.value = !isFullscreen.value;\n emit(\"fullscreen\", isFullscreen.value);\n}\n\n/**\n * 获取编程语言的显示标题\n */\nfunction getLanguageTitle(lang: string): string {\n const titleMap: Record<string, string> = {\n javascript: \"JavaScript\",\n typescript: \"TypeScript\",\n python: \"Python\",\n java: \"Java\",\n cpp: \"C++\",\n csharp: \"C#\",\n php: \"PHP\",\n go: \"Go\",\n rust: \"Rust\",\n html: \"HTML\",\n css: \"CSS\",\n json: \"JSON\",\n bash: \"Bash\",\n shell: \"Shell\",\n yaml: \"YAML\",\n xml: \"XML\",\n markdown: \"Markdown\",\n sql: \"SQL\",\n powershell: \"PowerShell\",\n swift: \"Swift\",\n kotlin: \"Kotlin\",\n ruby: \"Ruby\",\n vue: \"Vue\",\n react: \"React\",\n };\n return titleMap[lang.toLowerCase()] || lang.toUpperCase();\n}\n\n/**\n * 处理ESC键退出全屏\n */\nfunction handleEscapeKey(event: KeyboardEvent) {\n if (event.key === \"Escape\" && isFullscreen.value) {\n toggleFullscreen();\n }\n}\n\nonMounted(() => {\n document.addEventListener(\"keydown\", handleEscapeKey);\n});\n\nonUnmounted(() => {\n document.removeEventListener(\"keydown\", handleEscapeKey);\n});\n\ndefineExpose({\n copyCode,\n toggleFullscreen,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.mr-2 {\n margin-right: 0.5rem;\n}\n\n.ml-2 {\n margin-left: 0.5rem;\n}\n</style>\n","/* unplugin-vue-components disabled */<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-19\n * @Description: 全局代码高亮组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-code-wrapper\">\n <!-- 代码标题栏 -->\n <div v-if=\"showHeader\" class=\"c-code-header\">\n <div class=\"c-code-title\">\n <C_Icon\n v-if=\"languageIcon\"\n :name=\"languageIcon\"\n :size=\"16\"\n class=\"mr-2\"\n />\n <span>{{ title || getLanguageTitle(language) }}</span>\n </div>\n <div class=\"c-code-actions\">\n <NTooltip trigger=\"hover\">\n <template #trigger>\n <NButton\n size=\"tiny\"\n quaternary\n @click=\"copyCode\"\n :loading=\"copying\"\n >\n <template #icon>\n <C_Icon\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\n :loading=\"copying\"\n />\n </template>\n </NButton>\n </template>\n 复制代码\n </NTooltip>\n\n <NTooltip v-if=\"showFullscreen\" trigger=\"hover\">\n <template #trigger>\n <NButton size=\"tiny\" quaternary @click=\"toggleFullscreen\">\n <template #icon>\n <C_Icon\n :name=\"\n isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen'\n \"\n />\n </template>\n </NButton>\n </template>\n {{ isFullscreen ? \"退出全屏\" : \"全屏查看\" }}\n </NTooltip>\n </div>\n </div>\n\n <!-- 代码内容区域 -->\n <div class=\"c-code-content\">\n <div\n class=\"code-wrapper\"\n @mouseenter=\"showFloatingCopy = true\"\n @mouseleave=\"showFloatingCopy = false\"\n >\n <NCode\n :key=\"`code-${language}-${code.length}`\"\n :code=\"code\"\n :language=\"language\"\n :hljs=\"hljs\"\n :show-line-numbers=\"showLineNumbers\"\n :word-wrap=\"wordWrap\"\n :trim=\"trim\"\n :style=\"codeStyle\"\n @click=\"emit('click', $event)\"\n />\n\n <!-- 悬浮复制按钮 -->\n <Transition name=\"fade\">\n <div v-if=\"showFloatingCopy && !showHeader\" class=\"floating-copy-btn\">\n <NTooltip trigger=\"hover\">\n <template #trigger>\n <NButton\n size=\"small\"\n quaternary\n @click=\"copyCode\"\n :loading=\"copying\"\n class=\"copy-floating\"\n >\n <template #icon>\n <C_Icon\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\n :loading=\"copying\"\n />\n </template>\n </NButton>\n </template>\n 复制代码\n </NTooltip>\n </div>\n </Transition>\n </div>\n\n <!-- 语言加载状态 -->\n <div v-if=\"languageLoading\" class=\"c-code-loading\">\n <NSpin size=\"small\" />\n <span class=\"ml-2\">正在加载 {{ language }} 语言包...</span>\n </div>\n </div>\n\n <!-- 全屏模态框 -->\n <NModal\n v-model:show=\"isFullscreen\"\n :mask-closable=\"false\"\n :show-icon=\"false\"\n :bordered=\"false\"\n style=\"width: 100vw; height: 100vh; margin: 0; padding: 0\"\n >\n <div class=\"fullscreen-content\">\n <div class=\"fullscreen-header\">\n <div class=\"fullscreen-title\">\n <C_Icon\n v-if=\"languageIcon\"\n :name=\"languageIcon\"\n :size=\"16\"\n class=\"mr-2\"\n />\n <span>{{ title || getLanguageTitle(language) }}</span>\n </div>\n <NButton size=\"small\" quaternary @click=\"toggleFullscreen\">\n <template #icon>\n <C_Icon name=\"mdi:close\" />\n </template>\n </NButton>\n </div>\n <div class=\"fullscreen-body\">\n <NCode\n :key=\"`fullscreen-code-${language}-${code.length}`\"\n :code=\"code\"\n :language=\"language\"\n :hljs=\"hljs\"\n :show-line-numbers=\"showLineNumbers\"\n :word-wrap=\"wordWrap\"\n :trim=\"trim\"\n />\n </div>\n </div>\n </NModal>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n ref,\n computed,\n watch,\n onMounted,\n onUnmounted,\n nextTick,\n inject,\n} from \"vue\";\nimport C_Icon from \"../C_Icon/index.vue\";\n\n// 直接在组件内定义 HighlightManager 接口,避免导入错误\ninterface HighlightManager {\n getHljs?: () => any;\n loadLanguage?: (language: string) => Promise<void>;\n getLoadedLanguages?: () => string[];\n}\n\n// 组件名称定义\ndefineOptions({\n name: \"CCode\",\n});\n\ninterface Props {\n code: string;\n language?: string;\n title?: string;\n showHeader?: boolean;\n showLineNumbers?: boolean;\n wordWrap?: boolean;\n trim?: boolean;\n showFullscreen?: boolean;\n maxHeight?: string | number;\n autoLoadLanguage?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n code: \"\",\n language: \"text\",\n showHeader: true,\n showLineNumbers: true,\n wordWrap: false,\n trim: true,\n showFullscreen: false,\n autoLoadLanguage: true,\n});\n\nconst emit = defineEmits<{\n copy: [code: string];\n click: [event: MouseEvent];\n fullscreen: [isFullscreen: boolean];\n}>();\n\n// 尝试获取 highlight 实例(如果用户安装了插件)\nconst highlightManager = inject<HighlightManager | null>(\n \"highlightManager\",\n null,\n);\n\nconst copying = ref(false);\nconst isFullscreen = ref(false);\nconst languageLoading = ref(false);\nconst showFloatingCopy = ref(false);\n\n// 安全地获取 hljs 实例\nconst hljs = computed(() => {\n try {\n return highlightManager?.getHljs?.() || null;\n } catch (error) {\n console.warn(\"Highlight.js not available:\", error);\n return null;\n }\n});\n\n// 语言图标映射(使用标准的 MDI 图标名称)\nconst languageIcon = computed(() => {\n const iconMap: Record<string, string> = {\n javascript: \"mdi:language-javascript\",\n typescript: \"mdi:language-typescript\",\n python: \"mdi:language-python\",\n html: \"mdi:language-html5\",\n css: \"mdi:language-css3\",\n vue: \"mdi:vuejs\",\n react: \"mdi:react\",\n json: \"mdi:code-json\",\n java: \"mdi:language-java\",\n cpp: \"mdi:language-cpp\",\n go: \"mdi:language-go\",\n rust: \"mdi:language-rust\",\n php: \"mdi:language-php\",\n csharp: \"mdi:language-csharp\",\n sql: \"mdi:database\",\n yaml: \"mdi:file-code\",\n xml: \"mdi:xml\",\n markdown: \"mdi:language-markdown\",\n bash: \"mdi:bash\",\n shell: \"mdi:console\",\n powershell: \"mdi:powershell\",\n swift: \"mdi:language-swift\",\n kotlin: \"mdi:language-kotlin\",\n ruby: \"mdi:language-ruby\",\n };\n return iconMap[props.language.toLowerCase()] || \"mdi:code-braces\";\n});\n\n// 代码样式\nconst codeStyle = computed(() => {\n if (!props.maxHeight) return {};\n return {\n maxHeight:\n typeof props.maxHeight === \"number\"\n ? `${props.maxHeight}px`\n : props.maxHeight,\n };\n});\n\n// 自动加载语言包\nwatch(\n () => props.language,\n async (newLanguage) => {\n if (!props.autoLoadLanguage || newLanguage === \"text\" || !highlightManager)\n return;\n\n const loadedLanguages = highlightManager.getLoadedLanguages?.() || [];\n if (loadedLanguages.includes(newLanguage)) return;\n\n languageLoading.value = true;\n try {\n await highlightManager.loadLanguage?.(newLanguage);\n await nextTick();\n } catch (error) {\n console.warn(`Failed to load language: ${newLanguage}`, error);\n } finally {\n languageLoading.value = false;\n }\n },\n { immediate: true },\n);\n\n/**\n * 复制代码到剪贴板\n */\nasync function copyCode() {\n if (copying.value) return;\n copying.value = true;\n\n try {\n await navigator.clipboard.writeText(props.code);\n emit(\"copy\", props.code);\n } catch (error) {\n console.error(\"Copy failed:\", error);\n } finally {\n copying.value = false;\n }\n}\n\n/**\n * 切换全屏显示状态\n */\nfunction toggleFullscreen() {\n isFullscreen.value = !isFullscreen.value;\n emit(\"fullscreen\", isFullscreen.value);\n}\n\n/**\n * 获取编程语言的显示标题\n */\nfunction getLanguageTitle(lang: string): string {\n const titleMap: Record<string, string> = {\n javascript: \"JavaScript\",\n typescript: \"TypeScript\",\n python: \"Python\",\n java: \"Java\",\n cpp: \"C++\",\n csharp: \"C#\",\n php: \"PHP\",\n go: \"Go\",\n rust: \"Rust\",\n html: \"HTML\",\n css: \"CSS\",\n json: \"JSON\",\n bash: \"Bash\",\n shell: \"Shell\",\n yaml: \"YAML\",\n xml: \"XML\",\n markdown: \"Markdown\",\n sql: \"SQL\",\n powershell: \"PowerShell\",\n swift: \"Swift\",\n kotlin: \"Kotlin\",\n ruby: \"Ruby\",\n vue: \"Vue\",\n react: \"React\",\n };\n return titleMap[lang.toLowerCase()] || lang.toUpperCase();\n}\n\n/**\n * 处理ESC键退出全屏\n */\nfunction handleEscapeKey(event: KeyboardEvent) {\n if (event.key === \"Escape\" && isFullscreen.value) {\n toggleFullscreen();\n }\n}\n\nonMounted(() => {\n document.addEventListener(\"keydown\", handleEscapeKey);\n});\n\nonUnmounted(() => {\n document.removeEventListener(\"keydown\", handleEscapeKey);\n});\n\ndefineExpose({\n copyCode,\n toggleFullscreen,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.mr-2 {\n margin-right: 0.5rem;\n}\n\n.ml-2 {\n margin-left: 0.5rem;\n}\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-19\n * @Description: 全局代码高亮组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-code-wrapper\">\n <!-- 代码标题栏 -->\n <div v-if=\"showHeader\" class=\"c-code-header\">\n <div class=\"c-code-title\">\n <C_Icon\n v-if=\"languageIcon\"\n :name=\"languageIcon\"\n :size=\"16\"\n class=\"mr-2\"\n />\n <span>{{ title || getLanguageTitle(language) }}</span>\n </div>\n <div class=\"c-code-actions\">\n <NTooltip trigger=\"hover\">\n <template #trigger>\n <NButton\n size=\"tiny\"\n quaternary\n @click=\"copyCode\"\n :loading=\"copying\"\n >\n <template #icon>\n <C_Icon\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\n :loading=\"copying\"\n />\n </template>\n </NButton>\n </template>\n 复制代码\n </NTooltip>\n\n <NTooltip v-if=\"showFullscreen\" trigger=\"hover\">\n <template #trigger>\n <NButton size=\"tiny\" quaternary @click=\"toggleFullscreen\">\n <template #icon>\n <C_Icon\n :name=\"\n isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen'\n \"\n />\n </template>\n </NButton>\n </template>\n {{ isFullscreen ? \"退出全屏\" : \"全屏查看\" }}\n </NTooltip>\n </div>\n </div>\n\n <!-- 代码内容区域 -->\n <div class=\"c-code-content\">\n <div\n class=\"code-wrapper\"\n @mouseenter=\"showFloatingCopy = true\"\n @mouseleave=\"showFloatingCopy = false\"\n >\n <NCode\n :key=\"`code-${language}-${code.length}`\"\n :code=\"code\"\n :language=\"language\"\n :hljs=\"hljs\"\n :show-line-numbers=\"showLineNumbers\"\n :word-wrap=\"wordWrap\"\n :trim=\"trim\"\n :style=\"codeStyle\"\n @click=\"emit('click', $event)\"\n />\n\n <!-- 悬浮复制按钮 -->\n <Transition name=\"fade\">\n <div v-if=\"showFloatingCopy && !showHeader\" class=\"floating-copy-btn\">\n <NTooltip trigger=\"hover\">\n <template #trigger>\n <NButton\n size=\"small\"\n quaternary\n @click=\"copyCode\"\n :loading=\"copying\"\n class=\"copy-floating\"\n >\n <template #icon>\n <C_Icon\n :name=\"copying ? 'mdi:loading' : 'mdi:content-copy'\"\n :loading=\"copying\"\n />\n </template>\n </NButton>\n </template>\n 复制代码\n </NTooltip>\n </div>\n </Transition>\n </div>\n\n <!-- 语言加载状态 -->\n <div v-if=\"languageLoading\" class=\"c-code-loading\">\n <NSpin size=\"small\" />\n <span class=\"ml-2\">正在加载 {{ language }} 语言包...</span>\n </div>\n </div>\n\n <!-- 全屏模态框 -->\n <NModal\n v-model:show=\"isFullscreen\"\n :mask-closable=\"false\"\n :show-icon=\"false\"\n :bordered=\"false\"\n style=\"width: 100vw; height: 100vh; margin: 0; padding: 0\"\n >\n <div class=\"fullscreen-content\">\n <div class=\"fullscreen-header\">\n <div class=\"fullscreen-title\">\n <C_Icon\n v-if=\"languageIcon\"\n :name=\"languageIcon\"\n :size=\"16\"\n class=\"mr-2\"\n />\n <span>{{ title || getLanguageTitle(language) }}</span>\n </div>\n <NButton size=\"small\" quaternary @click=\"toggleFullscreen\">\n <template #icon>\n <C_Icon name=\"mdi:close\" />\n </template>\n </NButton>\n </div>\n <div class=\"fullscreen-body\">\n <NCode\n :key=\"`fullscreen-code-${language}-${code.length}`\"\n :code=\"code\"\n :language=\"language\"\n :hljs=\"hljs\"\n :show-line-numbers=\"showLineNumbers\"\n :word-wrap=\"wordWrap\"\n :trim=\"trim\"\n />\n </div>\n </div>\n </NModal>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n ref,\n computed,\n watch,\n onMounted,\n onUnmounted,\n nextTick,\n inject,\n} from \"vue\";\nimport C_Icon from \"../C_Icon/index.vue\";\n\n// 直接在组件内定义 HighlightManager 接口,避免导入错误\ninterface HighlightManager {\n getHljs?: () => any;\n loadLanguage?: (language: string) => Promise<void>;\n getLoadedLanguages?: () => string[];\n}\n\n// 组件名称定义\ndefineOptions({\n name: \"CCode\",\n});\n\ninterface Props {\n code: string;\n language?: string;\n title?: string;\n showHeader?: boolean;\n showLineNumbers?: boolean;\n wordWrap?: boolean;\n trim?: boolean;\n showFullscreen?: boolean;\n maxHeight?: string | number;\n autoLoadLanguage?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n code: \"\",\n language: \"text\",\n showHeader: true,\n showLineNumbers: true,\n wordWrap: false,\n trim: true,\n showFullscreen: false,\n autoLoadLanguage: true,\n});\n\nconst emit = defineEmits<{\n copy: [code: string];\n click: [event: MouseEvent];\n fullscreen: [isFullscreen: boolean];\n}>();\n\n// 尝试获取 highlight 实例(如果用户安装了插件)\nconst highlightManager = inject<HighlightManager | null>(\n \"highlightManager\",\n null,\n);\n\nconst copying = ref(false);\nconst isFullscreen = ref(false);\nconst languageLoading = ref(false);\nconst showFloatingCopy = ref(false);\n\n// 安全地获取 hljs 实例\nconst hljs = computed(() => {\n try {\n return highlightManager?.getHljs?.() || null;\n } catch (error) {\n console.warn(\"Highlight.js not available:\", error);\n return null;\n }\n});\n\n// 语言图标映射(使用标准的 MDI 图标名称)\nconst languageIcon = computed(() => {\n const iconMap: Record<string, string> = {\n javascript: \"mdi:language-javascript\",\n typescript: \"mdi:language-typescript\",\n python: \"mdi:language-python\",\n html: \"mdi:language-html5\",\n css: \"mdi:language-css3\",\n vue: \"mdi:vuejs\",\n react: \"mdi:react\",\n json: \"mdi:code-json\",\n java: \"mdi:language-java\",\n cpp: \"mdi:language-cpp\",\n go: \"mdi:language-go\",\n rust: \"mdi:language-rust\",\n php: \"mdi:language-php\",\n csharp: \"mdi:language-csharp\",\n sql: \"mdi:database\",\n yaml: \"mdi:file-code\",\n xml: \"mdi:xml\",\n markdown: \"mdi:language-markdown\",\n bash: \"mdi:bash\",\n shell: \"mdi:console\",\n powershell: \"mdi:powershell\",\n swift: \"mdi:language-swift\",\n kotlin: \"mdi:language-kotlin\",\n ruby: \"mdi:language-ruby\",\n };\n return iconMap[props.language.toLowerCase()] || \"mdi:code-braces\";\n});\n\n// 代码样式\nconst codeStyle = computed(() => {\n if (!props.maxHeight) return {};\n return {\n maxHeight:\n typeof props.maxHeight === \"number\"\n ? `${props.maxHeight}px`\n : props.maxHeight,\n };\n});\n\n// 自动加载语言包\nwatch(\n () => props.language,\n async (newLanguage) => {\n if (!props.autoLoadLanguage || newLanguage === \"text\" || !highlightManager)\n return;\n\n const loadedLanguages = highlightManager.getLoadedLanguages?.() || [];\n if (loadedLanguages.includes(newLanguage)) return;\n\n languageLoading.value = true;\n try {\n await highlightManager.loadLanguage?.(newLanguage);\n await nextTick();\n } catch (error) {\n console.warn(`Failed to load language: ${newLanguage}`, error);\n } finally {\n languageLoading.value = false;\n }\n },\n { immediate: true },\n);\n\n/**\n * 复制代码到剪贴板\n */\nasync function copyCode() {\n if (copying.value) return;\n copying.value = true;\n\n try {\n await navigator.clipboard.writeText(props.code);\n emit(\"copy\", props.code);\n } catch (error) {\n console.error(\"Copy failed:\", error);\n } finally {\n copying.value = false;\n }\n}\n\n/**\n * 切换全屏显示状态\n */\nfunction toggleFullscreen() {\n isFullscreen.value = !isFullscreen.value;\n emit(\"fullscreen\", isFullscreen.value);\n}\n\n/**\n * 获取编程语言的显示标题\n */\nfunction getLanguageTitle(lang: string): string {\n const titleMap: Record<string, string> = {\n javascript: \"JavaScript\",\n typescript: \"TypeScript\",\n python: \"Python\",\n java: \"Java\",\n cpp: \"C++\",\n csharp: \"C#\",\n php: \"PHP\",\n go: \"Go\",\n rust: \"Rust\",\n html: \"HTML\",\n css: \"CSS\",\n json: \"JSON\",\n bash: \"Bash\",\n shell: \"Shell\",\n yaml: \"YAML\",\n xml: \"XML\",\n markdown: \"Markdown\",\n sql: \"SQL\",\n powershell: \"PowerShell\",\n swift: \"Swift\",\n kotlin: \"Kotlin\",\n ruby: \"Ruby\",\n vue: \"Vue\",\n react: \"React\",\n };\n return titleMap[lang.toLowerCase()] || lang.toUpperCase();\n}\n\n/**\n * 处理ESC键退出全屏\n */\nfunction handleEscapeKey(event: KeyboardEvent) {\n if (event.key === \"Escape\" && isFullscreen.value) {\n toggleFullscreen();\n }\n}\n\nonMounted(() => {\n document.addEventListener(\"keydown\", handleEscapeKey);\n});\n\nonUnmounted(() => {\n document.removeEventListener(\"keydown\", handleEscapeKey);\n});\n\ndefineExpose({\n copyCode,\n toggleFullscreen,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.mr-2 {\n margin-right: 0.5rem;\n}\n\n.ml-2 {\n margin-left: 0.5rem;\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"C_ContextMenu-
|
|
1
|
+
{"version":3,"file":"C_ContextMenu-B3Cgz_wJ.css","names":[],"sources":["../src/components/C_ContextMenu/index.vue?vue&type=style&index=0&scoped=cdd4d8bb&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-context-menu[data-v-cdd4d8bb] {\n --ctx-bg: var(--card-color);\n --ctx-bg-blur: 12px;\n --ctx-border: var(--border-color);\n --ctx-radius: 10px;\n --ctx-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);\n --ctx-padding: 6px;\n --ctx-text-primary: var(--app-text-primary);\n --ctx-text-secondary: var(--text-color-3);\n --ctx-text-danger: #d03050;\n --ctx-text-disabled: var(--text-color-3);\n --ctx-item-height: 32px;\n --ctx-item-padding: 0 12px;\n --ctx-item-radius: 6px;\n --ctx-item-hover-bg: color-mix(in srgb, var(--primary-color) 8%, transparent);\n --ctx-item-active-bg: color-mix(in srgb, var(--primary-color) 14%, transparent);\n --ctx-icon-size: 16px;\n --ctx-icon-gap: 10px;\n --ctx-shortcut-color: var(--text-color-3);\n --ctx-divider-color: var(--border-color);\n --ctx-divider-margin: 4px 8px;\n position: fixed;\n padding: var(--ctx-padding);\n background: var(--ctx-bg);\n backdrop-filter: blur(var(--ctx-bg-blur));\n border: 1px solid var(--ctx-border);\n border-radius: var(--ctx-radius);\n box-shadow: var(--ctx-shadow);\n color: var(--ctx-text-primary);\n font-size: 13px;\n user-select: none;\n outline: none;\n}\n.c-context-menu__item[data-v-cdd4d8bb] {\n position: relative;\n display: flex;\n align-items: center;\n height: var(--ctx-item-height);\n padding: var(--ctx-item-padding);\n border-radius: var(--ctx-item-radius);\n cursor: pointer;\n transition: background 0.15s;\n white-space: nowrap;\n gap: var(--ctx-icon-gap);\n}\n.c-context-menu__item[data-v-cdd4d8bb]:hover {\n background: var(--ctx-item-hover-bg);\n}\n.c-context-menu__item[data-v-cdd4d8bb]:active {\n background: var(--ctx-item-active-bg);\n}\n.c-context-menu__item.is-disabled[data-v-cdd4d8bb] {\n color: var(--ctx-text-disabled);\n cursor: not-allowed;\n pointer-events: none;\n}\n.c-context-menu__item.is-danger[data-v-cdd4d8bb] {\n color: var(--ctx-text-danger);\n}\n.c-context-menu__item.is-danger[data-v-cdd4d8bb]:hover {\n background: color-mix(in srgb, #d03050 12%, transparent);\n}\n.c-context-menu__icon[data-v-cdd4d8bb] {\n flex-shrink: 0;\n width: var(--ctx-icon-size);\n height: var(--ctx-icon-size);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--ctx-icon-size);\n}\n.c-context-menu__label[data-v-cdd4d8bb] {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.c-context-menu__shortcut[data-v-cdd4d8bb] {\n flex-shrink: 0;\n margin-left: 24px;\n font-size: 11px;\n color: var(--ctx-shortcut-color);\n font-family: \"JetBrains Mono\", \"Fira Code\", monospace;\n}\n.c-context-menu__arrow[data-v-cdd4d8bb] {\n flex-shrink: 0;\n margin-left: 8px;\n font-size: 12px;\n color: var(--ctx-text-secondary);\n transition: transform 0.15s;\n}\n.c-context-menu__divider[data-v-cdd4d8bb] {\n height: 1px;\n margin: var(--ctx-divider-margin);\n background: var(--ctx-divider-color);\n}\n.c-context-menu__submenu[data-v-cdd4d8bb] {\n position: absolute;\n top: -6px;\n}\n.c-context-menu__submenu.is-right[data-v-cdd4d8bb] {\n left: 100%;\n margin-left: 4px;\n}\n.c-context-menu__submenu.is-left[data-v-cdd4d8bb] {\n right: 100%;\n margin-right: 4px;\n}\n.c-context-menu-overlay[data-v-cdd4d8bb] {\n position: fixed;\n inset: 0;\n z-index: 9998;\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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"C_ContextMenu-
|
|
1
|
+
{"version":3,"file":"C_ContextMenu-u1LixF3_.css","names":[],"sources":["../src/components/C_ContextMenu/index.vue?vue&type=style&index=0&scoped=cdd4d8bb&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-context-menu[data-v-cdd4d8bb] {\n --ctx-bg: var(--card-color);\n --ctx-bg-blur: 12px;\n --ctx-border: var(--border-color);\n --ctx-radius: 10px;\n --ctx-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);\n --ctx-padding: 6px;\n --ctx-text-primary: var(--app-text-primary);\n --ctx-text-secondary: var(--text-color-3);\n --ctx-text-danger: #d03050;\n --ctx-text-disabled: var(--text-color-3);\n --ctx-item-height: 32px;\n --ctx-item-padding: 0 12px;\n --ctx-item-radius: 6px;\n --ctx-item-hover-bg: color-mix(in srgb, var(--primary-color) 8%, transparent);\n --ctx-item-active-bg: color-mix(in srgb, var(--primary-color) 14%, transparent);\n --ctx-icon-size: 16px;\n --ctx-icon-gap: 10px;\n --ctx-shortcut-color: var(--text-color-3);\n --ctx-divider-color: var(--border-color);\n --ctx-divider-margin: 4px 8px;\n position: fixed;\n padding: var(--ctx-padding);\n background: var(--ctx-bg);\n backdrop-filter: blur(var(--ctx-bg-blur));\n border: 1px solid var(--ctx-border);\n border-radius: var(--ctx-radius);\n box-shadow: var(--ctx-shadow);\n color: var(--ctx-text-primary);\n font-size: 13px;\n user-select: none;\n outline: none;\n}\n.c-context-menu__item[data-v-cdd4d8bb] {\n position: relative;\n display: flex;\n align-items: center;\n height: var(--ctx-item-height);\n padding: var(--ctx-item-padding);\n border-radius: var(--ctx-item-radius);\n cursor: pointer;\n transition: background 0.15s;\n white-space: nowrap;\n gap: var(--ctx-icon-gap);\n}\n.c-context-menu__item[data-v-cdd4d8bb]:hover {\n background: var(--ctx-item-hover-bg);\n}\n.c-context-menu__item[data-v-cdd4d8bb]:active {\n background: var(--ctx-item-active-bg);\n}\n.c-context-menu__item.is-disabled[data-v-cdd4d8bb] {\n color: var(--ctx-text-disabled);\n cursor: not-allowed;\n pointer-events: none;\n}\n.c-context-menu__item.is-danger[data-v-cdd4d8bb] {\n color: var(--ctx-text-danger);\n}\n.c-context-menu__item.is-danger[data-v-cdd4d8bb]:hover {\n background: color-mix(in srgb, #d03050 12%, transparent);\n}\n.c-context-menu__icon[data-v-cdd4d8bb] {\n flex-shrink: 0;\n width: var(--ctx-icon-size);\n height: var(--ctx-icon-size);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--ctx-icon-size);\n}\n.c-context-menu__label[data-v-cdd4d8bb] {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.c-context-menu__shortcut[data-v-cdd4d8bb] {\n flex-shrink: 0;\n margin-left: 24px;\n font-size: 11px;\n color: var(--ctx-shortcut-color);\n font-family: \"JetBrains Mono\", \"Fira Code\", monospace;\n}\n.c-context-menu__arrow[data-v-cdd4d8bb] {\n flex-shrink: 0;\n margin-left: 8px;\n font-size: 12px;\n color: var(--ctx-text-secondary);\n transition: transform 0.15s;\n}\n.c-context-menu__divider[data-v-cdd4d8bb] {\n height: 1px;\n margin: var(--ctx-divider-margin);\n background: var(--ctx-divider-color);\n}\n.c-context-menu__submenu[data-v-cdd4d8bb] {\n position: absolute;\n top: -6px;\n}\n.c-context-menu__submenu.is-right[data-v-cdd4d8bb] {\n left: 100%;\n margin-left: 4px;\n}\n.c-context-menu__submenu.is-left[data-v-cdd4d8bb] {\n right: 100%;\n margin-right: 4px;\n}\n.c-context-menu-overlay[data-v-cdd4d8bb] {\n position: fixed;\n inset: 0;\n z-index: 9998;\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"}
|
package/dist/C_ContextMenu.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* unplugin-vue-components disabled */.c-context-menu[data-v-
|
|
1
|
+
/* unplugin-vue-components disabled */.c-context-menu[data-v-cdd4d8bb] {
|
|
2
2
|
--ctx-bg: var(--card-color);
|
|
3
3
|
--ctx-bg-blur: 12px;
|
|
4
4
|
--ctx-border: var(--border-color);
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
user-select: none;
|
|
32
32
|
outline: none;
|
|
33
33
|
}
|
|
34
|
-
.c-context-menu__item[data-v-
|
|
34
|
+
.c-context-menu__item[data-v-cdd4d8bb] {
|
|
35
35
|
position: relative;
|
|
36
36
|
display: flex;
|
|
37
37
|
align-items: center;
|
|
@@ -43,24 +43,24 @@
|
|
|
43
43
|
white-space: nowrap;
|
|
44
44
|
gap: var(--ctx-icon-gap);
|
|
45
45
|
}
|
|
46
|
-
.c-context-menu__item[data-v-
|
|
46
|
+
.c-context-menu__item[data-v-cdd4d8bb]:hover {
|
|
47
47
|
background: var(--ctx-item-hover-bg);
|
|
48
48
|
}
|
|
49
|
-
.c-context-menu__item[data-v-
|
|
49
|
+
.c-context-menu__item[data-v-cdd4d8bb]:active {
|
|
50
50
|
background: var(--ctx-item-active-bg);
|
|
51
51
|
}
|
|
52
|
-
.c-context-menu__item.is-disabled[data-v-
|
|
52
|
+
.c-context-menu__item.is-disabled[data-v-cdd4d8bb] {
|
|
53
53
|
color: var(--ctx-text-disabled);
|
|
54
54
|
cursor: not-allowed;
|
|
55
55
|
pointer-events: none;
|
|
56
56
|
}
|
|
57
|
-
.c-context-menu__item.is-danger[data-v-
|
|
57
|
+
.c-context-menu__item.is-danger[data-v-cdd4d8bb] {
|
|
58
58
|
color: var(--ctx-text-danger);
|
|
59
59
|
}
|
|
60
|
-
.c-context-menu__item.is-danger[data-v-
|
|
60
|
+
.c-context-menu__item.is-danger[data-v-cdd4d8bb]:hover {
|
|
61
61
|
background: color-mix(in srgb, #d03050 12%, transparent);
|
|
62
62
|
}
|
|
63
|
-
.c-context-menu__icon[data-v-
|
|
63
|
+
.c-context-menu__icon[data-v-cdd4d8bb] {
|
|
64
64
|
flex-shrink: 0;
|
|
65
65
|
width: var(--ctx-icon-size);
|
|
66
66
|
height: var(--ctx-icon-size);
|
|
@@ -69,47 +69,47 @@
|
|
|
69
69
|
justify-content: center;
|
|
70
70
|
font-size: var(--ctx-icon-size);
|
|
71
71
|
}
|
|
72
|
-
.c-context-menu__label[data-v-
|
|
72
|
+
.c-context-menu__label[data-v-cdd4d8bb] {
|
|
73
73
|
flex: 1;
|
|
74
74
|
min-width: 0;
|
|
75
75
|
overflow: hidden;
|
|
76
76
|
text-overflow: ellipsis;
|
|
77
77
|
}
|
|
78
|
-
.c-context-menu__shortcut[data-v-
|
|
78
|
+
.c-context-menu__shortcut[data-v-cdd4d8bb] {
|
|
79
79
|
flex-shrink: 0;
|
|
80
80
|
margin-left: 24px;
|
|
81
81
|
font-size: 11px;
|
|
82
82
|
color: var(--ctx-shortcut-color);
|
|
83
83
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
|
84
84
|
}
|
|
85
|
-
.c-context-menu__arrow[data-v-
|
|
85
|
+
.c-context-menu__arrow[data-v-cdd4d8bb] {
|
|
86
86
|
flex-shrink: 0;
|
|
87
87
|
margin-left: 8px;
|
|
88
88
|
font-size: 12px;
|
|
89
89
|
color: var(--ctx-text-secondary);
|
|
90
90
|
transition: transform 0.15s;
|
|
91
91
|
}
|
|
92
|
-
.c-context-menu__divider[data-v-
|
|
92
|
+
.c-context-menu__divider[data-v-cdd4d8bb] {
|
|
93
93
|
height: 1px;
|
|
94
94
|
margin: var(--ctx-divider-margin);
|
|
95
95
|
background: var(--ctx-divider-color);
|
|
96
96
|
}
|
|
97
|
-
.c-context-menu__submenu[data-v-
|
|
97
|
+
.c-context-menu__submenu[data-v-cdd4d8bb] {
|
|
98
98
|
position: absolute;
|
|
99
99
|
top: -6px;
|
|
100
100
|
}
|
|
101
|
-
.c-context-menu__submenu.is-right[data-v-
|
|
101
|
+
.c-context-menu__submenu.is-right[data-v-cdd4d8bb] {
|
|
102
102
|
left: 100%;
|
|
103
103
|
margin-left: 4px;
|
|
104
104
|
}
|
|
105
|
-
.c-context-menu__submenu.is-left[data-v-
|
|
105
|
+
.c-context-menu__submenu.is-left[data-v-cdd4d8bb] {
|
|
106
106
|
right: 100%;
|
|
107
107
|
margin-right: 4px;
|
|
108
108
|
}
|
|
109
|
-
.c-context-menu-overlay[data-v-
|
|
109
|
+
.c-context-menu-overlay[data-v-cdd4d8bb] {
|
|
110
110
|
position: fixed;
|
|
111
111
|
inset: 0;
|
|
112
112
|
z-index: 9998;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
/*# sourceMappingURL=C_ContextMenu-
|
|
115
|
+
/*# sourceMappingURL=C_ContextMenu-B3Cgz_wJ.css.map*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"C_ContextMenu.js","names":[],"sources":["../src/components/C_ContextMenu/types.ts","../src/components/C_ContextMenu/index.vue","../src/components/C_ContextMenu/index.vue","../src/components/C_ContextMenu/index.vue"],"sourcesContent":["/**\r\n * @Description: 右键菜单组件类型定义\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\r\n */\r\n\r\n/** 菜单项 */\r\nexport interface ContextMenuItem {\r\n /** 唯一标识 */\r\n key: string\r\n /** 菜单文字 */\r\n label: string\r\n /** 图标(Iconify 图标名) */\r\n icon?: string\r\n /** 快捷键标注文字(仅展示,不绑定) */\r\n shortcut?: string\r\n /** 是否禁用 */\r\n disabled?: boolean\r\n /** 是否隐藏 */\r\n hidden?: boolean\r\n /** 分割线(此项渲染为分割线,label 被忽略) */\r\n divider?: boolean\r\n /** 子菜单(支持无限嵌套) */\r\n children?: ContextMenuItem[]\r\n /** 自定义样式类 */\r\n className?: string\r\n /** 危险操作(红色高亮) */\r\n danger?: boolean\r\n}\r\n\r\n/** 组件 Props */\r\nexport interface ContextMenuProps {\r\n /** 菜单项列表 */\r\n items: ContextMenuItem[]\r\n /** 菜单最小宽度 */\r\n minWidth?: number\r\n /** 菜单最大宽度 */\r\n maxWidth?: number\r\n /** 子菜单展开方向 */\r\n subMenuPlacement?: 'right' | 'left'\r\n /** 点击菜单项后是否自动关闭 */\r\n autoClose?: boolean\r\n /** 是否禁用整个菜单 */\r\n disabled?: boolean\r\n /** 自定义 z-index */\r\n zIndex?: number\r\n}\r\n\r\n/** 默认 Props */\r\nexport const DEFAULT_CONTEXT_MENU_PROPS = {\r\n minWidth: 180,\r\n maxWidth: 280,\r\n subMenuPlacement: 'right' as const,\r\n autoClose: true,\r\n disabled: false,\r\n zIndex: 9999,\r\n}\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: 右键菜单组件 — 声明式配置、快捷键标注、嵌套子菜单\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\r\n-->\r\n<template>\r\n <!-- 遮罩层 -->\r\n <Teleport to=\"body\">\r\n <div\r\n v-if=\"visible\"\r\n class=\"c-context-menu-overlay\"\r\n :style=\"{ zIndex: props.zIndex - 1 }\"\r\n @click=\"close\"\r\n @contextmenu.prevent=\"close\"\r\n />\r\n\r\n <!-- 菜单面板 -->\r\n <div\r\n v-if=\"visible\"\r\n ref=\"menuRef\"\r\n class=\"c-context-menu\"\r\n :style=\"menuStyle\"\r\n role=\"menu\"\r\n tabindex=\"-1\"\r\n @keydown.escape=\"close\"\r\n >\r\n <template\r\n v-for=\"item in visibleItems\"\r\n :key=\"item.key\"\r\n >\r\n <!-- 分割线 -->\r\n <div\r\n v-if=\"item.divider\"\r\n class=\"c-context-menu__divider\"\r\n role=\"separator\"\r\n />\r\n\r\n <!-- 普通菜单项 -->\r\n <div\r\n v-else\r\n :class=\"[\r\n 'c-context-menu__item',\r\n {\r\n 'is-disabled': item.disabled,\r\n 'is-danger': item.danger,\r\n },\r\n ]\"\r\n role=\"menuitem\"\r\n :aria-disabled=\"item.disabled\"\r\n @click=\"handleItemClick(item)\"\r\n @mouseenter=\"handleMouseEnter(item)\"\r\n @mouseleave=\"handleMouseLeave\"\r\n >\r\n <!-- 图标 -->\r\n <span\r\n v-if=\"item.icon\"\r\n class=\"c-context-menu__icon\"\r\n >\r\n <C_Icon :name=\"item.icon\" />\r\n </span>\r\n\r\n <!-- 文本 -->\r\n <span class=\"c-context-menu__label\">{{ item.label }}</span>\r\n\r\n <!-- 快捷键 -->\r\n <span\r\n v-if=\"item.shortcut\"\r\n class=\"c-context-menu__shortcut\"\r\n >\r\n {{ item.shortcut }}\r\n </span>\r\n\r\n <!-- 子菜单箭头 -->\r\n <span\r\n v-if=\"item.children?.length\"\r\n class=\"c-context-menu__arrow\"\r\n >\r\n <C_Icon name=\"mdi:chevron-right\" />\r\n </span>\r\n\r\n <!-- 子菜单(递归) -->\r\n <C_ContextMenu\r\n v-if=\"item.children?.length && activeSubKey === item.key\"\r\n :items=\"item.children\"\r\n :min-width=\"props.minWidth\"\r\n :max-width=\"props.maxWidth\"\r\n :sub-menu-placement=\"props.subMenuPlacement\"\r\n :auto-close=\"props.autoClose\"\r\n :z-index=\"props.zIndex + 1\"\r\n class=\"c-context-menu__submenu\"\r\n :class=\"`is-${props.subMenuPlacement}`\"\r\n :style=\"{ position: 'absolute' }\"\r\n @select=\"handleSubSelect\"\r\n />\r\n </div>\r\n </template>\r\n </div>\r\n </Teleport>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed, nextTick, ref } from 'vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import {\r\n DEFAULT_CONTEXT_MENU_PROPS,\r\n type ContextMenuItem,\r\n type ContextMenuProps,\r\n } from './types'\r\n\r\n defineOptions({ name: 'C_ContextMenu' })\r\n\r\n const props = withDefaults(defineProps<ContextMenuProps>(), {\r\n items: () => [],\r\n minWidth: DEFAULT_CONTEXT_MENU_PROPS.minWidth,\r\n maxWidth: DEFAULT_CONTEXT_MENU_PROPS.maxWidth,\r\n subMenuPlacement: DEFAULT_CONTEXT_MENU_PROPS.subMenuPlacement,\r\n autoClose: DEFAULT_CONTEXT_MENU_PROPS.autoClose,\r\n disabled: DEFAULT_CONTEXT_MENU_PROPS.disabled,\r\n zIndex: DEFAULT_CONTEXT_MENU_PROPS.zIndex,\r\n })\r\n\r\n const emit = defineEmits<{\r\n (e: 'select', item: ContextMenuItem): void\r\n (e: 'open', position: { x: number; y: number }): void\r\n (e: 'close'): void\r\n }>()\r\n\r\n // ===== 状态 =====\r\n const visible = ref(false)\r\n const position = ref({ x: 0, y: 0 })\r\n const menuRef = ref<HTMLElement>()\r\n const activeSubKey = ref<string | null>(null)\r\n\r\n let subTimer: ReturnType<typeof setTimeout> | null = null\r\n\r\n // ===== 可见项 =====\r\n const visibleItems = computed(() => props.items.filter(item => !item.hidden))\r\n\r\n // ===== 菜单定位样式 =====\r\n const menuStyle = computed(() => ({\r\n left: `${position.value.x}px`,\r\n top: `${position.value.y}px`,\r\n minWidth: `${props.minWidth}px`,\r\n maxWidth: `${props.maxWidth}px`,\r\n zIndex: props.zIndex,\r\n }))\r\n\r\n // ===== 边界校正 =====\r\n const adjustPosition = () => {\r\n nextTick(() => {\r\n const el = menuRef.value\r\n if (!el) return\r\n const rect = el.getBoundingClientRect()\r\n const { innerWidth, innerHeight } = window\r\n\r\n if (rect.right > innerWidth) {\r\n position.value.x = innerWidth - rect.width - 8\r\n }\r\n if (rect.bottom > innerHeight) {\r\n position.value.y = innerHeight - rect.height - 8\r\n }\r\n if (position.value.x < 0) position.value.x = 8\r\n if (position.value.y < 0) position.value.y = 8\r\n })\r\n }\r\n\r\n // ===== 打开/关闭 =====\r\n const open = (x: number, y: number) => {\r\n if (props.disabled) return\r\n position.value = { x, y }\r\n visible.value = true\r\n emit('open', { x, y })\r\n adjustPosition()\r\n\r\n // 聚焦以支持 Escape 关闭\r\n nextTick(() => menuRef.value?.focus())\r\n }\r\n\r\n const close = () => {\r\n visible.value = false\r\n activeSubKey.value = null\r\n emit('close')\r\n }\r\n\r\n // ===== 菜单项点击 =====\r\n const handleItemClick = (item: ContextMenuItem) => {\r\n if (item.disabled) return\r\n if (item.children?.length) return // 有子菜单不触发\r\n\r\n emit('select', item)\r\n if (props.autoClose) close()\r\n }\r\n\r\n // ===== 子菜单悬停 =====\r\n const handleMouseEnter = (item: ContextMenuItem) => {\r\n if (subTimer) {\r\n clearTimeout(subTimer)\r\n subTimer = null\r\n }\r\n if (item.children?.length && !item.disabled) {\r\n subTimer = setTimeout(() => {\r\n activeSubKey.value = item.key\r\n }, 150)\r\n } else {\r\n activeSubKey.value = null\r\n }\r\n }\r\n\r\n const handleMouseLeave = () => {\r\n if (subTimer) {\r\n clearTimeout(subTimer)\r\n subTimer = null\r\n }\r\n subTimer = setTimeout(() => {\r\n activeSubKey.value = null\r\n }, 300)\r\n }\r\n\r\n // ===== 子菜单选择向上冒泡 =====\r\n const handleSubSelect = (item: ContextMenuItem) => {\r\n emit('select', item)\r\n if (props.autoClose) close()\r\n }\r\n\r\n // ===== 暴露 API =====\r\n defineExpose({\r\n /** 在指定坐标打开菜单 */\r\n open,\r\n /** 关闭菜单 */\r\n close,\r\n /** 当前是否可见 */\r\n visible: computed(() => visible.value),\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 * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\r\n-->\r\n<template>\r\n <!-- 遮罩层 -->\r\n <Teleport to=\"body\">\r\n <div\r\n v-if=\"visible\"\r\n class=\"c-context-menu-overlay\"\r\n :style=\"{ zIndex: props.zIndex - 1 }\"\r\n @click=\"close\"\r\n @contextmenu.prevent=\"close\"\r\n />\r\n\r\n <!-- 菜单面板 -->\r\n <div\r\n v-if=\"visible\"\r\n ref=\"menuRef\"\r\n class=\"c-context-menu\"\r\n :style=\"menuStyle\"\r\n role=\"menu\"\r\n tabindex=\"-1\"\r\n @keydown.escape=\"close\"\r\n >\r\n <template\r\n v-for=\"item in visibleItems\"\r\n :key=\"item.key\"\r\n >\r\n <!-- 分割线 -->\r\n <div\r\n v-if=\"item.divider\"\r\n class=\"c-context-menu__divider\"\r\n role=\"separator\"\r\n />\r\n\r\n <!-- 普通菜单项 -->\r\n <div\r\n v-else\r\n :class=\"[\r\n 'c-context-menu__item',\r\n {\r\n 'is-disabled': item.disabled,\r\n 'is-danger': item.danger,\r\n },\r\n ]\"\r\n role=\"menuitem\"\r\n :aria-disabled=\"item.disabled\"\r\n @click=\"handleItemClick(item)\"\r\n @mouseenter=\"handleMouseEnter(item)\"\r\n @mouseleave=\"handleMouseLeave\"\r\n >\r\n <!-- 图标 -->\r\n <span\r\n v-if=\"item.icon\"\r\n class=\"c-context-menu__icon\"\r\n >\r\n <C_Icon :name=\"item.icon\" />\r\n </span>\r\n\r\n <!-- 文本 -->\r\n <span class=\"c-context-menu__label\">{{ item.label }}</span>\r\n\r\n <!-- 快捷键 -->\r\n <span\r\n v-if=\"item.shortcut\"\r\n class=\"c-context-menu__shortcut\"\r\n >\r\n {{ item.shortcut }}\r\n </span>\r\n\r\n <!-- 子菜单箭头 -->\r\n <span\r\n v-if=\"item.children?.length\"\r\n class=\"c-context-menu__arrow\"\r\n >\r\n <C_Icon name=\"mdi:chevron-right\" />\r\n </span>\r\n\r\n <!-- 子菜单(递归) -->\r\n <C_ContextMenu\r\n v-if=\"item.children?.length && activeSubKey === item.key\"\r\n :items=\"item.children\"\r\n :min-width=\"props.minWidth\"\r\n :max-width=\"props.maxWidth\"\r\n :sub-menu-placement=\"props.subMenuPlacement\"\r\n :auto-close=\"props.autoClose\"\r\n :z-index=\"props.zIndex + 1\"\r\n class=\"c-context-menu__submenu\"\r\n :class=\"`is-${props.subMenuPlacement}`\"\r\n :style=\"{ position: 'absolute' }\"\r\n @select=\"handleSubSelect\"\r\n />\r\n </div>\r\n </template>\r\n </div>\r\n </Teleport>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed, nextTick, ref } from 'vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import {\r\n DEFAULT_CONTEXT_MENU_PROPS,\r\n type ContextMenuItem,\r\n type ContextMenuProps,\r\n } from './types'\r\n\r\n defineOptions({ name: 'C_ContextMenu' })\r\n\r\n const props = withDefaults(defineProps<ContextMenuProps>(), {\r\n items: () => [],\r\n minWidth: DEFAULT_CONTEXT_MENU_PROPS.minWidth,\r\n maxWidth: DEFAULT_CONTEXT_MENU_PROPS.maxWidth,\r\n subMenuPlacement: DEFAULT_CONTEXT_MENU_PROPS.subMenuPlacement,\r\n autoClose: DEFAULT_CONTEXT_MENU_PROPS.autoClose,\r\n disabled: DEFAULT_CONTEXT_MENU_PROPS.disabled,\r\n zIndex: DEFAULT_CONTEXT_MENU_PROPS.zIndex,\r\n })\r\n\r\n const emit = defineEmits<{\r\n (e: 'select', item: ContextMenuItem): void\r\n (e: 'open', position: { x: number; y: number }): void\r\n (e: 'close'): void\r\n }>()\r\n\r\n // ===== 状态 =====\r\n const visible = ref(false)\r\n const position = ref({ x: 0, y: 0 })\r\n const menuRef = ref<HTMLElement>()\r\n const activeSubKey = ref<string | null>(null)\r\n\r\n let subTimer: ReturnType<typeof setTimeout> | null = null\r\n\r\n // ===== 可见项 =====\r\n const visibleItems = computed(() => props.items.filter(item => !item.hidden))\r\n\r\n // ===== 菜单定位样式 =====\r\n const menuStyle = computed(() => ({\r\n left: `${position.value.x}px`,\r\n top: `${position.value.y}px`,\r\n minWidth: `${props.minWidth}px`,\r\n maxWidth: `${props.maxWidth}px`,\r\n zIndex: props.zIndex,\r\n }))\r\n\r\n // ===== 边界校正 =====\r\n const adjustPosition = () => {\r\n nextTick(() => {\r\n const el = menuRef.value\r\n if (!el) return\r\n const rect = el.getBoundingClientRect()\r\n const { innerWidth, innerHeight } = window\r\n\r\n if (rect.right > innerWidth) {\r\n position.value.x = innerWidth - rect.width - 8\r\n }\r\n if (rect.bottom > innerHeight) {\r\n position.value.y = innerHeight - rect.height - 8\r\n }\r\n if (position.value.x < 0) position.value.x = 8\r\n if (position.value.y < 0) position.value.y = 8\r\n })\r\n }\r\n\r\n // ===== 打开/关闭 =====\r\n const open = (x: number, y: number) => {\r\n if (props.disabled) return\r\n position.value = { x, y }\r\n visible.value = true\r\n emit('open', { x, y })\r\n adjustPosition()\r\n\r\n // 聚焦以支持 Escape 关闭\r\n nextTick(() => menuRef.value?.focus())\r\n }\r\n\r\n const close = () => {\r\n visible.value = false\r\n activeSubKey.value = null\r\n emit('close')\r\n }\r\n\r\n // ===== 菜单项点击 =====\r\n const handleItemClick = (item: ContextMenuItem) => {\r\n if (item.disabled) return\r\n if (item.children?.length) return // 有子菜单不触发\r\n\r\n emit('select', item)\r\n if (props.autoClose) close()\r\n }\r\n\r\n // ===== 子菜单悬停 =====\r\n const handleMouseEnter = (item: ContextMenuItem) => {\r\n if (subTimer) {\r\n clearTimeout(subTimer)\r\n subTimer = null\r\n }\r\n if (item.children?.length && !item.disabled) {\r\n subTimer = setTimeout(() => {\r\n activeSubKey.value = item.key\r\n }, 150)\r\n } else {\r\n activeSubKey.value = null\r\n }\r\n }\r\n\r\n const handleMouseLeave = () => {\r\n if (subTimer) {\r\n clearTimeout(subTimer)\r\n subTimer = null\r\n }\r\n subTimer = setTimeout(() => {\r\n activeSubKey.value = null\r\n }, 300)\r\n }\r\n\r\n // ===== 子菜单选择向上冒泡 =====\r\n const handleSubSelect = (item: ContextMenuItem) => {\r\n emit('select', item)\r\n if (props.autoClose) close()\r\n }\r\n\r\n // ===== 暴露 API =====\r\n defineExpose({\r\n /** 在指定坐标打开菜单 */\r\n open,\r\n /** 关闭菜单 */\r\n close,\r\n /** 当前是否可见 */\r\n visible: computed(() => visible.value),\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 * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\r\n-->\r\n<template>\r\n <!-- 遮罩层 -->\r\n <Teleport to=\"body\">\r\n <div\r\n v-if=\"visible\"\r\n class=\"c-context-menu-overlay\"\r\n :style=\"{ zIndex: props.zIndex - 1 }\"\r\n @click=\"close\"\r\n @contextmenu.prevent=\"close\"\r\n />\r\n\r\n <!-- 菜单面板 -->\r\n <div\r\n v-if=\"visible\"\r\n ref=\"menuRef\"\r\n class=\"c-context-menu\"\r\n :style=\"menuStyle\"\r\n role=\"menu\"\r\n tabindex=\"-1\"\r\n @keydown.escape=\"close\"\r\n >\r\n <template\r\n v-for=\"item in visibleItems\"\r\n :key=\"item.key\"\r\n >\r\n <!-- 分割线 -->\r\n <div\r\n v-if=\"item.divider\"\r\n class=\"c-context-menu__divider\"\r\n role=\"separator\"\r\n />\r\n\r\n <!-- 普通菜单项 -->\r\n <div\r\n v-else\r\n :class=\"[\r\n 'c-context-menu__item',\r\n {\r\n 'is-disabled': item.disabled,\r\n 'is-danger': item.danger,\r\n },\r\n ]\"\r\n role=\"menuitem\"\r\n :aria-disabled=\"item.disabled\"\r\n @click=\"handleItemClick(item)\"\r\n @mouseenter=\"handleMouseEnter(item)\"\r\n @mouseleave=\"handleMouseLeave\"\r\n >\r\n <!-- 图标 -->\r\n <span\r\n v-if=\"item.icon\"\r\n class=\"c-context-menu__icon\"\r\n >\r\n <C_Icon :name=\"item.icon\" />\r\n </span>\r\n\r\n <!-- 文本 -->\r\n <span class=\"c-context-menu__label\">{{ item.label }}</span>\r\n\r\n <!-- 快捷键 -->\r\n <span\r\n v-if=\"item.shortcut\"\r\n class=\"c-context-menu__shortcut\"\r\n >\r\n {{ item.shortcut }}\r\n </span>\r\n\r\n <!-- 子菜单箭头 -->\r\n <span\r\n v-if=\"item.children?.length\"\r\n class=\"c-context-menu__arrow\"\r\n >\r\n <C_Icon name=\"mdi:chevron-right\" />\r\n </span>\r\n\r\n <!-- 子菜单(递归) -->\r\n <C_ContextMenu\r\n v-if=\"item.children?.length && activeSubKey === item.key\"\r\n :items=\"item.children\"\r\n :min-width=\"props.minWidth\"\r\n :max-width=\"props.maxWidth\"\r\n :sub-menu-placement=\"props.subMenuPlacement\"\r\n :auto-close=\"props.autoClose\"\r\n :z-index=\"props.zIndex + 1\"\r\n class=\"c-context-menu__submenu\"\r\n :class=\"`is-${props.subMenuPlacement}`\"\r\n :style=\"{ position: 'absolute' }\"\r\n @select=\"handleSubSelect\"\r\n />\r\n </div>\r\n </template>\r\n </div>\r\n </Teleport>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed, nextTick, ref } from 'vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import {\r\n DEFAULT_CONTEXT_MENU_PROPS,\r\n type ContextMenuItem,\r\n type ContextMenuProps,\r\n } from './types'\r\n\r\n defineOptions({ name: 'C_ContextMenu' })\r\n\r\n const props = withDefaults(defineProps<ContextMenuProps>(), {\r\n items: () => [],\r\n minWidth: DEFAULT_CONTEXT_MENU_PROPS.minWidth,\r\n maxWidth: DEFAULT_CONTEXT_MENU_PROPS.maxWidth,\r\n subMenuPlacement: DEFAULT_CONTEXT_MENU_PROPS.subMenuPlacement,\r\n autoClose: DEFAULT_CONTEXT_MENU_PROPS.autoClose,\r\n disabled: DEFAULT_CONTEXT_MENU_PROPS.disabled,\r\n zIndex: DEFAULT_CONTEXT_MENU_PROPS.zIndex,\r\n })\r\n\r\n const emit = defineEmits<{\r\n (e: 'select', item: ContextMenuItem): void\r\n (e: 'open', position: { x: number; y: number }): void\r\n (e: 'close'): void\r\n }>()\r\n\r\n // ===== 状态 =====\r\n const visible = ref(false)\r\n const position = ref({ x: 0, y: 0 })\r\n const menuRef = ref<HTMLElement>()\r\n const activeSubKey = ref<string | null>(null)\r\n\r\n let subTimer: ReturnType<typeof setTimeout> | null = null\r\n\r\n // ===== 可见项 =====\r\n const visibleItems = computed(() => props.items.filter(item => !item.hidden))\r\n\r\n // ===== 菜单定位样式 =====\r\n const menuStyle = computed(() => ({\r\n left: `${position.value.x}px`,\r\n top: `${position.value.y}px`,\r\n minWidth: `${props.minWidth}px`,\r\n maxWidth: `${props.maxWidth}px`,\r\n zIndex: props.zIndex,\r\n }))\r\n\r\n // ===== 边界校正 =====\r\n const adjustPosition = () => {\r\n nextTick(() => {\r\n const el = menuRef.value\r\n if (!el) return\r\n const rect = el.getBoundingClientRect()\r\n const { innerWidth, innerHeight } = window\r\n\r\n if (rect.right > innerWidth) {\r\n position.value.x = innerWidth - rect.width - 8\r\n }\r\n if (rect.bottom > innerHeight) {\r\n position.value.y = innerHeight - rect.height - 8\r\n }\r\n if (position.value.x < 0) position.value.x = 8\r\n if (position.value.y < 0) position.value.y = 8\r\n })\r\n }\r\n\r\n // ===== 打开/关闭 =====\r\n const open = (x: number, y: number) => {\r\n if (props.disabled) return\r\n position.value = { x, y }\r\n visible.value = true\r\n emit('open', { x, y })\r\n adjustPosition()\r\n\r\n // 聚焦以支持 Escape 关闭\r\n nextTick(() => menuRef.value?.focus())\r\n }\r\n\r\n const close = () => {\r\n visible.value = false\r\n activeSubKey.value = null\r\n emit('close')\r\n }\r\n\r\n // ===== 菜单项点击 =====\r\n const handleItemClick = (item: ContextMenuItem) => {\r\n if (item.disabled) return\r\n if (item.children?.length) return // 有子菜单不触发\r\n\r\n emit('select', item)\r\n if (props.autoClose) close()\r\n }\r\n\r\n // ===== 子菜单悬停 =====\r\n const handleMouseEnter = (item: ContextMenuItem) => {\r\n if (subTimer) {\r\n clearTimeout(subTimer)\r\n subTimer = null\r\n }\r\n if (item.children?.length && !item.disabled) {\r\n subTimer = setTimeout(() => {\r\n activeSubKey.value = item.key\r\n }, 150)\r\n } else {\r\n activeSubKey.value = null\r\n }\r\n }\r\n\r\n const handleMouseLeave = () => {\r\n if (subTimer) {\r\n clearTimeout(subTimer)\r\n subTimer = null\r\n }\r\n subTimer = setTimeout(() => {\r\n activeSubKey.value = null\r\n }, 300)\r\n }\r\n\r\n // ===== 子菜单选择向上冒泡 =====\r\n const handleSubSelect = (item: ContextMenuItem) => {\r\n emit('select', item)\r\n if (props.autoClose) close()\r\n }\r\n\r\n // ===== 暴露 API =====\r\n defineExpose({\r\n /** 在指定坐标打开菜单 */\r\n open,\r\n /** 关闭菜单 */\r\n close,\r\n /** 当前是否可见 */\r\n visible: computed(() => visible.value),\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_ContextMenu.js","names":[],"sources":["../src/components/C_ContextMenu/types.ts","../src/components/C_ContextMenu/index.vue","../src/components/C_ContextMenu/index.vue","../src/components/C_ContextMenu/index.vue"],"sourcesContent":["/**\n * @Description: 右键菜单组件类型定义\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n */\n\n/** 菜单项 */\nexport interface ContextMenuItem {\n /** 唯一标识 */\n key: string\n /** 菜单文字 */\n label: string\n /** 图标(Iconify 图标名) */\n icon?: string\n /** 快捷键标注文字(仅展示,不绑定) */\n shortcut?: string\n /** 是否禁用 */\n disabled?: boolean\n /** 是否隐藏 */\n hidden?: boolean\n /** 分割线(此项渲染为分割线,label 被忽略) */\n divider?: boolean\n /** 子菜单(支持无限嵌套) */\n children?: ContextMenuItem[]\n /** 自定义样式类 */\n className?: string\n /** 危险操作(红色高亮) */\n danger?: boolean\n}\n\n/** 组件 Props */\nexport interface ContextMenuProps {\n /** 菜单项列表 */\n items: ContextMenuItem[]\n /** 菜单最小宽度 */\n minWidth?: number\n /** 菜单最大宽度 */\n maxWidth?: number\n /** 子菜单展开方向 */\n subMenuPlacement?: 'right' | 'left'\n /** 点击菜单项后是否自动关闭 */\n autoClose?: boolean\n /** 是否禁用整个菜单 */\n disabled?: boolean\n /** 自定义 z-index */\n zIndex?: number\n}\n\n/** 默认 Props */\nexport const DEFAULT_CONTEXT_MENU_PROPS = {\n minWidth: 180,\n maxWidth: 280,\n subMenuPlacement: 'right' as const,\n autoClose: true,\n disabled: false,\n zIndex: 9999,\n}\n","/* unplugin-vue-components disabled */<!--\n * @Description: 右键菜单组件 — 声明式配置、快捷键标注、嵌套子菜单\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <!-- 遮罩层 -->\n <Teleport to=\"body\">\n <div\n v-if=\"visible\"\n class=\"c-context-menu-overlay\"\n :style=\"{ zIndex: props.zIndex - 1 }\"\n @click=\"close\"\n @contextmenu.prevent=\"close\"\n />\n\n <!-- 菜单面板 -->\n <div\n v-if=\"visible\"\n ref=\"menuRef\"\n class=\"c-context-menu\"\n :style=\"menuStyle\"\n role=\"menu\"\n tabindex=\"-1\"\n @keydown.escape=\"close\"\n >\n <template\n v-for=\"item in visibleItems\"\n :key=\"item.key\"\n >\n <!-- 分割线 -->\n <div\n v-if=\"item.divider\"\n class=\"c-context-menu__divider\"\n role=\"separator\"\n />\n\n <!-- 普通菜单项 -->\n <div\n v-else\n :class=\"[\n 'c-context-menu__item',\n {\n 'is-disabled': item.disabled,\n 'is-danger': item.danger,\n },\n ]\"\n role=\"menuitem\"\n :aria-disabled=\"item.disabled\"\n @click=\"handleItemClick(item)\"\n @mouseenter=\"handleMouseEnter(item)\"\n @mouseleave=\"handleMouseLeave\"\n >\n <!-- 图标 -->\n <span\n v-if=\"item.icon\"\n class=\"c-context-menu__icon\"\n >\n <C_Icon :name=\"item.icon\" />\n </span>\n\n <!-- 文本 -->\n <span class=\"c-context-menu__label\">{{ item.label }}</span>\n\n <!-- 快捷键 -->\n <span\n v-if=\"item.shortcut\"\n class=\"c-context-menu__shortcut\"\n >\n {{ item.shortcut }}\n </span>\n\n <!-- 子菜单箭头 -->\n <span\n v-if=\"item.children?.length\"\n class=\"c-context-menu__arrow\"\n >\n <C_Icon name=\"mdi:chevron-right\" />\n </span>\n\n <!-- 子菜单(递归) -->\n <C_ContextMenu\n v-if=\"item.children?.length && activeSubKey === item.key\"\n :items=\"item.children\"\n :min-width=\"props.minWidth\"\n :max-width=\"props.maxWidth\"\n :sub-menu-placement=\"props.subMenuPlacement\"\n :auto-close=\"props.autoClose\"\n :z-index=\"props.zIndex + 1\"\n class=\"c-context-menu__submenu\"\n :class=\"`is-${props.subMenuPlacement}`\"\n :style=\"{ position: 'absolute' }\"\n @select=\"handleSubSelect\"\n />\n </div>\n </template>\n </div>\n </Teleport>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, nextTick, ref } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_CONTEXT_MENU_PROPS,\n type ContextMenuItem,\n type ContextMenuProps,\n } from './types'\n\n defineOptions({ name: 'C_ContextMenu' })\n\n const props = withDefaults(defineProps<ContextMenuProps>(), {\n items: () => [],\n minWidth: DEFAULT_CONTEXT_MENU_PROPS.minWidth,\n maxWidth: DEFAULT_CONTEXT_MENU_PROPS.maxWidth,\n subMenuPlacement: DEFAULT_CONTEXT_MENU_PROPS.subMenuPlacement,\n autoClose: DEFAULT_CONTEXT_MENU_PROPS.autoClose,\n disabled: DEFAULT_CONTEXT_MENU_PROPS.disabled,\n zIndex: DEFAULT_CONTEXT_MENU_PROPS.zIndex,\n })\n\n const emit = defineEmits<{\n (e: 'select', item: ContextMenuItem): void\n (e: 'open', position: { x: number; y: number }): void\n (e: 'close'): void\n }>()\n\n // ===== 状态 =====\n const visible = ref(false)\n const position = ref({ x: 0, y: 0 })\n const menuRef = ref<HTMLElement>()\n const activeSubKey = ref<string | null>(null)\n\n let subTimer: ReturnType<typeof setTimeout> | null = null\n\n // ===== 可见项 =====\n const visibleItems = computed(() => props.items.filter(item => !item.hidden))\n\n // ===== 菜单定位样式 =====\n const menuStyle = computed(() => ({\n left: `${position.value.x}px`,\n top: `${position.value.y}px`,\n minWidth: `${props.minWidth}px`,\n maxWidth: `${props.maxWidth}px`,\n zIndex: props.zIndex,\n }))\n\n // ===== 边界校正 =====\n const adjustPosition = () => {\n nextTick(() => {\n const el = menuRef.value\n if (!el) return\n const rect = el.getBoundingClientRect()\n const { innerWidth, innerHeight } = window\n\n if (rect.right > innerWidth) {\n position.value.x = innerWidth - rect.width - 8\n }\n if (rect.bottom > innerHeight) {\n position.value.y = innerHeight - rect.height - 8\n }\n if (position.value.x < 0) position.value.x = 8\n if (position.value.y < 0) position.value.y = 8\n })\n }\n\n // ===== 打开/关闭 =====\n const open = (x: number, y: number) => {\n if (props.disabled) return\n position.value = { x, y }\n visible.value = true\n emit('open', { x, y })\n adjustPosition()\n\n // 聚焦以支持 Escape 关闭\n nextTick(() => menuRef.value?.focus())\n }\n\n const close = () => {\n visible.value = false\n activeSubKey.value = null\n emit('close')\n }\n\n // ===== 菜单项点击 =====\n const handleItemClick = (item: ContextMenuItem) => {\n if (item.disabled) return\n if (item.children?.length) return // 有子菜单不触发\n\n emit('select', item)\n if (props.autoClose) close()\n }\n\n // ===== 子菜单悬停 =====\n const handleMouseEnter = (item: ContextMenuItem) => {\n if (subTimer) {\n clearTimeout(subTimer)\n subTimer = null\n }\n if (item.children?.length && !item.disabled) {\n subTimer = setTimeout(() => {\n activeSubKey.value = item.key\n }, 150)\n } else {\n activeSubKey.value = null\n }\n }\n\n const handleMouseLeave = () => {\n if (subTimer) {\n clearTimeout(subTimer)\n subTimer = null\n }\n subTimer = setTimeout(() => {\n activeSubKey.value = null\n }, 300)\n }\n\n // ===== 子菜单选择向上冒泡 =====\n const handleSubSelect = (item: ContextMenuItem) => {\n emit('select', item)\n if (props.autoClose) close()\n }\n\n // ===== 暴露 API =====\n defineExpose({\n /** 在指定坐标打开菜单 */\n open,\n /** 关闭菜单 */\n close,\n /** 当前是否可见 */\n visible: computed(() => visible.value),\n })\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","/* unplugin-vue-components disabled */<!--\n * @Description: 右键菜单组件 — 声明式配置、快捷键标注、嵌套子菜单\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <!-- 遮罩层 -->\n <Teleport to=\"body\">\n <div\n v-if=\"visible\"\n class=\"c-context-menu-overlay\"\n :style=\"{ zIndex: props.zIndex - 1 }\"\n @click=\"close\"\n @contextmenu.prevent=\"close\"\n />\n\n <!-- 菜单面板 -->\n <div\n v-if=\"visible\"\n ref=\"menuRef\"\n class=\"c-context-menu\"\n :style=\"menuStyle\"\n role=\"menu\"\n tabindex=\"-1\"\n @keydown.escape=\"close\"\n >\n <template\n v-for=\"item in visibleItems\"\n :key=\"item.key\"\n >\n <!-- 分割线 -->\n <div\n v-if=\"item.divider\"\n class=\"c-context-menu__divider\"\n role=\"separator\"\n />\n\n <!-- 普通菜单项 -->\n <div\n v-else\n :class=\"[\n 'c-context-menu__item',\n {\n 'is-disabled': item.disabled,\n 'is-danger': item.danger,\n },\n ]\"\n role=\"menuitem\"\n :aria-disabled=\"item.disabled\"\n @click=\"handleItemClick(item)\"\n @mouseenter=\"handleMouseEnter(item)\"\n @mouseleave=\"handleMouseLeave\"\n >\n <!-- 图标 -->\n <span\n v-if=\"item.icon\"\n class=\"c-context-menu__icon\"\n >\n <C_Icon :name=\"item.icon\" />\n </span>\n\n <!-- 文本 -->\n <span class=\"c-context-menu__label\">{{ item.label }}</span>\n\n <!-- 快捷键 -->\n <span\n v-if=\"item.shortcut\"\n class=\"c-context-menu__shortcut\"\n >\n {{ item.shortcut }}\n </span>\n\n <!-- 子菜单箭头 -->\n <span\n v-if=\"item.children?.length\"\n class=\"c-context-menu__arrow\"\n >\n <C_Icon name=\"mdi:chevron-right\" />\n </span>\n\n <!-- 子菜单(递归) -->\n <C_ContextMenu\n v-if=\"item.children?.length && activeSubKey === item.key\"\n :items=\"item.children\"\n :min-width=\"props.minWidth\"\n :max-width=\"props.maxWidth\"\n :sub-menu-placement=\"props.subMenuPlacement\"\n :auto-close=\"props.autoClose\"\n :z-index=\"props.zIndex + 1\"\n class=\"c-context-menu__submenu\"\n :class=\"`is-${props.subMenuPlacement}`\"\n :style=\"{ position: 'absolute' }\"\n @select=\"handleSubSelect\"\n />\n </div>\n </template>\n </div>\n </Teleport>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, nextTick, ref } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_CONTEXT_MENU_PROPS,\n type ContextMenuItem,\n type ContextMenuProps,\n } from './types'\n\n defineOptions({ name: 'C_ContextMenu' })\n\n const props = withDefaults(defineProps<ContextMenuProps>(), {\n items: () => [],\n minWidth: DEFAULT_CONTEXT_MENU_PROPS.minWidth,\n maxWidth: DEFAULT_CONTEXT_MENU_PROPS.maxWidth,\n subMenuPlacement: DEFAULT_CONTEXT_MENU_PROPS.subMenuPlacement,\n autoClose: DEFAULT_CONTEXT_MENU_PROPS.autoClose,\n disabled: DEFAULT_CONTEXT_MENU_PROPS.disabled,\n zIndex: DEFAULT_CONTEXT_MENU_PROPS.zIndex,\n })\n\n const emit = defineEmits<{\n (e: 'select', item: ContextMenuItem): void\n (e: 'open', position: { x: number; y: number }): void\n (e: 'close'): void\n }>()\n\n // ===== 状态 =====\n const visible = ref(false)\n const position = ref({ x: 0, y: 0 })\n const menuRef = ref<HTMLElement>()\n const activeSubKey = ref<string | null>(null)\n\n let subTimer: ReturnType<typeof setTimeout> | null = null\n\n // ===== 可见项 =====\n const visibleItems = computed(() => props.items.filter(item => !item.hidden))\n\n // ===== 菜单定位样式 =====\n const menuStyle = computed(() => ({\n left: `${position.value.x}px`,\n top: `${position.value.y}px`,\n minWidth: `${props.minWidth}px`,\n maxWidth: `${props.maxWidth}px`,\n zIndex: props.zIndex,\n }))\n\n // ===== 边界校正 =====\n const adjustPosition = () => {\n nextTick(() => {\n const el = menuRef.value\n if (!el) return\n const rect = el.getBoundingClientRect()\n const { innerWidth, innerHeight } = window\n\n if (rect.right > innerWidth) {\n position.value.x = innerWidth - rect.width - 8\n }\n if (rect.bottom > innerHeight) {\n position.value.y = innerHeight - rect.height - 8\n }\n if (position.value.x < 0) position.value.x = 8\n if (position.value.y < 0) position.value.y = 8\n })\n }\n\n // ===== 打开/关闭 =====\n const open = (x: number, y: number) => {\n if (props.disabled) return\n position.value = { x, y }\n visible.value = true\n emit('open', { x, y })\n adjustPosition()\n\n // 聚焦以支持 Escape 关闭\n nextTick(() => menuRef.value?.focus())\n }\n\n const close = () => {\n visible.value = false\n activeSubKey.value = null\n emit('close')\n }\n\n // ===== 菜单项点击 =====\n const handleItemClick = (item: ContextMenuItem) => {\n if (item.disabled) return\n if (item.children?.length) return // 有子菜单不触发\n\n emit('select', item)\n if (props.autoClose) close()\n }\n\n // ===== 子菜单悬停 =====\n const handleMouseEnter = (item: ContextMenuItem) => {\n if (subTimer) {\n clearTimeout(subTimer)\n subTimer = null\n }\n if (item.children?.length && !item.disabled) {\n subTimer = setTimeout(() => {\n activeSubKey.value = item.key\n }, 150)\n } else {\n activeSubKey.value = null\n }\n }\n\n const handleMouseLeave = () => {\n if (subTimer) {\n clearTimeout(subTimer)\n subTimer = null\n }\n subTimer = setTimeout(() => {\n activeSubKey.value = null\n }, 300)\n }\n\n // ===== 子菜单选择向上冒泡 =====\n const handleSubSelect = (item: ContextMenuItem) => {\n emit('select', item)\n if (props.autoClose) close()\n }\n\n // ===== 暴露 API =====\n defineExpose({\n /** 在指定坐标打开菜单 */\n open,\n /** 关闭菜单 */\n close,\n /** 当前是否可见 */\n visible: computed(() => visible.value),\n })\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Description: 右键菜单组件 — 声明式配置、快捷键标注、嵌套子菜单\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <!-- 遮罩层 -->\n <Teleport to=\"body\">\n <div\n v-if=\"visible\"\n class=\"c-context-menu-overlay\"\n :style=\"{ zIndex: props.zIndex - 1 }\"\n @click=\"close\"\n @contextmenu.prevent=\"close\"\n />\n\n <!-- 菜单面板 -->\n <div\n v-if=\"visible\"\n ref=\"menuRef\"\n class=\"c-context-menu\"\n :style=\"menuStyle\"\n role=\"menu\"\n tabindex=\"-1\"\n @keydown.escape=\"close\"\n >\n <template\n v-for=\"item in visibleItems\"\n :key=\"item.key\"\n >\n <!-- 分割线 -->\n <div\n v-if=\"item.divider\"\n class=\"c-context-menu__divider\"\n role=\"separator\"\n />\n\n <!-- 普通菜单项 -->\n <div\n v-else\n :class=\"[\n 'c-context-menu__item',\n {\n 'is-disabled': item.disabled,\n 'is-danger': item.danger,\n },\n ]\"\n role=\"menuitem\"\n :aria-disabled=\"item.disabled\"\n @click=\"handleItemClick(item)\"\n @mouseenter=\"handleMouseEnter(item)\"\n @mouseleave=\"handleMouseLeave\"\n >\n <!-- 图标 -->\n <span\n v-if=\"item.icon\"\n class=\"c-context-menu__icon\"\n >\n <C_Icon :name=\"item.icon\" />\n </span>\n\n <!-- 文本 -->\n <span class=\"c-context-menu__label\">{{ item.label }}</span>\n\n <!-- 快捷键 -->\n <span\n v-if=\"item.shortcut\"\n class=\"c-context-menu__shortcut\"\n >\n {{ item.shortcut }}\n </span>\n\n <!-- 子菜单箭头 -->\n <span\n v-if=\"item.children?.length\"\n class=\"c-context-menu__arrow\"\n >\n <C_Icon name=\"mdi:chevron-right\" />\n </span>\n\n <!-- 子菜单(递归) -->\n <C_ContextMenu\n v-if=\"item.children?.length && activeSubKey === item.key\"\n :items=\"item.children\"\n :min-width=\"props.minWidth\"\n :max-width=\"props.maxWidth\"\n :sub-menu-placement=\"props.subMenuPlacement\"\n :auto-close=\"props.autoClose\"\n :z-index=\"props.zIndex + 1\"\n class=\"c-context-menu__submenu\"\n :class=\"`is-${props.subMenuPlacement}`\"\n :style=\"{ position: 'absolute' }\"\n @select=\"handleSubSelect\"\n />\n </div>\n </template>\n </div>\n </Teleport>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, nextTick, ref } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_CONTEXT_MENU_PROPS,\n type ContextMenuItem,\n type ContextMenuProps,\n } from './types'\n\n defineOptions({ name: 'C_ContextMenu' })\n\n const props = withDefaults(defineProps<ContextMenuProps>(), {\n items: () => [],\n minWidth: DEFAULT_CONTEXT_MENU_PROPS.minWidth,\n maxWidth: DEFAULT_CONTEXT_MENU_PROPS.maxWidth,\n subMenuPlacement: DEFAULT_CONTEXT_MENU_PROPS.subMenuPlacement,\n autoClose: DEFAULT_CONTEXT_MENU_PROPS.autoClose,\n disabled: DEFAULT_CONTEXT_MENU_PROPS.disabled,\n zIndex: DEFAULT_CONTEXT_MENU_PROPS.zIndex,\n })\n\n const emit = defineEmits<{\n (e: 'select', item: ContextMenuItem): void\n (e: 'open', position: { x: number; y: number }): void\n (e: 'close'): void\n }>()\n\n // ===== 状态 =====\n const visible = ref(false)\n const position = ref({ x: 0, y: 0 })\n const menuRef = ref<HTMLElement>()\n const activeSubKey = ref<string | null>(null)\n\n let subTimer: ReturnType<typeof setTimeout> | null = null\n\n // ===== 可见项 =====\n const visibleItems = computed(() => props.items.filter(item => !item.hidden))\n\n // ===== 菜单定位样式 =====\n const menuStyle = computed(() => ({\n left: `${position.value.x}px`,\n top: `${position.value.y}px`,\n minWidth: `${props.minWidth}px`,\n maxWidth: `${props.maxWidth}px`,\n zIndex: props.zIndex,\n }))\n\n // ===== 边界校正 =====\n const adjustPosition = () => {\n nextTick(() => {\n const el = menuRef.value\n if (!el) return\n const rect = el.getBoundingClientRect()\n const { innerWidth, innerHeight } = window\n\n if (rect.right > innerWidth) {\n position.value.x = innerWidth - rect.width - 8\n }\n if (rect.bottom > innerHeight) {\n position.value.y = innerHeight - rect.height - 8\n }\n if (position.value.x < 0) position.value.x = 8\n if (position.value.y < 0) position.value.y = 8\n })\n }\n\n // ===== 打开/关闭 =====\n const open = (x: number, y: number) => {\n if (props.disabled) return\n position.value = { x, y }\n visible.value = true\n emit('open', { x, y })\n adjustPosition()\n\n // 聚焦以支持 Escape 关闭\n nextTick(() => menuRef.value?.focus())\n }\n\n const close = () => {\n visible.value = false\n activeSubKey.value = null\n emit('close')\n }\n\n // ===== 菜单项点击 =====\n const handleItemClick = (item: ContextMenuItem) => {\n if (item.disabled) return\n if (item.children?.length) return // 有子菜单不触发\n\n emit('select', item)\n if (props.autoClose) close()\n }\n\n // ===== 子菜单悬停 =====\n const handleMouseEnter = (item: ContextMenuItem) => {\n if (subTimer) {\n clearTimeout(subTimer)\n subTimer = null\n }\n if (item.children?.length && !item.disabled) {\n subTimer = setTimeout(() => {\n activeSubKey.value = item.key\n }, 150)\n } else {\n activeSubKey.value = null\n }\n }\n\n const handleMouseLeave = () => {\n if (subTimer) {\n clearTimeout(subTimer)\n subTimer = null\n }\n subTimer = setTimeout(() => {\n activeSubKey.value = null\n }, 300)\n }\n\n // ===== 子菜单选择向上冒泡 =====\n const handleSubSelect = (item: ContextMenuItem) => {\n emit('select', item)\n if (props.autoClose) close()\n }\n\n // ===== 暴露 API =====\n defineExpose({\n /** 在指定坐标打开菜单 */\n open,\n /** 关闭菜单 */\n close,\n /** 当前是否可见 */\n visible: computed(() => visible.value),\n })\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n"],"mappings":"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"}
|
package/dist/C_ContextMenu2.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./C_Icon2.js";import{t}from"./export-helper.js";import{Fragment as n,Teleport as r,computed as i,createBlock as a,createCommentVNode as o,createElementBlock as s,createElementVNode as c,createVNode as l,defineComponent as u,nextTick as d,normalizeClass as f,normalizeStyle as p,openBlock as m,ref as h,renderList as g,toDisplayString as _,withKeys as v,withModifiers as y}from"vue";const b={minWidth:180,maxWidth:280,subMenuPlacement:`right`,autoClose:!0,disabled:!1,zIndex:9999},x={key:0,class:`c-context-menu__divider`,role:`separator`},S=[`aria-disabled`,`onClick`,`onMouseenter`],C={key:0,class:`c-context-menu__icon`},w={class:`c-context-menu__label`},T={key:1,class:`c-context-menu__shortcut`},E={key:2,class:`c-context-menu__arrow`};var D=t(u({name:`C_ContextMenu`,__name:`index`,props:{items:{default:()=>[]},minWidth:{default:b.minWidth},maxWidth:{default:b.maxWidth},subMenuPlacement:{default:b.subMenuPlacement},autoClose:{type:Boolean,default:b.autoClose},disabled:{type:Boolean,default:b.disabled},zIndex:{default:b.zIndex}},emits:[`select`,`open`,`close`],setup(t,{expose:u,emit:b}){let O=t,k=b,A=h(!1),j=h({x:0,y:0}),M=h(),N=h(null),P=null,F=i(()=>O.items.filter(e=>!e.hidden)),I=i(()=>({left:`${j.value.x}px`,top:`${j.value.y}px`,minWidth:`${O.minWidth}px`,maxWidth:`${O.maxWidth}px`,zIndex:O.zIndex})),L=()=>{d(()=>{let e=M.value;if(!e)return;let t=e.getBoundingClientRect(),{innerWidth:n,innerHeight:r}=window;t.right>n&&(j.value.x=n-t.width-8),t.bottom>r&&(j.value.y=r-t.height-8),j.value.x<0&&(j.value.x=8),j.value.y<0&&(j.value.y=8)})},R=(e,t)=>{O.disabled||(j.value={x:e,y:t},A.value=!0,k(`open`,{x:e,y:t}),L(),d(()=>M.value?.focus()))},z=()=>{A.value=!1,N.value=null,k(`close`)},B=e=>{e.disabled||e.children?.length||(k(`select`,e),O.autoClose&&z())},V=e=>{P&&=(clearTimeout(P),null),e.children?.length&&!e.disabled?P=setTimeout(()=>{N.value=e.key},150):N.value=null},H=()=>{P&&=(clearTimeout(P),null),P=setTimeout(()=>{N.value=null},300)},U=e=>{k(`select`,e),O.autoClose&&z()};return u({open:R,close:z,visible:i(()=>A.value)}),(t,i)=>{let u=D;return m(),s(n,null,[o(` 遮罩层 `),(m(),a(r,{to:`body`},[A.value?(m(),s(`div`,{key:0,class:`c-context-menu-overlay`,style:p({zIndex:O.zIndex-1}),onClick:z,onContextmenu:y(z,[`prevent`])},null,36)):o(`v-if`,!0),o(` 菜单面板 `),A.value?(m(),s(`div`,{key:1,ref_key:`menuRef`,ref:M,class:`c-context-menu`,style:p(I.value),role:`menu`,tabindex:`-1`,onKeydown:v(z,[`escape`])},[(m(!0),s(n,null,g(F.value,t=>(m(),s(n,{key:t.key},[o(` 分割线 `),t.divider?(m(),s(`div`,x)):(m(),s(n,{key:1},[o(` 普通菜单项 `),c(`div`,{class:f([`c-context-menu__item`,{"is-disabled":t.disabled,"is-danger":t.danger}]),role:`menuitem`,"aria-disabled":t.disabled,onClick:e=>B(t),onMouseenter:e=>V(t),onMouseleave:H},[o(` 图标 `),t.icon?(m(),s(`span`,C,[l(e,{name:t.icon},null,8,[`name`])])):o(`v-if`,!0),o(` 文本 `),c(`span`,w,_(t.label),1),o(` 快捷键 `),t.shortcut?(m(),s(`span`,T,_(t.shortcut),1)):o(`v-if`,!0),o(` 子菜单箭头 `),t.children?.length?(m(),s(`span`,E,[l(e,{name:`mdi:chevron-right`})])):o(`v-if`,!0),o(` 子菜单(递归) `),t.children?.length&&N.value===t.key?(m(),a(u,{key:3,items:t.children,"min-width":O.minWidth,"max-width":O.maxWidth,"sub-menu-placement":O.subMenuPlacement,"auto-close":O.autoClose,"z-index":O.zIndex+1,class:f([`c-context-menu__submenu`,`is-${O.subMenuPlacement}`]),style:{position:`absolute`},onSelect:U},null,8,[`items`,`min-width`,`max-width`,`sub-menu-placement`,`auto-close`,`z-index`,`class`])):o(`v-if`,!0)],42,S)],2112))],64))),128))],36)):o(`v-if`,!0)]))],2112)}}}),[[`__scopeId`,`data-v-
|
|
1
|
+
import{t as e}from"./C_Icon2.js";import{t}from"./export-helper.js";import{Fragment as n,Teleport as r,computed as i,createBlock as a,createCommentVNode as o,createElementBlock as s,createElementVNode as c,createVNode as l,defineComponent as u,nextTick as d,normalizeClass as f,normalizeStyle as p,openBlock as m,ref as h,renderList as g,toDisplayString as _,withKeys as v,withModifiers as y}from"vue";const b={minWidth:180,maxWidth:280,subMenuPlacement:`right`,autoClose:!0,disabled:!1,zIndex:9999},x={key:0,class:`c-context-menu__divider`,role:`separator`},S=[`aria-disabled`,`onClick`,`onMouseenter`],C={key:0,class:`c-context-menu__icon`},w={class:`c-context-menu__label`},T={key:1,class:`c-context-menu__shortcut`},E={key:2,class:`c-context-menu__arrow`};var D=t(u({name:`C_ContextMenu`,__name:`index`,props:{items:{default:()=>[]},minWidth:{default:b.minWidth},maxWidth:{default:b.maxWidth},subMenuPlacement:{default:b.subMenuPlacement},autoClose:{type:Boolean,default:b.autoClose},disabled:{type:Boolean,default:b.disabled},zIndex:{default:b.zIndex}},emits:[`select`,`open`,`close`],setup(t,{expose:u,emit:b}){let O=t,k=b,A=h(!1),j=h({x:0,y:0}),M=h(),N=h(null),P=null,F=i(()=>O.items.filter(e=>!e.hidden)),I=i(()=>({left:`${j.value.x}px`,top:`${j.value.y}px`,minWidth:`${O.minWidth}px`,maxWidth:`${O.maxWidth}px`,zIndex:O.zIndex})),L=()=>{d(()=>{let e=M.value;if(!e)return;let t=e.getBoundingClientRect(),{innerWidth:n,innerHeight:r}=window;t.right>n&&(j.value.x=n-t.width-8),t.bottom>r&&(j.value.y=r-t.height-8),j.value.x<0&&(j.value.x=8),j.value.y<0&&(j.value.y=8)})},R=(e,t)=>{O.disabled||(j.value={x:e,y:t},A.value=!0,k(`open`,{x:e,y:t}),L(),d(()=>M.value?.focus()))},z=()=>{A.value=!1,N.value=null,k(`close`)},B=e=>{e.disabled||e.children?.length||(k(`select`,e),O.autoClose&&z())},V=e=>{P&&=(clearTimeout(P),null),e.children?.length&&!e.disabled?P=setTimeout(()=>{N.value=e.key},150):N.value=null},H=()=>{P&&=(clearTimeout(P),null),P=setTimeout(()=>{N.value=null},300)},U=e=>{k(`select`,e),O.autoClose&&z()};return u({open:R,close:z,visible:i(()=>A.value)}),(t,i)=>{let u=D;return m(),s(n,null,[o(` 遮罩层 `),(m(),a(r,{to:`body`},[A.value?(m(),s(`div`,{key:0,class:`c-context-menu-overlay`,style:p({zIndex:O.zIndex-1}),onClick:z,onContextmenu:y(z,[`prevent`])},null,36)):o(`v-if`,!0),o(` 菜单面板 `),A.value?(m(),s(`div`,{key:1,ref_key:`menuRef`,ref:M,class:`c-context-menu`,style:p(I.value),role:`menu`,tabindex:`-1`,onKeydown:v(z,[`escape`])},[(m(!0),s(n,null,g(F.value,t=>(m(),s(n,{key:t.key},[o(` 分割线 `),t.divider?(m(),s(`div`,x)):(m(),s(n,{key:1},[o(` 普通菜单项 `),c(`div`,{class:f([`c-context-menu__item`,{"is-disabled":t.disabled,"is-danger":t.danger}]),role:`menuitem`,"aria-disabled":t.disabled,onClick:e=>B(t),onMouseenter:e=>V(t),onMouseleave:H},[o(` 图标 `),t.icon?(m(),s(`span`,C,[l(e,{name:t.icon},null,8,[`name`])])):o(`v-if`,!0),o(` 文本 `),c(`span`,w,_(t.label),1),o(` 快捷键 `),t.shortcut?(m(),s(`span`,T,_(t.shortcut),1)):o(`v-if`,!0),o(` 子菜单箭头 `),t.children?.length?(m(),s(`span`,E,[l(e,{name:`mdi:chevron-right`})])):o(`v-if`,!0),o(` 子菜单(递归) `),t.children?.length&&N.value===t.key?(m(),a(u,{key:3,items:t.children,"min-width":O.minWidth,"max-width":O.maxWidth,"sub-menu-placement":O.subMenuPlacement,"auto-close":O.autoClose,"z-index":O.zIndex+1,class:f([`c-context-menu__submenu`,`is-${O.subMenuPlacement}`]),style:{position:`absolute`},onSelect:U},null,8,[`items`,`min-width`,`max-width`,`sub-menu-placement`,`auto-close`,`z-index`,`class`])):o(`v-if`,!0)],42,S)],2112))],64))),128))],36)):o(`v-if`,!0)]))],2112)}}}),[[`__scopeId`,`data-v-cdd4d8bb`]]);export{b as n,D as t};
|
|
2
2
|
//# sourceMappingURL=C_ContextMenu2.js.map
|