fantasia-ui 0.2.56 → 0.2.59

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.
@@ -6696,73 +6696,57 @@ to {
6696
6696
  color: #94a3b8;
6697
6697
  font-weight: 700;
6698
6698
  background: rgba(248, 250, 252, 0.5);
6699
- }.f-upload[data-v-8e954b9a] {
6699
+ }.f-upload[data-v-04ecda2b] {
6700
6700
  display: flex;
6701
6701
  flex-direction: column;
6702
6702
  gap: 0.5rem;
6703
6703
  }
6704
- .f-upload__zone[data-v-8e954b9a] {
6704
+ .f-upload__trigger[data-v-04ecda2b] {
6705
+ display: inline-block;
6706
+ cursor: pointer;
6707
+ }
6708
+ .f-upload__zone[data-v-04ecda2b] {
6705
6709
  height: 11rem;
6710
+ width: 100%;
6706
6711
  border: 2px dashed #e2e8f0;
6707
6712
  border-radius: 8px;
6708
6713
  display: flex;
6714
+ flex-direction: column;
6709
6715
  align-items: center;
6710
6716
  justify-content: center;
6711
6717
  padding: 1.25rem;
6712
6718
  text-align: center;
6713
6719
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
6714
6720
  }
6715
- .f-upload__zone[data-v-8e954b9a]:hover {
6721
+ .f-upload__zone[data-v-04ecda2b]:hover {
6716
6722
  border-color: #818cf8;
6717
6723
  background: rgba(248, 250, 252, 0.3);
6718
6724
  }
6719
- .f-upload--drag[data-v-8e954b9a] {
6725
+ .f-upload--drag .f-upload__zone[data-v-04ecda2b] {
6720
6726
  border-color: #6366f1;
6721
6727
  background: rgba(238, 242, 255, 0.2);
6722
6728
  }
6723
- .f-upload__input[data-v-8e954b9a] {
6729
+ .f-upload__input[data-v-04ecda2b] {
6724
6730
  display: none;
6725
6731
  }
6726
- .f-upload__label[data-v-8e954b9a] {
6727
- width: 100%;
6728
- height: 100%;
6729
- display: flex;
6730
- flex-direction: column;
6731
- align-items: center;
6732
- justify-content: center;
6733
- gap: 0.5rem;
6734
- cursor: pointer;
6735
- }
6736
- .f-upload__button[data-v-8e954b9a] {
6737
- display: inline-flex;
6738
- align-items: center;
6739
- justify-content: center;
6740
- min-height: 2.5rem;
6741
- padding: 0 1rem;
6742
- border-radius: 9999px;
6743
- background: linear-gradient(135deg, #ec4899, #a855f7);
6744
- color: #fff;
6745
- cursor: pointer;
6746
- transition: transform 0.2s ease, box-shadow 0.2s ease;
6747
- box-shadow: 0 6px 16px rgba(168, 85, 247, 0.2);
6748
- }
6749
- .f-upload__button[data-v-8e954b9a]:hover {
6750
- transform: translateY(-1px);
6751
- box-shadow: 0 10px 18px rgba(168, 85, 247, 0.24);
6752
- }
6753
- .f-upload__title[data-v-8e954b9a] {
6732
+ .f-upload__title[data-v-04ecda2b] {
6754
6733
  font-size: 0.75rem;
6755
6734
  font-weight: 700;
6756
6735
  color: #334155;
6757
6736
  }
6758
- .f-upload__hint[data-v-8e954b9a] {
6737
+ .f-upload__hint[data-v-04ecda2b] {
6759
6738
  font-size: 0.625rem;
6760
6739
  font-weight: 700;
6761
6740
  color: #94a3b8;
6762
6741
  margin-top: 0.25rem;
6763
6742
  line-height: 1.4;
6764
6743
  }
6765
- .f-upload__file-item[data-v-8e954b9a] {
6744
+ .f-upload__list[data-v-04ecda2b] {
6745
+ display: flex;
6746
+ flex-direction: column;
6747
+ gap: 0.4rem;
6748
+ }
6749
+ .f-upload__file-item[data-v-04ecda2b] {
6766
6750
  display: flex;
6767
6751
  align-items: center;
6768
6752
  gap: 0.5rem;
@@ -6773,18 +6757,30 @@ to {
6773
6757
  font-size: 0.75rem;
6774
6758
  color: #4338ca;
6775
6759
  }
6776
- .f-upload__file-info[data-v-8e954b9a] {
6760
+ .f-upload__file-item--error[data-v-04ecda2b] {
6761
+ background: rgba(254, 226, 226, 0.5);
6762
+ border-color: #fecaca;
6763
+ color: #b91c1c;
6764
+ }
6765
+ .f-upload__file-item--success[data-v-04ecda2b] {
6766
+ border-color: #bbf7d0;
6767
+ }
6768
+ .f-upload__file-info[data-v-04ecda2b] {
6777
6769
  display: flex;
6778
6770
  flex-direction: column;
6779
6771
  gap: 0.25rem;
6780
6772
  flex: 1;
6781
6773
  min-width: 0;
6782
6774
  }
6783
- .f-upload__file-name[data-v-8e954b9a] {
6775
+ .f-upload__file-name[data-v-04ecda2b] {
6784
6776
  font-weight: 600;
6785
6777
  overflow: hidden;
6786
6778
  text-overflow: ellipsis;
6787
6779
  white-space: nowrap;
6780
+ }
6781
+ .f-upload__file-error[data-v-04ecda2b] {
6782
+ font-size: 0.6875rem;
6783
+ color: #dc2626;
6788
6784
  }.f-button-group[data-v-62561657] {
6789
6785
  display: inline-flex;
6790
6786
  vertical-align: middle;
@@ -38,7 +38,7 @@ var __async = (__this, __arguments, generator) => {
38
38
  });
39
39
  };
40
40
  import { defineComponent, computed, createElementBlock, openBlock, normalizeStyle, unref, renderSlot, normalizeClass, createCommentVNode, Fragment, renderList, createElementVNode, toDisplayString, ref, watch, createVNode, withDirectives, Transition, withCtx, vShow, mergeModels, useModel, inject, createBlock, resolveDynamicComponent, nextTick, onUnmounted, Teleport, withModifiers, withKeys, isRef, vModelText, createTextVNode, TransitionGroup, useSlots, provide, onMounted, readonly, onBeforeUnmount, reactive, toRefs, createStaticVNode, h, render } from "vue";
41
- import { CloseCircleOutline, EyeOutline, EyeOffOutline, StarOutline, StarHalf, Star, ChevronDownOutline, ChevronBackOutline, EllipsisHorizontal, ChevronForwardOutline, CloseCircle, Close, Person, InformationCircleOutline, WarningOutline, AlertCircleOutline, CheckmarkCircleOutline, CloudUpload, DocumentOutline, RemoveOutline, AddOutline, ChevronUpOutline, InformationCircle, AlertCircle, CheckmarkCircle, CloseOutline, Warning } from "@vicons/ionicons5";
41
+ import { CloseCircleOutline, EyeOutline, EyeOffOutline, StarOutline, StarHalf, Star, ChevronDownOutline, ChevronBackOutline, EllipsisHorizontal, ChevronForwardOutline, CloseCircle, Close, Person, InformationCircleOutline, WarningOutline, AlertCircleOutline, CheckmarkCircleOutline, CloudUpload, CheckmarkCircle, AlertCircle, DocumentOutline, Reload, RemoveOutline, AddOutline, ChevronUpOutline, InformationCircle, CloseOutline, Warning } from "@vicons/ionicons5";
42
42
  import { useEventListener } from "@vueuse/core";
43
43
  const _sfc_main$R = /* @__PURE__ */ defineComponent({
44
44
  __name: "FIcon",
@@ -722,7 +722,7 @@ const _hoisted_7$5 = {
722
722
  class: "page-btn ellipsis"
723
723
  };
724
724
  const _hoisted_8$4 = ["onClick"];
725
- const _hoisted_9$4 = {
725
+ const _hoisted_9$3 = {
726
726
  key: 5,
727
727
  class: "f-pagination__jump"
728
728
  };
@@ -831,7 +831,7 @@ const _sfc_main$K = /* @__PURE__ */ defineComponent({
831
831
  ]),
832
832
  _: 1
833
833
  })
834
- ], 2)) : item === "jumper" ? (openBlock(), createElementBlock("div", _hoisted_9$4, [
834
+ ], 2)) : item === "jumper" ? (openBlock(), createElementBlock("div", _hoisted_9$3, [
835
835
  _cache[3] || (_cache[3] = createElementVNode("span", { class: "jump-label" }, "前往", -1)),
836
836
  withDirectives(createElementVNode("input", {
837
837
  "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => isRef(jumpInput) ? jumpInput.value = $event : null),
@@ -885,7 +885,7 @@ const _hoisted_8$3 = {
885
885
  key: 1,
886
886
  class: "f-card__cover"
887
887
  };
888
- const _hoisted_9$3 = { class: "f-card__content" };
888
+ const _hoisted_9$2 = { class: "f-card__content" };
889
889
  const _hoisted_10$2 = {
890
890
  key: 0,
891
891
  class: "f-card__body"
@@ -931,7 +931,7 @@ const _sfc_main$J = /* @__PURE__ */ defineComponent({
931
931
  _ctx.$slots.cover ? (openBlock(), createElementBlock("div", _hoisted_8$3, [
932
932
  renderSlot(_ctx.$slots, "cover", {}, void 0, true)
933
933
  ])) : createCommentVNode("", true),
934
- createElementVNode("div", _hoisted_9$3, [
934
+ createElementVNode("div", _hoisted_9$2, [
935
935
  _ctx.$slots.default ? (openBlock(), createElementBlock("div", _hoisted_10$2, [
936
936
  renderSlot(_ctx.$slots, "default", {}, void 0, true)
937
937
  ])) : createCommentVNode("", true),
@@ -2383,7 +2383,7 @@ const _hoisted_5$7 = { class: "f-transfer__actions" };
2383
2383
  const _hoisted_6$6 = ["disabled"];
2384
2384
  const _hoisted_7$3 = ["disabled"];
2385
2385
  const _hoisted_8$2 = { class: "f-transfer__panel" };
2386
- const _hoisted_9$2 = { class: "f-transfer__header" };
2386
+ const _hoisted_9$1 = { class: "f-transfer__header" };
2387
2387
  const _hoisted_10$1 = { class: "f-transfer__list" };
2388
2388
  const _hoisted_11$1 = ["onClick"];
2389
2389
  const _sfc_main$k = /* @__PURE__ */ defineComponent({
@@ -2469,7 +2469,7 @@ const _sfc_main$k = /* @__PURE__ */ defineComponent({
2469
2469
  }, " ← ", 8, _hoisted_7$3)
2470
2470
  ]),
2471
2471
  createElementVNode("div", _hoisted_8$2, [
2472
- createElementVNode("div", _hoisted_9$2, toDisplayString(_ctx.rightTitle), 1),
2472
+ createElementVNode("div", _hoisted_9$1, toDisplayString(_ctx.rightTitle), 1),
2473
2473
  createElementVNode("ul", _hoisted_10$1, [
2474
2474
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(rightList), (item) => {
2475
2475
  return openBlock(), createElementBlock("li", {
@@ -2806,7 +2806,7 @@ const _hoisted_6$4 = {
2806
2806
  };
2807
2807
  const _hoisted_7$2 = { class: "f-table__th-inner" };
2808
2808
  const _hoisted_8$1 = { class: "f-table__body" };
2809
- const _hoisted_9$1 = {
2809
+ const _hoisted_9 = {
2810
2810
  key: 0,
2811
2811
  class: "f-table__spacer"
2812
2812
  };
@@ -2929,7 +2929,7 @@ const _sfc_main$h = /* @__PURE__ */ defineComponent({
2929
2929
  ])
2930
2930
  ]),
2931
2931
  createElementVNode("tbody", _hoisted_8$1, [
2932
- unref(offsetTop) > 0 ? (openBlock(), createElementBlock("tr", _hoisted_9$1, [
2932
+ unref(offsetTop) > 0 ? (openBlock(), createElementBlock("tr", _hoisted_9, [
2933
2933
  createElementVNode("td", {
2934
2934
  colspan: unref(colSpan),
2935
2935
  style: normalizeStyle({ height: unref(offsetTop) + "px" })
@@ -2995,42 +2995,96 @@ const _sfc_main$h = /* @__PURE__ */ defineComponent({
2995
2995
  });
2996
2996
  const FTable = /* @__PURE__ */ _export_sfc(_sfc_main$h, [["__scopeId", "data-v-01724962"]]);
2997
2997
  const _hoisted_1$c = { class: "f-upload" };
2998
- const _hoisted_2$a = { class: "f-upload__label" };
2999
- const _hoisted_3$a = ["accept"];
2998
+ const _hoisted_2$a = ["accept", "multiple"];
2999
+ const _hoisted_3$a = { class: "f-upload__zone" };
3000
3000
  const _hoisted_4$7 = { class: "f-upload__hint" };
3001
3001
  const _hoisted_5$4 = {
3002
- key: 1,
3003
- class: "f-upload__button"
3002
+ key: 0,
3003
+ class: "f-upload__list"
3004
3004
  };
3005
- const _hoisted_6$3 = ["accept"];
3006
- const _hoisted_7$1 = {
3007
- key: 2,
3008
- class: "f-upload__file-item"
3005
+ const _hoisted_6$3 = { class: "f-upload__file-info" };
3006
+ const _hoisted_7$1 = { class: "f-upload__file-name" };
3007
+ const _hoisted_8 = {
3008
+ key: 1,
3009
+ class: "f-upload__file-error"
3009
3010
  };
3010
- const _hoisted_8 = { class: "f-upload__file-info" };
3011
- const _hoisted_9 = { class: "f-upload__file-name" };
3012
3011
  const _sfc_main$g = /* @__PURE__ */ defineComponent({
3013
3012
  __name: "FUpload",
3014
3013
  props: /* @__PURE__ */ mergeModels({
3015
3014
  accept: {},
3015
+ hint: { default: "支持 .xlsx .xls .json .txt" },
3016
3016
  limit: {},
3017
- progress: {},
3018
- buttonMode: { type: Boolean, default: false },
3019
- buttonText: { default: "选择文件" },
3020
- hint: { default: "支持 .xlsx .xls .json .txt" }
3017
+ autoUpload: { type: Boolean, default: true },
3018
+ beforeUpload: {},
3019
+ httpRequest: {}
3021
3020
  }, {
3022
- "file": { default: "" },
3023
- "fileModifiers": {}
3021
+ "fileList": { default: () => [] },
3022
+ "fileListModifiers": {}
3024
3023
  }),
3025
- emits: /* @__PURE__ */ mergeModels(["change", "exceed"], ["update:file"]),
3026
- setup(__props, { emit: __emit }) {
3027
- const file = useModel(__props, "file");
3024
+ emits: /* @__PURE__ */ mergeModels(["change", "progress", "success", "error", "remove", "exceed"], ["update:fileList"]),
3025
+ setup(__props, { expose: __expose, emit: __emit }) {
3026
+ const fileList = useModel(__props, "fileList");
3028
3027
  const props = __props;
3029
3028
  const emit = __emit;
3030
3029
  const dragActive = ref(false);
3031
- const isCloseHover = ref(false);
3032
- const clearFile = () => {
3033
- file.value = "";
3030
+ const hoveredUid = ref(null);
3031
+ const genUid = () => `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
3032
+ const findItem = (uid) => fileList.value.find((f) => f.uid === uid);
3033
+ const updateItem = (uid, patch) => {
3034
+ const item = findItem(uid);
3035
+ if (item) Object.assign(item, patch);
3036
+ return item;
3037
+ };
3038
+ const submitUpload = (item) => __async(null, null, function* () {
3039
+ if (!props.httpRequest) return;
3040
+ updateItem(item.uid, { status: "uploading", percentage: 0 });
3041
+ try {
3042
+ const response = yield props.httpRequest(item.raw, (percent) => {
3043
+ const current = updateItem(item.uid, { percentage: percent });
3044
+ if (current) emit("progress", current);
3045
+ });
3046
+ const done = updateItem(item.uid, { status: "success", percentage: 100, response });
3047
+ if (done) emit("success", done, response);
3048
+ } catch (err) {
3049
+ const failed = updateItem(item.uid, { status: "error", errorMessage: (err == null ? void 0 : err.message) || "上传失败" });
3050
+ if (failed) emit("error", failed, err);
3051
+ }
3052
+ });
3053
+ const addFiles = (files) => __async(null, null, function* () {
3054
+ if (!files.length) return;
3055
+ const remaining = props.limit != null ? props.limit - fileList.value.length : Infinity;
3056
+ if (remaining <= 0) {
3057
+ emit("exceed", files);
3058
+ return;
3059
+ }
3060
+ const accepted = files.slice(0, remaining);
3061
+ const rejected = files.slice(remaining);
3062
+ if (rejected.length) emit("exceed", rejected);
3063
+ for (const raw of accepted) {
3064
+ if (props.beforeUpload) {
3065
+ const ok = yield props.beforeUpload(raw);
3066
+ if (!ok) continue;
3067
+ }
3068
+ const item = {
3069
+ uid: genUid(),
3070
+ name: raw.name,
3071
+ status: "ready",
3072
+ percentage: 0,
3073
+ raw
3074
+ };
3075
+ fileList.value = [...fileList.value, item];
3076
+ emit("change", item);
3077
+ if (props.autoUpload) submitUpload(item);
3078
+ }
3079
+ });
3080
+ const retryUpload = (uid) => {
3081
+ const item = findItem(uid);
3082
+ if (item) submitUpload(item);
3083
+ };
3084
+ const removeFile = (uid) => {
3085
+ const item = findItem(uid);
3086
+ fileList.value = fileList.value.filter((f) => f.uid !== uid);
3087
+ if (item) emit("remove", item);
3034
3088
  };
3035
3089
  const onDragOver = (e) => {
3036
3090
  e.preventDefault();
@@ -3044,94 +3098,94 @@ const _sfc_main$g = /* @__PURE__ */ defineComponent({
3044
3098
  var _a, _b;
3045
3099
  e.preventDefault();
3046
3100
  dragActive.value = false;
3047
- const f = (_b = (_a = e.dataTransfer) == null ? void 0 : _a.files) == null ? void 0 : _b[0];
3048
- if (!f) return;
3049
- if (props.limit && f.size > props.limit) {
3050
- emit("exceed", f);
3051
- return;
3052
- }
3053
- emit("change", f);
3101
+ const files = Array.from((_b = (_a = e.dataTransfer) == null ? void 0 : _a.files) != null ? _b : []);
3102
+ addFiles(files);
3054
3103
  };
3055
3104
  const onFileChange = (e) => {
3056
3105
  var _a;
3057
- const f = (_a = e.target.files) == null ? void 0 : _a[0];
3058
- if (!f) return;
3059
- if (props.limit && f.size > props.limit) {
3060
- emit("exceed", f);
3061
- return;
3062
- }
3063
- emit("change", f);
3106
+ const input = e.target;
3107
+ const files = Array.from((_a = input.files) != null ? _a : []);
3108
+ input.value = "";
3109
+ addFiles(files);
3064
3110
  };
3111
+ __expose({ retryUpload, removeFile, submitAll: () => fileList.value.filter((f) => f.status === "ready").forEach(submitUpload) });
3065
3112
  return (_ctx, _cache) => {
3066
3113
  return openBlock(), createElementBlock("div", _hoisted_1$c, [
3067
- !_ctx.buttonMode ? (openBlock(), createElementBlock("div", {
3068
- key: 0,
3069
- class: normalizeClass(["f-upload__zone", { "f-upload--drag": unref(dragActive) }]),
3114
+ createElementVNode("label", {
3115
+ class: normalizeClass(["f-upload__trigger", { "f-upload--drag": unref(dragActive) }]),
3070
3116
  onDragover: onDragOver,
3071
3117
  onDragleave: onDragLeave,
3072
3118
  onDrop
3073
3119
  }, [
3074
- createElementVNode("label", _hoisted_2$a, [
3075
- createElementVNode("input", {
3076
- type: "file",
3077
- accept: _ctx.accept,
3078
- class: "f-upload__input",
3079
- onChange: onFileChange
3080
- }, null, 40, _hoisted_3$a),
3081
- renderSlot(_ctx.$slots, "default", {}, () => [
3120
+ createElementVNode("input", {
3121
+ type: "file",
3122
+ accept: _ctx.accept,
3123
+ multiple: _ctx.limit == null || _ctx.limit > 1,
3124
+ class: "f-upload__input",
3125
+ onChange: onFileChange
3126
+ }, null, 40, _hoisted_2$a),
3127
+ renderSlot(_ctx.$slots, "default", {}, () => [
3128
+ createElementVNode("div", _hoisted_3$a, [
3082
3129
  createVNode(FIcon, { size: 40 }, {
3083
3130
  default: withCtx(() => [
3084
3131
  createVNode(unref(CloudUpload))
3085
3132
  ]),
3086
3133
  _: 1
3087
3134
  }),
3088
- _cache[2] || (_cache[2] = createElementVNode("span", { class: "f-upload__title" }, "点击或拖拽文件至此处", -1)),
3135
+ _cache[1] || (_cache[1] = createElementVNode("span", { class: "f-upload__title" }, "点击或拖拽文件至此处", -1)),
3089
3136
  createElementVNode("span", _hoisted_4$7, toDisplayString(_ctx.hint), 1)
3090
- ], true)
3091
- ])
3092
- ], 34)) : (openBlock(), createElementBlock("label", _hoisted_5$4, [
3093
- createElementVNode("input", {
3094
- type: "file",
3095
- accept: _ctx.accept,
3096
- class: "f-upload__input",
3097
- onChange: onFileChange
3098
- }, null, 40, _hoisted_6$3),
3099
- renderSlot(_ctx.$slots, "default", {}, () => [
3100
- createElementVNode("span", null, toDisplayString(_ctx.buttonText), 1)
3137
+ ])
3101
3138
  ], true)
3102
- ])),
3103
- file.value ? (openBlock(), createElementBlock("div", _hoisted_7$1, [
3104
- createVNode(FIcon, null, {
3105
- default: withCtx(() => [
3106
- createVNode(unref(DocumentOutline))
3107
- ]),
3108
- _: 1
3109
- }),
3110
- createElementVNode("div", _hoisted_8, [
3111
- createElementVNode("span", _hoisted_9, toDisplayString(file.value), 1),
3112
- _ctx.progress != null ? (openBlock(), createBlock(FProgress, {
3113
- key: 0,
3114
- percent: _ctx.progress,
3115
- "show-text": ""
3116
- }, null, 8, ["percent"])) : createCommentVNode("", true)
3117
- ]),
3118
- createVNode(FIcon, {
3119
- onClick: withModifiers(clearFile, ["stop"]),
3120
- onMouseenter: _cache[0] || (_cache[0] = ($event) => isCloseHover.value = true),
3121
- onMouseleave: _cache[1] || (_cache[1] = ($event) => isCloseHover.value = false),
3122
- style: { "cursor": "pointer", "transition": "color 0.2s" }
3123
- }, {
3124
- default: withCtx(() => [
3125
- (openBlock(), createBlock(resolveDynamicComponent(unref(isCloseHover) ? unref(CloseCircle) : unref(Close))))
3126
- ]),
3127
- _: 1
3128
- })
3139
+ ], 34),
3140
+ fileList.value.length ? (openBlock(), createElementBlock("div", _hoisted_5$4, [
3141
+ (openBlock(true), createElementBlock(Fragment, null, renderList(fileList.value, (item) => {
3142
+ return openBlock(), createElementBlock("div", {
3143
+ key: item.uid,
3144
+ class: normalizeClass(["f-upload__file-item", `f-upload__file-item--${item.status}`])
3145
+ }, [
3146
+ createVNode(FIcon, null, {
3147
+ default: withCtx(() => [
3148
+ (openBlock(), createBlock(resolveDynamicComponent(item.status === "success" ? unref(CheckmarkCircle) : item.status === "error" ? unref(AlertCircle) : unref(DocumentOutline))))
3149
+ ]),
3150
+ _: 2
3151
+ }, 1024),
3152
+ createElementVNode("div", _hoisted_6$3, [
3153
+ createElementVNode("span", _hoisted_7$1, toDisplayString(item.name), 1),
3154
+ item.status === "uploading" ? (openBlock(), createBlock(FProgress, {
3155
+ key: 0,
3156
+ percent: item.percentage,
3157
+ "show-text": ""
3158
+ }, null, 8, ["percent"])) : item.status === "error" ? (openBlock(), createElementBlock("span", _hoisted_8, toDisplayString(item.errorMessage), 1)) : createCommentVNode("", true)
3159
+ ]),
3160
+ item.status === "error" ? (openBlock(), createBlock(FIcon, {
3161
+ key: 0,
3162
+ onClick: withModifiers(($event) => retryUpload(item.uid), ["stop"]),
3163
+ style: { "cursor": "pointer" }
3164
+ }, {
3165
+ default: withCtx(() => [
3166
+ createVNode(unref(Reload))
3167
+ ]),
3168
+ _: 2
3169
+ }, 1032, ["onClick"])) : createCommentVNode("", true),
3170
+ createVNode(FIcon, {
3171
+ onClick: withModifiers(($event) => removeFile(item.uid), ["stop"]),
3172
+ onMouseenter: ($event) => hoveredUid.value = item.uid,
3173
+ onMouseleave: _cache[0] || (_cache[0] = ($event) => hoveredUid.value = null),
3174
+ style: { "cursor": "pointer", "transition": "color 0.2s" }
3175
+ }, {
3176
+ default: withCtx(() => [
3177
+ (openBlock(), createBlock(resolveDynamicComponent(unref(hoveredUid) === item.uid ? unref(CloseCircle) : unref(Close))))
3178
+ ]),
3179
+ _: 2
3180
+ }, 1032, ["onClick", "onMouseenter"])
3181
+ ], 2);
3182
+ }), 128))
3129
3183
  ])) : createCommentVNode("", true)
3130
3184
  ]);
3131
3185
  };
3132
3186
  }
3133
3187
  });
3134
- const FUpload = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["__scopeId", "data-v-8e954b9a"]]);
3188
+ const FUpload = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["__scopeId", "data-v-04ecda2b"]]);
3135
3189
  const _sfc_main$f = /* @__PURE__ */ defineComponent({
3136
3190
  __name: "FButtonGroup",
3137
3191
  props: {
@@ -723,7 +723,7 @@ var __async = (__this, __arguments, generator) => {
723
723
  class: "page-btn ellipsis"
724
724
  };
725
725
  const _hoisted_8$4 = ["onClick"];
726
- const _hoisted_9$4 = {
726
+ const _hoisted_9$3 = {
727
727
  key: 5,
728
728
  class: "f-pagination__jump"
729
729
  };
@@ -832,7 +832,7 @@ var __async = (__this, __arguments, generator) => {
832
832
  ]),
833
833
  _: 1
834
834
  })
835
- ], 2)) : item === "jumper" ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_9$4, [
835
+ ], 2)) : item === "jumper" ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_9$3, [
836
836
  _cache[3] || (_cache[3] = vue.createElementVNode("span", { class: "jump-label" }, "前往", -1)),
837
837
  vue.withDirectives(vue.createElementVNode("input", {
838
838
  "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => vue.isRef(jumpInput) ? jumpInput.value = $event : null),
@@ -886,7 +886,7 @@ var __async = (__this, __arguments, generator) => {
886
886
  key: 1,
887
887
  class: "f-card__cover"
888
888
  };
889
- const _hoisted_9$3 = { class: "f-card__content" };
889
+ const _hoisted_9$2 = { class: "f-card__content" };
890
890
  const _hoisted_10$2 = {
891
891
  key: 0,
892
892
  class: "f-card__body"
@@ -932,7 +932,7 @@ var __async = (__this, __arguments, generator) => {
932
932
  _ctx.$slots.cover ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_8$3, [
933
933
  vue.renderSlot(_ctx.$slots, "cover", {}, void 0, true)
934
934
  ])) : vue.createCommentVNode("", true),
935
- vue.createElementVNode("div", _hoisted_9$3, [
935
+ vue.createElementVNode("div", _hoisted_9$2, [
936
936
  _ctx.$slots.default ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_10$2, [
937
937
  vue.renderSlot(_ctx.$slots, "default", {}, void 0, true)
938
938
  ])) : vue.createCommentVNode("", true),
@@ -2384,7 +2384,7 @@ var __async = (__this, __arguments, generator) => {
2384
2384
  const _hoisted_6$6 = ["disabled"];
2385
2385
  const _hoisted_7$3 = ["disabled"];
2386
2386
  const _hoisted_8$2 = { class: "f-transfer__panel" };
2387
- const _hoisted_9$2 = { class: "f-transfer__header" };
2387
+ const _hoisted_9$1 = { class: "f-transfer__header" };
2388
2388
  const _hoisted_10$1 = { class: "f-transfer__list" };
2389
2389
  const _hoisted_11$1 = ["onClick"];
2390
2390
  const _sfc_main$k = /* @__PURE__ */ vue.defineComponent({
@@ -2470,7 +2470,7 @@ var __async = (__this, __arguments, generator) => {
2470
2470
  }, " ← ", 8, _hoisted_7$3)
2471
2471
  ]),
2472
2472
  vue.createElementVNode("div", _hoisted_8$2, [
2473
- vue.createElementVNode("div", _hoisted_9$2, vue.toDisplayString(_ctx.rightTitle), 1),
2473
+ vue.createElementVNode("div", _hoisted_9$1, vue.toDisplayString(_ctx.rightTitle), 1),
2474
2474
  vue.createElementVNode("ul", _hoisted_10$1, [
2475
2475
  (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(rightList), (item) => {
2476
2476
  return vue.openBlock(), vue.createElementBlock("li", {
@@ -2807,7 +2807,7 @@ var __async = (__this, __arguments, generator) => {
2807
2807
  };
2808
2808
  const _hoisted_7$2 = { class: "f-table__th-inner" };
2809
2809
  const _hoisted_8$1 = { class: "f-table__body" };
2810
- const _hoisted_9$1 = {
2810
+ const _hoisted_9 = {
2811
2811
  key: 0,
2812
2812
  class: "f-table__spacer"
2813
2813
  };
@@ -2930,7 +2930,7 @@ var __async = (__this, __arguments, generator) => {
2930
2930
  ])
2931
2931
  ]),
2932
2932
  vue.createElementVNode("tbody", _hoisted_8$1, [
2933
- vue.unref(offsetTop) > 0 ? (vue.openBlock(), vue.createElementBlock("tr", _hoisted_9$1, [
2933
+ vue.unref(offsetTop) > 0 ? (vue.openBlock(), vue.createElementBlock("tr", _hoisted_9, [
2934
2934
  vue.createElementVNode("td", {
2935
2935
  colspan: vue.unref(colSpan),
2936
2936
  style: vue.normalizeStyle({ height: vue.unref(offsetTop) + "px" })
@@ -2996,42 +2996,96 @@ var __async = (__this, __arguments, generator) => {
2996
2996
  });
2997
2997
  const FTable = /* @__PURE__ */ _export_sfc(_sfc_main$h, [["__scopeId", "data-v-01724962"]]);
2998
2998
  const _hoisted_1$c = { class: "f-upload" };
2999
- const _hoisted_2$a = { class: "f-upload__label" };
3000
- const _hoisted_3$a = ["accept"];
2999
+ const _hoisted_2$a = ["accept", "multiple"];
3000
+ const _hoisted_3$a = { class: "f-upload__zone" };
3001
3001
  const _hoisted_4$7 = { class: "f-upload__hint" };
3002
3002
  const _hoisted_5$4 = {
3003
- key: 1,
3004
- class: "f-upload__button"
3003
+ key: 0,
3004
+ class: "f-upload__list"
3005
3005
  };
3006
- const _hoisted_6$3 = ["accept"];
3007
- const _hoisted_7$1 = {
3008
- key: 2,
3009
- class: "f-upload__file-item"
3006
+ const _hoisted_6$3 = { class: "f-upload__file-info" };
3007
+ const _hoisted_7$1 = { class: "f-upload__file-name" };
3008
+ const _hoisted_8 = {
3009
+ key: 1,
3010
+ class: "f-upload__file-error"
3010
3011
  };
3011
- const _hoisted_8 = { class: "f-upload__file-info" };
3012
- const _hoisted_9 = { class: "f-upload__file-name" };
3013
3012
  const _sfc_main$g = /* @__PURE__ */ vue.defineComponent({
3014
3013
  __name: "FUpload",
3015
3014
  props: /* @__PURE__ */ vue.mergeModels({
3016
3015
  accept: {},
3016
+ hint: { default: "支持 .xlsx .xls .json .txt" },
3017
3017
  limit: {},
3018
- progress: {},
3019
- buttonMode: { type: Boolean, default: false },
3020
- buttonText: { default: "选择文件" },
3021
- hint: { default: "支持 .xlsx .xls .json .txt" }
3018
+ autoUpload: { type: Boolean, default: true },
3019
+ beforeUpload: {},
3020
+ httpRequest: {}
3022
3021
  }, {
3023
- "file": { default: "" },
3024
- "fileModifiers": {}
3022
+ "fileList": { default: () => [] },
3023
+ "fileListModifiers": {}
3025
3024
  }),
3026
- emits: /* @__PURE__ */ vue.mergeModels(["change", "exceed"], ["update:file"]),
3027
- setup(__props, { emit: __emit }) {
3028
- const file = vue.useModel(__props, "file");
3025
+ emits: /* @__PURE__ */ vue.mergeModels(["change", "progress", "success", "error", "remove", "exceed"], ["update:fileList"]),
3026
+ setup(__props, { expose: __expose, emit: __emit }) {
3027
+ const fileList = vue.useModel(__props, "fileList");
3029
3028
  const props = __props;
3030
3029
  const emit = __emit;
3031
3030
  const dragActive = vue.ref(false);
3032
- const isCloseHover = vue.ref(false);
3033
- const clearFile = () => {
3034
- file.value = "";
3031
+ const hoveredUid = vue.ref(null);
3032
+ const genUid = () => `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
3033
+ const findItem = (uid) => fileList.value.find((f) => f.uid === uid);
3034
+ const updateItem = (uid, patch) => {
3035
+ const item = findItem(uid);
3036
+ if (item) Object.assign(item, patch);
3037
+ return item;
3038
+ };
3039
+ const submitUpload = (item) => __async(null, null, function* () {
3040
+ if (!props.httpRequest) return;
3041
+ updateItem(item.uid, { status: "uploading", percentage: 0 });
3042
+ try {
3043
+ const response = yield props.httpRequest(item.raw, (percent) => {
3044
+ const current = updateItem(item.uid, { percentage: percent });
3045
+ if (current) emit("progress", current);
3046
+ });
3047
+ const done = updateItem(item.uid, { status: "success", percentage: 100, response });
3048
+ if (done) emit("success", done, response);
3049
+ } catch (err) {
3050
+ const failed = updateItem(item.uid, { status: "error", errorMessage: (err == null ? void 0 : err.message) || "上传失败" });
3051
+ if (failed) emit("error", failed, err);
3052
+ }
3053
+ });
3054
+ const addFiles = (files) => __async(null, null, function* () {
3055
+ if (!files.length) return;
3056
+ const remaining = props.limit != null ? props.limit - fileList.value.length : Infinity;
3057
+ if (remaining <= 0) {
3058
+ emit("exceed", files);
3059
+ return;
3060
+ }
3061
+ const accepted = files.slice(0, remaining);
3062
+ const rejected = files.slice(remaining);
3063
+ if (rejected.length) emit("exceed", rejected);
3064
+ for (const raw of accepted) {
3065
+ if (props.beforeUpload) {
3066
+ const ok = yield props.beforeUpload(raw);
3067
+ if (!ok) continue;
3068
+ }
3069
+ const item = {
3070
+ uid: genUid(),
3071
+ name: raw.name,
3072
+ status: "ready",
3073
+ percentage: 0,
3074
+ raw
3075
+ };
3076
+ fileList.value = [...fileList.value, item];
3077
+ emit("change", item);
3078
+ if (props.autoUpload) submitUpload(item);
3079
+ }
3080
+ });
3081
+ const retryUpload = (uid) => {
3082
+ const item = findItem(uid);
3083
+ if (item) submitUpload(item);
3084
+ };
3085
+ const removeFile = (uid) => {
3086
+ const item = findItem(uid);
3087
+ fileList.value = fileList.value.filter((f) => f.uid !== uid);
3088
+ if (item) emit("remove", item);
3035
3089
  };
3036
3090
  const onDragOver = (e) => {
3037
3091
  e.preventDefault();
@@ -3045,94 +3099,94 @@ var __async = (__this, __arguments, generator) => {
3045
3099
  var _a, _b;
3046
3100
  e.preventDefault();
3047
3101
  dragActive.value = false;
3048
- const f = (_b = (_a = e.dataTransfer) == null ? void 0 : _a.files) == null ? void 0 : _b[0];
3049
- if (!f) return;
3050
- if (props.limit && f.size > props.limit) {
3051
- emit("exceed", f);
3052
- return;
3053
- }
3054
- emit("change", f);
3102
+ const files = Array.from((_b = (_a = e.dataTransfer) == null ? void 0 : _a.files) != null ? _b : []);
3103
+ addFiles(files);
3055
3104
  };
3056
3105
  const onFileChange = (e) => {
3057
3106
  var _a;
3058
- const f = (_a = e.target.files) == null ? void 0 : _a[0];
3059
- if (!f) return;
3060
- if (props.limit && f.size > props.limit) {
3061
- emit("exceed", f);
3062
- return;
3063
- }
3064
- emit("change", f);
3107
+ const input = e.target;
3108
+ const files = Array.from((_a = input.files) != null ? _a : []);
3109
+ input.value = "";
3110
+ addFiles(files);
3065
3111
  };
3112
+ __expose({ retryUpload, removeFile, submitAll: () => fileList.value.filter((f) => f.status === "ready").forEach(submitUpload) });
3066
3113
  return (_ctx, _cache) => {
3067
3114
  return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$c, [
3068
- !_ctx.buttonMode ? (vue.openBlock(), vue.createElementBlock("div", {
3069
- key: 0,
3070
- class: vue.normalizeClass(["f-upload__zone", { "f-upload--drag": vue.unref(dragActive) }]),
3115
+ vue.createElementVNode("label", {
3116
+ class: vue.normalizeClass(["f-upload__trigger", { "f-upload--drag": vue.unref(dragActive) }]),
3071
3117
  onDragover: onDragOver,
3072
3118
  onDragleave: onDragLeave,
3073
3119
  onDrop
3074
3120
  }, [
3075
- vue.createElementVNode("label", _hoisted_2$a, [
3076
- vue.createElementVNode("input", {
3077
- type: "file",
3078
- accept: _ctx.accept,
3079
- class: "f-upload__input",
3080
- onChange: onFileChange
3081
- }, null, 40, _hoisted_3$a),
3082
- vue.renderSlot(_ctx.$slots, "default", {}, () => [
3121
+ vue.createElementVNode("input", {
3122
+ type: "file",
3123
+ accept: _ctx.accept,
3124
+ multiple: _ctx.limit == null || _ctx.limit > 1,
3125
+ class: "f-upload__input",
3126
+ onChange: onFileChange
3127
+ }, null, 40, _hoisted_2$a),
3128
+ vue.renderSlot(_ctx.$slots, "default", {}, () => [
3129
+ vue.createElementVNode("div", _hoisted_3$a, [
3083
3130
  vue.createVNode(FIcon, { size: 40 }, {
3084
3131
  default: vue.withCtx(() => [
3085
3132
  vue.createVNode(vue.unref(ionicons5.CloudUpload))
3086
3133
  ]),
3087
3134
  _: 1
3088
3135
  }),
3089
- _cache[2] || (_cache[2] = vue.createElementVNode("span", { class: "f-upload__title" }, "点击或拖拽文件至此处", -1)),
3136
+ _cache[1] || (_cache[1] = vue.createElementVNode("span", { class: "f-upload__title" }, "点击或拖拽文件至此处", -1)),
3090
3137
  vue.createElementVNode("span", _hoisted_4$7, vue.toDisplayString(_ctx.hint), 1)
3091
- ], true)
3092
- ])
3093
- ], 34)) : (vue.openBlock(), vue.createElementBlock("label", _hoisted_5$4, [
3094
- vue.createElementVNode("input", {
3095
- type: "file",
3096
- accept: _ctx.accept,
3097
- class: "f-upload__input",
3098
- onChange: onFileChange
3099
- }, null, 40, _hoisted_6$3),
3100
- vue.renderSlot(_ctx.$slots, "default", {}, () => [
3101
- vue.createElementVNode("span", null, vue.toDisplayString(_ctx.buttonText), 1)
3138
+ ])
3102
3139
  ], true)
3103
- ])),
3104
- file.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_7$1, [
3105
- vue.createVNode(FIcon, null, {
3106
- default: vue.withCtx(() => [
3107
- vue.createVNode(vue.unref(ionicons5.DocumentOutline))
3108
- ]),
3109
- _: 1
3110
- }),
3111
- vue.createElementVNode("div", _hoisted_8, [
3112
- vue.createElementVNode("span", _hoisted_9, vue.toDisplayString(file.value), 1),
3113
- _ctx.progress != null ? (vue.openBlock(), vue.createBlock(FProgress, {
3114
- key: 0,
3115
- percent: _ctx.progress,
3116
- "show-text": ""
3117
- }, null, 8, ["percent"])) : vue.createCommentVNode("", true)
3118
- ]),
3119
- vue.createVNode(FIcon, {
3120
- onClick: vue.withModifiers(clearFile, ["stop"]),
3121
- onMouseenter: _cache[0] || (_cache[0] = ($event) => isCloseHover.value = true),
3122
- onMouseleave: _cache[1] || (_cache[1] = ($event) => isCloseHover.value = false),
3123
- style: { "cursor": "pointer", "transition": "color 0.2s" }
3124
- }, {
3125
- default: vue.withCtx(() => [
3126
- (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(vue.unref(isCloseHover) ? vue.unref(ionicons5.CloseCircle) : vue.unref(ionicons5.Close))))
3127
- ]),
3128
- _: 1
3129
- })
3140
+ ], 34),
3141
+ fileList.value.length ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_5$4, [
3142
+ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(fileList.value, (item) => {
3143
+ return vue.openBlock(), vue.createElementBlock("div", {
3144
+ key: item.uid,
3145
+ class: vue.normalizeClass(["f-upload__file-item", `f-upload__file-item--${item.status}`])
3146
+ }, [
3147
+ vue.createVNode(FIcon, null, {
3148
+ default: vue.withCtx(() => [
3149
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(item.status === "success" ? vue.unref(ionicons5.CheckmarkCircle) : item.status === "error" ? vue.unref(ionicons5.AlertCircle) : vue.unref(ionicons5.DocumentOutline))))
3150
+ ]),
3151
+ _: 2
3152
+ }, 1024),
3153
+ vue.createElementVNode("div", _hoisted_6$3, [
3154
+ vue.createElementVNode("span", _hoisted_7$1, vue.toDisplayString(item.name), 1),
3155
+ item.status === "uploading" ? (vue.openBlock(), vue.createBlock(FProgress, {
3156
+ key: 0,
3157
+ percent: item.percentage,
3158
+ "show-text": ""
3159
+ }, null, 8, ["percent"])) : item.status === "error" ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_8, vue.toDisplayString(item.errorMessage), 1)) : vue.createCommentVNode("", true)
3160
+ ]),
3161
+ item.status === "error" ? (vue.openBlock(), vue.createBlock(FIcon, {
3162
+ key: 0,
3163
+ onClick: vue.withModifiers(($event) => retryUpload(item.uid), ["stop"]),
3164
+ style: { "cursor": "pointer" }
3165
+ }, {
3166
+ default: vue.withCtx(() => [
3167
+ vue.createVNode(vue.unref(ionicons5.Reload))
3168
+ ]),
3169
+ _: 2
3170
+ }, 1032, ["onClick"])) : vue.createCommentVNode("", true),
3171
+ vue.createVNode(FIcon, {
3172
+ onClick: vue.withModifiers(($event) => removeFile(item.uid), ["stop"]),
3173
+ onMouseenter: ($event) => hoveredUid.value = item.uid,
3174
+ onMouseleave: _cache[0] || (_cache[0] = ($event) => hoveredUid.value = null),
3175
+ style: { "cursor": "pointer", "transition": "color 0.2s" }
3176
+ }, {
3177
+ default: vue.withCtx(() => [
3178
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(vue.unref(hoveredUid) === item.uid ? vue.unref(ionicons5.CloseCircle) : vue.unref(ionicons5.Close))))
3179
+ ]),
3180
+ _: 2
3181
+ }, 1032, ["onClick", "onMouseenter"])
3182
+ ], 2);
3183
+ }), 128))
3130
3184
  ])) : vue.createCommentVNode("", true)
3131
3185
  ]);
3132
3186
  };
3133
3187
  }
3134
3188
  });
3135
- const FUpload = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["__scopeId", "data-v-8e954b9a"]]);
3189
+ const FUpload = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["__scopeId", "data-v-04ecda2b"]]);
3136
3190
  const _sfc_main$f = /* @__PURE__ */ vue.defineComponent({
3137
3191
  __name: "FButtonGroup",
3138
3192
  props: {
@@ -1,32 +1,35 @@
1
- type __VLS_Props = {
2
- accept?: string;
3
- limit?: number;
4
- progress?: number | null;
5
- buttonMode?: boolean;
6
- buttonText?: string;
7
- hint?: string;
8
- };
1
+ import type { FUploadProps, UploadFileItem } from '../../types';
2
+ type __VLS_Props = FUploadProps;
9
3
  type __VLS_PublicProps = __VLS_Props & {
10
- 'file'?: string;
4
+ 'fileList'?: UploadFileItem[];
11
5
  };
12
- declare var __VLS_1: {}, __VLS_10: {};
6
+ declare var __VLS_1: {};
13
7
  type __VLS_Slots = {} & {
14
8
  default?: (props: typeof __VLS_1) => any;
15
- } & {
16
- default?: (props: typeof __VLS_10) => any;
17
9
  };
18
- declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
19
- change: (file: File) => any;
20
- exceed: (file: File) => any;
21
- "update:file": (value: string) => any;
10
+ declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps, {
11
+ retryUpload: (uid: string) => void;
12
+ removeFile: (uid: string) => void;
13
+ submitAll: () => void;
14
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
15
+ success: (file: UploadFileItem, response: string) => any;
16
+ error: (file: UploadFileItem, error: unknown) => any;
17
+ progress: (file: UploadFileItem) => any;
18
+ change: (file: UploadFileItem) => any;
19
+ remove: (file: UploadFileItem) => any;
20
+ exceed: (files: File[]) => any;
21
+ "update:fileList": (value: UploadFileItem[]) => any;
22
22
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
23
- onChange?: (file: File) => any;
24
- onExceed?: (file: File) => any;
25
- "onUpdate:file"?: (value: string) => any;
23
+ onSuccess?: (file: UploadFileItem, response: string) => any;
24
+ onError?: (file: UploadFileItem, error: unknown) => any;
25
+ onProgress?: (file: UploadFileItem) => any;
26
+ onChange?: (file: UploadFileItem) => any;
27
+ onRemove?: (file: UploadFileItem) => any;
28
+ onExceed?: (files: File[]) => any;
29
+ "onUpdate:fileList"?: (value: UploadFileItem[]) => any;
26
30
  }>, {
27
- buttonMode: boolean;
28
- buttonText: string;
29
31
  hint: string;
32
+ autoUpload: boolean;
30
33
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
31
34
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
32
35
  export default _default;
@@ -4,6 +4,4 @@ declare const install: Plugin;
4
4
  export default install;
5
5
  export * from './components';
6
6
  export * from './composables';
7
- export type { FDialogFn, FDialogOptions } from './types/components';
8
- export type { FMessageFn, FMessageOptions, FMessageInstance } from './types/components';
9
- export type { FNotificationFn, FNotificationOptions, FNotificationInstance } from './types/components';
7
+ export type { FDialogFn, FDialogOptions, FMessageFn, FMessageOptions, FMessageInstance, FNotificationFn, FNotificationOptions, FNotificationInstance, UploadFileItem } from './types/components';
@@ -1,11 +1,28 @@
1
- type imageType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/jpg' | 'image/bmp' | 'image/gif' | 'image/svg+xml' | 'image/svg';
2
- type fileType = imageType;
3
- type fileTypes = 'image';
4
- type fileTypesMap = {
5
- [key in fileTypes]: fileType[];
6
- };
7
- interface SheetData {
8
- sheetName: string;
9
- data: any[];
1
+ interface FUploadProps {
2
+ accept?: string;
3
+ hint?: string;
4
+ /** 最多允许存在的文件数(数量限制,不是字节大小) */
5
+ limit?: number;
6
+ /** 选中后是否立即自动上传,默认 true */
7
+ autoUpload?: boolean;
8
+ /**
9
+ * 校验/拦截钩子:返回 false 或 reject 则该文件不会进入列表也不会上传。
10
+ * 用来做文件大小、类型等前置校验,替代过去分散在各业务组件里的校验代码。
11
+ */
12
+ beforeUpload?: (file: File) => boolean | Promise<boolean>;
13
+ /**
14
+ * 实际上传逻辑,由调用方注入——组件本身不关心用的是 fetch/axios/dispatcher,
15
+ * 只负责在合适的时机调用它,并把 onProgress 回调交给它在上传过程中调用。
16
+ */
17
+ httpRequest?: (file: File, onProgress: (percent: number) => void) => Promise<string>;
10
18
  }
11
- export type { fileTypesMap, SheetData, imageType };
19
+ interface UploadFileItem {
20
+ uid: string;
21
+ name: string;
22
+ status: 'ready' | 'uploading' | 'success' | 'error';
23
+ percentage: number;
24
+ raw: File;
25
+ response?: string;
26
+ errorMessage?: string;
27
+ }
28
+ export type { FUploadProps, UploadFileItem };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fantasia-ui",
3
3
  "private": false,
4
- "version": "0.2.56",
4
+ "version": "0.2.59",
5
5
  "type": "module",
6
6
  "main": "dist/fantasia-ui.umd.js",
7
7
  "module": "dist/fantasia-ui.es.js",