@dazhicheng/hooks 1.4.50 → 1.4.52

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/dist/index.d.ts CHANGED
@@ -6,3 +6,4 @@ export { useIsScroll } from "./useIsScroll";
6
6
  export { useDebounceFn } from "./useDebounce";
7
7
  export { useElementScreenshot, type ComponentCaptureOptions, type ComponentCaptureOutput, type ElementScreenshotOptions, type ScreenshotTarget, } from "./useElementScreenshot";
8
8
  export { useVersionCheck, type UseVersionCheckOptions, type UseVersionCheckReturn } from "./useVersionCheck";
9
+ export { createUseAuth, resolveAuthMark, buildAuthListFromOriginMenu, findAuthListFromMenuTree, resolveBackendAuthList, type AuthItem, type CreateUseAuthOptions, type UseAuthReturn, } from "./useAuth";
package/dist/index.esm.js CHANGED
@@ -1,2 +1,2 @@
1
- import t,{toType as e}from"vue-types/shim";import{kebabToCamelCase as n,getFirstNonNullOrUndefined as o,debounce as r,isNumber as a}from"@dazhicheng/utils";import{getCurrentInstance as i,useSlots as u,useAttrs as l,computed as s,toValue as c,ref as f,render as p,nextTick as d,isVNode as h,h as w,readonly as m}from"vue";import{toBlob as v,toJpeg as y,toPng as g}from"html-to-image";import{ElNotification as C}from"element-plus";class E extends t{static get style(){return e("style",{type:[String,Object],default:void 0})}static get VNodeChild(){return e("VNodeChild",{default:void 0})}}function b(t){const e="tt";return{prefixCls:`${e}-${t}`,prefixVar:e}}function x(t,e,r){const a=i(),c=u(),f=l();return s(()=>{const i=a?.vnode?.props||{},u={};for(const[t,e]of Object.entries(i))u[n(t)]=e;const l=void 0===u?.[t]?void 0:e[t];return o(c[t],f[t],l,r?.value?.[t])})}function S(t,e){const n={};return Object.keys(t).forEach(o=>{n[o]=x(o,t,e)}),n}function V(t){const e={};for(const[n,o]of Object.entries(t))e[n]=c(o);return e}function $(t,e){return t in e}function k(t,e=new Map){if(null!==t&&"object"==typeof t){let n=e.get(t);return n||(Array.isArray(t)?(n=[],e.set(t,n),t.forEach((t,o)=>{n[o]=k(t,e)})):(n={},e.set(t,n),Object.keys(t).forEach(o=>{$(o,t)&&(n[o]=k(t[o],e))})),n)}return t}function A(...t){for(const e of t)if(null!=e)return e}function j(t){return t.split("-").filter(Boolean).map((t,e)=>0===e?t:t.charAt(0).toUpperCase()+t.slice(1)).join("")}function M(){const t=f(!1);return{hasScrollbar:t,update:e=>{if(!e.value)return;const n=e.value;t.value=n.scrollHeight>n.clientHeight}}}function R(t,e,n){const o=r(t,e??200,n);return{run:o,cancel:o.cancel,flush:o.flush}}function B(t){const e=c(t);if(!e)throw new Error("Target element not found");return e}async function F(t=16){await d(),await new Promise(t=>requestAnimationFrame(()=>t())),await new Promise(t=>requestAnimationFrame(()=>t())),t>0&&await new Promise(e=>setTimeout(e,t))}function O(t,e){t.style.cssText=e?.containerStyle??"position:fixed;left:-10000px;top:0;pointer-events:none;z-index:-1;",e?.containerStyle&&/width\s*:/i.test(e.containerStyle)||(t.style.width=function(t){const e=t?.width??document.documentElement.clientWidth;if(a(t?.width)&&e>0)return`${Math.round(e)}px`;const n=document.documentElement.clientWidth||window.innerWidth;return`${Math.round(n)}px`}(e))}function U(){const t=f(!1),e=f(null),n=i()?.appContext??null;async function o(t,e){await async function(t){t||"undefined"!=typeof document&&"fonts"in document&&await document.fonts.ready}(e?.skipFontsReady);const n=function(t){return{pixelRatio:t?.pixelRatio??Math.min(window.devicePixelRatio||2,3),backgroundColor:t?.backgroundColor??"#ffffff",cacheBust:t?.cacheBust??!0,quality:t?.quality??1,skipFontsReady:t?.skipFontsReady??!1,filter:t?.filter}}(e);if("blob"===(e?.output??"dataUrl")){const e=await v(t,n);if(!e)throw new Error("Failed to generate screenshot blob");return e}return"jpeg"===(e?.format??"png")?await y(t,n):await g(t,n)}async function r(t,e){const o=document.body,r=document.createElement("div");O(r,e),o.appendChild(r);try{const o=function(t,e){const o=h(t)?t:w(t,e?.props);return n&&(o.appContext=n),o}(t,e);p(o,r),await d(),await(e?.beforeCapture?.(r)),await d(),await F(e?.deferCaptureMs??16);const a=e?.selector,i=a?r.querySelector(a):r.firstChild;if(!i)throw new Error(`截图元素没有找到: ${a}`);return{container:r,target:i}}catch(t){throw p(null,r),r.remove(),t}}return{captureToBlob:async function(n,r){t.value=!0,e.value=null;try{return await async function(t,e){await F(e?.deferCaptureMs??16);const n=B(t);return await o(n,{...e,output:"blob"})}(n,r)}catch(t){const n=t instanceof Error?t:new Error(String(t));throw e.value=n,n}finally{t.value=!1}},captureToDataUrl:async function(n,r){t.value=!0,e.value=null;try{await F(r?.deferCaptureMs??16);const t=B(n);return await o(t,{...r,output:"dataUrl"})}catch(t){const n=t instanceof Error?t:new Error(String(t));throw e.value=n,n}finally{t.value=!1}},captureComponent:async function(n,a){t.value=!0,e.value=null;const{container:i,target:u}=await r(n,a);try{return await o(u,a)}catch(t){const n=t instanceof Error?t:new Error(String(t));throw e.value=n,n}finally{!function(t){p(null,t),t.remove()}(i),t.value=!1}},isCapturing:t}}let q=null;function T(t){if(q)return t?.appEntries?.length&&q.setAppEntries(t.appEntries),q;const e=f(!1),n=f(!1),o=f(!1);let r=null,a=!1;const i=new Map,u=f(["/"]);let l=null,s=!1;function c(){return"undefined"==typeof BroadcastChannel?null:l||(l=new BroadcastChannel(`${location.origin}/version`),l)}function p(t){c()?.postMessage(t)}function d(){if(!r)return;const t=r;r=null,a=!0,t.close()}function h(){e.value&&n.value&&(r||(r=C({title:"版本更新",message:"新版本已发布,点击刷新获取最新内容",type:"info",duration:0,showClose:!0,position:"bottom-right",onClick:()=>g(),onClose:()=>{r=null,a?a=!1:y()}})))}function w(t){if("reload"!==t.data){if("updateVersion"!==t.data)return"openVersion"===t.data?(n.value=!0,void h()):void("closeVersion"===t.data&&(n.value=!1,d()));e.value=!0}else location.reload()}function v(){n.value=!0,p("openVersion"),h()}function y(){n.value=!1,d(),p("closeVersion")}function g(){p("reload"),location.reload()}return q={isUpdate:m(e),isShow:m(n),setAppEntries:function(t){const e=Array.from(new Set(["/",...t.map(t=>function(t){return t&&"/"!==t?/^https?:\/\//i.test(t)?t.replace(/\/+$/,""):(t.startsWith("/")?t:`/${t}`).replace(/\/+$/,"")||"/":"/"}(t))]));u.value=e;for(const t of e)i.has(t)||i.set(t,"")},checkVersionUpdate:async function(){if(!o.value)if(function(){const t=c();t&&!s&&(t.onmessage=w,s=!0)}(),e.value)v();else if(u.value.length){o.value=!0;try{const t=await Promise.allSettled(u.value.map(async t=>{const e=await fetch(function(t){return`${"/"===t?"":t}/index.html?t=${Date.now()}`}(t),{cache:"no-store",signal:AbortSignal.timeout(5e3)});if(!e.ok)throw new Error(`Request failed: ${e.status}`);const n=await e.text(),o=i.get(t)||"";return i.set(t,n),o&&n!==o?t:null}));u.value.forEach((e,n)=>{const o=t[n];"rejected"===o?.status&&console.warn(`[useVersionCheck] 版本检测失败,请检查该入口是否已配置 CORS:${e}`,o.reason)});t.filter(t=>"fulfilled"===t.status&&t.value).map(t=>t.value).filter(Boolean).length&&(e.value=!0,p("updateVersion"),v())}finally{o.value=!1}}},reloadVersion:g,closeVersion:y},t?.appEntries?.length&&q.setAppEntries(t.appEntries),q}export{k as deepCopy,A as getFirstNonNullOrUndefined,V as getPriorityValues,$ as isValidKey,j as kebabToCamelCase,E as propTypes,R as useDebounceFn,b as useDesign,U as useElementScreenshot,M as useIsScroll,x as usePriorityValue,S as usePriorityValues,T as useVersionCheck};
1
+ import t,{toType as e}from"vue-types/shim";import{kebabToCamelCase as n,getFirstNonNullOrUndefined as r,debounce as o,isNumber as i}from"@dazhicheng/utils";import{getCurrentInstance as a,useSlots as u,useAttrs as s,computed as l,toValue as c,ref as f,render as d,nextTick as p,isVNode as h,h as m,readonly as w}from"vue";import{toBlob as y,toJpeg as g,toPng as v}from"html-to-image";import{ElNotification as C}from"element-plus";import{useRoute as E}from"vue-router";class b extends t{static get style(){return e("style",{type:[String,Object],default:void 0})}static get VNodeChild(){return e("VNodeChild",{default:void 0})}}function S(t){const e="tt";return{prefixCls:`${e}-${t}`,prefixVar:e}}function x(t,e,o){const i=a(),c=u(),f=s();return l(()=>{const a=i?.vnode?.props||{},u={};for(const[t,e]of Object.entries(a))u[n(t)]=e;const s=void 0===u?.[t]?void 0:e[t];return r(c[t],f[t],s,o?.value?.[t])})}function A(t,e){const n={};return Object.keys(t).forEach(r=>{n[r]=x(r,t,e)}),n}function M(t){const e={};for(const[n,r]of Object.entries(t))e[n]=c(r);return e}function $(t,e){return t in e}function k(t,e=new Map){if(null!==t&&"object"==typeof t){let n=e.get(t);return n||(Array.isArray(t)?(n=[],e.set(t,n),t.forEach((t,r)=>{n[r]=k(t,e)})):(n={},e.set(t,n),Object.keys(t).forEach(r=>{$(r,t)&&(n[r]=k(t[r],e))})),n)}return t}function O(...t){for(const e of t)if(null!=e)return e}function V(t){return t.split("-").filter(Boolean).map((t,e)=>0===e?t:t.charAt(0).toUpperCase()+t.slice(1)).join("")}function L(){const t=f(!1);return{hasScrollbar:t,update:e=>{if(!e.value)return;const n=e.value;t.value=n.scrollHeight>n.clientHeight}}}function j(t,e,n){const r=o(t,e??200,n);return{run:r,cancel:r.cancel,flush:r.flush}}function B(t){const e=c(t);if(!e)throw new Error("Target element not found");return e}async function F(t=16){await p(),await new Promise(t=>requestAnimationFrame(()=>t())),await new Promise(t=>requestAnimationFrame(()=>t())),t>0&&await new Promise(e=>setTimeout(e,t))}function R(t,e){t.style.cssText=e?.containerStyle??"position:fixed;left:-10000px;top:0;pointer-events:none;z-index:-1;",e?.containerStyle&&/width\s*:/i.test(e.containerStyle)||(t.style.width=function(t){const e=t?.width??document.documentElement.clientWidth;if(i(t?.width)&&e>0)return`${Math.round(e)}px`;const n=document.documentElement.clientWidth||window.innerWidth;return`${Math.round(n)}px`}(e))}function T(){const t=f(!1),e=f(null),n=a()?.appContext??null;async function r(t,e){await async function(t){t||"undefined"!=typeof document&&"fonts"in document&&await document.fonts.ready}(e?.skipFontsReady);const n=function(t){return{pixelRatio:t?.pixelRatio??Math.min(window.devicePixelRatio||2,3),backgroundColor:t?.backgroundColor??"#ffffff",cacheBust:t?.cacheBust??!0,quality:t?.quality??1,skipFontsReady:t?.skipFontsReady??!1,filter:t?.filter}}(e);if("blob"===(e?.output??"dataUrl")){const e=await y(t,n);if(!e)throw new Error("Failed to generate screenshot blob");return e}return"jpeg"===(e?.format??"png")?await g(t,n):await v(t,n)}async function o(t,e){const r=document.body,o=document.createElement("div");R(o,e),r.appendChild(o);try{const r=function(t,e){const r=h(t)?t:m(t,e?.props);return n&&(r.appContext=n),r}(t,e);d(r,o),await p(),await(e?.beforeCapture?.(o)),await p(),await F(e?.deferCaptureMs??16);const i=e?.selector,a=i?o.querySelector(i):o.firstChild;if(!a)throw new Error(`截图元素没有找到: ${i}`);return{container:o,target:a}}catch(t){throw d(null,o),o.remove(),t}}return{captureToBlob:async function(n,o){t.value=!0,e.value=null;try{return await async function(t,e){await F(e?.deferCaptureMs??16);const n=B(t);return await r(n,{...e,output:"blob"})}(n,o)}catch(t){const n=t instanceof Error?t:new Error(String(t));throw e.value=n,n}finally{t.value=!1}},captureToDataUrl:async function(n,o){t.value=!0,e.value=null;try{await F(o?.deferCaptureMs??16);const t=B(n);return await r(t,{...o,output:"dataUrl"})}catch(t){const n=t instanceof Error?t:new Error(String(t));throw e.value=n,n}finally{t.value=!1}},captureComponent:async function(n,i){t.value=!0,e.value=null;const{container:a,target:u}=await o(n,i);try{return await r(u,i)}catch(t){const n=t instanceof Error?t:new Error(String(t));throw e.value=n,n}finally{!function(t){d(null,t),t.remove()}(a),t.value=!1}},isCapturing:t}}let U=null;function W(t){if(U)return t?.appEntries?.length&&U.setAppEntries(t.appEntries),U;const e=f(!1),n=f(!1),r=f(!1);let o=null,i=!1;const a=new Map,u=f(["/"]);let s=null,l=!1;function c(){return"undefined"==typeof BroadcastChannel?null:s||(s=new BroadcastChannel(`${location.origin}/version`),s)}function d(t){c()?.postMessage(t)}function p(){if(!o)return;const t=o;o=null,i=!0,t.close()}function h(){e.value&&n.value&&(o||(o=C({title:"版本更新",message:"新版本已发布,点击刷新获取最新内容",type:"info",duration:0,showClose:!0,position:"bottom-right",onClick:()=>v(),onClose:()=>{o=null,i?i=!1:g()}})))}function m(t){if("reload"!==t.data){if("updateVersion"!==t.data)return"openVersion"===t.data?(n.value=!0,void h()):void("closeVersion"===t.data&&(n.value=!1,p()));e.value=!0}else location.reload()}function y(){n.value=!0,d("openVersion"),h()}function g(){n.value=!1,p(),d("closeVersion")}function v(){d("reload"),location.reload()}return U={isUpdate:w(e),isShow:w(n),setAppEntries:function(t){const e=Array.from(new Set(["/",...t.map(t=>function(t){return t&&"/"!==t?/^https?:\/\//i.test(t)?t.replace(/\/+$/,""):(t.startsWith("/")?t:`/${t}`).replace(/\/+$/,"")||"/":"/"}(t))]));u.value=e;for(const t of e)a.has(t)||a.set(t,"")},checkVersionUpdate:async function(){if(!r.value)if(function(){const t=c();t&&!l&&(t.onmessage=m,l=!0)}(),e.value)y();else if(u.value.length){r.value=!0;try{const t=await Promise.allSettled(u.value.map(async t=>{const e=await fetch(function(t){return`${"/"===t?"":t}/index.html?t=${Date.now()}`}(t),{cache:"no-store",signal:AbortSignal.timeout(5e3)});if(!e.ok)throw new Error(`Request failed: ${e.status}`);const n=await e.text(),r=a.get(t)||"";return a.set(t,n),r&&n!==r?t:null}));u.value.forEach((e,n)=>{const r=t[n];"rejected"===r?.status&&console.warn(`[useVersionCheck] 版本检测失败,请检查该入口是否已配置 CORS:${e}`,r.reason)});t.filter(t=>"fulfilled"===t.status&&t.value).map(t=>t.value).filter(Boolean).length&&(e.value=!0,d("updateVersion"),y())}finally{r.value=!1}}},reloadVersion:v,closeVersion:g},t?.appEntries?.length&&U.setAppEntries(t.appEntries),U}function q(t){return t?.split(":").filter(Boolean).pop()||""}function N(t,e){if(!e?.length)return[];const n=t.meta.permissionOnlyCode,r=String(t.name||""),o=t.path,i=e.find(t=>{if("2"===String(t.resourceType))return!1;if(n&&t.permissionOnlyCode===n)return!0;const e=q(t.permissionCode);return!(!r||!e||e.toLowerCase()!==r.toLowerCase())||!(!t.value||o!==t.value&&!o.endsWith(`/${t.value}`))});return i?e.filter(t=>"2"===String(t.resourceType)&&t.parentId==i.id).map(t=>({authMark:q(t.permissionCode),title:t.functionName})):[]}function P(t,e){if(!e?.length)return[];const n=t.meta.permissionOnlyCode,r=String(t.name||""),o=t.path,i=t=>{const e=t.meta||{};if(n&&(e.permissionOnlyCode||t.permissionOnlyCode)===n)return!0;if(r&&String(t.name||"")===r)return!0;const i=t.path||t.value||t.component;return!(!i||o!==i&&!o.endsWith(`/${i}`)&&!o.endsWith(String(i)))},a=t=>{for(const e of t){if(i(e)){const t=e.meta?.authList||e.authList;if(Array.isArray(t)&&t.length>0)return t}if(e.children?.length){const t=a(e.children);if(t)return t}}return null};return a(e)||[]}function z(t,e={}){const n=t.meta.authList;if(Array.isArray(n)&&n.length>0)return n;for(let e=t.matched.length-1;e>=0;e-=1){const n=t.matched[e]?.meta?.authList;if(Array.isArray(n)&&n.length>0)return n}if(e.getOriginMenuList){const n=N(t,e.getOriginMenuList()||[]);if(n.length>0)return n}return e.getMenuList?P(t,e.getMenuList()||[]):[]}function D(t){return function(){const e=E();return{hasAuth:n=>{if(t.isFrontendMode())return(t.getFrontendButtons()||[]).includes(n);return z(e,t).some(t=>t?.authMark===n)}}}}export{N as buildAuthListFromOriginMenu,D as createUseAuth,k as deepCopy,P as findAuthListFromMenuTree,O as getFirstNonNullOrUndefined,M as getPriorityValues,$ as isValidKey,V as kebabToCamelCase,b as propTypes,q as resolveAuthMark,z as resolveBackendAuthList,j as useDebounceFn,S as useDesign,T as useElementScreenshot,L as useIsScroll,x as usePriorityValue,A as usePriorityValues,W as useVersionCheck};
2
2
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/propTypes.ts","../src/useDesign.ts","../src/usePriorityValue.ts","../src/tools.ts","../src/useIsScroll.ts","../src/useDebounce.ts","../src/useElementScreenshot.ts","../src/useVersionCheck.ts"],"sourcesContent":["import type { VueTypeDef } from 'vue-types'\nimport VueTypes, { toType } from 'vue-types/shim'\n\n// export type VueNode = VNodeChild | JSX.Element\n\nexport class propTypes extends VueTypes {\n static get style(): VueTypeDef<any> {\n return toType('style', {\n type: [String, Object],\n default: undefined,\n })\n }\n\n static get VNodeChild(): VueTypeDef<any> {\n return toType('VNodeChild', {\n default: undefined,\n })\n }\n}\n","export function useDesign(scope: string) {\n const values = {\n prefixCls: 'tt',\n }\n\n return {\n prefixCls: `${values.prefixCls}-${scope}`,\n prefixVar: values.prefixCls,\n }\n}\n","import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue'\nimport { getFirstNonNullOrUndefined, kebabToCamelCase } from '@dazhicheng/utils'\n\nimport { computed, getCurrentInstance, toValue, unref, useAttrs, useSlots } from 'vue'\n\n/**\n * 依次从插槽、attrs、props、state 中获取值\n * @param key\n * @param props\n * @param state\n */\nfunction usePriorityValue<\n T extends Record<string, any>,\n S extends Record<string, any>,\n K extends keyof T = keyof T,\n>(key: K, props: T, state: Readonly<Ref<NoInfer<S>>> | undefined) {\n const instance = getCurrentInstance()\n const slots = useSlots()\n const attrs = useAttrs() as T\n\n const value = computed((): T[K] => {\n // props不管有没有传,都会有默认值,会影响这里的顺序,\n // 通过判断原始props是否有值来判断是否传入\n const rawProps = (instance?.vnode?.props || {}) as T\n\n const standardRawProps = {} as T\n\n for (const [key, value] of Object.entries(rawProps)) {\n standardRawProps[kebabToCamelCase(key) as K] = value\n }\n const propsKey = standardRawProps?.[key] === undefined ? undefined : props[key]\n\n // slot可以关闭\n return getFirstNonNullOrUndefined(\n slots[key as string],\n attrs[key],\n propsKey,\n state?.value?.[key as keyof S]\n ) as T[K]\n })\n\n return value\n}\n\n/**\n * 批量获取state中的值(每个值都是ref)\n * @param props\n * @param state\n */\nfunction usePriorityValues<\n T extends Record<string, any>,\n S extends Ref<Record<string, any>> = Readonly<Ref<NoInfer<T>, NoInfer<T>>>,\n>(props: T, state: S | undefined) {\n const result: { [K in keyof T]: ComputedRef<T[K]> } = {} as never\n\n ;(Object.keys(props) as (keyof T)[]).forEach(key => {\n result[key] = usePriorityValue(key as keyof typeof props, props, state)\n })\n\n return result\n}\n\n/**\n * 批量获取state中的值(集中在一个computed,用于透传)\n * @param props\n * @param state\n */\nfunction useForwardPriorityValues<\n T extends Record<string, any>,\n S extends Ref<Record<string, any>> = Readonly<Ref<NoInfer<T>, NoInfer<T>>>,\n>(props: T, state: S | undefined) {\n const computedResult: { [K in keyof T]: ComputedRef<T[K]> } = {} as never\n\n ;(Object.keys(props) as (keyof T)[]).forEach(key => {\n computedResult[key] = usePriorityValue(key as keyof typeof props, props, state)\n })\n\n return computed(() => {\n const unwrapResult: Record<string, any> = {}\n Object.keys(props).forEach(key => {\n unwrapResult[key] = unref(computedResult[key])\n })\n return unwrapResult as { [K in keyof T]: T[K] }\n })\n}\n\n/**\n * 将包含ComputedRef的对象转换为普通值对象\n * @param props - 包含ComputedRef的响应式对象\n * @returns 解包后的普通对象,所有值都是原始值而非响应式引用\n */\nfunction getPriorityValues<\n T extends Record<string, MaybeRefOrGetter<any>>,\n S extends {\n [K in keyof T]: T[K] extends MaybeRefOrGetter<infer U> ? U : T[K]\n },\n>(props: T): S {\n // 创建解包后的对象类型\n const standardProps: S = {} as never\n\n // 遍历props对象的每个键值对\n for (const [key, value] of Object.entries(props)) {\n // 使用toValue解包计算属性值,并赋值给新对象\n standardProps[key as keyof T] = toValue(value)\n }\n\n return standardProps\n}\n\nexport { getPriorityValues, useForwardPriorityValues, usePriorityValue, usePriorityValues }\n","/**\n * 判断对象中是否包含该属性\n * @param key 键\n * @param object 对象\n * @returns 是否包含\n */\nexport function isValidKey(\n key: string | number | symbol,\n object: object\n): key is keyof typeof object {\n return key in object\n}\n\n/**\n * 深拷贝\n * 兼容函数,对象,相互引用场景\n * @param target 需要深拷贝的原对象\n * @return 深拷贝后的对象\n */\nexport function deepCopy<T>(target: T, map = new Map()) {\n if (target !== null && typeof target === 'object') {\n let res = map.get(target)\n if (res) return res\n if (Array.isArray(target)) {\n res = []\n map.set(target, res)\n target.forEach((item, index) => {\n res[index] = deepCopy(item, map)\n })\n } else {\n res = {}\n map.set(target, res)\n Object.keys(target).forEach(key => {\n if (isValidKey(key, target)) res[key] = deepCopy(target[key], map)\n })\n }\n return res\n }\n return target\n}\n\n\n/**\n * 获取第一个非 null 和非 undefined 的值\n * @description 从传入的多个值中返回第一个既不是 null 也不是 undefined 的值\n * @template T 返回值类型\n * @param args 要检查的值数组\n * @returns 第一个非 null 和非 undefined 的值,如果都是 null 或 undefined 则返回 undefined\n *\n * @example\n * getFirstNonNullOrUndefined(null, undefined, 1, 2) // 返回 1\n * getFirstNonNullOrUndefined(null, undefined) // 返回 undefined\n * getFirstNonNullOrUndefined('', null, 'test') // 返回 ''\n */\nexport function getFirstNonNullOrUndefined(...args: any[]): any {\n for (const value of args) {\n if (value !== undefined && value !== null) {\n return value\n }\n }\n return undefined\n}\n\n\n/**\n * 将短横线命名转换为驼峰命名\n * @description 将形如 'my-component-name' 的短横线命名法转换为 'myComponentName' 的驼峰命名法\n * @param str 要转换的短横线命名字符串\n * @returns 转换后的驼峰命名字符串\n *\n * @example\n * kebabToCamelCase('my-component') // 返回 'myComponent'\n * kebabToCamelCase('my--component') // 返回 'myComponent'\n * kebabToCamelCase('-my-component-') // 返回 'myComponent'\n */\nexport function kebabToCamelCase(str: string): string {\n return str\n .split('-')\n .filter(Boolean)\n .map((word, index) => (index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1)))\n .join('')\n}","import type { Ref } from 'vue'\nimport { ref } from 'vue'\n\nexport function useIsScroll(): {\n hasScrollbar: Ref<boolean>\n update: (elementRef: Ref<HTMLDivElement>) => void\n} {\n const hasScrollbar = ref(false)\n\n const update = (elementRef: Ref<HTMLDivElement>): void => {\n if (!elementRef.value) return\n const element = elementRef.value\n hasScrollbar.value = element.scrollHeight > element.clientHeight\n }\n\n return { hasScrollbar, update }\n}\n","import { debounce } from \"@dazhicheng/utils\";\r\n\r\nexport type DebounceSettings = {\r\n /**\r\n * 前沿触发\r\n */\r\n leading?: boolean | undefined;\r\n /**\r\n * 最大等待时间\r\n */\r\n maxWait?: number | undefined;\r\n /**\r\n * 尾部触发\r\n */\r\n trailing?: boolean | undefined;\r\n};\r\n\r\ntype noop = (...args: any[]) => any;\r\n\r\n/**\r\n * 防抖函数 Hook 返回类型\r\n */\r\ntype UseDebounceFnReturn<T extends noop> = {\r\n /**\r\n * 防抖函数,保持原函数的完整签名\r\n */\r\n run: (...args: Parameters<T>) => ReturnType<T>;\r\n /**\r\n * 取消防抖\r\n */\r\n cancel: () => void;\r\n /**\r\n * 立即执行防抖函数,保持原函数的完整签名\r\n */\r\n flush: (...args: Parameters<T>) => ReturnType<T>;\r\n};\r\n\r\n/**\r\n * 防抖函数 Hook\r\n * @param fn 要防抖的函数\r\n * @param wait 延迟时间(毫秒)默认200\r\n * @param options 防抖选项\r\n * @returns 返回防抖后的函数和相关控制方法\r\n */\r\nexport function useDebounceFn<T extends noop>(\r\n fn: T,\r\n wait?: number,\r\n options?: DebounceSettings,\r\n): UseDebounceFnReturn<T> {\r\n const debounced = debounce(fn, wait ?? 200, options);\r\n return {\r\n /**\r\n * 防抖函数,保持原函数的返回类型\r\n * 如果原函数是异步的,返回 Promise;如果是同步的,返回原始类型\r\n */\r\n run: debounced as unknown as (...args: Parameters<T>) => ReturnType<T>,\r\n\r\n /**\r\n * 取消防抖\r\n */\r\n cancel: debounced.cancel,\r\n\r\n /**\r\n * 立即执行防抖函数,保持原函数的返回类型\r\n */\r\n flush: debounced.flush as unknown as (...args: Parameters<T>) => ReturnType<T>,\r\n };\r\n}\r\n","import type { Component, VNode } from \"vue\";\nimport type { MaybeRefOrGetter } from \"vue\";\nimport { getCurrentInstance, h, isVNode, nextTick, ref, render, toValue } from \"vue\";\nimport { isNumber } from \"@dazhicheng/utils\";\nimport { toBlob, toPng as htmlToPng, toJpeg as htmlToJpeg } from \"html-to-image\";\n\nexport type ScreenshotTarget = HTMLElement | null | undefined;\ntype CaptureFormat = \"png\" | \"jpeg\";\nexport type ComponentCaptureOutput = \"dataUrl\" | \"blob\";\n\n/**\n * 截图配置项\n */\nexport interface ElementScreenshotOptions {\n /**\n * 输出格式\n * 默认 png\n */\n format?: CaptureFormat;\n\n /**\n * jpeg 输出质量\n * 取值范围 0 到 1\n * 默认 1\n */\n quality?: number;\n\n /**\n * 渲染像素比\n * 默认 min(devicePixelRatio, 3)\n */\n pixelRatio?: number;\n\n /**\n * 背景色\n * 默认 #ffffff\n */\n backgroundColor?: string;\n\n /**\n * 资源防缓存参数\n * 默认 true\n */\n cacheBust?: boolean;\n\n /**\n * 是否跳过 document.fonts.ready\n * 默认 false\n */\n skipFontsReady?: boolean;\n\n /**\n * 截图前额外延迟毫秒数\n * 用于保证 loading 先渲染\n * 默认 16\n */\n deferCaptureMs?: number;\n /**\n * html-to-image 节点过滤(返回 false 的节点及其子树不会出现在截图中)\n */\n filter?: (domNode: HTMLElement) => boolean;\n}\n\n/**\n * 离屏挂载组件并截图的配置项\n */\nexport interface ComponentCaptureOptions extends ElementScreenshotOptions {\n /**\n * 传给组件的 props\n */\n props?: Record<string, unknown>;\n\n /**\n * 离屏容器行内样式\n * 若未设置 width,会与 width 选项合并\n */\n containerStyle?: string;\n\n /**\n * 离屏容器宽度(px)\n * 默认 document.documentElement.clientWidth(当前页面视口宽)\n */\n width: number;\n\n /**\n * 截图目标在离屏容器内的选择器\n * 未传则使用容器第一个子元素\n */\n selector?: string;\n\n /**\n * 输出类型\n * dataUrl 为 DataURL 字符串,blob 为 Blob\n * 默认 dataUrl\n */\n output?: ComponentCaptureOutput;\n\n /**\n * 组件挂载并完成 nextTick 后的钩子(如同步表单值)\n * @param container 离屏挂载根容器\n */\n beforeCapture?: (container: HTMLElement) => void | Promise<void>;\n}\n\nconst DEFAULT_OFFSCREEN_CONTAINER_STYLE = \"position:fixed;left:-10000px;top:0;pointer-events:none;z-index:-1;\";\n\n/**\n * 解析截图目标元素\n */\nfunction resolveTarget(target: MaybeRefOrGetter<ScreenshotTarget>): HTMLElement {\n const node = toValue(target);\n if (!node) {\n throw new Error(\"Target element not found\");\n }\n return node;\n}\n\n/**\n * 构建 html-to-image 渲染参数\n */\nfunction getRenderOptions(options?: ElementScreenshotOptions) {\n return {\n pixelRatio: options?.pixelRatio ?? Math.min(window.devicePixelRatio || 2, 3),\n backgroundColor: options?.backgroundColor ?? \"#ffffff\",\n cacheBust: options?.cacheBust ?? true,\n quality: options?.quality ?? 1,\n skipFontsReady: options?.skipFontsReady ?? false,\n filter: options?.filter,\n // includeStyleProperties: [\"hidden\"],\n };\n}\n\n/**\n * 截图前等待字体加载完成\n */\nasync function ensureFontsReady(skipFontsReady?: boolean) {\n if (skipFontsReady) return;\n if (typeof document === \"undefined\" || !(\"fonts\" in document)) return;\n await document.fonts.ready;\n}\n\n/**\n * 让出一帧,确保 DOM 绘制完成\n */\nasync function waitPaintFrame(deferCaptureMs = 16) {\n await nextTick();\n await new Promise<void>(resolve => requestAnimationFrame(() => resolve()));\n await new Promise<void>(resolve => requestAnimationFrame(() => resolve()));\n if (deferCaptureMs > 0) {\n await new Promise<void>(resolve => setTimeout(resolve, deferCaptureMs));\n }\n}\n\nfunction resolveOffscreenWidth(options?: ComponentCaptureOptions): string {\n const width = options?.width ?? document.documentElement.clientWidth;\n if (isNumber(options?.width) && width > 0) {\n return `${Math.round(width)}px`;\n }\n const viewportWidth = document.documentElement.clientWidth || window.innerWidth;\n return `${Math.round(viewportWidth)}px`;\n}\n\nfunction applyOffscreenContainerStyle(container: HTMLElement, options?: ComponentCaptureOptions) {\n container.style.cssText = options?.containerStyle ?? DEFAULT_OFFSCREEN_CONTAINER_STYLE;\n if (!options?.containerStyle || !/width\\s*:/i.test(options.containerStyle)) {\n container.style.width = resolveOffscreenWidth(options);\n }\n}\n\n/**\n * DOM 截图 Hook\n */\nexport function useElementScreenshot() {\n const isCapturing = ref(false);\n const lastError = ref<Error | null>(null);\n const fallbackAppContext = getCurrentInstance()?.appContext ?? null;\n\n /**\n * 对已有 DOM 节点执行 html-to-image 截图\n */\n async function renderTargetToResult(\n target: HTMLElement,\n options?: ElementScreenshotOptions & { output?: ComponentCaptureOutput },\n ): Promise<string | Blob> {\n await ensureFontsReady(options?.skipFontsReady);\n const renderOptions = getRenderOptions(options);\n const output = options?.output ?? \"dataUrl\";\n\n if (output === \"blob\") {\n const blob = await toBlob(target, renderOptions);\n if (!blob) {\n throw new Error(\"Failed to generate screenshot blob\");\n }\n return blob;\n }\n\n const format = options?.format ?? \"png\";\n if (format === \"jpeg\") {\n return await htmlToJpeg(target, renderOptions);\n }\n return await htmlToPng(target, renderOptions);\n }\n\n /**\n * 离屏挂载组件并解析截图目标\n */\n function resolveRenderVNode(component: Component | VNode, options?: ComponentCaptureOptions): VNode {\n const vnode = isVNode(component) ? component : h(component, options?.props);\n if (fallbackAppContext) {\n vnode.appContext = fallbackAppContext;\n }\n return vnode;\n }\n\n async function mountComponentOffscreen(component: Component | VNode, options?: ComponentCaptureOptions) {\n const host = document.body;\n const container = document.createElement(\"div\");\n applyOffscreenContainerStyle(container, options);\n host.appendChild(container);\n\n try {\n const vnode = resolveRenderVNode(component, options);\n render(vnode, container);\n await nextTick();\n await options?.beforeCapture?.(container);\n await nextTick();\n await waitPaintFrame(options?.deferCaptureMs ?? 16);\n const selector = options?.selector;\n const target = selector\n ? (container.querySelector(selector) as HTMLElement | null)\n : (container.firstChild as HTMLElement);\n if (!target) {\n throw new Error(`截图元素没有找到: ${selector}`);\n }\n return { container, target };\n } catch (error) {\n render(null, container);\n container.remove();\n throw error;\n }\n }\n\n function unmountOffscreen(container: HTMLElement) {\n render(null, container);\n container.remove();\n }\n\n async function renderToBaseBlob(\n target: MaybeRefOrGetter<ScreenshotTarget>,\n options?: ElementScreenshotOptions,\n ): Promise<Blob> {\n await waitPaintFrame(options?.deferCaptureMs ?? 16);\n const node = resolveTarget(target);\n const result = await renderTargetToResult(node, { ...options, output: \"blob\" });\n return result as Blob;\n }\n\n async function captureToBlob(\n target: MaybeRefOrGetter<ScreenshotTarget>,\n options?: ElementScreenshotOptions,\n ): Promise<Blob> {\n isCapturing.value = true;\n lastError.value = null;\n try {\n return await renderToBaseBlob(target, options);\n } catch (error) {\n const finalError = error instanceof Error ? error : new Error(String(error));\n lastError.value = finalError;\n throw finalError;\n } finally {\n isCapturing.value = false;\n }\n }\n\n async function captureToDataUrl(\n target: MaybeRefOrGetter<ScreenshotTarget>,\n options?: ElementScreenshotOptions,\n ): Promise<string> {\n isCapturing.value = true;\n lastError.value = null;\n try {\n await waitPaintFrame(options?.deferCaptureMs ?? 16);\n const node = resolveTarget(target);\n const result = await renderTargetToResult(node, { ...options, output: \"dataUrl\" });\n return result as string;\n } catch (error) {\n const finalError = error instanceof Error ? error : new Error(String(error));\n lastError.value = finalError;\n throw finalError;\n } finally {\n isCapturing.value = false;\n }\n }\n\n /**\n * 截取传入的组件\n */\n async function captureComponent(\n component: Component | VNode,\n options?: ComponentCaptureOptions & { output?: \"dataUrl\" },\n ): Promise<string>;\n async function captureComponent(\n component: Component | VNode,\n options: ComponentCaptureOptions & { output: \"blob\" },\n ): Promise<Blob>;\n async function captureComponent(\n component: Component | VNode,\n options?: ComponentCaptureOptions,\n ): Promise<string | Blob> {\n isCapturing.value = true;\n lastError.value = null;\n const { container, target } = await mountComponentOffscreen(component, options);\n try {\n return await renderTargetToResult(target, options);\n } catch (error) {\n const finalError = error instanceof Error ? error : new Error(String(error));\n lastError.value = finalError;\n throw finalError;\n } finally {\n unmountOffscreen(container);\n isCapturing.value = false;\n }\n }\n\n return {\n captureToBlob,\n captureToDataUrl,\n captureComponent,\n isCapturing,\n };\n}\n","import type { Ref } from \"vue\";\r\nimport { readonly, ref } from \"vue\";\r\nimport type { NotificationHandle } from \"element-plus\";\r\nimport { ElNotification } from \"element-plus\";\r\n\r\n/** 广播消息类型 */\r\ntype VersionMessage = \"reload\" | \"updateVersion\" | \"openVersion\" | \"closeVersion\";\r\n\r\n/** Hook 配置 */\r\nexport type UseVersionCheckOptions = {\r\n /**\r\n * 需要检测的子应用入口\r\n */\r\n appEntries?: string[];\r\n};\r\n\r\n/** Hook 返回值 */\r\nexport type UseVersionCheckReturn = {\r\n /**\r\n * 是否已检测到新版本\r\n */\r\n isUpdate: Readonly<Ref<boolean>>;\r\n /**\r\n * 是否展示更新提示\r\n */\r\n isShow: Readonly<Ref<boolean>>;\r\n /**\r\n * 设置需要检测的入口列表\r\n */\r\n setAppEntries: (appEntries: string[]) => void;\r\n /**\r\n * 执行版本检测\r\n */\r\n checkVersionUpdate: () => Promise<void>;\r\n /**\r\n * 刷新当前页,同时通知其他标签\r\n */\r\n reloadVersion: () => void;\r\n /**\r\n * 关闭更新提示\r\n */\r\n closeVersion: () => void;\r\n};\r\n\r\n/** 共享 Hook 实例 */\r\nlet versionCheckInstance: UseVersionCheckReturn | null = null;\r\n\r\n/** 是否支持 BroadcastChannel */\r\nfunction isBroadcastChannelSupported(): boolean {\r\n return typeof BroadcastChannel !== \"undefined\";\r\n}\r\n\r\n/** 规范化入口路径 */\r\nfunction normalizeEntry(entry: string): string {\r\n if (!entry || entry === \"/\") return \"/\";\r\n // 绝对地址(http(s)://...):去掉尾部斜杠后原样保留,用于跨源检测子应用\r\n if (/^https?:\\/\\//i.test(entry)) {\r\n return entry.replace(/\\/+$/, \"\");\r\n }\r\n const normalizedEntry = entry.startsWith(\"/\") ? entry : `/${entry}`;\r\n return normalizedEntry.replace(/\\/+$/, \"\") || \"/\";\r\n}\r\n\r\n/**\r\n * 版本更新检测 Hook\r\n * @param options Hook 配置\r\n * @returns 版本检测状态和操作方法\r\n */\r\nexport function useVersionCheck(options?: UseVersionCheckOptions): UseVersionCheckReturn {\r\n if (versionCheckInstance) {\r\n if (options?.appEntries?.length) {\r\n versionCheckInstance.setAppEntries(options.appEntries);\r\n }\r\n return versionCheckInstance;\r\n }\r\n\r\n /** 是否已检测到新版本 */\r\n const isUpdate = ref(false);\r\n\r\n /** 是否展示更新提示 */\r\n const isShow = ref(false);\r\n\r\n /** 是否正在执行版本检测 */\r\n const isChecking = ref(false);\r\n\r\n /** 当前更新提示实例 */\r\n let notificationHandle: NotificationHandle | null = null;\r\n\r\n /** 是否为程序化关闭提示(用于区分用户主动关闭) */\r\n let isProgrammaticClose = false;\r\n\r\n /** 各入口 index.html 缓存 */\r\n const cache = new Map<string, string>();\r\n\r\n /** 入口列表 */\r\n const entries = ref<string[]>([\"/\"]);\r\n\r\n /** 版本广播通道 */\r\n let channel: BroadcastChannel | null = null;\r\n\r\n /** 是否已绑定通道消息 */\r\n let isChannelBound = false;\r\n\r\n /** 获取版本广播通道 */\r\n function getChannel(): BroadcastChannel | null {\r\n if (!isBroadcastChannelSupported()) return null;\r\n if (channel) return channel;\r\n channel = new BroadcastChannel(`${location.origin}/version`);\r\n return channel;\r\n }\r\n\r\n /** 发送广播消息 */\r\n function postVersionMessage(message: VersionMessage): void {\r\n getChannel()?.postMessage(message);\r\n }\r\n\r\n /** 获取入口 index.html 请求地址 */\r\n function getEntryHtmlUrl(entry: string): string {\r\n const normalizedEntry = entry === \"/\" ? \"\" : entry;\r\n return `${normalizedEntry}/index.html?t=${Date.now()}`;\r\n }\r\n\r\n /** 弹出更新提示 */\r\n function showNotification(): void {\r\n if (notificationHandle) return;\r\n notificationHandle = ElNotification({\r\n title: \"版本更新\",\r\n message: \"新版本已发布,点击刷新获取最新内容\",\r\n type: \"info\",\r\n duration: 0,\r\n showClose: true,\r\n position: \"bottom-right\",\r\n onClick: () => reloadVersion(),\r\n onClose: () => {\r\n notificationHandle = null;\r\n // 程序化关闭无需重复处理,用户主动关闭(点 X)视为“稍后”\r\n if (isProgrammaticClose) {\r\n isProgrammaticClose = false;\r\n return;\r\n }\r\n closeVersion();\r\n },\r\n });\r\n }\r\n\r\n /** 关闭更新提示 */\r\n function hideNotification(): void {\r\n if (!notificationHandle) return;\r\n const handle = notificationHandle;\r\n notificationHandle = null;\r\n isProgrammaticClose = true;\r\n handle.close();\r\n }\r\n\r\n /** 按当前状态尝试弹出提示 */\r\n function tryShowNotification(): void {\r\n if (!isUpdate.value || !isShow.value) return;\r\n showNotification();\r\n }\r\n\r\n /** 处理广播消息 */\r\n function handleChannelMessage(event: MessageEvent<VersionMessage>): void {\r\n if (event.data === \"reload\") {\r\n location.reload();\r\n return;\r\n }\r\n\r\n if (event.data === \"updateVersion\") {\r\n isUpdate.value = true;\r\n return;\r\n }\r\n\r\n if (event.data === \"openVersion\") {\r\n isShow.value = true;\r\n tryShowNotification();\r\n return;\r\n }\r\n\r\n if (event.data === \"closeVersion\") {\r\n isShow.value = false;\r\n hideNotification();\r\n }\r\n }\r\n\r\n /** 绑定广播通道消息 */\r\n function ensureChannelListener(): void {\r\n const currentChannel = getChannel();\r\n if (!currentChannel || isChannelBound) return;\r\n currentChannel.onmessage = handleChannelMessage;\r\n isChannelBound = true;\r\n }\r\n\r\n /** 标记有新版本 */\r\n function updateVersion(): void {\r\n isUpdate.value = true;\r\n postVersionMessage(\"updateVersion\");\r\n }\r\n\r\n /** 打开更新提示 */\r\n function openVersion(): void {\r\n isShow.value = true;\r\n postVersionMessage(\"openVersion\");\r\n tryShowNotification();\r\n }\r\n\r\n /** 关闭更新提示 */\r\n function closeVersion(): void {\r\n isShow.value = false;\r\n hideNotification();\r\n postVersionMessage(\"closeVersion\");\r\n }\r\n\r\n /** 刷新当前页,同时通知其他标签 */\r\n function reloadVersion(): void {\r\n postVersionMessage(\"reload\");\r\n location.reload();\r\n }\r\n\r\n /** 设置需要检测的入口列表 */\r\n function setAppEntries(appEntries: string[]): void {\r\n const normalizedEntries = Array.from(new Set([\"/\", ...appEntries.map(entry => normalizeEntry(entry))]));\r\n entries.value = normalizedEntries;\r\n for (const entry of normalizedEntries) {\r\n if (!cache.has(entry)) {\r\n cache.set(entry, \"\");\r\n }\r\n }\r\n }\r\n\r\n /** 执行版本检测 */\r\n async function checkVersionUpdate(): Promise<void> {\r\n if (isChecking.value) return;\r\n\r\n ensureChannelListener();\r\n\r\n // 已检测到新版本:不再重复请求,仅在本次检测时重新提示(“稍后”关闭后下一次检测会再次提醒)\r\n if (isUpdate.value) {\r\n openVersion();\r\n return;\r\n }\r\n\r\n if (!entries.value.length) return;\r\n\r\n isChecking.value = true;\r\n\r\n try {\r\n const results = await Promise.allSettled(\r\n entries.value.map(async entry => {\r\n const response = await fetch(getEntryHtmlUrl(entry), {\r\n cache: \"no-store\",\r\n signal: AbortSignal.timeout(5000),\r\n });\r\n if (!response.ok) {\r\n throw new Error(`Request failed: ${response.status}`);\r\n }\r\n const html = await response.text();\r\n const oldHtml = cache.get(entry) || \"\";\r\n cache.set(entry, html);\r\n return oldHtml && html !== oldHtml ? entry : null;\r\n }),\r\n );\r\n\r\n // 失败入口告警:跨域子应用若未配置 CORS 响应头,请求会被浏览器拦截而静默失效,这里打印便于排查\r\n entries.value.forEach((entry, index) => {\r\n const result = results[index];\r\n if (result?.status === \"rejected\") {\r\n console.warn(`[useVersionCheck] 版本检测失败,请检查该入口是否已配置 CORS:${entry}`, result.reason);\r\n }\r\n });\r\n\r\n const updatedEntries = results\r\n .filter(result => result.status === \"fulfilled\" && result.value)\r\n .map(result => (result as PromiseFulfilledResult<string | null>).value)\r\n .filter(Boolean);\r\n\r\n if (updatedEntries.length) {\r\n updateVersion();\r\n openVersion();\r\n }\r\n } finally {\r\n isChecking.value = false;\r\n }\r\n }\r\n\r\n versionCheckInstance = {\r\n isUpdate: readonly(isUpdate),\r\n isShow: readonly(isShow),\r\n setAppEntries,\r\n checkVersionUpdate,\r\n reloadVersion,\r\n closeVersion,\r\n };\r\n\r\n if (options?.appEntries?.length) {\r\n versionCheckInstance.setAppEntries(options.appEntries);\r\n }\r\n\r\n return versionCheckInstance;\r\n}\r\n"],"names":["propTypes","VueTypes","style","toType","type","String","Object","default","undefined","VNodeChild","useDesign","scope","values","prefixCls","prefixVar","usePriorityValue","key","props","state","instance","getCurrentInstance","slots","useSlots","attrs","useAttrs","computed","rawProps","vnode","standardRawProps","value","entries","kebabToCamelCase","propsKey","getFirstNonNullOrUndefined","usePriorityValues","result","keys","forEach","getPriorityValues","standardProps","toValue","isValidKey","object","deepCopy","target","map","Map","res","get","Array","isArray","set","item","index","args","str","split","filter","Boolean","word","charAt","toUpperCase","slice","join","useIsScroll","hasScrollbar","ref","update","elementRef","element","scrollHeight","clientHeight","useDebounceFn","fn","wait","options","debounced","debounce","run","cancel","flush","resolveTarget","node","Error","async","waitPaintFrame","deferCaptureMs","nextTick","Promise","resolve","requestAnimationFrame","setTimeout","applyOffscreenContainerStyle","container","cssText","containerStyle","test","width","document","documentElement","clientWidth","isNumber","Math","round","viewportWidth","window","innerWidth","resolveOffscreenWidth","useElementScreenshot","isCapturing","lastError","fallbackAppContext","appContext","renderTargetToResult","skipFontsReady","fonts","ready","ensureFontsReady","renderOptions","pixelRatio","min","devicePixelRatio","backgroundColor","cacheBust","quality","getRenderOptions","output","blob","toBlob","format","htmlToJpeg","htmlToPng","mountComponentOffscreen","component","host","body","createElement","appendChild","isVNode","h","resolveRenderVNode","render","beforeCapture","selector","querySelector","firstChild","error","remove","captureToBlob","renderToBaseBlob","finalError","captureToDataUrl","captureComponent","unmountOffscreen","versionCheckInstance","useVersionCheck","appEntries","length","setAppEntries","isUpdate","isShow","isChecking","notificationHandle","isProgrammaticClose","cache","channel","isChannelBound","getChannel","BroadcastChannel","location","origin","postVersionMessage","message","postMessage","hideNotification","handle","close","tryShowNotification","ElNotification","title","duration","showClose","position","onClick","reloadVersion","onClose","closeVersion","handleChannelMessage","event","data","reload","openVersion","readonly","normalizedEntries","from","Set","entry","replace","startsWith","normalizeEntry","has","checkVersionUpdate","currentChannel","onmessage","ensureChannelListener","results","allSettled","response","fetch","Date","now","getEntryHtmlUrl","signal","AbortSignal","timeout","ok","status","html","text","oldHtml","console","warn","reason"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/propTypes.ts","../src/useDesign.ts","../src/usePriorityValue.ts","../src/tools.ts","../src/useIsScroll.ts","../src/useDebounce.ts","../src/useElementScreenshot.ts","../src/useVersionCheck.ts","../src/useAuth.ts"],"sourcesContent":["import type { VueTypeDef } from 'vue-types'\nimport VueTypes, { toType } from 'vue-types/shim'\n\n// export type VueNode = VNodeChild | JSX.Element\n\nexport class propTypes extends VueTypes {\n static get style(): VueTypeDef<any> {\n return toType('style', {\n type: [String, Object],\n default: undefined,\n })\n }\n\n static get VNodeChild(): VueTypeDef<any> {\n return toType('VNodeChild', {\n default: undefined,\n })\n }\n}\n","export function useDesign(scope: string) {\n const values = {\n prefixCls: 'tt',\n }\n\n return {\n prefixCls: `${values.prefixCls}-${scope}`,\n prefixVar: values.prefixCls,\n }\n}\n","import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue'\nimport { getFirstNonNullOrUndefined, kebabToCamelCase } from '@dazhicheng/utils'\n\nimport { computed, getCurrentInstance, toValue, unref, useAttrs, useSlots } from 'vue'\n\n/**\n * 依次从插槽、attrs、props、state 中获取值\n * @param key\n * @param props\n * @param state\n */\nfunction usePriorityValue<\n T extends Record<string, any>,\n S extends Record<string, any>,\n K extends keyof T = keyof T,\n>(key: K, props: T, state: Readonly<Ref<NoInfer<S>>> | undefined) {\n const instance = getCurrentInstance()\n const slots = useSlots()\n const attrs = useAttrs() as T\n\n const value = computed((): T[K] => {\n // props不管有没有传,都会有默认值,会影响这里的顺序,\n // 通过判断原始props是否有值来判断是否传入\n const rawProps = (instance?.vnode?.props || {}) as T\n\n const standardRawProps = {} as T\n\n for (const [key, value] of Object.entries(rawProps)) {\n standardRawProps[kebabToCamelCase(key) as K] = value\n }\n const propsKey = standardRawProps?.[key] === undefined ? undefined : props[key]\n\n // slot可以关闭\n return getFirstNonNullOrUndefined(\n slots[key as string],\n attrs[key],\n propsKey,\n state?.value?.[key as keyof S]\n ) as T[K]\n })\n\n return value\n}\n\n/**\n * 批量获取state中的值(每个值都是ref)\n * @param props\n * @param state\n */\nfunction usePriorityValues<\n T extends Record<string, any>,\n S extends Ref<Record<string, any>> = Readonly<Ref<NoInfer<T>, NoInfer<T>>>,\n>(props: T, state: S | undefined) {\n const result: { [K in keyof T]: ComputedRef<T[K]> } = {} as never\n\n ;(Object.keys(props) as (keyof T)[]).forEach(key => {\n result[key] = usePriorityValue(key as keyof typeof props, props, state)\n })\n\n return result\n}\n\n/**\n * 批量获取state中的值(集中在一个computed,用于透传)\n * @param props\n * @param state\n */\nfunction useForwardPriorityValues<\n T extends Record<string, any>,\n S extends Ref<Record<string, any>> = Readonly<Ref<NoInfer<T>, NoInfer<T>>>,\n>(props: T, state: S | undefined) {\n const computedResult: { [K in keyof T]: ComputedRef<T[K]> } = {} as never\n\n ;(Object.keys(props) as (keyof T)[]).forEach(key => {\n computedResult[key] = usePriorityValue(key as keyof typeof props, props, state)\n })\n\n return computed(() => {\n const unwrapResult: Record<string, any> = {}\n Object.keys(props).forEach(key => {\n unwrapResult[key] = unref(computedResult[key])\n })\n return unwrapResult as { [K in keyof T]: T[K] }\n })\n}\n\n/**\n * 将包含ComputedRef的对象转换为普通值对象\n * @param props - 包含ComputedRef的响应式对象\n * @returns 解包后的普通对象,所有值都是原始值而非响应式引用\n */\nfunction getPriorityValues<\n T extends Record<string, MaybeRefOrGetter<any>>,\n S extends {\n [K in keyof T]: T[K] extends MaybeRefOrGetter<infer U> ? U : T[K]\n },\n>(props: T): S {\n // 创建解包后的对象类型\n const standardProps: S = {} as never\n\n // 遍历props对象的每个键值对\n for (const [key, value] of Object.entries(props)) {\n // 使用toValue解包计算属性值,并赋值给新对象\n standardProps[key as keyof T] = toValue(value)\n }\n\n return standardProps\n}\n\nexport { getPriorityValues, useForwardPriorityValues, usePriorityValue, usePriorityValues }\n","/**\n * 判断对象中是否包含该属性\n * @param key 键\n * @param object 对象\n * @returns 是否包含\n */\nexport function isValidKey(\n key: string | number | symbol,\n object: object\n): key is keyof typeof object {\n return key in object\n}\n\n/**\n * 深拷贝\n * 兼容函数,对象,相互引用场景\n * @param target 需要深拷贝的原对象\n * @return 深拷贝后的对象\n */\nexport function deepCopy<T>(target: T, map = new Map()) {\n if (target !== null && typeof target === 'object') {\n let res = map.get(target)\n if (res) return res\n if (Array.isArray(target)) {\n res = []\n map.set(target, res)\n target.forEach((item, index) => {\n res[index] = deepCopy(item, map)\n })\n } else {\n res = {}\n map.set(target, res)\n Object.keys(target).forEach(key => {\n if (isValidKey(key, target)) res[key] = deepCopy(target[key], map)\n })\n }\n return res\n }\n return target\n}\n\n\n/**\n * 获取第一个非 null 和非 undefined 的值\n * @description 从传入的多个值中返回第一个既不是 null 也不是 undefined 的值\n * @template T 返回值类型\n * @param args 要检查的值数组\n * @returns 第一个非 null 和非 undefined 的值,如果都是 null 或 undefined 则返回 undefined\n *\n * @example\n * getFirstNonNullOrUndefined(null, undefined, 1, 2) // 返回 1\n * getFirstNonNullOrUndefined(null, undefined) // 返回 undefined\n * getFirstNonNullOrUndefined('', null, 'test') // 返回 ''\n */\nexport function getFirstNonNullOrUndefined(...args: any[]): any {\n for (const value of args) {\n if (value !== undefined && value !== null) {\n return value\n }\n }\n return undefined\n}\n\n\n/**\n * 将短横线命名转换为驼峰命名\n * @description 将形如 'my-component-name' 的短横线命名法转换为 'myComponentName' 的驼峰命名法\n * @param str 要转换的短横线命名字符串\n * @returns 转换后的驼峰命名字符串\n *\n * @example\n * kebabToCamelCase('my-component') // 返回 'myComponent'\n * kebabToCamelCase('my--component') // 返回 'myComponent'\n * kebabToCamelCase('-my-component-') // 返回 'myComponent'\n */\nexport function kebabToCamelCase(str: string): string {\n return str\n .split('-')\n .filter(Boolean)\n .map((word, index) => (index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1)))\n .join('')\n}","import type { Ref } from 'vue'\nimport { ref } from 'vue'\n\nexport function useIsScroll(): {\n hasScrollbar: Ref<boolean>\n update: (elementRef: Ref<HTMLDivElement>) => void\n} {\n const hasScrollbar = ref(false)\n\n const update = (elementRef: Ref<HTMLDivElement>): void => {\n if (!elementRef.value) return\n const element = elementRef.value\n hasScrollbar.value = element.scrollHeight > element.clientHeight\n }\n\n return { hasScrollbar, update }\n}\n","import { debounce } from \"@dazhicheng/utils\";\r\n\r\nexport type DebounceSettings = {\r\n /**\r\n * 前沿触发\r\n */\r\n leading?: boolean | undefined;\r\n /**\r\n * 最大等待时间\r\n */\r\n maxWait?: number | undefined;\r\n /**\r\n * 尾部触发\r\n */\r\n trailing?: boolean | undefined;\r\n};\r\n\r\ntype noop = (...args: any[]) => any;\r\n\r\n/**\r\n * 防抖函数 Hook 返回类型\r\n */\r\ntype UseDebounceFnReturn<T extends noop> = {\r\n /**\r\n * 防抖函数,保持原函数的完整签名\r\n */\r\n run: (...args: Parameters<T>) => ReturnType<T>;\r\n /**\r\n * 取消防抖\r\n */\r\n cancel: () => void;\r\n /**\r\n * 立即执行防抖函数,保持原函数的完整签名\r\n */\r\n flush: (...args: Parameters<T>) => ReturnType<T>;\r\n};\r\n\r\n/**\r\n * 防抖函数 Hook\r\n * @param fn 要防抖的函数\r\n * @param wait 延迟时间(毫秒)默认200\r\n * @param options 防抖选项\r\n * @returns 返回防抖后的函数和相关控制方法\r\n */\r\nexport function useDebounceFn<T extends noop>(\r\n fn: T,\r\n wait?: number,\r\n options?: DebounceSettings,\r\n): UseDebounceFnReturn<T> {\r\n const debounced = debounce(fn, wait ?? 200, options);\r\n return {\r\n /**\r\n * 防抖函数,保持原函数的返回类型\r\n * 如果原函数是异步的,返回 Promise;如果是同步的,返回原始类型\r\n */\r\n run: debounced as unknown as (...args: Parameters<T>) => ReturnType<T>,\r\n\r\n /**\r\n * 取消防抖\r\n */\r\n cancel: debounced.cancel,\r\n\r\n /**\r\n * 立即执行防抖函数,保持原函数的返回类型\r\n */\r\n flush: debounced.flush as unknown as (...args: Parameters<T>) => ReturnType<T>,\r\n };\r\n}\r\n","import type { Component, VNode } from \"vue\";\nimport type { MaybeRefOrGetter } from \"vue\";\nimport { getCurrentInstance, h, isVNode, nextTick, ref, render, toValue } from \"vue\";\nimport { isNumber } from \"@dazhicheng/utils\";\nimport { toBlob, toPng as htmlToPng, toJpeg as htmlToJpeg } from \"html-to-image\";\n\nexport type ScreenshotTarget = HTMLElement | null | undefined;\ntype CaptureFormat = \"png\" | \"jpeg\";\nexport type ComponentCaptureOutput = \"dataUrl\" | \"blob\";\n\n/**\n * 截图配置项\n */\nexport interface ElementScreenshotOptions {\n /**\n * 输出格式\n * 默认 png\n */\n format?: CaptureFormat;\n\n /**\n * jpeg 输出质量\n * 取值范围 0 到 1\n * 默认 1\n */\n quality?: number;\n\n /**\n * 渲染像素比\n * 默认 min(devicePixelRatio, 3)\n */\n pixelRatio?: number;\n\n /**\n * 背景色\n * 默认 #ffffff\n */\n backgroundColor?: string;\n\n /**\n * 资源防缓存参数\n * 默认 true\n */\n cacheBust?: boolean;\n\n /**\n * 是否跳过 document.fonts.ready\n * 默认 false\n */\n skipFontsReady?: boolean;\n\n /**\n * 截图前额外延迟毫秒数\n * 用于保证 loading 先渲染\n * 默认 16\n */\n deferCaptureMs?: number;\n /**\n * html-to-image 节点过滤(返回 false 的节点及其子树不会出现在截图中)\n */\n filter?: (domNode: HTMLElement) => boolean;\n}\n\n/**\n * 离屏挂载组件并截图的配置项\n */\nexport interface ComponentCaptureOptions extends ElementScreenshotOptions {\n /**\n * 传给组件的 props\n */\n props?: Record<string, unknown>;\n\n /**\n * 离屏容器行内样式\n * 若未设置 width,会与 width 选项合并\n */\n containerStyle?: string;\n\n /**\n * 离屏容器宽度(px)\n * 默认 document.documentElement.clientWidth(当前页面视口宽)\n */\n width: number;\n\n /**\n * 截图目标在离屏容器内的选择器\n * 未传则使用容器第一个子元素\n */\n selector?: string;\n\n /**\n * 输出类型\n * dataUrl 为 DataURL 字符串,blob 为 Blob\n * 默认 dataUrl\n */\n output?: ComponentCaptureOutput;\n\n /**\n * 组件挂载并完成 nextTick 后的钩子(如同步表单值)\n * @param container 离屏挂载根容器\n */\n beforeCapture?: (container: HTMLElement) => void | Promise<void>;\n}\n\nconst DEFAULT_OFFSCREEN_CONTAINER_STYLE = \"position:fixed;left:-10000px;top:0;pointer-events:none;z-index:-1;\";\n\n/**\n * 解析截图目标元素\n */\nfunction resolveTarget(target: MaybeRefOrGetter<ScreenshotTarget>): HTMLElement {\n const node = toValue(target);\n if (!node) {\n throw new Error(\"Target element not found\");\n }\n return node;\n}\n\n/**\n * 构建 html-to-image 渲染参数\n */\nfunction getRenderOptions(options?: ElementScreenshotOptions) {\n return {\n pixelRatio: options?.pixelRatio ?? Math.min(window.devicePixelRatio || 2, 3),\n backgroundColor: options?.backgroundColor ?? \"#ffffff\",\n cacheBust: options?.cacheBust ?? true,\n quality: options?.quality ?? 1,\n skipFontsReady: options?.skipFontsReady ?? false,\n filter: options?.filter,\n // includeStyleProperties: [\"hidden\"],\n };\n}\n\n/**\n * 截图前等待字体加载完成\n */\nasync function ensureFontsReady(skipFontsReady?: boolean) {\n if (skipFontsReady) return;\n if (typeof document === \"undefined\" || !(\"fonts\" in document)) return;\n await document.fonts.ready;\n}\n\n/**\n * 让出一帧,确保 DOM 绘制完成\n */\nasync function waitPaintFrame(deferCaptureMs = 16) {\n await nextTick();\n await new Promise<void>(resolve => requestAnimationFrame(() => resolve()));\n await new Promise<void>(resolve => requestAnimationFrame(() => resolve()));\n if (deferCaptureMs > 0) {\n await new Promise<void>(resolve => setTimeout(resolve, deferCaptureMs));\n }\n}\n\nfunction resolveOffscreenWidth(options?: ComponentCaptureOptions): string {\n const width = options?.width ?? document.documentElement.clientWidth;\n if (isNumber(options?.width) && width > 0) {\n return `${Math.round(width)}px`;\n }\n const viewportWidth = document.documentElement.clientWidth || window.innerWidth;\n return `${Math.round(viewportWidth)}px`;\n}\n\nfunction applyOffscreenContainerStyle(container: HTMLElement, options?: ComponentCaptureOptions) {\n container.style.cssText = options?.containerStyle ?? DEFAULT_OFFSCREEN_CONTAINER_STYLE;\n if (!options?.containerStyle || !/width\\s*:/i.test(options.containerStyle)) {\n container.style.width = resolveOffscreenWidth(options);\n }\n}\n\n/**\n * DOM 截图 Hook\n */\nexport function useElementScreenshot() {\n const isCapturing = ref(false);\n const lastError = ref<Error | null>(null);\n const fallbackAppContext = getCurrentInstance()?.appContext ?? null;\n\n /**\n * 对已有 DOM 节点执行 html-to-image 截图\n */\n async function renderTargetToResult(\n target: HTMLElement,\n options?: ElementScreenshotOptions & { output?: ComponentCaptureOutput },\n ): Promise<string | Blob> {\n await ensureFontsReady(options?.skipFontsReady);\n const renderOptions = getRenderOptions(options);\n const output = options?.output ?? \"dataUrl\";\n\n if (output === \"blob\") {\n const blob = await toBlob(target, renderOptions);\n if (!blob) {\n throw new Error(\"Failed to generate screenshot blob\");\n }\n return blob;\n }\n\n const format = options?.format ?? \"png\";\n if (format === \"jpeg\") {\n return await htmlToJpeg(target, renderOptions);\n }\n return await htmlToPng(target, renderOptions);\n }\n\n /**\n * 离屏挂载组件并解析截图目标\n */\n function resolveRenderVNode(component: Component | VNode, options?: ComponentCaptureOptions): VNode {\n const vnode = isVNode(component) ? component : h(component, options?.props);\n if (fallbackAppContext) {\n vnode.appContext = fallbackAppContext;\n }\n return vnode;\n }\n\n async function mountComponentOffscreen(component: Component | VNode, options?: ComponentCaptureOptions) {\n const host = document.body;\n const container = document.createElement(\"div\");\n applyOffscreenContainerStyle(container, options);\n host.appendChild(container);\n\n try {\n const vnode = resolveRenderVNode(component, options);\n render(vnode, container);\n await nextTick();\n await options?.beforeCapture?.(container);\n await nextTick();\n await waitPaintFrame(options?.deferCaptureMs ?? 16);\n const selector = options?.selector;\n const target = selector\n ? (container.querySelector(selector) as HTMLElement | null)\n : (container.firstChild as HTMLElement);\n if (!target) {\n throw new Error(`截图元素没有找到: ${selector}`);\n }\n return { container, target };\n } catch (error) {\n render(null, container);\n container.remove();\n throw error;\n }\n }\n\n function unmountOffscreen(container: HTMLElement) {\n render(null, container);\n container.remove();\n }\n\n async function renderToBaseBlob(\n target: MaybeRefOrGetter<ScreenshotTarget>,\n options?: ElementScreenshotOptions,\n ): Promise<Blob> {\n await waitPaintFrame(options?.deferCaptureMs ?? 16);\n const node = resolveTarget(target);\n const result = await renderTargetToResult(node, { ...options, output: \"blob\" });\n return result as Blob;\n }\n\n async function captureToBlob(\n target: MaybeRefOrGetter<ScreenshotTarget>,\n options?: ElementScreenshotOptions,\n ): Promise<Blob> {\n isCapturing.value = true;\n lastError.value = null;\n try {\n return await renderToBaseBlob(target, options);\n } catch (error) {\n const finalError = error instanceof Error ? error : new Error(String(error));\n lastError.value = finalError;\n throw finalError;\n } finally {\n isCapturing.value = false;\n }\n }\n\n async function captureToDataUrl(\n target: MaybeRefOrGetter<ScreenshotTarget>,\n options?: ElementScreenshotOptions,\n ): Promise<string> {\n isCapturing.value = true;\n lastError.value = null;\n try {\n await waitPaintFrame(options?.deferCaptureMs ?? 16);\n const node = resolveTarget(target);\n const result = await renderTargetToResult(node, { ...options, output: \"dataUrl\" });\n return result as string;\n } catch (error) {\n const finalError = error instanceof Error ? error : new Error(String(error));\n lastError.value = finalError;\n throw finalError;\n } finally {\n isCapturing.value = false;\n }\n }\n\n /**\n * 截取传入的组件\n */\n async function captureComponent(\n component: Component | VNode,\n options?: ComponentCaptureOptions & { output?: \"dataUrl\" },\n ): Promise<string>;\n async function captureComponent(\n component: Component | VNode,\n options: ComponentCaptureOptions & { output: \"blob\" },\n ): Promise<Blob>;\n async function captureComponent(\n component: Component | VNode,\n options?: ComponentCaptureOptions,\n ): Promise<string | Blob> {\n isCapturing.value = true;\n lastError.value = null;\n const { container, target } = await mountComponentOffscreen(component, options);\n try {\n return await renderTargetToResult(target, options);\n } catch (error) {\n const finalError = error instanceof Error ? error : new Error(String(error));\n lastError.value = finalError;\n throw finalError;\n } finally {\n unmountOffscreen(container);\n isCapturing.value = false;\n }\n }\n\n return {\n captureToBlob,\n captureToDataUrl,\n captureComponent,\n isCapturing,\n };\n}\n","import type { Ref } from \"vue\";\r\nimport { readonly, ref } from \"vue\";\r\nimport type { NotificationHandle } from \"element-plus\";\r\nimport { ElNotification } from \"element-plus\";\r\n\r\n/** 广播消息类型 */\r\ntype VersionMessage = \"reload\" | \"updateVersion\" | \"openVersion\" | \"closeVersion\";\r\n\r\n/** Hook 配置 */\r\nexport type UseVersionCheckOptions = {\r\n /**\r\n * 需要检测的子应用入口\r\n */\r\n appEntries?: string[];\r\n};\r\n\r\n/** Hook 返回值 */\r\nexport type UseVersionCheckReturn = {\r\n /**\r\n * 是否已检测到新版本\r\n */\r\n isUpdate: Readonly<Ref<boolean>>;\r\n /**\r\n * 是否展示更新提示\r\n */\r\n isShow: Readonly<Ref<boolean>>;\r\n /**\r\n * 设置需要检测的入口列表\r\n */\r\n setAppEntries: (appEntries: string[]) => void;\r\n /**\r\n * 执行版本检测\r\n */\r\n checkVersionUpdate: () => Promise<void>;\r\n /**\r\n * 刷新当前页,同时通知其他标签\r\n */\r\n reloadVersion: () => void;\r\n /**\r\n * 关闭更新提示\r\n */\r\n closeVersion: () => void;\r\n};\r\n\r\n/** 共享 Hook 实例 */\r\nlet versionCheckInstance: UseVersionCheckReturn | null = null;\r\n\r\n/** 是否支持 BroadcastChannel */\r\nfunction isBroadcastChannelSupported(): boolean {\r\n return typeof BroadcastChannel !== \"undefined\";\r\n}\r\n\r\n/** 规范化入口路径 */\r\nfunction normalizeEntry(entry: string): string {\r\n if (!entry || entry === \"/\") return \"/\";\r\n // 绝对地址(http(s)://...):去掉尾部斜杠后原样保留,用于跨源检测子应用\r\n if (/^https?:\\/\\//i.test(entry)) {\r\n return entry.replace(/\\/+$/, \"\");\r\n }\r\n const normalizedEntry = entry.startsWith(\"/\") ? entry : `/${entry}`;\r\n return normalizedEntry.replace(/\\/+$/, \"\") || \"/\";\r\n}\r\n\r\n/**\r\n * 版本更新检测 Hook\r\n * @param options Hook 配置\r\n * @returns 版本检测状态和操作方法\r\n */\r\nexport function useVersionCheck(options?: UseVersionCheckOptions): UseVersionCheckReturn {\r\n if (versionCheckInstance) {\r\n if (options?.appEntries?.length) {\r\n versionCheckInstance.setAppEntries(options.appEntries);\r\n }\r\n return versionCheckInstance;\r\n }\r\n\r\n /** 是否已检测到新版本 */\r\n const isUpdate = ref(false);\r\n\r\n /** 是否展示更新提示 */\r\n const isShow = ref(false);\r\n\r\n /** 是否正在执行版本检测 */\r\n const isChecking = ref(false);\r\n\r\n /** 当前更新提示实例 */\r\n let notificationHandle: NotificationHandle | null = null;\r\n\r\n /** 是否为程序化关闭提示(用于区分用户主动关闭) */\r\n let isProgrammaticClose = false;\r\n\r\n /** 各入口 index.html 缓存 */\r\n const cache = new Map<string, string>();\r\n\r\n /** 入口列表 */\r\n const entries = ref<string[]>([\"/\"]);\r\n\r\n /** 版本广播通道 */\r\n let channel: BroadcastChannel | null = null;\r\n\r\n /** 是否已绑定通道消息 */\r\n let isChannelBound = false;\r\n\r\n /** 获取版本广播通道 */\r\n function getChannel(): BroadcastChannel | null {\r\n if (!isBroadcastChannelSupported()) return null;\r\n if (channel) return channel;\r\n channel = new BroadcastChannel(`${location.origin}/version`);\r\n return channel;\r\n }\r\n\r\n /** 发送广播消息 */\r\n function postVersionMessage(message: VersionMessage): void {\r\n getChannel()?.postMessage(message);\r\n }\r\n\r\n /** 获取入口 index.html 请求地址 */\r\n function getEntryHtmlUrl(entry: string): string {\r\n const normalizedEntry = entry === \"/\" ? \"\" : entry;\r\n return `${normalizedEntry}/index.html?t=${Date.now()}`;\r\n }\r\n\r\n /** 弹出更新提示 */\r\n function showNotification(): void {\r\n if (notificationHandle) return;\r\n notificationHandle = ElNotification({\r\n title: \"版本更新\",\r\n message: \"新版本已发布,点击刷新获取最新内容\",\r\n type: \"info\",\r\n duration: 0,\r\n showClose: true,\r\n position: \"bottom-right\",\r\n onClick: () => reloadVersion(),\r\n onClose: () => {\r\n notificationHandle = null;\r\n // 程序化关闭无需重复处理,用户主动关闭(点 X)视为“稍后”\r\n if (isProgrammaticClose) {\r\n isProgrammaticClose = false;\r\n return;\r\n }\r\n closeVersion();\r\n },\r\n });\r\n }\r\n\r\n /** 关闭更新提示 */\r\n function hideNotification(): void {\r\n if (!notificationHandle) return;\r\n const handle = notificationHandle;\r\n notificationHandle = null;\r\n isProgrammaticClose = true;\r\n handle.close();\r\n }\r\n\r\n /** 按当前状态尝试弹出提示 */\r\n function tryShowNotification(): void {\r\n if (!isUpdate.value || !isShow.value) return;\r\n showNotification();\r\n }\r\n\r\n /** 处理广播消息 */\r\n function handleChannelMessage(event: MessageEvent<VersionMessage>): void {\r\n if (event.data === \"reload\") {\r\n location.reload();\r\n return;\r\n }\r\n\r\n if (event.data === \"updateVersion\") {\r\n isUpdate.value = true;\r\n return;\r\n }\r\n\r\n if (event.data === \"openVersion\") {\r\n isShow.value = true;\r\n tryShowNotification();\r\n return;\r\n }\r\n\r\n if (event.data === \"closeVersion\") {\r\n isShow.value = false;\r\n hideNotification();\r\n }\r\n }\r\n\r\n /** 绑定广播通道消息 */\r\n function ensureChannelListener(): void {\r\n const currentChannel = getChannel();\r\n if (!currentChannel || isChannelBound) return;\r\n currentChannel.onmessage = handleChannelMessage;\r\n isChannelBound = true;\r\n }\r\n\r\n /** 标记有新版本 */\r\n function updateVersion(): void {\r\n isUpdate.value = true;\r\n postVersionMessage(\"updateVersion\");\r\n }\r\n\r\n /** 打开更新提示 */\r\n function openVersion(): void {\r\n isShow.value = true;\r\n postVersionMessage(\"openVersion\");\r\n tryShowNotification();\r\n }\r\n\r\n /** 关闭更新提示 */\r\n function closeVersion(): void {\r\n isShow.value = false;\r\n hideNotification();\r\n postVersionMessage(\"closeVersion\");\r\n }\r\n\r\n /** 刷新当前页,同时通知其他标签 */\r\n function reloadVersion(): void {\r\n postVersionMessage(\"reload\");\r\n location.reload();\r\n }\r\n\r\n /** 设置需要检测的入口列表 */\r\n function setAppEntries(appEntries: string[]): void {\r\n const normalizedEntries = Array.from(new Set([\"/\", ...appEntries.map(entry => normalizeEntry(entry))]));\r\n entries.value = normalizedEntries;\r\n for (const entry of normalizedEntries) {\r\n if (!cache.has(entry)) {\r\n cache.set(entry, \"\");\r\n }\r\n }\r\n }\r\n\r\n /** 执行版本检测 */\r\n async function checkVersionUpdate(): Promise<void> {\r\n if (isChecking.value) return;\r\n\r\n ensureChannelListener();\r\n\r\n // 已检测到新版本:不再重复请求,仅在本次检测时重新提示(“稍后”关闭后下一次检测会再次提醒)\r\n if (isUpdate.value) {\r\n openVersion();\r\n return;\r\n }\r\n\r\n if (!entries.value.length) return;\r\n\r\n isChecking.value = true;\r\n\r\n try {\r\n const results = await Promise.allSettled(\r\n entries.value.map(async entry => {\r\n const response = await fetch(getEntryHtmlUrl(entry), {\r\n cache: \"no-store\",\r\n signal: AbortSignal.timeout(5000),\r\n });\r\n if (!response.ok) {\r\n throw new Error(`Request failed: ${response.status}`);\r\n }\r\n const html = await response.text();\r\n const oldHtml = cache.get(entry) || \"\";\r\n cache.set(entry, html);\r\n return oldHtml && html !== oldHtml ? entry : null;\r\n }),\r\n );\r\n\r\n // 失败入口告警:跨域子应用若未配置 CORS 响应头,请求会被浏览器拦截而静默失效,这里打印便于排查\r\n entries.value.forEach((entry, index) => {\r\n const result = results[index];\r\n if (result?.status === \"rejected\") {\r\n console.warn(`[useVersionCheck] 版本检测失败,请检查该入口是否已配置 CORS:${entry}`, result.reason);\r\n }\r\n });\r\n\r\n const updatedEntries = results\r\n .filter(result => result.status === \"fulfilled\" && result.value)\r\n .map(result => (result as PromiseFulfilledResult<string | null>).value)\r\n .filter(Boolean);\r\n\r\n if (updatedEntries.length) {\r\n updateVersion();\r\n openVersion();\r\n }\r\n } finally {\r\n isChecking.value = false;\r\n }\r\n }\r\n\r\n versionCheckInstance = {\r\n isUpdate: readonly(isUpdate),\r\n isShow: readonly(isShow),\r\n setAppEntries,\r\n checkVersionUpdate,\r\n reloadVersion,\r\n closeVersion,\r\n };\r\n\r\n if (options?.appEntries?.length) {\r\n versionCheckInstance.setAppEntries(options.appEntries);\r\n }\r\n\r\n return versionCheckInstance;\r\n}\r\n","import { useRoute } from \"vue-router\";\r\nimport type { RouteLocationNormalizedLoaded } from \"vue-router\";\r\n\r\n/** 按钮权限项 */\r\nexport type AuthItem = {\r\n authMark?: string;\r\n title?: string;\r\n [key: string]: any;\r\n};\r\n\r\n/** createUseAuth 注入配置(由各应用适配 store / 环境) */\r\nexport type CreateUseAuthOptions = {\r\n /** 是否前端权限模式 */\r\n isFrontendMode: () => boolean;\r\n /** 前端模式按钮权限列表,如 ['add', 'edit'] */\r\n getFrontendButtons: () => string[];\r\n /**\r\n * 扁平原始菜单(含 resourceType / permissionCode / parentId)\r\n * meta.authList 为空时用于重建\r\n */\r\n getOriginMenuList?: () => Array<Record<string, any>>;\r\n /**\r\n * 已转换菜单树(节点上带 authList 或 meta.authList)\r\n * 子应用常用;origin 不可用时的兜底\r\n */\r\n getMenuList?: () => Array<Record<string, any>>;\r\n};\r\n\r\nexport type UseAuthReturn = {\r\n hasAuth: (auth: string) => boolean;\r\n};\r\n\r\n/** permissionCode 取最后一段,兼容 `edit` / `Role:edit` / `sys:Role:edit` */\r\nexport function resolveAuthMark(permissionCode?: string): string {\r\n return permissionCode?.split(\":\").filter(Boolean).pop() || \"\";\r\n}\r\n\r\n/**\r\n * 从扁平原始菜单按当前页重建 authList\r\n */\r\nexport function buildAuthListFromOriginMenu(\r\n route: RouteLocationNormalizedLoaded,\r\n origin: Array<Record<string, any>>,\r\n): AuthItem[] {\r\n if (!origin?.length) return [];\r\n\r\n const permissionOnlyCode = route.meta.permissionOnlyCode as string | undefined;\r\n const routeName = String(route.name || \"\");\r\n const routePath = route.path;\r\n\r\n const pageMenu = origin.find(item => {\r\n if (String(item.resourceType) === \"2\") return false;\r\n if (permissionOnlyCode && item.permissionOnlyCode === permissionOnlyCode) return true;\r\n const nameFromCode = resolveAuthMark(item.permissionCode);\r\n if (routeName && nameFromCode && nameFromCode.toLowerCase() === routeName.toLowerCase()) {\r\n return true;\r\n }\r\n if (item.value && (routePath === item.value || routePath.endsWith(`/${item.value}`))) {\r\n return true;\r\n }\r\n return false;\r\n });\r\n\r\n if (!pageMenu) return [];\r\n\r\n return origin\r\n .filter(child => String(child.resourceType) === \"2\" && child.parentId == pageMenu.id)\r\n .map(child => ({\r\n authMark: resolveAuthMark(child.permissionCode),\r\n title: child.functionName,\r\n }));\r\n}\r\n\r\n/**\r\n * 从已转换菜单树查找当前页 authList\r\n */\r\nexport function findAuthListFromMenuTree(\r\n route: RouteLocationNormalizedLoaded,\r\n menus: Array<Record<string, any>>,\r\n): AuthItem[] {\r\n if (!menus?.length) return [];\r\n\r\n const permissionOnlyCode = route.meta.permissionOnlyCode as string | undefined;\r\n const routeName = String(route.name || \"\");\r\n const routePath = route.path;\r\n\r\n const matchNode = (item: Record<string, any>): boolean => {\r\n const meta = item.meta || {};\r\n if (permissionOnlyCode && (meta.permissionOnlyCode || item.permissionOnlyCode) === permissionOnlyCode) {\r\n return true;\r\n }\r\n if (routeName && String(item.name || \"\") === routeName) return true;\r\n const path = item.path || item.value || item.component;\r\n if (path && (routePath === path || routePath.endsWith(`/${path}`) || routePath.endsWith(String(path)))) {\r\n return true;\r\n }\r\n return false;\r\n };\r\n\r\n const walk = (list: Array<Record<string, any>>): AuthItem[] | null => {\r\n for (const item of list) {\r\n if (matchNode(item)) {\r\n const listFromItem = item.meta?.authList || item.authList;\r\n if (Array.isArray(listFromItem) && listFromItem.length > 0) {\r\n return listFromItem as AuthItem[];\r\n }\r\n }\r\n if (item.children?.length) {\r\n const found = walk(item.children);\r\n if (found) return found;\r\n }\r\n }\r\n return null;\r\n };\r\n\r\n return walk(menus) || [];\r\n}\r\n\r\n/**\r\n * 解析当前页按钮权限列表\r\n * 优先级:非空 meta.authList > matched > origin 重建 > 菜单树\r\n */\r\nexport function resolveBackendAuthList(\r\n route: RouteLocationNormalizedLoaded,\r\n options: Pick<CreateUseAuthOptions, \"getOriginMenuList\" | \"getMenuList\"> = {},\r\n): AuthItem[] {\r\n const fromMeta = route.meta.authList;\r\n if (Array.isArray(fromMeta) && fromMeta.length > 0) {\r\n return fromMeta as AuthItem[];\r\n }\r\n\r\n for (let i = route.matched.length - 1; i >= 0; i -= 1) {\r\n const list = route.matched[i]?.meta?.authList;\r\n if (Array.isArray(list) && list.length > 0) {\r\n return list as AuthItem[];\r\n }\r\n }\r\n\r\n if (options.getOriginMenuList) {\r\n const fromOrigin = buildAuthListFromOriginMenu(route, options.getOriginMenuList() || []);\r\n if (fromOrigin.length > 0) return fromOrigin;\r\n }\r\n\r\n if (options.getMenuList) {\r\n return findAuthListFromMenuTree(route, options.getMenuList() || []);\r\n }\r\n\r\n return [];\r\n}\r\n\r\n/**\r\n * 创建按钮权限 hook(各应用注入权限数据源)\r\n *\r\n * @example\r\n * ```ts\r\n * export const useAuth = createUseAuth({\r\n * isFrontendMode: () => import.meta.env.VITE_ACCESS_MODE === 'frontend',\r\n * getFrontendButtons: () => useUserStore().info?.buttons ?? [],\r\n * getOriginMenuList: () => useMenuStore().originMenuList,\r\n * })\r\n * ```\r\n */\r\nexport function createUseAuth(options: CreateUseAuthOptions): () => UseAuthReturn {\r\n return function useAuth(): UseAuthReturn {\r\n const route = useRoute();\r\n\r\n const hasAuth = (auth: string): boolean => {\r\n if (options.isFrontendMode()) {\r\n return (options.getFrontendButtons() || []).includes(auth);\r\n }\r\n\r\n const backendAuthList = resolveBackendAuthList(route, options);\r\n return backendAuthList.some(item => item?.authMark === auth);\r\n };\r\n\r\n return { hasAuth };\r\n };\r\n}\r\n"],"names":["propTypes","VueTypes","style","toType","type","String","Object","default","undefined","VNodeChild","useDesign","scope","values","prefixCls","prefixVar","usePriorityValue","key","props","state","instance","getCurrentInstance","slots","useSlots","attrs","useAttrs","computed","rawProps","vnode","standardRawProps","value","entries","kebabToCamelCase","propsKey","getFirstNonNullOrUndefined","usePriorityValues","result","keys","forEach","getPriorityValues","standardProps","toValue","isValidKey","object","deepCopy","target","map","Map","res","get","Array","isArray","set","item","index","args","str","split","filter","Boolean","word","charAt","toUpperCase","slice","join","useIsScroll","hasScrollbar","ref","update","elementRef","element","scrollHeight","clientHeight","useDebounceFn","fn","wait","options","debounced","debounce","run","cancel","flush","resolveTarget","node","Error","async","waitPaintFrame","deferCaptureMs","nextTick","Promise","resolve","requestAnimationFrame","setTimeout","applyOffscreenContainerStyle","container","cssText","containerStyle","test","width","document","documentElement","clientWidth","isNumber","Math","round","viewportWidth","window","innerWidth","resolveOffscreenWidth","useElementScreenshot","isCapturing","lastError","fallbackAppContext","appContext","renderTargetToResult","skipFontsReady","fonts","ready","ensureFontsReady","renderOptions","pixelRatio","min","devicePixelRatio","backgroundColor","cacheBust","quality","getRenderOptions","output","blob","toBlob","format","htmlToJpeg","htmlToPng","mountComponentOffscreen","component","host","body","createElement","appendChild","isVNode","h","resolveRenderVNode","render","beforeCapture","selector","querySelector","firstChild","error","remove","captureToBlob","renderToBaseBlob","finalError","captureToDataUrl","captureComponent","unmountOffscreen","versionCheckInstance","useVersionCheck","appEntries","length","setAppEntries","isUpdate","isShow","isChecking","notificationHandle","isProgrammaticClose","cache","channel","isChannelBound","getChannel","BroadcastChannel","location","origin","postVersionMessage","message","postMessage","hideNotification","handle","close","tryShowNotification","ElNotification","title","duration","showClose","position","onClick","reloadVersion","onClose","closeVersion","handleChannelMessage","event","data","reload","openVersion","readonly","normalizedEntries","from","Set","entry","replace","startsWith","normalizeEntry","has","checkVersionUpdate","currentChannel","onmessage","ensureChannelListener","results","allSettled","response","fetch","Date","now","getEntryHtmlUrl","signal","AbortSignal","timeout","ok","status","html","text","oldHtml","console","warn","reason","resolveAuthMark","permissionCode","pop","buildAuthListFromOriginMenu","route","permissionOnlyCode","meta","routeName","name","routePath","path","pageMenu","find","resourceType","nameFromCode","toLowerCase","endsWith","child","parentId","id","authMark","functionName","findAuthListFromMenuTree","menus","matchNode","walk","list","listFromItem","authList","children","found","resolveBackendAuthList","fromMeta","i","matched","getOriginMenuList","fromOrigin","getMenuList","createUseAuth","useRoute","hasAuth","auth","isFrontendMode","getFrontendButtons","includes","some"],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { RouteLocationNormalizedLoaded } from "vue-router";
2
+ /** 按钮权限项 */
3
+ export type AuthItem = {
4
+ authMark?: string;
5
+ title?: string;
6
+ [key: string]: any;
7
+ };
8
+ /** createUseAuth 注入配置(由各应用适配 store / 环境) */
9
+ export type CreateUseAuthOptions = {
10
+ /** 是否前端权限模式 */
11
+ isFrontendMode: () => boolean;
12
+ /** 前端模式按钮权限列表,如 ['add', 'edit'] */
13
+ getFrontendButtons: () => string[];
14
+ /**
15
+ * 扁平原始菜单(含 resourceType / permissionCode / parentId)
16
+ * meta.authList 为空时用于重建
17
+ */
18
+ getOriginMenuList?: () => Array<Record<string, any>>;
19
+ /**
20
+ * 已转换菜单树(节点上带 authList 或 meta.authList)
21
+ * 子应用常用;origin 不可用时的兜底
22
+ */
23
+ getMenuList?: () => Array<Record<string, any>>;
24
+ };
25
+ export type UseAuthReturn = {
26
+ hasAuth: (auth: string) => boolean;
27
+ };
28
+ /** permissionCode 取最后一段,兼容 `edit` / `Role:edit` / `sys:Role:edit` */
29
+ export declare function resolveAuthMark(permissionCode?: string): string;
30
+ /**
31
+ * 从扁平原始菜单按当前页重建 authList
32
+ */
33
+ export declare function buildAuthListFromOriginMenu(route: RouteLocationNormalizedLoaded, origin: Array<Record<string, any>>): AuthItem[];
34
+ /**
35
+ * 从已转换菜单树查找当前页 authList
36
+ */
37
+ export declare function findAuthListFromMenuTree(route: RouteLocationNormalizedLoaded, menus: Array<Record<string, any>>): AuthItem[];
38
+ /**
39
+ * 解析当前页按钮权限列表
40
+ * 优先级:非空 meta.authList > matched > origin 重建 > 菜单树
41
+ */
42
+ export declare function resolveBackendAuthList(route: RouteLocationNormalizedLoaded, options?: Pick<CreateUseAuthOptions, "getOriginMenuList" | "getMenuList">): AuthItem[];
43
+ /**
44
+ * 创建按钮权限 hook(各应用注入权限数据源)
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * export const useAuth = createUseAuth({
49
+ * isFrontendMode: () => import.meta.env.VITE_ACCESS_MODE === 'frontend',
50
+ * getFrontendButtons: () => useUserStore().info?.buttons ?? [],
51
+ * getOriginMenuList: () => useMenuStore().originMenuList,
52
+ * })
53
+ * ```
54
+ */
55
+ export declare function createUseAuth(options: CreateUseAuthOptions): () => UseAuthReturn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dazhicheng/hooks",
3
- "version": "1.4.50",
3
+ "version": "1.4.52",
4
4
  "description": "",
5
5
  "main": "./dist/index.esm.js",
6
6
  "module": "./dist/index.esm.js",
@@ -26,8 +26,10 @@
26
26
  "html-to-image": "^1.11.13"
27
27
  },
28
28
  "peerDependencies": {
29
+ "vue": ">=3.3.0",
30
+ "vue-router": ">=4.0.0",
29
31
  "element-plus": "^2.13.7",
30
- "@dazhicheng/utils": "1.3.47"
32
+ "@dazhicheng/utils": "1.3.49"
31
33
  },
32
34
  "scripts": {
33
35
  "build": "rimraf dist && node ../../scripts/build.js hooks",