Skip to content

Commit

Permalink
[Upload][Refactor] (#1723)
Browse files Browse the repository at this point in the history
* feat(upload): initial version

* feat(upload): base demo

* feat(upload): initial upload code

* feat(upload): some features

* docs(upload): demos

* feat(upload): progress

* test(unit): update snapshots

* Merge remote-tracking branch 'origin/develop' into 20220912_feature_upload

* feat(_common): update

* feat(_common): update commone

* feat(image-viewer): show arrow

* docs(upload): fix demo @click to @click

* fix(upload): event name error

* chore(upload): remove useless code

* feat(upload): demo improvement

* test(unit): update snapshots

* docs(upload): update document

* feat(_common): update common
  • Loading branch information
chaishi authored Sep 26, 2022
1 parent 6b85b34 commit 174dfb0
Show file tree
Hide file tree
Showing 40 changed files with 3,500 additions and 3,223 deletions.
15 changes: 12 additions & 3 deletions src/hooks/tnode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { h, getCurrentInstance, ComponentInternalInstance, VNode } from 'vue';
import isEmpty from 'lodash/isEmpty';
import isFunction from 'lodash/isFunction';
import camelCase from 'lodash/camelCase';
import kebabCase from 'lodash/kebabCase';
Expand All @@ -16,6 +15,16 @@ function handleSlots(instance: ComponentInternalInstance, name: string, params:
return null;
}

/**
* 是否为空节点,需要过滤掉注释节点。注释节点也会被认为是空节点
*/
function isEmptyNode(node: any) {
if ([undefined, null, ''].includes(node)) return true;
const innerNodes = node instanceof Array ? node : [node];
const r = innerNodes.filter((node) => node?.type?.toString() !== 'Symbol(Comment)');
return !r.length;
}

/**
* 通过 JSX 的方式渲染 TNode,props 和 插槽同时处理,也能处理默认值为 true 则渲染默认节点的情况
* 优先级:Props 大于插槽
Expand Down Expand Up @@ -103,7 +112,7 @@ export const useContent = () => {
const node1 = renderTNodeJSX(name1, toParams);
const node2 = renderTNodeJSX(name2, toParams);

const res = isEmpty(node1) ? node2 : node1;
return isEmpty(res) ? defaultNode : res;
const res = isEmptyNode(node1) ? node2 : node1;
return isEmptyNode(res) ? defaultNode : res;
};
};
2 changes: 2 additions & 0 deletions src/hooks/useGlobalIcon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export function useGlobalIcon(tdIcon: Object) {

return resultIcon;
}

export default useGlobalIcon;
2 changes: 1 addition & 1 deletion src/image-viewer/image-viewer.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mode | String | modal | options:modal/modeless | N
navigationArrow | Boolean / Slot / Function | true | Typescript:`boolean | TNode`[see more ts definition](https://github.com/Tencent/tdesign-vue-next/blob/develop/src/common.ts) | N
showOverlay | Boolean | undefined | \- | N
title | String / Slot / Function | - | preview title。Typescript:`string | TNode`[see more ts definition](https://github.com/Tencent/tdesign-vue-next/blob/develop/src/common.ts) | N
trigger | String / Slot / Function | - | Typescript:`string | TNode`[see more ts definition](https://github.com/Tencent/tdesign-vue-next/blob/develop/src/common.ts) | N
trigger | String / Slot / Function | - | trigger element。Typescript:`string | TNode<{ open: () => void }>`[see more ts definition](https://github.com/Tencent/tdesign-vue-next/blob/develop/src/common.ts) | N
viewerScale | Object | - | Typescript:`ImageViewerScale` `interface ImageViewerScale { minWidth: number; minHeight: number }`[see more ts definition](https://github.com/Tencent/tdesign-vue-next/tree/develop/src/image-viewer/type.ts) | N
visible | Boolean | false | `v-model` and `v-model:visible` is supported | N
defaultVisible | Boolean | false | uncontrolled property | N
Expand Down
2 changes: 1 addition & 1 deletion src/image-viewer/image-viewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mode | String | modal | 模态预览(modal)和非模态预览(modeless)。
navigationArrow | Boolean / Slot / Function | true | 切换预览图片的左图标,可自定义。TS 类型:`boolean | TNode`[通用类型定义](https://github.com/Tencent/tdesign-vue-next/blob/develop/src/common.ts) | N
showOverlay | Boolean | undefined | 是否显示遮罩层。`mode=modal` 时,默认显示;`mode=modeless` 时,默认不显示 | N
title | String / Slot / Function | - | 预览标题。TS 类型:`string | TNode`[通用类型定义](https://github.com/Tencent/tdesign-vue-next/blob/develop/src/common.ts) | N
trigger | String / Slot / Function | - | 触发图片预览的元素,可能是一个预览按钮,可能是一张缩略图,完全自定义。TS 类型:`string | TNode`[通用类型定义](https://github.com/Tencent/tdesign-vue-next/blob/develop/src/common.ts) | N
trigger | String / Slot / Function | - | 触发图片预览的元素,可能是一个预览按钮,可能是一张缩略图,完全自定义。TS 类型:`string | TNode<{ open: () => void }>`[通用类型定义](https://github.com/Tencent/tdesign-vue-next/blob/develop/src/common.ts) | N
viewerScale | Object | - | 限制预览器缩放的最小宽度和最小高度,仅 `mode=modeless` 时有效。TS 类型:`ImageViewerScale` `interface ImageViewerScale { minWidth: number; minHeight: number }`[详细类型定义](https://github.com/Tencent/tdesign-vue-next/tree/develop/src/image-viewer/type.ts) | N
visible | Boolean | false | 隐藏/显示预览。支持语法糖 `v-model``v-model:visible` | N
defaultVisible | Boolean | false | 隐藏/显示预览。非受控属性 | N
Expand Down
2 changes: 1 addition & 1 deletion src/image-viewer/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export interface TdImageViewerProps {
/**
* 触发图片预览的元素,可能是一个预览按钮,可能是一张缩略图,完全自定义
*/
trigger?: string | TNode;
trigger?: string | TNode<{ open: () => void }>;
/**
* 限制预览器缩放的最小宽度和最小高度,仅 `mode=modeless` 时有效
*/
Expand Down
8 changes: 0 additions & 8 deletions src/tree-select/__tests__/__snapshots__/index.test.jsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,7 @@ exports[`TreeSelect > :props > :defaultValue 1`] = `
class="t-tag--text"
style="max-width: 300px;"
>
<!---->
</span>
<svg
class="t-icon t-icon-close t-tag__icon-close"
Expand Down Expand Up @@ -578,9 +576,7 @@ exports[`TreeSelect > :props > :multiple 1`] = `
class="t-tag--text"
style="max-width: 300px;"
>
<!---->
</span>
<svg
class="t-icon t-icon-close t-tag__icon-close"
Expand Down Expand Up @@ -1007,9 +1003,7 @@ exports[`TreeSelect > <slot> > <collapsedItems> 1`] = `
class="t-tag--text"
style="max-width: 300px;"
>
<!---->
</span>
<svg
class="t-icon t-icon-close t-tag__icon-close"
Expand Down Expand Up @@ -1337,9 +1331,7 @@ exports[`TreeSelect > function > :collapsedItems 1`] = `
class="t-tag--text"
style="max-width: 300px;"
>
<!---->
</span>
<svg
class="t-icon t-icon-close t-tag__icon-close"
Expand Down
210 changes: 193 additions & 17 deletions src/upload/_example/base.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,211 @@
<!-- :sizeLimit="{ size: 2, unit: 'MB' }" -->
<!-- :sizeLimit="{ size: 2, unit: 'MB', message: '图片太大' }" -->
<!-- :sizeLimit="{ size: 2, unit: 'MB', message: '图片太大,不能超过 {sizeLimit} MB' }" -->
<t-space direction="vertical">
<t-space>
<t-radio-group v-model="multiple" variant="default-filled">
<t-radio-button :value="false">单文件上传</t-radio-button>
<t-radio-button :value="true">多文件上传</t-radio-button>
</t-radio-group>
</t-space>
<t-space>
<t-checkbox v-model="disabled">禁用状态</t-checkbox>
<t-checkbox v-if="multiple" v-model="uploadInOneRequest">多个文件一个请求上传</t-checkbox>
<t-checkbox v-if="multiple" v-model="isBatchUpload">整体替换上传</t-checkbox>
<t-checkbox v-model="autoUpload">自动上传</t-checkbox>
<t-button v-if="!autoUpload" variant="base" theme="default" style="height: 22px" @click="uploadFiles">
点击手动上传
</t-button>
</t-space>

<!-- 上传接口默认只传一个参数,如果希望自定义参数,可以使用 format 方法格式化参数-->
<t-upload
v-model="files"
action="https://service-bv448zsw-1257786608.gz.apigw.tencentcs.com/api/upload-demo"
tips="上传文件大小在 5M 以内"
:size-limit="{ size: 5, unit: 'MB' }"
:format-response="formatResponse"
:on-select-change="handleSelectChange"
@fail="handleFail"
></t-upload>
<br />
<t-space>
<t-upload
ref="uploadRef1"
v-model="files1"
action="https://service-bv448zsw-1257786608.gz.apigw.tencentcs.com/api/upload-demo"
:placeholder="multiple ? '文件数量不超过 5 个' : '要求文件大小在 1M 以内'"
:multiple="multiple"
:auto-upload="autoUpload"
:upload-all-files-in-one-request="uploadInOneRequest"
:is-batch-upload="isBatchUpload"
:size-limit="{ size: 1, unit: 'MB' }"
:max="5"
:disabled="disabled"
:allow-upload-duplicate-file="true"
@select-change="handleSelectChange"
@fail="handleFail"
@success="handleSuccess"
@one-file-success="onOneFileSuccess"
@validate="onValidate"
/>

<t-upload
ref="uploadRef2"
v-model="files2"
:multiple="multiple"
:disabled="disabled"
:auto-upload="autoUpload"
:upload-all-files-in-one-request="uploadInOneRequest"
:is-batch-upload="isBatchUpload"
:trigger-button-props="{ theme: 'primary', variant: 'base' }"
placeholder="这是一段没有文件时的占位文本"
action="//service-bv448zsw-1257786608.gz.apigw.tencentcs.com/api/upload-demo"
:style="{ marginLeft: '40px' }"
@fail="handleFail"
/>

<!-- formatResponse 可控制上传成功或者失败 -->
<!-- tips="文件上传失败示例" 和 status="error" 控制固定文本显示 -->
<t-upload
ref="uploadRef3"
v-model="files3"
:multiple="multiple"
:disabled="disabled"
:auto-upload="autoUpload"
:upload-all-files-in-one-request="uploadInOneRequest"
:is-batch-upload="isBatchUpload"
:format-response="formatResponse"
placeholder="文件上传失败示例"
action="//service-bv448zsw-1257786608.gz.apigw.tencentcs.com/api/upload-demo"
:style="{ marginLeft: '60px' }"
@fail="handleFail"
>
<!-- 自定义文件列表,示例代码有效,勿删 -->
<!-- <template #fileListDisplay>
<div>
<div
v-for="(file, index) in files3"
:key="file.name"
class="t-upload__single-display-text t-upload__display-text--margin"
>
{{file.name}}({{file.size}} B)
<CloseIcon class="t-upload__icon-delete" @click="() => outsideRemove(index)" />
</div>
</div>
</template> -->
</t-upload>
</t-space>
</t-space>
</template>
<script setup>
import { ref } from 'vue';
import { ref, watch } from 'vue';
import { MessagePlugin } from 'tdesign-vue-next';
// import { CloseIcon } from 'tdesign-icons-vue-next';
const uploadRef1 = ref();
const uploadRef2 = ref();
const uploadRef3 = ref();
const files1 = ref([]);
const files2 = ref([
{
name: '这是一个默认文件',
status: 'success',
url: 'https://tdesign.gtimg.com/site/source/figma-pc.png',
size: 1000,
},
]);
const files3 = ref([]);
const multiple = ref(false);
const uploadInOneRequest = ref(false);
const autoUpload = ref(true);
const isBatchUpload = ref(false);
const disabled = ref(false);
const files = ref([]);
watch(multiple, (multiple) => {
files3.value = multiple
? [
{
name: '这是一个上传成功的文件',
status: 'success',
url: 'https://tdesign.gtimg.com/site/source/figma-pc.png',
size: 1000,
},
{
name: '这是一个上传中的文件',
status: 'progress',
percent: 30,
url: 'https://tdesign.gtimg.com/site/source/figma-pc.png',
size: 1000,
},
{
name: '这是一个上传失败的文件',
status: 'fail',
url: 'https://tdesign.gtimg.com/site/source/figma-pc.png',
size: 1000,
},
{
name: '这是一个等待上传的文件',
status: 'waiting',
url: 'https://tdesign.gtimg.com/site/source/figma-pc.png',
size: 1000,
},
]
: [];
});
const handleFail = ({ file }) => {
MessagePlugin.error(`文件 ${file.name} 上传失败`);
};
const handleSelectChange = (files) => {
console.log(files);
console.log('onSelectChange', files);
};
const handleSuccess = () => {
MessagePlugin.success('上传成功');
};
// 多文件上传,一个文件一个请求场景,每个文件也会单独触发上传成功的事件
const onOneFileSuccess = (params) => {
console.log('onOneFileSuccess', params);
};
const formatResponse = (res) => {
if (!res.url) {
return { error: '上传失败,请重试' };
// 有文件数量超出时会触发,文件大小超出限制、文件同名时会触发等场景。注意如果设置允许上传同名文件,则此事件不会触发
const onValidate = (params) => {
const { files, type } = params;
console.log('onValidate', params);
if (type === 'FILE_OVER_SIZE_LIMIT') {
files.map((t) => t.name).join('');
MessagePlugin.warning(`${files.map((t) => t.name).join('')} 等文件大小超出限制,已自动过滤`, 5000);
} else if (type === 'FILES_OVER_LENGTH_LIMIT') {
MessagePlugin.warning('文件数量超出限制,仅上传未超出数量的文件');
} else if (type === 'FILTER_FILE_SAME_NAME') {
// 如果希望支持上传同名文件,请设置 allowUploadDuplicateFile={true}
MessagePlugin.warning('不允许上传同名文件');
}
return { ...res, url: res.url };
};
// 仅自定义文件列表所需
// eslint-disable-next-line
const outsideRemove = (index) => {
const tmpFiles = [...files3.value];
tmpFiles.splice(index, 1);
files3.value = tmpFiles;
};
// 非自动上传文件,需要在父组件单独执行上传请求
const uploadFiles = () => {
uploadRef1.value.uploadFiles();
uploadRef2.value.uploadFiles();
uploadRef3.value.uploadFiles();
};
const formatResponse = () => {
return { error: '上传失败,请重试' };
};
/** 单个文件校验方法,示例代码有效,勿删 */
// const beforeUpload = (file) => {
// MessagePlugin.error(`文件 ${file.name} 不满足条件`);
// return false;
// };
/** 全部文件一次性校验方法,示例代码有效,勿删 */
// const beforeAllFilesUpload = () => {
// MessagePlugin.error(`文件不满足条件`);
// return false;
// };
</script>
7 changes: 3 additions & 4 deletions src/upload/_example/custom-drag.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,15 @@
@success="handleSuccess"
@progress="onProgress"
>
<template #default="params">
<ul v-if="files && files.length">
<li v-for="file in files" :key="file.name">{{ file.name }}</li>
<template #dragContent="params">
<ul v-if="files && files.length" style="padding: 0">
<li v-for="file in files" :key="file.name" style="list-style-type: none">{{ file.name }}</li>
</ul>
<template v-else>
<p v-if="params && params.dragActive">释放鼠标</p>
<t-button v-else-if="progress < 1">自定义拖拽区域</t-button>
</template>
<t-button v-if="files && files.length" size="small" style="margin-top: 36px">更换文件</t-button>
<br /><br />
<!-- <span>数据状态:{{params}}</span> -->
</template>
</t-upload>
Expand Down
Loading

0 comments on commit 174dfb0

Please sign in to comment.