qidian-vue-ui 1.4.0 → 1.5.0

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.
@@ -2,7 +2,7 @@ export declare const QdConfigProvider: import("vue").DefineComponent<{
2
2
  locale?: string | undefined;
3
3
  user?: import("./types").QdUser | undefined;
4
4
  dict?: import("../..").QdDictBaseOptions | undefined;
5
- upload?: Pick<import("..").QdUploadProps, "action" | "headers" | "isBatchUpload" | "method" | "name" | "requestMethod" | "listOssByIds" | "listOssByUrls" | "del"> | undefined;
5
+ upload?: Pick<import("..").QdUploadProps, "action" | "headers" | "isBatchUpload" | "method" | "name" | "requestMethod" | "listOssByIds" | "listOssByUrls" | "data" | "withCredentials" | "formatRequest" | "useMockProgress" | "mockProgressDuration" | "onProgress" | "del"> | undefined;
6
6
  avatar?: import("../..").QdAvatarBaseOptions | undefined;
7
7
  agentChat?: Pick<import("../..").UseAgentChatOptions, "getSessionId" | "getUrl"> | undefined;
8
8
  org?: {
@@ -56,6 +56,7 @@ export declare const QdConfigProvider: import("vue").DefineComponent<{
56
56
  sameNameMessage?: string;
57
57
  fileSizeExceedsLimit?: string;
58
58
  fileUploadFailed?: string;
59
+ fileUploadTimeout?: string;
59
60
  viewFileText?: string;
60
61
  };
61
62
  chat?: NonNullable<import("tdesign-vue-next").TdConfigProviderProps["globalConfig"]>["chat"] & {
@@ -131,7 +132,7 @@ export declare const QdConfigProvider: import("vue").DefineComponent<{
131
132
  locale?: string | undefined;
132
133
  user?: import("./types").QdUser | undefined;
133
134
  dict?: import("../..").QdDictBaseOptions | undefined;
134
- upload?: Pick<import("..").QdUploadProps, "action" | "headers" | "isBatchUpload" | "method" | "name" | "requestMethod" | "listOssByIds" | "listOssByUrls" | "del"> | undefined;
135
+ upload?: Pick<import("..").QdUploadProps, "action" | "headers" | "isBatchUpload" | "method" | "name" | "requestMethod" | "listOssByIds" | "listOssByUrls" | "data" | "withCredentials" | "formatRequest" | "useMockProgress" | "mockProgressDuration" | "onProgress" | "del"> | undefined;
135
136
  avatar?: import("../..").QdAvatarBaseOptions | undefined;
136
137
  agentChat?: Pick<import("../..").UseAgentChatOptions, "getSessionId" | "getUrl"> | undefined;
137
138
  org?: {
@@ -185,6 +186,7 @@ export declare const QdConfigProvider: import("vue").DefineComponent<{
185
186
  sameNameMessage?: string;
186
187
  fileSizeExceedsLimit?: string;
187
188
  fileUploadFailed?: string;
189
+ fileUploadTimeout?: string;
188
190
  viewFileText?: string;
189
191
  };
190
192
  chat?: NonNullable<import("tdesign-vue-next").TdConfigProviderProps["globalConfig"]>["chat"] & {
@@ -299,6 +301,7 @@ export declare const QdConfigProvider: import("vue").DefineComponent<{
299
301
  sameNameMessage?: string;
300
302
  fileSizeExceedsLimit?: string;
301
303
  fileUploadFailed?: string;
304
+ fileUploadTimeout?: string;
302
305
  viewFileText?: string;
303
306
  };
304
307
  chat?: NonNullable<import("tdesign-vue-next").TdConfigProviderProps["globalConfig"]>["chat"] & {
@@ -368,7 +371,7 @@ export declare const QdConfigProvider: import("vue").DefineComponent<{
368
371
  closeText?: string;
369
372
  };
370
373
  }) | undefined;
371
- upload: Pick<import("..").QdUploadProps<import("tdesign-vue-next").UploadFile, unknown, string | number | (string | number)[], string | number | (string | number)[], unknown, string | string[], string | string[], unknown, string | number | (string | number)[], string | number | (string | number)[]>, "name" | "del" | "action" | "headers" | "method" | "isBatchUpload" | "requestMethod" | "listOssByIds" | "listOssByUrls"> | undefined;
374
+ upload: Pick<import("..").QdUploadProps<import("tdesign-vue-next").UploadFile, unknown, string | number | (string | number)[], string | number | (string | number)[], unknown, string | string[], string | string[], unknown, string | number | (string | number)[], string | number | (string | number)[]>, "data" | "name" | "del" | "onProgress" | "action" | "headers" | "method" | "formatRequest" | "isBatchUpload" | "mockProgressDuration" | "requestMethod" | "useMockProgress" | "withCredentials" | "listOssByIds" | "listOssByUrls"> | undefined;
372
375
  office: {
373
376
  pdfSandboxUrl?: string | undefined;
374
377
  pdfWorkerUrl?: string | undefined;
@@ -15,7 +15,7 @@ export interface QdConfigProviderProps {
15
15
  locale?: string;
16
16
  user?: QdUser;
17
17
  dict?: QdDictBaseOptions;
18
- upload?: Pick<QdUploadProps, 'action' | 'headers' | 'isBatchUpload' | 'method' | 'name' | 'requestMethod' | 'listOssByIds' | 'listOssByUrls' | 'del'>;
18
+ upload?: Pick<QdUploadProps, 'action' | 'headers' | 'isBatchUpload' | 'method' | 'name' | 'requestMethod' | 'listOssByIds' | 'listOssByUrls' | 'data' | 'withCredentials' | 'formatRequest' | 'useMockProgress' | 'mockProgressDuration' | 'onProgress' | 'del'>;
19
19
  avatar?: QdAvatarBaseOptions;
20
20
  agentChat?: Pick<UseAgentChatOptions, 'getSessionId' | 'getUrl'>;
21
21
  org?: {
@@ -71,6 +71,7 @@ export interface QdConfigProviderProps {
71
71
  sameNameMessage?: string;
72
72
  fileSizeExceedsLimit?: string;
73
73
  fileUploadFailed?: string;
74
+ fileUploadTimeout?: string;
74
75
  viewFileText?: string;
75
76
  };
76
77
  chat?: NonNullable<TdConfigProviderProps['globalConfig']>['chat'] & {
@@ -44,6 +44,7 @@ export declare function useConfig<T extends keyof NonNullable<QdConfigProviderPr
44
44
  sameNameMessage?: string;
45
45
  fileSizeExceedsLimit?: string;
46
46
  fileUploadFailed?: string;
47
+ fileUploadTimeout?: string;
47
48
  viewFileText?: string;
48
49
  };
49
50
  chat?: NonNullable<import("tdesign-vue-next").TdConfigProviderProps["globalConfig"]>["chat"] & {
@@ -156,6 +157,7 @@ export declare function useConfig<T extends keyof NonNullable<QdConfigProviderPr
156
157
  sameNameMessage?: string;
157
158
  fileSizeExceedsLimit?: string;
158
159
  fileUploadFailed?: string;
160
+ fileUploadTimeout?: string;
159
161
  viewFileText?: string;
160
162
  };
161
163
  chat?: NonNullable<import("tdesign-vue-next").TdConfigProviderProps["globalConfig"]>["chat"] & {
@@ -268,6 +270,7 @@ export declare function useConfig<T extends keyof NonNullable<QdConfigProviderPr
268
270
  sameNameMessage?: string;
269
271
  fileSizeExceedsLimit?: string;
270
272
  fileUploadFailed?: string;
273
+ fileUploadTimeout?: string;
271
274
  viewFileText?: string;
272
275
  };
273
276
  chat?: NonNullable<import("tdesign-vue-next").TdConfigProviderProps["globalConfig"]>["chat"] & {
@@ -381,6 +384,7 @@ export declare function useConfig<T extends keyof NonNullable<QdConfigProviderPr
381
384
  sameNameMessage?: string;
382
385
  fileSizeExceedsLimit?: string;
383
386
  fileUploadFailed?: string;
387
+ fileUploadTimeout?: string;
384
388
  viewFileText?: string;
385
389
  };
386
390
  chat?: NonNullable<import("tdesign-vue-next").TdConfigProviderProps["globalConfig"]>["chat"] & {
@@ -1,25 +1,25 @@
1
- .qd-service-table[data-v-5806889d] {
1
+ .qd-service-table[data-v-c2177f89] {
2
2
  height: 100%;
3
3
  display: flex;
4
4
  flex-direction: column;
5
5
  }
6
- .qd-service-table[data-v-5806889d] > .t-table__content {
6
+ .qd-service-table[data-v-c2177f89] > .t-table__content {
7
7
  flex: 1;
8
8
  }
9
- .qd-service-table[data-v-5806889d] .t-table__tree-col:not(:has(.t-table__tree-op-icon)) {
9
+ .qd-service-table[data-v-c2177f89] .t-table__tree-col:not(:has(.t-table__tree-op-icon)) {
10
10
  margin-left: calc(var(--td-font-size-body-large) + var(--td-comp-margin-s));
11
11
  }
12
- .qd-service-table[data-v-5806889d] .t-table__tree-leaf-node .t-table__tree-op-icon:empty {
12
+ .qd-service-table[data-v-c2177f89] .t-table__tree-leaf-node .t-table__tree-op-icon:empty {
13
13
  width: calc(var(--td-font-size-body-large) + var(--td-comp-margin-s));
14
14
  }
15
- .qd-service-table[data-v-5806889d] .t-affix {
16
- transform: var(--v5ea6151a);
15
+ .qd-service-table[data-v-c2177f89] .t-affix {
16
+ transform: var(--v072441ea);
17
17
  }
18
- .qd-service-table--ps-x-fixed[data-v-5806889d] .ps__rail-x {
18
+ .qd-service-table--ps-x-fixed[data-v-c2177f89] .ps__rail-x {
19
19
  position: fixed;
20
- left: var(--v60ce71c8) !important;
21
- bottom: var(--v6db618c6) !important;
20
+ left: var(--ac91cc66) !important;
21
+ bottom: var(--v79d9a896) !important;
22
22
  }
23
- .qd-service-table--disable-horizontal-scroll[data-v-5806889d] colgroup > col {
23
+ .qd-service-table--disable-horizontal-scroll[data-v-c2177f89] colgroup > col {
24
24
  width: auto !important;
25
25
  }
@@ -2,7 +2,7 @@ import './index.css'
2
2
  import _sfc_main from "./index.vue2.mjs";
3
3
  /* empty css */
4
4
  import _export_sfc from "../../../_virtual/_plugin-vue_export-helper.mjs";
5
- const QdServiceTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-5806889d"]]);
5
+ const QdServiceTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-c2177f89"]]);
6
6
  export {
7
7
  QdServiceTable as default
8
8
  };
@@ -307,9 +307,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
307
307
  emit: __emit
308
308
  }) {
309
309
  useCssVars((_ctx) => ({
310
- "v5ea6151a": affixedHeaderTransform.value,
311
- "v60ce71c8": tableContentLeft.value,
312
- "v6db618c6": outsideBottom.value
310
+ "v072441ea": affixedHeaderTransform.value,
311
+ "ac91cc66": tableContentLeft.value,
312
+ "v79d9a896": outsideBottom.value
313
313
  }));
314
314
  const props = __props;
315
315
  const emit = __emit;
@@ -357,17 +357,22 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
357
357
  const tableContentRect = reactive(useElementBounding(tableContentEl));
358
358
  const tableContentLeft = computed(() => `${tableContentRect.left - affixedRailOffsetX.value}px`);
359
359
  const outsideScrollRect = reactive(useElementBounding(outsideScrollEl));
360
- const outsideBottom = computed(() => outsideScrollEl.value ? `${window.innerHeight - outsideScrollRect.bottom}px` : "0");
360
+ const affixBottomOffset = ref(0);
361
+ let probeRafId = 0;
362
+ let animatingAncestorCleanup = null;
363
+ const outsideBottom = computed(() => {
364
+ if (!outsideScrollEl.value) return "0";
365
+ return `${Math.max(0, window.innerHeight - outsideScrollRect.bottom + affixBottomOffset.value)}px`;
366
+ });
361
367
  const tableHeaderHalfVisible = computed(() => {
362
368
  if (!tableContentVisible.value) return false;
363
369
  return tableContentRect.top + tableHeaderH.value * 0.5 <= outsideScrollRect.bottom;
364
370
  });
365
371
  const tableContentFullVisible = computed(() => {
366
372
  if (!tableContentVisible.value) return false;
367
- const contentBottom = tableContentRect.bottom;
368
- const containerBottom = outsideScrollRect.bottom;
369
- return contentBottom <= containerBottom;
373
+ return tableContentRect.bottom <= outsideScrollRect.bottom - affixBottomOffset.value;
370
374
  });
375
+ const isPsXFixed = computed(() => tableContentVisible.value && tableHeaderHalfVisible.value && !tableContentFullVisible.value);
371
376
  const {
372
377
  ps: tablePs
373
378
  } = usePerfectScrollbar(tableContentEl, {
@@ -827,11 +832,15 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
827
832
  return keys;
828
833
  }, []);
829
834
  }
835
+ function refreshAffixedMetrics() {
836
+ tableContentRect.update();
837
+ outsideScrollRect.update();
838
+ affixedRailOffsetX.value = getTransformOffset(affixBaseEl.value).x;
839
+ }
830
840
  function onResize(entries) {
831
841
  const el = entries[0].target;
832
842
  outsideScrollEl.value = getScrollEl(el);
833
- outsideScrollRect.update();
834
- affixedRailOffsetX.value = getTransformOffset(affixBaseEl.value).x;
843
+ refreshAffixedMetrics();
835
844
  const headerEl = el.querySelector(".t-table__header");
836
845
  tableHeaderEl.value = headerEl;
837
846
  let headerH = 0;
@@ -839,6 +848,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
839
848
  tableHeaderH.value = headerH;
840
849
  const contentEl = el.querySelector(".t-table__content");
841
850
  tableContentEl.value = contentEl;
851
+ probeBottomBlockHeight();
842
852
  }
843
853
  function isTransformAnimating(node) {
844
854
  return node.getAnimations().length > 0;
@@ -853,6 +863,23 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
853
863
  }
854
864
  return null;
855
865
  }
866
+ function watchAnimatingTransformAncestor() {
867
+ animatingAncestorCleanup?.();
868
+ const animatingNode = findAnimatingTransformAncestor(affixBaseEl.value);
869
+ if (!animatingNode) return;
870
+ const cleanup = () => {
871
+ animatingNode.removeEventListener("transitionend", onEnd);
872
+ animatingNode.removeEventListener("animationend", onEnd);
873
+ animatingAncestorCleanup = null;
874
+ };
875
+ const onEnd = () => {
876
+ cleanup();
877
+ refreshAffixedMetrics();
878
+ };
879
+ animatingNode.addEventListener("transitionend", onEnd);
880
+ animatingNode.addEventListener("animationend", onEnd);
881
+ animatingAncestorCleanup = cleanup;
882
+ }
856
883
  async function applyAffixedTransform() {
857
884
  const el = affixBaseEl.value;
858
885
  if (!el) return;
@@ -863,6 +890,33 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
863
890
  });
864
891
  affixedHeaderTransform.value = `translateY(-${y}px)`;
865
892
  }
893
+ function scheduleProbeBottomBlock() {
894
+ if (probeRafId) return;
895
+ probeRafId = requestAnimationFrame(() => {
896
+ probeRafId = 0;
897
+ probeBottomBlockHeight();
898
+ });
899
+ }
900
+ function probeBottomBlockHeight() {
901
+ const scrollEl = outsideScrollEl.value;
902
+ const tableEl = tableRef.value?.$el;
903
+ if (!scrollEl || !tableEl) return;
904
+ const sRect = scrollEl.getBoundingClientRect();
905
+ const stack = document.elementsFromPoint(sRect.left + sRect.width / 2, sRect.bottom - 1);
906
+ let blockHeight = 0;
907
+ for (const el of stack) {
908
+ if (tableEl.contains(el)) continue;
909
+ if (el === scrollEl || el.contains(scrollEl)) continue;
910
+ const {
911
+ position
912
+ } = getComputedStyle(el);
913
+ if (position !== "fixed" && position !== "sticky") continue;
914
+ const rect = el.getBoundingClientRect();
915
+ if (rect.top < sRect.top) continue;
916
+ blockHeight = Math.max(blockHeight, sRect.bottom - rect.top);
917
+ }
918
+ affixBottomOffset.value = blockHeight;
919
+ }
866
920
  watch(columnControllerVisible, (visible) => {
867
921
  const {
868
922
  columns
@@ -881,6 +935,31 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
881
935
  requestAnimationFrame(() => {
882
936
  tablePs.value?.update();
883
937
  });
938
+ watchAnimatingTransformAncestor();
939
+ nextTick(() => {
940
+ setTimeout(refreshAffixedMetrics, 320);
941
+ });
942
+ });
943
+ watch(isOutsideTableContent, (outside) => {
944
+ if (outside) return;
945
+ refreshAffixedMetrics();
946
+ });
947
+ watch(outsideScrollEl, (el, _old, onCleanup) => {
948
+ if (!el) return;
949
+ probeBottomBlockHeight();
950
+ const onScroll = () => scheduleProbeBottomBlock();
951
+ el.addEventListener("scroll", onScroll, {
952
+ passive: true
953
+ });
954
+ onCleanup(() => {
955
+ el.removeEventListener("scroll", onScroll);
956
+ if (probeRafId) {
957
+ cancelAnimationFrame(probeRafId);
958
+ probeRafId = 0;
959
+ }
960
+ });
961
+ }, {
962
+ immediate: true
884
963
  });
885
964
  onActivated(() => {
886
965
  setScrollElTo();
@@ -892,6 +971,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
892
971
  onBeforeUnmount(() => {
893
972
  tableContentStop();
894
973
  outsideTableContentStop();
974
+ if (probeRafId) cancelAnimationFrame(probeRafId);
975
+ animatingAncestorCleanup?.();
895
976
  });
896
977
  __expose(createExposeProxy({
897
978
  data,
@@ -919,7 +1000,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
919
1000
  return (_ctx, _cache) => {
920
1001
  return withDirectives((openBlock(), createBlock(resolveDynamicComponent(reProps.value.tableOptions.tree ? unref(EnhancedTable) : unref(Table)), mergeProps({
921
1002
  ref: "table",
922
- class: ["qd-service-table", __props.headerBg ? "" : "t-table--header-bg-inherit", unref(isOutsideTableContent) ? "" : "qd-service-table--inside-table-content", tableContentVisible.value && tableHeaderHalfVisible.value && !tableContentFullVisible.value ? "qd-service-table--ps-x-fixed" : "", __props.disableHorizontalScroll ? "qd-service-table--disable-horizontal-scroll" : ""],
1003
+ class: ["qd-service-table", __props.headerBg ? "" : "t-table--header-bg-inherit", unref(isOutsideTableContent) ? "" : "qd-service-table--inside-table-content", isPsXFixed.value ? "qd-service-table--ps-x-fixed" : "", __props.disableHorizontalScroll ? "qd-service-table--disable-horizontal-scroll" : ""],
923
1004
  "selected-row-keys": unref(selectedRowKeys),
924
1005
  "onUpdate:selectedRowKeys": _cache[0] || (_cache[0] = ($event) => isRef(selectedRowKeys) ? selectedRowKeys.value = $event : null),
925
1006
  "active-row-keys": unref(activeRowKeys),
@@ -1,97 +1,97 @@
1
- .qd-upload[data-v-987466f3] {
1
+ .qd-upload[data-v-b860b275] {
2
2
  width: 100%;
3
3
  outline: none;
4
4
  }
5
- .qd-upload[data-v-987466f3] .t-image-viewer__trigger {
5
+ .qd-upload[data-v-b860b275] .t-image-viewer__trigger {
6
6
  display: none;
7
7
  }
8
- .qd-upload[data-v-987466f3] .t-upload__dragger {
8
+ .qd-upload[data-v-b860b275] .t-upload__dragger {
9
9
  min-width: auto;
10
10
  min-height: 144px;
11
11
  width: 100%;
12
12
  height: 100%;
13
13
  }
14
- .qd-upload[data-v-987466f3] .t-upload__flow {
14
+ .qd-upload[data-v-b860b275] .t-upload__flow {
15
15
  width: 100%;
16
16
  min-width: auto;
17
17
  max-width: 100%;
18
18
  }
19
- .qd-upload[data-v-987466f3] .t-upload__flow-empty {
19
+ .qd-upload[data-v-b860b275] .t-upload__flow-empty {
20
20
  height: auto;
21
21
  min-height: 144px;
22
22
  }
23
- .qd-upload[data-v-987466f3] .t-upload__flow-card-area,
24
- .qd-upload[data-v-987466f3] .t-upload__flow-table {
23
+ .qd-upload[data-v-b860b275] .t-upload__flow-card-area,
24
+ .qd-upload[data-v-b860b275] .t-upload__flow-table {
25
25
  margin-top: var(--td-comp-margin-s);
26
26
  }
27
- .qd-upload[data-v-987466f3] .t-upload__file-name__container {
27
+ .qd-upload[data-v-b860b275] .t-upload__file-name__container {
28
28
  display: flex;
29
29
  align-items: center;
30
30
  }
31
- .qd-upload[data-v-987466f3] .t-upload__file-name-text {
31
+ .qd-upload[data-v-b860b275] .t-upload__file-name-text {
32
32
  cursor: pointer;
33
33
  }
34
- .qd-upload[data-v-987466f3] .t-upload__file-thumbnail {
34
+ .qd-upload[data-v-b860b275] .t-upload__file-thumbnail {
35
35
  width: var(--td-comp-size-m);
36
36
  height: var(--td-comp-size-m);
37
37
  }
38
- .qd-upload[data-v-987466f3] .t-upload__card-image {
38
+ .qd-upload[data-v-b860b275] .t-upload__card-image {
39
39
  width: 100%;
40
40
  height: 100%;
41
41
  }
42
- .qd-upload__popup-upload[data-v-987466f3] .t-image-viewer__trigger {
42
+ .qd-upload__popup-upload[data-v-b860b275] .t-image-viewer__trigger {
43
43
  display: none;
44
44
  }
45
- .qd-upload__popup-upload[data-v-987466f3] .t-upload__flow-table {
45
+ .qd-upload__popup-upload[data-v-b860b275] .t-upload__flow-table {
46
46
  margin-top: 0;
47
47
  }
48
- .qd-upload__popup-upload[data-v-987466f3] .t-upload__file-name__container {
48
+ .qd-upload__popup-upload[data-v-b860b275] .t-upload__file-name__container {
49
49
  display: flex;
50
50
  align-items: center;
51
51
  }
52
- .qd-upload__popup-upload[data-v-987466f3] .t-upload__file-name-text {
52
+ .qd-upload__popup-upload[data-v-b860b275] .t-upload__file-name-text {
53
53
  cursor: pointer;
54
54
  }
55
- .qd-upload__popup-upload[data-v-987466f3] .t-upload__file-thumbnail {
55
+ .qd-upload__popup-upload[data-v-b860b275] .t-upload__file-thumbnail {
56
56
  width: var(--td-comp-size-m);
57
57
  height: var(--td-comp-size-m);
58
58
  }
59
- .qd-upload__popup-upload[data-v-987466f3] .t-upload__card-image {
59
+ .qd-upload__popup-upload[data-v-b860b275] .t-upload__card-image {
60
60
  width: 100%;
61
61
  height: 100%;
62
62
  }
63
- .qd-upload__popup-upload--disabled[data-v-987466f3] .t-upload__flow-table th:last-child,
64
- .qd-upload__popup-upload--disabled[data-v-987466f3] .t-upload__flow-table td:last-child {
63
+ .qd-upload__popup-upload--disabled[data-v-b860b275] .t-upload__flow-table th:last-child,
64
+ .qd-upload__popup-upload--disabled[data-v-b860b275] .t-upload__flow-table td:last-child {
65
65
  display: none;
66
66
  }
67
- .qd-upload[data-v-987466f3]:focus .t-upload__flow-card-area,
68
- .qd-upload[data-v-987466f3]:focus .t-upload__flow-table {
67
+ .qd-upload[data-v-b860b275]:focus .t-upload__flow-card-area,
68
+ .qd-upload[data-v-b860b275]:focus .t-upload__flow-table {
69
69
  border-color: var(--td-brand-color);
70
70
  }
71
- .qd-upload--only-view[data-v-987466f3] .t-upload__flow-empty,
72
- .qd-upload--only-view[data-v-987466f3] .t-upload__card-mask-item-divider {
71
+ .qd-upload--only-view[data-v-b860b275] .t-upload__flow-empty,
72
+ .qd-upload--only-view[data-v-b860b275] .t-upload__card-mask-item-divider {
73
73
  display: none;
74
74
  }
75
- .qd-upload--only-view[data-v-987466f3] .t-upload__flow-card-area {
75
+ .qd-upload--only-view[data-v-b860b275] .t-upload__flow-card-area {
76
76
  border-color: var(--td-component-border);
77
77
  }
78
- .qd-upload--only-view[data-v-987466f3]:not(.qd-upload--btn-image-popup):not(.qd-upload--btn-file-popup) .t-upload__flow-op,
79
- .qd-upload--only-view[data-v-987466f3]:not(.qd-upload--btn-image-popup):not(.qd-upload--btn-file-popup) .t-upload__trigger,
80
- .qd-upload--only-view[data-v-987466f3]:not(.qd-upload--btn-image-popup):not(.qd-upload--btn-file-popup) .t-upload__flow-bottom {
78
+ .qd-upload--only-view[data-v-b860b275]:not(.qd-upload--btn-image-popup):not(.qd-upload--btn-file-popup) .t-upload__flow-op,
79
+ .qd-upload--only-view[data-v-b860b275]:not(.qd-upload--btn-image-popup):not(.qd-upload--btn-file-popup) .t-upload__trigger,
80
+ .qd-upload--only-view[data-v-b860b275]:not(.qd-upload--btn-image-popup):not(.qd-upload--btn-file-popup) .t-upload__flow-bottom {
81
81
  display: none;
82
82
  }
83
- .qd-upload--only-view[data-v-987466f3]:not(.qd-upload--btn-image-popup):not(.qd-upload--btn-file-popup) .t-upload__flow-table,
84
- .qd-upload--only-view[data-v-987466f3]:not(.qd-upload--btn-image-popup):not(.qd-upload--btn-file-popup) .t-upload__flow-card-area {
83
+ .qd-upload--only-view[data-v-b860b275]:not(.qd-upload--btn-image-popup):not(.qd-upload--btn-file-popup) .t-upload__flow-table,
84
+ .qd-upload--only-view[data-v-b860b275]:not(.qd-upload--btn-image-popup):not(.qd-upload--btn-file-popup) .t-upload__flow-card-area {
85
85
  margin-top: 0;
86
86
  }
87
- .qd-upload--hide-manual-upload[data-v-987466f3] .t-upload__flow-bottom {
87
+ .qd-upload--hide-manual-upload[data-v-b860b275] .t-upload__flow-bottom {
88
88
  display: none;
89
89
  margin-top: 0;
90
90
  }
91
- .qd-upload--hide-progress[data-v-987466f3] .t-upload__flow-table th:last-child,
92
- .qd-upload--hide-progress[data-v-987466f3] .t-upload__flow-table td:last-child {
91
+ .qd-upload--hide-progress[data-v-b860b275] .t-upload__flow-table th:last-child,
92
+ .qd-upload--hide-progress[data-v-b860b275] .t-upload__flow-table td:last-child {
93
93
  display: none;
94
94
  }
95
- .qd-upload--disabled[data-v-987466f3] .t-upload__flow-card-area {
95
+ .qd-upload--disabled[data-v-b860b275] .t-upload__flow-card-area {
96
96
  border-color: var(--td-component-border);
97
97
  }
@@ -2,7 +2,7 @@ import './index.css'
2
2
  import _sfc_main from "./index.vue2.mjs";
3
3
  /* empty css */
4
4
  import _export_sfc from "../../_virtual/_plugin-vue_export-helper.mjs";
5
- const QdUpload = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-987466f3"]]);
5
+ const QdUpload = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-b860b275"]]);
6
6
  export {
7
7
  QdUpload as default
8
8
  };
@@ -1,5 +1,5 @@
1
1
  import './index.css'
2
- import { defineComponent, mergeDefaults, computed, inject, useTemplateRef, ref, watch, onMounted, createBlock, openBlock, unref, mergeProps, isRef, createSlots, withCtx, createCommentVNode, createTextVNode, toDisplayString, createVNode, withModifiers, normalizeClass, createElementVNode, renderList, renderSlot, normalizeProps, guardReactiveProps } from "vue";
2
+ import { defineComponent, mergeDefaults, computed, inject, useTemplateRef, ref, watch, onUnmounted, onMounted, createBlock, openBlock, unref, mergeProps, isRef, createSlots, withCtx, createCommentVNode, createTextVNode, toDisplayString, createVNode, withModifiers, normalizeClass, createElementVNode, renderList, renderSlot, normalizeProps, guardReactiveProps, nextTick } from "vue";
3
3
  import { useVModels } from "../../node_modules/.pnpm/@vueuse_core@14.2.0_vue@3.5.26_typescript@5.9.3_/node_modules/@vueuse/core/dist/index.mjs";
4
4
  import dayjs from "../../_virtual/dayjs.min.mjs";
5
5
  import { isEmpty, useTimer, easyCopy, checkSameObject, to, filterSlots, isTypeByName, suspectedWrapperPromise } from "qidian-shared";
@@ -10,6 +10,7 @@ import { useConfig } from "../config-provider/useConfig.mjs";
10
10
  import { qdDictConvStrSeparator } from "../dict/tag/props.mjs";
11
11
  import { qdUploadAcceptMap } from "./config.mjs";
12
12
  import { qdUploadSizeLimit, qdUploadProps } from "./props.mjs";
13
+ import { uploadByXhr, calcUploadTimeout } from "./request.mjs";
13
14
  import { PROVIDE_FORM_ADD_BEFORE_SUBMIT_QUEUE, PROVIDE_FORM_ADD_AFTER_SUBMIT_QUEUE, PROVIDE_FORM_RESET_QUEUE } from "../form/config.mjs";
14
15
  import { useReadonly } from "../../hooks/useReadonly.mjs";
15
16
  import { useDisabled } from "../../hooks/useDisabled.mjs";
@@ -19,6 +20,7 @@ import _sfc_main$3 from "../multi-file-viewer/index.vue.mjs";
19
20
  /* empty css */
20
21
  import _sfc_main$4 from "./image-flow-file-list-display/index.vue.mjs";
21
22
  import { useModal } from "../../hooks/useModal.mjs";
23
+ const tempOssIdPrefix = "__qd_upload_temp_";
22
24
  const _sfc_main = /* @__PURE__ */ defineComponent({
23
25
  ...{
24
26
  name: "QdUpload"
@@ -28,6 +30,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
28
30
  modelValue: {},
29
31
  action: { type: [String, Function] },
30
32
  bucketConfigKey: {},
33
+ timeout: {},
31
34
  convStr: { type: [Boolean, String] },
32
35
  valueMode: {},
33
36
  theme: {},
@@ -132,6 +135,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
132
135
  const { msgError, msgWarning } = useModal();
133
136
  const uploadRef = useTemplateRef("upload");
134
137
  const { timer: uploadTimer, clearTimer: clearUploadTimer } = useTimer("interval");
138
+ let isSyncingUploadModelValue = false;
139
+ const tempUrlMap = /* @__PURE__ */ new WeakMap();
140
+ const tempModelValueSet = /* @__PURE__ */ new Set();
135
141
  const delList = ref([]);
136
142
  const popupFileList = computed(() => [...fileList.value]);
137
143
  const popupVisible = ref(false);
@@ -171,6 +177,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
171
177
  del,
172
178
  action,
173
179
  bucketConfigKey,
180
+ timeout,
174
181
  showImageFileName,
175
182
  showUploadProgress,
176
183
  showThumbnail,
@@ -183,6 +190,49 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
183
190
  const globalUploadPropsV = globalUploadProps.value;
184
191
  const reAction = action || globalUploadPropsV?.action;
185
192
  const actionUrl = typeof reAction === "string" ? reAction : reAction?.(bucketConfigKey);
193
+ const useUploadTimeout = timeout === void 0 || timeout > 0;
194
+ const baseTimeout = timeout ?? 1e3 * 30;
195
+ const customRequestMethod = props.requestMethod ?? globalUploadPropsV?.requestMethod;
196
+ const useRequestMethod = !customRequestMethod && useUploadTimeout && !!actionUrl;
197
+ const uploadMethod = props.method ?? globalUploadPropsV?.method;
198
+ const uploadName = props.name ?? globalUploadPropsV?.name;
199
+ const uploadWithCredentials = props.withCredentials ?? globalUploadPropsV?.withCredentials;
200
+ const uploadUseMockProgress = props.useMockProgress ?? globalUploadPropsV?.useMockProgress;
201
+ const uploadMockProgressDuration = props.mockProgressDuration ?? globalUploadPropsV?.mockProgressDuration;
202
+ const uploadHeaders = props.headers ?? globalUploadPropsV?.headers;
203
+ const uploadData = props.data ?? globalUploadPropsV?.data;
204
+ const uploadFormatRequest = props.formatRequest ?? globalUploadPropsV?.formatRequest;
205
+ const uploadOnProgress = props.onProgress ?? globalUploadPropsV?.onProgress;
206
+ const reRequestMethod = useRequestMethod ? (files2) => {
207
+ const fileArr = Array.isArray(files2) ? files2 : [files2];
208
+ const totalSize = fileArr.reduce(
209
+ (sum, file) => sum + (file.raw?.size ?? file.size ?? 0),
210
+ 0
211
+ );
212
+ return uploadByXhr(fileArr, {
213
+ method: uploadMethod,
214
+ action: actionUrl,
215
+ withCredentials: uploadWithCredentials,
216
+ headers: uploadHeaders,
217
+ data: uploadData,
218
+ name: uploadName,
219
+ formatRequest: uploadFormatRequest,
220
+ useMockProgress: uploadUseMockProgress,
221
+ mockProgressDuration: uploadMockProgressDuration,
222
+ onProgress: uploadOnProgress,
223
+ timeout: calcUploadTimeout(baseTimeout, totalSize)
224
+ }).then((result) => {
225
+ if (result.status === "success") {
226
+ const response = result.response;
227
+ return {
228
+ status: "success",
229
+ response: { ...response, url: response?.url ?? response?.data?.url }
230
+ };
231
+ }
232
+ const errorMsg = result.error === "timeout" ? t("fileUploadTimeout") : t("fileUploadFailed");
233
+ return { status: "fail", error: errorMsg, response: {} };
234
+ });
235
+ } : void 0;
186
236
  const reSizeLimit = easyCopy(qdUploadSizeLimit);
187
237
  if (reSizeLimit) {
188
238
  if (typeof sizeLimit === "number") {
@@ -238,15 +288,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
238
288
  };
239
289
  const reOnSuccess = (context) => {
240
290
  onSuccess?.(context);
241
- updateModelValue(fileList.value);
291
+ revokeTempUrl(context.file);
292
+ updateTempModelValue(fileList.value);
242
293
  };
243
294
  const reOnRemove = (context) => {
244
295
  onRemove?.(context);
296
+ revokeTempUrl(context.file);
245
297
  const { ossId } = context.file?.response || {};
246
298
  if (ossId) {
247
299
  addDel(ossId);
248
300
  }
249
- updateModelValue(fileList.value);
301
+ updateTempModelValue(fileList.value);
250
302
  };
251
303
  const reOnPreview = ({ file, index, e }) => {
252
304
  popupVisible.value = false;
@@ -266,11 +318,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
266
318
  onWaitingUploadFilesChange?.(context);
267
319
  if (context.trigger === "validate" && context.files.length) {
268
320
  onValidateSuccess?.(context.files);
321
+ nextTick(() => updateTempModelValue(fileList.value));
269
322
  }
270
323
  };
271
324
  const reUploadOptions = {
272
325
  ...uploadOptions,
273
- action: actionUrl,
326
+ ...useRequestMethod ? { requestMethod: reRequestMethod } : { action: actionUrl },
274
327
  theme: ["custom", "file", "file-input", "file-flow", "image", "image-flow"].includes(theme) ? theme : "custom",
275
328
  sizeLimit: reSizeLimit,
276
329
  accept: acceptStr,
@@ -335,13 +388,18 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
335
388
  });
336
389
  const expose = {};
337
390
  const uploadFiles = (options) => {
391
+ fileList.value.forEach((file) => {
392
+ if (file.status === "fail" && file.response) {
393
+ delete file.response.error;
394
+ }
395
+ });
338
396
  uploadRef.value?.uploadFiles(options?.files);
339
397
  return new Promise((resolve, reject) => {
340
398
  uploadTimer.value = setInterval(() => {
341
399
  const isFinished = fileList.value.every((item) => item.status !== "progress");
342
400
  if (!isFinished) return;
343
401
  clearUploadTimer();
344
- updateModelValue(fileList.value);
402
+ updateTempModelValue(fileList.value);
345
403
  const errorList = fileList.value.filter((item) => item.status === "fail");
346
404
  if (errorList.length) {
347
405
  const msg = `${errorList.map((item) => item.name).join(",")} ${t("fileUploadFailed")}`;
@@ -355,13 +413,52 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
355
413
  }, 100);
356
414
  });
357
415
  };
358
- function updateModelValue(files) {
416
+ function isTempModelValue(value) {
417
+ return value.startsWith(tempOssIdPrefix) || tempModelValueSet.has(value);
418
+ }
419
+ function genTempValue(file) {
420
+ if (!file.uid) {
421
+ file.uid = `qd_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
422
+ }
423
+ if (reProps.value.valueMode === "url") {
424
+ if (file.raw) {
425
+ let tempUrl = tempUrlMap.get(file);
426
+ if (!tempUrl) {
427
+ tempUrl = URL.createObjectURL(file.raw);
428
+ tempUrlMap.set(file, tempUrl);
429
+ }
430
+ tempModelValueSet.add(tempUrl);
431
+ return tempUrl;
432
+ }
433
+ return file.url || file.uid;
434
+ }
435
+ return `${tempOssIdPrefix}${file.uid}`;
436
+ }
437
+ function updateTempModelValue(files) {
359
438
  const tempModelValue = [];
360
439
  files.forEach((item) => {
361
- if (item.status !== "success") return;
362
- tempModelValue.push(item.response[reProps.value.valueMode]);
440
+ if (item.status === "success") {
441
+ tempModelValue.push(item.response[reProps.value.valueMode]);
442
+ return;
443
+ }
444
+ tempModelValue.push(genTempValue(item));
363
445
  });
446
+ isSyncingUploadModelValue = true;
364
447
  reModelValue.value = tempModelValue;
448
+ nextTick(() => {
449
+ isSyncingUploadModelValue = false;
450
+ });
451
+ }
452
+ function revokeTempUrl(file) {
453
+ if (!file) return;
454
+ const tempUrl = tempUrlMap.get(file);
455
+ if (!tempUrl) return;
456
+ URL.revokeObjectURL(tempUrl);
457
+ tempUrlMap.delete(file);
458
+ tempModelValueSet.delete(tempUrl);
459
+ }
460
+ function revokeAllTempUrls() {
461
+ fileList.value.forEach((file) => revokeTempUrl(file));
365
462
  }
366
463
  async function handleDel(ossId, showErrorMessage) {
367
464
  const { del } = reProps.value;
@@ -456,40 +553,64 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
456
553
  return true;
457
554
  }
458
555
  function clear() {
556
+ revokeAllTempUrls();
459
557
  reModelValue.value = [];
460
558
  fileList.value = [];
461
559
  }
462
560
  formAddBeforeSubmitQueue?.(uploadBeforeFormSubmit);
463
561
  formAddAfterSubmitQueue?.(delBeforeFormSubmit);
464
562
  formResetQueue?.(clear);
563
+ function getFileModelValue(file) {
564
+ if (file.status === "success") {
565
+ return file.response?.[reProps.value.valueMode];
566
+ }
567
+ return genTempValue(file);
568
+ }
569
+ function buildFileListByModelValue(modelValue2, ossList) {
570
+ const { valueMode } = reProps.value;
571
+ const ossMap = new Map(ossList.map((item) => [item[valueMode], item]));
572
+ return modelValue2.flatMap((value) => {
573
+ if (isTempModelValue(value)) {
574
+ const localFile = fileList.value.find((f) => getFileModelValue(f) === value);
575
+ return localFile ? [localFile] : [];
576
+ }
577
+ const item = ossMap.get(value);
578
+ if (!item) return [];
579
+ const { url, ossId, originalName, fileSize, contentType, createTime } = item;
580
+ return [
581
+ {
582
+ url,
583
+ uid: ossId,
584
+ name: originalName,
585
+ size: fileSize,
586
+ type: contentType,
587
+ uploadTime: createTime,
588
+ response: { status: "success", url, ossId, name: originalName, uploadTime: createTime },
589
+ status: "success"
590
+ }
591
+ ];
592
+ });
593
+ }
465
594
  watch(
466
595
  reModelValue,
467
596
  async (newVal, oldVal) => {
468
- if (checkSameObject(newVal, oldVal)) return;
597
+ if (isSyncingUploadModelValue) return;
598
+ const reNewVal = newVal.filter((item) => !isTempModelValue(item));
599
+ const reOldVal = oldVal?.filter((item) => !isTempModelValue(item));
469
600
  if (!newVal.length) {
470
601
  fileList.value = [];
471
602
  return;
472
603
  }
604
+ if (!reNewVal.length || checkSameObject(reNewVal, reOldVal)) return;
473
605
  const { valueMode } = reProps.value;
474
606
  const apiFn = valueMode === "url" ? getOssByUrls : getOssByIds;
475
- const [err, res] = await to(apiFn(newVal));
607
+ const [err, res] = await to(apiFn(reNewVal));
476
608
  if (err) return;
477
- fileList.value = res.map((item) => {
478
- const { url, ossId, originalName, fileSize, contentType, createTime } = item;
479
- return {
480
- url,
481
- uid: ossId,
482
- name: originalName,
483
- size: fileSize,
484
- type: contentType,
485
- uploadTime: createTime,
486
- response: { status: "success", url, ossId, name: originalName, uploadTime: createTime },
487
- status: "success"
488
- };
489
- });
609
+ fileList.value = buildFileListByModelValue(newVal, res);
490
610
  },
491
611
  { deep: true, immediate: true }
492
612
  );
613
+ onUnmounted(revokeAllTempUrls);
493
614
  onMounted(() => {
494
615
  Object.assign(expose, uploadRef.value, { uploadFiles, clear });
495
616
  });
@@ -9,16 +9,13 @@ export declare const qdUploadProps: {
9
9
  readonly allowUploadDuplicateFile: true;
10
10
  readonly isBatchUpload: false;
11
11
  readonly uploadAllFilesInOneRequest: false;
12
- readonly method: "POST";
13
12
  readonly multiple: undefined;
14
- readonly name: "file";
15
13
  readonly disabled: undefined;
16
14
  readonly showImageFileName: true;
17
15
  readonly showThumbnail: true;
18
16
  readonly showUploadProgress: true;
19
17
  readonly uploadPastedFiles: true;
20
- readonly useMockProgress: true;
21
- readonly withCredentials: false;
18
+ readonly timeout: undefined;
22
19
  readonly showManualButton: false;
23
20
  readonly del: undefined;
24
21
  };
@@ -9,16 +9,13 @@ const qdUploadProps = {
9
9
  allowUploadDuplicateFile: true,
10
10
  isBatchUpload: false,
11
11
  uploadAllFilesInOneRequest: false,
12
- method: "POST",
13
12
  multiple: void 0,
14
- name: "file",
15
13
  disabled: void 0,
16
14
  showImageFileName: true,
17
15
  showThumbnail: true,
18
16
  showUploadProgress: true,
19
17
  uploadPastedFiles: true,
20
- useMockProgress: true,
21
- withCredentials: false,
18
+ timeout: void 0,
22
19
  showManualButton: false,
23
20
  del: void 0
24
21
  };
@@ -0,0 +1,21 @@
1
+ import type { TdUploadProps, UploadFile } from 'tdesign-vue-next';
2
+ export interface QdUploadXhrOptions {
3
+ method?: string;
4
+ action: string;
5
+ withCredentials?: boolean;
6
+ headers?: Record<string, string>;
7
+ data?: Record<string, any> | ((files: UploadFile[]) => Record<string, any>);
8
+ name?: string;
9
+ formatRequest?: (requestData: Record<string, any>) => Record<string, any>;
10
+ useMockProgress?: boolean;
11
+ mockProgressDuration?: number;
12
+ timeout: number;
13
+ onProgress?: TdUploadProps['onProgress'];
14
+ }
15
+ export interface QdUploadRequestResult {
16
+ status: 'success' | 'fail';
17
+ error?: 'timeout' | 'error' | 'fail';
18
+ response: Record<string, any>;
19
+ }
20
+ export declare function calcUploadTimeout(baseTimeout: number, size: number): number;
21
+ export declare function uploadByXhr(files: UploadFile[], options: QdUploadXhrOptions): Promise<QdUploadRequestResult>;
@@ -0,0 +1,132 @@
1
+ import isFunction from "../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isFunction.mjs";
2
+ function calcUploadTimeout(baseTimeout, size) {
3
+ const fileSizeMB = Math.max(Math.ceil(size / 1024 / 1024), 1);
4
+ return Math.max(Math.round(baseTimeout + (fileSizeMB - 1) * (baseTimeout / 3)), 1e3 * 20);
5
+ }
6
+ function uploadByXhr(files, options) {
7
+ const {
8
+ method = "POST",
9
+ action,
10
+ withCredentials = false,
11
+ headers = {},
12
+ data = {},
13
+ name = "file",
14
+ formatRequest,
15
+ useMockProgress = true,
16
+ mockProgressDuration = 300,
17
+ timeout,
18
+ onProgress
19
+ } = options;
20
+ return new Promise((resolve) => {
21
+ const xhr = new XMLHttpRequest();
22
+ if (withCredentials) xhr.withCredentials = true;
23
+ let percent = 0;
24
+ let mockTimer;
25
+ let mockDelayTimer;
26
+ let settled = false;
27
+ const finish = (result) => {
28
+ if (settled) return;
29
+ settled = true;
30
+ clearInterval(mockTimer);
31
+ clearTimeout(mockDelayTimer);
32
+ resolve(result);
33
+ };
34
+ if (useMockProgress && files[0]?.status === "progress") {
35
+ mockDelayTimer = setTimeout(() => {
36
+ mockTimer = setInterval(() => {
37
+ if (percent + 10 < 100) {
38
+ percent = Math.max(percent + 10, percent);
39
+ if (files[0] && percent !== files[0].percent) {
40
+ files.forEach((file) => {
41
+ file.percent = percent;
42
+ });
43
+ onProgress?.({
44
+ file: files[0],
45
+ currentFiles: files,
46
+ percent,
47
+ type: "mock",
48
+ XMLHttpRequest: xhr
49
+ });
50
+ }
51
+ } else {
52
+ clearInterval(mockTimer);
53
+ }
54
+ }, mockProgressDuration);
55
+ clearTimeout(mockDelayTimer);
56
+ }, mockProgressDuration);
57
+ }
58
+ let requestData = {};
59
+ const extraData = isFunction(data) ? data(files) : data;
60
+ Object.assign(requestData, extraData);
61
+ files.forEach((file, index) => {
62
+ const fileField = files.length > 1 ? `${name}[${index}]` : name;
63
+ requestData[fileField] = file.raw;
64
+ });
65
+ requestData[name] = files.length === 1 ? files[0].raw : files.map((file) => file.raw);
66
+ requestData.length = files.length;
67
+ if (formatRequest) requestData = formatRequest(requestData);
68
+ const formData = new FormData();
69
+ Object.keys(requestData).forEach((key) => {
70
+ const value = requestData[key];
71
+ if (Array.isArray(value)) {
72
+ value.forEach((item) => formData.append(key, item));
73
+ } else {
74
+ formData.append(key, value);
75
+ }
76
+ });
77
+ xhr.open(method, action, true);
78
+ Object.keys(headers).forEach((key) => {
79
+ xhr.setRequestHeader(key, headers[key]);
80
+ });
81
+ if (timeout > 0) xhr.timeout = timeout;
82
+ xhr.ontimeout = () => {
83
+ finish({ status: "fail", error: "timeout", response: {} });
84
+ };
85
+ xhr.onerror = () => {
86
+ finish({ status: "fail", error: "error", response: {} });
87
+ };
88
+ if (xhr.upload) {
89
+ xhr.upload.onprogress = (event) => {
90
+ let realPercent = 0;
91
+ if (event.total > 0) realPercent = Math.round(event.loaded / event.total * 100);
92
+ percent = Math.max(realPercent, percent);
93
+ if (files[0]?.percent !== percent) {
94
+ files.forEach((file) => {
95
+ file.percent = percent;
96
+ });
97
+ onProgress?.({
98
+ e: event,
99
+ file: files[0],
100
+ currentFiles: files,
101
+ percent,
102
+ type: "real",
103
+ XMLHttpRequest: xhr
104
+ });
105
+ }
106
+ };
107
+ }
108
+ xhr.onload = () => {
109
+ const isFail = xhr.status < 200 || xhr.status >= 300;
110
+ const text = xhr.responseText || xhr.response;
111
+ if (isFail) {
112
+ finish({ status: "fail", error: "fail", response: {} });
113
+ return;
114
+ }
115
+ let response = {};
116
+ try {
117
+ response = JSON.parse(text);
118
+ } catch {
119
+ response = text;
120
+ }
121
+ files.forEach((file) => {
122
+ file.percent = 100;
123
+ });
124
+ finish({ status: "success", response });
125
+ };
126
+ xhr.send(formData);
127
+ });
128
+ }
129
+ export {
130
+ calcUploadTimeout,
131
+ uploadByXhr
132
+ };
@@ -15,6 +15,7 @@ export interface QdUploadProps<F extends UploadFile = UploadFile, OR = unknown,
15
15
  modelValue?: string | string[];
16
16
  action?: string | ((bucketConfigKey?: string) => string);
17
17
  bucketConfigKey?: string;
18
+ timeout?: number;
18
19
  convStr?: boolean | string;
19
20
  valueMode?: 'ossId' | 'url';
20
21
  theme?: TdUploadProps['theme'] | 'btn-file-popup' | 'btn-image-popup';
@@ -305,6 +305,7 @@ declare const _default: {
305
305
  sameNameMessage: string;
306
306
  fileSizeExceedsLimit: string;
307
307
  fileUploadFailed: string;
308
+ fileUploadTimeout: string;
308
309
  viewFileText: string;
309
310
  };
310
311
  chat: {
@@ -47,6 +47,7 @@ const enUS = merge(enUS$1, {
47
47
  sameNameMessage: "Uploading files with the same name is not allowed",
48
48
  fileSizeExceedsLimit: "File size exceeded limit, automatically filtered",
49
49
  fileUploadFailed: "File upload failed",
50
+ fileUploadTimeout: "Upload timed out, please retry",
50
51
  viewFileText: "View file"
51
52
  },
52
53
  chat: {
@@ -305,6 +305,7 @@ declare const _default: {
305
305
  sameNameMessage: string;
306
306
  fileSizeExceedsLimit: string;
307
307
  fileUploadFailed: string;
308
+ fileUploadTimeout: string;
308
309
  viewFileText: string;
309
310
  };
310
311
  chat: {
@@ -44,6 +44,7 @@ const zhCN = merge(zhCN$1, {
44
44
  sameNameMessage: "不允许上传同名文件",
45
45
  fileSizeExceedsLimit: "文件大小超出限制,已自动过滤",
46
46
  fileUploadFailed: "文件上传失败",
47
+ fileUploadTimeout: "上传超时,请重试",
47
48
  viewFileText: "查看文件"
48
49
  },
49
50
  chat: {
@@ -305,6 +305,7 @@ declare const _default: {
305
305
  sameNameMessage: string;
306
306
  fileSizeExceedsLimit: string;
307
307
  fileUploadFailed: string;
308
+ fileUploadTimeout: string;
308
309
  viewFileText: string;
309
310
  };
310
311
  chat: {
@@ -44,6 +44,7 @@ const zhTW = merge(zhTW$1, {
44
44
  sameNameMessage: "不允許上傳同名文件",
45
45
  fileSizeExceedsLimit: "文件大小超出限制,已自動過濾",
46
46
  fileUploadFailed: "文件上傳失敗",
47
+ fileUploadTimeout: "上傳逾時,請重試",
47
48
  viewFileText: "查看文件"
48
49
  },
49
50
  chat: {
@@ -33,6 +33,11 @@ export declare function getTransformOffset(el?: HTMLElement | null, options?: {
33
33
  }): {
34
34
  x: number;
35
35
  y: number;
36
+ node: HTMLElement;
37
+ } | {
38
+ x: number;
39
+ y: number;
40
+ node: null | undefined;
36
41
  };
37
42
  export declare const IMAGE_REGEXP: RegExp;
38
43
  export declare function isImage(value: string): boolean;
@@ -13,11 +13,11 @@ function getTransformOffset(el, options) {
13
13
  while (node) {
14
14
  if (getComputedStyle(node).transform !== "none" && !skip?.(node)) {
15
15
  const { x, y } = node.getBoundingClientRect();
16
- return { x, y };
16
+ return { x, y, node };
17
17
  }
18
18
  node = node.parentElement;
19
19
  }
20
- return { x: 0, y: 0 };
20
+ return { x: 0, y: 0, node };
21
21
  }
22
22
  const IMAGE_REGEXP = /(.png|.jpg|.jpeg|.jpe|.webp|.avif|.heic|.heif|.svg|.gif|.bmp)/i;
23
23
  function isImage(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qidian-vue-ui",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "基于 TDesign 的 QiDian Vue UI 组件库",
5
5
  "author": "qidian",
6
6
  "license": "MIT",