|
@@ -2,11 +2,13 @@
|
|
|
<FlexCol>
|
|
<FlexCol>
|
|
|
<Toast ref="toast" />
|
|
<Toast ref="toast" />
|
|
|
<DialogRoot ref="dialog" />
|
|
<DialogRoot ref="dialog" />
|
|
|
|
|
+ <!-- #ifndef MP -->
|
|
|
<slot
|
|
<slot
|
|
|
name="uploader"
|
|
name="uploader"
|
|
|
:onClick="onUploadPress"
|
|
:onClick="onUploadPress"
|
|
|
:items="currentUpladList"
|
|
:items="currentUpladList"
|
|
|
>
|
|
>
|
|
|
|
|
+ <!-- #endif -->
|
|
|
<FlexView
|
|
<FlexView
|
|
|
v-if="showUpload"
|
|
v-if="showUpload"
|
|
|
:direction="props.listType === 'grid' ? 'row' : 'column'"
|
|
:direction="props.listType === 'grid' ? 'row' : 'column'"
|
|
@@ -17,6 +19,7 @@
|
|
|
v-for="(item, index) in currentUpladList"
|
|
v-for="(item, index) in currentUpladList"
|
|
|
:key="index"
|
|
:key="index"
|
|
|
>
|
|
>
|
|
|
|
|
+ <!-- #ifndef MP -->
|
|
|
<slot
|
|
<slot
|
|
|
name="uploadItem"
|
|
name="uploadItem"
|
|
|
:index="index"
|
|
:index="index"
|
|
@@ -32,6 +35,7 @@
|
|
|
:defaultSource="props.itemDefaultSource"
|
|
:defaultSource="props.itemDefaultSource"
|
|
|
|
|
|
|
|
>
|
|
>
|
|
|
|
|
+ <!-- #endif -->
|
|
|
<UploaderListItem
|
|
<UploaderListItem
|
|
|
:item="item"
|
|
:item="item"
|
|
|
:showDelete="showDelete"
|
|
:showDelete="showDelete"
|
|
@@ -45,7 +49,9 @@
|
|
|
@click="() => onItemPress(item)"
|
|
@click="() => onItemPress(item)"
|
|
|
@delete="() => onItemDeletePress(item)"
|
|
@delete="() => onItemDeletePress(item)"
|
|
|
/>
|
|
/>
|
|
|
|
|
+ <!-- #ifndef MP -->
|
|
|
</slot>
|
|
</slot>
|
|
|
|
|
+ <!-- #endif -->
|
|
|
</template>
|
|
</template>
|
|
|
<slot v-if="currentUpladList.length < maxUploadCount && !disabled" name="addButton" :onUploadPress="onUploadPress" :itemSize="itemSize">
|
|
<slot v-if="currentUpladList.length < maxUploadCount && !disabled" name="addButton" :onUploadPress="onUploadPress" :itemSize="itemSize">
|
|
|
<UploaderListAddItem :itemSize="itemSize" :style="itemStyle" @click="onUploadPress" :isListStyle="props.listType === 'list'" />
|
|
<UploaderListAddItem :itemSize="itemSize" :style="itemStyle" @click="onUploadPress" :isListStyle="props.listType === 'list'" />
|
|
@@ -57,12 +63,15 @@
|
|
|
<slot v-else name="addButton" :onUploadPress="onUploadPress" :itemSize="itemSize">
|
|
<slot v-else name="addButton" :onUploadPress="onUploadPress" :itemSize="itemSize">
|
|
|
<UploaderListAddItem :itemSize="itemSize" :style="itemStyle" @click="onUploadPress" :isListStyle="props.listType === 'list'" />
|
|
<UploaderListAddItem :itemSize="itemSize" :style="itemStyle" @click="onUploadPress" :isListStyle="props.listType === 'list'" />
|
|
|
</slot>
|
|
</slot>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- #ifndef MP -->
|
|
|
</slot>
|
|
</slot>
|
|
|
|
|
+ <!-- #endif -->
|
|
|
</FlexCol>
|
|
</FlexCol>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import { ref, watch } from 'vue';
|
|
|
|
|
|
|
+import { reactive, ref, watch } from 'vue';
|
|
|
import { propGetThemeVar, useTheme, type TextStyle, type ViewStyle } from '../theme/ThemeDefine';
|
|
import { propGetThemeVar, useTheme, type TextStyle, type ViewStyle } from '../theme/ThemeDefine';
|
|
|
import type { ToastInstance } from '../feedback/Toast.vue';
|
|
import type { ToastInstance } from '../feedback/Toast.vue';
|
|
|
import Toast from '../feedback/Toast.vue';
|
|
import Toast from '../feedback/Toast.vue';
|
|
@@ -72,7 +81,7 @@ import UploaderListItem from './UploaderListItem.vue';
|
|
|
import FlexView from '../layout/FlexView.vue';
|
|
import FlexView from '../layout/FlexView.vue';
|
|
|
import FlexCol from '../layout/FlexCol.vue';
|
|
import FlexCol from '../layout/FlexCol.vue';
|
|
|
import type { UploaderAction, UploaderItem } from './Uploader';
|
|
import type { UploaderAction, UploaderItem } from './Uploader';
|
|
|
-import { LogUtils } from '@imengyu/imengyu-utils';
|
|
|
|
|
|
|
+import { Debounce, LogUtils } from '@imengyu/imengyu-utils';
|
|
|
import Text from '../basic/Text.vue';
|
|
import Text from '../basic/Text.vue';
|
|
|
import { actionSheet } from '../dialog/CommonRoot';
|
|
import { actionSheet } from '../dialog/CommonRoot';
|
|
|
|
|
|
|
@@ -180,7 +189,7 @@ export interface UploaderProps {
|
|
|
* 上传处理。不提供则无法上传
|
|
* 上传处理。不提供则无法上传
|
|
|
* @required true
|
|
* @required true
|
|
|
*/
|
|
*/
|
|
|
- upload: (item: UploaderAction) => void;
|
|
|
|
|
|
|
+ upload: (item: UploaderAction) => (() => void);
|
|
|
/**
|
|
/**
|
|
|
* 自定义选择文件组件,你可以调用自己的文件选择器。默认调用 ImagePicker 选择文件.
|
|
* 自定义选择文件组件,你可以调用自己的文件选择器。默认调用 ImagePicker 选择文件.
|
|
|
*/
|
|
*/
|
|
@@ -288,7 +297,7 @@ const props = withDefaults(defineProps<UploaderProps>(), {
|
|
|
itemSize: () => propGetThemeVar('UploaderItemSize', { width: 750 / 4 - 15, height: 750 / 4 - 15 }),
|
|
itemSize: () => propGetThemeVar('UploaderItemSize', { width: 750 / 4 - 15, height: 750 / 4 - 15 }),
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-const currentUpladList = ref<UploaderItem[]>(props.intitalItems || []);
|
|
|
|
|
|
|
+const currentUpladList = ref<UploaderItem[]>(props.intitalItems?.concat() || []);
|
|
|
|
|
|
|
|
//上传按钮点击
|
|
//上传按钮点击
|
|
|
function onUploadPress() {
|
|
function onUploadPress() {
|
|
@@ -323,7 +332,9 @@ function onUploadPress() {
|
|
|
size: item.size,
|
|
size: item.size,
|
|
|
state: 'notstart',
|
|
state: 'notstart',
|
|
|
isImage,
|
|
isImage,
|
|
|
- } as UploaderItem
|
|
|
|
|
|
|
+ message: '',
|
|
|
|
|
+ progress: 0,
|
|
|
|
|
+ } as UploaderItem;
|
|
|
}))
|
|
}))
|
|
|
}
|
|
}
|
|
|
//#ifdef MP
|
|
//#ifdef MP
|
|
@@ -333,9 +344,11 @@ function onUploadPress() {
|
|
|
actions: [
|
|
actions: [
|
|
|
{
|
|
{
|
|
|
name: '从相册选择',
|
|
name: '从相册选择',
|
|
|
|
|
+ subname: '从相机立即拍摄或者相册中选择照片/视频',
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
name: '从微信聊天中选择',
|
|
name: '从微信聊天中选择',
|
|
|
|
|
+ subname: '可在录音机或者WPS文档分享给文件传输助手\n然后选择任意文档文件',
|
|
|
},
|
|
},
|
|
|
],
|
|
],
|
|
|
onSelect(index, name) {
|
|
onSelect(index, name) {
|
|
@@ -348,9 +361,11 @@ function onUploadPress() {
|
|
|
type: props.chooseType || 'all',
|
|
type: props.chooseType || 'all',
|
|
|
count: props.maxUploadCount - currentUpladList.value.length,
|
|
count: props.maxUploadCount - currentUpladList.value.length,
|
|
|
success: (res) => {
|
|
success: (res) => {
|
|
|
|
|
+ LogUtils.printLog(TAG, 'info', 'chooseMessageFile', res);
|
|
|
handleFiles(res.tempFiles as { path: string; size: number; }[])
|
|
handleFiles(res.tempFiles as { path: string; size: number; }[])
|
|
|
},
|
|
},
|
|
|
fail: (e) => {
|
|
fail: (e) => {
|
|
|
|
|
+ LogUtils.printLog(TAG, 'error', 'chooseMessageFile', e);
|
|
|
reject(e);
|
|
reject(e);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -389,8 +404,8 @@ function onUploadPress() {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
items
|
|
items
|
|
|
- .then((res) => {
|
|
|
|
|
- if (props.maxFileSize > 0)
|
|
|
|
|
|
|
+ .then((res) => {
|
|
|
|
|
+ if (props.maxFileSize > 0) {
|
|
|
res = res.filter((item) => {
|
|
res = res.filter((item) => {
|
|
|
if (item.size && item.size > props.maxFileSize) {
|
|
if (item.size && item.size > props.maxFileSize) {
|
|
|
props.onOverSize?.(item);
|
|
props.onOverSize?.(item);
|
|
@@ -398,11 +413,24 @@ function onUploadPress() {
|
|
|
}
|
|
}
|
|
|
return true;
|
|
return true;
|
|
|
});
|
|
});
|
|
|
|
|
+ if (res.length === 0) {
|
|
|
|
|
+ toast.value?.text('您选择的文件过大,请重新选择!');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ res = res.map((item) => {
|
|
|
|
|
+ item.state = 'notstart';
|
|
|
|
|
+ item.message = '';
|
|
|
|
|
+ item.progress = 0;
|
|
|
|
|
+ return reactive(item);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
//添加条目
|
|
//添加条目
|
|
|
currentUpladList.value = props.maxUploadCount > 1 ? currentUpladList.value.concat(res) : res;
|
|
currentUpladList.value = props.maxUploadCount > 1 ? currentUpladList.value.concat(res) : res;
|
|
|
//自动上传
|
|
//自动上传
|
|
|
if (props.uploadWhenAdded)
|
|
if (props.uploadWhenAdded)
|
|
|
- startUploadMulitItem(res);
|
|
|
|
|
|
|
+ startUploadMulitItem(currentUpladList.value);
|
|
|
})
|
|
})
|
|
|
.catch((e) => console.warn('PickImage failed', e));
|
|
.catch((e) => console.warn('PickImage failed', e));
|
|
|
}
|
|
}
|
|
@@ -450,21 +478,26 @@ function onItemDeletePress(item: UploaderItem) {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
//更新列表条目
|
|
//更新列表条目
|
|
|
-function updateListItem(item: UploaderItem) {/*
|
|
|
|
|
- currentUpladList.value = ((prev) => {
|
|
|
|
|
- const newList = prev.concat();
|
|
|
|
|
- const index = prev.findIndex((k) => k.filePath === item.filePath);
|
|
|
|
|
- index >= 0 ? newList[index] = { ...item } : newList.push(item);
|
|
|
|
|
- return newList;
|
|
|
|
|
- })(currentUpladList.value); */
|
|
|
|
|
|
|
+function updateListItem(item: UploaderItem) {
|
|
|
emit('updateList', currentUpladList.value);
|
|
emit('updateList', currentUpladList.value);
|
|
|
}
|
|
}
|
|
|
//删除列表条目
|
|
//删除列表条目
|
|
|
function deleteListItem(item: UploaderItem) {
|
|
function deleteListItem(item: UploaderItem) {
|
|
|
currentUpladList.value = currentUpladList.value.filter((k) => k.filePath !== item.filePath);
|
|
currentUpladList.value = currentUpladList.value.filter((k) => k.filePath !== item.filePath);
|
|
|
|
|
+
|
|
|
|
|
+ //如果正在上传,先取消上传
|
|
|
|
|
+ if (item.state === 'uploading') {
|
|
|
|
|
+ item.cancelUpload?.();
|
|
|
|
|
+ item.state = 'fail';
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
//开始上传条目
|
|
//开始上传条目
|
|
|
function startUploadItem(item: UploaderItem) {
|
|
function startUploadItem(item: UploaderItem) {
|
|
|
|
|
+ if (item.state === 'uploading')
|
|
|
|
|
+ return;
|
|
|
return new Promise<void>((resolve, reject) => {
|
|
return new Promise<void>((resolve, reject) => {
|
|
|
if (item.state === 'success') {
|
|
if (item.state === 'success') {
|
|
|
resolve();
|
|
resolve();
|
|
@@ -472,7 +505,14 @@ function startUploadItem(item: UploaderItem) {
|
|
|
}
|
|
}
|
|
|
LogUtils.printLog(TAG, 'message', `调用上传文件 ${item.filePath}`);
|
|
LogUtils.printLog(TAG, 'message', `调用上传文件 ${item.filePath}`);
|
|
|
|
|
|
|
|
- props.upload({
|
|
|
|
|
|
|
+ const updateProgressDebounce = new Debounce<number>(400, (precent) => {
|
|
|
|
|
+ item.state = 'uploading';
|
|
|
|
|
+ item.message = precent ? `上传中 ${precent}%` : '上传中...';
|
|
|
|
|
+ item.progress = precent;
|
|
|
|
|
+ updateListItem(item);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ item.cancelUpload = props.upload({
|
|
|
item,
|
|
item,
|
|
|
onError(error) {
|
|
onError(error) {
|
|
|
item.state = 'fail';
|
|
item.state = 'fail';
|
|
@@ -482,6 +522,7 @@ function startUploadItem(item: UploaderItem) {
|
|
|
LogUtils.printLog(TAG, 'error', `上传文件 ${item.filePath} 失败,错误信息:${error}`);
|
|
LogUtils.printLog(TAG, 'error', `上传文件 ${item.filePath} 失败,错误信息:${error}`);
|
|
|
},
|
|
},
|
|
|
onFinish(result, message) {
|
|
onFinish(result, message) {
|
|
|
|
|
+ updateProgressDebounce.cancel();
|
|
|
item.state = 'success';
|
|
item.state = 'success';
|
|
|
item.message = message || '上传完成';
|
|
item.message = message || '上传完成';
|
|
|
item.progress = 100;
|
|
item.progress = 100;
|
|
@@ -493,10 +534,7 @@ function startUploadItem(item: UploaderItem) {
|
|
|
LogUtils.printLog(TAG, 'success', `上传文件 ${item.filePath} 成功,上传路径:${result.uploadedUrl}`);
|
|
LogUtils.printLog(TAG, 'success', `上传文件 ${item.filePath} 成功,上传路径:${result.uploadedUrl}`);
|
|
|
},
|
|
},
|
|
|
onProgress(precent) {
|
|
onProgress(precent) {
|
|
|
- item.state = 'uploading';
|
|
|
|
|
- item.message = precent ? `${precent}%` : '上传中...';
|
|
|
|
|
- item.progress = precent;
|
|
|
|
|
- updateListItem(item);
|
|
|
|
|
|
|
+ updateProgressDebounce.executeWithDelay(200, precent);
|
|
|
},
|
|
},
|
|
|
onStart(message) {
|
|
onStart(message) {
|
|
|
item.state = 'uploading';
|
|
item.state = 'uploading';
|
|
@@ -523,11 +561,21 @@ defineExpose<UploaderInstance>({
|
|
|
startUploadAll() {
|
|
startUploadAll() {
|
|
|
return startUploadMulitItem(currentUpladList.value);
|
|
return startUploadMulitItem(currentUpladList.value);
|
|
|
},
|
|
},
|
|
|
- startUpload(item) {
|
|
|
|
|
- return startUploadItem(item);
|
|
|
|
|
|
|
+ async startUpload(item) {
|
|
|
|
|
+ return await startUploadItem(item);
|
|
|
},
|
|
},
|
|
|
setList(list) {
|
|
setList(list) {
|
|
|
- currentUpladList.value = list;
|
|
|
|
|
|
|
+ const needRemoveItems = [] as string[];
|
|
|
|
|
+ for (const item of currentUpladList.value) {
|
|
|
|
|
+ if (list.findIndex(k => k.filePath === item.filePath) === -1)
|
|
|
|
|
+ needRemoveItems.push(item.filePath);
|
|
|
|
|
+ }
|
|
|
|
|
+ for (const filePath of needRemoveItems)
|
|
|
|
|
+ currentUpladList.value.splice(currentUpladList.value.findIndex(k => k.filePath === filePath), 1);
|
|
|
|
|
+ list.forEach(item => {
|
|
|
|
|
+ if (currentUpladList.value.findIndex(k => k.filePath === item.filePath) === -1)
|
|
|
|
|
+ currentUpladList.value.push(item);
|
|
|
|
|
+ });
|
|
|
},
|
|
},
|
|
|
getList() {
|
|
getList() {
|
|
|
return currentUpladList.value;
|
|
return currentUpladList.value;
|