220 lines
6.8 KiB
Vue
220 lines
6.8 KiB
Vue
<script setup lang="ts">
|
||
/**
|
||
* 通用条目动作面板:头部文件卡(类型图标 + 名称/元信息)+ 宫格动作 + 取消通栏。
|
||
* file-action-sheet 的通用版——动作集由调用方按业务喂进来(共享中心的下载/取消共享等),
|
||
* 组件只渲染与派发,不认识 service;视觉与网盘动作面板保持一致,替代系统 showActionSheet。
|
||
*/
|
||
import type { PropType } from "vue";
|
||
import type { FileItem, FileKind } from "@/data/custom/fileBrowser";
|
||
import { useLocale } from "uview-pro";
|
||
import { useThemeTokens } from "@/composables";
|
||
|
||
export interface ItemSheetAction {
|
||
key: string;
|
||
label: string;
|
||
icon: string;
|
||
/** 危险动作(取消共享/删除):图标与文字标红 */
|
||
danger?: boolean;
|
||
}
|
||
|
||
const props = defineProps({
|
||
/** 面板是否展开 */
|
||
visible: {
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
/** 目标条目;null 时渲染空面板(正常情况下 visible=true 必有值) */
|
||
file: {
|
||
type: Object as PropType<FileItem | null>,
|
||
default: null,
|
||
},
|
||
/** 本次条目可用的动作集(调用方按类型派生) */
|
||
actions: {
|
||
type: Array as PropType<ItemSheetAction[]>,
|
||
default: () => [],
|
||
},
|
||
});
|
||
|
||
const emit = defineEmits<{
|
||
/** 面板关闭(遮罩/取消按钮) */
|
||
(e: "close"): void;
|
||
/** 点了某个动作,原样带上目标条目 */
|
||
(e: "action", key: string, file: FileItem): void;
|
||
}>();
|
||
|
||
const { t } = useLocale();
|
||
|
||
// 文件夹图形随主题(与 file-action-sheet 同一强调渐变)
|
||
const { accentGradient, accentSoft } = useThemeTokens();
|
||
|
||
// 类型 -> 头部图标与配色,与 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" },
|
||
};
|
||
|
||
function onAction(action: ItemSheetAction): 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="item-action-sheet">
|
||
<!-- 头部白卡:类型图标 + 名称/元信息 -->
|
||
<view class="item-action-sheet__head">
|
||
<view v-if="file && file.kind === 'folder'" class="item-action-sheet__folder">
|
||
<view class="item-action-sheet__folder-tab" :style="{ background: accentSoft }" />
|
||
<view class="item-action-sheet__folder-body" :style="{ background: accentGradient }" />
|
||
</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="item-action-sheet__meta">
|
||
<text class="item-action-sheet__name">
|
||
{{ file?.name }}
|
||
</text>
|
||
<text class="item-action-sheet__sub">
|
||
{{ file && file.kind !== 'folder' ? `${file.sizeText} · ` : '' }}{{ file?.modifiedAt }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 宫格动作:白卡上图标下文字,危险动作标红 -->
|
||
<view class="item-action-sheet__grid">
|
||
<view v-for="action in actions" :key="action.key" class="item-action-sheet__grid-btn"
|
||
@tap="onAction(action)">
|
||
<u-icon :name="action.icon" size="44" :color="action.danger ? '#f0413e' : '#1f2329'" />
|
||
<text class="item-action-sheet__grid-text"
|
||
:style="action.danger ? { color: '#f0413e' } : undefined">
|
||
{{ action.label }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 取消通栏:退出锚点 -->
|
||
<view class="item-action-sheet__cancel" @tap="emit('close')">
|
||
<text class="item-action-sheet__cancel-text">
|
||
{{ t("FilePage.folderCancel") }}
|
||
</text>
|
||
</view>
|
||
|
||
<view class="item-action-sheet__safe" />
|
||
</view>
|
||
</u-popup>
|
||
</template>
|
||
|
||
<style lang="scss" scoped>
|
||
/* 底部抽屉统一风格(对齐 file-action-sheet):浅灰面板底 + 白色圆角卡分块 */
|
||
.item-action-sheet {
|
||
padding: 32rpx 24rpx 0;
|
||
background-color: #f2f3f5;
|
||
|
||
&__head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 24rpx;
|
||
margin-bottom: 20rpx;
|
||
padding: 28rpx 32rpx;
|
||
border-radius: 20rpx;
|
||
background-color: #ffffff;
|
||
}
|
||
|
||
/* 文件夹造型与 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;
|
||
}
|
||
|
||
&__folder-body {
|
||
position: absolute;
|
||
top: 12rpx;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
border-radius: 10rpx;
|
||
}
|
||
|
||
&__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;
|
||
}
|
||
|
||
&__grid {
|
||
display: flex;
|
||
gap: 16rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
/* 宫格钮:白卡、上图标下文字居中,动作数 ≤4 时平分一行 */
|
||
&__grid-btn {
|
||
display: flex;
|
||
flex: 1;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 28rpx 0;
|
||
border-radius: 20rpx;
|
||
background-color: #ffffff;
|
||
}
|
||
|
||
&__grid-text {
|
||
margin-top: 14rpx;
|
||
font-size: 24rpx;
|
||
color: #1f2329;
|
||
}
|
||
|
||
/* 取消通栏:与内容隔一段灰底收尾,防误点动作区 */
|
||
&__cancel {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: 28rpx;
|
||
padding: 26rpx 0;
|
||
border-radius: 20rpx;
|
||
background-color: #ffffff;
|
||
}
|
||
|
||
&__cancel-text {
|
||
font-size: 28rpx;
|
||
color: #5c626b;
|
||
}
|
||
|
||
/* 底部留白:贴屏安全区(H5 无 env 时也有 32rpx 呼吸) */
|
||
&__safe {
|
||
height: calc(32rpx + env(safe-area-inset-bottom));
|
||
}
|
||
}
|
||
</style>
|