diff --git a/cache.dockerfile b/cache.dockerfile
deleted file mode 100644
index dce57c1ae..000000000
--- a/cache.dockerfile
+++ /dev/null
@@ -1,15 +0,0 @@
-# 选择一个 Base 镜像
-FROM node:12
-
-# 设置工作目录
-WORKDIR /space
-
-# 将 by 中的文件列表 COPY 过来
-COPY . .
-
-# 根据 COPY 过来的文件进行依赖的安装
-RUN npm install
-
-# 设置好需要的环境变量
-ENV NODE_PATH=/space/node_modules
-
diff --git a/package.json b/package.json
index 4c82e0f4b..6ddf6ce2a 100644
--- a/package.json
+++ b/package.json
@@ -90,7 +90,7 @@
"@vueuse/core": "10.7.0",
"dayjs": "^1.10.7",
"lodash": "^4.17.21",
- "tdesign-icons-vue-next": "^0.2.6",
+ "tdesign-icons-vue-next": "^0.3.3",
"validator": "^13.5.1"
},
"peerDependencies": {
@@ -128,7 +128,6 @@
"@vue/babel-plugin-jsx": "1.1.1",
"@vue/eslint-config-typescript": "^9.1.0",
"@vue/runtime-core": "^3.2.41",
- "@vue/server-renderer": "3.3.8",
"@vue/shared": "3.3.8",
"@vue/test-utils": "2.4.1",
"autoprefixer": "^10.2.4",
@@ -172,7 +171,7 @@
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-vue": "^6.0.0",
"rollup-pluginutils": "^2.8.2",
- "tdesign-icons-view": "^0.2.2",
+ "tdesign-icons-view": "^0.3.2",
"tdesign-publish-cli": "^0.0.10",
"tdesign-site-components": "^0.15.2",
"tslib": "^2.3.1",
diff --git a/site/web/app.vue b/site/web/app.vue
index 67fa25087..e207db1d9 100644
--- a/site/web/app.vue
+++ b/site/web/app.vue
@@ -17,7 +17,8 @@ import { sortDocs } from './utils';
import { defineComponent } from 'vue';
import packageJson from '../../package.json';
-const registryUrl = 'https://mirrors.tencent.com/npm/tdesign-mobile-vue';
+const registryUrl =
+ 'https://service-edbzjd6y-1257786608.hk.apigw.tencentcs.com/release/npm/versions/tdesign-mobile-vue';
const currentVersion = packageJson.version.replace(/\./g, '_');
const { docs, enDocs } = JSON.parse(JSON.stringify(siteConfig).replace(/component:.+/g, ''));
@@ -33,8 +34,8 @@ function watchHtmlMode(callback = () => {}) {
const observerCallback = (mutationsList) => {
for (const mutation of mutationsList) {
- if (mutation.attributeName === "theme-mode") {
- const themeMode = mutation.target.getAttribute("theme-mode") || 'light';
+ if (mutation.attributeName === 'theme-mode') {
+ const themeMode = mutation.target.getAttribute('theme-mode') || 'light';
if (themeMode) callback(themeMode);
}
}
@@ -46,7 +47,7 @@ function watchHtmlMode(callback = () => {}) {
return observer;
}
-function changeIframeMode(mode){
+function changeIframeMode(mode) {
const iframe = document.querySelector('iframe');
if (iframe?.contentWindow) {
iframe.contentWindow.document.documentElement.setAttribute('theme-mode', mode);
@@ -88,7 +89,7 @@ export default defineComponent({
};
this.initHistoryVersions();
- watchHtmlMode(changeIframeMode)
+ watchHtmlMode(changeIframeMode);
},
watch: {
diff --git a/src/collapse/__test__/__snapshots__/demo.test.jsx.snap b/src/collapse/__test__/__snapshots__/demo.test.jsx.snap
index 6f4e17106..21215929d 100644
--- a/src/collapse/__test__/__snapshots__/demo.test.jsx.snap
+++ b/src/collapse/__test__/__snapshots__/demo.test.jsx.snap
@@ -70,15 +70,24 @@ exports[`Collapse > Collapse accordionVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse accordionVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse accordionVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse accordionVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
@@ -380,15 +416,24 @@ exports[`Collapse > Collapse actionVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
@@ -458,15 +503,24 @@ exports[`Collapse > Collapse baseVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
@@ -537,15 +591,24 @@ exports[`Collapse > Collapse cardVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse cardVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse cardVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse cardVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
@@ -857,15 +947,24 @@ exports[`Collapse > Collapse mobileVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
@@ -952,11 +1051,20 @@ exports[`Collapse > Collapse mobileVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容
-
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容
+
+
+
@@ -1049,15 +1157,24 @@ exports[`Collapse > Collapse mobileVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
@@ -1151,15 +1268,24 @@ exports[`Collapse > Collapse mobileVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse mobileVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse mobileVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse mobileVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
@@ -1477,15 +1630,24 @@ exports[`Collapse > Collapse mobileVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse mobileVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse mobileVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
Collapse mobileVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容可自定义内容
+
+
+
-
@@ -1767,11 +1956,20 @@ exports[`Collapse > Collapse placementVue demo works fine 1`] = `
-
- 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容
-
+
+
+
+ 此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容此处可自定义内容
+
+
+
diff --git a/src/collapse/collapse-panel.tsx b/src/collapse/collapse-panel.tsx
index 9bfaf1a97..b22464d9d 100644
--- a/src/collapse/collapse-panel.tsx
+++ b/src/collapse/collapse-panel.tsx
@@ -1,4 +1,4 @@
-import { ref, computed, nextTick, watch, onMounted, inject, defineComponent, getCurrentInstance } from 'vue';
+import { computed, onMounted, inject, defineComponent, getCurrentInstance } from 'vue';
import { ChevronDownIcon, ChevronUpIcon } from 'tdesign-icons-vue-next';
import TCell from '../cell';
import props from './collapse-panel-props';
@@ -45,45 +45,6 @@ export default defineComponent({
updatePanelValue({ e });
};
- // 设置折叠/展开高度过渡
- const bodyRef = ref();
- const wrapRef = ref();
- const headRef = ref();
- const wrapperHeight = ref('');
- let isToggle = true;
- const updatePanelState = () => {
- nextTick(() => {
- if (!wrapRef.value) {
- return;
- }
- const { height: headHeight } = headRef.value.getBoundingClientRect();
- if (!isActive.value) {
- isToggle = false;
- wrapperHeight.value = `${headHeight}px`;
- return;
- }
- if (isToggle) {
- isToggle = false;
- wrapperHeight.value = 'auto';
- return;
- }
- setContentWrapperHeight();
- });
- };
-
- watch(
- isActive,
- () => {
- if (wrapperHeight.value === 'auto') {
- setContentWrapperHeight();
- }
- nextTick(() => updatePanelState());
- },
- {
- immediate: true,
- },
- );
-
onMounted(() => {
if (parent?.defaultExpandAll) {
updatePanelValue();
@@ -110,24 +71,7 @@ export default defineComponent({
return null;
}
- return (
-
- {panelContent}
-
- );
- };
-
- const setContentWrapperHeight = () => {
- const { height: headHeight } = headRef.value.getBoundingClientRect();
- const { height: bodyHeight } = bodyRef.value.getBoundingClientRect();
- const height = headHeight + bodyHeight;
- wrapperHeight.value = `${height}px`;
- };
-
- const onTransitionEnd = () => {
- if (isActive.value) {
- wrapperHeight.value = 'auto';
- }
+ return {panelContent}
;
};
return () => {
@@ -136,13 +80,8 @@ export default defineComponent({
const leftIcon = renderTNodeJSX('headerLeftIcon');
return (
-
-
+
+
- {renderPanelContent()}
+
+
{renderPanelContent()}
+
);
};
diff --git a/src/count-down/count-down.tsx b/src/count-down/count-down.tsx
index 09171d0d6..a55d8f01b 100644
--- a/src/count-down/count-down.tsx
+++ b/src/count-down/count-down.tsx
@@ -1,4 +1,4 @@
-import { computed, defineComponent } from 'vue';
+import { computed, defineComponent, onBeforeUnmount, onMounted, ref } from 'vue';
import config from '../config';
import CountDownProps from './props';
import { useCountDown } from '../shared/useCountDown';
@@ -18,8 +18,17 @@ export default defineComponent({
`${countDownClass.value}--${props.theme}`,
`${countDownClass.value}--${props.size}`,
]);
-
- const { showTimes } = useCountDown(props);
+ const visibility = ref(true);
+ const visibilitychangeListener = () => {
+ visibility.value = !document.hidden;
+ };
+ onMounted(() => {
+ document.addEventListener('visibilitychange', visibilitychangeListener, false);
+ });
+ onBeforeUnmount(() => {
+ document.removeEventListener('visibilitychange', visibilitychangeListener, false);
+ });
+ const { showTimes } = useCountDown(props, visibility);
return () => {
const renderContent = () => {
const content = renderTNodeJSX('content');
diff --git a/src/form/form-item.tsx b/src/form/form-item.tsx
index 8bb926257..611135395 100644
--- a/src/form/form-item.tsx
+++ b/src/form/form-item.tsx
@@ -5,6 +5,7 @@ import {
nextTick,
onBeforeUnmount,
onMounted,
+ provide,
reactive,
ref,
toRefs,
@@ -37,6 +38,7 @@ import {
ErrorListType,
FormInjectionKey,
FormItemContext,
+ FormItemInjectionKey,
SuccessListType,
ValidateStatus,
} from './const';
@@ -312,6 +314,14 @@ export default defineComponent({
},
);
+ const handleBlur = async () => {
+ await validateHandler('blur');
+ };
+
+ provide(FormItemInjectionKey, {
+ handleBlur,
+ });
+
return () => {
const renderRightIconContent = () => {
if (!props.arrow) {
diff --git a/src/hooks/useLockScroll.ts b/src/hooks/useLockScroll.ts
index f38734798..85a6dea86 100644
--- a/src/hooks/useLockScroll.ts
+++ b/src/hooks/useLockScroll.ts
@@ -3,7 +3,7 @@ import { useTouch } from '../_util/useTouch';
import getScrollParent from '../_util/getScrollParent';
import { supportsPassive } from '../_util/supportsPassive';
-let totalLockCount = 0;
+const totalLockCount = new Map
();
let mounted: boolean = null;
// 移植自vant:https://github.com/youzan/vant/blob/HEAD/src/composables/use-lock-scroll.ts
@@ -37,21 +37,22 @@ export function useLockScroll(rootRef: Ref, shouldLock:
document.addEventListener('touchstart', touch.start);
document.addEventListener('touchmove', onTouchMove, supportsPassive.value ? { passive: false } : false);
- if (!totalLockCount) {
+ if (!totalLockCount.get(BODY_LOCK_CLASS)) {
document.body.classList.add(BODY_LOCK_CLASS);
}
- totalLockCount += 1;
+ totalLockCount.set(BODY_LOCK_CLASS, (totalLockCount.get(BODY_LOCK_CLASS) ?? 0) + 1);
};
const unlock = () => {
- if (totalLockCount) {
+ const sum = Array.from(totalLockCount.values()).reduce((acc, val) => acc + val, 0);
+ if (sum) {
document.removeEventListener('touchstart', touch.start);
document.removeEventListener('touchmove', onTouchMove);
- totalLockCount -= 1;
+ totalLockCount.set(BODY_LOCK_CLASS, Math.max((totalLockCount.get(BODY_LOCK_CLASS) ?? 0) - 1, 0));
- if (!totalLockCount) {
+ if (!totalLockCount.get(BODY_LOCK_CLASS)) {
document.body.classList.remove(BODY_LOCK_CLASS);
}
}
diff --git a/src/icon/icon.en-US.md b/src/icon/icon.en-US.md
index f357aaf77..68fb2f1a5 100644
--- a/src/icon/icon.en-US.md
+++ b/src/icon/icon.en-US.md
@@ -4,7 +4,6 @@
Icons are published and managed as a separate npm package. If you want to use it directly in your project, please install `tdesign-icons-vue-next`. At the same time, `tdesign-vue-next` also includes icons and supports direct use through `t-icon`.
-
### Import on-demand
SVG icons can be imported on demand. When using the Icon component in component development, SVG icons are imported on demand.
@@ -37,10 +36,8 @@ New iconfont icons can be added by passing in the URL.
The component will import default iconfont icons. If you want to disable the loading of default iconfont icons, set `loadDefaultIcons` to `false`.
-
{{ iconfont-enhanced }}
-
### FAQ
#### How to get all the names of icons?
@@ -50,18 +47,21 @@ You can get all the name of icon by import manifest from the bundle `import { ma
#### the usage of full import needs network. What if my project is in a no-network scenario?
if your project is in a no-network scenario, please use on-demand loading of icons. For example,` ` should be changed to ` `
+
### All Icons
- Most icons were added to the icon library after version 0.2.0. If you find that the icon cannot be displayed normally after being imported, please check the version of tdesign-icons-vue-next you have installed
+ Most icons were added to the icon library after version 0.3.0. If you find that the icon cannot be displayed normally after being imported, please check the version of tdesign-icons-vue-next you have installed. Supports Chinese and English search. If you think other keyword prompts can be added, feel free to submit a Pull Request to the Icon Repository to help us complete it together.
+
+
-
## API
+
### IconSVG Props
name | type | default | description | required
@@ -79,6 +79,7 @@ name | params | description
-- | -- | --
click | `(context: { e: MouseEvent })` | \-
+
### Iconfont Props
name | type | default | description | required
diff --git a/src/icon/icon.md b/src/icon/icon.md
index 28487f186..0f344f4e8 100644
--- a/src/icon/icon.md
+++ b/src/icon/icon.md
@@ -3,7 +3,7 @@
### 安装独立 Icon 包
图标相对其他基础组件较为独立,所以作为一个独立的 `npm` 包做发布管理。如果项目中直接使用,请安装 `tdesign-icons-vue-next`。 同时 `tdesign-mobile-vue` 也内置了 `icon`, 支持直接通过 `t-icon` 来使用。
-图标库中共包含超过 **25** 类,**1200+** 个图标,推荐您按需引用图标,减少产物的体积。
+图标库中共包含超过 **25** 类,**2100+** 个图标,推荐您按需引用图标,减少产物的体积。
### 按需引入使用图标
@@ -35,7 +35,6 @@ TDesign 支持通过使用 Iconfont 图标,使用时需要单独引入 Iconfon
{{ enhanced }}
-
### iconfont 高级用法
可以传入 url 加入新的 iconfont 图标。
@@ -46,18 +45,17 @@ TDesign 支持通过使用 Iconfont 图标,使用时需要单独引入 Iconfon
{{ iconfont-enhanced }}
-
### FAQ
#### 如何获取全部图标的名称列表?
可以通过`import { manifest } from 'tdesign-icons-vue-next'` 获取全部图标的名称列表。
-### t-icon、iconfont和icon使用时都会发起网络请求,我的项目是无网络场景,如何使用?
+### t-icon、iconfont 和 icon 使用时都会发起网络请求,我的项目是无网络场景,如何使用?
-首先明确`t-icon`、`iconfont`和`icon`三者的关系。如上面的示例所示,`iconfont`和`icon`都是从icon独立包中引入,而`t-icon`只是为了方便用户习惯,对`icon`的一个简单封装。
+首先明确`t-icon`、`iconfont`和`icon`三者的关系。如上面的示例所示,`iconfont`和`icon`都是从 icon 独立包中引入,而`t-icon`只是为了方便用户习惯,对`icon`的一个简单封装。
-`iconfont`需要加载图标的字体资源,而`icon`需要加载图标的svgsprite资源,这些资源都是相对来说比较大的,所以没有直接放在包里(当然不排除未来会做改动),所以会发起网络请求。
+`iconfont`需要加载图标的字体资源,而`icon`需要加载图标的 svgsprite 资源,这些资源都是相对来说比较大的,所以没有直接放在包里(当然不排除未来会做改动),所以会发起网络请求。
所以如果你的项目是无网络场景,请使用按需加载的图标,如`
`请改为`
`。
@@ -67,16 +65,17 @@ TDesign 支持通过使用 Iconfont 图标,使用时需要单独引入 Iconfon
- 大部分图标在 0.2.0 版本后的图标库新增,如果发现图标引入后无法正常展示,请检查安装的图标库`tdesign-icons-vue-next`的版本。
+
大部分图标在 0.3.0 版本后的图标库新增,如果发现图标引入后无法正常展示,请检查安装的图标库`tdesign-icons-vue-next`的版本。支持中文英文搜索,如果觉得可以再增加其他关键词提示,欢迎到 图标仓库 提交PR,帮我们一起补充。
+
-
## API
+
### IconSVG Props
-名称 | 类型 | 默认值 | 说明 | 必传
+名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
loadDefaultIcons | Boolean | true | 是否加载组件库内置图标 | N
name | String | - | 必需。图标名称 | Y
@@ -91,9 +90,10 @@ onClick | Function | | TS 类型:`(context: { e: MouseEvent }) => void`
-- | -- | --
click | `(context: { e: MouseEvent })` | 点击时触发
+
### Iconfont Props
-名称 | 类型 | 默认值 | 说明 | 必传
+名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
loadDefaultIcons | Boolean | true | 是否加载组件库内置图标 | N
name | String | - | 必需。图标名称 | Y
diff --git a/src/image-viewer/image-viewer-interface.ts b/src/image-viewer/image-viewer-interface.ts
deleted file mode 100644
index a4969130b..000000000
--- a/src/image-viewer/image-viewer-interface.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export interface ImageInfo {
- url: string;
- align: 'start' | 'center' | 'end';
-}
diff --git a/src/image-viewer/image-viewer.en-US.md b/src/image-viewer/image-viewer.en-US.md
index 62ed1c1bc..981a03829 100644
--- a/src/image-viewer/image-viewer.en-US.md
+++ b/src/image-viewer/image-viewer.en-US.md
@@ -8,27 +8,27 @@ name | type | default | description | required
-- | -- | -- | -- | --
closeBtn | Boolean / Slot / Function | true | Typescript:`boolean \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
deleteBtn | Boolean / Slot / Function | false | Typescript:`boolean \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
-images | Array | [] | Typescript:`Array` | N
+images | Array | [] | Typescript:`Array` `interface ImageInfo { url: string; align: 'start' \| 'center' \| 'end' }`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/image-viewer/type.ts) | N
index | Number | - | `v-model:index` is supported | N
defaultIndex | Number | - | uncontrolled property | N
maxZoom | Number | 3 | Typescript:`Number` | N
showIndex | Boolean | false | \- | N
visible | Boolean | false | hide or show image viewer。`v-model` and `v-model:visible` is supported | N
defaultVisible | Boolean | false | hide or show image viewer。uncontrolled property | N
-onClose | Function | | Typescript:`(context: { trigger: 'overlay' \| 'close-btn', visible: Boolean, index: Number }) => void` | N
-onDelete | Function | | Typescript:`(index: Number) => void` | N
+onClose | Function | | Typescript:`(context: { trigger: 'overlay' \| 'close-btn', visible: boolean, index: number }) => void` | N
+onDelete | Function | | Typescript:`(index: number) => void` | N
onIndexChange | Function | | Typescript:`(index: number, context: { trigger: 'prev' \| 'next' }) => void` | N
### ImageViewer Events
name | params | description
-- | -- | --
-close | `(context: { trigger: 'overlay' \| 'close-btn', visible: Boolean, index: Number })` | \-
-delete | `(index: Number)` | \-
+close | `(context: { trigger: 'overlay' \| 'close-btn', visible: boolean, index: number })` | \-
+delete | `(index: number)` | \-
index-change | `(index: number, context: { trigger: 'prev' \| 'next' })` | \-
-
### CSS Variables
+
The component provides the following CSS variables, which can be used to customize styles.
Name | Default Value | Description
-- | -- | --
@@ -38,4 +38,4 @@ Name | Default Value | Description
--td-image-viewer-nav-bg-color | @font-gray-3 | -
--td-image-viewer-nav-color | @font-white-1 | -
--td-image-viewer-nav-height | 48px | -
---td-image-viewer-nav-index-font-size | @font-size-base | -
+--td-image-viewer-nav-index-font-size | @font-size-base | -
\ No newline at end of file
diff --git a/src/image-viewer/image-viewer.md b/src/image-viewer/image-viewer.md
index 138155ee0..c381b319b 100644
--- a/src/image-viewer/image-viewer.md
+++ b/src/image-viewer/image-viewer.md
@@ -8,27 +8,27 @@
-- | -- | -- | -- | --
closeBtn | Boolean / Slot / Function | true | 是否展示关闭按钮,值为 `true` 显示默认关闭按钮;值为 `false` 则不显示关闭按钮;也可以完全自定义关闭按钮。TS 类型:`boolean \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
deleteBtn | Boolean / Slot / Function | false | 是否显示删除操作,前提需要开启页码。TS 类型:`boolean \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
-images | Array | [] | 图片数组。TS 类型:`Array` | N
+images | Array | [] | 图片数组。TS 类型:`Array` `interface ImageInfo { url: string; align: 'start' \| 'center' \| 'end' }`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/image-viewer/type.ts) | N
index | Number | - | 当前预览图片所在的下标。支持语法糖 `v-model:index` | N
defaultIndex | Number | - | 当前预览图片所在的下标。非受控属性 | N
maxZoom | Number | 3 | 【开发中】最大放大比例。TS 类型:`Number` | N
showIndex | Boolean | false | 是否显示页码 | N
visible | Boolean | false | 隐藏/显示预览。支持语法糖 `v-model` 或 `v-model:visible` | N
defaultVisible | Boolean | false | 隐藏/显示预览。非受控属性 | N
-onClose | Function | | TS 类型:`(context: { trigger: 'overlay' \| 'close-btn', visible: Boolean, index: Number }) => void` 关闭时触发 | N
-onDelete | Function | | TS 类型:`(index: Number) => void` 点击删除操作按钮时触发 | N
+onClose | Function | | TS 类型:`(context: { trigger: 'overlay' \| 'close-btn', visible: boolean, index: number }) => void` 关闭时触发 | N
+onDelete | Function | | TS 类型:`(index: number) => void` 点击删除操作按钮时触发 | N
onIndexChange | Function | | TS 类型:`(index: number, context: { trigger: 'prev' \| 'next' }) => void` 预览图片切换时触发,`context.prev` 切换到上一张图片,`context.next` 切换到下一张图片 | N
### ImageViewer Events
名称 | 参数 | 描述
-- | -- | --
-close | `(context: { trigger: 'overlay' \| 'close-btn', visible: Boolean, index: Number })` | 关闭时触发
-delete | `(index: Number)` | 点击删除操作按钮时触发
+close | `(context: { trigger: 'overlay' \| 'close-btn', visible: boolean, index: number })` | 关闭时触发
+delete | `(index: number)` | 点击删除操作按钮时触发
index-change | `(index: number, context: { trigger: 'prev' \| 'next' })` | 预览图片切换时触发,`context.prev` 切换到上一张图片,`context.next` 切换到下一张图片
-
### CSS Variables
+
组件提供了下列 CSS 变量,可用于自定义样式。
名称 | 默认值 | 描述
-- | -- | --
@@ -38,4 +38,4 @@ index-change | `(index: number, context: { trigger: 'prev' \| 'next' })` | 预
--td-image-viewer-nav-bg-color | @font-gray-3 | -
--td-image-viewer-nav-color | @font-white-1 | -
--td-image-viewer-nav-height | 48px | -
---td-image-viewer-nav-index-font-size | @font-size-base | -
+--td-image-viewer-nav-index-font-size | @font-size-base | -
\ No newline at end of file
diff --git a/src/image-viewer/image-viewer.tsx b/src/image-viewer/image-viewer.tsx
index 5758dd8d8..ee09fed85 100644
--- a/src/image-viewer/image-viewer.tsx
+++ b/src/image-viewer/image-viewer.tsx
@@ -8,10 +8,8 @@ import { useTNodeJSX } from '../hooks/tnode';
import { usePrefixClass } from '../hooks/useClass';
// inner components
-import { Swiper as TSwiper, SwiperItem as TSwiperItem } from '../swiper';
-import TImage from '../image';
-import { TdImageViewerProps } from './type';
-import { ImageInfo } from './image-viewer-interface';
+import { SwiperChangeSource, Swiper as TSwiper, SwiperItem as TSwiperItem } from '../swiper';
+import { TdImageViewerProps, ImageInfo } from './type';
const { prefix } = config;
@@ -19,12 +17,6 @@ const TAP_TIME = 300;
export default defineComponent({
name: `${prefix}-image-viewer`,
- components: {
- Transition,
- TSwiper,
- TSwiperItem,
- TImage,
- },
props,
emits: ['close', 'index-change', 'update:visible', 'update:modelValue', 'update:index', 'delete'],
setup(props, { emit }) {
@@ -85,7 +77,7 @@ export default defineComponent({
const disabled = ref(false);
const rootRef = ref();
- const imagesSize = reactive({});
+ const imagesSize: Record = reactive({});
const swiperRootRef = ref();
const swiperItemRefs = ref([]);
const gestureRef = ref();
@@ -133,9 +125,10 @@ export default defineComponent({
imageInfoList.value[nextIndex].preload = true;
};
- const onSwiperChange = (index: number, context: any) => {
+ const onSwiperChange = (index: number, context: { source: SwiperChangeSource }) => {
if (currentIndex.value !== index) {
- setIndex(index, { context });
+ const trigger = currentIndex.value < index ? 'next' : 'prev';
+ setIndex(index, { trigger });
setScale(1);
setImagePreload(index);
}
@@ -393,7 +386,7 @@ export default defineComponent({
});
return () => (
-
+
{visible.value && (
handleClose(e, 'overlay')} />
@@ -455,7 +448,7 @@ export default defineComponent({
)}
-
+
);
},
});
diff --git a/src/image-viewer/type.ts b/src/image-viewer/type.ts
index 3d50de165..50e20dda5 100644
--- a/src/image-viewer/type.ts
+++ b/src/image-viewer/type.ts
@@ -21,7 +21,7 @@ export interface TdImageViewerProps {
* 图片数组
* @default []
*/
- images?: Array;
+ images?: Array;
/**
* 当前预览图片所在的下标
*/
@@ -58,13 +58,18 @@ export interface TdImageViewerProps {
/**
* 关闭时触发
*/
- onClose?: (context: { trigger: 'overlay' | 'close-btn'; visible: Boolean; index: Number }) => void;
+ onClose?: (context: { trigger: 'overlay' | 'close-btn'; visible: boolean; index: number }) => void;
/**
* 点击删除操作按钮时触发
*/
- onDelete?: (index: Number) => void;
+ onDelete?: (index: number) => void;
/**
* 预览图片切换时触发,`context.prev` 切换到上一张图片,`context.next` 切换到下一张图片
*/
onIndexChange?: (index: number, context: { trigger: 'prev' | 'next' }) => void;
}
+
+export interface ImageInfo {
+ url: string;
+ align: 'start' | 'center' | 'end';
+}
diff --git a/src/input/input.tsx b/src/input/input.tsx
index f6c47f05a..ff15bd50c 100644
--- a/src/input/input.tsx
+++ b/src/input/input.tsx
@@ -1,4 +1,4 @@
-import { PropType, ref, computed, defineComponent, nextTick, watch } from 'vue';
+import { PropType, ref, computed, defineComponent, nextTick, watch, inject } from 'vue';
import {
BrowseIcon as TBrowseIcon,
BrowseOffIcon as TBrowseOffIcon,
@@ -9,6 +9,7 @@ import config from '../config';
import InputProps from './props';
import { InputValue, TdInputProps } from './type';
import { getCharacterLength, useDefault, extendAPI } from '../shared';
+import { FormItemInjectionKey } from '../form/const';
import { useFormDisabled } from '../form/hooks';
import { usePrefixClass } from '../hooks/useClass';
import { useTNodeJSX } from '../hooks/tnode';
@@ -45,6 +46,7 @@ export default defineComponent({
const status = props.status || 'default';
const renderType = ref(props.type);
const focused = ref(false);
+ const formItem = inject(FormItemInjectionKey, undefined);
const inputClasses = computed(() => [
`${inputClass.value}__control`,
@@ -133,18 +135,19 @@ export default defineComponent({
const handleBlur = (e: FocusEvent) => {
focused.value = false;
-
// 失焦时处理 format
if (isFunction(props.format)) {
innerValue.value = props.format(innerValue.value);
nextTick(() => {
setInputValue(innerValue.value);
props.onBlur?.(innerValue.value, { e });
+ formItem?.handleBlur();
});
return;
}
props.onBlur?.(innerValue.value, { e });
+ formItem?.handleBlur();
};
const handleCompositionend = (e: CompositionEvent) => {
diff --git a/src/shared/useCountDown/index.ts b/src/shared/useCountDown/index.ts
index 7d28f71d9..03a19441b 100644
--- a/src/shared/useCountDown/index.ts
+++ b/src/shared/useCountDown/index.ts
@@ -1,10 +1,10 @@
-import { ref, reactive } from 'vue';
+import { ref, reactive, Ref, watch } from 'vue';
import { useRafFn } from '@vueuse/core';
import { TdUseCountDownProps, TdUseCountDown } from './type';
import { getRemainTimes, getShowTimes, getScreenFps } from './utils';
import { isBrowser } from '../util';
-export function useCountDown(props: TdUseCountDownProps): TdUseCountDown {
+export function useCountDown(props: TdUseCountDownProps, visibility?: Ref): TdUseCountDown {
const {
time = 0,
autoStart,
@@ -18,27 +18,37 @@ export function useCountDown(props: TdUseCountDownProps): TdUseCountDown {
const fps = ref();
const count = ref(Number(time));
const showTimes = reactive(getShowTimes(getRemainTimes(time), format, millisecond, splitWithUnit));
+ let hiddenTime = 0;
- // raf
- const { pause, resume } = useRafFn(
- async () => {
- if (!isBrowser) return;
- if (!fps.value) {
- const res = await getScreenFps?.();
- fps.value = res || 60;
- }
- count.value = parseInt(`${Number(count.value) - 1000 / fps.value}`, 10);
- if (count.value <= 0) {
- pause?.();
- count.value = 0;
+ visibility &&
+ watch(visibility, (val) => {
+ if (val) {
+ count.value -= Date.now() - hiddenTime;
+ rafFn();
+ } else {
+ hiddenTime = Date.now();
}
- const times = getRemainTimes(count.value);
- onChange?.(times);
- count.value === 0 && onFinish?.();
- getShowTimes(times, format, millisecond, splitWithUnit)?.forEach?.((i, idx) => (showTimes[idx].value = i?.value));
- },
- { immediate: autoStart },
- );
+ });
+
+ const rafFn = async () => {
+ if (!isBrowser) return;
+ if (!fps.value) {
+ const res = await getScreenFps?.();
+ fps.value = res || 60;
+ }
+ count.value = parseInt(`${Number(count.value) - 1000 / fps.value}`, 10);
+ if (count.value <= 0) {
+ pause?.();
+ count.value = 0;
+ }
+ const times = getRemainTimes(count.value);
+ onChange?.(times);
+ count.value === 0 && onFinish?.();
+ getShowTimes(times, format, millisecond, splitWithUnit)?.forEach?.((i, idx) => (showTimes[idx].value = i?.value));
+ };
+
+ // raf
+ const { pause, resume } = useRafFn(rafFn, { immediate: autoStart });
/**
* return
diff --git a/src/tabs/tabs.tsx b/src/tabs/tabs.tsx
index 9a7e249ee..5fdae8e57 100644
--- a/src/tabs/tabs.tsx
+++ b/src/tabs/tabs.tsx
@@ -119,6 +119,19 @@ export default defineComponent({
lineStyle.value = style;
}
+ if (navScroll.value) {
+ const tab = navScroll.value.querySelector(`.${activeClass}`);
+ if (!tab) return;
+ const tabLeft = tab?.offsetLeft;
+ const tabWidth = tab?.offsetWidth;
+ const navScrollScrollLeft = navScroll.value.scrollLeft;
+ const navScrollWidth = navScroll.value.offsetWidth;
+ if (tabLeft + tabWidth - navScrollScrollLeft > navScrollWidth) {
+ navScroll.value.scrollTo({ left: tabLeft + tabWidth - navScrollWidth, behavior: 'smooth' });
+ } else if (tabLeft < navScrollScrollLeft) {
+ navScroll.value.scrollTo({ left: tabLeft, behavior: 'smooth' });
+ }
+ }
};
onMounted(() => {
diff --git a/src/textarea/textarea.tsx b/src/textarea/textarea.tsx
index 3e2c980f3..53188ffcf 100644
--- a/src/textarea/textarea.tsx
+++ b/src/textarea/textarea.tsx
@@ -1,9 +1,10 @@
-import { computed, ref, onMounted, defineComponent, toRefs, nextTick, watch } from 'vue';
+import { computed, ref, onMounted, defineComponent, toRefs, nextTick, watch, inject } from 'vue';
import { getCharacterLength, useVModel } from '../shared';
import config from '../config';
import props from './props';
import { TextareaValue } from './type';
import calcTextareaHeight from '../_common/js/utils/calcTextareaHeight';
+import { FormItemInjectionKey } from '../form/const';
import { useFormDisabled } from '../form/hooks';
import { usePrefixClass } from '../hooks/useClass';
import { useTNodeJSX } from '../hooks/tnode';
@@ -16,6 +17,7 @@ export default defineComponent({
setup(props, context) {
const renderTNodeJSX = useTNodeJSX();
const isDisabled = useFormDisabled();
+ const formItem = inject(FormItemInjectionKey, undefined);
const textareaClass = usePrefixClass('textarea');
@@ -107,6 +109,7 @@ export default defineComponent({
props.onFocus?.(innerValue.value, { e });
};
const handleBlur = (e: FocusEvent) => {
+ formItem?.handleBlur();
props.onBlur?.(innerValue.value, { e });
};
diff --git a/src/upload/props.ts b/src/upload/props.ts
index 6a6178ae2..01b1abd84 100644
--- a/src/upload/props.ts
+++ b/src/upload/props.ts
@@ -18,7 +18,7 @@ export default {
type: String,
default: '',
},
- /** 添加按钮内容。值为空,使用默认图标渲染;值为 slot 则表示使用插槽渲染;其他值无效。 */
+ /** 添加按钮内容。值为空,使用默认图标渲染;值为 slot 则表示使用插槽渲染;其他值无效 */
addContent: {
type: [String, Function] as PropType,
},
@@ -37,12 +37,19 @@ export default {
beforeUpload: {
type: Function as PropType,
},
+ /** 图片选取模式,可选值为 camera (直接调起摄像头) */
+ capture: {
+ type: String,
+ },
/** 上传请求所需的额外字段,默认字段有 `file`,表示文件信息。可以添加额外的文件名字段,如:`{file_name: "custom-file-name.txt"}`。`autoUpload=true` 时有效。也可以使用 `formatRequest` 完全自定义上传请求的字段 */
data: {
type: Object as PropType,
},
- /** 是否禁用 */
- disabled: Boolean,
+ /** 是否禁用组件 */
+ disabled: {
+ type: Boolean,
+ default: undefined,
+ },
/** 用于完全自定义文件列表界面内容(UI),单文件和多文件均有效 */
fileListDisplay: {
type: Function as PropType,
@@ -61,7 +68,7 @@ export default {
format: {
type: Function as PropType,
},
- /** 用于新增或修改文件上传请求参数。`action` 存在时有效。一个请求上传一个文件时,默认请求字段有 `file`; 一个请求上传多个文件时,默认字段有 `file[0]/file[1]/file[2]/.../length`,其中 `length` 表示本次上传的文件数量。 ⚠️非常注意,此处的 `file[0]/file[1]` 仅仅是一个字段名,并非表示 `file` 是一个数组,接口获取字段时注意区分。 可以使用 `name` 定义 `file` 字段的别名,也可以使用 `formatRequest` 自定义任意字段 */
+ /** 用于新增或修改文件上传请求 参数。`action` 存在时有效。一个请求上传一个文件时,默认请求字段有 `file`。 一个请求上传多个文件时,默认字段有 `file[0]/file[1]/file[2]/.../length`,其中 `length` 表示本次上传的文件数量。 ⚠️非常注意,此处的 `file[0]/file[1]` 仅仅是一个字段名,并非表示 `file` 是一个数组,接口获取字段时注意区分。 可以使用 `name` 定义 `file` 字段的别名。 也可以使用 `formatRequest` 自定义任意字段,如添加一个字段 `fileList` ,存储文件数组 */
formatRequest: {
type: Function as PropType,
},
@@ -104,6 +111,11 @@ export default {
type: String,
default: 'file',
},
+ /** 是否支持图片预览,文件没有预览 */
+ preview: {
+ type: Boolean,
+ default: true,
+ },
/** 自定义上传方法。返回值 `status` 表示上传成功或失败;`error` 或 `response.error` 表示上传失败的原因; `response` 表示请求上传成功后的返回数据,`response.url` 表示上传成功后的图片/文件地址,`response.files` 表示一个请求上传多个文件/图片后的返回值。 示例一:`{ status: 'fail', error: '上传失败', response }`。 示例二:`{ status: 'success', response: { url: 'https://tdesign.gtimg.com/site/avatar.jpg' } }`。 示例三:`{ status: 'success', files: [{ url: 'https://xxx.png', name: 'xxx.png' }]}` */
requestMethod: {
type: Function as PropType,
@@ -112,8 +124,7 @@ export default {
sizeLimit: {
type: [Number, Object] as PropType,
},
-
- /** 是否在请求时间超过 300ms 后显示模拟进度。上传进度有模拟进度和真实进度两种。一般大小的文件上传,真实的上传进度只有 0 和 100,不利于交互呈现,因此组件内置模拟上传进度。真实上传进度一般用于大文件上传。 */
+ /** 是否在请求时间超过 300ms 后显示模拟进度。上传进度有模拟进度和真实进度两种。一般大小的文件上传,真实的上传进度只有 0 和 100,不利于交互呈现,因此组件内置模拟上传进度。真实上传进度一般用于大文件上传 */
useMockProgress: {
type: Boolean,
default: true,
@@ -140,6 +151,8 @@ export default {
onCancelUpload: Function as PropType,
/** 已上传文件列表发生变化时触发,`trigger` 表示触发本次的来源 */
onChange: Function as PropType,
+ /** 点击上传区域时触发 */
+ onClickUpload: Function as PropType,
/** 上传失败后触发。`response` 指接口响应结果,`response.error` 会作为错误文本提醒。如果希望判定为上传失败,但接口响应数据不包含 `error` 字段,可以使用 `formatResponse` 格式化 `response` 数据结构。如果是多文件多请求上传场景,请到事件 `onOneFileFail` 中查看 `response` */
onFail: Function as PropType,
/** 多文件/图片场景下,单个文件上传失败后触发,如果一个请求上传一个文件,则会触发多次。单文件/图片不会触发 */
@@ -160,6 +173,4 @@ export default {
onValidate: Function as PropType,
/** 待上传文件列表发生变化时触发。`context.files` 表示事件参数为待上传文件,`context.trigger` 引起此次变化的触发来源 */
onWaitingUploadFilesChange: Function as PropType,
- /** 点击上传区域时触发。TS 类型:`(context: { e: MouseEvent })` */
- onClickUpload: Function as PropType,
};
diff --git a/src/upload/type.ts b/src/upload/type.ts
index 26724211e..522566ad3 100644
--- a/src/upload/type.ts
+++ b/src/upload/type.ts
@@ -19,7 +19,7 @@ export interface TdUploadProps {
*/
action?: string;
/**
- * 添加按钮内容。值为空,使用默认图标渲染;值为 slot 则表示使用插槽渲染;其他值无效。
+ * 添加按钮内容。值为空,使用默认图标渲染;值为 slot 则表示使用插槽渲染;其他值无效
*/
addContent?: string | TNode;
/**
@@ -40,12 +40,17 @@ export interface TdUploadProps {
*/
beforeAllFilesUpload?: (file: UploadFile[]) => boolean | Promise;
beforeUpload?: (file: UploadFile) => boolean | Promise;
+ /**
+ * 图片选取模式,可选值为 camera (直接调起摄像头)
+ * @default ''
+ */
+ capture?: string;
/**
* 上传请求所需的额外字段,默认字段有 `file`,表示文件信息。可以添加额外的文件名字段,如:`{file_name: "custom-file-name.txt"}`。`autoUpload=true` 时有效。也可以使用 `formatRequest` 完全自定义上传请求的字段
*/
data?: Record | ((files: UploadFile[]) => Record);
/**
- * 是否禁用
+ * 是否禁用组件
*/
disabled?: boolean;
/**
@@ -67,7 +72,7 @@ export interface TdUploadProps {
*/
format?: (file: File) => UploadFile;
/**
- * 用于新增或修改文件上传请求参数。`action` 存在时有效。一个请求上传一个文件时,默认请求字段有 `file`; 一个请求上传多个文件时,默认字段有 `file[0]/file[1]/file[2]/.../length`,其中 `length` 表示本次上传的文件数量。 ⚠️非常注意,此处的 `file[0]/file[1]` 仅仅是一个字段名,并非表示 `file` 是一个数组,接口获取字段时注意区分。 可以使用 `name` 定义 `file` 字段的别名,也可以使用 `formatRequest` 自定义任意字段
+ * 用于新增或修改文件上传请求 参数。`action` 存在时有效。一个请求上传一个文件时,默认请求字段有 `file`。 一个请求上传多个文件时,默认字段有 `file[0]/file[1]/file[2]/.../length`,其中 `length` 表示本次上传的文件数量。 ⚠️非常注意,此处的 `file[0]/file[1]` 仅仅是一个字段名,并非表示 `file` 是一个数组,接口获取字段时注意区分。 可以使用 `name` 定义 `file` 字段的别名。 也可以使用 `formatRequest` 自定义任意字段,如添加一个字段 `fileList` ,存储文件数组
*/
formatRequest?: (requestData: { [key: string]: any }) => { [key: string]: any };
/**
@@ -111,6 +116,11 @@ export interface TdUploadProps {
*/
mockProgressDuration?: number;
multiple?: boolean;
+ /**
+ * 是否支持图片预览,文件没有预览
+ * @default true
+ */
+ preview?: boolean;
/**
* 自定义上传方法。返回值 `status` 表示上传成功或失败;`error` 或 `response.error` 表示上传失败的原因; `response` 表示请求上传成功后的返回数据,`response.url` 表示上传成功后的图片/文件地址,`response.files` 表示一个请求上传多个文件/图片后的返回值。 示例一:`{ status: 'fail', error: '上传失败', response }`。 示例二:`{ status: 'success', response: { url: 'https://tdesign.gtimg.com/site/avatar.jpg' } }`。 示例三:`{ status: 'success', files: [{ url: 'https://xxx.png', name: 'xxx.png' }]}`
*/
@@ -120,7 +130,7 @@ export interface TdUploadProps {
*/
sizeLimit?: number | SizeLimitObj;
/**
- * 是否在请求时间超过 300ms 后显示模拟进度。上传进度有模拟进度和真实进度两种。一般大小的文件上传,真实的上传进度只有 0 和 100,不利于交互呈现,因此组件内置模拟上传进度。真实上传进度一般用于大文件上传。
+ * 是否在请求时间超过 300ms 后显示模拟进度。上传进度有模拟进度和真实进度两种。一般大小的文件上传,真实的上传进度只有 0 和 100,不利于交互呈现,因此组件内置模拟上传进度。真实上传进度一般用于大文件上传
* @default true
*/
useMockProgress?: boolean;
@@ -157,6 +167,10 @@ export interface TdUploadProps {
* 已上传文件列表发生变化时触发,`trigger` 表示触发本次的来源
*/
onChange?: (value: Array, context: UploadChangeContext) => void;
+ /**
+ * 点击上传区域时触发
+ */
+ onClickUpload?: (context: { e: MouseEvent }) => void;
/**
* 上传失败后触发。`response` 指接口响应结果,`response.error` 会作为错误文本提醒。如果希望判定为上传失败,但接口响应数据不包含 `error` 字段,可以使用 `formatResponse` 格式化 `response` 数据结构。如果是多文件多请求上传场景,请到事件 `onOneFileFail` 中查看 `response`
*/
@@ -200,10 +214,6 @@ export interface TdUploadProps {
files: Array;
trigger: 'validate' | 'remove' | 'uploaded';
}) => void;
- /**
- * 点击上传区域时触发。TS 类型:`(context: { e: MouseEvent })`
- */
- onClickUpload?: (context: { e: MouseEvent }) => void;
}
export interface UploadFile extends PlainObject {
@@ -309,7 +319,7 @@ export interface ProgressContext {
export type UploadProgressType = 'real' | 'mock';
export interface UploadRemoveContext {
- index: number;
+ index?: number;
file?: UploadFile;
e: MouseEvent;
}
diff --git a/src/upload/upload.en-US.md b/src/upload/upload.en-US.md
index dd1edde34..ed214fdd9 100644
--- a/src/upload/upload.en-US.md
+++ b/src/upload/upload.en-US.md
@@ -1,6 +1,7 @@
:: BASE_DOC ::
## API
+
### Upload Props
name | type | default | description | required
@@ -11,19 +12,20 @@ addContent | String / Slot / Function | - | Typescript:`string \| TNode`。[se
allowUploadDuplicateFile | Boolean | false | allow to upload duplicate name files | N
autoUpload | Boolean | true | post upload request automatically after files being selected | N
beforeUpload | Function | - | stop one of files to upload。Typescript:`(file: UploadFile) => boolean \| Promise` | N
+capture | String | - | \- | N
data | Object | - | extra request data of uploading. `formatRequest` can redefine all request data。Typescript:`Record \| ((files: UploadFile[]) => Record)` | N
-disabled | Boolean | - | make upload to be disabled | N
-fileListDisplay | Slot / Function | - | used to render file list UI。Typescript:`TNode<{ files: UploadFile[]; dragEvents?: UploadDisplayDragEvents }>`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+disabled | Boolean | undefined | make upload to be disabled | N
files | Array | [] | `v-model:files` is supported。Typescript:`Array` | N
defaultFiles | Array | [] | uncontrolled property。Typescript:`Array` | N
format | Function | - | to redefine `UploadFile` data structure。Typescript:`(file: File) => UploadFile` | N
formatRequest | Function | - | redefine request data。Typescript:`(requestData: { [key: string]: any }) => { [key: string]: any }` | N
-formatResponse | Function | - | redefine response data structure。Typescript:`(response: any, context: FormatResponseContext) => ResponseType ` `type ResponseType = { error?: string; url?: string } & Record` `interface FormatResponseContext { file: UploadFile; currentFiles?: UploadFile[] }`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts) | N
+formatResponse | Function | - | redefine response data structure。Typescript:`(response: any, context: FormatResponseContext) => ResponseType` `type ResponseType = { error?: string; url?: string } & Record` ` interface FormatResponseContext { file: UploadFile; currentFiles?: UploadFile[] }`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts) | N
headers | Object | - | HTTP Request Header。Typescript:`{[key: string]: string}` | N
imageProps | Object | - | Typescript:`ImageProps`,[Image API Documents](./image?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts) | N
max | Number | 0 | max count of files limit | N
-method | String | POST | HTTP request method。options: POST/GET/PUT/OPTION/PATCH/post/get/put/option/patch | N
+method | String | POST | HTTP request method。options: POST/GET/PUT/OPTIONS/PATCH/post/get/put/options/patch | N
multiple | Boolean | false | multiple files uploading | N
+preview | Boolean | true | \- | N
requestMethod | Function | - | custom upload request method。Typescript:`(files: UploadFile \| UploadFile[]) => Promise` `interface RequestMethodResponse { status: 'success' \| 'fail'; error?: string; response: { url?: string; files?: UploadFile[]; [key: string]: any } }`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts) | N
sizeLimit | Number / Object | - | files size limit。Typescript:`number \| SizeLimitObj` `interface SizeLimitObj { size: number; unit: SizeUnit ; message?: string }` `type SizeUnitArray = ['B', 'KB', 'MB', 'GB']` `type SizeUnit = SizeUnitArray[number]`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts) | N
useMockProgress | Boolean | true | use mock progress, instead of real progress | N
@@ -31,6 +33,7 @@ value | Array | [] | file list。`v-model` and `v-model:value` is supported。Ty
defaultValue | Array | [] | file list。uncontrolled property。Typescript:`Array` | N
withCredentials | Boolean | false | uploading request with cookie | N
onChange | Function | | Typescript:`(value: Array, context: UploadChangeContext) => void` trigger on uploaded files change。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadChangeContext { e?: MouseEvent \| ProgressEvent; response?: any; trigger: UploadChangeTrigger; index?: number; file?: UploadFile; files?: UploadFile[] }` `type UploadChangeTrigger = 'add' \| 'remove' \| 'abort' \| 'progress-success' \| 'progress' \| 'progress-fail'` | N
+onClickUpload | Function | | Typescript:`(context: { e: MouseEvent }) => void` | N
onFail | Function | | Typescript:`(options: UploadFailContext) => void` `response.error` used for error tips, `formatResponse` can format `response`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadFailContext { e?: ProgressEvent; failedFiles: UploadFile[]; currentFiles: UploadFile[]; response?: any; file: UploadFile; XMLHttpRequest?: XMLHttpRequest}` | N
onPreview | Function | | Typescript:`(options: { file: UploadFile; index: number; e: MouseEvent }) => void` trigger on preview elements click | N
onProgress | Function | | Typescript:`(options: ProgressContext) => void` uploading request progress event。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface ProgressContext { e?: ProgressEvent; file?: UploadFile; currentFiles: UploadFile[]; percent: number; type: UploadProgressType; XMLHttpRequest?: XMLHttpRequest }` `type UploadProgressType = 'real' \| 'mock'` | N
@@ -38,14 +41,13 @@ onRemove | Function | | Typescript:`(context: UploadRemoveContext) => void` void` trigger after file choose and before upload。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadSelectChangeContext { currentSelectedFiles: UploadFile[] }` | N
onSuccess | Function | | Typescript:`(context: SuccessContext) => void` trigger on all files uploaded successfully。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface SuccessContext { e?: ProgressEvent; file?: UploadFile; fileList?: UploadFile[]; currentFiles?: UploadFile[]; response?: any; results?: SuccessContext[]; XMLHttpRequest?: XMLHttpRequest }` | N
onValidate | Function | | Typescript:`(context: { type: UploadValidateType, files: UploadFile[] }) => void` trigger on length over limit, or trigger on file size over limit。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `type UploadValidateType = 'FILE_OVER_SIZE_LIMIT' \| 'FILES_OVER_LENGTH_LIMIT' \| 'FILTER_FILE_SAME_NAME' \| 'BEFORE_ALL_FILES_UPLOAD' \| 'CUSTOM_BEFORE_UPLOAD'` | N
-onClickUpload | Function | - | trigger on upload area click. Typescript:`(context: { e: MouseEvent })` | N
-
### Upload Events
name | params | description
-- | -- | --
change | `(value: Array, context: UploadChangeContext)` | trigger on uploaded files change。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadChangeContext { e?: MouseEvent \| ProgressEvent; response?: any; trigger: UploadChangeTrigger; index?: number; file?: UploadFile; files?: UploadFile[] }` `type UploadChangeTrigger = 'add' \| 'remove' \| 'abort' \| 'progress-success' \| 'progress' \| 'progress-fail'`
+click-upload | `(context: { e: MouseEvent })` | \-
fail | `(options: UploadFailContext)` | `response.error` used for error tips, `formatResponse` can format `response`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadFailContext { e?: ProgressEvent; failedFiles: UploadFile[]; currentFiles: UploadFile[]; response?: any; file: UploadFile; XMLHttpRequest?: XMLHttpRequest}`
preview | `(options: { file: UploadFile; index: number; e: MouseEvent })` | trigger on preview elements click
progress | `(options: ProgressContext)` | uploading request progress event。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface ProgressContext { e?: ProgressEvent; file?: UploadFile; currentFiles: UploadFile[]; percent: number; type: UploadProgressType; XMLHttpRequest?: XMLHttpRequest }` `type UploadProgressType = 'real' \| 'mock'`
@@ -53,7 +55,6 @@ remove | `(context: UploadRemoveContext)` | trigger on file removed。[see more
select-change | `(files: File[], context: UploadSelectChangeContext)` | trigger after file choose and before upload。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadSelectChangeContext { currentSelectedFiles: UploadFile[] }`
success | `(context: SuccessContext)` | trigger on all files uploaded successfully。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface SuccessContext { e?: ProgressEvent; file?: UploadFile; fileList?: UploadFile[]; currentFiles?: UploadFile[]; response?: any; results?: SuccessContext[]; XMLHttpRequest?: XMLHttpRequest }`
validate | `(context: { type: UploadValidateType, files: UploadFile[] })` | trigger on length over limit, or trigger on file size over limit。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `type UploadValidateType = 'FILE_OVER_SIZE_LIMIT' \| 'FILES_OVER_LENGTH_LIMIT' \| 'FILTER_FILE_SAME_NAME' \| 'BEFORE_ALL_FILES_UPLOAD' \| 'CUSTOM_BEFORE_UPLOAD'`
-clickUpload | `(context: { e: MouseEvent })` | trigger on upload area click.
### UploadFile
@@ -71,8 +72,8 @@ uploadTime | String | - | upload time | N
url | String | - | \- | N
`PlainObject` | \- | - | `PlainObject` is not an attribute of UploadFile,it means you can add and attributes to UploadFile, `type PlainObject = {[key: string]: any}`' | N
-
### CSS Variables
+
The component provides the following CSS variables, which can be used to customize styles.
Name | Default Value | Description
-- | -- | --
@@ -83,4 +84,4 @@ Name | Default Value | Description
--td-upload-grid-columns | 4 | -
--td-upload-height | 80px | -
--td-upload-radius | @radius-default | -
---td-upload-width | 80px | -
+--td-upload-width | 80px | -
\ No newline at end of file
diff --git a/src/upload/upload.md b/src/upload/upload.md
index 988c9e2d1..67e7d7b1e 100644
--- a/src/upload/upload.md
+++ b/src/upload/upload.md
@@ -1,36 +1,39 @@
:: BASE_DOC ::
## API
+
### Upload Props
-名称 | 类型 | 默认值 | 说明 | 必传
+名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
accept | String | - | 接受上传的文件类型,[查看 W3C示例](https://www.w3schools.com/tags/att_input_accept.asp),[查看 MDN 示例](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/Input/file) | N
action | String | - | 上传接口。设接口响应数据为字段 `response`,那么 `response.error` 存在时会判断此次上传失败,并显示错误文本信息;`response.url` 会作为文件上传成功后的地址,并使用该地址显示图片或文件 | N
-addContent | String / Slot / Function | - | 添加按钮内容。值为空,使用默认图标渲染;值为 slot 则表示使用插槽渲染;其他值无效。。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+addContent | String / Slot / Function | - | 添加按钮内容。值为空,使用默认图标渲染;值为 slot 则表示使用插槽渲染;其他值无效。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
allowUploadDuplicateFile | Boolean | false | 是否允许重复上传相同文件名的文件 | N
autoUpload | Boolean | true | 是否在选择文件后自动发起请求上传文件 | N
beforeUpload | Function | - | 如果是自动上传模式 `autoUpload=true`,表示单个文件上传之前的钩子函数,若函数返回值为 `false` 则表示不上传当前文件。 如果是非自动上传模式 `autoUpload=false`,函数返回值为 `false` 时表示从上传文件中剔除当前文件。TS 类型:`(file: UploadFile) => boolean \| Promise` | N
+capture | String | - | 图片选取模式,可选值为 camera (直接调起摄像头) | N
data | Object | - | 上传请求所需的额外字段,默认字段有 `file`,表示文件信息。可以添加额外的文件名字段,如:`{file_name: "custom-file-name.txt"}`。`autoUpload=true` 时有效。也可以使用 `formatRequest` 完全自定义上传请求的字段。TS 类型:`Record \| ((files: UploadFile[]) => Record)` | N
-disabled | Boolean | - | 是否禁用 | N
-fileListDisplay | Slot / Function | - | 【开发中】用于完全自定义文件列表界面内容(UI),单文件和多文件均有效。TS 类型:`TNode<{ files: UploadFile[]; dragEvents?: UploadDisplayDragEvents }>`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+disabled | Boolean | undefined | 是否禁用组件 | N
files | Array | [] | 已上传文件列表,同 `value`。TS 类型:`UploadFile`。支持语法糖 `v-model:files`。TS 类型:`Array` | N
defaultFiles | Array | [] | 已上传文件列表,同 `value`。TS 类型:`UploadFile`。非受控属性。TS 类型:`Array` | N
format | Function | - | 转换文件 `UploadFile` 的数据结构,可新增或修改 `UploadFile` 的属性,注意不能删除 `UploadFile` 属性。`action` 存在时有效。TS 类型:`(file: File) => UploadFile` | N
-formatRequest | Function | - | 用于新增或修改文件上传请求参数。`action` 存在时有效。一个请求上传一个文件时,默认请求字段有 `file`; 一个请求上传多个文件时,默认字段有 `file[0]/file[1]/file[2]/.../length`,其中 `length` 表示本次上传的文件数量。 ⚠️非常注意,此处的 `file[0]/file[1]` 仅仅是一个字段名,并非表示 `file` 是一个数组,接口获取字段时注意区分。 可以使用 `name` 定义 `file` 字段的别名,也可以使用 `formatRequest` 自定义任意字段。TS 类型:`(requestData: { [key: string]: any }) => { [key: string]: any }` | N
-formatResponse | Function | - | 用于格式化文件上传后的接口响应数据,`response` 便是接口响应的原始数据。`action` 存在时有效。 此函数的返回值 `error` 或 `response.error` 会作为错误文本提醒,如果存在会判定为本次上传失败。 此函数的返回值 `url` 或 `response.url` 会作为上传成功后的链接。TS 类型:`(response: any, context: FormatResponseContext) => ResponseType ` `type ResponseType = { error?: string; url?: string } & Record` `interface FormatResponseContext { file: UploadFile; currentFiles?: UploadFile[] }`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts) | N
+formatRequest | Function | - | 用于新增或修改文件上传请求 参数。`action` 存在时有效。一个请求上传一个文件时,默认请求字段有 `file`。 一个请求上传多个文件时,默认字段有 `file[0]/file[1]/file[2]/.../length`,其中 `length` 表示本次上传的文件数量。 ⚠️非常注意,此处的 `file[0]/file[1]` 仅仅是一个字段名,并非表示 `file` 是一个数组,接口获取字段时注意区分。 可以使用 `name` 定义 `file` 字段的别名。 也可以使用 `formatRequest` 自定义任意字段,如添加一个字段 `fileList` ,存储文件数组。TS 类型:`(requestData: { [key: string]: any }) => { [key: string]: any }` | N
+formatResponse | Function | - | 用于格式化文件上传后的接口响应数据,`response` 便是接口响应的原始数据。`action` 存在时有效。 此函数的返回值 `error` 或 `response.error` 会作为错误文本提醒,如果存在会判定为本次上传失败。 此函数的返回值 `url` 或 `response.url` 会作为上传成功后的链接。TS 类型:`(response: any, context: FormatResponseContext) => ResponseType` `type ResponseType = { error?: string; url?: string } & Record` ` interface FormatResponseContext { file: UploadFile; currentFiles?: UploadFile[] }`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts) | N
headers | Object | - | 设置上传的请求头部,`action` 存在时有效。TS 类型:`{[key: string]: string}` | N
imageProps | Object | - | 透传 Image 组件全部属性。TS 类型:`ImageProps`,[Image API Documents](./image?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts) | N
max | Number | 0 | 用于控制文件上传数量,值为 0 则不限制 | N
-method | String | POST | HTTP 请求类型。可选项:POST/GET/PUT/OPTION/PATCH/post/get/put/option/patch | N
+method | String | POST | HTTP 请求类型。可选项:POST/GET/PUT/OPTIONS/PATCH/post/get/put/options/patch | N
multiple | Boolean | false | 支持多文件上传 | N
+preview | Boolean | true | 是否支持图片预览,文件没有预览 | N
requestMethod | Function | - | 自定义上传方法。返回值 `status` 表示上传成功或失败;`error` 或 `response.error` 表示上传失败的原因; `response` 表示请求上传成功后的返回数据,`response.url` 表示上传成功后的图片/文件地址,`response.files` 表示一个请求上传多个文件/图片后的返回值。 示例一:`{ status: 'fail', error: '上传失败', response }`。 示例二:`{ status: 'success', response: { url: 'https://tdesign.gtimg.com/site/avatar.jpg' } }`。 示例三:`{ status: 'success', files: [{ url: 'https://xxx.png', name: 'xxx.png' }]}`。TS 类型:`(files: UploadFile \| UploadFile[]) => Promise` `interface RequestMethodResponse { status: 'success' \| 'fail'; error?: string; response: { url?: string; files?: UploadFile[]; [key: string]: any } }`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts) | N
sizeLimit | Number / Object | - | 图片文件大小限制,默认单位 KB。可选单位有:`'B' \| 'KB' \| 'MB' \| 'GB'`。示例一:`1000`。示例二:`{ size: 2, unit: 'MB', message: '图片大小不超过 {sizeLimit} MB' }`。TS 类型:`number \| SizeLimitObj` `interface SizeLimitObj { size: number; unit: SizeUnit ; message?: string }` `type SizeUnitArray = ['B', 'KB', 'MB', 'GB']` `type SizeUnit = SizeUnitArray[number]`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts) | N
-useMockProgress | Boolean | true | 是否在请求时间超过 300ms 后显示模拟进度。上传进度有模拟进度和真实进度两种。一般大小的文件上传,真实的上传进度只有 0 和 100,不利于交互呈现,因此组件内置模拟上传进度。真实上传进度一般用于大文件上传。 | N
+useMockProgress | Boolean | true | 是否在请求时间超过 300ms 后显示模拟进度。上传进度有模拟进度和真实进度两种。一般大小的文件上传,真实的上传进度只有 0 和 100,不利于交互呈现,因此组件内置模拟上传进度。真实上传进度一般用于大文件上传 | N
value | Array | [] | 已上传文件列表,同 `files`。TS 类型:`UploadFile`。支持语法糖 `v-model` 或 `v-model:value`。TS 类型:`Array` | N
defaultValue | Array | [] | 已上传文件列表,同 `files`。TS 类型:`UploadFile`。非受控属性。TS 类型:`Array` | N
withCredentials | Boolean | false | 上传请求时是否携带 cookie | N
onChange | Function | | TS 类型:`(value: Array, context: UploadChangeContext) => void` 已上传文件列表发生变化时触发,`trigger` 表示触发本次的来源。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadChangeContext { e?: MouseEvent \| ProgressEvent; response?: any; trigger: UploadChangeTrigger; index?: number; file?: UploadFile; files?: UploadFile[] }` `type UploadChangeTrigger = 'add' \| 'remove' \| 'abort' \| 'progress-success' \| 'progress' \| 'progress-fail'` | N
+onClickUpload | Function | | TS 类型:`(context: { e: MouseEvent }) => void` 点击上传区域时触发 | N
onFail | Function | | TS 类型:`(options: UploadFailContext) => void` 上传失败后触发。`response` 指接口响应结果,`response.error` 会作为错误文本提醒。如果希望判定为上传失败,但接口响应数据不包含 `error` 字段,可以使用 `formatResponse` 格式化 `response` 数据结构。如果是多文件多请求上传场景,请到事件 `onOneFileFail` 中查看 `response`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadFailContext { e?: ProgressEvent; failedFiles: UploadFile[]; currentFiles: UploadFile[]; response?: any; file: UploadFile; XMLHttpRequest?: XMLHttpRequest}` | N
onPreview | Function | | TS 类型:`(options: { file: UploadFile; index: number; e: MouseEvent }) => void` 点击图片预览时触发,文件没有预览 | N
onProgress | Function | | TS 类型:`(options: ProgressContext) => void` 上传进度变化时触发,真实进度和模拟进度都会触发。 ⚠️ 原始上传请求,小文件的上传进度只有 0 和 100,故而不会触发 `progress` 事件;只有大文件才有真实的中间进度。如果你希望很小的文件也显示上传进度,保证 `useMockProgress=true` 的情况下,设置 `mockProgressDuration` 为更小的值。 参数 `options.type=real` 表示真实上传进度,`options.type=mock` 表示模拟上传进度。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface ProgressContext { e?: ProgressEvent; file?: UploadFile; currentFiles: UploadFile[]; percent: number; type: UploadProgressType; XMLHttpRequest?: XMLHttpRequest }` `type UploadProgressType = 'real' \| 'mock'` | N
@@ -38,13 +41,13 @@ onRemove | Function | | TS 类型:`(context: UploadRemoveContext) => void` void` 选择文件或图片之后,上传之前,触发该事件。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadSelectChangeContext { currentSelectedFiles: UploadFile[] }` | N
onSuccess | Function | | TS 类型:`(context: SuccessContext) => void` 上传成功后触发。 `context.currentFiles` 表示当次请求上传的文件(无论成功或失败),`context.fileList` 表示上传成功后的文件,`context.response` 表示上传请求的返回数据。 `context.results` 表示单次选择全部文件上传成功后的响应结果,可以在这个字段存在时提醒用户上传成功或失败。 。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface SuccessContext { e?: ProgressEvent; file?: UploadFile; fileList?: UploadFile[]; currentFiles?: UploadFile[]; response?: any; results?: SuccessContext[]; XMLHttpRequest?: XMLHttpRequest }` | N
onValidate | Function | | TS 类型:`(context: { type: UploadValidateType, files: UploadFile[] }) => void` 文件上传校验结束事件,文件数量超出、文件大小超出限制、文件同名、`beforeAllFilesUpload` 返回值为假、`beforeUpload` 返回值为假等场景会触发。 注意:如果设置允许上传同名文件,即 `allowUploadDuplicateFile=true`,则不会因为文件重名触发该事件。 结合 `status` 和 `tips` 可以在组件中呈现不同类型的错误(或告警)提示。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `type UploadValidateType = 'FILE_OVER_SIZE_LIMIT' \| 'FILES_OVER_LENGTH_LIMIT' \| 'FILTER_FILE_SAME_NAME' \| 'BEFORE_ALL_FILES_UPLOAD' \| 'CUSTOM_BEFORE_UPLOAD'` | N
-onClickUpload | Function | - | 点击上传区域时触发。TS 类型:`(context: { e: MouseEvent })` | N
### Upload Events
名称 | 参数 | 描述
-- | -- | --
change | `(value: Array, context: UploadChangeContext)` | 已上传文件列表发生变化时触发,`trigger` 表示触发本次的来源。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadChangeContext { e?: MouseEvent \| ProgressEvent; response?: any; trigger: UploadChangeTrigger; index?: number; file?: UploadFile; files?: UploadFile[] }` `type UploadChangeTrigger = 'add' \| 'remove' \| 'abort' \| 'progress-success' \| 'progress' \| 'progress-fail'`
+click-upload | `(context: { e: MouseEvent })` | 点击上传区域时触发
fail | `(options: UploadFailContext)` | 上传失败后触发。`response` 指接口响应结果,`response.error` 会作为错误文本提醒。如果希望判定为上传失败,但接口响应数据不包含 `error` 字段,可以使用 `formatResponse` 格式化 `response` 数据结构。如果是多文件多请求上传场景,请到事件 `onOneFileFail` 中查看 `response`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadFailContext { e?: ProgressEvent; failedFiles: UploadFile[]; currentFiles: UploadFile[]; response?: any; file: UploadFile; XMLHttpRequest?: XMLHttpRequest}`
preview | `(options: { file: UploadFile; index: number; e: MouseEvent })` | 点击图片预览时触发,文件没有预览
progress | `(options: ProgressContext)` | 上传进度变化时触发,真实进度和模拟进度都会触发。 ⚠️ 原始上传请求,小文件的上传进度只有 0 和 100,故而不会触发 `progress` 事件;只有大文件才有真实的中间进度。如果你希望很小的文件也显示上传进度,保证 `useMockProgress=true` 的情况下,设置 `mockProgressDuration` 为更小的值。 参数 `options.type=real` 表示真实上传进度,`options.type=mock` 表示模拟上传进度。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface ProgressContext { e?: ProgressEvent; file?: UploadFile; currentFiles: UploadFile[]; percent: number; type: UploadProgressType; XMLHttpRequest?: XMLHttpRequest }` `type UploadProgressType = 'real' \| 'mock'`
@@ -52,11 +55,10 @@ remove | `(context: UploadRemoveContext)` | 移除文件时触发。[详细类
select-change | `(files: File[], context: UploadSelectChangeContext)` | 选择文件或图片之后,上传之前,触发该事件。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface UploadSelectChangeContext { currentSelectedFiles: UploadFile[] }`
success | `(context: SuccessContext)` | 上传成功后触发。 `context.currentFiles` 表示当次请求上传的文件(无论成功或失败),`context.fileList` 表示上传成功后的文件,`context.response` 表示上传请求的返回数据。 `context.results` 表示单次选择全部文件上传成功后的响应结果,可以在这个字段存在时提醒用户上传成功或失败。 。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `interface SuccessContext { e?: ProgressEvent; file?: UploadFile; fileList?: UploadFile[]; currentFiles?: UploadFile[]; response?: any; results?: SuccessContext[]; XMLHttpRequest?: XMLHttpRequest }`
validate | `(context: { type: UploadValidateType, files: UploadFile[] })` | 文件上传校验结束事件,文件数量超出、文件大小超出限制、文件同名、`beforeAllFilesUpload` 返回值为假、`beforeUpload` 返回值为假等场景会触发。 注意:如果设置允许上传同名文件,即 `allowUploadDuplicateFile=true`,则不会因为文件重名触发该事件。 结合 `status` 和 `tips` 可以在组件中呈现不同类型的错误(或告警)提示。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/upload/type.ts)。 `type UploadValidateType = 'FILE_OVER_SIZE_LIMIT' \| 'FILES_OVER_LENGTH_LIMIT' \| 'FILTER_FILE_SAME_NAME' \| 'BEFORE_ALL_FILES_UPLOAD' \| 'CUSTOM_BEFORE_UPLOAD'`
-clickUpload | `(context: { e: MouseEvent })` | 点击上传区域时触发。
### UploadFile
-名称 | 类型 | 默认值 | 说明 | 必传
+名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
lastModified | Number | - | 上一次变更的时间 | N
name | String | - | 文件名称 | N
@@ -70,8 +72,8 @@ uploadTime | String | - | 上传时间 | N
url | String | - | 文件上传成功后的下载/访问地址 | N
`PlainObject` | \- | - | `PlainObject` 不是 UploadFile 中的属性,而表示 UploadFile 本身支持添加任意属性,`type PlainObject = {[key: string]: any}`' | N
-
### CSS Variables
+
组件提供了下列 CSS 变量,可用于自定义样式。
名称 | 默认值 | 描述
-- | -- | --
@@ -82,4 +84,4 @@ url | String | - | 文件上传成功后的下载/访问地址 | N
--td-upload-grid-columns | 4 | -
--td-upload-height | 80px | -
--td-upload-radius | @radius-default | -
---td-upload-width | 80px | -
+--td-upload-width | 80px | -
\ No newline at end of file
diff --git a/src/upload/upload.tsx b/src/upload/upload.tsx
index 0bf2150d4..1feda9a9c 100644
--- a/src/upload/upload.tsx
+++ b/src/upload/upload.tsx
@@ -64,7 +64,7 @@ export default defineComponent({
const handlePreview = (e: MouseEvent, file: UploadFile, index: number) => {
initialIndex.value = index;
- showViewer.value = true;
+ showViewer.value = props.preview;
props.onPreview?.({
e,
file,
@@ -183,6 +183,7 @@ export default defineComponent({
type="file"
multiple={this.$props.multiple}
hidden
+ capture={this.$props.capture as unknown as boolean}
accept={this.$props.accept}
onChange={this.onNormalFileChange}
/>