ow
This commit is contained in:
1 parent
bd678b0112
commit
4007871658
8 files changed
+569
-23
No files matched your search
@@ -0,0 +1,258 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 文件操作面板:条目点「…」弹出的底部抽屉(图1 版式:头部信息 + 宫格动作 + 行式动作)。
|
||||
* 动作集参考旧系统 DmsFileEnquiry 的单选规则(不同类型显示不同动作):
|
||||
* 目录只有 重命名/移动/删除;文件多一个 下载;非目录可 共享给设备内的用户/发布;
|
||||
* 图片=预览、视频=播放、音频=试听(旧系统 preview/play-video/play-audio 三合一按类型取一)。
|
||||
* 组件只渲染与派发 action,业务由页面统一处理;数据契约见 @/data/custom/fileBrowser。
|
||||
*/
|
||||
import type { PropType } from "vue";
|
||||
import type { FileItem, FileKind } from "@/data/custom/fileBrowser";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed } from "vue";
|
||||
|
||||
interface ActionDef {
|
||||
key: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
/** 面板是否展开 */
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/** 目标条目;null 时渲染空面板(正常情况下 visible=true 必有值) */
|
||||
file: {
|
||||
type: Object as PropType<FileItem | null>,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
/** 抽屉关闭(遮罩/下滑) */
|
||||
(e: "close"): void;
|
||||
/** 点了某个动作,原样带上目标条目 */
|
||||
(e: "action", key: string, file: FileItem): void;
|
||||
}>();
|
||||
|
||||
const { t } = useLocale();
|
||||
|
||||
// 类型 -> 头部图标与配色,与 file-grid-list 的 kindVisual 同一套
|
||||
const kindVisual: Record<Exclude<FileKind, "folder">, { icon: string; color: string }> = {
|
||||
image: { icon: "photo-fill", color: "#0ea5e9" },
|
||||
video: { icon: "play-right-fill", color: "#8b5cf6" },
|
||||
document: { icon: "file-text-fill", color: "#3b82f6" },
|
||||
audio: { icon: "volume-up", color: "#f59e0b" },
|
||||
};
|
||||
|
||||
/** 宫格区:重命名/移动人人有;下载仅文件;删除收尾(旧系统目录也可删) */
|
||||
const gridActions = computed<ActionDef[]>(() => {
|
||||
const file = props.file;
|
||||
if (!file) {
|
||||
return [];
|
||||
}
|
||||
const actions: ActionDef[] = [
|
||||
{ key: "rename", label: t("FilePage.actionRename"), icon: "edit-pen" },
|
||||
{ key: "move", label: t("FilePage.actionMove"), icon: "arrow-right-double" },
|
||||
];
|
||||
if (file.kind !== "folder") {
|
||||
actions.unshift({ key: "download", label: t("FilePage.actionDownload"), icon: "download" });
|
||||
}
|
||||
actions.push({ key: "delete", label: t("FilePage.actionDelete"), icon: "trash" });
|
||||
return actions;
|
||||
});
|
||||
|
||||
/** 行式区:媒体预览/播放(按类型取一)+ 共享/发布(仅文件,目录在旧系统被过滤) */
|
||||
const listActions = computed<ActionDef[]>(() => {
|
||||
const file = props.file;
|
||||
if (!file) {
|
||||
return [];
|
||||
}
|
||||
const actions: ActionDef[] = [];
|
||||
// 预览/播放命名(均不超 4 字):图片=相册预览、视频=视频播放、音频=音乐播放
|
||||
if (file.kind === "image") {
|
||||
actions.push({ key: "preview", label: t("FilePage.actionAlbumShow"), icon: "eye" });
|
||||
}
|
||||
else if (file.kind === "video") {
|
||||
actions.push({ key: "play", label: t("FilePage.actionVideoPlay"), icon: "play-circle-fill" });
|
||||
}
|
||||
else if (file.kind === "audio") {
|
||||
actions.push({ key: "play", label: t("FilePage.actionAudioPlay"), icon: "volume" });
|
||||
}
|
||||
if (file.kind !== "folder") {
|
||||
actions.push({ key: "share", label: t("FilePage.actionShare"), icon: "share-fill" });
|
||||
// 发布命名:媒体类型统一「发布专辑」(不超 4 字),文档仍「发布」
|
||||
const isMedia = file.kind === "image" || file.kind === "video" || file.kind === "audio";
|
||||
actions.push({ key: "publish", label: isMedia ? t("FilePage.actionPublishAlbum") : t("FilePage.actionPublish"), icon: "volume-fill" });
|
||||
}
|
||||
return actions;
|
||||
});
|
||||
|
||||
function onAction(action: ActionDef): void {
|
||||
if (props.file) {
|
||||
emit("action", action.key, props.file);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- u-popup 只认 modelValue(v-model),绑 :show 会静默弹不出 -->
|
||||
<u-popup :model-value="visible" mode="bottom" round="24" @close="emit('close')">
|
||||
<view class="file-action-sheet">
|
||||
<!-- 头部:类型图标 + 名称/元信息 + 详情 -->
|
||||
<view class="file-action-sheet__head">
|
||||
<view v-if="file && file.kind === 'folder'" class="file-action-sheet__folder">
|
||||
<view class="file-action-sheet__folder-tab" />
|
||||
<view class="file-action-sheet__folder-body" />
|
||||
</view>
|
||||
<u-icon v-else-if="file" :name="kindVisual[file.kind as Exclude<FileKind, 'folder'>].icon" size="64"
|
||||
:color="kindVisual[file.kind as Exclude<FileKind, 'folder'>].color" />
|
||||
<view class="file-action-sheet__meta">
|
||||
<text class="file-action-sheet__name">
|
||||
{{ file?.name }}
|
||||
</text>
|
||||
<text class="file-action-sheet__sub">
|
||||
{{ file && file.kind !== 'folder' ? `${file.sizeText} · ` : '' }}{{ file?.modifiedAt }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="file-action-sheet__info" @tap="onAction({ key: 'detail', label: '', icon: '' })">
|
||||
<u-icon name="info-circle" size="40" color="#8f959e" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 宫格动作(图1 第一排大按钮) -->
|
||||
<view class="file-action-sheet__grid">
|
||||
<view v-for="action in gridActions" :key="action.key" class="file-action-sheet__grid-btn"
|
||||
@tap="onAction(action)">
|
||||
<u-icon :name="action.icon" size="44" color="#1f2329" />
|
||||
<text class="file-action-sheet__grid-text">
|
||||
{{ action.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 行式动作(图1 下方整行菜单) -->
|
||||
<view v-for="action in listActions" :key="action.key" class="file-action-sheet__row"
|
||||
@tap="onAction(action)">
|
||||
<text class="file-action-sheet__row-text">
|
||||
{{ action.label }}
|
||||
</text>
|
||||
<u-icon :name="action.icon" size="38" color="#1f2329" />
|
||||
</view>
|
||||
|
||||
<view class="file-action-sheet__safe" />
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.file-action-sheet {
|
||||
padding: 40rpx 32rpx 0;
|
||||
background-color: #ffffff;
|
||||
|
||||
&__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
padding: 0 8rpx 32rpx;
|
||||
}
|
||||
|
||||
/* 文件夹造型与 file-grid-list 同源:主体 + 左上页签 */
|
||||
&__folder {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 84rpx;
|
||||
height: 68rpx;
|
||||
}
|
||||
|
||||
&__folder-tab {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 36rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 8rpx 8rpx 0 0;
|
||||
background-color: #6cb7ff;
|
||||
}
|
||||
|
||||
&__folder-body {
|
||||
position: absolute;
|
||||
top: 12rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-radius: 10rpx;
|
||||
background: linear-gradient(135deg, #7cc0ff, #3d8bfd);
|
||||
}
|
||||
|
||||
&__meta {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__name {
|
||||
overflow: hidden;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1f2329;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__sub {
|
||||
font-size: 24rpx;
|
||||
color: #8f959e;
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
&__grid {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
&__grid-btn {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
padding: 28rpx 0;
|
||||
border-radius: 20rpx;
|
||||
background-color: #f5f6f8;
|
||||
}
|
||||
|
||||
&__grid-text {
|
||||
margin-top: 14rpx;
|
||||
font-size: 24rpx;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
&__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 16rpx;
|
||||
padding: 28rpx 32rpx;
|
||||
border-radius: 20rpx;
|
||||
background-color: #f5f6f8;
|
||||
}
|
||||
|
||||
&__row-text {
|
||||
font-size: 28rpx;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
/* 底部留白:贴屏安全区(H5 无 env 时也有 32rpx 呼吸) */
|
||||
&__safe {
|
||||
height: calc(32rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -78,7 +78,8 @@ function onCardTap(file: FileItem): void {
|
||||
</view>
|
||||
<view v-for="file in files" :key="file.id" class="fgl-card"
|
||||
:class="{ 'fgl-card--selected': selectMode && isSelected(file) }">
|
||||
<view class="fgl-card__more" @tap.stop="emit('more', file)">
|
||||
<!-- 「更多」与多选勾选圈互斥:多选模式下只留 check 圆,不再同屏两个操作图标 -->
|
||||
<view v-if="!selectMode" class="fgl-card__more" @tap.stop="emit('more', file)">
|
||||
<u-icon name="more-dot-fill" size="30" color="#b8bcc4" />
|
||||
</view>
|
||||
|
||||
|
||||
@@ -57,8 +57,10 @@ const emit = defineEmits<{
|
||||
<view class="file-tab-filter__tool" @tap="emit('toggle-view')">
|
||||
<u-icon :name="viewMode === 'grid' ? 'list' : 'grid-fill'" size="36" color="#1f2329" />
|
||||
</view>
|
||||
<!-- 多选开关:两个图标互切——未进入给勾选圆,进入后给关闭圆(高亮蓝) -->
|
||||
<view class="file-tab-filter__tool" @tap="emit('toggle-select')">
|
||||
<u-icon name="checkmark-circle" size="36" :color="selectMode ? '#2b8fff' : '#1f2329'" />
|
||||
<u-icon :name="selectMode ? 'close-circle' : 'checkmark-circle'" size="36"
|
||||
:color="selectMode ? '#2b8fff' : '#1f2329'" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 快捷功能入口:横向滚动的图标按钮组(最近访问 / 收藏 / 已下载 / 团队文件 / 共享文件)。
|
||||
* 快捷功能入口:横向滚动的图标按钮组(有家相册 / 音乐厅 / 影视厅 / 共享中心 / 回收站)。
|
||||
* 单项内容(图标名 + 渐变色 + 文案)全部由页面通过 items 传入,组件只渲染与派发点击。
|
||||
*/
|
||||
import type { PropType } from "vue";
|
||||
|
||||
@@ -59,7 +59,7 @@ function onClear(): void {
|
||||
<style lang="scss" scoped>
|
||||
/* 分区卡片风格:外层透明露渐变底,胶囊内部浅白色、留描边定形 */
|
||||
.search-input {
|
||||
padding: 2rpx 24rpx 0;
|
||||
padding: 20rpx 24rpx 0;
|
||||
}
|
||||
|
||||
.search-input__box {
|
||||
|
||||
Reference in new issue
Block a user