vft 0.0.385 → 0.0.387

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,6 +1,6 @@
1
- import { defineComponent as L, useSlots as j, shallowRef as N, computed as b, onBeforeUnmount as O, provide as q, toRef as D, createElementBlock as H, openBlock as c, normalizeClass as K, unref as o, createBlock as y, createCommentVNode as a, renderSlot as s, createSlots as z, withCtx as f, createVNode as W, mergeProps as B } from "vue";
1
+ import { defineComponent as L, useSlots as j, shallowRef as N, computed as z, onBeforeUnmount as O, provide as q, toRef as D, createElementBlock as H, openBlock as c, normalizeClass as K, unref as o, createBlock as y, createCommentVNode as s, renderSlot as n, createSlots as B, withCtx as f, createVNode as W, mergeProps as C } from "vue";
2
2
  import "../form/index.js";
3
- import { VftMessage as C } from "../message/index.js";
3
+ import { VftMessage as g } from "../message/index.js";
4
4
  import { ajaxUpload as A } from "./ajax.js";
5
5
  import { uploadContextKey as G } from "./constants.js";
6
6
  import E from "./upload-content.vue2.js";
@@ -35,6 +35,7 @@ const X = L({
35
35
  sizeWidth: { default: void 0 },
36
36
  sizeHeight: { default: void 0 },
37
37
  onSizeInvalid: {},
38
+ onTypeInvalid: {},
38
39
  beforeUpload: {},
39
40
  beforeRemove: {},
40
41
  onRemove: {},
@@ -47,30 +48,30 @@ const X = L({
47
48
  } },
48
49
  onSizeExceed: {}
49
50
  },
50
- setup(P, { expose: M }) {
51
- const t = P, r = j(), v = Q(), u = N(), {
52
- abort: g,
53
- submit: h,
54
- clearFiles: w,
55
- uploadFiles: n,
51
+ setup(I, { expose: P }) {
52
+ const t = I, i = j(), v = Q(), u = N(), {
53
+ abort: h,
54
+ submit: w,
55
+ clearFiles: $,
56
+ uploadFiles: r,
56
57
  handleStart: S,
57
- handleError: U,
58
+ handleError: M,
58
59
  handleRemove: p,
59
- handleSuccess: I,
60
- handleProgress: T
61
- } = J(t, u), m = b(() => t.listType === "picture-card"), V = {
62
- abort: g,
63
- submit: h,
64
- clearFiles: w,
65
- uploadFiles: n,
60
+ handleSuccess: T,
61
+ handleProgress: U
62
+ } = J(t, u), m = z(() => t.listType === "picture-card"), V = {
63
+ abort: h,
64
+ submit: w,
65
+ clearFiles: $,
66
+ uploadFiles: r,
66
67
  uploadRef: u
67
- }, $ = b(() => ({
68
+ }, k = z(() => ({
68
69
  ...t,
69
- fileList: n.value,
70
+ fileList: r.value,
70
71
  onStart: S,
71
- onProgress: T,
72
- onSuccess: I,
73
- onError: U,
72
+ onProgress: U,
73
+ onSuccess: T,
74
+ onError: M,
74
75
  onRemove: p,
75
76
  onChange: (e, l) => {
76
77
  t.onChange?.(e, l, V);
@@ -80,31 +81,39 @@ const X = L({
80
81
  t.onSizeExceed?.(e, l);
81
82
  else {
82
83
  const d = `文件大小不能超过 ${(l / 1048576).toFixed(2)}MB`;
83
- console.warn("默认文件大小超出提示:", d), C.warning(d);
84
+ console.warn("默认文件大小超出提示:", d), g.warning(d);
84
85
  }
85
86
  },
86
- onSizeInvalid: (e, l, i, d, k) => {
87
+ onSizeInvalid: (e, l, a, d, R) => {
87
88
  if (t.onSizeInvalid)
88
- t.onSizeInvalid(e, l, i, d, k);
89
+ t.onSizeInvalid(e, l, a, d, R);
89
90
  else {
90
- const R = `文件尺寸不符合要求,当前尺寸:${l}x${i},期望尺寸:${d}x${k}`;
91
- console.warn("默认尺寸校验提示:", R), C.warning(R);
91
+ const b = `文件尺寸不符合要求,当前尺寸:${l}x${a},期望尺寸:${d}x${R}`;
92
+ console.warn("默认尺寸校验提示:", b), g.warning(b);
93
+ }
94
+ },
95
+ onTypeInvalid: (e, l) => {
96
+ if (t.onTypeInvalid)
97
+ t.onTypeInvalid(e, l);
98
+ else {
99
+ const a = `文件类型不符合要求,当前文件:${e.name},支持的类型:${l}`;
100
+ console.warn("默认文件类型校验提示:", a), g.warning(a);
92
101
  }
93
102
  }
94
103
  }));
95
104
  return O(() => {
96
- n.value.forEach(({ url: e }) => {
105
+ r.value.forEach(({ url: e }) => {
97
106
  e?.startsWith("blob:") && URL.revokeObjectURL(e);
98
107
  });
99
108
  }), q(G, {
100
109
  accept: D(t, "accept")
101
- }), M({
102
- abort: g,
103
- submit: h,
104
- clearFiles: w,
110
+ }), P({
111
+ abort: h,
112
+ submit: w,
113
+ clearFiles: $,
105
114
  handleStart: S,
106
115
  handleRemove: p,
107
- fileList: n
116
+ fileList: r
108
117
  }), (e, l) => (c(), H("div", {
109
118
  class: K(["vft-upload-wrapper", { disabled: o(v) }])
110
119
  }, [
@@ -112,18 +121,18 @@ const X = L({
112
121
  key: 0,
113
122
  disabled: o(v),
114
123
  "list-type": e.listType,
115
- files: o(n),
124
+ files: o(r),
116
125
  "handle-preview": e.onPreview,
117
126
  onRemove: o(p)
118
- }, z({
127
+ }, B({
119
128
  append: f(() => [
120
- W(E, B({
129
+ W(E, C({
121
130
  ref_key: "uploadRef",
122
131
  ref: u
123
- }, $.value), {
132
+ }, k.value), {
124
133
  default: f(() => [
125
- o(r).trigger ? s(e.$slots, "trigger", { key: 0 }) : a("", !0),
126
- !o(r).trigger && o(r).default ? s(e.$slots, "default", { key: 1 }) : a("", !0)
134
+ o(i).trigger ? n(e.$slots, "trigger", { key: 0 }) : s("", !0),
135
+ !o(i).trigger && o(i).default ? n(e.$slots, "default", { key: 1 }) : s("", !0)
127
136
  ]),
128
137
  _: 3
129
138
  }, 16)
@@ -132,41 +141,41 @@ const X = L({
132
141
  }, [
133
142
  e.$slots.file ? {
134
143
  name: "default",
135
- fn: f(({ file: i }) => [
136
- s(e.$slots, "file", { file: i })
144
+ fn: f(({ file: a }) => [
145
+ n(e.$slots, "file", { file: a })
137
146
  ]),
138
147
  key: "0"
139
148
  } : void 0
140
- ]), 1032, ["disabled", "list-type", "files", "handle-preview", "onRemove"])) : a("", !0),
141
- !m.value || m.value && !e.showFileList ? (c(), y(E, B({
149
+ ]), 1032, ["disabled", "list-type", "files", "handle-preview", "onRemove"])) : s("", !0),
150
+ !m.value || m.value && !e.showFileList ? (c(), y(E, C({
142
151
  key: 1,
143
152
  ref_key: "uploadRef",
144
153
  ref: u
145
- }, $.value), {
154
+ }, k.value), {
146
155
  default: f(() => [
147
- o(r).trigger ? s(e.$slots, "trigger", { key: 0 }) : a("", !0),
148
- !o(r).trigger && o(r).default ? s(e.$slots, "default", { key: 1 }) : a("", !0)
156
+ o(i).trigger ? n(e.$slots, "trigger", { key: 0 }) : s("", !0),
157
+ !o(i).trigger && o(i).default ? n(e.$slots, "default", { key: 1 }) : s("", !0)
149
158
  ]),
150
159
  _: 3
151
- }, 16)) : a("", !0),
152
- e.$slots.trigger ? s(e.$slots, "default", { key: 2 }) : a("", !0),
153
- s(e.$slots, "tip"),
160
+ }, 16)) : s("", !0),
161
+ e.$slots.trigger ? n(e.$slots, "default", { key: 2 }) : s("", !0),
162
+ n(e.$slots, "tip"),
154
163
  !m.value && e.showFileList ? (c(), y(F, {
155
164
  key: 3,
156
165
  disabled: o(v),
157
166
  "list-type": e.listType,
158
- files: o(n),
167
+ files: o(r),
159
168
  "handle-preview": e.onPreview,
160
169
  onRemove: o(p)
161
- }, z({ _: 2 }, [
170
+ }, B({ _: 2 }, [
162
171
  e.$slots.file ? {
163
172
  name: "default",
164
- fn: f(({ file: i }) => [
165
- s(e.$slots, "file", { file: i })
173
+ fn: f(({ file: a }) => [
174
+ n(e.$slots, "file", { file: a })
166
175
  ]),
167
176
  key: "0"
168
177
  } : void 0
169
- ]), 1032, ["disabled", "list-type", "files", "handle-preview", "onRemove"])) : a("", !0)
178
+ ]), 1032, ["disabled", "list-type", "files", "handle-preview", "onRemove"])) : s("", !0)
170
179
  ], 2));
171
180
  }
172
181
  });
@@ -25,3 +25,10 @@ export declare function validateFileSize(file: File, expectedWidth: number, expe
25
25
  * @returns boolean
26
26
  */
27
27
  export declare function isImageOrVideo(file: File): boolean;
28
+ /**
29
+ * 验证文件类型是否符合accept属性要求
30
+ * @param file 文件对象
31
+ * @param accept accept属性值,如 "image/*,.pdf,.doc"
32
+ * @returns boolean
33
+ */
34
+ export declare function validateFileType(file: File, accept: string): boolean;
@@ -1,40 +1,53 @@
1
- function a(e) {
2
- return new Promise((n, i) => {
3
- const r = e.type.startsWith("image/"), o = e.type.startsWith("video/");
4
- if (!r && !o) {
5
- i(new Error("文件类型不支持尺寸校验"));
1
+ function o(r) {
2
+ return new Promise((n, s) => {
3
+ const e = r.type.startsWith("image/"), i = r.type.startsWith("video/");
4
+ if (!e && !i) {
5
+ s(new Error("文件类型不支持尺寸校验"));
6
6
  return;
7
7
  }
8
- if (r) {
8
+ if (e) {
9
9
  const t = new Image();
10
10
  t.onload = () => {
11
11
  n({ width: t.naturalWidth, height: t.naturalHeight });
12
12
  }, t.onerror = () => {
13
- i(new Error("图片加载失败"));
14
- }, t.src = URL.createObjectURL(e);
15
- } else if (o) {
13
+ s(new Error("图片加载失败"));
14
+ }, t.src = URL.createObjectURL(r);
15
+ } else if (i) {
16
16
  const t = document.createElement("video");
17
17
  t.onloadedmetadata = () => {
18
18
  n({ width: t.videoWidth, height: t.videoHeight }), URL.revokeObjectURL(t.src);
19
19
  }, t.onerror = () => {
20
- i(new Error("视频加载失败")), URL.revokeObjectURL(t.src);
21
- }, t.src = URL.createObjectURL(e);
20
+ s(new Error("视频加载失败")), URL.revokeObjectURL(t.src);
21
+ }, t.src = URL.createObjectURL(r);
22
22
  }
23
23
  });
24
24
  }
25
- async function s(e, n, i) {
25
+ async function a(r, n, s) {
26
26
  try {
27
- const { width: r, height: o } = await a(e);
28
- return { isValid: r === n && o === i, width: r, height: o };
29
- } catch (r) {
30
- return console.error("尺寸校验失败:", r), { isValid: !1, width: 0, height: 0 };
27
+ const { width: e, height: i } = await o(r);
28
+ return { isValid: e === n && i === s, width: e, height: i };
29
+ } catch (e) {
30
+ return console.error("尺寸校验失败:", e), { isValid: !1, width: 0, height: 0 };
31
31
  }
32
32
  }
33
- function c(e) {
34
- return e.type.startsWith("image/") || e.type.startsWith("video/");
33
+ function c(r) {
34
+ return r.type.startsWith("image/") || r.type.startsWith("video/");
35
+ }
36
+ function d(r, n) {
37
+ return n ? n.split(",").map((e) => e.trim()).some((e) => {
38
+ if (e.startsWith(".")) {
39
+ const i = e.toLowerCase();
40
+ return r.name.toLowerCase().endsWith(i);
41
+ } else if (e.includes("/*")) {
42
+ const i = e.replace("/*", "");
43
+ return r.type.startsWith(i + "/");
44
+ } else
45
+ return r.type === e;
46
+ }) : !0;
35
47
  }
36
48
  export {
37
- a as getFileDimensions,
49
+ o as getFileDimensions,
38
50
  c as isImageOrVideo,
39
- s as validateFileSize
51
+ a as validateFileSize,
52
+ d as validateFileType
40
53
  };
@@ -1,6 +1,6 @@
1
1
  declare const _default: {
2
2
  "name": "vft",
3
- "version": "0.0.385",
3
+ "version": "0.0.387",
4
4
  "license": "MIT",
5
5
  "main": "lib/index.cjs",
6
6
  "module": "es/index.js",
@@ -1,4 +1,4 @@
1
- const o = "0.0.385";
1
+ const o = "0.0.387";
2
2
  export {
3
3
  o as version
4
4
  };
@@ -1 +1 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),i=require("@vft/utils");require("../button/index.cjs");require("@vueuse/core");const l=require("./hooks/use-global-config.cjs");require("lodash-es");const d=require("../../hooks/use-namespace/index.cjs");require("../../hooks/use-model-toggle/index.cjs");require("@popperjs/core");require("../../hooks/use-z-index/index.cjs");const f=require("./constants.cjs"),h=e.defineComponent({name:"vft-config-provider"}),g=e.defineComponent({...h,props:{theme:{default:"light"},themeVars:{},themeVarsDark:{},themeVarsLight:{},iconfontClass:{},iconifyPrefixClass:{default:"ico-"},namespace:{default:"vft"},zIndex:{},message:{},projectCfg:{},button:{},baseUnit:{type:[String,Boolean],default:!1},baseUnitSize:{default:1080}},setup(n){const o=d.useNamespace("config-provider");e.watch(()=>n.message,t=>{Object.assign(f.messageConfig,t??{})},{immediate:!0,deep:!0});const c=e.computed(()=>{const t=n.theme==="dark";return u({...n.themeVars,...t?n.themeVarsDark:n.themeVarsLight})});if(i.inBrowser){const t=()=>{document.documentElement.classList.add(n.theme)},s=(a=n.theme)=>{document.documentElement.classList.remove(a)};e.watch(()=>n.theme,(a,r)=>{r&&s(r),t()},{immediate:!0}),e.onActivated(t),e.onDeactivated(s),e.onBeforeUnmount(s)}function u(t){const s={};return Object.keys(t).forEach(a=>{s[`--${n.namespace}-${i.kebabCase(a)}`]=t[a]}),s}const m=e.getCurrentInstance();return l.provideGlobalConfig(m.props),(t,s)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(e.unref(o).b()),style:e.normalizeStyle(c.value)},[e.renderSlot(t.$slots,"default")],6))}});exports.default=g;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),i=require("@vft/utils");require("../button/index.cjs");require("@vueuse/core");const d=require("./hooks/use-global-config.cjs");require("lodash-es");const f=require("../../hooks/use-namespace/index.cjs");require("../../hooks/use-model-toggle/index.cjs");require("@popperjs/core");require("../../hooks/use-z-index/index.cjs");const r=require("./constants.cjs"),g=e.defineComponent({name:"vft-config-provider"}),h=e.defineComponent({...g,props:{theme:{default:"light"},themeVars:{},themeVarsDark:{},themeVarsLight:{},iconfontClass:{},iconifyPrefixClass:{default:"ico-"},namespace:{default:"vft"},zIndex:{},message:{},projectCfg:{},button:{},baseUnit:{type:[String,Boolean],default:!1},baseUnitSize:{default:1080}},setup(n){const c=f.useNamespace("config-provider");e.watch(()=>n.message,t=>{Object.keys(r.messageConfig).forEach(s=>{delete r.messageConfig[s]}),Object.assign(r.messageConfig,t??{})},{immediate:!0,deep:!0});const u=e.computed(()=>{const t=n.theme==="dark";return m({...n.themeVars,...t?n.themeVarsDark:n.themeVarsLight})});if(i.inBrowser){const t=()=>{document.documentElement.classList.add(n.theme)},s=(a=n.theme)=>{document.documentElement.classList.remove(a)};e.watch(()=>n.theme,(a,o)=>{o&&s(o),t()},{immediate:!0}),e.onActivated(t),e.onDeactivated(s),e.onBeforeUnmount(s)}function m(t){const s={};return Object.keys(t).forEach(a=>{s[`--${n.namespace}-${i.kebabCase(a)}`]=t[a]}),s}const l=e.getCurrentInstance();return d.provideGlobalConfig(l.props),(t,s)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(e.unref(c).b()),style:e.normalizeStyle(u.value)},[e.renderSlot(t.$slots,"default")],6))}});exports.default=h;
@@ -95,6 +95,9 @@ export declare const VftUpload: import("vft/es/utils").SFCWithInstall<{
95
95
  onSizeInvalid: {
96
96
  type: import("vue").PropType<(file: File, width: number, height: number, expectedWidth: number, expectedHeight: number) => void>;
97
97
  };
98
+ onTypeInvalid: {
99
+ type: import("vue").PropType<(file: File, acceptedTypes: string) => void>;
100
+ };
98
101
  beforeUpload: {
99
102
  type: import("vue").PropType<(rawFile: import("./types").UploadRawFile) => import("../types").Awaitable<void | undefined | null | boolean | File | Blob>>;
100
103
  };
@@ -288,6 +291,9 @@ export declare const VftUpload: import("vft/es/utils").SFCWithInstall<{
288
291
  onSizeInvalid: {
289
292
  type: import("vue").PropType<(file: File, width: number, height: number, expectedWidth: number, expectedHeight: number) => void>;
290
293
  };
294
+ onTypeInvalid: {
295
+ type: import("vue").PropType<(file: File, acceptedTypes: string) => void>;
296
+ };
291
297
  beforeUpload: {
292
298
  type: import("vue").PropType<(rawFile: import("./types").UploadRawFile) => import("../types").Awaitable<void | undefined | null | boolean | File | Blob>>;
293
299
  };
@@ -478,6 +484,9 @@ export declare const VftUpload: import("vft/es/utils").SFCWithInstall<{
478
484
  onSizeInvalid: {
479
485
  type: import("vue").PropType<(file: File, width: number, height: number, expectedWidth: number, expectedHeight: number) => void>;
480
486
  };
487
+ onTypeInvalid: {
488
+ type: import("vue").PropType<(file: File, acceptedTypes: string) => void>;
489
+ };
481
490
  beforeUpload: {
482
491
  type: import("vue").PropType<(rawFile: import("./types").UploadRawFile) => import("../types").Awaitable<void | undefined | null | boolean | File | Blob>>;
483
492
  };
@@ -46,6 +46,8 @@ export interface UploadHooks {
46
46
  onExceed: (files: File[], uploadFiles: UploadUserFile[]) => void;
47
47
  /** 文件大小超出限制时的回调 */
48
48
  onSizeExceed?: (file: File, maxSize: number) => void;
49
+ /** 文件类型不符合要求时的回调 */
50
+ onTypeInvalid?: (file: File, acceptedTypes: string) => void;
49
51
  }
50
52
  export interface UploadBasicProps {
51
53
  action?: string;
@@ -77,6 +79,8 @@ export interface UploadBasicProps {
77
79
  sizeHeight?: number;
78
80
  /** 尺寸不符合时的回调函数 */
79
81
  onSizeInvalid?: (file: File, width: number, height: number, expectedWidth: number, expectedHeight: number) => void;
82
+ /** 文件类型不符合要求时的回调 */
83
+ onTypeInvalid?: (file: File, acceptedTypes: string) => void;
80
84
  }
81
85
  export interface UploadEventProps {
82
86
  beforeUpload?: UploadHooks['beforeUpload'];
@@ -90,6 +94,7 @@ export interface UploadEventProps {
90
94
  onExceed?: UploadHooks['onExceed'];
91
95
  onSizeExceed?: UploadHooks['onSizeExceed'];
92
96
  onSizeInvalid?: UploadBasicProps['onSizeInvalid'];
97
+ onTypeInvalid?: UploadBasicProps['onTypeInvalid'];
93
98
  }
94
99
  export interface UploadContentEventProps {
95
100
  beforeUpload?: UploadHooks['beforeUpload'];
@@ -100,6 +105,7 @@ export interface UploadContentEventProps {
100
105
  onError?: (err: UploadAjaxError, rawFile: UploadRawFile) => void;
101
106
  onExceed?: UploadHooks['onExceed'];
102
107
  onSizeExceed?: UploadHooks['onSizeExceed'];
108
+ onTypeInvalid?: UploadHooks['onTypeInvalid'];
103
109
  }
104
110
  export interface UploadContentBasicProps extends Omit<UploadBasicProps, 'fileList'> {
105
111
  fileList?: UploadUserFile[];
@@ -1 +1 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const t=require("vue");require("../form/index.cjs");require("@vueuse/core");const S=require("@vft/utils"),D=require("../../utils/error.cjs");require("../config-provider/hooks/use-global-config.cjs");const z=require("lodash-es"),L=require("../../hooks/use-namespace/index.cjs");require("../../hooks/use-model-toggle/index.cjs");require("@popperjs/core");require("../../hooks/use-z-index/index.cjs");const M=require("./ajax.cjs"),F=require("./constants.cjs"),K=require("./upload-dragger.vue2.cjs"),R=require("./utils.cjs"),T=require("../form/hooks/use-form-common-props.cjs"),V=require("../form/hooks/use-form-item.cjs"),H=["onKeydown"],W=["name","multiple","accept"],$=t.defineComponent({name:"VftUploadContent",inheritAttrs:!1}),A=t.defineComponent({...$,props:{fileList:{default:[]},replaceOnLimit:{type:Boolean,default:!1},action:{default:"#"},headers:{},method:{default:"post"},data:{},multiple:{type:Boolean,default:!1},name:{default:"file"},drag:{type:Boolean,default:!1},withCredentials:{type:Boolean},showFileList:{type:Boolean,default:!0},accept:{default:""},type:{default:"select"},autoUpload:{type:Boolean,default:!1},listType:{default:"text"},httpRequest:{type:Function,default:M.ajaxUpload},disabled:{type:Boolean},limit:{},maxSize:{default:void 0},sizeExceedMessage:{default:"文件大小超出限制"},sizeWidth:{default:void 0},sizeHeight:{default:void 0},onSizeInvalid:{},beforeUpload:{},onRemove:{},onStart:{},onSuccess:{},onProgress:{},onError:{},onExceed:{},onSizeExceed:{}},setup(U,{expose:w}){const r=U,g=L.useNamespace("upload"),E=T.useFormDisabled(),{formItem:x}=V.useFormItem(),c=t.shallowRef({}),y=t.shallowRef(),C=async e=>{if(e.length===0)return;const{autoUpload:o,limit:a,fileList:s,multiple:f,onStart:p,onExceed:q,replaceOnLimit:b,maxSize:u,onSizeExceed:v,sizeWidth:m,sizeHeight:h,onSizeInvalid:d}=r;if(u){const i=e.filter(n=>n.size>u);if(i.length>0&&(i.forEach(n=>{v&&v(n,u)}),e=e.filter(n=>n.size<=u),e.length===0))return}if(m&&h){const i=[];for(const n of e)if(R.isImageOrVideo(n))try{const{isValid:l,width:P,height:j}=await R.validateFileSize(n,m,h);l?i.push(n):d&&d(n,P,j,m,h)}catch(l){console.error("尺寸校验失败:",l)}else i.push(n);if(e=i,e.length===0)return}if(a&&s&&s.length+e.length>a&&!(a===1&&b)){q?.(e,s);return}f||(e=e.slice(0,1));for(const i of e){const n=i;n.uid=F.genFileId(),p?.(n),o&&k(n)}},k=async e=>{if(y.value.value="",!r.beforeUpload)return _(e);let o,a={};try{const f=r.data,p=r.beforeUpload(e);a=S.isObject(r.data)?z.cloneDeep(r.data):r.data,o=await p,S.isObject(r.data)&&z.isEqual(f,a)&&(a=z.cloneDeep(r.data))}catch{o=!1}if(o===!1){r.onRemove?.(e);return}let s=e;o instanceof Blob&&(o instanceof File?s=o:s=new File([o],e.name,{type:e.type})),_(Object.assign(s,{uid:e.uid}),a)},_=(e,o)=>{const{headers:a,data:s,method:f,withCredentials:p,name:q,action:b,onProgress:u,onSuccess:v,onError:m,httpRequest:h}=r,{uid:d}=e,i={headers:a||{},withCredentials:p,file:e,data:o??(s||{}),method:f,filename:q,action:b,onProgress:l=>{u?.(l,e)},onSuccess:l=>{v?.(l,e),delete c.value[d]},onError:l=>{m?.(l,e),delete c.value[d]}},n=h(i);c.value[d]=n,n instanceof Promise&&n.then(i.onSuccess,i.onError)},O=async e=>{const o=e.target.files;o&&(await C(Array.from(o)),t.nextTick(()=>{x?.validate("change").catch(a=>D.debugWarn(a))}))},B=()=>{E.value||(y.value.value="",y.value.click())},I=()=>{B()};return w({abort:e=>{S.entriesOf(c.value).filter(e?([a])=>String(e.uid)===a:()=>!0).forEach(([a,s])=>{s instanceof XMLHttpRequest&&s.abort(),delete c.value[a]})},upload:k}),(e,o)=>(t.openBlock(),t.createElementBlock("div",{class:t.normalizeClass([t.unref(g).b(),t.unref(g).m(e.listType),t.unref(g).is("drag",e.drag)]),tabindex:"0",onClick:B,onKeydown:t.withKeys(t.withModifiers(I,["self"]),["enter","space"])},[e.drag?(t.openBlock(),t.createBlock(K.default,{key:0,disabled:t.unref(E),onFile:o[0]||(o[0]=a=>C(a.file))},{default:t.withCtx(()=>[t.renderSlot(e.$slots,"default")]),_:3},8,["disabled"])):t.renderSlot(e.$slots,"default",{key:1}),t.createElementVNode("input",{ref_key:"inputRef",ref:y,class:t.normalizeClass(t.unref(g).e("input")),name:e.name,multiple:e.multiple,accept:e.accept,type:"file",onChange:O,onClick:o[1]||(o[1]=t.withModifiers(()=>{},["stop"]))},null,42,W)],42,H))}});exports.default=A;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const t=require("vue");require("../form/index.cjs");require("@vueuse/core");const C=require("@vft/utils"),D=require("../../utils/error.cjs");require("../config-provider/hooks/use-global-config.cjs");const k=require("lodash-es"),L=require("../../hooks/use-namespace/index.cjs");require("../../hooks/use-model-toggle/index.cjs");require("@popperjs/core");require("../../hooks/use-z-index/index.cjs");const M=require("./ajax.cjs"),K=require("./constants.cjs"),V=require("./upload-dragger.vue2.cjs"),b=require("./utils.cjs"),H=require("../form/hooks/use-form-common-props.cjs"),W=require("../form/hooks/use-form-item.cjs"),$=["onKeydown"],A=["name","multiple","accept"],N=t.defineComponent({name:"VftUploadContent",inheritAttrs:!1}),X=t.defineComponent({...N,props:{fileList:{default:[]},replaceOnLimit:{type:Boolean,default:!1},action:{default:"#"},headers:{},method:{default:"post"},data:{},multiple:{type:Boolean,default:!1},name:{default:"file"},drag:{type:Boolean,default:!1},withCredentials:{type:Boolean},showFileList:{type:Boolean,default:!0},accept:{default:""},type:{default:"select"},autoUpload:{type:Boolean,default:!1},listType:{default:"text"},httpRequest:{type:Function,default:M.ajaxUpload},disabled:{type:Boolean},limit:{},maxSize:{default:void 0},sizeExceedMessage:{default:"文件大小超出限制"},sizeWidth:{default:void 0},sizeHeight:{default:void 0},onSizeInvalid:{},onTypeInvalid:{},beforeUpload:{},onRemove:{},onStart:{},onSuccess:{},onProgress:{},onError:{},onExceed:{},onSizeExceed:{}},setup(U,{expose:w}){const l=U,y=L.useNamespace("upload"),_=H.useFormDisabled(),{formItem:x}=W.useFormItem(),f=t.shallowRef({}),v=t.shallowRef(),B=async e=>{if(e.length===0)return;const{autoUpload:n,limit:a,fileList:s,multiple:p,onStart:h,onExceed:S,replaceOnLimit:z,maxSize:u,onSizeExceed:q,sizeWidth:m,sizeHeight:g,onSizeInvalid:d,accept:r,onTypeInvalid:c}=l;if(r){const i=e.filter(o=>!b.validateFileType(o,r));if(i.length>0&&(i.forEach(o=>{c&&c(o,r)}),e=e.filter(o=>b.validateFileType(o,r)),e.length===0))return}if(u){const i=e.filter(o=>o.size>u);if(i.length>0&&(i.forEach(o=>{q&&q(o,u)}),e=e.filter(o=>o.size<=u),e.length===0))return}if(m&&g){const i=[];for(const o of e)if(b.isImageOrVideo(o))try{const{isValid:E,width:P,height:j}=await b.validateFileSize(o,m,g);E?i.push(o):d&&d(o,P,j,m,g)}catch(E){console.error("尺寸校验失败:",E)}else i.push(o);if(e=i,e.length===0)return}if(a&&s&&s.length+e.length>a&&!(a===1&&z)){S?.(e,s);return}p||(e=e.slice(0,1));for(const i of e){const o=i;o.uid=K.genFileId(),h?.(o),n&&R(o)}},R=async e=>{if(v.value.value="",!l.beforeUpload)return I(e);let n,a={};try{const p=l.data,h=l.beforeUpload(e);a=C.isObject(l.data)?k.cloneDeep(l.data):l.data,n=await h,C.isObject(l.data)&&k.isEqual(p,a)&&(a=k.cloneDeep(l.data))}catch{n=!1}if(n===!1){l.onRemove?.(e);return}let s=e;n instanceof Blob&&(n instanceof File?s=n:s=new File([n],e.name,{type:e.type})),I(Object.assign(s,{uid:e.uid}),a)},I=(e,n)=>{const{headers:a,data:s,method:p,withCredentials:h,name:S,action:z,onProgress:u,onSuccess:q,onError:m,httpRequest:g}=l,{uid:d}=e,r={headers:a||{},withCredentials:h,file:e,data:n??(s||{}),method:p,filename:S,action:z,onProgress:i=>{u?.(i,e)},onSuccess:i=>{q?.(i,e),delete f.value[d]},onError:i=>{m?.(i,e),delete f.value[d]}},c=g(r);f.value[d]=c,c instanceof Promise&&c.then(r.onSuccess,r.onError)},F=async e=>{const n=e.target.files;n&&(await B(Array.from(n)),t.nextTick(()=>{x?.validate("change").catch(a=>D.debugWarn(a))}))},T=()=>{_.value||(v.value.value="",v.value.click())},O=()=>{T()};return w({abort:e=>{C.entriesOf(f.value).filter(e?([a])=>String(e.uid)===a:()=>!0).forEach(([a,s])=>{s instanceof XMLHttpRequest&&s.abort(),delete f.value[a]})},upload:R}),(e,n)=>(t.openBlock(),t.createElementBlock("div",{class:t.normalizeClass([t.unref(y).b(),t.unref(y).m(e.listType),t.unref(y).is("drag",e.drag)]),tabindex:"0",onClick:T,onKeydown:t.withKeys(t.withModifiers(O,["self"]),["enter","space"])},[e.drag?(t.openBlock(),t.createBlock(V.default,{key:0,disabled:t.unref(_),onFile:n[0]||(n[0]=a=>B(a.file))},{default:t.withCtx(()=>[t.renderSlot(e.$slots,"default")]),_:3},8,["disabled"])):t.renderSlot(e.$slots,"default",{key:1}),t.createElementVNode("input",{ref_key:"inputRef",ref:v,class:t.normalizeClass(t.unref(y).e("input")),name:e.name,multiple:e.multiple,accept:e.accept,type:"file",onChange:F,onClick:n[1]||(n[1]=t.withModifiers(()=>{},["stop"]))},null,42,A)],42,$))}});exports.default=X;
@@ -1 +1 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue");require("../form/index.cjs");const h=require("../message/index.cjs"),P=require("./ajax.cjs"),_=require("./constants.cjs"),C=require("./upload-content.vue2.cjs"),w=require("./upload-list.vue2.cjs"),E=require("./use-handlers.cjs"),F=require("../form/hooks/use-form-common-props.cjs"),L=e.defineComponent({name:"vft-upload"}),V=e.defineComponent({...L,props:{action:{default:"#"},headers:{},method:{default:"post"},data:{},multiple:{type:Boolean,default:!1},name:{default:"file"},drag:{type:Boolean,default:!1},withCredentials:{type:Boolean},showFileList:{type:Boolean,default:!0},accept:{default:""},type:{default:"select"},fileList:{default:[]},autoUpload:{type:Boolean,default:!1},listType:{default:"text"},httpRequest:{type:Function,default:P.ajaxUpload},disabled:{type:Boolean},limit:{},replaceOnLimit:{type:Boolean,default:!1},maxSize:{default:void 0},sizeExceedMessage:{default:"文件大小超出限制"},sizeWidth:{default:void 0},sizeHeight:{default:void 0},onSizeInvalid:{},beforeUpload:{},beforeRemove:{},onRemove:{},onChange:{},onPreview:{},onSuccess:{},onProgress:{},onError:{},onExceed:{type:Function,default:()=>{}},onSizeExceed:{}},setup(k,{expose:B}){const o=k,r=e.useSlots(),f=F.useFormDisabled(),i=e.shallowRef(),{abort:p,submit:c,clearFiles:m,uploadFiles:n,handleStart:v,handleError:b,handleRemove:d,handleSuccess:R,handleProgress:$}=E.useHandlers(o,i),u=e.computed(()=>o.listType==="picture-card"),z={abort:p,submit:c,clearFiles:m,uploadFiles:n,uploadRef:i},y=e.computed(()=>({...o,fileList:n.value,onStart:v,onProgress:$,onSuccess:R,onError:b,onRemove:d,onChange:(t,l)=>{o.onChange?.(t,l,z)},onSizeExceed:(t,l)=>{if(o.onSizeExceed)o.onSizeExceed?.(t,l);else{const s=`文件大小不能超过 ${(l/1048576).toFixed(2)}MB`;console.warn("默认文件大小超出提示:",s),h.VftMessage.warning(s)}},onSizeInvalid:(t,l,a,s,g)=>{if(o.onSizeInvalid)o.onSizeInvalid(t,l,a,s,g);else{const S=`文件尺寸不符合要求,当前尺寸:${l}x${a},期望尺寸:${s}x${g}`;console.warn("默认尺寸校验提示:",S),h.VftMessage.warning(S)}}}));return e.onBeforeUnmount(()=>{n.value.forEach(({url:t})=>{t?.startsWith("blob:")&&URL.revokeObjectURL(t)})}),e.provide(_.uploadContextKey,{accept:e.toRef(o,"accept")}),B({abort:p,submit:c,clearFiles:m,handleStart:v,handleRemove:d,fileList:n}),(t,l)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["vft-upload-wrapper",{disabled:e.unref(f)}])},[u.value&&t.showFileList?(e.openBlock(),e.createBlock(w.default,{key:0,disabled:e.unref(f),"list-type":t.listType,files:e.unref(n),"handle-preview":t.onPreview,onRemove:e.unref(d)},e.createSlots({append:e.withCtx(()=>[e.createVNode(C.default,e.mergeProps({ref_key:"uploadRef",ref:i},y.value),{default:e.withCtx(()=>[e.unref(r).trigger?e.renderSlot(t.$slots,"trigger",{key:0}):e.createCommentVNode("",!0),!e.unref(r).trigger&&e.unref(r).default?e.renderSlot(t.$slots,"default",{key:1}):e.createCommentVNode("",!0)]),_:3},16)]),_:2},[t.$slots.file?{name:"default",fn:e.withCtx(({file:a})=>[e.renderSlot(t.$slots,"file",{file:a})]),key:"0"}:void 0]),1032,["disabled","list-type","files","handle-preview","onRemove"])):e.createCommentVNode("",!0),!u.value||u.value&&!t.showFileList?(e.openBlock(),e.createBlock(C.default,e.mergeProps({key:1,ref_key:"uploadRef",ref:i},y.value),{default:e.withCtx(()=>[e.unref(r).trigger?e.renderSlot(t.$slots,"trigger",{key:0}):e.createCommentVNode("",!0),!e.unref(r).trigger&&e.unref(r).default?e.renderSlot(t.$slots,"default",{key:1}):e.createCommentVNode("",!0)]),_:3},16)):e.createCommentVNode("",!0),t.$slots.trigger?e.renderSlot(t.$slots,"default",{key:2}):e.createCommentVNode("",!0),e.renderSlot(t.$slots,"tip"),!u.value&&t.showFileList?(e.openBlock(),e.createBlock(w.default,{key:3,disabled:e.unref(f),"list-type":t.listType,files:e.unref(n),"handle-preview":t.onPreview,onRemove:e.unref(d)},e.createSlots({_:2},[t.$slots.file?{name:"default",fn:e.withCtx(({file:a})=>[e.renderSlot(t.$slots,"file",{file:a})]),key:"0"}:void 0]),1032,["disabled","list-type","files","handle-preview","onRemove"])):e.createCommentVNode("",!0)],2))}});exports.default=V;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue");require("../form/index.cjs");const p=require("../message/index.cjs"),M=require("./ajax.cjs"),P=require("./constants.cjs"),C=require("./upload-content.vue2.cjs"),w=require("./upload-list.vue2.cjs"),V=require("./use-handlers.cjs"),_=require("../form/hooks/use-form-common-props.cjs"),E=e.defineComponent({name:"vft-upload"}),F=e.defineComponent({...E,props:{action:{default:"#"},headers:{},method:{default:"post"},data:{},multiple:{type:Boolean,default:!1},name:{default:"file"},drag:{type:Boolean,default:!1},withCredentials:{type:Boolean},showFileList:{type:Boolean,default:!0},accept:{default:""},type:{default:"select"},fileList:{default:[]},autoUpload:{type:Boolean,default:!1},listType:{default:"text"},httpRequest:{type:Function,default:M.ajaxUpload},disabled:{type:Boolean},limit:{},replaceOnLimit:{type:Boolean,default:!1},maxSize:{default:void 0},sizeExceedMessage:{default:"文件大小超出限制"},sizeWidth:{default:void 0},sizeHeight:{default:void 0},onSizeInvalid:{},onTypeInvalid:{},beforeUpload:{},beforeRemove:{},onRemove:{},onChange:{},onPreview:{},onSuccess:{},onProgress:{},onError:{},onExceed:{type:Function,default:()=>{}},onSizeExceed:{}},setup(k,{expose:B}){const o=k,a=e.useSlots(),f=_.useFormDisabled(),i=e.shallowRef(),{abort:c,submit:m,clearFiles:v,uploadFiles:r,handleStart:y,handleError:$,handleRemove:d,handleSuccess:b,handleProgress:R}=V.useHandlers(o,i),u=e.computed(()=>o.listType==="picture-card"),z={abort:c,submit:m,clearFiles:v,uploadFiles:r,uploadRef:i},g=e.computed(()=>({...o,fileList:r.value,onStart:y,onProgress:R,onSuccess:b,onError:$,onRemove:d,onChange:(t,l)=>{o.onChange?.(t,l,z)},onSizeExceed:(t,l)=>{if(o.onSizeExceed)o.onSizeExceed?.(t,l);else{const s=`文件大小不能超过 ${(l/1048576).toFixed(2)}MB`;console.warn("默认文件大小超出提示:",s),p.VftMessage.warning(s)}},onSizeInvalid:(t,l,n,s,S)=>{if(o.onSizeInvalid)o.onSizeInvalid(t,l,n,s,S);else{const h=`文件尺寸不符合要求,当前尺寸:${l}x${n},期望尺寸:${s}x${S}`;console.warn("默认尺寸校验提示:",h),p.VftMessage.warning(h)}},onTypeInvalid:(t,l)=>{if(o.onTypeInvalid)o.onTypeInvalid(t,l);else{const n=`文件类型不符合要求,当前文件:${t.name},支持的类型:${l}`;console.warn("默认文件类型校验提示:",n),p.VftMessage.warning(n)}}}));return e.onBeforeUnmount(()=>{r.value.forEach(({url:t})=>{t?.startsWith("blob:")&&URL.revokeObjectURL(t)})}),e.provide(P.uploadContextKey,{accept:e.toRef(o,"accept")}),B({abort:c,submit:m,clearFiles:v,handleStart:y,handleRemove:d,fileList:r}),(t,l)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["vft-upload-wrapper",{disabled:e.unref(f)}])},[u.value&&t.showFileList?(e.openBlock(),e.createBlock(w.default,{key:0,disabled:e.unref(f),"list-type":t.listType,files:e.unref(r),"handle-preview":t.onPreview,onRemove:e.unref(d)},e.createSlots({append:e.withCtx(()=>[e.createVNode(C.default,e.mergeProps({ref_key:"uploadRef",ref:i},g.value),{default:e.withCtx(()=>[e.unref(a).trigger?e.renderSlot(t.$slots,"trigger",{key:0}):e.createCommentVNode("",!0),!e.unref(a).trigger&&e.unref(a).default?e.renderSlot(t.$slots,"default",{key:1}):e.createCommentVNode("",!0)]),_:3},16)]),_:2},[t.$slots.file?{name:"default",fn:e.withCtx(({file:n})=>[e.renderSlot(t.$slots,"file",{file:n})]),key:"0"}:void 0]),1032,["disabled","list-type","files","handle-preview","onRemove"])):e.createCommentVNode("",!0),!u.value||u.value&&!t.showFileList?(e.openBlock(),e.createBlock(C.default,e.mergeProps({key:1,ref_key:"uploadRef",ref:i},g.value),{default:e.withCtx(()=>[e.unref(a).trigger?e.renderSlot(t.$slots,"trigger",{key:0}):e.createCommentVNode("",!0),!e.unref(a).trigger&&e.unref(a).default?e.renderSlot(t.$slots,"default",{key:1}):e.createCommentVNode("",!0)]),_:3},16)):e.createCommentVNode("",!0),t.$slots.trigger?e.renderSlot(t.$slots,"default",{key:2}):e.createCommentVNode("",!0),e.renderSlot(t.$slots,"tip"),!u.value&&t.showFileList?(e.openBlock(),e.createBlock(w.default,{key:3,disabled:e.unref(f),"list-type":t.listType,files:e.unref(r),"handle-preview":t.onPreview,onRemove:e.unref(d)},e.createSlots({_:2},[t.$slots.file?{name:"default",fn:e.withCtx(({file:n})=>[e.renderSlot(t.$slots,"file",{file:n})]),key:"0"}:void 0]),1032,["disabled","list-type","files","handle-preview","onRemove"])):e.createCommentVNode("",!0)],2))}});exports.default=F;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function a(t){return new Promise((n,r)=>{const i=t.type.startsWith("image/"),o=t.type.startsWith("video/");if(!i&&!o){r(new Error("文件类型不支持尺寸校验"));return}if(i){const e=new Image;e.onload=()=>{n({width:e.naturalWidth,height:e.naturalHeight})},e.onerror=()=>{r(new Error("图片加载失败"))},e.src=URL.createObjectURL(t)}else if(o){const e=document.createElement("video");e.onloadedmetadata=()=>{n({width:e.videoWidth,height:e.videoHeight}),URL.revokeObjectURL(e.src)},e.onerror=()=>{r(new Error("视频加载失败")),URL.revokeObjectURL(e.src)},e.src=URL.createObjectURL(t)}})}async function s(t,n,r){try{const{width:i,height:o}=await a(t);return{isValid:i===n&&o===r,width:i,height:o}}catch(i){return console.error("尺寸校验失败:",i),{isValid:!1,width:0,height:0}}}function d(t){return t.type.startsWith("image/")||t.type.startsWith("video/")}exports.getFileDimensions=a;exports.isImageOrVideo=d;exports.validateFileSize=s;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function o(i){return new Promise((n,s)=>{const e=i.type.startsWith("image/"),r=i.type.startsWith("video/");if(!e&&!r){s(new Error("文件类型不支持尺寸校验"));return}if(e){const t=new Image;t.onload=()=>{n({width:t.naturalWidth,height:t.naturalHeight})},t.onerror=()=>{s(new Error("图片加载失败"))},t.src=URL.createObjectURL(i)}else if(r){const t=document.createElement("video");t.onloadedmetadata=()=>{n({width:t.videoWidth,height:t.videoHeight}),URL.revokeObjectURL(t.src)},t.onerror=()=>{s(new Error("视频加载失败")),URL.revokeObjectURL(t.src)},t.src=URL.createObjectURL(i)}})}async function a(i,n,s){try{const{width:e,height:r}=await o(i);return{isValid:e===n&&r===s,width:e,height:r}}catch(e){return console.error("尺寸校验失败:",e),{isValid:!1,width:0,height:0}}}function c(i){return i.type.startsWith("image/")||i.type.startsWith("video/")}function d(i,n){return n?n.split(",").map(e=>e.trim()).some(e=>{if(e.startsWith(".")){const r=e.toLowerCase();return i.name.toLowerCase().endsWith(r)}else if(e.includes("/*")){const r=e.replace("/*","");return i.type.startsWith(r+"/")}else return i.type===e}):!0}exports.getFileDimensions=o;exports.isImageOrVideo=c;exports.validateFileSize=a;exports.validateFileType=d;
@@ -25,3 +25,10 @@ export declare function validateFileSize(file: File, expectedWidth: number, expe
25
25
  * @returns boolean
26
26
  */
27
27
  export declare function isImageOrVideo(file: File): boolean;
28
+ /**
29
+ * 验证文件类型是否符合accept属性要求
30
+ * @param file 文件对象
31
+ * @param accept accept属性值,如 "image/*,.pdf,.doc"
32
+ * @returns boolean
33
+ */
34
+ export declare function validateFileType(file: File, accept: string): boolean;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e="0.0.385";exports.version=e;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e="0.0.387";exports.version=e;
@@ -1,6 +1,6 @@
1
1
  declare const _default: {
2
2
  "name": "vft",
3
- "version": "0.0.385",
3
+ "version": "0.0.387",
4
4
  "license": "MIT",
5
5
  "main": "lib/index.cjs",
6
6
  "module": "es/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vft",
3
- "version": "0.0.385",
3
+ "version": "0.0.387",
4
4
  "license": "MIT",
5
5
  "main": "lib/index.cjs",
6
6
  "module": "es/index.js",
@@ -56,11 +56,11 @@
56
56
  "sortablejs": "1.15.0",
57
57
  "photoswipe": "5.4.4",
58
58
  "@vft/constants": "0.0.72",
59
- "@vft/utils": "0.0.136",
60
59
  "@vft/router": "0.0.67",
60
+ "@vft/utils": "0.0.136",
61
61
  "@vft/store": "0.0.54",
62
- "@vft/directives": "0.0.34",
63
- "@vft/use": "0.0.79"
62
+ "@vft/use": "0.0.79",
63
+ "@vft/directives": "0.0.34"
64
64
  },
65
65
  "vetur": {
66
66
  "tags": "tags.json",