@qxs-bns/components 0.0.75 → 0.0.77

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.
@@ -1,2 +1,2 @@
1
- var r="0.0.75";export{r as version};
1
+ var r="0.0.77";export{r as version};
2
2
  //# sourceMappingURL=package.json.mjs.map
@@ -1,2 +1,2 @@
1
- import{defineComponent as e,useAttrs as t,computed as o,createBlock as r,createElementBlock as i,createCommentVNode as l,openBlock as n,resolveDynamicComponent as s,normalizeStyle as u,normalizeClass as a,unref as c,createVNode as p,createElementVNode as d}from"vue";import{Icon as f}from"../../../node_modules/.pnpm/@iconify_vue@5.0.0_vue@3.5.18_typescript@5.9.2_/node_modules/@iconify/vue/dist/iconify.mjs";import{useNamespace as v}from"@qxs-bns/hooks";const y=["xlink:href"];var m=e({name:"QxsIcon",__name:"icon",props:{icon:{type:null,required:!0},runtime:{type:Boolean,required:!1},flip:{type:null,required:!1,default:""},rotate:{type:Number,required:!1,default:0},color:{type:String,required:!1},size:{type:[String,Number],required:!1},localIconPrefix:{type:String,required:!1,default:"icon-"}},setup(e){const m=t(),g=v("icon"),h=o(()=>"object"==typeof e.icon||"function"==typeof e.icon),b=o(()=>h.value?"component":0===e.icon.indexOf("i-")?e.runtime?"svg":"css":e.icon.includes(":")?"svg":"custom"),$=o(()=>{if(h.value)return"";if(0===e.icon.indexOf("i-")){let t=e.icon;return e.runtime&&(t=t.replace("i-","")),t}return e.icon});const x=o(()=>{const t=[];switch(e.flip){case"horizontal":t.push("rotateY(180deg)");break;case"vertical":t.push("rotateX(180deg)");break;case"both":t.push("rotateX(180deg)"),t.push("rotateY(180deg)")}return e.rotate&&t.push(`rotate(${e.rotate%360}deg)`),`${e.color?`color: ${e.color};`:""}${e.size?`font-size: ${o=e.size,o?"number"==typeof o||/^\d+(?:\.\d+)?$/.test(o)?`${o}px`:o:""};`:""}${t.length?`transform: ${t.join(" ")};`:""}`;var o});return(e,t)=>"component"===b.value?(n(),r(s(e.icon),{key:0,class:a([c(g).b()]),style:u(x.value+(c(m).style||""))},null,8,["class","style"])):"css"===b.value&&$.value?(n(),i("i",{key:1,class:a([c(g).b(),$.value]),style:u(x.value+(c(m).style||""))},null,6)):"svg"===b.value&&$.value?(n(),i("i",{key:2,style:u(x.value+(c(m).style||"")),class:a([c(g).b()])},[p(c(f),{icon:$.value},null,8,["icon"])],6)):$.value?(n(),i("svg",{key:3,"aria-hidden":"true",class:a([c(g).b()]),style:u(x.value+(c(m).style||""))},[d("use",{"xlink:href":`#${e.localIconPrefix}${$.value}`},null,8,y)],6)):l("v-if",!0)}});export{m as default};
1
+ import{defineComponent as e,useAttrs as i,computed as t,ref as n,createElementBlock as s,openBlock as l,Fragment as r,createCommentVNode as o,createBlock as u,resolveDynamicComponent as a,normalizeStyle as c,normalizeClass as f,unref as p,createElementVNode as y,createVNode as v}from"vue";import{Icon as d}from"../../../node_modules/.pnpm/@iconify_vue@5.0.0_vue@3.5.18_typescript@5.9.2_/node_modules/@iconify/vue/dist/iconify.mjs";import{useNamespace as h}from"@qxs-bns/hooks";const k=["src"],m=["xlink:href"];var g=e({name:"QxsIcon",__name:"icon",props:{icon:{type:null,required:!0},flip:{type:null,required:!1,default:""},rotate:{type:Number,required:!1,default:0},color:{type:String,required:!1},size:{type:[String,Number],required:!1},localIconPrefix:{type:String,required:!1,default:"icon-"},fallback:{type:String,required:!1},loading:{type:String,required:!1}},setup(e){const g=i(),b=h("icon"),$=t(()=>"object"==typeof e.icon||"function"==typeof e.icon),z=t(()=>{if($.value)return"component";const i=e.icon;return/^https?:\/\//.test(i)||(/^\.{1,2}\//.test(t=i)||t.startsWith("/")||t.includes("/"))?"img":/^i-[^:]+:[^:]+/.test(i)?"unocss":0===i.indexOf("i-")?"css":i.includes(":")?"iconify":"svg";var t}),S=t(()=>{if($.value)return"";const i=e.icon;return"img"===z.value||"unocss"===z.value||0===i.indexOf("i-")&&z.value,i});const x=t(()=>{const i=[];switch(e.flip){case"horizontal":i.push("rotateY(180deg)");break;case"vertical":i.push("rotateX(180deg)");break;case"both":i.push("rotateX(180deg)"),i.push("rotateY(180deg)")}e.rotate&&i.push(`rotate(${e.rotate%360}deg)`);let t=`${e.color?`color: ${e.color};`:""}${e.size?`font-size: ${n=e.size,n?"number"==typeof n||/^\d+(?:\.\d+)?$/.test(n)?`${n}px`:n:""};`:""}${i.length?`transform: ${i.join(" ")};`:""}`;var n;const s=g.style;if(s)if("string"==typeof s)t+=s;else if("object"==typeof s){t+=Object.entries(s).map(([e,i])=>`${e.replace(/([A-Z])/g,"-$1").toLowerCase()}: ${i};`).join("")}return t}),q=n(!0),_=n(!1);function j(){q.value=!1,_.value=!1}function I(){q.value=!1,_.value=!0}return(e,i)=>(l(),s(r,null,[o(" Vue组件 "),"component"===z.value?(l(),u(a(e.icon),{key:0,class:f([[p(b).b()],"size-inherit shrink-0"]),style:c([x.value])},null,8,["class","style"])):"unocss"===z.value?(l(),s(r,{key:1},[o(" UnoCSS图标 "),y("i",{class:f([[p(b).b()],"size-inherit shrink-0"]),style:c([x.value])},null,6)],2112)):"css"===z.value?(l(),s(r,{key:2},[o(" CSS类图标 "),y("i",{class:f(["size-inherit shrink-0",[p(b).b()]]),style:c([x.value])},null,6)],2112)):"iconify"===z.value?(l(),s(r,{key:3},[o(" Iconify图标 "),v(p(d),{class:f(["size-inherit shrink-0",[p(b).b()]]),icon:S.value,style:c([x.value])},null,8,["icon","class","style"])],2112)):"img"===z.value?(l(),s(r,{key:4},[o(" 图片 "),o(" 加载中状态 "),q.value&&e.loading?(l(),s("i",{key:0,class:f(["size-inherit shrink-0",e.loading])},null,2)):_.value&&e.fallback?(l(),s(r,{key:1},[o(" 错误状态 "),y("i",{class:f(["size-inherit shrink-0",e.fallback])},null,2)],2112)):(l(),s(r,{key:2},[o(" 图片本体 "),y("img",{src:S.value,class:f(["size-inherit shrink-0",[p(b).b()]]),style:c([x.value]),onLoad:j,onError:I},null,46,k)],2112))],64)):S.value?(l(),s(r,{key:5},[o(" SVG Sprite "),(l(),s("svg",{"aria-hidden":"true",class:f([[p(b).b()],"size-inherit shrink-0"]),style:c([x.value])},[y("use",{"xlink:href":`#${e.localIconPrefix}${S.value}`},null,8,m)],6))],2112)):o("v-if",!0)],2112))}});export{g as default};
2
2
  //# sourceMappingURL=icon.vue.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"icon.vue.mjs","sources":["../../../../../../packages/components/src/icon/src/icon.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Component } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport { useNamespace } from '@qxs-bns/hooks'\nimport { computed, useAttrs } from 'vue'\n\ndefineOptions({\n name: 'QxsIcon',\n})\n\nconst {\n icon,\n flip = '',\n rotate = 0,\n runtime,\n color,\n size,\n localIconPrefix = 'icon-',\n} = defineProps<PropsType>()\n\nconst attrs = useAttrs()\n\nconst ns = useNamespace('icon')\n\nexport interface PropsType {\n icon: string | Component\n runtime?: boolean\n flip?: 'horizontal' | 'vertical' | 'both' | '' | undefined\n rotate?: number\n color?: string\n size?: string | number\n localIconPrefix?: string\n}\n\nconst isComponentName = computed(() => typeof icon === 'object' || typeof icon === 'function')\n\nconst outputType = computed(() => {\n if (isComponentName.value) {\n return 'component'\n }\n if ((icon as string).indexOf('i-') === 0) {\n return runtime ? 'svg' : 'css'\n }\n else if ((icon as string).includes(':')) {\n return 'svg'\n }\n else {\n return 'custom'\n }\n})\n\nconst outputName = computed(() => {\n if (isComponentName.value) {\n return ''\n }\n if ((icon as string).indexOf('i-') === 0) {\n let conversionName = icon as string\n if (runtime) {\n conversionName = conversionName.replace('i-', '')\n }\n return conversionName\n }\n else {\n return icon as string\n }\n})\n\n// 用正则匹配 size 是不是 number 值,再判断是否有 px 结尾的单位,没有则拼接\nfunction formatSize(size: string | number | undefined): string {\n if (!size) {\n return ''\n }\n\n // 如果是数字,直接添加 px\n if (typeof size === 'number') {\n return `${size}px`\n }\n\n // 如果是纯数字字符串,添加 px\n if (/^\\d+(?:\\.\\d+)?$/.test(size)) {\n return `${size}px`\n }\n\n // 如果已经有单位(px、em、rem等)或者不是纯数字,直接返回\n return size\n}\n\n// 统一的样式计算属性,适用于所有图标类型\n// 包含颜色、尺寸和变换(旋转、翻转)\n// Iconify 图标通过外层 i 标签应用这些样式\nconst style = computed(() => {\n const transform = [] as string[]\n switch (flip) {\n case 'horizontal':\n transform.push('rotateY(180deg)')\n break\n case 'vertical':\n transform.push('rotateX(180deg)')\n break\n case 'both':\n transform.push('rotateX(180deg)')\n transform.push('rotateY(180deg)')\n break\n // 对于 '' 和 undefined,不做任何处理\n }\n if (rotate) {\n transform.push(`rotate(${rotate % 360}deg)`)\n }\n return `${color ? `color: ${color};` : ''}${size ? `font-size: ${formatSize(size)};` : ''}${transform.length ? `transform: ${transform.join(' ')};` : ''}`\n})\n</script>\n\n<template>\n <component\n :is=\"icon as any\"\n v-if=\"outputType === 'component'\"\n :class=\"[ns.b()]\"\n :style=\"style + (attrs.style || '')\"\n />\n <i\n v-else-if=\"outputType === 'css' && outputName\"\n :class=\"[ns.b(), outputName]\"\n :style=\"style + (attrs.style || '')\"\n />\n <i\n v-else-if=\"outputType === 'svg' && outputName\"\n :style=\"style + (attrs.style || '')\"\n :class=\"[ns.b()]\"\n >\n <Icon\n :icon=\"outputName\"\n />\n </i>\n <svg\n v-else-if=\"outputName\"\n aria-hidden=\"true\"\n :class=\"[ns.b()]\"\n :style=\"style + (attrs.style || '')\"\n >\n <use :xlink:href=\"`#${localIconPrefix}${outputName}`\" />\n </svg>\n</template>\n"],"names":["attrs","useAttrs","ns","useNamespace","isComponentName","computed","__props","icon","outputType","value","indexOf","runtime","includes","outputName","conversionName","replace","style","transform","flip","push","rotate","color","size","test","length","join","_openBlock","_createBlock","_resolveDynamicComponent","class","_normalizeClass","_unref","b","_normalizeStyle","_createElementBlock","_createVNode","Icon","_createElementVNode","localIconPrefix"],"mappings":"0yBAoBA,MAAMA,EAAQC,IAERC,EAAKC,EAAa,QAYlBC,EAAkBC,EAAS,IAAsB,iBAATC,EAAAC,MAAqC,mBAATD,EAAAC,MAEpEC,EAAaH,EAAS,IACtBD,EAAgBK,MACX,YAE8B,IAAlCH,EAAAC,KAAgBG,QAAQ,MACpBJ,EAAAK,QAAU,MAAQ,MAEjBL,EAAAC,KAAgBK,SAAS,KAC1B,MAGA,UAILC,EAAaR,EAAS,KAC1B,GAAID,EAAgBK,MAClB,MAAO,GAET,GAAuC,IAAlCH,EAAAC,KAAgBG,QAAQ,MAAa,CACxC,IAAII,EAAiBR,EAAAC,KAIrB,OAHID,EAAAK,UACFG,EAAiBA,EAAeC,QAAQ,KAAM,KAEzCD,CACT,CAEE,OAAOR,EAAAC,OA2BX,MAAMS,EAAQX,EAAS,KACrB,MAAMY,EAAY,GAClB,OAAQX,EAAAY,MACN,IAAK,aACHD,EAAUE,KAAK,mBACf,MACF,IAAK,WACHF,EAAUE,KAAK,mBACf,MACF,IAAK,OACHF,EAAUE,KAAK,mBACfF,EAAUE,KAAK,mBAOnB,OAHIb,EAAAc,QACFH,EAAUE,KAAK,UAAUb,SAAS,WAE7B,GAAGA,EAAAe,MAAQ,UAAUf,EAAAe,SAAW,KAAKf,EAAAgB,KAAO,cAxCjCA,EAwC0DhB,OAvCvEgB,EAKe,iBAATA,GAKP,kBAAkBC,KAAKD,GAJlB,GAAGA,MASLA,EAdE,MAsC8E,KAAKL,EAAUO,OAAS,cAAcP,EAAUQ,KAAK,QAAU,KAxCxJ,IAAoBH,iBA+CA,cAAVd,EAAAC,OAFRiB,IAAAC,EAKEC,EAJKrB,EAAAA,MAAI,OAERsB,MAAKC,EAAA,CAAGC,EAAA7B,GAAG8B,MACXhB,MAAKiB,EAAEjB,EAAAP,OAASsB,EAAA/B,GAAMgB,OAAK,yCAGjBR,EAAAC,OAAwBI,EAAAJ,WADrCyB,EAIE,IAAA,OAFCL,MAAKC,EAAA,CAAGC,EAAA7B,GAAG8B,IAAKnB,EAAAJ,QAChBO,MAAKiB,EAAEjB,EAAAP,OAASsB,EAAA/B,GAAMgB,OAAK,uBAGjBR,EAAAC,OAAwBI,EAAAJ,WADrCyB,EAQI,IAAA,OANDlB,MAAKiB,EAAEjB,EAAAP,OAASsB,EAAA/B,GAAMgB,OAAK,KAC3Ba,MAAKC,EAAA,CAAGC,EAAA7B,GAAG8B,QAEZG,EAEEJ,EAAAK,GAAA,CADC7B,KAAMM,EAAAJ,OAAU,KAAA,EAAA,CAAA,cAIRI,EAAAJ,WADbyB,EAOM,MAAA,OALJ,cAAY,OACXL,MAAKC,EAAA,CAAGC,EAAA7B,GAAG8B,MACXhB,MAAKiB,EAAEjB,EAAAP,OAASsB,EAAA/B,GAAMgB,OAAK,OAE5BqB,EAAwD,MAAA,CAAlD,aAAU,IAAMC,EAAAA,kBAAkBzB,EAAAJ"}
1
+ {"version":3,"file":"icon.vue.mjs","sources":["../../../../../../packages/components/src/icon/src/icon.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Component, CSSProperties } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport { useNamespace } from '@qxs-bns/hooks'\nimport { computed, ref, useAttrs } from 'vue'\n\ndefineOptions({\n name: 'QxsIcon',\n})\n\nconst {\n icon,\n flip = '',\n rotate = 0,\n color,\n size,\n localIconPrefix = 'icon-',\n fallback,\n loading,\n} = defineProps<PropsType>()\n\nconst attrs = useAttrs()\n\nconst ns = useNamespace('icon')\n\nexport interface PropsType {\n icon: string | Component\n flip?: 'horizontal' | 'vertical' | 'both' | '' | undefined\n rotate?: number\n color?: string\n size?: string | number\n localIconPrefix?: string\n // 新增功能:支持图片URL\n fallback?: string // 加载失败时的备用图标\n loading?: string // 加载中显示的图标\n}\n\nconst isComponentName = computed(() => typeof icon === 'object' || typeof icon === 'function')\n\nconst outputType = computed(() => {\n if (isComponentName.value) {\n return 'component'\n }\n\n const iconStr = icon as string\n\n // 检测是否为图片URL或路径\n const hasPathFeatures = (str: string) => {\n return /^\\.{1,2}\\//.test(str) || str.startsWith('/') || str.includes('/')\n }\n if (/^https?:\\/\\//.test(iconStr) || hasPathFeatures(iconStr)) {\n return 'img'\n }\n\n // 检测UnoCSS图标格式 (i-[provider]:[name])\n if (/^i-[^:]+:[^:]+/.test(iconStr)) {\n return 'unocss'\n }\n\n // 检测传统的i-开头的CSS图标\n if (iconStr.indexOf('i-') === 0) {\n return 'css'\n }\n\n // 检测Iconify格式 (provider:name)\n if (iconStr.includes(':')) {\n return 'iconify'\n }\n\n // 默认为SVG sprite\n return 'svg'\n})\n\nconst outputName = computed(() => {\n if (isComponentName.value) {\n return ''\n }\n\n const iconStr = icon as string\n\n // 对于图片类型,直接返回URL\n if (outputType.value === 'img') {\n return iconStr\n }\n\n // 对于UnoCSS图标,直接返回类名\n if (outputType.value === 'unocss') {\n return iconStr\n }\n\n // 对于传统CSS图标\n if (iconStr.indexOf('i-') === 0 && outputType.value === 'css') {\n return iconStr\n }\n\n // 其他情况直接返回\n return iconStr\n})\n\n// 用正则匹配 size 是不是 number 值,再判断是否有 px 结尾的单位,没有则拼接\nfunction formatSize(size: string | number | undefined): string {\n if (!size) {\n return ''\n }\n\n // 如果是数字,直接添加 px\n if (typeof size === 'number') {\n return `${size}px`\n }\n\n // 如果是纯数字字符串,添加 px\n if (/^\\d+(?:\\.\\d+)?$/.test(size)) {\n return `${size}px`\n }\n\n // 如果已经有单位(px、em、rem等)或者不是纯数字,直接返回\n return size\n}\n\n// 统一的样式计算属性,适用于所有图标类型\n// 包含颜色、尺寸和变换(旋转、翻转)\n// Iconify 图标通过外层 i 标签应用这些样式\nconst style = computed(() => {\n const transform = [] as string[]\n switch (flip) {\n case 'horizontal':\n transform.push('rotateY(180deg)')\n break\n case 'vertical':\n transform.push('rotateX(180deg)')\n break\n case 'both':\n transform.push('rotateX(180deg)')\n transform.push('rotateY(180deg)')\n break\n // 对于 '' 和 undefined,不做任何处理\n }\n if (rotate) {\n transform.push(`rotate(${rotate % 360}deg)`)\n }\n\n // 构建基础样式\n let baseStyle = `${color ? `color: ${color};` : ''}${size ? `font-size: ${formatSize(size)};` : ''}${transform.length ? `transform: ${transform.join(' ')};` : ''}`\n\n // 拼接 attrs.style\n const attrsStyle = attrs.style\n if (attrsStyle) {\n if (typeof attrsStyle === 'string') {\n baseStyle += attrsStyle\n }\n else if (typeof attrsStyle === 'object') {\n // 处理对象形式的样式\n const styleEntries = Object.entries(attrsStyle as CSSProperties)\n const styleString = styleEntries\n .map(([key, value]) => `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`)\n .join('')\n baseStyle += styleString\n }\n }\n\n return baseStyle\n})\n\n// 图片加载状态管理\nconst imageLoading = ref(true)\nconst imageError = ref(false)\n\nfunction handleImageLoad() {\n imageLoading.value = false\n imageError.value = false\n}\n\nfunction handleImageError() {\n imageLoading.value = false\n imageError.value = true\n}\n</script>\n\n<template>\n <!-- Vue组件 -->\n <component\n :is=\"icon\"\n v-if=\"outputType === 'component'\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n class=\"size-inherit shrink-0\"\n />\n\n <!-- UnoCSS图标 -->\n <i\n v-else-if=\"outputType === 'unocss'\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n class=\"size-inherit shrink-0\"\n />\n\n <!-- CSS类图标 -->\n <i\n v-else-if=\"outputType === 'css'\"\n class=\"size-inherit shrink-0\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n />\n\n <!-- Iconify图标 -->\n <Icon\n v-else-if=\"outputType === 'iconify'\"\n class=\"size-inherit shrink-0\"\n :icon=\"outputName\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n />\n\n <!-- 图片 -->\n <template v-else-if=\"outputType === 'img'\">\n <!-- 加载中状态 -->\n <i\n v-if=\"imageLoading && loading\"\n class=\"size-inherit shrink-0\"\n :class=\"loading\"\n />\n <!-- 错误状态 -->\n <i\n v-else-if=\"imageError && fallback\"\n class=\"size-inherit shrink-0\"\n :class=\"fallback\"\n />\n <!-- 图片本体 -->\n <img\n v-else\n :src=\"outputName\"\n class=\"size-inherit shrink-0\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n @load=\"handleImageLoad\"\n @error=\"handleImageError\"\n >\n </template>\n\n <!-- SVG Sprite -->\n <svg\n v-else-if=\"outputName\"\n aria-hidden=\"true\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n class=\"size-inherit shrink-0\"\n >\n <use :xlink:href=\"`#${localIconPrefix}${outputName}`\" />\n </svg>\n</template>\n"],"names":["attrs","useAttrs","ns","useNamespace","isComponentName","computed","__props","icon","outputType","value","iconStr","test","str","startsWith","includes","indexOf","outputName","style","transform","flip","push","rotate","baseStyle","color","size","length","join","attrsStyle","Object","entries","map","key","replace","toLowerCase","imageLoading","ref","imageError","handleImageLoad","handleImageError","_createCommentVNode","_openBlock","_createBlock","_resolveDynamicComponent","class","_normalizeClass","_unref","b","_createElementBlock","_Fragment","_createElementVNode","_createVNode","Icon","loading","fallback","src","onLoad","onError","localIconPrefix"],"mappings":"62BAqBA,MAAMA,EAAQC,IAERC,EAAKC,EAAa,QAclBC,EAAkBC,EAAS,IAAsB,iBAATC,EAAAC,MAAqC,mBAATD,EAAAC,MAEpEC,EAAaH,EAAS,KAC1B,GAAID,EAAgBK,MAClB,MAAO,YAGT,MAAMC,EAAUJ,EAAAC,KAMhB,MAAI,eAAeI,KAAKD,KAFf,aAAaC,KADGC,EAG2BF,IAFjBE,EAAIC,WAAW,MAAQD,EAAIE,SAAS,MAG9D,MAIL,iBAAiBH,KAAKD,GACjB,SAIqB,IAA1BA,EAAQK,QAAQ,MACX,MAILL,EAAQI,SAAS,KACZ,UAIF,MAvBiB,IAACF,IA0BrBI,EAAaX,EAAS,KAC1B,GAAID,EAAgBK,MAClB,MAAO,GAGT,MAAMC,EAAUJ,EAAAC,KAGhB,MAAyB,QAArBC,EAAWC,OAKU,WAArBD,EAAWC,OAKe,IAA1BC,EAAQK,QAAQ,OAAeP,EAAWC,MATrCC,IAwCX,MAAMO,EAAQZ,EAAS,KACrB,MAAMa,EAAY,GAClB,OAAQZ,EAAAa,MACN,IAAK,aACHD,EAAUE,KAAK,mBACf,MACF,IAAK,WACHF,EAAUE,KAAK,mBACf,MACF,IAAK,OACHF,EAAUE,KAAK,mBACfF,EAAUE,KAAK,mBAIfd,EAAAe,QACFH,EAAUE,KAAK,UAAUd,SAAS,WAIpC,IAAIgB,EAAY,GAAGhB,EAAAiB,MAAQ,UAAUjB,EAAAiB,SAAW,KAAKjB,EAAAkB,KAAO,cA1C1CA,EA0CmElB,EAAAkB,KAzChFA,EAKe,iBAATA,GAKP,kBAAkBb,KAAKa,GAJlB,GAAGA,MASLA,EAdE,MAwCuF,KAAKN,EAAUO,OAAS,cAAcP,EAAUQ,KAAK,QAAU,KA1CjK,IAAoBF,EA6ClB,MAAMG,EAAa3B,EAAMiB,MACzB,GAAIU,EACF,GAA0B,iBAAfA,EACTL,GAAaK,OACf,GAC+B,iBAAfA,EAAyB,CAMvCL,GAJqBM,OAAOC,QAAQF,GAEjCG,IAAI,EAAEC,EAAKtB,KAAW,GAAGsB,EAAIC,QAAQ,WAAY,OAAOC,kBAAkBxB,MAC1EiB,KAAK,GAEV,CAGF,OAAOJ,IAIHY,EAAeC,GAAI,GACnBC,EAAaD,GAAI,GAEvB,SAASE,IACPH,EAAazB,OAAQ,EACrB2B,EAAW3B,OAAQ,CACrB,CAEA,SAAS6B,IACPJ,EAAazB,OAAQ,EACrB2B,EAAW3B,OAAQ,CACrB,6BAIE8B,EAAA,WAGkB,cAAV/B,EAAAC,OAFR+B,IAAAC,EAMEC,EALKnC,EAAAA,MAAI,OAERoC,MAAKC,EAAA,CAAA,CAAGC,EAAA3C,GAAG4C,KAEN,0BADL7B,SAAQA,EAAAR,oCAMY,WAAVD,EAAAC,WADbsC,EAKEC,EAAA,CAAAjB,IAAA,GAAA,CANFQ,EAAA,cACAU,EAKE,IAAA,CAHCN,MAAKC,EAAA,CAAA,CAAGC,EAAA3C,GAAG4C,KAEN,0BADL7B,SAAQA,EAAAR,yBAMY,QAAVD,EAAAC,WADbsC,EAKEC,EAAA,CAAAjB,IAAA,GAAA,CANFQ,EAAA,YACAU,EAKE,IAAA,CAHAN,MAAKC,EAAA,CAAC,wBAAuB,CACpBC,EAAA3C,GAAG4C,OACX7B,SAAQA,EAAAR,yBAKY,YAAVD,EAAAC,WADbsC,EAMEC,EAAA,CAAAjB,IAAA,GAAA,CAPFQ,EAAA,eACAW,EAMEL,EAAAM,GAAA,CAJAR,MAAKC,EAAA,CAAC,wBAAuB,CAEpBC,EAAA3C,GAAG4C,OADXvC,KAAMS,EAAAP,MAENQ,SAAQA,EAAAR,kDAIoB,QAAVD,EAAAC,WAArBsC,EAuBWC,EAAA,CAAAjB,IAAA,GAAA,CAxBXQ,EAAA,QAEEA,EAAA,WAEQL,EAAAzB,OAAgB2C,EAAAA,aADxBL,EAIE,IAAA,OAFAJ,MAAKC,EAAA,CAAC,wBACEQ,EAAAA,oBAIGhB,EAAA3B,OAAc4C,EAAAA,cAD3BN,EAIEC,EAAA,CAAAjB,IAAA,GAAA,CALFQ,EAAA,UACAU,EAIE,IAAA,CAFAN,MAAKC,EAAA,CAAC,wBACES,EAAAA,iCAGVN,EAQCC,EAAA,CAAAjB,IAAA,GAAA,CATDQ,EAAA,UACAU,EAQC,MAAA,CANEK,IAAKtC,EAAAP,MACNkC,MAAKC,EAAA,CAAC,wBAAuB,CACpBC,EAAA3C,GAAG4C,OACX7B,SAAQA,EAAAR,QACR8C,OAAMlB,EACNmB,QAAOlB,4BAMCtB,EAAAP,WADbsC,EAQMC,EAAA,CAAAjB,IAAA,GAAA,CATNQ,EAAA,qBACAQ,EAQM,MAAA,CANJ,cAAY,OACXJ,MAAKC,EAAA,CAAA,CAAGC,EAAA3C,GAAG4C,KAEN,0BADL7B,SAAQA,EAAAR,UAGTwC,EAAwD,MAAA,CAAlD,aAAU,IAAMQ,EAAAA,kBAAkBzC,EAAAP"}
@@ -1,2 +1,2 @@
1
- import{defineComponent as e,mergeModels as a,useAttrs as i,useModel as t,computed as l,ref as s,onUnmounted as r,resolveComponent as u,createElementBlock as n,openBlock as o,normalizeStyle as p,normalizeClass as d,unref as c,createVNode as m,createBlock as v,createCommentVNode as f,mergeProps as g,withCtx as h,renderSlot as y,createElementVNode as b,toDisplayString as w}from"vue";import{UploadImage as _,ZoomIn as $,Delete as x}from"@qxs-bns/icons";import{useNamespace as q,ElUpload as k,ElImageViewer as L,ElMessage as U}from"element-plus";const j={class:"image-slot"},V=["src","alt"],z=["onClick"],C=["onClick"],R={class:"tip-content"};var B=e({name:"QxsImageUpload",__name:"image-upload",props:a({action:{type:String,required:!0},headers:{type:null,required:!1},data:{type:null,required:!1},name:{type:String,required:!1,default:"file"},size:{type:Number,required:!1,default:20},width:{type:Number,required:!1,default:160},height:{type:Number,required:!1,default:90},accept:{type:String,required:!1,default:"image/jpeg,image/jpg,image/png,image/gif"},placeholder:{type:String,required:!1,default:"上传图片"},notip:{type:Boolean,required:!1,default:!1},tipText:{type:String,required:!1,default:""},beforeUpload:{type:null,required:!1},disabled:{type:Boolean,required:!1,default:!1},limit:{type:Number,required:!1,default:1}},{fileList:{type:Array,default:()=>[]},fileListModifiers:{}}),emits:a(["success","remove"],["update:fileList"]),setup(e,{emit:a}){const B=a,S=i(),I=t(e,"fileList");let M=0;const N=l({get:()=>I.value.map((e,a)=>e.name&&e.status&&e.uid?e:{...e,name:e.name||`image-${a+1}.jpg`,status:e.status||"success",uid:e.uid||`qxs-image-${Date.now()}-${++M}`}),set(e){I.value=e}}),O=q("image-upload"),T=s(0),Q=s({imageViewerVisible:!1,progress:{preview:"",percent:0}}),A=l(()=>O.cssVarBlock({ns:O.namespace.value,width:`${e.width}px`,height:`${e.height}px`})),D=l(()=>e.accept.split(",").map(e=>e.split("/").pop())),E=l(()=>{if(e.tipText)return e.tipText;return`上传图片支持 ${D.value.join(" / ")} 格式,且图片大小不超过 ${e.size}MB`+(e.width&&e.height?`,建议图片尺寸为 ${e.width}×${e.height}`:"")});function F(e){e.url?(T.value=N.value.findIndex(a=>a.uid===e.uid),Q.value.imageViewerVisible=!0):console.warn("文件缺少 URL,无法预览:",e)}function G(a){if(e.disabled)return;const i=N.value.findIndex(e=>e.uid===a.uid);i>-1&&I.value.splice(i,1)}function H(){Q.value.imageViewerVisible=!1}const J=a=>{if(!a||!a.name)return U.error("文件信息无效!"),!1;const i=a.name.split(".").pop()?.toLowerCase()??"";if(!D.value.some(e=>e?.toLowerCase()===i))return U.error(`上传图片只支持 ${D.value.join(" / ")} 格式!`),!1;if(a.size/1024/1024>e.size)return U.error(`上传图片大小不能超过 ${e.size}MB!`),!1;try{Q.value.progress.preview=URL.createObjectURL(a)}catch(e){console.warn("创建预览失败:",e)}return!e.beforeUpload||e.beforeUpload(a)},K=e=>{Q.value.progress.percent=Math.floor(e.percent)},P=(...e)=>{Q.value.progress.preview&&URL.revokeObjectURL(Q.value.progress.preview),Q.value.progress={preview:"",percent:0},B("success",...e)};return r(()=>{Q.value.progress.preview&&URL.revokeObjectURL(Q.value.progress.preview)}),(e,a)=>{const i=u("QxsIcon");return o(),n("div",{class:d(c(O).e("container")),style:p(A.value)},[m(c(k),g(c(S),{"file-list":N.value,"onUpdate:fileList":a[0]||(a[0]=e=>N.value=e),drag:"",limit:e.limit,"list-type":"picture-card",headers:e.headers,action:e.action,data:e.data,name:e.name,accept:e.accept,"before-upload":J,"on-progress":K,"on-preview":F,"on-success":P,class:[c(O).e("control"),e.limit<=N.value.length?c(O).e("more-than-limit"):""],"on-remove":G}),{file:h(({file:a})=>[a.url?(o(),n("img",{key:0,class:d(`${c(O).namespace.value}-upload-list__item-thumbnail`),src:a.url,alt:a.name||"图片"},null,10,V)):(o(),n("div",{key:1,class:d(`${c(O).namespace.value}-upload-list__item-thumbnail`),style:{display:"flex","align-items":"center","justify-content":"center",color:"#909399",background:"#f5f7fa"}}," 无图片 ",2)),b("span",{class:d(`${c(O).namespace.value}-upload-list__item-actions`)},[a.url?(o(),n("span",{key:0,class:d([`${c(O).namespace.value}-upload-list__item-preview`,`${c(O).namespace.value}-image-upload__action-btn`,`${c(O).namespace.value}-image-upload__action-btn--preview`]),onClick:e=>F(a)},[m(c($),{size:"14px"})],10,z)):f("v-if",!0),a.url?(o(),n("span",{key:1,class:d(`${c(O).namespace.value}-upload-list__item-divider`)},null,2)):f("v-if",!0),b("span",{class:d([`${c(O).namespace.value}-upload-list__item-delete`,`${c(O).namespace.value}-image-upload__action-btn`,`${c(O).namespace.value}-image-upload__action-btn--delete`,e.disabled?"is-disabled":""]),onClick:e=>G(a)},[m(c(x),{size:"14px"})],10,C)],2)]),tip:h(()=>[y(e.$slots,"tip",{},()=>[e.notip?f("v-if",!0):(o(),n("div",{key:0,class:d(`${c(O).namespace.value}-upload__tip-text`)},[b("div",R,w(E.value),1)],2))])]),default:h(()=>[y(e.$slots,"default",{},()=>[b("div",j,[m(i,{size:"32px",icon:c(_)},null,8,["icon"]),b("p",null,w(e.placeholder),1)])])]),_:3},16,["file-list","limit","headers","action","data","name","accept","class"]),Q.value.imageViewerVisible?(o(),v(c(L),{key:0,"url-list":N.value.map(e=>e.url).filter(Boolean),"initial-index":T.value,"show-progress":N.value.length>1,teleported:"",onClose:H},null,8,["url-list","initial-index","show-progress"])):f("v-if",!0)],6)}}});export{B as default};
1
+ import{defineComponent as e,mergeModels as a,useAttrs as i,useModel as l,ref as r,computed as t,onUnmounted as s,resolveComponent as o,createElementBlock as u,openBlock as p,normalizeStyle as n,normalizeClass as c,unref as d,createVNode as v,createBlock as m,createCommentVNode as f,mergeProps as g,withCtx as b,renderSlot as h,createElementVNode as w,toDisplayString as y}from"vue";import{UploadImage as _,ZoomIn as $,Delete as L}from"@qxs-bns/icons";import{useNamespace as x,ElUpload as U,ElImageViewer as k,ElMessage as q}from"element-plus";const R={class:"image-slot"},j=["src","alt"],V=["onClick"],z=["onClick"],O={class:"tip-content"};var C=e({name:"QxsImageUpload",__name:"image-upload",props:a({action:{type:String,required:!0},headers:{type:null,required:!1},data:{type:null,required:!1},name:{type:String,required:!1,default:"file"},size:{type:Number,required:!1,default:20},width:{type:Number,required:!1,default:160},height:{type:Number,required:!1,default:90},accept:{type:String,required:!1,default:"image/jpeg,image/jpg,image/png,image/gif"},placeholder:{type:String,required:!1,default:"上传图片"},notip:{type:Boolean,required:!1,default:!1},tipText:{type:String,required:!1,default:""},beforeUpload:{type:null,required:!1},disabled:{type:Boolean,required:!1,default:!1},limit:{type:Number,required:!1,default:1}},{fileList:{type:Array,default:()=>[]},fileListModifiers:{}}),emits:a(["success","remove"],["update:fileList"]),setup(e,{emit:a}){const C=a,B=i(),S=l(e,"fileList"),M=x("image-upload"),N=r(0),T=r({imageViewerVisible:!1,progress:{preview:"",percent:0}}),I=t(()=>M.cssVarBlock({ns:M.namespace.value,width:`${e.width}px`,height:`${e.height}px`})),Q=t(()=>e.accept.split(",").map(e=>e.split("/").pop())),W=t(()=>{if(e.tipText)return e.tipText;return`上传图片支持 ${Q.value.join(" / ")} 格式,且图片大小不超过 ${e.size}MB`+(e.width&&e.height?`,建议图片尺寸为 ${e.width}×${e.height}`:"")});function A(e){e.url?(N.value=S.value.indexOf(e),T.value.imageViewerVisible=!0):console.warn("文件缺少 URL,无法预览:",e)}function E(a){if(e.disabled)return;a.url&&a.url.startsWith("blob:")&&URL.revokeObjectURL(a.url);const i=S.value.indexOf(a);i>-1&&S.value.splice(i,1),C("remove",a)}function D(){T.value.imageViewerVisible=!1}const F=a=>{if(!a||!a.name)return q.error("文件信息无效!"),!1;const i=a.name.split(".").pop()?.toLowerCase()??"";if(!Q.value.some(e=>e?.toLowerCase()===i))return q.error(`上传图片只支持 ${Q.value.join(" / ")} 格式!`),!1;if(a.size/1024/1024>e.size)return q.error(`上传图片大小不能超过 ${e.size}MB!`),!1;try{T.value.progress.preview=URL.createObjectURL(a)}catch(e){console.warn("创建预览失败:",e)}return!e.beforeUpload||e.beforeUpload(a)},G=e=>{T.value.progress.percent=Math.floor(e.percent)},H=(e,a,i)=>{T.value.progress.preview&&(URL.revokeObjectURL(T.value.progress.preview),T.value.progress.preview=""),T.value.progress.percent=0,q.error(`上传失败: ${e.message||"未知错误"}`)},J=(...e)=>{T.value.progress.preview&&URL.revokeObjectURL(T.value.progress.preview),T.value.progress={preview:"",percent:0},C("success",...e)};return s(()=>{T.value.progress.preview&&(URL.revokeObjectURL(T.value.progress.preview),T.value.progress.preview=""),S.value.forEach(e=>{e.url&&e.url.startsWith("blob:")&&URL.revokeObjectURL(e.url)})}),(e,a)=>{const i=o("QxsIcon");return p(),u("div",{class:c(d(M).e("container")),style:n(I.value)},[v(d(U),g(d(B),{"file-list":S.value,"onUpdate:fileList":a[0]||(a[0]=e=>S.value=e),drag:"",limit:e.limit,"list-type":"picture-card",headers:e.headers,action:e.action,data:e.data,name:e.name,accept:e.accept,"before-upload":F,"on-progress":G,"on-preview":A,"on-success":J,"on-error":H,class:[d(M).e("control"),e.limit<=S.value.length?d(M).e("more-than-limit"):""],"on-remove":E}),{file:b(({file:a})=>[a.url?(p(),u("img",{key:0,class:c(`${d(M).namespace.value}-upload-list__item-thumbnail`),src:a.url,alt:a.name||"图片"},null,10,j)):(p(),u("div",{key:1,class:c(`${d(M).namespace.value}-upload-list__item-thumbnail`),style:{display:"flex","align-items":"center","justify-content":"center",color:"#909399",background:"#f5f7fa"}}," 无图片 ",2)),w("span",{class:c(`${d(M).namespace.value}-upload-list__item-actions`)},[a.url?(p(),u("span",{key:0,class:c([`${d(M).namespace.value}-upload-list__item-preview`,`${d(M).namespace.value}-image-upload__action-btn`,`${d(M).namespace.value}-image-upload__action-btn--preview`]),onClick:e=>A(a)},[v(d($),{size:"14px"})],10,V)):f("v-if",!0),a.url?(p(),u("span",{key:1,class:c(`${d(M).namespace.value}-upload-list__item-divider`)},null,2)):f("v-if",!0),w("span",{class:c([`${d(M).namespace.value}-upload-list__item-delete`,`${d(M).namespace.value}-image-upload__action-btn`,`${d(M).namespace.value}-image-upload__action-btn--delete`,e.disabled?"is-disabled":""]),onClick:e=>E(a)},[v(d(L),{size:"14px"})],10,z)],2)]),tip:b(()=>[h(e.$slots,"tip",{},()=>[e.notip?f("v-if",!0):(p(),u("div",{key:0,class:c(`${d(M).namespace.value}-upload__tip-text`)},[w("div",O,y(W.value),1)],2))])]),default:b(()=>[h(e.$slots,"default",{},()=>[w("div",R,[v(i,{size:"32px",icon:d(_)},null,8,["icon"]),w("p",null,y(e.placeholder),1)])])]),_:3},16,["file-list","limit","headers","action","data","name","accept","class"]),T.value.imageViewerVisible?(p(),m(d(k),{key:0,"url-list":S.value.map(e=>e.url).filter(Boolean),"initial-index":N.value,"show-progress":S.value.length>1,teleported:"",onClose:D},null,8,["url-list","initial-index","show-progress"])):f("v-if",!0)],6)}}});export{C as default};
2
2
  //# sourceMappingURL=image-upload.vue.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-upload.vue.mjs","sources":["../../../../../../packages/components/src/image-upload/src/image-upload.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { UploadFile, UploadFiles, UploadProps } from 'element-plus'\nimport { Delete, UploadImage, ZoomIn } from '@qxs-bns/icons'\nimport { ElImageViewer, ElMessage, ElUpload, useNamespace } from 'element-plus'\nimport { computed, onUnmounted, ref, useAttrs } from 'vue'\n\n// 类型定义\nexport interface ImageUploadProps {\n /** 上传地址(必需) */\n action: UploadProps['action']\n /** 请求头 */\n headers?: UploadProps['headers']\n /** 上传时附带的额外参数 */\n data?: UploadProps['data']\n /** 上传的文件字段名 */\n name?: UploadProps['name']\n /** 文件大小限制(MB) */\n size?: number\n /** 图片显示宽度(px) */\n width?: number\n /** 图片显示高度(px) */\n height?: number\n /** 接受的文件类型 */\n accept?: string\n /** 上传区域提示文字 */\n placeholder?: string\n /** 是否隐藏提示文字 */\n notip?: boolean\n /** 自定义提示文字 */\n tipText?: string\n /** 上传前的钩子函数 */\n beforeUpload?: UploadProps['beforeUpload']\n /** 是否禁用 */\n disabled?: boolean\n /** 最大上传数量 */\n limit?: number\n}\n\ninterface UploadProgress {\n preview: string\n percent: number\n}\n\n// 组件名称\ndefineOptions({\n name: 'QxsImageUpload',\n})\n\n// Props 定义\nconst {\n action,\n headers,\n data,\n name = 'file',\n size = 20,\n width = 160,\n height = 90,\n limit = 1,\n placeholder = '上传图片',\n notip = false,\n accept = 'image/jpeg,image/jpg,image/png,image/gif',\n beforeUpload,\n tipText = '',\n disabled = false,\n} = defineProps<ImageUploadProps>()\n\n// Emits 定义 - 统一事件命名\nconst emits = defineEmits<{\n success: [res: any, uploadFile: UploadFile, uploadFiles: UploadFiles]\n remove: [file: UploadFile]\n}>()\n\nconst attrs = useAttrs()\n\nconst fileList = defineModel('fileList', {\n type: Array as () => UploadFile[],\n default: () => [],\n})\n\n// 生成唯一 ID 的计数器\nlet uidCounter = 0\n\n// 处理不完整的文件对象,自动补全必要属性\nconst normalizedFileList = computed({\n get() {\n return fileList.value.map((file, index) => {\n // 如果文件对象不完整,自动补全必要属性\n if (!file.name || !file.status || !file.uid) {\n return {\n ...file, // 保留原有属性\n name: file.name || `image-${index + 1}.jpg`,\n status: file.status || 'success',\n uid: file.uid || `qxs-image-${Date.now()}-${++uidCounter}`, // 确保唯一性\n } as UploadFile\n }\n return file\n })\n },\n set(value: UploadFile[]) {\n fileList.value = value\n },\n})\n\n// 命名空间\nconst nsEl = useNamespace('image-upload')\n\nconst initialIndex = ref(0)\n// 响应式数据\nconst uploadData = ref<{\n imageViewerVisible: boolean\n progress: UploadProgress\n}>({\n imageViewerVisible: false,\n progress: {\n preview: '',\n percent: 0,\n },\n})\n\nconst cssVar = computed(() => {\n return nsEl.cssVarBlock({\n ns: nsEl.namespace.value,\n width: `${width}px`,\n height: `${height}px`,\n })\n})\n\n// 计算属性\nconst exts = computed(() =>\n accept.split(',').map(ext => ext.split('/').pop()),\n)\n\nconst tipMessage = computed(() => {\n if (tipText) {\n return tipText\n }\n\n const formatText = `上传图片支持 ${exts.value.join(' / ')} 格式,且图片大小不超过 ${size}MB`\n const sizeText = width && height ? `,建议图片尺寸为 ${width}×${height}` : ''\n\n return formatText + sizeText\n})\n\n// 方法\nfunction onPreview(file: UploadFile) {\n // 确保文件有有效的 URL 才能预览\n if (!file.url) {\n console.warn('文件缺少 URL,无法预览:', file)\n return\n }\n\n // 使用 uid 作为唯一标识符来查找文件索引\n initialIndex.value = normalizedFileList.value.findIndex(item => item.uid === file.uid)\n uploadData.value.imageViewerVisible = true\n}\n\nfunction onRemove(file: UploadFile) {\n // 如果组件被禁用,阻止删除\n if (disabled) {\n return\n }\n\n // 使用 uid 作为唯一标识符来查找并删除文件\n const index = normalizedFileList.value.findIndex(item => item.uid === file.uid)\n if (index > -1) {\n fileList.value.splice(index, 1)\n }\n}\n\nfunction previewClose() {\n uploadData.value.imageViewerVisible = false\n}\n\nconst handleBeforeUpload: UploadProps['beforeUpload'] = (file) => {\n // 参数校验\n if (!file || !file.name) {\n ElMessage.error('文件信息无效!')\n return false\n }\n\n // 文件类型校验\n const fileExt = file.name.split('.').pop()?.toLowerCase() ?? ''\n const isTypeOk = exts.value.some(ext => ext?.toLowerCase() === fileExt)\n\n if (!isTypeOk) {\n ElMessage.error(`上传图片只支持 ${exts.value.join(' / ')} 格式!`)\n return false\n }\n\n // 文件大小校验\n const fileSizeMB = file.size / 1024 / 1024\n if (fileSizeMB > size) {\n ElMessage.error(`上传图片大小不能超过 ${size}MB!`)\n return false\n }\n\n // 设置预览\n try {\n uploadData.value.progress.preview = URL.createObjectURL(file)\n }\n catch (error) {\n console.warn('创建预览失败:', error)\n }\n\n // 执行自定义校验\n return beforeUpload ? beforeUpload(file) : true\n}\n\nconst onProgress: UploadProps['onProgress'] = (evt) => {\n uploadData.value.progress.percent = Math.floor(evt.percent)\n}\n\nconst onSuccess: UploadProps['onSuccess'] = (...args) => {\n // 清理预览 URL,防止内存泄漏\n if (uploadData.value.progress.preview) {\n URL.revokeObjectURL(uploadData.value.progress.preview)\n }\n\n uploadData.value.progress = {\n preview: '',\n percent: 0,\n }\n emits('success', ...args)\n}\n\n// 组件卸载时清理资源\nonUnmounted(() => {\n if (uploadData.value.progress.preview) {\n URL.revokeObjectURL(uploadData.value.progress.preview)\n }\n})\n</script>\n\n<template>\n <div\n :class=\"nsEl.e('container')\"\n :style=\"cssVar\"\n >\n <ElUpload\n v-bind=\"attrs\"\n v-model:file-list=\"normalizedFileList\"\n drag\n :limit=\"limit\"\n list-type=\"picture-card\"\n :headers=\"headers\"\n :action=\"action\"\n :data=\"data\"\n :name=\"name\"\n :accept=\"accept\"\n :before-upload=\"handleBeforeUpload\"\n :on-progress=\"onProgress\"\n :on-preview=\"onPreview\"\n :on-success=\"onSuccess\"\n :class=\"[nsEl.e('control'), limit <= normalizedFileList.length ? nsEl.e('more-than-limit') : '']\"\n :on-remove=\"onRemove\"\n >\n <slot>\n <div class=\"image-slot\">\n <QxsIcon\n size=\"32px\"\n :icon=\"UploadImage\"\n />\n <p>{{ placeholder }}</p>\n </div>\n </slot>\n <template #file=\"{ file }\">\n <img\n v-if=\"file.url\"\n :class=\"`${nsEl.namespace.value}-upload-list__item-thumbnail`\"\n :src=\"file.url\"\n :alt=\"file.name || '图片'\"\n >\n <div\n v-else\n :class=\"`${nsEl.namespace.value}-upload-list__item-thumbnail`\"\n style=\"display: flex; align-items: center; justify-content: center; color: #909399; background: #f5f7fa;\"\n >\n 无图片\n </div>\n <span :class=\"`${nsEl.namespace.value}-upload-list__item-actions`\">\n <span\n v-if=\"file.url\"\n :class=\"[\n `${nsEl.namespace.value}-upload-list__item-preview`,\n `${nsEl.namespace.value}-image-upload__action-btn`,\n `${nsEl.namespace.value}-image-upload__action-btn--preview`,\n ]\"\n @click=\"onPreview(file)\"\n >\n <ZoomIn size=\"14px\" />\n </span>\n <span\n v-if=\"file.url\"\n :class=\"`${nsEl.namespace.value}-upload-list__item-divider`\"\n />\n <span\n :class=\"[\n `${nsEl.namespace.value}-upload-list__item-delete`,\n `${nsEl.namespace.value}-image-upload__action-btn`,\n `${nsEl.namespace.value}-image-upload__action-btn--delete`,\n disabled ? 'is-disabled' : '',\n ]\"\n @click=\"onRemove(file)\"\n >\n <Delete size=\"14px\" />\n </span>\n </span>\n </template>\n <template #tip>\n <slot name=\"tip\">\n <div\n v-if=\"!notip\"\n :class=\"`${nsEl.namespace.value}-upload__tip-text`\"\n >\n <div class=\"tip-content\">\n {{ tipMessage }}\n </div>\n </div>\n </slot>\n </template>\n </ElUpload>\n\n <ElImageViewer\n v-if=\"uploadData.imageViewerVisible\"\n :url-list=\"normalizedFileList.map((item: UploadFile) => item.url!).filter(Boolean)\"\n :initial-index=\"initialIndex\"\n :show-progress=\"normalizedFileList.length > 1\"\n teleported\n @close=\"previewClose\"\n />\n </div>\n</template>\n"],"names":["emits","__emit","attrs","useAttrs","fileList","_useModel","__props","uidCounter","normalizedFileList","computed","get","value","map","file","index","name","status","uid","Date","now","set","nsEl","useNamespace","initialIndex","ref","uploadData","imageViewerVisible","progress","preview","percent","cssVar","cssVarBlock","ns","namespace","width","height","exts","split","ext","pop","tipMessage","tipText","join","size","onPreview","url","findIndex","item","console","warn","onRemove","disabled","splice","previewClose","handleBeforeUpload","ElMessage","error","fileExt","toLowerCase","some","URL","createObjectURL","beforeUpload","onProgress","evt","Math","floor","onSuccess","args","revokeObjectURL","onUnmounted","_createElementBlock","class","_normalizeClass","_unref","e","style","_createVNode","ElUpload","_mergeProps","$event","drag","limit","headers","action","data","accept","length","_withCtx","src","alt","display","color","background","_createElementVNode","onClick","ZoomIn","Delete","tip","_renderSlot","_ctx","notip","_hoisted_5","_toDisplayString","_hoisted_1","_component_QxsIcon","icon","UploadImage","placeholder","_createBlock","ElImageViewer","filter","Boolean","teleported","onClose"],"mappings":"+6CAmEA,MAAMA,EAAQC,EAKRC,EAAQC,IAERC,EAAWC,EAAWC,EAAC,YAM7B,IAAIC,EAAa,EAGjB,MAAMC,EAAqBC,EAAS,CAClCC,IAAA,IACSN,EAASO,MAAMC,IAAI,CAACC,EAAMC,IAE1BD,EAAKE,MAASF,EAAKG,QAAWH,EAAKI,IAQjCJ,EAPE,IACFA,EACHE,KAAMF,EAAKE,MAAQ,SAASD,EAAQ,QACpCE,OAAQH,EAAKG,QAAU,UACvBC,IAAKJ,EAAKI,KAAO,aAAaC,KAAKC,WAAWZ,MAMtD,GAAAa,CAAIT,GACFP,EAASO,MAAQA,CACnB,IAIIU,EAAOC,EAAa,gBAEpBC,EAAeC,EAAI,GAEnBC,EAAaD,EAGhB,CACDE,oBAAoB,EACpBC,SAAU,CACRC,QAAS,GACTC,QAAS,KAIPC,EAASrB,EAAS,IACfY,EAAKU,YAAY,CACtBC,GAAIX,EAAKY,UAAUtB,MACnBuB,MAAO,GAAG5B,YACV6B,OAAQ,GAAG7B,gBAKT8B,EAAO3B,EAAS,IACpBH,SAAO+B,MAAM,KAAKzB,IAAI0B,GAAOA,EAAID,MAAM,KAAKE,QAGxCC,EAAa/B,EAAS,KAC1B,GAAIH,EAAAmC,QACF,OAAOnC,EAAAmC,QAMT,MAHmB,UAAUL,EAAKzB,MAAM+B,KAAK,sBAAsBpC,EAAAqC,UAClDrC,SAASA,EAAA6B,OAAS,YAAY7B,EAAA4B,SAAS5B,EAAA6B,SAAW,MAMrE,SAASS,EAAU/B,GAEZA,EAAKgC,KAMVtB,EAAaZ,MAAQH,EAAmBG,MAAMmC,aAAkBC,EAAK9B,MAAQJ,EAAKI,KAClFQ,EAAWd,MAAMe,oBAAqB,GANpCsB,QAAQC,KAAK,iBAAkBpC,EAOnC,CAEA,SAASqC,EAASrC,GAEhB,GAAIP,EAAA6C,SACF,OAIF,MAAMrC,EAAQN,EAAmBG,MAAMmC,aAAkBC,EAAK9B,MAAQJ,EAAKI,KACvEH,GAAQ,GACVV,EAASO,MAAMyC,OAAOtC,EAAO,EAEjC,CAEA,SAASuC,IACP5B,EAAWd,MAAMe,oBAAqB,CACxC,CAEA,MAAM4B,EAAmDzC,IAEvD,IAAKA,IAASA,EAAKE,KAEjB,OADAwC,EAAUC,MAAM,YACT,EAIT,MAAMC,EAAU5C,EAAKE,KAAKsB,MAAM,KAAKE,OAAOmB,eAAiB,GAG7D,IAFiBtB,EAAKzB,MAAMgD,QAAYrB,GAAKoB,gBAAkBD,GAI7D,OADAF,EAAUC,MAAM,WAAWpB,EAAKzB,MAAM+B,KAAK,eACpC,EAKT,GADmB7B,EAAK8B,KAAO,KAAO,KACrBrC,EAAAqC,KAEf,OADAY,EAAUC,MAAM,cAAclD,EAAAqC,YACvB,EAIT,IACElB,EAAWd,MAAMgB,SAASC,QAAUgC,IAAIC,gBAAgBhD,EAC1D,OACO2C,GACLR,QAAQC,KAAK,UAAWO,EAC1B,CAGA,OAAOlD,EAAAwD,cAAexD,eAAaO,IAG/BkD,EAAyCC,IAC7CvC,EAAWd,MAAMgB,SAASE,QAAUoC,KAAKC,MAAMF,EAAInC,UAG/CsC,EAAsC,IAAIC,KAE1C3C,EAAWd,MAAMgB,SAASC,SAC5BgC,IAAIS,gBAAgB5C,EAAWd,MAAMgB,SAASC,SAGhDH,EAAWd,MAAMgB,SAAW,CAC1BC,QAAS,GACTC,QAAS,GAEX7B,EAAM,aAAcoE,WAItBE,EAAY,KACN7C,EAAWd,MAAMgB,SAASC,SAC5BgC,IAAIS,gBAAgB5C,EAAWd,MAAMgB,SAASC,mDAMhD2C,EAgGM,MAAA,CA/FHC,MAAKC,EAAEC,EAAArD,GAAKsD,EAAC,cACbC,QAAO9C,EAAAnB,SAERkE,EAkFWH,EAAAI,GAlFXC,EAkFWL,EAAAxE,GAjFI,CACL,YAAWM,EAAAG,yCAAAH,EAAkBG,MAAAqE,GACrCC,KAAA,GACCC,MAAOA,EAAAA,MACR,YAAU,eACTC,QAASA,EAAAA,QACTC,OAAQA,EAAAA,OACRC,KAAMA,EAAAA,KACNtE,KAAMA,EAAAA,KACNuE,OAAQA,EAAAA,OACR,gBAAehC,EACf,cAAaS,EACb,aAAYnB,EACZ,aAAYuB,EACZK,MAAK,CAAGE,EAAArD,GAAKsD,EAAC,WAAaO,EAAAA,OAAS1E,EAAAG,MAAmB4E,OAASb,EAAArD,GAAKsD,EAAC,mBAAA,IACtE,YAAWzB,KAWDrC,KAAI2E,EACb,EADiB3E,UAAI,CAEbA,EAAKgC,SADb0B,EAKC,MAAA,OAHEC,MAAKC,EAAA,GAAKC,EAAArD,GAAKY,UAAUtB,qCACzB8E,IAAK5E,EAAKgC,IACV6C,IAAK7E,EAAKE,MAAI,uBAEjBwD,EAMM,MAAA,OAJHC,MAAKC,EAAA,GAAKC,EAAArD,GAAKY,UAAUtB,qCAC1BiE,MAAA,CAAAe,QAAA,OAAA,cAAA,SAAA,kBAAA,SAAAC,MAAA,UAAAC,WAAA,YACD,QAED,IACAC,EA2BO,OAAA,CA3BAtB,MAAKC,EAAA,GAAKC,EAAArD,GAAKY,UAAUtB,qCAEtBE,EAAKgC,SADb0B,EAUO,OAAA,OARJC,MAAKC,EAAA,IAAqBC,EAAArD,GAAKY,UAAUtB,qCAAoD+D,EAAArD,GAAKY,UAAUtB,oCAAmD+D,EAAArD,GAAKY,UAAUtB,4CAK9KoF,QAAKf,GAAEpC,EAAU/B,KAElBgE,EAAsBH,EAAAsB,GAAA,CAAdrD,KAAK,8BAGP9B,EAAKgC,SADb0B,EAGE,OAAA,OADCC,MAAKC,EAAA,GAAKC,EAAArD,GAAKY,UAAUtB,0DAE5BmF,EAUO,OAAA,CATJtB,MAAKC,EAAA,IAAqBC,EAAArD,GAAKY,UAAUtB,oCAAmD+D,EAAArD,GAAKY,UAAUtB,oCAAmD+D,EAAArD,GAAKY,UAAUtB,yCAAwDwC,EAAAA,SAAQ,cAAA,KAM7O4C,QAAKf,GAAE9B,EAASrC,KAEjBgE,EAAsBH,EAAAuB,GAAA,CAAdtD,KAAK,sBAIRuD,MACT,IASO,CATPC,EASOC,kBATP,IASO,CAPIC,EAAAA,wBADT9B,EAOM,MAAA,OALHC,MAAKC,EAAA,GAAKC,EAAArD,GAAKY,UAAUtB,4BAE1BmF,EAEM,MAFNQ,EAEMC,EADD/D,EAAA7B,OAAU,sBA3DrB,IAQO,CARPwF,EAQOC,sBARP,IAQO,CAPLN,EAMM,MANNU,EAMM,CALJ3B,EAGE4B,EAAA,CAFA9D,KAAK,OACJ+D,KAAMhC,EAAAiC,qBAETb,EAAwB,WAAlBc,EAAAA,aAAW,0FA6DfnF,EAAAd,MAAWe,wBADnBmF,EAOEnC,EAAAoC,GAAA,OALC,WAAUtG,EAAAG,MAAmBC,IAAKmC,GAAqBA,EAAKF,KAAMkE,OAAOC,SACzE,gBAAezF,EAAAZ,MACf,gBAAeH,EAAAG,MAAmB4E,OAAM,EACzC0B,WAAA,GACCC,QAAO7D"}
1
+ {"version":3,"file":"image-upload.vue.mjs","sources":["../../../../../../packages/components/src/image-upload/src/image-upload.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { UploadFile, UploadFiles, UploadProps } from 'element-plus'\nimport { Delete, UploadImage, ZoomIn } from '@qxs-bns/icons'\nimport { ElImageViewer, ElMessage, ElUpload, useNamespace } from 'element-plus'\nimport { computed, onUnmounted, ref, useAttrs } from 'vue'\n\n// 类型定义\nexport interface ImageUploadProps {\n /** 上传地址(必需) */\n action: UploadProps['action']\n /** 请求头 */\n headers?: UploadProps['headers']\n /** 上传时附带的额外参数 */\n data?: UploadProps['data']\n /** 上传的文件字段名 */\n name?: UploadProps['name']\n /** 文件大小限制(MB) */\n size?: number\n /** 图片显示宽度(px) */\n width?: number\n /** 图片显示高度(px) */\n height?: number\n /** 接受的文件类型 */\n accept?: string\n /** 上传区域提示文字 */\n placeholder?: string\n /** 是否隐藏提示文字 */\n notip?: boolean\n /** 自定义提示文字 */\n tipText?: string\n /** 上传前的钩子函数 */\n beforeUpload?: UploadProps['beforeUpload']\n /** 是否禁用 */\n disabled?: boolean\n /** 最大上传数量 */\n limit?: number\n}\n\ninterface UploadProgress {\n preview: string\n percent: number\n}\n\n// 组件名称\ndefineOptions({\n name: 'QxsImageUpload',\n})\n\n// Props 定义\nconst {\n action,\n headers,\n data,\n name = 'file',\n size = 20,\n width = 160,\n height = 90,\n limit = 1,\n placeholder = '上传图片',\n notip = false,\n accept = 'image/jpeg,image/jpg,image/png,image/gif',\n beforeUpload,\n tipText = '',\n disabled = false,\n} = defineProps<ImageUploadProps>()\n\n// Emits 定义 - 统一事件命名\nconst emits = defineEmits<{\n success: [res: any, uploadFile: UploadFile, uploadFiles: UploadFiles]\n remove: [file: UploadFile]\n}>()\n\nconst attrs = useAttrs()\n\nconst fileList = defineModel('fileList', {\n type: Array as () => UploadFile[],\n default: () => [],\n})\n\n// 命名空间\nconst nsEl = useNamespace('image-upload')\n\nconst initialIndex = ref(0)\n// 响应式数据\nconst uploadData = ref<{\n imageViewerVisible: boolean\n progress: UploadProgress\n}>({\n imageViewerVisible: false,\n progress: {\n preview: '',\n percent: 0,\n },\n})\n\nconst cssVar = computed(() => {\n return nsEl.cssVarBlock({\n ns: nsEl.namespace.value,\n width: `${width}px`,\n height: `${height}px`,\n })\n})\n\n// 计算属性\nconst exts = computed(() =>\n accept.split(',').map(ext => ext.split('/').pop()),\n)\n\nconst tipMessage = computed(() => {\n if (tipText) {\n return tipText\n }\n\n const formatText = `上传图片支持 ${exts.value.join(' / ')} 格式,且图片大小不超过 ${size}MB`\n const sizeText = width && height ? `,建议图片尺寸为 ${width}×${height}` : ''\n\n return formatText + sizeText\n})\n\n// 方法\nfunction onPreview(file: UploadFile) {\n // 确保文件有有效的 URL 才能预览\n if (!file.url) {\n console.warn('文件缺少 URL,无法预览:', file)\n return\n }\n\n // 直接使用文件在列表中的索引\n initialIndex.value = fileList.value.indexOf(file)\n uploadData.value.imageViewerVisible = true\n}\n\nfunction onRemove(file: UploadFile) {\n // 如果组件被禁用,阻止删除\n if (disabled) {\n return\n }\n\n // 清理可能的 Object URL\n if (file.url && file.url.startsWith('blob:')) {\n URL.revokeObjectURL(file.url)\n }\n\n // 直接使用文件在列表中的索引来删除\n const index = fileList.value.indexOf(file)\n if (index > -1) {\n fileList.value.splice(index, 1)\n }\n\n // 触发删除事件\n emits('remove', file)\n}\n\nfunction previewClose() {\n uploadData.value.imageViewerVisible = false\n}\n\nconst handleBeforeUpload: UploadProps['beforeUpload'] = (file) => {\n // 参数校验\n if (!file || !file.name) {\n ElMessage.error('文件信息无效!')\n return false\n }\n\n // 文件类型校验\n const fileExt = file.name.split('.').pop()?.toLowerCase() ?? ''\n const isTypeOk = exts.value.some(ext => ext?.toLowerCase() === fileExt)\n\n if (!isTypeOk) {\n ElMessage.error(`上传图片只支持 ${exts.value.join(' / ')} 格式!`)\n return false\n }\n\n // 文件大小校验\n const fileSizeMB = file.size / 1024 / 1024\n if (fileSizeMB > size) {\n ElMessage.error(`上传图片大小不能超过 ${size}MB!`)\n return false\n }\n\n // 设置预览\n try {\n uploadData.value.progress.preview = URL.createObjectURL(file)\n }\n catch (error) {\n console.warn('创建预览失败:', error)\n }\n\n // 执行自定义校验\n return beforeUpload ? beforeUpload(file) : true\n}\n\nconst onProgress: UploadProps['onProgress'] = (evt) => {\n uploadData.value.progress.percent = Math.floor(evt.percent)\n}\n\nconst onError: UploadProps['onError'] = (error, _file, _fileList) => {\n // 上传失败时也要清理预览 URL\n if (uploadData.value.progress.preview) {\n URL.revokeObjectURL(uploadData.value.progress.preview)\n uploadData.value.progress.preview = ''\n }\n uploadData.value.progress.percent = 0\n\n ElMessage.error(`上传失败: ${error.message || '未知错误'}`)\n}\n\nconst onSuccess: UploadProps['onSuccess'] = (...args) => {\n // 清理预览 URL,防止内存泄漏\n if (uploadData.value.progress.preview) {\n URL.revokeObjectURL(uploadData.value.progress.preview)\n }\n\n uploadData.value.progress = {\n preview: '',\n percent: 0,\n }\n emits('success', ...args)\n}\n\n// 清理所有可能的 Object URL\nfunction cleanupObjectURLs() {\n // 清理进度预览 URL\n if (uploadData.value.progress.preview) {\n URL.revokeObjectURL(uploadData.value.progress.preview)\n uploadData.value.progress.preview = ''\n }\n\n // 清理文件列表中可能的 Object URL\n fileList.value.forEach((file) => {\n if (file.url && file.url.startsWith('blob:')) {\n URL.revokeObjectURL(file.url)\n }\n })\n}\n\n// 组件卸载时清理资源\nonUnmounted(() => {\n cleanupObjectURLs()\n})\n</script>\n\n<template>\n <div\n :class=\"nsEl.e('container')\"\n :style=\"cssVar\"\n >\n <ElUpload\n v-bind=\"attrs\"\n v-model:file-list=\"fileList\"\n drag\n :limit=\"limit\"\n list-type=\"picture-card\"\n :headers=\"headers\"\n :action=\"action\"\n :data=\"data\"\n :name=\"name\"\n :accept=\"accept\"\n :before-upload=\"handleBeforeUpload\"\n :on-progress=\"onProgress\"\n :on-preview=\"onPreview\"\n :on-success=\"onSuccess\"\n :on-error=\"onError\"\n :class=\"[nsEl.e('control'), limit <= fileList.length ? nsEl.e('more-than-limit') : '']\"\n :on-remove=\"onRemove\"\n >\n <slot>\n <div class=\"image-slot\">\n <QxsIcon\n size=\"32px\"\n :icon=\"UploadImage\"\n />\n <p>{{ placeholder }}</p>\n </div>\n </slot>\n <template #file=\"{ file }\">\n <img\n v-if=\"file.url\"\n :class=\"`${nsEl.namespace.value}-upload-list__item-thumbnail`\"\n :src=\"file.url\"\n :alt=\"file.name || '图片'\"\n >\n <div\n v-else\n :class=\"`${nsEl.namespace.value}-upload-list__item-thumbnail`\"\n style=\"display: flex; align-items: center; justify-content: center; color: #909399; background: #f5f7fa;\"\n >\n 无图片\n </div>\n <span :class=\"`${nsEl.namespace.value}-upload-list__item-actions`\">\n <span\n v-if=\"file.url\"\n :class=\"[\n `${nsEl.namespace.value}-upload-list__item-preview`,\n `${nsEl.namespace.value}-image-upload__action-btn`,\n `${nsEl.namespace.value}-image-upload__action-btn--preview`,\n ]\"\n @click=\"onPreview(file)\"\n >\n <ZoomIn size=\"14px\" />\n </span>\n <span\n v-if=\"file.url\"\n :class=\"`${nsEl.namespace.value}-upload-list__item-divider`\"\n />\n <span\n :class=\"[\n `${nsEl.namespace.value}-upload-list__item-delete`,\n `${nsEl.namespace.value}-image-upload__action-btn`,\n `${nsEl.namespace.value}-image-upload__action-btn--delete`,\n disabled ? 'is-disabled' : '',\n ]\"\n @click=\"onRemove(file)\"\n >\n <Delete size=\"14px\" />\n </span>\n </span>\n </template>\n <template #tip>\n <slot name=\"tip\">\n <div\n v-if=\"!notip\"\n :class=\"`${nsEl.namespace.value}-upload__tip-text`\"\n >\n <div class=\"tip-content\">\n {{ tipMessage }}\n </div>\n </div>\n </slot>\n </template>\n </ElUpload>\n\n <ElImageViewer\n v-if=\"uploadData.imageViewerVisible\"\n :url-list=\"fileList.map((item: UploadFile) => item.url!).filter(Boolean)\"\n :initial-index=\"initialIndex\"\n :show-progress=\"fileList.length > 1\"\n teleported\n @close=\"previewClose\"\n />\n </div>\n</template>\n"],"names":["emits","__emit","attrs","useAttrs","fileList","_useModel","__props","nsEl","useNamespace","initialIndex","ref","uploadData","imageViewerVisible","progress","preview","percent","cssVar","computed","cssVarBlock","ns","namespace","value","width","height","exts","split","map","ext","pop","tipMessage","tipText","join","size","onPreview","file","url","indexOf","console","warn","onRemove","disabled","startsWith","URL","revokeObjectURL","index","splice","previewClose","handleBeforeUpload","name","ElMessage","error","fileExt","toLowerCase","some","createObjectURL","beforeUpload","onProgress","evt","Math","floor","onError","_file","_fileList","message","onSuccess","args","onUnmounted","forEach","_createElementBlock","class","_normalizeClass","_unref","e","style","_createVNode","ElUpload","_mergeProps","$event","drag","limit","headers","action","data","accept","length","_withCtx","src","alt","display","color","background","_createElementVNode","onClick","ZoomIn","Delete","tip","_renderSlot","_ctx","notip","_hoisted_5","_toDisplayString","_hoisted_1","_component_QxsIcon","icon","UploadImage","placeholder","_createBlock","ElImageViewer","item","filter","Boolean","teleported","onClose"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";exports.version="0.0.75";
1
+ "use strict";exports.version="0.0.77";
2
2
  //# sourceMappingURL=package.json.cjs.map
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("vue"),o=require("../../../node_modules/.pnpm/@iconify_vue@5.0.0_vue@3.5.18_typescript@5.9.2_/node_modules/@iconify/vue/dist/iconify.cjs"),t=require("@qxs-bns/hooks");const n=["xlink:href"];var r=e.defineComponent({name:"QxsIcon",__name:"icon",props:{icon:{type:null,required:!0},runtime:{type:Boolean,required:!1},flip:{type:null,required:!1,default:""},rotate:{type:Number,required:!1,default:0},color:{type:String,required:!1},size:{type:[String,Number],required:!1},localIconPrefix:{type:String,required:!1,default:"icon-"}},setup(r){const l=e.useAttrs(),u=t.useNamespace("icon"),i=e.computed(()=>"object"==typeof r.icon||"function"==typeof r.icon),s=e.computed(()=>i.value?"component":0===r.icon.indexOf("i-")?r.runtime?"svg":"css":r.icon.includes(":")?"svg":"custom"),c=e.computed(()=>{if(i.value)return"";if(0===r.icon.indexOf("i-")){let e=r.icon;return r.runtime&&(e=e.replace("i-","")),e}return r.icon});const a=e.computed(()=>{const e=[];switch(r.flip){case"horizontal":e.push("rotateY(180deg)");break;case"vertical":e.push("rotateX(180deg)");break;case"both":e.push("rotateX(180deg)"),e.push("rotateY(180deg)")}return r.rotate&&e.push(`rotate(${r.rotate%360}deg)`),`${r.color?`color: ${r.color};`:""}${r.size?`font-size: ${o=r.size,o?"number"==typeof o||/^\d+(?:\.\d+)?$/.test(o)?`${o}px`:o:""};`:""}${e.length?`transform: ${e.join(" ")};`:""}`;var o});return(t,r)=>"component"===s.value?(e.openBlock(),e.createBlock(e.resolveDynamicComponent(t.icon),{key:0,class:e.normalizeClass([e.unref(u).b()]),style:e.normalizeStyle(a.value+(e.unref(l).style||""))},null,8,["class","style"])):"css"===s.value&&c.value?(e.openBlock(),e.createElementBlock("i",{key:1,class:e.normalizeClass([e.unref(u).b(),c.value]),style:e.normalizeStyle(a.value+(e.unref(l).style||""))},null,6)):"svg"===s.value&&c.value?(e.openBlock(),e.createElementBlock("i",{key:2,style:e.normalizeStyle(a.value+(e.unref(l).style||"")),class:e.normalizeClass([e.unref(u).b()])},[e.createVNode(e.unref(o.Icon),{icon:c.value},null,8,["icon"])],6)):c.value?(e.openBlock(),e.createElementBlock("svg",{key:3,"aria-hidden":"true",class:e.normalizeClass([e.unref(u).b()]),style:e.normalizeStyle(a.value+(e.unref(l).style||""))},[e.createElementVNode("use",{"xlink:href":`#${t.localIconPrefix}${c.value}`},null,8,n)],6)):e.createCommentVNode("v-if",!0)}});exports.default=r;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("vue"),t=require("../../../node_modules/.pnpm/@iconify_vue@5.0.0_vue@3.5.18_typescript@5.9.2_/node_modules/@iconify/vue/dist/iconify.cjs"),n=require("@qxs-bns/hooks");const o=["src"],l=["xlink:href"];var r=e.defineComponent({name:"QxsIcon",__name:"icon",props:{icon:{type:null,required:!0},flip:{type:null,required:!1,default:""},rotate:{type:Number,required:!1,default:0},color:{type:String,required:!1},size:{type:[String,Number],required:!1},localIconPrefix:{type:String,required:!1,default:"icon-"},fallback:{type:String,required:!1},loading:{type:String,required:!1}},setup(r){const a=e.useAttrs(),i=n.useNamespace("icon"),s=e.computed(()=>"object"==typeof r.icon||"function"==typeof r.icon),c=e.computed(()=>{if(s.value)return"component";const e=r.icon;return/^https?:\/\//.test(e)||(/^\.{1,2}\//.test(t=e)||t.startsWith("/")||t.includes("/"))?"img":/^i-[^:]+:[^:]+/.test(e)?"unocss":0===e.indexOf("i-")?"css":e.includes(":")?"iconify":"svg";var t}),u=e.computed(()=>{if(s.value)return"";const e=r.icon;return"img"===c.value||"unocss"===c.value||0===e.indexOf("i-")&&c.value,e});const m=e.computed(()=>{const e=[];switch(r.flip){case"horizontal":e.push("rotateY(180deg)");break;case"vertical":e.push("rotateX(180deg)");break;case"both":e.push("rotateX(180deg)"),e.push("rotateY(180deg)")}r.rotate&&e.push(`rotate(${r.rotate%360}deg)`);let t=`${r.color?`color: ${r.color};`:""}${r.size?`font-size: ${n=r.size,n?"number"==typeof n||/^\d+(?:\.\d+)?$/.test(n)?`${n}px`:n:""};`:""}${e.length?`transform: ${e.join(" ")};`:""}`;var n;const o=a.style;if(o)if("string"==typeof o)t+=o;else if("object"==typeof o){t+=Object.entries(o).map(([e,t])=>`${e.replace(/([A-Z])/g,"-$1").toLowerCase()}: ${t};`).join("")}return t}),d=e.ref(!0),p=e.ref(!1);function f(){d.value=!1,p.value=!1}function k(){d.value=!1,p.value=!0}return(n,r)=>(e.openBlock(),e.createElementBlock(e.Fragment,null,[e.createCommentVNode(" Vue组件 "),"component"===c.value?(e.openBlock(),e.createBlock(e.resolveDynamicComponent(n.icon),{key:0,class:e.normalizeClass([[e.unref(i).b()],"size-inherit shrink-0"]),style:e.normalizeStyle([m.value])},null,8,["class","style"])):"unocss"===c.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createCommentVNode(" UnoCSS图标 "),e.createElementVNode("i",{class:e.normalizeClass([[e.unref(i).b()],"size-inherit shrink-0"]),style:e.normalizeStyle([m.value])},null,6)],2112)):"css"===c.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:2},[e.createCommentVNode(" CSS类图标 "),e.createElementVNode("i",{class:e.normalizeClass(["size-inherit shrink-0",[e.unref(i).b()]]),style:e.normalizeStyle([m.value])},null,6)],2112)):"iconify"===c.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:3},[e.createCommentVNode(" Iconify图标 "),e.createVNode(e.unref(t.Icon),{class:e.normalizeClass(["size-inherit shrink-0",[e.unref(i).b()]]),icon:u.value,style:e.normalizeStyle([m.value])},null,8,["icon","class","style"])],2112)):"img"===c.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:4},[e.createCommentVNode(" 图片 "),e.createCommentVNode(" 加载中状态 "),d.value&&n.loading?(e.openBlock(),e.createElementBlock("i",{key:0,class:e.normalizeClass(["size-inherit shrink-0",n.loading])},null,2)):p.value&&n.fallback?(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createCommentVNode(" 错误状态 "),e.createElementVNode("i",{class:e.normalizeClass(["size-inherit shrink-0",n.fallback])},null,2)],2112)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:2},[e.createCommentVNode(" 图片本体 "),e.createElementVNode("img",{src:u.value,class:e.normalizeClass(["size-inherit shrink-0",[e.unref(i).b()]]),style:e.normalizeStyle([m.value]),onLoad:f,onError:k},null,46,o)],2112))],64)):u.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:5},[e.createCommentVNode(" SVG Sprite "),(e.openBlock(),e.createElementBlock("svg",{"aria-hidden":"true",class:e.normalizeClass([[e.unref(i).b()],"size-inherit shrink-0"]),style:e.normalizeStyle([m.value])},[e.createElementVNode("use",{"xlink:href":`#${n.localIconPrefix}${u.value}`},null,8,l)],6))],2112)):e.createCommentVNode("v-if",!0)],2112))}});exports.default=r;
2
2
  //# sourceMappingURL=icon.vue.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"icon.vue.cjs","sources":["../../../../../../packages/components/src/icon/src/icon.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Component } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport { useNamespace } from '@qxs-bns/hooks'\nimport { computed, useAttrs } from 'vue'\n\ndefineOptions({\n name: 'QxsIcon',\n})\n\nconst {\n icon,\n flip = '',\n rotate = 0,\n runtime,\n color,\n size,\n localIconPrefix = 'icon-',\n} = defineProps<PropsType>()\n\nconst attrs = useAttrs()\n\nconst ns = useNamespace('icon')\n\nexport interface PropsType {\n icon: string | Component\n runtime?: boolean\n flip?: 'horizontal' | 'vertical' | 'both' | '' | undefined\n rotate?: number\n color?: string\n size?: string | number\n localIconPrefix?: string\n}\n\nconst isComponentName = computed(() => typeof icon === 'object' || typeof icon === 'function')\n\nconst outputType = computed(() => {\n if (isComponentName.value) {\n return 'component'\n }\n if ((icon as string).indexOf('i-') === 0) {\n return runtime ? 'svg' : 'css'\n }\n else if ((icon as string).includes(':')) {\n return 'svg'\n }\n else {\n return 'custom'\n }\n})\n\nconst outputName = computed(() => {\n if (isComponentName.value) {\n return ''\n }\n if ((icon as string).indexOf('i-') === 0) {\n let conversionName = icon as string\n if (runtime) {\n conversionName = conversionName.replace('i-', '')\n }\n return conversionName\n }\n else {\n return icon as string\n }\n})\n\n// 用正则匹配 size 是不是 number 值,再判断是否有 px 结尾的单位,没有则拼接\nfunction formatSize(size: string | number | undefined): string {\n if (!size) {\n return ''\n }\n\n // 如果是数字,直接添加 px\n if (typeof size === 'number') {\n return `${size}px`\n }\n\n // 如果是纯数字字符串,添加 px\n if (/^\\d+(?:\\.\\d+)?$/.test(size)) {\n return `${size}px`\n }\n\n // 如果已经有单位(px、em、rem等)或者不是纯数字,直接返回\n return size\n}\n\n// 统一的样式计算属性,适用于所有图标类型\n// 包含颜色、尺寸和变换(旋转、翻转)\n// Iconify 图标通过外层 i 标签应用这些样式\nconst style = computed(() => {\n const transform = [] as string[]\n switch (flip) {\n case 'horizontal':\n transform.push('rotateY(180deg)')\n break\n case 'vertical':\n transform.push('rotateX(180deg)')\n break\n case 'both':\n transform.push('rotateX(180deg)')\n transform.push('rotateY(180deg)')\n break\n // 对于 '' 和 undefined,不做任何处理\n }\n if (rotate) {\n transform.push(`rotate(${rotate % 360}deg)`)\n }\n return `${color ? `color: ${color};` : ''}${size ? `font-size: ${formatSize(size)};` : ''}${transform.length ? `transform: ${transform.join(' ')};` : ''}`\n})\n</script>\n\n<template>\n <component\n :is=\"icon as any\"\n v-if=\"outputType === 'component'\"\n :class=\"[ns.b()]\"\n :style=\"style + (attrs.style || '')\"\n />\n <i\n v-else-if=\"outputType === 'css' && outputName\"\n :class=\"[ns.b(), outputName]\"\n :style=\"style + (attrs.style || '')\"\n />\n <i\n v-else-if=\"outputType === 'svg' && outputName\"\n :style=\"style + (attrs.style || '')\"\n :class=\"[ns.b()]\"\n >\n <Icon\n :icon=\"outputName\"\n />\n </i>\n <svg\n v-else-if=\"outputName\"\n aria-hidden=\"true\"\n :class=\"[ns.b()]\"\n :style=\"style + (attrs.style || '')\"\n >\n <use :xlink:href=\"`#${localIconPrefix}${outputName}`\" />\n </svg>\n</template>\n"],"names":["attrs","useAttrs","ns","useNamespace","isComponentName","computed","__props","icon","outputType","value","indexOf","runtime","includes","outputName","conversionName","replace","style","transform","flip","push","rotate","color","size","test","length","join","_openBlock","_createBlock","_resolveDynamicComponent","class","_normalizeClass","_unref","b","_normalizeStyle","_createElementBlock","_createVNode","Icon","_createElementVNode","localIconPrefix"],"mappings":"4mBAoBA,MAAMA,EAAQC,EAAAA,WAERC,EAAKC,EAAAA,aAAa,QAYlBC,EAAkBC,WAAS,IAAsB,iBAATC,EAAAC,MAAqC,mBAATD,EAAAC,MAEpEC,EAAaH,EAAAA,SAAS,IACtBD,EAAgBK,MACX,YAE8B,IAAlCH,EAAAC,KAAgBG,QAAQ,MACpBJ,EAAAK,QAAU,MAAQ,MAEjBL,EAAAC,KAAgBK,SAAS,KAC1B,MAGA,UAILC,EAAaR,EAAAA,SAAS,KAC1B,GAAID,EAAgBK,MAClB,MAAO,GAET,GAAuC,IAAlCH,EAAAC,KAAgBG,QAAQ,MAAa,CACxC,IAAII,EAAiBR,EAAAC,KAIrB,OAHID,EAAAK,UACFG,EAAiBA,EAAeC,QAAQ,KAAM,KAEzCD,CACT,CAEE,OAAOR,EAAAC,OA2BX,MAAMS,EAAQX,EAAAA,SAAS,KACrB,MAAMY,EAAY,GAClB,OAAQX,EAAAY,MACN,IAAK,aACHD,EAAUE,KAAK,mBACf,MACF,IAAK,WACHF,EAAUE,KAAK,mBACf,MACF,IAAK,OACHF,EAAUE,KAAK,mBACfF,EAAUE,KAAK,mBAOnB,OAHIb,EAAAc,QACFH,EAAUE,KAAK,UAAUb,SAAS,WAE7B,GAAGA,EAAAe,MAAQ,UAAUf,EAAAe,SAAW,KAAKf,EAAAgB,KAAO,cAxCjCA,EAwC0DhB,OAvCvEgB,EAKe,iBAATA,GAKP,kBAAkBC,KAAKD,GAJlB,GAAGA,MASLA,EAdE,MAsC8E,KAAKL,EAAUO,OAAS,cAAcP,EAAUQ,KAAK,QAAU,KAxCxJ,IAAoBH,iBA+CA,cAAVd,EAAAC,OAFRiB,EAAAA,YAAAC,EAAAA,YAKEC,EAAAA,wBAJKrB,EAAAA,MAAI,OAERsB,MAAKC,EAAAA,eAAA,CAAGC,EAAAA,MAAA7B,GAAG8B,MACXhB,MAAKiB,EAAAA,eAAEjB,EAAAP,OAASsB,EAAAA,MAAA/B,GAAMgB,OAAK,yCAGjBR,EAAAC,OAAwBI,EAAAJ,qBADrCyB,EAAAA,mBAIE,IAAA,OAFCL,MAAKC,EAAAA,eAAA,CAAGC,QAAA7B,GAAG8B,IAAKnB,EAAAJ,QAChBO,MAAKiB,EAAAA,eAAEjB,EAAAP,OAASsB,EAAAA,MAAA/B,GAAMgB,OAAK,uBAGjBR,EAAAC,OAAwBI,EAAAJ,qBADrCyB,EAAAA,mBAQI,IAAA,OANDlB,MAAKiB,EAAAA,eAAEjB,EAAAP,OAASsB,EAAAA,MAAA/B,GAAMgB,OAAK,KAC3Ba,MAAKC,EAAAA,eAAA,CAAGC,EAAAA,MAAA7B,GAAG8B,QAEZG,EAAAA,YAEEJ,EAAAA,MAAAK,QAAA,CADC7B,KAAMM,EAAAJ,OAAU,KAAA,EAAA,CAAA,cAIRI,EAAAJ,qBADbyB,EAAAA,mBAOM,MAAA,OALJ,cAAY,OACXL,MAAKC,EAAAA,eAAA,CAAGC,EAAAA,MAAA7B,GAAG8B,MACXhB,MAAKiB,EAAAA,eAAEjB,EAAAP,OAASsB,EAAAA,MAAA/B,GAAMgB,OAAK,OAE5BqB,EAAAA,mBAAwD,MAAA,CAAlD,aAAU,IAAMC,EAAAA,kBAAkBzB,EAAAJ"}
1
+ {"version":3,"file":"icon.vue.cjs","sources":["../../../../../../packages/components/src/icon/src/icon.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Component, CSSProperties } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport { useNamespace } from '@qxs-bns/hooks'\nimport { computed, ref, useAttrs } from 'vue'\n\ndefineOptions({\n name: 'QxsIcon',\n})\n\nconst {\n icon,\n flip = '',\n rotate = 0,\n color,\n size,\n localIconPrefix = 'icon-',\n fallback,\n loading,\n} = defineProps<PropsType>()\n\nconst attrs = useAttrs()\n\nconst ns = useNamespace('icon')\n\nexport interface PropsType {\n icon: string | Component\n flip?: 'horizontal' | 'vertical' | 'both' | '' | undefined\n rotate?: number\n color?: string\n size?: string | number\n localIconPrefix?: string\n // 新增功能:支持图片URL\n fallback?: string // 加载失败时的备用图标\n loading?: string // 加载中显示的图标\n}\n\nconst isComponentName = computed(() => typeof icon === 'object' || typeof icon === 'function')\n\nconst outputType = computed(() => {\n if (isComponentName.value) {\n return 'component'\n }\n\n const iconStr = icon as string\n\n // 检测是否为图片URL或路径\n const hasPathFeatures = (str: string) => {\n return /^\\.{1,2}\\//.test(str) || str.startsWith('/') || str.includes('/')\n }\n if (/^https?:\\/\\//.test(iconStr) || hasPathFeatures(iconStr)) {\n return 'img'\n }\n\n // 检测UnoCSS图标格式 (i-[provider]:[name])\n if (/^i-[^:]+:[^:]+/.test(iconStr)) {\n return 'unocss'\n }\n\n // 检测传统的i-开头的CSS图标\n if (iconStr.indexOf('i-') === 0) {\n return 'css'\n }\n\n // 检测Iconify格式 (provider:name)\n if (iconStr.includes(':')) {\n return 'iconify'\n }\n\n // 默认为SVG sprite\n return 'svg'\n})\n\nconst outputName = computed(() => {\n if (isComponentName.value) {\n return ''\n }\n\n const iconStr = icon as string\n\n // 对于图片类型,直接返回URL\n if (outputType.value === 'img') {\n return iconStr\n }\n\n // 对于UnoCSS图标,直接返回类名\n if (outputType.value === 'unocss') {\n return iconStr\n }\n\n // 对于传统CSS图标\n if (iconStr.indexOf('i-') === 0 && outputType.value === 'css') {\n return iconStr\n }\n\n // 其他情况直接返回\n return iconStr\n})\n\n// 用正则匹配 size 是不是 number 值,再判断是否有 px 结尾的单位,没有则拼接\nfunction formatSize(size: string | number | undefined): string {\n if (!size) {\n return ''\n }\n\n // 如果是数字,直接添加 px\n if (typeof size === 'number') {\n return `${size}px`\n }\n\n // 如果是纯数字字符串,添加 px\n if (/^\\d+(?:\\.\\d+)?$/.test(size)) {\n return `${size}px`\n }\n\n // 如果已经有单位(px、em、rem等)或者不是纯数字,直接返回\n return size\n}\n\n// 统一的样式计算属性,适用于所有图标类型\n// 包含颜色、尺寸和变换(旋转、翻转)\n// Iconify 图标通过外层 i 标签应用这些样式\nconst style = computed(() => {\n const transform = [] as string[]\n switch (flip) {\n case 'horizontal':\n transform.push('rotateY(180deg)')\n break\n case 'vertical':\n transform.push('rotateX(180deg)')\n break\n case 'both':\n transform.push('rotateX(180deg)')\n transform.push('rotateY(180deg)')\n break\n // 对于 '' 和 undefined,不做任何处理\n }\n if (rotate) {\n transform.push(`rotate(${rotate % 360}deg)`)\n }\n\n // 构建基础样式\n let baseStyle = `${color ? `color: ${color};` : ''}${size ? `font-size: ${formatSize(size)};` : ''}${transform.length ? `transform: ${transform.join(' ')};` : ''}`\n\n // 拼接 attrs.style\n const attrsStyle = attrs.style\n if (attrsStyle) {\n if (typeof attrsStyle === 'string') {\n baseStyle += attrsStyle\n }\n else if (typeof attrsStyle === 'object') {\n // 处理对象形式的样式\n const styleEntries = Object.entries(attrsStyle as CSSProperties)\n const styleString = styleEntries\n .map(([key, value]) => `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`)\n .join('')\n baseStyle += styleString\n }\n }\n\n return baseStyle\n})\n\n// 图片加载状态管理\nconst imageLoading = ref(true)\nconst imageError = ref(false)\n\nfunction handleImageLoad() {\n imageLoading.value = false\n imageError.value = false\n}\n\nfunction handleImageError() {\n imageLoading.value = false\n imageError.value = true\n}\n</script>\n\n<template>\n <!-- Vue组件 -->\n <component\n :is=\"icon\"\n v-if=\"outputType === 'component'\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n class=\"size-inherit shrink-0\"\n />\n\n <!-- UnoCSS图标 -->\n <i\n v-else-if=\"outputType === 'unocss'\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n class=\"size-inherit shrink-0\"\n />\n\n <!-- CSS类图标 -->\n <i\n v-else-if=\"outputType === 'css'\"\n class=\"size-inherit shrink-0\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n />\n\n <!-- Iconify图标 -->\n <Icon\n v-else-if=\"outputType === 'iconify'\"\n class=\"size-inherit shrink-0\"\n :icon=\"outputName\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n />\n\n <!-- 图片 -->\n <template v-else-if=\"outputType === 'img'\">\n <!-- 加载中状态 -->\n <i\n v-if=\"imageLoading && loading\"\n class=\"size-inherit shrink-0\"\n :class=\"loading\"\n />\n <!-- 错误状态 -->\n <i\n v-else-if=\"imageError && fallback\"\n class=\"size-inherit shrink-0\"\n :class=\"fallback\"\n />\n <!-- 图片本体 -->\n <img\n v-else\n :src=\"outputName\"\n class=\"size-inherit shrink-0\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n @load=\"handleImageLoad\"\n @error=\"handleImageError\"\n >\n </template>\n\n <!-- SVG Sprite -->\n <svg\n v-else-if=\"outputName\"\n aria-hidden=\"true\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n class=\"size-inherit shrink-0\"\n >\n <use :xlink:href=\"`#${localIconPrefix}${outputName}`\" />\n </svg>\n</template>\n"],"names":["attrs","useAttrs","ns","useNamespace","isComponentName","computed","__props","icon","outputType","value","iconStr","test","str","startsWith","includes","indexOf","outputName","style","transform","flip","push","rotate","baseStyle","color","size","length","join","attrsStyle","Object","entries","map","key","replace","toLowerCase","imageLoading","ref","imageError","handleImageLoad","handleImageError","_createCommentVNode","_openBlock","_createBlock","_resolveDynamicComponent","class","_normalizeClass","_unref","b","_createElementBlock","_Fragment","_createElementVNode","_createVNode","Icon","loading","fallback","src","onLoad","onError","localIconPrefix"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("vue"),a=require("@qxs-bns/icons"),t=require("element-plus");const l={class:"image-slot"},i=["src","alt"],r=["onClick"],s=["onClick"],n={class:"tip-content"};var o=e.defineComponent({name:"QxsImageUpload",__name:"image-upload",props:e.mergeModels({action:{type:String,required:!0},headers:{type:null,required:!1},data:{type:null,required:!1},name:{type:String,required:!1,default:"file"},size:{type:Number,required:!1,default:20},width:{type:Number,required:!1,default:160},height:{type:Number,required:!1,default:90},accept:{type:String,required:!1,default:"image/jpeg,image/jpg,image/png,image/gif"},placeholder:{type:String,required:!1,default:"上传图片"},notip:{type:Boolean,required:!1,default:!1},tipText:{type:String,required:!1,default:""},beforeUpload:{type:null,required:!1},disabled:{type:Boolean,required:!1,default:!1},limit:{type:Number,required:!1,default:1}},{fileList:{type:Array,default:()=>[]},fileListModifiers:{}}),emits:e.mergeModels(["success","remove"],["update:fileList"]),setup(o,{emit:u}){const p=u,c=e.useAttrs(),d=e.useModel(o,"fileList");let m=0;const f=e.computed({get:()=>d.value.map((e,a)=>e.name&&e.status&&e.uid?e:{...e,name:e.name||`image-${a+1}.jpg`,status:e.status||"success",uid:e.uid||`qxs-image-${Date.now()}-${++m}`}),set(e){d.value=e}}),v=t.useNamespace("image-upload"),g=e.ref(0),h=e.ref({imageViewerVisible:!1,progress:{preview:"",percent:0}}),y=e.computed(()=>v.cssVarBlock({ns:v.namespace.value,width:`${o.width}px`,height:`${o.height}px`})),w=e.computed(()=>o.accept.split(",").map(e=>e.split("/").pop())),b=e.computed(()=>{if(o.tipText)return o.tipText;return`上传图片支持 ${w.value.join(" / ")} 格式,且图片大小不超过 ${o.size}MB`+(o.width&&o.height?`,建议图片尺寸为 ${o.width}×${o.height}`:"")});function k(e){e.url?(g.value=f.value.findIndex(a=>a.uid===e.uid),h.value.imageViewerVisible=!0):console.warn("文件缺少 URL,无法预览:",e)}function _(e){if(o.disabled)return;const a=f.value.findIndex(a=>a.uid===e.uid);a>-1&&d.value.splice(a,1)}function C(){h.value.imageViewerVisible=!1}const $=e=>{if(!e||!e.name)return t.ElMessage.error("文件信息无效!"),!1;const a=e.name.split(".").pop()?.toLowerCase()??"";if(!w.value.some(e=>e?.toLowerCase()===a))return t.ElMessage.error(`上传图片只支持 ${w.value.join(" / ")} 格式!`),!1;if(e.size/1024/1024>o.size)return t.ElMessage.error(`上传图片大小不能超过 ${o.size}MB!`),!1;try{h.value.progress.preview=URL.createObjectURL(e)}catch(e){console.warn("创建预览失败:",e)}return!o.beforeUpload||o.beforeUpload(e)},x=e=>{h.value.progress.percent=Math.floor(e.percent)},V=(...e)=>{h.value.progress.preview&&URL.revokeObjectURL(h.value.progress.preview),h.value.progress={preview:"",percent:0},p("success",...e)};return e.onUnmounted(()=>{h.value.progress.preview&&URL.revokeObjectURL(h.value.progress.preview)}),(o,u)=>{const p=e.resolveComponent("QxsIcon");return e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(e.unref(v).e("container")),style:e.normalizeStyle(y.value)},[e.createVNode(e.unref(t.ElUpload),e.mergeProps(e.unref(c),{"file-list":f.value,"onUpdate:fileList":u[0]||(u[0]=e=>f.value=e),drag:"",limit:o.limit,"list-type":"picture-card",headers:o.headers,action:o.action,data:o.data,name:o.name,accept:o.accept,"before-upload":$,"on-progress":x,"on-preview":k,"on-success":V,class:[e.unref(v).e("control"),o.limit<=f.value.length?e.unref(v).e("more-than-limit"):""],"on-remove":_}),{file:e.withCtx(({file:t})=>[t.url?(e.openBlock(),e.createElementBlock("img",{key:0,class:e.normalizeClass(`${e.unref(v).namespace.value}-upload-list__item-thumbnail`),src:t.url,alt:t.name||"图片"},null,10,i)):(e.openBlock(),e.createElementBlock("div",{key:1,class:e.normalizeClass(`${e.unref(v).namespace.value}-upload-list__item-thumbnail`),style:{display:"flex","align-items":"center","justify-content":"center",color:"#909399",background:"#f5f7fa"}}," 无图片 ",2)),e.createElementVNode("span",{class:e.normalizeClass(`${e.unref(v).namespace.value}-upload-list__item-actions`)},[t.url?(e.openBlock(),e.createElementBlock("span",{key:0,class:e.normalizeClass([`${e.unref(v).namespace.value}-upload-list__item-preview`,`${e.unref(v).namespace.value}-image-upload__action-btn`,`${e.unref(v).namespace.value}-image-upload__action-btn--preview`]),onClick:e=>k(t)},[e.createVNode(e.unref(a.ZoomIn),{size:"14px"})],10,r)):e.createCommentVNode("v-if",!0),t.url?(e.openBlock(),e.createElementBlock("span",{key:1,class:e.normalizeClass(`${e.unref(v).namespace.value}-upload-list__item-divider`)},null,2)):e.createCommentVNode("v-if",!0),e.createElementVNode("span",{class:e.normalizeClass([`${e.unref(v).namespace.value}-upload-list__item-delete`,`${e.unref(v).namespace.value}-image-upload__action-btn`,`${e.unref(v).namespace.value}-image-upload__action-btn--delete`,o.disabled?"is-disabled":""]),onClick:e=>_(t)},[e.createVNode(e.unref(a.Delete),{size:"14px"})],10,s)],2)]),tip:e.withCtx(()=>[e.renderSlot(o.$slots,"tip",{},()=>[o.notip?e.createCommentVNode("v-if",!0):(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(`${e.unref(v).namespace.value}-upload__tip-text`)},[e.createElementVNode("div",n,e.toDisplayString(b.value),1)],2))])]),default:e.withCtx(()=>[e.renderSlot(o.$slots,"default",{},()=>[e.createElementVNode("div",l,[e.createVNode(p,{size:"32px",icon:e.unref(a.UploadImage)},null,8,["icon"]),e.createElementVNode("p",null,e.toDisplayString(o.placeholder),1)])])]),_:3},16,["file-list","limit","headers","action","data","name","accept","class"]),h.value.imageViewerVisible?(e.openBlock(),e.createBlock(e.unref(t.ElImageViewer),{key:0,"url-list":f.value.map(e=>e.url).filter(Boolean),"initial-index":g.value,"show-progress":f.value.length>1,teleported:"",onClose:C},null,8,["url-list","initial-index","show-progress"])):e.createCommentVNode("v-if",!0)],6)}}});exports.default=o;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("vue"),r=require("@qxs-bns/icons"),l=require("element-plus");const a={class:"image-slot"},t=["src","alt"],i=["onClick"],s=["onClick"],o={class:"tip-content"};var n=e.defineComponent({name:"QxsImageUpload",__name:"image-upload",props:e.mergeModels({action:{type:String,required:!0},headers:{type:null,required:!1},data:{type:null,required:!1},name:{type:String,required:!1,default:"file"},size:{type:Number,required:!1,default:20},width:{type:Number,required:!1,default:160},height:{type:Number,required:!1,default:90},accept:{type:String,required:!1,default:"image/jpeg,image/jpg,image/png,image/gif"},placeholder:{type:String,required:!1,default:"上传图片"},notip:{type:Boolean,required:!1,default:!1},tipText:{type:String,required:!1,default:""},beforeUpload:{type:null,required:!1},disabled:{type:Boolean,required:!1,default:!1},limit:{type:Number,required:!1,default:1}},{fileList:{type:Array,default:()=>[]},fileListModifiers:{}}),emits:e.mergeModels(["success","remove"],["update:fileList"]),setup(n,{emit:u}){const p=u,c=e.useAttrs(),d=e.useModel(n,"fileList"),m=l.useNamespace("image-upload"),v=e.ref(0),f=e.ref({imageViewerVisible:!1,progress:{preview:"",percent:0}}),g=e.computed(()=>m.cssVarBlock({ns:m.namespace.value,width:`${n.width}px`,height:`${n.height}px`})),h=e.computed(()=>n.accept.split(",").map(e=>e.split("/").pop())),b=e.computed(()=>{if(n.tipText)return n.tipText;return`上传图片支持 ${h.value.join(" / ")} 格式,且图片大小不超过 ${n.size}MB`+(n.width&&n.height?`,建议图片尺寸为 ${n.width}×${n.height}`:"")});function w(e){e.url?(v.value=d.value.indexOf(e),f.value.imageViewerVisible=!0):console.warn("文件缺少 URL,无法预览:",e)}function y(e){if(n.disabled)return;e.url&&e.url.startsWith("blob:")&&URL.revokeObjectURL(e.url);const r=d.value.indexOf(e);r>-1&&d.value.splice(r,1),p("remove",e)}function k(){f.value.imageViewerVisible=!1}const _=e=>{if(!e||!e.name)return l.ElMessage.error("文件信息无效!"),!1;const r=e.name.split(".").pop()?.toLowerCase()??"";if(!h.value.some(e=>e?.toLowerCase()===r))return l.ElMessage.error(`上传图片只支持 ${h.value.join(" / ")} 格式!`),!1;if(e.size/1024/1024>n.size)return l.ElMessage.error(`上传图片大小不能超过 ${n.size}MB!`),!1;try{f.value.progress.preview=URL.createObjectURL(e)}catch(e){console.warn("创建预览失败:",e)}return!n.beforeUpload||n.beforeUpload(e)},C=e=>{f.value.progress.percent=Math.floor(e.percent)},V=(e,r,a)=>{f.value.progress.preview&&(URL.revokeObjectURL(f.value.progress.preview),f.value.progress.preview=""),f.value.progress.percent=0,l.ElMessage.error(`上传失败: ${e.message||"未知错误"}`)},x=(...e)=>{f.value.progress.preview&&URL.revokeObjectURL(f.value.progress.preview),f.value.progress={preview:"",percent:0},p("success",...e)};return e.onUnmounted(()=>{f.value.progress.preview&&(URL.revokeObjectURL(f.value.progress.preview),f.value.progress.preview=""),d.value.forEach(e=>{e.url&&e.url.startsWith("blob:")&&URL.revokeObjectURL(e.url)})}),(n,u)=>{const p=e.resolveComponent("QxsIcon");return e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(e.unref(m).e("container")),style:e.normalizeStyle(g.value)},[e.createVNode(e.unref(l.ElUpload),e.mergeProps(e.unref(c),{"file-list":d.value,"onUpdate:fileList":u[0]||(u[0]=e=>d.value=e),drag:"",limit:n.limit,"list-type":"picture-card",headers:n.headers,action:n.action,data:n.data,name:n.name,accept:n.accept,"before-upload":_,"on-progress":C,"on-preview":w,"on-success":x,"on-error":V,class:[e.unref(m).e("control"),n.limit<=d.value.length?e.unref(m).e("more-than-limit"):""],"on-remove":y}),{file:e.withCtx(({file:l})=>[l.url?(e.openBlock(),e.createElementBlock("img",{key:0,class:e.normalizeClass(`${e.unref(m).namespace.value}-upload-list__item-thumbnail`),src:l.url,alt:l.name||"图片"},null,10,t)):(e.openBlock(),e.createElementBlock("div",{key:1,class:e.normalizeClass(`${e.unref(m).namespace.value}-upload-list__item-thumbnail`),style:{display:"flex","align-items":"center","justify-content":"center",color:"#909399",background:"#f5f7fa"}}," 无图片 ",2)),e.createElementVNode("span",{class:e.normalizeClass(`${e.unref(m).namespace.value}-upload-list__item-actions`)},[l.url?(e.openBlock(),e.createElementBlock("span",{key:0,class:e.normalizeClass([`${e.unref(m).namespace.value}-upload-list__item-preview`,`${e.unref(m).namespace.value}-image-upload__action-btn`,`${e.unref(m).namespace.value}-image-upload__action-btn--preview`]),onClick:e=>w(l)},[e.createVNode(e.unref(r.ZoomIn),{size:"14px"})],10,i)):e.createCommentVNode("v-if",!0),l.url?(e.openBlock(),e.createElementBlock("span",{key:1,class:e.normalizeClass(`${e.unref(m).namespace.value}-upload-list__item-divider`)},null,2)):e.createCommentVNode("v-if",!0),e.createElementVNode("span",{class:e.normalizeClass([`${e.unref(m).namespace.value}-upload-list__item-delete`,`${e.unref(m).namespace.value}-image-upload__action-btn`,`${e.unref(m).namespace.value}-image-upload__action-btn--delete`,n.disabled?"is-disabled":""]),onClick:e=>y(l)},[e.createVNode(e.unref(r.Delete),{size:"14px"})],10,s)],2)]),tip:e.withCtx(()=>[e.renderSlot(n.$slots,"tip",{},()=>[n.notip?e.createCommentVNode("v-if",!0):(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(`${e.unref(m).namespace.value}-upload__tip-text`)},[e.createElementVNode("div",o,e.toDisplayString(b.value),1)],2))])]),default:e.withCtx(()=>[e.renderSlot(n.$slots,"default",{},()=>[e.createElementVNode("div",a,[e.createVNode(p,{size:"32px",icon:e.unref(r.UploadImage)},null,8,["icon"]),e.createElementVNode("p",null,e.toDisplayString(n.placeholder),1)])])]),_:3},16,["file-list","limit","headers","action","data","name","accept","class"]),f.value.imageViewerVisible?(e.openBlock(),e.createBlock(e.unref(l.ElImageViewer),{key:0,"url-list":d.value.map(e=>e.url).filter(Boolean),"initial-index":v.value,"show-progress":d.value.length>1,teleported:"",onClose:k},null,8,["url-list","initial-index","show-progress"])):e.createCommentVNode("v-if",!0)],6)}}});exports.default=n;
2
2
  //# sourceMappingURL=image-upload.vue.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-upload.vue.cjs","sources":["../../../../../../packages/components/src/image-upload/src/image-upload.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { UploadFile, UploadFiles, UploadProps } from 'element-plus'\nimport { Delete, UploadImage, ZoomIn } from '@qxs-bns/icons'\nimport { ElImageViewer, ElMessage, ElUpload, useNamespace } from 'element-plus'\nimport { computed, onUnmounted, ref, useAttrs } from 'vue'\n\n// 类型定义\nexport interface ImageUploadProps {\n /** 上传地址(必需) */\n action: UploadProps['action']\n /** 请求头 */\n headers?: UploadProps['headers']\n /** 上传时附带的额外参数 */\n data?: UploadProps['data']\n /** 上传的文件字段名 */\n name?: UploadProps['name']\n /** 文件大小限制(MB) */\n size?: number\n /** 图片显示宽度(px) */\n width?: number\n /** 图片显示高度(px) */\n height?: number\n /** 接受的文件类型 */\n accept?: string\n /** 上传区域提示文字 */\n placeholder?: string\n /** 是否隐藏提示文字 */\n notip?: boolean\n /** 自定义提示文字 */\n tipText?: string\n /** 上传前的钩子函数 */\n beforeUpload?: UploadProps['beforeUpload']\n /** 是否禁用 */\n disabled?: boolean\n /** 最大上传数量 */\n limit?: number\n}\n\ninterface UploadProgress {\n preview: string\n percent: number\n}\n\n// 组件名称\ndefineOptions({\n name: 'QxsImageUpload',\n})\n\n// Props 定义\nconst {\n action,\n headers,\n data,\n name = 'file',\n size = 20,\n width = 160,\n height = 90,\n limit = 1,\n placeholder = '上传图片',\n notip = false,\n accept = 'image/jpeg,image/jpg,image/png,image/gif',\n beforeUpload,\n tipText = '',\n disabled = false,\n} = defineProps<ImageUploadProps>()\n\n// Emits 定义 - 统一事件命名\nconst emits = defineEmits<{\n success: [res: any, uploadFile: UploadFile, uploadFiles: UploadFiles]\n remove: [file: UploadFile]\n}>()\n\nconst attrs = useAttrs()\n\nconst fileList = defineModel('fileList', {\n type: Array as () => UploadFile[],\n default: () => [],\n})\n\n// 生成唯一 ID 的计数器\nlet uidCounter = 0\n\n// 处理不完整的文件对象,自动补全必要属性\nconst normalizedFileList = computed({\n get() {\n return fileList.value.map((file, index) => {\n // 如果文件对象不完整,自动补全必要属性\n if (!file.name || !file.status || !file.uid) {\n return {\n ...file, // 保留原有属性\n name: file.name || `image-${index + 1}.jpg`,\n status: file.status || 'success',\n uid: file.uid || `qxs-image-${Date.now()}-${++uidCounter}`, // 确保唯一性\n } as UploadFile\n }\n return file\n })\n },\n set(value: UploadFile[]) {\n fileList.value = value\n },\n})\n\n// 命名空间\nconst nsEl = useNamespace('image-upload')\n\nconst initialIndex = ref(0)\n// 响应式数据\nconst uploadData = ref<{\n imageViewerVisible: boolean\n progress: UploadProgress\n}>({\n imageViewerVisible: false,\n progress: {\n preview: '',\n percent: 0,\n },\n})\n\nconst cssVar = computed(() => {\n return nsEl.cssVarBlock({\n ns: nsEl.namespace.value,\n width: `${width}px`,\n height: `${height}px`,\n })\n})\n\n// 计算属性\nconst exts = computed(() =>\n accept.split(',').map(ext => ext.split('/').pop()),\n)\n\nconst tipMessage = computed(() => {\n if (tipText) {\n return tipText\n }\n\n const formatText = `上传图片支持 ${exts.value.join(' / ')} 格式,且图片大小不超过 ${size}MB`\n const sizeText = width && height ? `,建议图片尺寸为 ${width}×${height}` : ''\n\n return formatText + sizeText\n})\n\n// 方法\nfunction onPreview(file: UploadFile) {\n // 确保文件有有效的 URL 才能预览\n if (!file.url) {\n console.warn('文件缺少 URL,无法预览:', file)\n return\n }\n\n // 使用 uid 作为唯一标识符来查找文件索引\n initialIndex.value = normalizedFileList.value.findIndex(item => item.uid === file.uid)\n uploadData.value.imageViewerVisible = true\n}\n\nfunction onRemove(file: UploadFile) {\n // 如果组件被禁用,阻止删除\n if (disabled) {\n return\n }\n\n // 使用 uid 作为唯一标识符来查找并删除文件\n const index = normalizedFileList.value.findIndex(item => item.uid === file.uid)\n if (index > -1) {\n fileList.value.splice(index, 1)\n }\n}\n\nfunction previewClose() {\n uploadData.value.imageViewerVisible = false\n}\n\nconst handleBeforeUpload: UploadProps['beforeUpload'] = (file) => {\n // 参数校验\n if (!file || !file.name) {\n ElMessage.error('文件信息无效!')\n return false\n }\n\n // 文件类型校验\n const fileExt = file.name.split('.').pop()?.toLowerCase() ?? ''\n const isTypeOk = exts.value.some(ext => ext?.toLowerCase() === fileExt)\n\n if (!isTypeOk) {\n ElMessage.error(`上传图片只支持 ${exts.value.join(' / ')} 格式!`)\n return false\n }\n\n // 文件大小校验\n const fileSizeMB = file.size / 1024 / 1024\n if (fileSizeMB > size) {\n ElMessage.error(`上传图片大小不能超过 ${size}MB!`)\n return false\n }\n\n // 设置预览\n try {\n uploadData.value.progress.preview = URL.createObjectURL(file)\n }\n catch (error) {\n console.warn('创建预览失败:', error)\n }\n\n // 执行自定义校验\n return beforeUpload ? beforeUpload(file) : true\n}\n\nconst onProgress: UploadProps['onProgress'] = (evt) => {\n uploadData.value.progress.percent = Math.floor(evt.percent)\n}\n\nconst onSuccess: UploadProps['onSuccess'] = (...args) => {\n // 清理预览 URL,防止内存泄漏\n if (uploadData.value.progress.preview) {\n URL.revokeObjectURL(uploadData.value.progress.preview)\n }\n\n uploadData.value.progress = {\n preview: '',\n percent: 0,\n }\n emits('success', ...args)\n}\n\n// 组件卸载时清理资源\nonUnmounted(() => {\n if (uploadData.value.progress.preview) {\n URL.revokeObjectURL(uploadData.value.progress.preview)\n }\n})\n</script>\n\n<template>\n <div\n :class=\"nsEl.e('container')\"\n :style=\"cssVar\"\n >\n <ElUpload\n v-bind=\"attrs\"\n v-model:file-list=\"normalizedFileList\"\n drag\n :limit=\"limit\"\n list-type=\"picture-card\"\n :headers=\"headers\"\n :action=\"action\"\n :data=\"data\"\n :name=\"name\"\n :accept=\"accept\"\n :before-upload=\"handleBeforeUpload\"\n :on-progress=\"onProgress\"\n :on-preview=\"onPreview\"\n :on-success=\"onSuccess\"\n :class=\"[nsEl.e('control'), limit <= normalizedFileList.length ? nsEl.e('more-than-limit') : '']\"\n :on-remove=\"onRemove\"\n >\n <slot>\n <div class=\"image-slot\">\n <QxsIcon\n size=\"32px\"\n :icon=\"UploadImage\"\n />\n <p>{{ placeholder }}</p>\n </div>\n </slot>\n <template #file=\"{ file }\">\n <img\n v-if=\"file.url\"\n :class=\"`${nsEl.namespace.value}-upload-list__item-thumbnail`\"\n :src=\"file.url\"\n :alt=\"file.name || '图片'\"\n >\n <div\n v-else\n :class=\"`${nsEl.namespace.value}-upload-list__item-thumbnail`\"\n style=\"display: flex; align-items: center; justify-content: center; color: #909399; background: #f5f7fa;\"\n >\n 无图片\n </div>\n <span :class=\"`${nsEl.namespace.value}-upload-list__item-actions`\">\n <span\n v-if=\"file.url\"\n :class=\"[\n `${nsEl.namespace.value}-upload-list__item-preview`,\n `${nsEl.namespace.value}-image-upload__action-btn`,\n `${nsEl.namespace.value}-image-upload__action-btn--preview`,\n ]\"\n @click=\"onPreview(file)\"\n >\n <ZoomIn size=\"14px\" />\n </span>\n <span\n v-if=\"file.url\"\n :class=\"`${nsEl.namespace.value}-upload-list__item-divider`\"\n />\n <span\n :class=\"[\n `${nsEl.namespace.value}-upload-list__item-delete`,\n `${nsEl.namespace.value}-image-upload__action-btn`,\n `${nsEl.namespace.value}-image-upload__action-btn--delete`,\n disabled ? 'is-disabled' : '',\n ]\"\n @click=\"onRemove(file)\"\n >\n <Delete size=\"14px\" />\n </span>\n </span>\n </template>\n <template #tip>\n <slot name=\"tip\">\n <div\n v-if=\"!notip\"\n :class=\"`${nsEl.namespace.value}-upload__tip-text`\"\n >\n <div class=\"tip-content\">\n {{ tipMessage }}\n </div>\n </div>\n </slot>\n </template>\n </ElUpload>\n\n <ElImageViewer\n v-if=\"uploadData.imageViewerVisible\"\n :url-list=\"normalizedFileList.map((item: UploadFile) => item.url!).filter(Boolean)\"\n :initial-index=\"initialIndex\"\n :show-progress=\"normalizedFileList.length > 1\"\n teleported\n @close=\"previewClose\"\n />\n </div>\n</template>\n"],"names":["emits","__emit","attrs","useAttrs","fileList","_useModel","__props","uidCounter","normalizedFileList","computed","get","value","map","file","index","name","status","uid","Date","now","set","nsEl","useNamespace","initialIndex","ref","uploadData","imageViewerVisible","progress","preview","percent","cssVar","cssVarBlock","ns","namespace","width","height","exts","split","ext","pop","tipMessage","tipText","join","size","onPreview","url","findIndex","item","console","warn","onRemove","disabled","splice","previewClose","handleBeforeUpload","ElMessage","error","fileExt","toLowerCase","some","URL","createObjectURL","beforeUpload","onProgress","evt","Math","floor","onSuccess","args","revokeObjectURL","onUnmounted","_createElementBlock","class","_normalizeClass","_unref","e","style","_createVNode","ElUpload","_mergeProps","$event","drag","limit","headers","action","data","accept","length","_withCtx","src","alt","display","color","background","_createElementVNode","onClick","ZoomIn","Delete","tip","_renderSlot","_ctx","notip","_hoisted_5","_toDisplayString","_hoisted_1","_component_QxsIcon","icon","UploadImage","placeholder","_createBlock","ElImageViewer","filter","Boolean","teleported","onClose"],"mappings":"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"}
1
+ {"version":3,"file":"image-upload.vue.cjs","sources":["../../../../../../packages/components/src/image-upload/src/image-upload.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { UploadFile, UploadFiles, UploadProps } from 'element-plus'\nimport { Delete, UploadImage, ZoomIn } from '@qxs-bns/icons'\nimport { ElImageViewer, ElMessage, ElUpload, useNamespace } from 'element-plus'\nimport { computed, onUnmounted, ref, useAttrs } from 'vue'\n\n// 类型定义\nexport interface ImageUploadProps {\n /** 上传地址(必需) */\n action: UploadProps['action']\n /** 请求头 */\n headers?: UploadProps['headers']\n /** 上传时附带的额外参数 */\n data?: UploadProps['data']\n /** 上传的文件字段名 */\n name?: UploadProps['name']\n /** 文件大小限制(MB) */\n size?: number\n /** 图片显示宽度(px) */\n width?: number\n /** 图片显示高度(px) */\n height?: number\n /** 接受的文件类型 */\n accept?: string\n /** 上传区域提示文字 */\n placeholder?: string\n /** 是否隐藏提示文字 */\n notip?: boolean\n /** 自定义提示文字 */\n tipText?: string\n /** 上传前的钩子函数 */\n beforeUpload?: UploadProps['beforeUpload']\n /** 是否禁用 */\n disabled?: boolean\n /** 最大上传数量 */\n limit?: number\n}\n\ninterface UploadProgress {\n preview: string\n percent: number\n}\n\n// 组件名称\ndefineOptions({\n name: 'QxsImageUpload',\n})\n\n// Props 定义\nconst {\n action,\n headers,\n data,\n name = 'file',\n size = 20,\n width = 160,\n height = 90,\n limit = 1,\n placeholder = '上传图片',\n notip = false,\n accept = 'image/jpeg,image/jpg,image/png,image/gif',\n beforeUpload,\n tipText = '',\n disabled = false,\n} = defineProps<ImageUploadProps>()\n\n// Emits 定义 - 统一事件命名\nconst emits = defineEmits<{\n success: [res: any, uploadFile: UploadFile, uploadFiles: UploadFiles]\n remove: [file: UploadFile]\n}>()\n\nconst attrs = useAttrs()\n\nconst fileList = defineModel('fileList', {\n type: Array as () => UploadFile[],\n default: () => [],\n})\n\n// 命名空间\nconst nsEl = useNamespace('image-upload')\n\nconst initialIndex = ref(0)\n// 响应式数据\nconst uploadData = ref<{\n imageViewerVisible: boolean\n progress: UploadProgress\n}>({\n imageViewerVisible: false,\n progress: {\n preview: '',\n percent: 0,\n },\n})\n\nconst cssVar = computed(() => {\n return nsEl.cssVarBlock({\n ns: nsEl.namespace.value,\n width: `${width}px`,\n height: `${height}px`,\n })\n})\n\n// 计算属性\nconst exts = computed(() =>\n accept.split(',').map(ext => ext.split('/').pop()),\n)\n\nconst tipMessage = computed(() => {\n if (tipText) {\n return tipText\n }\n\n const formatText = `上传图片支持 ${exts.value.join(' / ')} 格式,且图片大小不超过 ${size}MB`\n const sizeText = width && height ? `,建议图片尺寸为 ${width}×${height}` : ''\n\n return formatText + sizeText\n})\n\n// 方法\nfunction onPreview(file: UploadFile) {\n // 确保文件有有效的 URL 才能预览\n if (!file.url) {\n console.warn('文件缺少 URL,无法预览:', file)\n return\n }\n\n // 直接使用文件在列表中的索引\n initialIndex.value = fileList.value.indexOf(file)\n uploadData.value.imageViewerVisible = true\n}\n\nfunction onRemove(file: UploadFile) {\n // 如果组件被禁用,阻止删除\n if (disabled) {\n return\n }\n\n // 清理可能的 Object URL\n if (file.url && file.url.startsWith('blob:')) {\n URL.revokeObjectURL(file.url)\n }\n\n // 直接使用文件在列表中的索引来删除\n const index = fileList.value.indexOf(file)\n if (index > -1) {\n fileList.value.splice(index, 1)\n }\n\n // 触发删除事件\n emits('remove', file)\n}\n\nfunction previewClose() {\n uploadData.value.imageViewerVisible = false\n}\n\nconst handleBeforeUpload: UploadProps['beforeUpload'] = (file) => {\n // 参数校验\n if (!file || !file.name) {\n ElMessage.error('文件信息无效!')\n return false\n }\n\n // 文件类型校验\n const fileExt = file.name.split('.').pop()?.toLowerCase() ?? ''\n const isTypeOk = exts.value.some(ext => ext?.toLowerCase() === fileExt)\n\n if (!isTypeOk) {\n ElMessage.error(`上传图片只支持 ${exts.value.join(' / ')} 格式!`)\n return false\n }\n\n // 文件大小校验\n const fileSizeMB = file.size / 1024 / 1024\n if (fileSizeMB > size) {\n ElMessage.error(`上传图片大小不能超过 ${size}MB!`)\n return false\n }\n\n // 设置预览\n try {\n uploadData.value.progress.preview = URL.createObjectURL(file)\n }\n catch (error) {\n console.warn('创建预览失败:', error)\n }\n\n // 执行自定义校验\n return beforeUpload ? beforeUpload(file) : true\n}\n\nconst onProgress: UploadProps['onProgress'] = (evt) => {\n uploadData.value.progress.percent = Math.floor(evt.percent)\n}\n\nconst onError: UploadProps['onError'] = (error, _file, _fileList) => {\n // 上传失败时也要清理预览 URL\n if (uploadData.value.progress.preview) {\n URL.revokeObjectURL(uploadData.value.progress.preview)\n uploadData.value.progress.preview = ''\n }\n uploadData.value.progress.percent = 0\n\n ElMessage.error(`上传失败: ${error.message || '未知错误'}`)\n}\n\nconst onSuccess: UploadProps['onSuccess'] = (...args) => {\n // 清理预览 URL,防止内存泄漏\n if (uploadData.value.progress.preview) {\n URL.revokeObjectURL(uploadData.value.progress.preview)\n }\n\n uploadData.value.progress = {\n preview: '',\n percent: 0,\n }\n emits('success', ...args)\n}\n\n// 清理所有可能的 Object URL\nfunction cleanupObjectURLs() {\n // 清理进度预览 URL\n if (uploadData.value.progress.preview) {\n URL.revokeObjectURL(uploadData.value.progress.preview)\n uploadData.value.progress.preview = ''\n }\n\n // 清理文件列表中可能的 Object URL\n fileList.value.forEach((file) => {\n if (file.url && file.url.startsWith('blob:')) {\n URL.revokeObjectURL(file.url)\n }\n })\n}\n\n// 组件卸载时清理资源\nonUnmounted(() => {\n cleanupObjectURLs()\n})\n</script>\n\n<template>\n <div\n :class=\"nsEl.e('container')\"\n :style=\"cssVar\"\n >\n <ElUpload\n v-bind=\"attrs\"\n v-model:file-list=\"fileList\"\n drag\n :limit=\"limit\"\n list-type=\"picture-card\"\n :headers=\"headers\"\n :action=\"action\"\n :data=\"data\"\n :name=\"name\"\n :accept=\"accept\"\n :before-upload=\"handleBeforeUpload\"\n :on-progress=\"onProgress\"\n :on-preview=\"onPreview\"\n :on-success=\"onSuccess\"\n :on-error=\"onError\"\n :class=\"[nsEl.e('control'), limit <= fileList.length ? nsEl.e('more-than-limit') : '']\"\n :on-remove=\"onRemove\"\n >\n <slot>\n <div class=\"image-slot\">\n <QxsIcon\n size=\"32px\"\n :icon=\"UploadImage\"\n />\n <p>{{ placeholder }}</p>\n </div>\n </slot>\n <template #file=\"{ file }\">\n <img\n v-if=\"file.url\"\n :class=\"`${nsEl.namespace.value}-upload-list__item-thumbnail`\"\n :src=\"file.url\"\n :alt=\"file.name || '图片'\"\n >\n <div\n v-else\n :class=\"`${nsEl.namespace.value}-upload-list__item-thumbnail`\"\n style=\"display: flex; align-items: center; justify-content: center; color: #909399; background: #f5f7fa;\"\n >\n 无图片\n </div>\n <span :class=\"`${nsEl.namespace.value}-upload-list__item-actions`\">\n <span\n v-if=\"file.url\"\n :class=\"[\n `${nsEl.namespace.value}-upload-list__item-preview`,\n `${nsEl.namespace.value}-image-upload__action-btn`,\n `${nsEl.namespace.value}-image-upload__action-btn--preview`,\n ]\"\n @click=\"onPreview(file)\"\n >\n <ZoomIn size=\"14px\" />\n </span>\n <span\n v-if=\"file.url\"\n :class=\"`${nsEl.namespace.value}-upload-list__item-divider`\"\n />\n <span\n :class=\"[\n `${nsEl.namespace.value}-upload-list__item-delete`,\n `${nsEl.namespace.value}-image-upload__action-btn`,\n `${nsEl.namespace.value}-image-upload__action-btn--delete`,\n disabled ? 'is-disabled' : '',\n ]\"\n @click=\"onRemove(file)\"\n >\n <Delete size=\"14px\" />\n </span>\n </span>\n </template>\n <template #tip>\n <slot name=\"tip\">\n <div\n v-if=\"!notip\"\n :class=\"`${nsEl.namespace.value}-upload__tip-text`\"\n >\n <div class=\"tip-content\">\n {{ tipMessage }}\n </div>\n </div>\n </slot>\n </template>\n </ElUpload>\n\n <ElImageViewer\n v-if=\"uploadData.imageViewerVisible\"\n :url-list=\"fileList.map((item: UploadFile) => item.url!).filter(Boolean)\"\n :initial-index=\"initialIndex\"\n :show-progress=\"fileList.length > 1\"\n teleported\n @close=\"previewClose\"\n />\n </div>\n</template>\n"],"names":["emits","__emit","attrs","useAttrs","fileList","_useModel","__props","nsEl","useNamespace","initialIndex","ref","uploadData","imageViewerVisible","progress","preview","percent","cssVar","computed","cssVarBlock","ns","namespace","value","width","height","exts","split","map","ext","pop","tipMessage","tipText","join","size","onPreview","file","url","indexOf","console","warn","onRemove","disabled","startsWith","URL","revokeObjectURL","index","splice","previewClose","handleBeforeUpload","name","ElMessage","error","fileExt","toLowerCase","some","createObjectURL","beforeUpload","onProgress","evt","Math","floor","onError","_file","_fileList","message","onSuccess","args","onUnmounted","forEach","_createElementBlock","class","_normalizeClass","_unref","e","style","_createVNode","ElUpload","_mergeProps","$event","drag","limit","headers","action","data","accept","length","_withCtx","src","alt","display","color","background","_createElementVNode","onClick","ZoomIn","Delete","tip","_renderSlot","_ctx","notip","_hoisted_5","_toDisplayString","_hoisted_1","_component_QxsIcon","icon","UploadImage","placeholder","_createBlock","ElImageViewer","item","filter","Boolean","teleported","onClose"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qxs-bns/components",
3
3
  "type": "module",
4
- "version": "0.0.75",
4
+ "version": "0.0.77",
5
5
  "description": "Vue 3 Component Library",
6
6
  "license": "MIT",
7
7
  "homepage": "https://qxs-bns.pages.dev/guide/components/overview.html",