@robot-admin/layout 3.1.0 → 3.2.1
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 +166 -148
- package/README.md +518 -469
- package/dist/core/index.cjs +8 -1
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +6 -1
- package/dist/core/index.d.ts +6 -1
- package/dist/core/index.js +7 -1
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +1 -39
- package/dist/index.css +1 -1
- package/dist/index.d.cts +1327 -347
- package/dist/index.d.ts +1327 -347
- package/dist/index.js +3 -2338
- package/dist/naive/index.cjs +1 -0
- package/dist/naive/index.d.cts +2919 -0
- package/dist/naive/index.d.ts +2919 -0
- package/dist/naive/index.js +3 -0
- package/dist/naive-AliKeHvO.js +1635 -0
- package/dist/naive-AliKeHvO.js.map +1 -0
- package/dist/naive-CuOhPLBc.cjs +28 -0
- package/dist/naive-CuOhPLBc.cjs.map +1 -0
- package/dist/vue/index.cjs +1 -0
- package/dist/vue/index.d.cts +1181 -0
- package/dist/vue/index.d.ts +1181 -0
- package/dist/vue/index.js +2 -0
- package/dist/vue-DVWXVFUx.cjs +2 -0
- package/dist/vue-DVWXVFUx.cjs.map +1 -0
- package/dist/vue-h8jl2I5K.js +739 -0
- package/dist/vue-h8jl2I5K.js.map +1 -0
- package/package.json +128 -102
- package/src/styles/layouts.scss +137 -82
- package/src/styles/settings.scss +421 -391
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { A as e, C as t, D as n, E as r, F as i, I as a, L as o, M as s, N as c, O as l, P as u, S as d, T as f, _ as p, a as m, b as h, c as g, d as _, f as v, g as y, h as b, i as x, j as S, k as C, l as w, m as T, n as E, o as D, p as O, r as k, s as A, t as j, u as M, v as N, w as P, x as F, y as I } from "../vue-h8jl2I5K.js";
|
|
2
|
+
export { _ as BORDER_RADIUS_MAP, v as COLOR_SWATCHES, S as DEFAULT_BRAND_CONFIG, O as DEFAULT_SETTINGS, s as DRAWER_HANDLER_KEY, c as LAYOUT_CONTEXT_KEY, T as LAYOUT_MODES, u as LAYOUT_SETTINGS_KEY, j as LayoutActionUnavailableError, i as MENU_COLLAPSE_KEY, b as PRESET_COLORS, N as SETTINGS_CONFIG_SCHEMA_VERSION, y as THEME_PRESETS, p as TRANSITION_MAP, I as adjustColor, h as assertLayoutSettingsRelationships, w as bindLayoutCssVariables, r as calculateVisibleMenuCount, k as createLayoutContext, A as createSettingsStore, n as estimateMenuItemWidth, P as isPathSegmentPrefix, m as normalizeLayoutMenus, x as provideLayout, a as provideLayoutContext, F as sanitizeLayoutSettingsConfig, d as sanitizeSettingsPatch, D as setupLayout, C as shouldCacheRoute, e as useLayoutCache, o as useLayoutContext, M as useLayoutCssVariables, t as useLayoutIcon, f as useMenuSplit, l as useResponsiveMenu, E as useSettingsController, g as useSettingsStore };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require(`vue`),t=require(`vue-router`),n=require(`pinia`);var r=Symbol(`layout-context`),i=Symbol(`layout-settings`),a=Symbol(`menu-collapse`),o=Symbol(`drawer-handlers`);function s(){let t=(0,e.inject)(r);if(!t)throw Error(`[Layout] LayoutContext not provided. 请在上层组件中使用 provide(LAYOUT_CONTEXT_KEY, ...) 注入布局数据。`);return t}function c(t){return(0,e.provide)(r,t),t}var l={name:`Admin`,subtitle:``,logoSrc:``,logoType:`image`,logoSize:36,homePath:`/home`,copyright:``};function u(e){return typeof e.name==`string`&&e.meta.keepAlive===!0}function d(n={}){let{maxCacheCount:r=20,enableDevLog:i=!1,exposeToWindow:a=!1}=n,o=(0,t.useRoute)(),s=Number.isFinite(r)&&r>0?Math.floor(r):20,c=(0,e.ref)([]),l=(0,e.ref)(s),d=e=>{e&&!c.value.includes(e)&&(c.value.push(e),c.value.length>l.value&&c.value.shift(),i&&console.debug(`[KeepAlive] ✅ 缓存: ${e} (${c.value.length}/${l.value})`))},f=e=>{let t=c.value.indexOf(e);t>-1&&(c.value.splice(t,1),i&&console.debug(`[KeepAlive] ❌ 移除: ${e}`))},p=()=>{c.value=[],i&&console.debug(`[KeepAlive] 🗑️ 清空所有缓存`)},m=()=>c.value;if(a&&typeof window<`u`){let t=window;t.__clearCache__=p,t.__removeCache__=f,t.__getCachedViews__=m,(0,e.onUnmounted)(()=>{t.__clearCache__===p&&delete t.__clearCache__,t.__removeCache__===f&&delete t.__removeCache__,t.__getCachedViews__===m&&delete t.__getCachedViews__})}return(0,e.watch)(()=>[o.name,o.meta.keepAlive],()=>{u(o)&&d(o.name)},{immediate:!0}),{cachedViews:c,maxCacheCount:l,addCache:d,removeCache:f,clearAllCache:p}}function f(e){let t=e.meta?.title||e.label||e.name||``,n=0;for(let e of t){let t=e.charCodeAt(0);n+=t>=19968&&t<=40959||t>=13312&&t<=19903||t>=12288&&t<=12351||t>=65280&&t<=65519||t>=12352&&t<=12447||t>=12448&&t<=12543?15:8}let r=e.meta?.icon||e.icon?26:0;return n+r+36+8+8}function p(e,t,n=95){if(e.length===0||t<=0)return 0;if(e.reduce((e,t)=>e+f(t),0)+15<=t)return e.length;let r=Math.max(0,t-n),i=0,a=0;for(let t of e){let e=f(t);if(i+e>r)break;i+=e,a+=1}return Math.max(a,1)}function m(t){let n=Number.isFinite(t.debounceMs)&&(t.debounceMs??0)>=0?Math.floor(t.debounceMs??100):100,r=Number.isFinite(t.overflowReserve)&&(t.overflowReserve??0)>=0?t.overflowReserve??95:95,i=(0,e.ref)(),a=(0,e.computed)(()=>[...(0,e.toValue)(t.data)]),o=(0,e.ref)([]),s=(0,e.ref)([]),c=()=>{let e=i.value?.offsetWidth??0;if(e<=0||a.value.length===0){o.value=[],s.value=[];return}let t=p(a.value,e,r);o.value=a.value.slice(0,t),s.value=a.value.slice(t)},l,u=()=>{l!==void 0&&clearTimeout(l),l=setTimeout(c,n)},d;return(0,e.onMounted)(()=>{(0,e.nextTick)(c),i.value&&typeof ResizeObserver<`u`?(d=new ResizeObserver(u),d.observe(i.value)):typeof window<`u`&&window.addEventListener(`resize`,u,{passive:!0})}),(0,e.onUnmounted)(()=>{d?.disconnect(),typeof window<`u`&&window.removeEventListener(`resize`,u),l!==void 0&&clearTimeout(l)}),(0,e.watch)(()=>(0,e.toValue)(t.data),()=>void(0,e.nextTick)(c),{deep:!0}),{containerRef:i,hiddenItems:s,refresh:c,visibleItems:o}}function h(e,t){if(!e)return!1;let n=e.split(`/`).filter(Boolean),r=t.split(`/`).filter(Boolean);return n.length===0?r.length===0:n.every((e,t)=>r[t]===e)}function g(n){let{menus:r,floatingSecondMenu:i=!1}=n,a=(0,t.useRoute)(),o=(0,t.useRouter)(),s=(0,e.ref)(``),c=(0,e.ref)(null),l=(0,e.ref)(!1),u=(0,e.computed)(()=>c.value||p.value||null),d=e=>h(e,a.path),f=e=>{for(let t of e)if(d(t.path)||t.children?.length&&f(t.children))return t;return null},p=(0,e.computed)(()=>(0,e.toValue)(r).find(e=>e.path===s.value)),m=(0,e.computed)(()=>p.value?.children||[]),g=t=>{t.disabled||((0,e.toValue)(i)?t.children&&t.children.length>0?l.value&&s.value===t.path?l.value=!1:(s.value=t.path||``,l.value=!0):(s.value=t.path||``,l.value=!1,t.path&&o.push(t.path)):(!t.children||t.children.length===0)&&t.path&&o.push(t.path))},_=e=>{!e.disabled&&e.path&&o.push(e.path)},v=()=>{let t=(0,e.toValue)(r),n=f(t);n?(s.value=n.path||``,(0,e.toValue)(i)&&n.children?.length&&(l.value=!0)):t.length>0&&!s.value&&(s.value=t[0].path||``)};return(0,e.watch)([()=>a.path,()=>(0,e.toValue)(r)],()=>v(),{immediate:!0}),{activeFirstMenu:s,activeFirstMenuItem:p,currentSecondMenus:m,hoveredMenuItem:c,displayMenuItem:u,showSecondMenu:l,handleFirstMenuClick:g,handleSecondMenuClick:_,isMenuItemActive:d}}var _=(0,e.defineComponent)({name:`FallbackLayoutIcon`,props:{name:String,size:{type:Number,default:18}},setup(t){return()=>(0,e.h)(`i`,{class:t.name,style:{alignItems:`center`,display:`inline-flex`,fontSize:`${t.size}px`,justifyContent:`center`}})}});function v(){return s().iconComponent??_}var y={themeMode:new Set([`light`,`dark`,`system`]),borderRadius:new Set([`small`,`medium`,`large`]),transitionType:new Set([`fade`,`slide`,`zoom`,`none`]),layoutMode:new Set([`side`,`top`,`mix`,`mix-top`,`reverse-horizontal-mix`,`card-layout`]),menuExpandMode:new Set([`inline`,`panel`]),tagsViewStyle:new Set([`default`,`card`,`smart`])},b=new Set([`enableTransition`,`collapsed`,`fixedHeader`,`showBreadcrumb`,`showBreadcrumbIcon`,`showTagsView`,`showFooter`,`enableHotkeys`]),x={tagsViewHeight:[24,100],sidebarWidth:[120,480],sidebarCollapsedWidth:[32,160],headerHeight:[32,160]},S=1;function C(e){if(e.sidebarCollapsedWidth>e.sidebarWidth)throw RangeError(`sidebarCollapsedWidth 不能大于 sidebarWidth`)}function w(e){if(!e||typeof e!=`object`||Array.isArray(e))throw TypeError(`settings 必须是对象`);let t=e,n={},r=(e,t)=>{n[e]=t};for(let[e,n]of Object.entries(y)){let i=t[e];if(i!==void 0){if(typeof i!=`string`||!n.has(i))throw RangeError(`无效的设置项 ${e}: ${String(i)}`);r(e,i)}}for(let e of b){let n=t[e];if(n!==void 0){if(typeof n!=`boolean`)throw TypeError(`设置项 ${e} 必须是布尔值`);r(e,n)}}for(let[e,n]of Object.entries(x)){let i=t[e];if(i!==void 0){if(typeof i!=`number`||!Number.isFinite(i)||i<n[0]||i>n[1])throw RangeError(`设置项 ${e} 超出允许范围`);r(e,i)}}if(t.primaryColor!==void 0){if(typeof t.primaryColor!=`string`||!/^#[0-9a-fA-F]{6}$/.test(t.primaryColor))throw RangeError(`primaryColor 必须是 #rrggbb 格式`);n.primaryColor=t.primaryColor}if(t.version!==void 0){if(typeof t.version!=`string`||t.version.length>50)throw TypeError(`version 必须是长度不超过 50 的字符串`);n.version=t.version}return n}function T(e){if(!e||typeof e!=`object`||Array.isArray(e))throw TypeError(`配置文件根节点必须是对象`);let t=e,n={};if(t.schemaVersion!==void 0){if(typeof t.schemaVersion!=`number`||!Number.isInteger(t.schemaVersion)||t.schemaVersion<1||t.schemaVersion>1)throw RangeError(`不支持的配置格式版本`);n.schemaVersion=t.schemaVersion}t.settings!==void 0&&(n.settings=w(t.settings));for(let e of[`gray`,`colorWeak`]){let r=t[e];if(r!==void 0){if(typeof r!=`boolean`)throw TypeError(`${e} 必须是布尔值`);n[e]=r}}if(t.watermark!==void 0){if(!t.watermark||typeof t.watermark!=`object`||Array.isArray(t.watermark))throw TypeError(`watermark 必须是对象`);let e=t.watermark;if(typeof e.enabled!=`boolean`||typeof e.text!=`string`||e.text.length>200)throw TypeError(`watermark 配置无效`);n.watermark={enabled:e.enabled,text:e.text}}return n}function E(e,t){if(typeof e!=`string`)return`#000000`;if(!Number.isFinite(t))return e;let n=Math.round(t),r=e.trim(),i=r.startsWith(`#`)?r.slice(1):r;if(i.length===3&&(i=i.split(``).map(e=>e+e).join(``)),!/^[0-9a-fA-F]{6}$/.test(i))return e;let a=parseInt(i,16),o=Math.min(255,Math.max(0,(a>>16)+n)),s=Math.min(255,Math.max(0,(a>>8&255)+n)),c=Math.min(255,Math.max(0,(a&255)+n));return`#${(o<<16|s<<8|c).toString(16).padStart(6,`0`)}`}var D=[{name:`拂晓蓝`,value:`#409eff`},{name:`薄暮红`,value:`#f5222d`},{name:`火山橙`,value:`#fa541c`},{name:`日暮黄`,value:`#faad14`},{name:`极光绿`,value:`#52c41a`},{name:`明青`,value:`#13c2c2`},{name:`极客蓝`,value:`#2f54eb`},{name:`酱紫`,value:`#722ed1`}],O=[{name:`科技蓝`,icon:`💙`,primaryColor:`#409eff`},{name:`清新绿`,icon:`💚`,primaryColor:`#52c41a`},{name:`商务灰`,icon:`🖤`,primaryColor:`#595959`},{name:`活力橙`,icon:`🧡`,primaryColor:`#fa8c16`},{name:`优雅紫`,icon:`💜`,primaryColor:`#722ed1`},{name:`经典红`,icon:`❤️`,primaryColor:`#f5222d`}],k=[{mode:`side`,label:`左侧菜单`,description:`经典的左侧导航布局`,icon:`i-mdi:page-layout-sidebar-left`},{mode:`top`,label:`顶部菜单`,description:`顶部水平导航布局`,icon:`i-mdi:page-layout-header`},{mode:`mix`,label:`混合菜单`,description:`顶部 + 侧边混合布局`,icon:`i-mdi:view-dashboard-outline`},{mode:`mix-top`,label:`顶部混合`,description:`侧边优先的混合布局`,icon:`i-mdi:view-dashboard-variant-outline`},{mode:`reverse-horizontal-mix`,label:`反转混合`,description:`反转的水平混合布局`,icon:`i-mdi:page-layout-header-footer`},{mode:`card-layout`,label:`卡片布局`,description:`卡片式布局风格`,icon:`i-mdi:card-outline`}],A={small:`4px`,medium:`6px`,large:`8px`},j={fade:`fade-transform`,slide:`fade-slide`,zoom:`fade-zoom`,none:``},M={themeMode:`light`,primaryColor:`#409eff`,borderRadius:`medium`,transitionType:`slide`,enableTransition:!0,layoutMode:`side`,menuExpandMode:`inline`,collapsed:!1,fixedHeader:!0,showBreadcrumb:!0,showBreadcrumbIcon:!0,showTagsView:!0,tagsViewHeight:44,tagsViewStyle:`default`,showFooter:!0,sidebarWidth:220,sidebarCollapsedWidth:64,headerHeight:56,enableHotkeys:!0,version:`3.2.1`},N=D.map(e=>e.value),P=()=>typeof document>`u`?void 0:document.documentElement;function F(t,n){let r=(0,e.toValue)(t.primaryColor),i={"primary-color":r,"primary-color-hover":E(r,10),"primary-color-pressed":E(r,-10),"border-radius":(0,e.toValue)(t.borderRadiusValue),"sidebar-width":`${(0,e.toValue)(t.sidebarWidth)}px`,"sidebar-collapsed-width":`${(0,e.toValue)(t.sidebarCollapsedWidth)}px`,"header-height":`${(0,e.toValue)(t.headerHeight)}px`,"tags-view-height":`${(0,e.toValue)(t.tagsViewHeight)}px`};return Object.entries(i).flatMap(([e,t])=>{let r=[[`--ra-layout-${e}`,t]];return n&&r.push([`--${e}`,t]),r})}function I(t,n={}){let r,i=new Map,a=!1,o=()=>{if(r){for(let[e,t]of i)t?r.style.setProperty(e,t):r.style.removeProperty(e);r=void 0,i=new Map}},s=()=>{if(a)return;let s=n.target?(0,e.toValue)(n.target)??void 0:P();if(!s){o();return}let c=F(t,n.legacyAliases??!1);r!==s&&(o(),r=s,i=new Map(c.map(([e])=>[e,s.style.getPropertyValue(e)])));for(let[e,t]of c)s.style.setProperty(e,t)},c=(0,e.watch)(()=>[(0,e.toValue)(t.primaryColor),(0,e.toValue)(t.borderRadiusValue),(0,e.toValue)(t.sidebarWidth),(0,e.toValue)(t.sidebarCollapsedWidth),(0,e.toValue)(t.headerHeight),(0,e.toValue)(t.tagsViewHeight),n.target?(0,e.toValue)(n.target):void 0],s,{immediate:!0});return{dispose:()=>{a||(a=!0,c(),o())},sync:s}}function L(t,n={}){let r=I(t,n);return(0,e.getCurrentScope)()&&(0,e.onScopeDispose)(r.dispose),r}function R(t={}){let{id:r=`settings`,defaults:i={},onThemeModeChange:a,syncCssVariables:o=!0}=t;if(typeof r!=`string`||!r.trim())throw RangeError(`Settings store id 不能为空`);let s={...M,...w(i)};C(s);let c=Object.freeze({...s});return(0,n.defineStore)(r,()=>{let t=(0,e.ref)(s.themeMode),n=(0,e.ref)(s.primaryColor),r=(0,e.ref)(s.borderRadius),i=(0,e.ref)(s.transitionType),l=(0,e.ref)(s.enableTransition),u=(0,e.ref)(s.layoutMode),d=(0,e.ref)(s.menuExpandMode),f=(0,e.ref)(s.collapsed),p=(0,e.ref)(s.fixedHeader),m=(0,e.ref)(s.showBreadcrumb),h=(0,e.ref)(s.showBreadcrumbIcon),g=(0,e.ref)(s.showTagsView),_=(0,e.ref)(s.tagsViewHeight),v=(0,e.ref)(s.tagsViewStyle),y=(0,e.ref)(s.showFooter),b=(0,e.ref)(s.sidebarWidth),x=(0,e.ref)(s.sidebarCollapsedWidth),S=(0,e.ref)(s.headerHeight),C=(0,e.ref)(s.enableHotkeys),T=(0,e.ref)(s.version),E=(0,e.computed)(()=>A[r.value]),D=(0,e.computed)(()=>l.value&&i.value!==`none`),O=(0,e.computed)(()=>D.value?j[i.value]:``),k=(0,e.computed)(()=>({themeMode:t.value,primaryColor:n.value,borderRadius:r.value,transitionType:i.value,enableTransition:l.value,layoutMode:u.value,menuExpandMode:d.value,collapsed:f.value,fixedHeader:p.value,showBreadcrumb:m.value,showBreadcrumbIcon:h.value,showTagsView:g.value,tagsViewHeight:_.value,tagsViewStyle:v.value,showFooter:y.value,sidebarWidth:b.value,sidebarCollapsedWidth:x.value,headerHeight:S.value,enableHotkeys:C.value,version:T.value})),M=o?I({primaryColor:n,borderRadiusValue:E,sidebarWidth:b,sidebarCollapsedWidth:x,headerHeight:S,tagsViewHeight:_},{legacyAliases:!0}):void 0;return M&&(0,e.onScopeDispose)(M.dispose),{defaults:c,themeMode:t,primaryColor:n,borderRadius:r,transitionType:i,enableTransition:l,layoutMode:u,menuExpandMode:d,collapsed:f,fixedHeader:p,showBreadcrumb:m,showBreadcrumbIcon:h,showTagsView:g,tagsViewHeight:_,tagsViewStyle:v,showFooter:y,sidebarWidth:b,sidebarCollapsedWidth:x,headerHeight:S,enableHotkeys:C,version:T,borderRadiusValue:E,transitionName:O,shouldEnableTransition:D,settingsState:k,syncCSSVariables:()=>M?.sync(),applyPreset:async e=>{let t=w({primaryColor:e.primaryColor,...e.settings});n.value=t.primaryColor,t.borderRadius&&(r.value=t.borderRadius),t.transitionType&&(i.value=t.transitionType),t.showBreadcrumb!==void 0&&(m.value=t.showBreadcrumb),t.showTagsView!==void 0&&(g.value=t.showTagsView),t.tagsViewStyle&&(v.value=t.tagsViewStyle)},resetSettings:()=>{t.value=s.themeMode,n.value=s.primaryColor,r.value=s.borderRadius,i.value=s.transitionType,l.value=s.enableTransition,u.value=s.layoutMode,d.value=s.menuExpandMode,f.value=s.collapsed,p.value=s.fixedHeader,m.value=s.showBreadcrumb,h.value=s.showBreadcrumbIcon,g.value=s.showTagsView,_.value=s.tagsViewHeight,v.value=s.tagsViewStyle,y.value=s.showFooter,b.value=s.sidebarWidth,x.value=s.sidebarCollapsedWidth,S.value=s.headerHeight,C.value=s.enableHotkeys,T.value=s.version},updateThemeMode:async e=>{w({themeMode:e});let n=t.value;t.value=e;try{a&&await a(e)}catch(e){throw t.value=n,e}},toggleCollapse:()=>{f.value=!f.value}}})}var z=R();function B(e,t={}){let n=R(t),r=n();return e.provide(i,r),n}function V(e){return e.flatMap(e=>{let t=e.path||e.key;if(!t)return[];let n=e.meta?.title||e.label||e.name||t,r=e.meta?.icon??(typeof e.icon==`string`?e.icon:void 0),i={...e.meta,title:n,...r?{icon:r}:{}};return[{...e,key:e.key||t,path:t,meta:i,children:e.children?V(e.children):void 0}]})}function H(t){let{settings:n}=t;return{menus:(0,e.computed)(()=>(0,e.toValue)(t.menus)),isDark:(0,e.computed)(()=>(0,e.toValue)(t.isDark)),layoutMode:(0,e.computed)(()=>n.layoutMode),collapsed:(0,e.computed)({get:()=>n.collapsed,set:e=>{n.collapsed=e}}),menuExpandMode:(0,e.computed)(()=>n.menuExpandMode),sidebarWidth:(0,e.computed)(()=>n.sidebarWidth),sidebarCollapsedWidth:(0,e.computed)(()=>n.sidebarCollapsedWidth),showFooter:(0,e.computed)(()=>n.showFooter),showTagsView:(0,e.computed)(()=>n.showTagsView),tagsViewHeight:(0,e.computed)(()=>n.tagsViewHeight),headerHeight:(0,e.computed)(()=>n.headerHeight),transitionName:(0,e.computed)(()=>n.transitionName),showBreadcrumb:(0,e.computed)(()=>n.showBreadcrumb),showBreadcrumbIcon:(0,e.computed)(()=>n.showBreadcrumbIcon),fixedHeader:(0,e.computed)(()=>n.fixedHeader),brand:{...l,...t.brand},iconComponent:t.iconComponent}}function U(e){return c(H(e))}var W=class extends Error{constructor(e){super(`[Layout] Host action "${e}" is not configured.`),this.name=`LayoutActionUnavailableError`}},G=()=>typeof document>`u`?void 0:document;function K(t){let{store:n}=t,r=t.visualRoot?.ownerDocument??t.watermarkContainer?.ownerDocument??G(),i=t.visualRoot??r?.documentElement,a=t.watermarkContainer??r?.body,o=Number.isFinite(t.maxImportBytes)&&(t.maxImportBytes??0)>0?Math.floor(t.maxImportBytes??1024*1024):1024*1024,s=i?.classList.contains(`gray-mode`)??!1,c=i?.classList.contains(`color-weak-mode`)??!1,l=(0,e.ref)(s),u=(0,e.ref)(c),d=(0,e.ref)(!1),f=(0,e.ref)(t.watermarkText??`Robot Admin`),p,m=()=>{p?.remove(),p=void 0},h=e=>{if(m(),!r||!a)return;let n=r.createElement(`canvas`),i=n.getContext(`2d`);if(!i){t.onVisualEffectError?.(`当前浏览器无法创建水印画布`);return}n.width=200,n.height=150,i.font=`14px Microsoft JhengHei`,i.fillStyle=`rgba(120, 120, 120, 0.15)`,i.rotate(-20*Math.PI/180),i.fillText(e,0,80),p=r.createElement(`div`),p.dataset.robotAdminLayoutWatermark=`true`,Object.assign(p.style,{backgroundImage:`url(${n.toDataURL()})`,backgroundRepeat:`repeat`,height:`100%`,left:`0`,pointerEvents:`none`,position:`fixed`,top:`0`,userSelect:`none`,width:`100%`,zIndex:`9999`}),a.appendChild(p)},g=(0,e.watch)(l,e=>i?.classList.toggle(`gray-mode`,e)),_=(0,e.watch)(u,e=>i?.classList.toggle(`color-weak-mode`,e)),v=(0,e.watch)(d,e=>{e?h(f.value):m()}),y=(0,e.watch)(f,e=>{d.value&&h(e)}),b=(0,e.computed)(()=>{let e=r?.defaultView,t=e?.navigator;if(!e||!t)return{browser:`Unknown`,os:`Unknown`,resolution:`Unknown`,pixelRatio:`Unknown`,language:`Unknown`,timezone:`Unknown`};let n=t.userAgent,i=`Unknown`,a=`Unknown`;return n.includes(`Edg/`)?i=`Edge`:n.includes(`Firefox`)?i=`Firefox`:n.includes(`Chrome`)?i=`Chrome`:n.includes(`Safari`)&&(i=`Safari`),n.includes(`Windows`)?a=`Windows`:n.includes(`Mac`)?a=`macOS`:n.includes(`Android`)?a=`Android`:n.includes(`iOS`)?a=`iOS`:n.includes(`Linux`)&&(a=`Linux`),{browser:i,os:a,resolution:`${e.screen.width} × ${e.screen.height}`,pixelRatio:`${e.devicePixelRatio}x`,language:t.language,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone}}),x=e=>n.applyPreset(e),S=e=>n.updateThemeMode(e),w=async()=>{await n.updateThemeMode(n.defaults.themeMode),n.primaryColor=n.defaults.primaryColor,n.borderRadius=n.defaults.borderRadius,n.transitionType=n.defaults.transitionType,n.enableTransition=n.defaults.enableTransition},E=()=>{n.layoutMode=n.defaults.layoutMode,n.menuExpandMode=n.defaults.menuExpandMode,n.fixedHeader=n.defaults.fixedHeader,n.showBreadcrumb=n.defaults.showBreadcrumb,n.showBreadcrumbIcon=n.defaults.showBreadcrumbIcon,n.showTagsView=n.defaults.showTagsView,n.tagsViewHeight=n.defaults.tagsViewHeight,n.tagsViewStyle=n.defaults.tagsViewStyle,n.showFooter=n.defaults.showFooter,n.sidebarWidth=n.defaults.sidebarWidth,n.sidebarCollapsedWidth=n.defaults.sidebarCollapsedWidth,n.headerHeight=n.defaults.headerHeight},D=async()=>{let e={...n.settingsState};try{n.resetSettings(),await n.updateThemeMode(n.themeMode)}catch(t){throw n.$patch(e),t}},O=async()=>{if(!t.actions?.clearCache)throw new W(`clearCache`);await t.actions.clearCache()},k=()=>{t.actions?.reloadPage?t.actions.reloadPage():r?.defaultView?.location.reload()},A=()=>({schemaVersion:1,settings:n.settingsState,gray:l.value,colorWeak:u.value,watermark:{enabled:d.value,text:f.value},exportTime:new Date().toISOString()}),j=()=>{if(!r||typeof Blob>`u`||typeof URL>`u`||typeof URL.createObjectURL!=`function`)return!1;let e=new Blob([JSON.stringify(A(),null,2)],{type:`application/json`}),t=URL.createObjectURL(e),n=r.createElement(`a`);return n.href=t,n.download=`robot-admin-config-${Date.now()}.json`,n.click(),URL.revokeObjectURL(t),!0},M=async e=>{let t=T(e);if(C({...n.settingsState,...t.settings}),t.settings!==void 0){let{themeMode:e,...r}=t.settings;e!==void 0&&await n.updateThemeMode(e),n.$patch(r)}return t.gray!==void 0&&(l.value=t.gray),t.colorWeak!==void 0&&(u.value=t.colorWeak),t.watermark!==void 0&&(d.value=t.watermark.enabled,f.value=t.watermark.text),t},N=async e=>{if(e.size>o)throw RangeError(`配置文件过大`);return M(JSON.parse(await e.text()))},P=!1,F=()=>{P||(P=!0,g(),_(),v(),y(),m(),i?.classList.toggle(`gray-mode`,s),i?.classList.toggle(`color-weak-mode`,c))};return(0,e.getCurrentScope)()&&(0,e.onScopeDispose)(F),{applyImportedConfig:M,applyPreset:x,clearCache:O,colorWeakMode:u,createExportConfig:A,dispose:F,downloadConfig:j,grayMode:l,importFile:N,reloadPage:k,resetAll:D,resetAppearance:w,resetLayout:E,systemInfo:b,updateThemeMode:S,watermarkEnabled:d,watermarkText:f}}Object.defineProperty(exports,`A`,{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,`C`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`D`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`E`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`F`,{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,`I`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`L`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`M`,{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,`N`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`O`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`P`,{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,`S`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`T`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`_`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,`b`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return z}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return U}}),Object.defineProperty(exports,`j`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`k`,{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return K}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return B}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return W}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return L}}),Object.defineProperty(exports,`v`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`w`,{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,`x`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`y`,{enumerable:!0,get:function(){return E}});
|
|
2
|
+
//# sourceMappingURL=vue-DVWXVFUx.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue-DVWXVFUx.cjs","names":[],"sources":["../src/composables/useLayoutContext.ts","../src/composables/useLayoutCache.ts","../src/composables/useResponsiveMenu.ts","../src/composables/useMenuSplit.ts","../src/composables/useLayoutIcon.ts","../src/core/settings.ts","../src/constants/index.ts","../src/composables/useLayoutCssVariables.ts","../src/stores/settings.ts","../src/setup.ts","../src/utils/menu.ts","../src/composables/createLayoutContext.ts","../src/composables/useSettingsController.ts"],"sourcesContent":["/**\r\n * @robot-admin/layout\r\n *\r\n * 布局上下文 - Provide/Inject 机制\r\n * 消费方通过 provide 注入数据,骨架组件通过 inject 读取\r\n * 实现布局骨架与业务数据的完全解耦\r\n */\r\n\r\nimport type {\n InjectionKey,\n ComputedRef,\n Component,\n WritableComputedRef,\n} from \"vue\";\nimport { inject, provide } from \"vue\";\nimport type { MenuOptions } from \"../types/menu\";\r\nimport type { LayoutMode, MenuExpandMode } from \"../types\";\r\nimport type { SettingsStoreInstance } from \"../stores/settings\";\n\r\n/**\r\n * 品牌配置\r\n */\r\nexport interface LayoutBrandConfig {\r\n /** 品牌名称,如 'Robot Admin' */\r\n name?: string;\r\n /** 品牌副标题,如 '机器人管理系统' */\r\n subtitle?: string;\r\n /** Logo 地址(图片或视频) */\r\n logoSrc?: string;\r\n /** Logo 类型 */\r\n logoType?: \"video\" | \"image\";\r\n /** Logo 尺寸 */\r\n logoSize?: number;\r\n /** 首页路径,默认 '/home' */\r\n homePath?: string;\r\n /** 页脚版权文本 */\r\n copyright?: string;\r\n}\r\n\r\n/**\r\n * 布局上下文数据接口\r\n * 骨架组件通过 inject 获取这些数据\r\n */\r\nexport interface LayoutContext {\r\n // ============ 菜单数据 ============\r\n /** 菜单列表数据 */\r\n menus: ComputedRef<MenuOptions[]>;\r\n\r\n // ============ 主题状态 ============\r\n /** 是否为暗色模式 */\r\n isDark: ComputedRef<boolean>;\r\n\r\n // ============ 布局配置(来自 settings store) ============\r\n /** 当前布局模式 */\r\n layoutMode: ComputedRef<LayoutMode>;\r\n /** 侧边栏折叠状态;提供后由容器和宿主头部共享同一数据源 */\n collapsed?: WritableComputedRef<boolean>;\n /** 菜单展开方式 */\r\n menuExpandMode?: ComputedRef<MenuExpandMode>;\r\n /** 侧边栏宽度 (px) */\r\n sidebarWidth: ComputedRef<number>;\r\n /** 侧边栏折叠后宽度 (px) */\r\n sidebarCollapsedWidth: ComputedRef<number>;\r\n /** 是否显示页脚 */\r\n showFooter: ComputedRef<boolean>;\r\n /** 是否显示标签页 */\r\n showTagsView: ComputedRef<boolean>;\r\n /** 标签页高度 (px) */\r\n tagsViewHeight: ComputedRef<number>;\r\n /** 头部高度 (px) */\r\n headerHeight: ComputedRef<number>;\r\n /** 过渡动画名称 */\r\n transitionName: ComputedRef<string>;\r\n /** 是否显示面包屑 */\r\n showBreadcrumb: ComputedRef<boolean>;\r\n /** 是否显示面包屑图标 */\r\n showBreadcrumbIcon: ComputedRef<boolean>;\r\n /** 是否固定头部 */\r\n fixedHeader: ComputedRef<boolean>;\r\n\r\n // ============ 品牌配置 ============\r\n /** 品牌信息 */\r\n brand: LayoutBrandConfig;\r\n\r\n // ============ 可选:图标组件 ============\r\n /**\r\n * 自定义图标组件(如 @iconify/vue 的 Icon 组件)\r\n * 用于布局内部组件渲染菜单图标\r\n * 组件需接受 name (string) 和 size (number) 两个 props\r\n * 如果不提供,将使用 CSS class 渲染图标\r\n */\r\n iconComponent?: Component;\r\n}\r\n\r\n/**\r\n * 布局上下文 Injection Key\r\n */\r\nexport const LAYOUT_CONTEXT_KEY: InjectionKey<LayoutContext> =\r\n Symbol(\"layout-context\");\r\n\r\n/** 当前布局使用的设置 Store,支持自定义 Store id 与多实例。 */\nexport const LAYOUT_SETTINGS_KEY: InjectionKey<SettingsStoreInstance> =\n Symbol(\"layout-settings\");\n\n/** 侧边栏折叠控制器。 */\nexport interface MenuCollapseHandlers {\n isCollapsed: ComputedRef<boolean>;\n handleCollapsedChange: (collapsed: boolean) => void;\n}\n\nexport const MENU_COLLAPSE_KEY: InjectionKey<MenuCollapseHandlers> =\n Symbol(\"menu-collapse\");\n\n/**\r\n * 抽屉菜单统一控制器\r\n * 解决 MenuTrigger 与 DrawerMenu 各自拥有独立定时器导致的\r\n * \"鼠标从 trigger 移到 drawer 过程中 drawer 被关闭\"问题\r\n */\r\nexport interface DrawerHandlers {\r\n /** 显示抽屉并取消所有待执行的隐藏操作 */\r\n show: () => void;\r\n /** 启动延迟隐藏(300ms) */\r\n startHide: () => void;\r\n /** 立即隐藏抽屉(如导航跳转后) */\r\n hide: () => void;\r\n}\r\n\r\n/**\r\n * 抽屉菜单控制器 Injection Key\r\n */\r\nexport const DRAWER_HANDLER_KEY: InjectionKey<DrawerHandlers> =\r\n Symbol(\"drawer-handlers\");\r\n\r\n/**\r\n * 在骨架组件中获取布局上下文\r\n *\r\n * @throws 如果没有 provide LayoutContext 会抛出错误\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup>\r\n * // 在包的骨架组件内部使用\r\n * const ctx = useLayoutContext()\r\n * const menus = ctx.menus\r\n * const isDark = ctx.isDark\r\n * </script>\r\n * ```\r\n */\r\nexport function useLayoutContext(): LayoutContext {\r\n const ctx = inject(LAYOUT_CONTEXT_KEY);\r\n if (!ctx) {\r\n throw new Error(\r\n \"[Layout] LayoutContext not provided. \" +\r\n \"请在上层组件中使用 provide(LAYOUT_CONTEXT_KEY, ...) 注入布局数据。\",\r\n );\r\n }\r\n return ctx;\r\n}\r\n\r\n/** 在组件 setup 中提供布局上下文,避免消费方直接操作 InjectionKey。 */\nexport function provideLayoutContext(context: LayoutContext): LayoutContext {\n provide(LAYOUT_CONTEXT_KEY, context);\n return context;\n}\n\n/**\r\n * 默认品牌配置\r\n */\r\nexport const DEFAULT_BRAND_CONFIG: Required<LayoutBrandConfig> = {\r\n name: \"Admin\",\r\n subtitle: \"\",\r\n logoSrc: \"\",\r\n logoType: \"image\",\r\n logoSize: 36,\r\n homePath: \"/home\",\r\n copyright: \"\",\r\n};\r\n","/**\r\n * @robot-admin/layout\r\n *\r\n * 统一的 KeepAlive 缓存管理\r\n * 从主项目提取的通用 composable,零业务依赖\r\n */\r\n\r\nimport { onUnmounted, ref, watch } from \"vue\";\r\nimport { useRoute } from \"vue-router\";\r\nimport type { RouteLocationNormalizedLoaded } from \"vue-router\";\r\n\r\ninterface LayoutCacheDebugWindow extends Window {\r\n __clearCache__?: () => void;\r\n __removeCache__?: (name: string) => void;\r\n __getCachedViews__?: () => string[];\r\n}\r\n\r\n/** KeepAlive 缓存选项 */\r\nexport interface LayoutCacheOptions {\r\n /** 最大缓存数量,默认 20 */\r\n maxCacheCount?: number;\r\n /** 是否输出缓存调试日志,默认 false */\r\n enableDevLog?: boolean;\r\n /** 是否暴露调试方法到 window,默认 false */\r\n exposeToWindow?: boolean;\r\n}\r\n\r\n/** KeepAlive 仅接受具名组件,并由路由 meta.keepAlive 明确开启。 */\r\nexport function shouldCacheRoute(\r\n route: Pick<RouteLocationNormalizedLoaded, \"name\" | \"meta\">,\r\n): route is Pick<RouteLocationNormalizedLoaded, \"name\" | \"meta\"> & {\r\n name: string;\r\n} {\r\n return typeof route.name === \"string\" && route.meta.keepAlive === true;\r\n}\r\n\r\n/**\r\n * 统一的 KeepAlive 缓存管理 composable\r\n *\r\n * @param options - 缓存配置选项\r\n * @returns 缓存视图列表和管理方法\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup>\r\n * import { useLayoutCache } from '@robot-admin/layout/vue'\r\n *\r\n * const { cachedViews, maxCacheCount } = useLayoutCache({ maxCacheCount: 30 })\r\n * </script>\r\n *\r\n * <template>\r\n * <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\r\n * <RouterView />\r\n * </KeepAlive>\r\n * </template>\r\n * ```\r\n */\r\nexport function useLayoutCache(options: LayoutCacheOptions = {}) {\r\n const {\r\n maxCacheCount: maxCount = 20,\r\n enableDevLog = false,\r\n exposeToWindow: expose = false,\r\n } = options;\r\n\r\n const route = useRoute();\r\n const normalizedMaxCount =\r\n Number.isFinite(maxCount) && maxCount > 0 ? Math.floor(maxCount) : 20;\r\n const cachedViews = ref<string[]>([]);\r\n const maxCacheCount = ref(normalizedMaxCount);\r\n\r\n /** 添加缓存 */\r\n const addCache = (name: string) => {\r\n if (name && !cachedViews.value.includes(name)) {\r\n cachedViews.value.push(name);\r\n\r\n // 控制缓存数量\r\n if (cachedViews.value.length > maxCacheCount.value) {\r\n cachedViews.value.shift();\r\n }\r\n\r\n if (enableDevLog) {\r\n console.debug(\r\n `[KeepAlive] ✅ 缓存: ${name} (${cachedViews.value.length}/${maxCacheCount.value})`,\r\n );\r\n }\r\n }\r\n };\r\n\r\n /** 移除缓存 */\r\n const removeCache = (name: string) => {\r\n const index = cachedViews.value.indexOf(name);\r\n if (index > -1) {\r\n cachedViews.value.splice(index, 1);\r\n if (enableDevLog) {\r\n console.debug(`[KeepAlive] ❌ 移除: ${name}`);\r\n }\r\n }\r\n };\r\n\r\n /** 清空所有缓存 */\r\n const clearAllCache = () => {\r\n cachedViews.value = [];\r\n if (enableDevLog) {\r\n console.debug(\"[KeepAlive] 🗑️ 清空所有缓存\");\r\n }\r\n };\r\n\r\n // 暴露调试方法到 window\r\n const getCachedViews = () => cachedViews.value;\r\n if (expose && typeof window !== \"undefined\") {\r\n const debugWindow = window as LayoutCacheDebugWindow;\r\n debugWindow.__clearCache__ = clearAllCache;\r\n debugWindow.__removeCache__ = removeCache;\r\n debugWindow.__getCachedViews__ = getCachedViews;\r\n\r\n onUnmounted(() => {\r\n if (debugWindow.__clearCache__ === clearAllCache) {\r\n delete debugWindow.__clearCache__;\r\n }\r\n if (debugWindow.__removeCache__ === removeCache) {\r\n delete debugWindow.__removeCache__;\r\n }\r\n if (debugWindow.__getCachedViews__ === getCachedViews) {\r\n delete debugWindow.__getCachedViews__;\r\n }\r\n });\r\n }\r\n\r\n // 监听路由变化,动态管理缓存\r\n watch(\r\n () => [route.name, route.meta.keepAlive] as const,\r\n () => {\r\n if (shouldCacheRoute(route)) addCache(route.name);\r\n },\r\n { immediate: true },\r\n );\r\n\r\n return {\r\n cachedViews,\r\n maxCacheCount,\r\n addCache,\r\n removeCache,\r\n clearAllCache,\r\n };\r\n}\r\n","import {\r\n computed,\r\n nextTick,\r\n onMounted,\r\n onUnmounted,\r\n ref,\r\n toValue,\r\n watch,\r\n type MaybeRefOrGetter,\r\n} from \"vue\";\r\nimport type { MenuOptions } from \"../types/menu\";\r\n\r\n/** 响应式菜单测量选项。 */\r\nexport interface UseResponsiveMenuOptions {\r\n data: MaybeRefOrGetter<readonly MenuOptions[]>;\r\n /** 溢出菜单按钮和安全间距占用的宽度,默认 95px。 */\r\n overflowReserve?: number;\r\n /** ResizeObserver 触发后的防抖时间,默认 100ms。 */\r\n debounceMs?: number;\r\n}\r\n\r\n/** 按现有菜单字号与间距估算单项宽度。 */\r\nexport function estimateMenuItemWidth(item: MenuOptions): number {\r\n const title = item.meta?.title || item.label || item.name || \"\";\r\n let textWidth = 0;\r\n\r\n for (const character of title) {\r\n const code = character.charCodeAt(0);\r\n const isWide =\r\n (code >= 0x4e00 && code <= 0x9fff) ||\r\n (code >= 0x3400 && code <= 0x4dbf) ||\r\n (code >= 0x3000 && code <= 0x303f) ||\r\n (code >= 0xff00 && code <= 0xffef) ||\r\n (code >= 0x3040 && code <= 0x309f) ||\r\n (code >= 0x30a0 && code <= 0x30ff);\r\n textWidth += isWide ? 15 : 8;\r\n }\r\n\r\n const iconWidth = item.meta?.icon || item.icon ? 26 : 0;\r\n return textWidth + iconWidth + 36 + 8 + 8;\r\n}\r\n\r\n/** 计算指定容器内可直接展示的菜单项数量。 */\r\nexport function calculateVisibleMenuCount(\r\n items: readonly MenuOptions[],\r\n containerWidth: number,\r\n overflowReserve = 95,\r\n): number {\r\n if (items.length === 0 || containerWidth <= 0) return 0;\r\n\r\n const totalWidth = items.reduce(\r\n (total, item) => total + estimateMenuItemWidth(item),\r\n 0,\r\n );\r\n const safetyWidth = 15;\r\n if (totalWidth + safetyWidth <= containerWidth) return items.length;\r\n\r\n const availableWidth = Math.max(0, containerWidth - overflowReserve);\r\n let usedWidth = 0;\r\n let count = 0;\r\n\r\n for (const item of items) {\r\n const itemWidth = estimateMenuItemWidth(item);\r\n if (usedWidth + itemWidth > availableWidth) break;\r\n usedWidth += itemWidth;\r\n count += 1;\r\n }\r\n\r\n // 与既有交互一致:有菜单时至少保留一个直接可见项。\r\n return Math.max(count, 1);\r\n}\r\n\r\n/**\r\n * UI 框架无关的响应式菜单测量逻辑。\r\n * Naive UI 和 Element Plus 适配器只负责把 visible/hidden 数据渲染出来。\r\n */\r\nexport function useResponsiveMenu(options: UseResponsiveMenuOptions) {\r\n const normalizedDebounce =\r\n Number.isFinite(options.debounceMs) && (options.debounceMs ?? 0) >= 0\r\n ? Math.floor(options.debounceMs ?? 100)\r\n : 100;\r\n const normalizedReserve =\r\n Number.isFinite(options.overflowReserve) &&\r\n (options.overflowReserve ?? 0) >= 0\r\n ? (options.overflowReserve ?? 95)\r\n : 95;\r\n\r\n const containerRef = ref<HTMLElement>();\r\n const menuData = computed(() => [...toValue(options.data)]);\r\n const visibleItems = ref<MenuOptions[]>([]);\r\n const hiddenItems = ref<MenuOptions[]>([]);\r\n\r\n const refresh = () => {\r\n const containerWidth = containerRef.value?.offsetWidth ?? 0;\r\n if (containerWidth <= 0 || menuData.value.length === 0) {\r\n visibleItems.value = [];\r\n hiddenItems.value = [];\r\n return;\r\n }\r\n\r\n const count = calculateVisibleMenuCount(\r\n menuData.value,\r\n containerWidth,\r\n normalizedReserve,\r\n );\r\n visibleItems.value = menuData.value.slice(0, count);\r\n hiddenItems.value = menuData.value.slice(count);\r\n };\r\n\r\n let resizeTimer: ReturnType<typeof setTimeout> | undefined;\r\n const scheduleRefresh = () => {\r\n if (resizeTimer !== undefined) clearTimeout(resizeTimer);\r\n resizeTimer = setTimeout(refresh, normalizedDebounce);\r\n };\r\n\r\n let resizeObserver: ResizeObserver | undefined;\r\n onMounted(() => {\r\n void nextTick(refresh);\r\n if (containerRef.value && typeof ResizeObserver !== \"undefined\") {\r\n resizeObserver = new ResizeObserver(scheduleRefresh);\r\n resizeObserver.observe(containerRef.value);\r\n } else if (typeof window !== \"undefined\") {\r\n window.addEventListener(\"resize\", scheduleRefresh, { passive: true });\r\n }\r\n });\r\n\r\n onUnmounted(() => {\r\n resizeObserver?.disconnect();\r\n if (typeof window !== \"undefined\") {\r\n window.removeEventListener(\"resize\", scheduleRefresh);\r\n }\r\n if (resizeTimer !== undefined) clearTimeout(resizeTimer);\r\n });\r\n\r\n watch(\r\n () => toValue(options.data),\r\n () => void nextTick(refresh),\r\n { deep: true },\r\n );\r\n\r\n return {\r\n containerRef,\r\n hiddenItems,\r\n refresh,\r\n visibleItems,\r\n };\r\n}\r\n","/**\r\n * @robot-admin/layout\r\n *\r\n * 菜单拆分 Composable - 通用的一级/二级菜单拆分逻辑\r\n * 用于 Mix / MixTop / ReverseHorizontalMix 等需要菜单拆分的布局\r\n */\r\n\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n toValue,\r\n type ComputedRef,\r\n type Ref,\r\n type MaybeRefOrGetter,\r\n} from \"vue\";\r\nimport { useRoute, useRouter } from \"vue-router\";\r\nimport type { MenuOptions } from \"../types/menu\";\r\n\r\nexport interface UseMenuSplitOptions {\r\n /** 菜单数据(响应式) */\r\n menus: MaybeRefOrGetter<readonly MenuOptions[]>;\r\n /** 是否使用悬浮式二级菜单(C_MixLayout 的 toggle 模式) */\n floatingSecondMenu?: MaybeRefOrGetter<boolean>;\r\n}\r\n\r\nexport interface UseMenuSplitReturn {\r\n /** 当前激活的一级菜单路径 */\r\n activeFirstMenu: Ref<string>;\r\n /** 当前激活的一级菜单完整数据 */\r\n activeFirstMenuItem: ComputedRef<MenuOptions | undefined>;\r\n /** 当前一级菜单对应的二级菜单列表 */\r\n currentSecondMenus: ComputedRef<MenuOptions[]>;\r\n /** 悬浮的菜单项(C_MixLayout 使用) */\n hoveredMenuItem: Ref<MenuOptions | null>;\r\n /** 实际展示的菜单项(hovered 优先,fallback 到 active) */\r\n displayMenuItem: ComputedRef<MenuOptions | null>;\r\n /** 是否显示悬浮二级菜单(C_MixLayout 使用) */\n showSecondMenu: Ref<boolean>;\r\n /** 处理一级菜单点击 */\r\n handleFirstMenuClick: (item: MenuOptions) => void;\r\n /** 处理二级菜单点击 */\r\n handleSecondMenuClick: (item: MenuOptions) => void;\r\n /** 检查路由是否匹配菜单项 */\r\n isMenuItemActive: (menuPath: string | undefined) => boolean;\r\n}\r\n\r\n/** 判断当前路径是否等于菜单路径或位于其子路径下。 */\r\nexport function isPathSegmentPrefix(\r\n menuPath: string | undefined,\r\n currentPath: string,\r\n): boolean {\r\n if (!menuPath) return false;\r\n const menuSegments = menuPath.split(\"/\").filter(Boolean);\r\n const currentSegments = currentPath.split(\"/\").filter(Boolean);\r\n if (menuSegments.length === 0) return currentSegments.length === 0;\r\n return menuSegments.every(\r\n (segment, index) => currentSegments[index] === segment,\r\n );\r\n}\r\n\r\n/**\r\n * 菜单拆分 Composable\r\n *\r\n * 提供一级/二级菜单拆分状态管理、路由自动匹配、菜单点击处理。\r\n * 供 C_MixLayout / C_MixTopLayout / C_ReverseHorizontalMixLayout 共用。\n *\r\n * @example\r\n * ```ts\r\n * const menuSplit = useMenuSplit({\r\n * menus: ctx.menus,\r\n * floatingSecondMenu: computed(() => layoutMode.value === 'mix'),\r\n * })\r\n * ```\r\n */\r\nexport function useMenuSplit(options: UseMenuSplitOptions): UseMenuSplitReturn {\r\n const { menus, floatingSecondMenu = false } = options;\r\n const route = useRoute();\r\n const router = useRouter();\r\n\r\n const activeFirstMenu = ref<string>(\"\");\r\n const hoveredMenuItem = ref<MenuOptions | null>(null);\r\n const showSecondMenu = ref(false);\r\n\r\n /** 当前展示在悬浮面板上的菜单项(优先 hovered,fallback 到 active) */\r\n const displayMenuItem = computed<MenuOptions | null>(\r\n () => hoveredMenuItem.value || activeFirstMenuItem.value || null,\r\n );\r\n\r\n // ============ 路径匹配 ============\r\n\r\n const isMenuItemActive = (menuPath: string | undefined): boolean => {\r\n return isPathSegmentPrefix(menuPath, route.path);\r\n };\r\n\r\n // ============ 查找一级菜单 ============\r\n\r\n const findActiveTopMenu = (\r\n items: readonly MenuOptions[],\r\n ): MenuOptions | null => {\r\n for (const item of items) {\r\n if (isMenuItemActive(item.path)) return item;\r\n if (item.children?.length) {\r\n const found = findActiveTopMenu(item.children);\r\n if (found) return item;\r\n }\r\n }\r\n return null;\r\n };\r\n\r\n // ============ 计算属性 ============\r\n\r\n const activeFirstMenuItem = computed(() =>\r\n toValue(menus).find((item) => item.path === activeFirstMenu.value),\r\n );\r\n\r\n const currentSecondMenus = computed(\r\n () => activeFirstMenuItem.value?.children || [],\r\n );\r\n\r\n // ============ 交互处理 ============\r\n\r\n const handleFirstMenuClick = (item: MenuOptions) => {\r\n if (item.disabled) return;\r\n if (toValue(floatingSecondMenu)) {\r\n // C_MixLayout: 纯点击模式\n if (item.children && item.children.length > 0) {\r\n if (showSecondMenu.value && activeFirstMenu.value === item.path) {\r\n // 点击同一个已展开的菜单 → 关闭\r\n showSecondMenu.value = false;\r\n } else {\r\n // 点击另一个或未展开 → 展开/切换\r\n activeFirstMenu.value = item.path || \"\";\r\n showSecondMenu.value = true;\r\n }\r\n } else {\r\n // 没有子菜单 → 关闭二级面板并跳转\r\n activeFirstMenu.value = item.path || \"\";\r\n showSecondMenu.value = false;\r\n if (item.path) void router.push(item.path);\r\n }\r\n } else {\r\n // MixTop / Reverse: 直接切换\r\n if (!item.children || item.children.length === 0) {\r\n if (item.path) void router.push(item.path);\r\n }\r\n }\r\n };\r\n\r\n const handleSecondMenuClick = (item: MenuOptions) => {\r\n if (!item.disabled && item.path) void router.push(item.path);\r\n };\r\n\r\n // ============ 路由自动匹配 ============\r\n\r\n const updateActiveMenuByRoute = () => {\r\n const menuList = toValue(menus);\r\n const matchedFirstMenu = findActiveTopMenu(menuList);\r\n if (matchedFirstMenu) {\r\n activeFirstMenu.value = matchedFirstMenu.path || \"\";\r\n // 悬浮模式下,如果当前一级菜单有子菜单,自动展示二级菜单\r\n if (toValue(floatingSecondMenu) && matchedFirstMenu.children?.length) {\r\n showSecondMenu.value = true;\r\n }\r\n } else if (menuList.length > 0 && !activeFirstMenu.value) {\r\n activeFirstMenu.value = menuList[0].path || \"\";\r\n }\r\n };\r\n\r\n watch(\r\n [() => route.path, () => toValue(menus)],\r\n () => updateActiveMenuByRoute(),\r\n { immediate: true },\r\n );\r\n\r\n return {\r\n activeFirstMenu,\r\n activeFirstMenuItem,\r\n currentSecondMenus,\r\n hoveredMenuItem,\r\n /** 实际在悬浮面板上展示的菜单项(hovered 优先,fallback 到 active) */\r\n displayMenuItem,\r\n showSecondMenu,\r\n handleFirstMenuClick,\r\n handleSecondMenuClick,\r\n isMenuItemActive,\r\n };\r\n}\r\n","import { defineComponent, h, type Component } from \"vue\";\r\nimport { useLayoutContext } from \"./useLayoutContext\";\r\n\r\nconst FallbackLayoutIcon = defineComponent({\r\n name: \"FallbackLayoutIcon\",\r\n props: {\r\n name: String,\r\n size: { type: Number, default: 18 },\r\n },\r\n setup(props) {\r\n return () =>\r\n h(\"i\", {\r\n class: props.name,\r\n style: {\r\n alignItems: \"center\",\r\n display: \"inline-flex\",\r\n fontSize: `${props.size}px`,\r\n justifyContent: \"center\",\r\n },\r\n });\r\n },\r\n});\r\n\r\n/** 返回宿主图标组件;未配置时使用零依赖 CSS class 渲染器。 */\r\nexport function useLayoutIcon(): Component {\r\n return useLayoutContext().iconComponent ?? FallbackLayoutIcon;\r\n}\r\n","import type { SettingsState } from \"./types\";\r\n\r\nconst ENUM_SETTINGS = {\r\n themeMode: new Set([\"light\", \"dark\", \"system\"]),\r\n borderRadius: new Set([\"small\", \"medium\", \"large\"]),\r\n transitionType: new Set([\"fade\", \"slide\", \"zoom\", \"none\"]),\r\n layoutMode: new Set([\r\n \"side\",\r\n \"top\",\r\n \"mix\",\r\n \"mix-top\",\r\n \"reverse-horizontal-mix\",\r\n \"card-layout\",\r\n ]),\r\n menuExpandMode: new Set([\"inline\", \"panel\"]),\r\n tagsViewStyle: new Set([\"default\", \"card\", \"smart\"]),\r\n} as const;\r\n\r\nconst BOOLEAN_SETTINGS: ReadonlySet<keyof SettingsState> = new Set([\r\n \"enableTransition\",\r\n \"collapsed\",\r\n \"fixedHeader\",\r\n \"showBreadcrumb\",\r\n \"showBreadcrumbIcon\",\r\n \"showTagsView\",\r\n \"showFooter\",\r\n \"enableHotkeys\",\r\n]);\r\n\r\nconst NUMBER_RANGES: Partial<\r\n Record<keyof SettingsState, readonly [number, number]>\r\n> = {\r\n tagsViewHeight: [24, 100],\r\n sidebarWidth: [120, 480],\r\n sidebarCollapsedWidth: [32, 160],\r\n headerHeight: [32, 160],\r\n};\r\n\r\n/** 当前导入导出配置格式版本。 */\r\nexport const SETTINGS_CONFIG_SCHEMA_VERSION = 1 as const;\r\n\r\nexport interface LayoutVisualEffects {\r\n gray?: boolean;\r\n colorWeak?: boolean;\r\n watermark?: {\r\n enabled: boolean;\r\n text: string;\r\n };\r\n}\r\n\r\n/** 经校验后可原子应用的布局配置。 */\r\nexport interface LayoutSettingsConfig extends LayoutVisualEffects {\r\n schemaVersion?: number;\r\n settings?: Partial<SettingsState>;\r\n}\r\n\r\n/** 校验需要跨字段判断的布局尺寸约束。 */\r\nexport function assertLayoutSettingsRelationships(\r\n settings: Pick<SettingsState, \"sidebarCollapsedWidth\" | \"sidebarWidth\">,\r\n): void {\r\n if (settings.sidebarCollapsedWidth > settings.sidebarWidth) {\r\n throw new RangeError(\"sidebarCollapsedWidth 不能大于 sidebarWidth\");\r\n }\r\n}\r\n\r\n/**\r\n * 校验来自配置文件等不可信来源的设置。\r\n * 未知键为向前兼容而忽略;已知键的非法值会让整次导入失败。\r\n */\r\nexport function sanitizeSettingsPatch(input: unknown): Partial<SettingsState> {\r\n if (!input || typeof input !== \"object\" || Array.isArray(input)) {\r\n throw new TypeError(\"settings 必须是对象\");\r\n }\r\n\r\n const source = input as Record<string, unknown>;\r\n const result: Partial<SettingsState> = {};\r\n const assign = (key: keyof SettingsState, value: unknown) => {\r\n (result as Record<string, unknown>)[key] = value;\r\n };\r\n\r\n for (const [key, allowed] of Object.entries(ENUM_SETTINGS)) {\r\n const value = source[key];\r\n if (value === undefined) continue;\r\n if (typeof value !== \"string\" || !allowed.has(value as never)) {\r\n throw new RangeError(`无效的设置项 ${key}: ${String(value)}`);\r\n }\r\n assign(key as keyof SettingsState, value);\r\n }\r\n\r\n for (const key of BOOLEAN_SETTINGS) {\r\n const value = source[key];\r\n if (value === undefined) continue;\r\n if (typeof value !== \"boolean\") {\r\n throw new TypeError(`设置项 ${key} 必须是布尔值`);\r\n }\r\n assign(key, value);\r\n }\r\n\r\n for (const [key, range] of Object.entries(NUMBER_RANGES)) {\r\n const value = source[key];\r\n if (value === undefined) continue;\r\n if (\r\n typeof value !== \"number\" ||\r\n !Number.isFinite(value) ||\r\n value < range[0] ||\r\n value > range[1]\r\n ) {\r\n throw new RangeError(`设置项 ${key} 超出允许范围`);\r\n }\r\n assign(key as keyof SettingsState, value);\r\n }\r\n\r\n if (source.primaryColor !== undefined) {\r\n if (\r\n typeof source.primaryColor !== \"string\" ||\r\n !/^#[0-9a-fA-F]{6}$/.test(source.primaryColor)\r\n ) {\r\n throw new RangeError(\"primaryColor 必须是 #rrggbb 格式\");\r\n }\r\n result.primaryColor = source.primaryColor;\r\n }\r\n\r\n if (source.version !== undefined) {\r\n if (typeof source.version !== \"string\" || source.version.length > 50) {\r\n throw new TypeError(\"version 必须是长度不超过 50 的字符串\");\r\n }\r\n result.version = source.version;\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 一次性校验完整导入文件。此函数不修改任何响应式状态或浏览器 API,\r\n * 消费方可以在成功返回后再原子应用结果。\r\n */\r\nexport function sanitizeLayoutSettingsConfig(\r\n input: unknown,\r\n): LayoutSettingsConfig {\r\n if (!input || typeof input !== \"object\" || Array.isArray(input)) {\r\n throw new TypeError(\"配置文件根节点必须是对象\");\r\n }\r\n\r\n const source = input as Record<string, unknown>;\r\n const result: LayoutSettingsConfig = {};\r\n\r\n if (source.schemaVersion !== undefined) {\r\n if (\r\n typeof source.schemaVersion !== \"number\" ||\r\n !Number.isInteger(source.schemaVersion) ||\r\n source.schemaVersion < 1 ||\r\n source.schemaVersion > SETTINGS_CONFIG_SCHEMA_VERSION\r\n ) {\r\n throw new RangeError(\"不支持的配置格式版本\");\r\n }\r\n result.schemaVersion = source.schemaVersion;\r\n }\r\n\r\n if (source.settings !== undefined) {\r\n result.settings = sanitizeSettingsPatch(source.settings);\r\n }\r\n\r\n for (const key of [\"gray\", \"colorWeak\"] as const) {\r\n const value = source[key];\r\n if (value === undefined) continue;\r\n if (typeof value !== \"boolean\") {\r\n throw new TypeError(`${key} 必须是布尔值`);\r\n }\r\n result[key] = value;\r\n }\r\n\r\n if (source.watermark !== undefined) {\r\n if (\r\n !source.watermark ||\r\n typeof source.watermark !== \"object\" ||\r\n Array.isArray(source.watermark)\r\n ) {\r\n throw new TypeError(\"watermark 必须是对象\");\r\n }\r\n const watermark = source.watermark as Record<string, unknown>;\r\n if (\r\n typeof watermark.enabled !== \"boolean\" ||\r\n typeof watermark.text !== \"string\" ||\r\n watermark.text.length > 200\r\n ) {\r\n throw new TypeError(\"watermark 配置无效\");\r\n }\r\n result.watermark = {\r\n enabled: watermark.enabled,\r\n text: watermark.text,\r\n };\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 调整颜色亮度。\r\n * @returns 调整后的 #rrggbb;输入非法时原样返回。\r\n */\r\nexport function adjustColor(color: string, amount: number): string {\r\n if (typeof color !== \"string\") return \"#000000\";\r\n if (!Number.isFinite(amount)) return color;\r\n const normalizedAmount = Math.round(amount);\r\n const trimmedColor = color.trim();\r\n let hex = trimmedColor.startsWith(\"#\") ? trimmedColor.slice(1) : trimmedColor;\r\n if (hex.length === 3) {\r\n hex = hex\r\n .split(\"\")\r\n .map((character) => character + character)\r\n .join(\"\");\r\n }\r\n if (!/^[0-9a-fA-F]{6}$/.test(hex)) return color;\r\n\r\n const value = parseInt(hex, 16);\r\n const red = Math.min(255, Math.max(0, (value >> 16) + normalizedAmount));\r\n const green = Math.min(\r\n 255,\r\n Math.max(0, ((value >> 8) & 0x00ff) + normalizedAmount),\r\n );\r\n const blue = Math.min(\r\n 255,\r\n Math.max(0, (value & 0x0000ff) + normalizedAmount),\r\n );\r\n return `#${((red << 16) | (green << 8) | blue).toString(16).padStart(6, \"0\")}`;\r\n}\r\n","/**\r\n * 布局和设置系统 - 常量定义\r\n */\r\n\r\nimport type {\r\n ThemePreset,\r\n SettingsState,\r\n LayoutInfo,\r\n PresetColor,\r\n} from \"../core/types\";\r\n\r\n// ============ 主题色预设 ============\r\nexport const PRESET_COLORS: PresetColor[] = [\r\n { name: \"拂晓蓝\", value: \"#409eff\" },\r\n { name: \"薄暮红\", value: \"#f5222d\" },\r\n { name: \"火山橙\", value: \"#fa541c\" },\r\n { name: \"日暮黄\", value: \"#faad14\" },\r\n { name: \"极光绿\", value: \"#52c41a\" },\r\n { name: \"明青\", value: \"#13c2c2\" },\r\n { name: \"极客蓝\", value: \"#2f54eb\" },\r\n { name: \"酱紫\", value: \"#722ed1\" },\r\n] as const;\r\n\r\n// ============ 主题预设方案 ============\r\nexport const THEME_PRESETS: ThemePreset[] = [\r\n {\r\n name: \"科技蓝\",\r\n icon: \"💙\",\r\n primaryColor: \"#409eff\",\r\n },\r\n {\r\n name: \"清新绿\",\r\n icon: \"💚\",\r\n primaryColor: \"#52c41a\",\r\n },\r\n {\r\n name: \"商务灰\",\r\n icon: \"🖤\",\r\n primaryColor: \"#595959\",\r\n },\r\n {\r\n name: \"活力橙\",\r\n icon: \"🧡\",\r\n primaryColor: \"#fa8c16\",\r\n },\r\n {\r\n name: \"优雅紫\",\r\n icon: \"💜\",\r\n primaryColor: \"#722ed1\",\r\n },\r\n {\r\n name: \"经典红\",\r\n icon: \"❤️\",\r\n primaryColor: \"#f5222d\",\r\n },\r\n];\r\n\r\n// ============ 布局模式信息 ============\r\nexport const LAYOUT_MODES: LayoutInfo[] = [\r\n {\r\n mode: \"side\",\r\n label: \"左侧菜单\",\r\n description: \"经典的左侧导航布局\",\r\n icon: \"i-mdi:page-layout-sidebar-left\",\r\n },\r\n {\r\n mode: \"top\",\r\n label: \"顶部菜单\",\r\n description: \"顶部水平导航布局\",\r\n icon: \"i-mdi:page-layout-header\",\r\n },\r\n {\r\n mode: \"mix\",\r\n label: \"混合菜单\",\r\n description: \"顶部 + 侧边混合布局\",\r\n icon: \"i-mdi:view-dashboard-outline\",\r\n },\r\n {\r\n mode: \"mix-top\",\r\n label: \"顶部混合\",\r\n description: \"侧边优先的混合布局\",\r\n icon: \"i-mdi:view-dashboard-variant-outline\",\r\n },\r\n {\r\n mode: \"reverse-horizontal-mix\",\r\n label: \"反转混合\",\r\n description: \"反转的水平混合布局\",\r\n icon: \"i-mdi:page-layout-header-footer\",\r\n },\r\n {\r\n mode: \"card-layout\",\r\n label: \"卡片布局\",\r\n description: \"卡片式布局风格\",\r\n icon: \"i-mdi:card-outline\",\r\n },\r\n];\r\n\r\n// ============ 圆角映射 ============\r\nexport const BORDER_RADIUS_MAP = {\r\n small: \"4px\",\r\n medium: \"6px\",\r\n large: \"8px\",\r\n} as const;\r\n\r\n// ============ 动画映射 ============\r\nexport const TRANSITION_MAP = {\r\n fade: \"fade-transform\",\r\n slide: \"fade-slide\",\r\n zoom: \"fade-zoom\",\r\n none: \"\",\r\n} as const;\r\n\r\n// ============ 默认配置 ============\r\nexport const DEFAULT_SETTINGS: SettingsState = {\r\n // 外观\r\n themeMode: \"light\",\r\n primaryColor: \"#409eff\",\r\n borderRadius: \"medium\",\r\n transitionType: \"slide\",\r\n enableTransition: true,\r\n\r\n // 布局\r\n layoutMode: \"side\",\r\n menuExpandMode: \"inline\",\r\n collapsed: false,\r\n fixedHeader: true,\r\n showBreadcrumb: true,\r\n showBreadcrumbIcon: true,\r\n showTagsView: true,\r\n tagsViewHeight: 44,\r\n tagsViewStyle: \"default\",\r\n showFooter: true,\r\n sidebarWidth: 220,\r\n sidebarCollapsedWidth: 64,\r\n headerHeight: 56,\r\n\r\n // 高级\r\n enableHotkeys: true,\r\n version: \"3.2.1\",\n};\r\n\r\n// ============ 其他常量 ============\r\n\r\n/** Color swatches for color picker */\r\nexport const COLOR_SWATCHES = PRESET_COLORS.map((c) => c.value);\r\n","import {\r\n getCurrentScope,\r\n onScopeDispose,\r\n toValue,\r\n watch,\r\n type MaybeRefOrGetter,\r\n} from \"vue\";\r\nimport { adjustColor } from \"../core/settings\";\r\n\r\nexport interface LayoutCssVariableSource {\r\n primaryColor: MaybeRefOrGetter<string>;\r\n borderRadiusValue: MaybeRefOrGetter<string>;\r\n sidebarWidth: MaybeRefOrGetter<number>;\r\n sidebarCollapsedWidth: MaybeRefOrGetter<number>;\r\n headerHeight: MaybeRefOrGetter<number>;\r\n tagsViewHeight: MaybeRefOrGetter<number>;\r\n}\r\n\r\nexport interface LayoutStyleDeclaration {\r\n getPropertyValue(name: string): string;\r\n removeProperty(name: string): string;\r\n setProperty(name: string, value: string): void;\r\n}\r\n\r\nexport interface LayoutCssVariableTarget {\r\n style: LayoutStyleDeclaration;\r\n}\r\n\r\nexport interface LayoutCssVariableOptions {\r\n /** 变量写入目标;默认 document.documentElement。 */\r\n target?: MaybeRefOrGetter<LayoutCssVariableTarget | null | undefined>;\r\n /** 同时维护 3.x 无前缀变量,默认 false。 */\r\n legacyAliases?: boolean;\r\n}\r\n\r\nexport interface LayoutCssVariableBinding {\r\n dispose: () => void;\r\n sync: () => void;\r\n}\r\n\r\nconst getDefaultTarget = (): LayoutCssVariableTarget | undefined =>\r\n typeof document === \"undefined\" ? undefined : document.documentElement;\r\n\r\nfunction createVariableEntries(\r\n source: LayoutCssVariableSource,\r\n legacyAliases: boolean,\r\n): Array<readonly [string, string]> {\r\n const primaryColor = toValue(source.primaryColor);\r\n const values = {\r\n \"primary-color\": primaryColor,\r\n \"primary-color-hover\": adjustColor(primaryColor, 10),\r\n \"primary-color-pressed\": adjustColor(primaryColor, -10),\r\n \"border-radius\": toValue(source.borderRadiusValue),\r\n \"sidebar-width\": `${toValue(source.sidebarWidth)}px`,\r\n \"sidebar-collapsed-width\": `${toValue(source.sidebarCollapsedWidth)}px`,\r\n \"header-height\": `${toValue(source.headerHeight)}px`,\r\n \"tags-view-height\": `${toValue(source.tagsViewHeight)}px`,\r\n };\r\n\r\n return Object.entries(values).flatMap(([name, value]) => {\r\n const entries: Array<readonly [string, string]> = [\r\n [`--ra-layout-${name}`, value],\r\n ];\r\n if (legacyAliases) entries.push([`--${name}`, value]);\r\n return entries;\r\n });\r\n}\r\n\r\n/**\r\n * 将响应式设置绑定到指定样式作用域,并在 dispose 时精确恢复原值。\r\n */\r\nexport function bindLayoutCssVariables(\r\n source: LayoutCssVariableSource,\r\n options: LayoutCssVariableOptions = {},\r\n): LayoutCssVariableBinding {\r\n let activeTarget: LayoutCssVariableTarget | undefined;\r\n let originalValues = new Map<string, string>();\r\n let disposed = false;\r\n\r\n const restore = () => {\r\n if (!activeTarget) return;\r\n for (const [name, value] of originalValues) {\r\n if (value) activeTarget.style.setProperty(name, value);\r\n else activeTarget.style.removeProperty(name);\r\n }\r\n activeTarget = undefined;\r\n originalValues = new Map();\r\n };\r\n\r\n const sync = () => {\r\n if (disposed) return;\r\n const nextTarget = options.target\r\n ? (toValue(options.target) ?? undefined)\r\n : getDefaultTarget();\r\n if (!nextTarget) {\r\n restore();\r\n return;\r\n }\r\n\r\n const entries = createVariableEntries(\r\n source,\r\n options.legacyAliases ?? false,\r\n );\r\n if (activeTarget !== nextTarget) {\r\n restore();\r\n activeTarget = nextTarget;\r\n originalValues = new Map(\r\n entries.map(([name]) => [\r\n name,\r\n nextTarget.style.getPropertyValue(name),\r\n ]),\r\n );\r\n }\r\n for (const [name, value] of entries)\r\n nextTarget.style.setProperty(name, value);\r\n };\r\n\r\n const stop = watch(\r\n () => [\r\n toValue(source.primaryColor),\r\n toValue(source.borderRadiusValue),\r\n toValue(source.sidebarWidth),\r\n toValue(source.sidebarCollapsedWidth),\r\n toValue(source.headerHeight),\r\n toValue(source.tagsViewHeight),\r\n options.target ? toValue(options.target) : undefined,\r\n ],\r\n sync,\r\n { immediate: true },\r\n );\r\n\r\n const dispose = () => {\r\n if (disposed) return;\r\n disposed = true;\r\n stop();\r\n restore();\r\n };\r\n\r\n return { dispose, sync };\r\n}\r\n\r\n/** setup 作用域内使用的自动清理版本。 */\r\nexport function useLayoutCssVariables(\r\n source: LayoutCssVariableSource,\r\n options: LayoutCssVariableOptions = {},\r\n): LayoutCssVariableBinding {\r\n const binding = bindLayoutCssVariables(source, options);\r\n if (getCurrentScope()) onScopeDispose(binding.dispose);\r\n return binding;\r\n}\r\n","import { defineStore } from \"pinia\";\r\nimport { ref, computed, onScopeDispose } from \"vue\";\r\nimport type {\r\n SettingsState,\r\n ThemePreset,\r\n LayoutMode,\r\n MenuExpandMode,\r\n TransitionType,\r\n BorderRadiusSize,\r\n TagsViewStyle,\r\n SettingsStoreOptions,\r\n ThemeMode,\r\n} from \"../types\";\r\nimport {\r\n DEFAULT_SETTINGS,\r\n BORDER_RADIUS_MAP,\r\n TRANSITION_MAP,\r\n} from \"../constants\";\r\nexport { adjustColor, sanitizeSettingsPatch } from \"../core/settings\";\r\nimport {\r\n assertLayoutSettingsRelationships,\r\n sanitizeSettingsPatch,\r\n} from \"../core/settings\";\r\nimport { bindLayoutCssVariables } from \"../composables/useLayoutCssVariables\";\r\n\r\n/**\r\n * 创建设置管理 Store\r\n * @param options - 配置选项\r\n */\r\nexport function createSettingsStore(options: SettingsStoreOptions = {}) {\r\n const {\r\n id = \"settings\",\r\n defaults = {},\r\n onThemeModeChange,\r\n syncCssVariables = true,\r\n } = options;\r\n if (typeof id !== \"string\" || !id.trim()) {\r\n throw new RangeError(\"Settings store id 不能为空\");\r\n }\r\n\r\n // 合并默认配置\r\n const finalDefaults = {\r\n ...DEFAULT_SETTINGS,\r\n ...sanitizeSettingsPatch(defaults),\r\n };\r\n assertLayoutSettingsRelationships(finalDefaults);\r\n const resolvedDefaults = Object.freeze({ ...finalDefaults });\r\n\r\n return defineStore(id, () => {\r\n // ============ 状态定义 ============\r\n\r\n // 外观设置\r\n const themeMode = ref<ThemeMode>(finalDefaults.themeMode);\r\n const primaryColor = ref<string>(finalDefaults.primaryColor);\r\n const borderRadius = ref<BorderRadiusSize>(finalDefaults.borderRadius);\r\n const transitionType = ref<TransitionType>(finalDefaults.transitionType);\r\n const enableTransition = ref<boolean>(finalDefaults.enableTransition);\r\n\r\n // 布局设置\r\n const layoutMode = ref<LayoutMode>(finalDefaults.layoutMode);\r\n const menuExpandMode = ref<MenuExpandMode>(finalDefaults.menuExpandMode);\r\n const collapsed = ref<boolean>(finalDefaults.collapsed);\r\n const fixedHeader = ref<boolean>(finalDefaults.fixedHeader);\r\n const showBreadcrumb = ref<boolean>(finalDefaults.showBreadcrumb);\r\n const showBreadcrumbIcon = ref<boolean>(finalDefaults.showBreadcrumbIcon);\r\n const showTagsView = ref<boolean>(finalDefaults.showTagsView);\r\n const tagsViewHeight = ref<number>(finalDefaults.tagsViewHeight);\r\n const tagsViewStyle = ref<TagsViewStyle>(finalDefaults.tagsViewStyle);\r\n const showFooter = ref<boolean>(finalDefaults.showFooter);\r\n const sidebarWidth = ref<number>(finalDefaults.sidebarWidth);\r\n const sidebarCollapsedWidth = ref<number>(\r\n finalDefaults.sidebarCollapsedWidth,\r\n );\r\n const headerHeight = ref<number>(finalDefaults.headerHeight);\r\n\r\n // 高级设置\r\n const enableHotkeys = ref<boolean>(finalDefaults.enableHotkeys);\r\n const version = ref<string>(finalDefaults.version);\r\n\r\n // ============ 计算属性 ============\r\n\r\n /** 获取圆角值 */\r\n const borderRadiusValue = computed(\r\n () => BORDER_RADIUS_MAP[borderRadius.value],\r\n );\r\n\r\n /** 是否启用过渡动画 */\r\n const shouldEnableTransition = computed(\r\n () => enableTransition.value && transitionType.value !== \"none\",\r\n );\r\n\r\n /** 获取实际生效的过渡动画名称 */\r\n const transitionName = computed(() =>\r\n shouldEnableTransition.value ? TRANSITION_MAP[transitionType.value] : \"\",\r\n );\r\n\r\n /** 获取完整的设置状态 */\r\n const settingsState = computed<SettingsState>(() => ({\r\n themeMode: themeMode.value,\r\n primaryColor: primaryColor.value,\r\n borderRadius: borderRadius.value,\r\n transitionType: transitionType.value,\r\n enableTransition: enableTransition.value,\r\n layoutMode: layoutMode.value,\r\n menuExpandMode: menuExpandMode.value,\r\n collapsed: collapsed.value,\r\n fixedHeader: fixedHeader.value,\r\n showBreadcrumb: showBreadcrumb.value,\r\n showBreadcrumbIcon: showBreadcrumbIcon.value,\r\n showTagsView: showTagsView.value,\r\n tagsViewHeight: tagsViewHeight.value,\r\n tagsViewStyle: tagsViewStyle.value,\r\n showFooter: showFooter.value,\r\n sidebarWidth: sidebarWidth.value,\r\n sidebarCollapsedWidth: sidebarCollapsedWidth.value,\r\n headerHeight: headerHeight.value,\r\n enableHotkeys: enableHotkeys.value,\r\n version: version.value,\r\n }));\r\n\r\n // ============ 方法 ============\r\n\r\n const cssVariableBinding = syncCssVariables\r\n ? bindLayoutCssVariables(\r\n {\r\n primaryColor,\r\n borderRadiusValue,\r\n sidebarWidth,\r\n sidebarCollapsedWidth,\r\n headerHeight,\r\n tagsViewHeight,\r\n },\r\n { legacyAliases: true },\r\n )\r\n : undefined;\r\n if (cssVariableBinding) onScopeDispose(cssVariableBinding.dispose);\r\n\r\n /** 立即把当前设置同步到已配置的 CSS 变量作用域。 */\r\n const syncCSSVariables = () => cssVariableBinding?.sync();\r\n\r\n /**\r\n * 应用主题预设方案\r\n * 注意:只应用颜色配置,不改变布局模式和主题模式\r\n */\r\n const applyPreset = async (preset: ThemePreset) => {\r\n const safePreset = sanitizeSettingsPatch({\r\n primaryColor: preset.primaryColor,\r\n ...preset.settings,\r\n });\r\n\r\n // 完整校验成功后再修改状态,避免部分应用。\r\n primaryColor.value = safePreset.primaryColor!;\r\n\r\n // 应用其他细节配置(如果存在)\r\n if (safePreset.borderRadius) borderRadius.value = safePreset.borderRadius;\r\n if (safePreset.transitionType)\r\n transitionType.value = safePreset.transitionType;\r\n if (safePreset.showBreadcrumb !== undefined)\r\n showBreadcrumb.value = safePreset.showBreadcrumb;\r\n if (safePreset.showTagsView !== undefined)\r\n showTagsView.value = safePreset.showTagsView;\r\n if (safePreset.tagsViewStyle) {\r\n tagsViewStyle.value = safePreset.tagsViewStyle;\r\n }\r\n };\r\n\r\n /**\r\n * 重置配置\r\n */\r\n const resetSettings = () => {\r\n themeMode.value = finalDefaults.themeMode;\r\n primaryColor.value = finalDefaults.primaryColor;\r\n borderRadius.value = finalDefaults.borderRadius;\r\n transitionType.value = finalDefaults.transitionType;\r\n enableTransition.value = finalDefaults.enableTransition;\r\n\r\n layoutMode.value = finalDefaults.layoutMode;\r\n menuExpandMode.value = finalDefaults.menuExpandMode;\r\n collapsed.value = finalDefaults.collapsed;\r\n fixedHeader.value = finalDefaults.fixedHeader;\r\n showBreadcrumb.value = finalDefaults.showBreadcrumb;\r\n showBreadcrumbIcon.value = finalDefaults.showBreadcrumbIcon;\r\n showTagsView.value = finalDefaults.showTagsView;\r\n tagsViewHeight.value = finalDefaults.tagsViewHeight;\r\n tagsViewStyle.value = finalDefaults.tagsViewStyle;\r\n showFooter.value = finalDefaults.showFooter;\r\n sidebarWidth.value = finalDefaults.sidebarWidth;\r\n sidebarCollapsedWidth.value = finalDefaults.sidebarCollapsedWidth;\r\n headerHeight.value = finalDefaults.headerHeight;\r\n\r\n enableHotkeys.value = finalDefaults.enableHotkeys;\r\n version.value = finalDefaults.version;\r\n };\r\n\r\n /**\r\n * 更新主题模式\r\n */\r\n const updateThemeMode = async (mode: ThemeMode) => {\r\n sanitizeSettingsPatch({ themeMode: mode });\r\n const previousMode = themeMode.value;\r\n themeMode.value = mode;\r\n try {\r\n if (onThemeModeChange) {\r\n await onThemeModeChange(mode);\r\n }\r\n } catch (error) {\r\n themeMode.value = previousMode;\r\n throw error;\r\n }\r\n };\r\n\r\n /**\r\n * 切换侧边栏折叠状态\r\n */\r\n const toggleCollapse = () => {\r\n collapsed.value = !collapsed.value;\r\n };\r\n\r\n // ============ 返回 ============\r\n\r\n return {\r\n /** 当前 Store 实例实际使用的默认值。 */\r\n defaults: resolvedDefaults,\r\n\r\n // 状态\r\n themeMode,\r\n primaryColor,\r\n borderRadius,\r\n transitionType,\r\n enableTransition,\r\n layoutMode,\r\n menuExpandMode,\r\n collapsed,\r\n fixedHeader,\r\n showBreadcrumb,\r\n showBreadcrumbIcon,\r\n showTagsView,\r\n tagsViewHeight,\r\n tagsViewStyle,\r\n showFooter,\r\n sidebarWidth,\r\n sidebarCollapsedWidth,\r\n headerHeight,\r\n enableHotkeys,\r\n version,\r\n\r\n // 计算属性\r\n borderRadiusValue,\r\n transitionName,\r\n shouldEnableTransition,\r\n settingsState,\r\n\r\n // 方法\r\n syncCSSVariables,\r\n applyPreset,\r\n resetSettings,\r\n updateThemeMode,\r\n toggleCollapse,\r\n };\r\n });\r\n}\r\n\r\n/**\r\n * 默认的设置 Store(使用默认配置)\r\n */\r\nexport const useSettingsStore = createSettingsStore();\r\n\r\n/** 默认设置 Store 实例类型,供组件注入或显式传入。 */\r\nexport type SettingsStoreInstance = ReturnType<typeof useSettingsStore>;\r\n","/**\r\n * @robot-admin/layout\r\n *\r\n * 布局系统一键初始化\r\n */\r\n\r\nimport type { App } from \"vue\";\r\nimport type { SettingsStoreOptions } from \"./types\";\r\nimport { createSettingsStore } from \"./stores/settings\";\r\nimport { LAYOUT_SETTINGS_KEY } from \"./composables/useLayoutContext\";\r\n\r\n/**\r\n * 初始化布局系统\r\n *\r\n * @example\r\n * ```ts\r\n * import { setupLayout } from '@robot-admin/layout/vue'\r\n * setupLayout(app, {\r\n * onThemeModeChange: async (mode) => {\r\n * const themeStore = useThemeStore()\r\n * await themeStore.setMode(mode)\r\n * }\r\n * })\r\n * ```\r\n */\r\nexport function setupLayout(app: App, options: SettingsStoreOptions = {}) {\r\n const settingsStore = createSettingsStore(options);\r\n const settings = settingsStore();\r\n\r\n // 让所有内置组件读取同一个实例;Store 内的 immediate watcher 负责同步 CSS 变量。\r\n app.provide(LAYOUT_SETTINGS_KEY, settings);\r\n\r\n return settingsStore;\r\n}\r\n","import type { MenuMeta, MenuOptions } from \"../types/menu\";\r\n\r\n/** 布局内置导航可以安全消费的标准菜单项。 */\r\nexport type LayoutMenuItem<TMeta extends MenuMeta = MenuMeta> = Omit<\r\n MenuOptions<TMeta>,\r\n \"children\" | \"key\" | \"meta\" | \"path\"\r\n> & {\r\n key: string;\r\n path: string;\r\n meta: TMeta & { title: string };\r\n children?: LayoutMenuItem<TMeta>[];\r\n};\r\n\r\n/**\r\n * 非破坏地统一 path/key、label/meta.title 与字符串图标来源。\r\n * 缺少稳定路径或 key 的节点不会进入导航,避免意外跳转到根路由。\r\n */\r\nexport function normalizeLayoutMenus<TMeta extends MenuMeta = MenuMeta>(\r\n menus: readonly MenuOptions<TMeta>[],\r\n): LayoutMenuItem<TMeta>[] {\r\n return menus.flatMap((item) => {\r\n const path = item.path || item.key;\r\n if (!path) return [];\r\n\r\n const title = item.meta?.title || item.label || item.name || path;\r\n const stringIcon =\r\n item.meta?.icon ??\r\n (typeof item.icon === \"string\" ? item.icon : undefined);\r\n const meta = {\r\n ...item.meta,\r\n title,\r\n ...(stringIcon ? { icon: stringIcon } : {}),\r\n } as TMeta & { title: string };\r\n\r\n return [\r\n {\r\n ...item,\r\n key: item.key || path,\r\n path,\r\n meta,\r\n children: item.children\r\n ? normalizeLayoutMenus<TMeta>(item.children)\r\n : undefined,\r\n },\r\n ];\r\n });\r\n}\r\n","import { computed, toValue, type Component, type MaybeRefOrGetter } from \"vue\";\r\nimport type { MenuOptions } from \"../types/menu\";\r\nimport type { LayoutMode, MenuExpandMode } from \"../core/types\";\r\nimport {\r\n DEFAULT_BRAND_CONFIG,\r\n provideLayoutContext,\r\n type LayoutBrandConfig,\r\n type LayoutContext,\r\n} from \"./useLayoutContext\";\r\n\r\n/**\r\n * 布局桥接所需的最小设置协议。\r\n *\r\n * 默认 Pinia Store 天然满足该协议,宿主也可以传入其他响应式 Store,\r\n * 避免上下文助手与具体状态管理实现强绑定。\r\n */\r\nexport interface LayoutSettingsSource {\r\n layoutMode: LayoutMode;\r\n collapsed: boolean;\r\n menuExpandMode: MenuExpandMode;\r\n sidebarWidth: number;\r\n sidebarCollapsedWidth: number;\r\n showFooter: boolean;\r\n showTagsView: boolean;\r\n tagsViewHeight: number;\r\n headerHeight: number;\r\n transitionName: string;\r\n showBreadcrumb: boolean;\r\n showBreadcrumbIcon: boolean;\r\n fixedHeader: boolean;\r\n}\r\n\r\n/** 创建标准布局上下文所需的最小宿主适配。 */\r\nexport interface CreateLayoutContextOptions {\r\n /** 布局设置 Store,可使用默认 Store 或 createSettingsStore() 创建的实例。 */\r\n settings: LayoutSettingsSource;\r\n /** 宿主菜单;支持普通值、Ref、ComputedRef 或 getter。 */\r\n menus: MaybeRefOrGetter<MenuOptions[]>;\r\n /** 宿主暗色状态;支持普通值、Ref、ComputedRef 或 getter。 */\r\n isDark: MaybeRefOrGetter<boolean>;\r\n /** 品牌信息,未提供的字段使用包内安全默认值。 */\r\n brand?: LayoutBrandConfig;\r\n /** 布局内部用于渲染菜单图标的宿主组件。 */\r\n iconComponent?: Component;\r\n}\r\n\r\n/**\r\n * 将最小宿主输入转换为完整 LayoutContext。\r\n *\r\n * 保留底层 LayoutContext 作为高级扩展协议,同时避免普通接入重复桥接 Store 字段。\r\n */\r\nexport function createLayoutContext(\r\n options: CreateLayoutContextOptions,\r\n): LayoutContext {\r\n const { settings } = options;\r\n\r\n return {\r\n menus: computed(() => toValue(options.menus)),\r\n isDark: computed(() => toValue(options.isDark)),\r\n layoutMode: computed(() => settings.layoutMode),\r\n collapsed: computed({\r\n get: () => settings.collapsed,\r\n set: (value: boolean) => {\r\n settings.collapsed = value;\r\n },\r\n }),\r\n menuExpandMode: computed(() => settings.menuExpandMode),\r\n sidebarWidth: computed(() => settings.sidebarWidth),\r\n sidebarCollapsedWidth: computed(() => settings.sidebarCollapsedWidth),\r\n showFooter: computed(() => settings.showFooter),\r\n showTagsView: computed(() => settings.showTagsView),\r\n tagsViewHeight: computed(() => settings.tagsViewHeight),\r\n headerHeight: computed(() => settings.headerHeight),\r\n transitionName: computed(() => settings.transitionName),\r\n showBreadcrumb: computed(() => settings.showBreadcrumb),\r\n showBreadcrumbIcon: computed(() => settings.showBreadcrumbIcon),\r\n fixedHeader: computed(() => settings.fixedHeader),\r\n brand: { ...DEFAULT_BRAND_CONFIG, ...options.brand },\r\n iconComponent: options.iconComponent,\r\n };\r\n}\r\n\r\n/** 在组件 setup 中创建并提供标准布局上下文。 */\r\nexport function provideLayout(\r\n options: CreateLayoutContextOptions,\r\n): LayoutContext {\r\n return provideLayoutContext(createLayoutContext(options));\r\n}\r\n","import { computed, getCurrentScope, onScopeDispose, ref, watch } from \"vue\";\r\nimport {\r\n assertLayoutSettingsRelationships,\r\n sanitizeLayoutSettingsConfig,\r\n SETTINGS_CONFIG_SCHEMA_VERSION,\r\n type LayoutSettingsConfig,\r\n} from \"../core/settings\";\r\nimport type { SettingsDrawerActions, ThemeMode, ThemePreset } from \"../types\";\r\nimport type { SettingsStoreInstance } from \"../stores/settings\";\r\n\r\n/** 宿主没有实现可选高副作用操作。 */\r\nexport class LayoutActionUnavailableError extends Error {\r\n constructor(action: keyof SettingsDrawerActions) {\r\n super(`[Layout] Host action \"${action}\" is not configured.`);\r\n this.name = \"LayoutActionUnavailableError\";\r\n }\r\n}\r\n\r\nexport interface SettingsControllerOptions {\r\n store: SettingsStoreInstance;\r\n actions?: SettingsDrawerActions;\r\n maxImportBytes?: number;\r\n /** 灰度/色弱 class 的作用元素;默认 document.documentElement。 */\r\n visualRoot?: HTMLElement | null;\r\n /** 水印挂载容器;默认 document.body。 */\r\n watermarkContainer?: HTMLElement | null;\r\n watermarkText?: string;\r\n onVisualEffectError?: (message: string) => void;\r\n}\r\n\r\nexport interface ExportedLayoutSettings extends LayoutSettingsConfig {\r\n schemaVersion: typeof SETTINGS_CONFIG_SCHEMA_VERSION;\r\n exportTime: string;\r\n}\r\n\r\nconst getDefaultDocument = (): Document | undefined =>\r\n typeof document === \"undefined\" ? undefined : document;\r\n\r\n/**\r\n * SettingsDrawer 的 UI 框架无关控制器。\n *\n * 负责配置事务、导入导出和页面级视觉效果;Naive UI / Element Plus\n * 适配器只处理组件渲染、消息和确认框。\n * 在 Vue setup/effect scope 内会自动清理;在作用域外创建时应调用返回值的 dispose()。\n */\nexport function useSettingsController(options: SettingsControllerOptions) {\r\n const { store } = options;\r\n const ownerDocument =\r\n options.visualRoot?.ownerDocument ??\r\n options.watermarkContainer?.ownerDocument ??\r\n getDefaultDocument();\r\n const visualRoot = options.visualRoot ?? ownerDocument?.documentElement;\r\n const watermarkContainer = options.watermarkContainer ?? ownerDocument?.body;\r\n const maxImportBytes =\r\n Number.isFinite(options.maxImportBytes) && (options.maxImportBytes ?? 0) > 0\r\n ? Math.floor(options.maxImportBytes ?? 1024 * 1024)\r\n : 1024 * 1024;\r\n\r\n const initialGrayMode = visualRoot?.classList.contains(\"gray-mode\") ?? false;\r\n const initialColorWeakMode =\r\n visualRoot?.classList.contains(\"color-weak-mode\") ?? false;\r\n const grayMode = ref(initialGrayMode);\r\n const colorWeakMode = ref(initialColorWeakMode);\r\n const watermarkEnabled = ref(false);\r\n const watermarkText = ref(options.watermarkText ?? \"Robot Admin\");\r\n let watermarkElement: HTMLElement | undefined;\r\n\r\n const removeWatermark = () => {\r\n watermarkElement?.remove();\r\n watermarkElement = undefined;\r\n };\r\n\r\n const createWatermark = (text: string) => {\r\n removeWatermark();\r\n if (!ownerDocument || !watermarkContainer) return;\r\n\r\n const canvas = ownerDocument.createElement(\"canvas\");\r\n const context = canvas.getContext(\"2d\");\r\n if (!context) {\r\n options.onVisualEffectError?.(\"当前浏览器无法创建水印画布\");\r\n return;\r\n }\r\n\r\n canvas.width = 200;\r\n canvas.height = 150;\r\n context.font = \"14px Microsoft JhengHei\";\r\n context.fillStyle = \"rgba(120, 120, 120, 0.15)\";\r\n context.rotate((-20 * Math.PI) / 180);\r\n context.fillText(text, 0, 80);\r\n\r\n watermarkElement = ownerDocument.createElement(\"div\");\r\n watermarkElement.dataset.robotAdminLayoutWatermark = \"true\";\r\n Object.assign(watermarkElement.style, {\r\n backgroundImage: `url(${canvas.toDataURL()})`,\r\n backgroundRepeat: \"repeat\",\r\n height: \"100%\",\r\n left: \"0\",\r\n pointerEvents: \"none\",\r\n position: \"fixed\",\r\n top: \"0\",\r\n userSelect: \"none\",\r\n width: \"100%\",\r\n zIndex: \"9999\",\r\n });\r\n watermarkContainer.appendChild(watermarkElement);\r\n };\r\n\r\n const stopGrayMode = watch(grayMode, (value) =>\r\n visualRoot?.classList.toggle(\"gray-mode\", value),\r\n );\r\n const stopColorWeakMode = watch(colorWeakMode, (value) =>\r\n visualRoot?.classList.toggle(\"color-weak-mode\", value),\r\n );\r\n const stopWatermarkEnabled = watch(watermarkEnabled, (value) => {\r\n if (value) createWatermark(watermarkText.value);\r\n else removeWatermark();\r\n });\r\n const stopWatermarkText = watch(watermarkText, (value) => {\r\n if (watermarkEnabled.value) createWatermark(value);\r\n });\r\n\r\n const systemInfo = computed(() => {\r\n const view = ownerDocument?.defaultView;\r\n const navigatorInfo = view?.navigator;\r\n if (!view || !navigatorInfo) {\r\n return {\r\n browser: \"Unknown\",\r\n os: \"Unknown\",\r\n resolution: \"Unknown\",\r\n pixelRatio: \"Unknown\",\r\n language: \"Unknown\",\r\n timezone: \"Unknown\",\r\n };\r\n }\r\n\r\n const userAgent = navigatorInfo.userAgent;\r\n let browser = \"Unknown\";\r\n let os = \"Unknown\";\r\n if (userAgent.includes(\"Edg/\")) browser = \"Edge\";\r\n else if (userAgent.includes(\"Firefox\")) browser = \"Firefox\";\r\n else if (userAgent.includes(\"Chrome\")) browser = \"Chrome\";\r\n else if (userAgent.includes(\"Safari\")) browser = \"Safari\";\r\n\r\n if (userAgent.includes(\"Windows\")) os = \"Windows\";\r\n else if (userAgent.includes(\"Mac\")) os = \"macOS\";\r\n else if (userAgent.includes(\"Android\")) os = \"Android\";\r\n else if (userAgent.includes(\"iOS\")) os = \"iOS\";\r\n else if (userAgent.includes(\"Linux\")) os = \"Linux\";\r\n\r\n return {\r\n browser,\r\n os,\r\n resolution: `${view.screen.width} × ${view.screen.height}`,\r\n pixelRatio: `${view.devicePixelRatio}x`,\r\n language: navigatorInfo.language,\r\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\n };\r\n });\r\n\r\n const applyPreset = (preset: ThemePreset) => store.applyPreset(preset);\r\n const updateThemeMode = (mode: ThemeMode) => store.updateThemeMode(mode);\r\n\r\n const resetAppearance = async () => {\r\n await store.updateThemeMode(store.defaults.themeMode);\r\n store.primaryColor = store.defaults.primaryColor;\r\n store.borderRadius = store.defaults.borderRadius;\r\n store.transitionType = store.defaults.transitionType;\r\n store.enableTransition = store.defaults.enableTransition;\r\n };\r\n\r\n const resetLayout = () => {\r\n store.layoutMode = store.defaults.layoutMode;\r\n store.menuExpandMode = store.defaults.menuExpandMode;\r\n store.fixedHeader = store.defaults.fixedHeader;\r\n store.showBreadcrumb = store.defaults.showBreadcrumb;\r\n store.showBreadcrumbIcon = store.defaults.showBreadcrumbIcon;\r\n store.showTagsView = store.defaults.showTagsView;\r\n store.tagsViewHeight = store.defaults.tagsViewHeight;\r\n store.tagsViewStyle = store.defaults.tagsViewStyle;\r\n store.showFooter = store.defaults.showFooter;\r\n store.sidebarWidth = store.defaults.sidebarWidth;\r\n store.sidebarCollapsedWidth = store.defaults.sidebarCollapsedWidth;\r\n store.headerHeight = store.defaults.headerHeight;\r\n };\r\n\r\n const resetAll = async () => {\r\n const previousSettings = { ...store.settingsState };\r\n try {\r\n store.resetSettings();\r\n await store.updateThemeMode(store.themeMode);\r\n } catch (error) {\r\n store.$patch(previousSettings);\r\n throw error;\r\n }\r\n };\r\n\r\n const clearCache = async () => {\r\n if (!options.actions?.clearCache) {\r\n throw new LayoutActionUnavailableError(\"clearCache\");\r\n }\r\n await options.actions.clearCache();\r\n };\r\n\r\n const reloadPage = () => {\r\n if (options.actions?.reloadPage) options.actions.reloadPage();\r\n else ownerDocument?.defaultView?.location.reload();\r\n };\r\n\r\n const createExportConfig = (): ExportedLayoutSettings => ({\r\n schemaVersion: SETTINGS_CONFIG_SCHEMA_VERSION,\r\n settings: store.settingsState,\r\n gray: grayMode.value,\r\n colorWeak: colorWeakMode.value,\r\n watermark: {\r\n enabled: watermarkEnabled.value,\r\n text: watermarkText.value,\r\n },\r\n exportTime: new Date().toISOString(),\r\n });\r\n\r\n const downloadConfig = () => {\r\n if (\r\n !ownerDocument ||\r\n typeof Blob === \"undefined\" ||\r\n typeof URL === \"undefined\" ||\r\n typeof URL.createObjectURL !== \"function\"\r\n ) {\r\n return false;\r\n }\r\n const blob = new Blob([JSON.stringify(createExportConfig(), null, 2)], {\r\n type: \"application/json\",\r\n });\r\n const url = URL.createObjectURL(blob);\r\n const anchor = ownerDocument.createElement(\"a\");\r\n anchor.href = url;\r\n anchor.download = `robot-admin-config-${Date.now()}.json`;\r\n anchor.click();\r\n URL.revokeObjectURL(url);\r\n return true;\r\n };\r\n\r\n const applyImportedConfig = async (input: unknown) => {\r\n const imported = sanitizeLayoutSettingsConfig(input);\r\n assertLayoutSettingsRelationships({\r\n ...store.settingsState,\r\n ...imported.settings,\r\n });\r\n if (imported.settings !== undefined) {\r\n const { themeMode, ...settingsPatch } = imported.settings;\r\n if (themeMode !== undefined) await store.updateThemeMode(themeMode);\r\n store.$patch(settingsPatch);\r\n }\r\n if (imported.gray !== undefined) grayMode.value = imported.gray;\r\n if (imported.colorWeak !== undefined)\r\n colorWeakMode.value = imported.colorWeak;\r\n if (imported.watermark !== undefined) {\r\n watermarkEnabled.value = imported.watermark.enabled;\r\n watermarkText.value = imported.watermark.text;\r\n }\r\n return imported;\r\n };\r\n\r\n const importFile = async (file: Pick<File, \"size\" | \"text\">) => {\r\n if (file.size > maxImportBytes) throw new RangeError(\"配置文件过大\");\r\n return applyImportedConfig(JSON.parse(await file.text()));\r\n };\r\n\r\n let disposed = false;\r\n const dispose = () => {\r\n if (disposed) return;\r\n disposed = true;\r\n stopGrayMode();\r\n stopColorWeakMode();\r\n stopWatermarkEnabled();\r\n stopWatermarkText();\r\n removeWatermark();\r\n visualRoot?.classList.toggle(\"gray-mode\", initialGrayMode);\r\n visualRoot?.classList.toggle(\"color-weak-mode\", initialColorWeakMode);\r\n };\r\n if (getCurrentScope()) onScopeDispose(dispose);\r\n\r\n return {\r\n applyImportedConfig,\r\n applyPreset,\r\n clearCache,\r\n colorWeakMode,\r\n createExportConfig,\r\n dispose,\r\n downloadConfig,\r\n grayMode,\r\n importFile,\r\n reloadPage,\r\n resetAll,\r\n resetAppearance,\r\n resetLayout,\r\n systemInfo,\r\n updateThemeMode,\r\n watermarkEnabled,\r\n watermarkText,\r\n };\r\n}\r\n"],"mappings":"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"}
|