|
|
@@ -6,7 +6,7 @@
|
|
|
<slot
|
|
|
name="uploader"
|
|
|
:onClick="onUploadPress"
|
|
|
- :items="currentUpladList"
|
|
|
+ :items="finalUploadList"
|
|
|
>
|
|
|
<!-- #endif -->
|
|
|
<FlexView
|
|
|
@@ -16,41 +16,44 @@
|
|
|
:innerStyle="(props.itemListStyle as ViewStyle)"
|
|
|
>
|
|
|
<template
|
|
|
- v-for="(item, index) in currentUpladList"
|
|
|
- :key="index"
|
|
|
+ v-for="(item, index) in finalUploadList"
|
|
|
+ :key="item.isTitle ? `title-${item.filePath}-${index}` : `${item.filePath}-${index}`"
|
|
|
>
|
|
|
- <!-- #ifndef MP -->
|
|
|
- <slot
|
|
|
- name="uploadItem"
|
|
|
- :index="index"
|
|
|
- :item="item"
|
|
|
- :onClick="() => onItemPress(item)"
|
|
|
- :onDeleteClick="() => onItemDeletePress(item)"
|
|
|
- :style="props.itemStyle"
|
|
|
- :imageStyle="props.itemImageStyle"
|
|
|
- :itemMaskStyle="props.itemMaskStyle"
|
|
|
- :itemMaskTextStyle="props.itemMaskTextStyle"
|
|
|
- :itemSize="itemSize"
|
|
|
- :showDelete="showDelete"
|
|
|
- :defaultSource="props.itemDefaultSource"
|
|
|
- >
|
|
|
- <!-- #endif -->
|
|
|
- <UploaderListItem
|
|
|
+ <Text v-if="item.isTitle" bold :text="item.filePath" />
|
|
|
+ <template v-else>
|
|
|
+ <!-- #ifndef MP -->
|
|
|
+ <slot
|
|
|
+ name="uploadItem"
|
|
|
+ :index="index"
|
|
|
:item="item"
|
|
|
- :showDelete="showDelete && !disabled && !readonly"
|
|
|
- :isListStyle="props.listType === 'list'"
|
|
|
- :style="itemStyle"
|
|
|
- :imageStyle="itemImageStyle"
|
|
|
- :itemMaskStyle="itemMaskStyle"
|
|
|
- :itemMaskTextStyle="itemMaskTextStyle"
|
|
|
- :defaultSource="itemDefaultSource"
|
|
|
+ :onClick="() => onItemPress(item)"
|
|
|
+ :onDeleteClick="() => onItemDeletePress(item)"
|
|
|
+ :style="props.itemStyle"
|
|
|
+ :imageStyle="props.itemImageStyle"
|
|
|
+ :itemMaskStyle="props.itemMaskStyle"
|
|
|
+ :itemMaskTextStyle="props.itemMaskTextStyle"
|
|
|
:itemSize="itemSize"
|
|
|
- @click="() => onItemPress(item)"
|
|
|
- @delete="() => onItemDeletePress(item)"
|
|
|
- />
|
|
|
- <!-- #ifndef MP -->
|
|
|
- </slot>
|
|
|
- <!-- #endif -->
|
|
|
+ :showDelete="showDelete"
|
|
|
+ :defaultSource="props.itemDefaultSource"
|
|
|
+ >
|
|
|
+ <!-- #endif -->
|
|
|
+ <UploaderListItem
|
|
|
+ :item="item"
|
|
|
+ :showDelete="showDelete && !disabled && !readonly"
|
|
|
+ :isListStyle="props.listType === 'list'"
|
|
|
+ :style="itemStyle"
|
|
|
+ :imageStyle="itemImageStyle"
|
|
|
+ :itemMaskStyle="itemMaskStyle"
|
|
|
+ :itemMaskTextStyle="itemMaskTextStyle"
|
|
|
+ :defaultSource="itemDefaultSource"
|
|
|
+ :itemSize="itemSize"
|
|
|
+ @click="() => onItemPress(item)"
|
|
|
+ @delete="() => onItemDeletePress(item)"
|
|
|
+ />
|
|
|
+ <!-- #ifndef MP -->
|
|
|
+ </slot>
|
|
|
+ <!-- #endif -->
|
|
|
+ </template>
|
|
|
</template>
|
|
|
<slot v-if="currentUpladList.length < maxUploadCount && !disabled && !readonly" name="addButton" :onUploadPress="onUploadPress" :itemSize="itemSize">
|
|
|
<UploaderListAddItem :itemSize="itemSize" :style="itemStyle" @click="onUploadPress" :isListStyle="props.listType === 'list'" />
|
|
|
@@ -70,7 +73,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { reactive, ref } from 'vue';
|
|
|
+import { computed, reactive, ref } from 'vue';
|
|
|
import { propGetThemeVar, useTheme, type TextStyle, type ViewStyle } from '../theme/ThemeDefine';
|
|
|
import { Debounce, LogUtils } from '@imengyu/imengyu-utils';
|
|
|
import { actionSheet } from '../dialog/CommonRoot';
|
|
|
@@ -183,6 +186,11 @@ export interface UploaderProps {
|
|
|
* @default true
|
|
|
*/
|
|
|
formMessage?: boolean;
|
|
|
+ /**
|
|
|
+ * 列表是否按类型分组,仅当listType为list时有效
|
|
|
+ * @default false
|
|
|
+ */
|
|
|
+ groupType?: boolean;
|
|
|
|
|
|
/**
|
|
|
* 上传处理。不提供则无法上传
|
|
|
@@ -285,11 +293,78 @@ const props = withDefaults(defineProps<UploaderProps>(), {
|
|
|
uploadQueueMode: 'all',
|
|
|
listType: 'grid',
|
|
|
chooseType: 'image',
|
|
|
+ groupType: false,
|
|
|
itemSize: () => propGetThemeVar('UploaderItemSize', { width: 750 / 4 - 15, height: 750 / 4 - 15 }),
|
|
|
});
|
|
|
|
|
|
const currentUpladList = ref<UploaderItem[]>(props.intitalItems?.concat() || []);
|
|
|
|
|
|
+type UploadDisplayKind = 'image' | 'video' | 'audio' | 'document' | 'other';
|
|
|
+
|
|
|
+const UPLOAD_GROUP_META: { kind: UploadDisplayKind; label: string }[] = [
|
|
|
+ { kind: 'image', label: '图片' },
|
|
|
+ { kind: 'video', label: '视频' },
|
|
|
+ { kind: 'audio', label: '音频' },
|
|
|
+ { kind: 'document', label: '文档' },
|
|
|
+ { kind: 'other', label: '其他' },
|
|
|
+];
|
|
|
+
|
|
|
+function isImagePath(path: string) {
|
|
|
+ return path.match(/\.(jpg|jpeg|png|gif|webp)$/i) !== null;
|
|
|
+}
|
|
|
+
|
|
|
+function getUploadItemDisplayKind(item: UploaderItem): UploadDisplayKind {
|
|
|
+ if (item.isTitle)
|
|
|
+ return 'other';
|
|
|
+ const path = item.previewPath || item.uploadedPath || item.filePath;
|
|
|
+ if (item.isImage || isImagePath(path))
|
|
|
+ return 'image';
|
|
|
+ if (/\.(mp4|m4v|mov|webm|avi|wmv|flv|mkv|3gp|3g2|ts)$/i.test(path))
|
|
|
+ return 'video';
|
|
|
+ if (/\.(mp3|wav|m4a|aac|ogg|opus|flac|wma|amr|aiff|aif)$/i.test(path))
|
|
|
+ return 'audio';
|
|
|
+ if (/\.(pdf|doc|docx|xls|xlsx|ppt|pptx|txt|rtf|csv|md)$/i.test(path))
|
|
|
+ return 'document';
|
|
|
+ return 'other';
|
|
|
+}
|
|
|
+
|
|
|
+function makeUploadTitleItem(label: string): UploaderItem {
|
|
|
+ return {
|
|
|
+ filePath: label,
|
|
|
+ isTitle: true,
|
|
|
+ state: 'success',
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+/** 列表模式 + groupType:按文件类型插入分组标题行(isTitle) */
|
|
|
+function buildGroupedUploadList(items: UploaderItem[]): UploaderItem[] {
|
|
|
+ const files = items.filter((i) => !i.isTitle);
|
|
|
+ if (files.length === 0)
|
|
|
+ return [];
|
|
|
+ const buckets = new Map<UploadDisplayKind, UploaderItem[]>();
|
|
|
+ for (const { kind } of UPLOAD_GROUP_META)
|
|
|
+ buckets.set(kind, []);
|
|
|
+ for (const item of files) {
|
|
|
+ const kind = getUploadItemDisplayKind(item);
|
|
|
+ buckets.get(kind)!.push(item);
|
|
|
+ }
|
|
|
+ const out: UploaderItem[] = [];
|
|
|
+ for (const { kind, label } of UPLOAD_GROUP_META) {
|
|
|
+ const group = buckets.get(kind)!;
|
|
|
+ if (group.length === 0)
|
|
|
+ continue;
|
|
|
+ out.push(makeUploadTitleItem(label));
|
|
|
+ out.push(...group);
|
|
|
+ }
|
|
|
+ return out;
|
|
|
+}
|
|
|
+
|
|
|
+const finalUploadList = computed(() => {
|
|
|
+ if (props.groupType && props.listType === 'list')
|
|
|
+ return buildGroupedUploadList(currentUpladList.value);
|
|
|
+ return currentUpladList.value;
|
|
|
+});
|
|
|
+
|
|
|
//上传按钮点击
|
|
|
function onUploadPress() {
|
|
|
if (props.disabled || props.readonly)
|
|
|
@@ -487,11 +562,6 @@ function deleteListItem(item: UploaderItem) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-function isImagePath(path: string) {
|
|
|
- return path.match(/\.(jpg|jpeg|png|gif|webp)$/) !== null;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
//开始上传条目
|
|
|
function startUploadItem(item: UploaderItem) {
|
|
|
if (item.state === 'uploading')
|