ow
This commit is contained in:
1 parent
e740ec77ec
commit
ceb3e7beec
20 files changed
+2280
-138
No files matched your search
@@ -14,15 +14,29 @@
|
||||
* 目录导航:点文件夹下钻;子目录态隐藏底部 tabbar,返回箭头/面包屑退层级。
|
||||
* 权限暂用 UserOperation(见 routes.ts 的 PAGE_PERMISSIONS);useAuthGuard 兜冷启动直达。
|
||||
*/
|
||||
import type { FolderRow } from "@/components/folder-move-picker/folder-move-picker.vue";
|
||||
import type { PickerOption } from "@/components/option-picker/option-picker.vue";
|
||||
import type { FileCategory, FileItem, FileKind, FileTabItem, QuickEntryItem } from "@/data/custom/fileBrowser";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { useAuthGuard, useDmsFiles, useThemeGradient } from "@/composables";
|
||||
import { DmsFileEntity } from "@/data/auto/dmsFileEntity";
|
||||
import { FamilyGroupFolderEntity } from "@/data/auto/familyGroupFolderEntity";
|
||||
import { FamilyGroupManagerUserSearchCriteria } from "@/data/auto/familyGroupManagerUserSearchCriteria";
|
||||
import { PublishAudioSearchCriteria } from "@/data/auto/publishAudioSearchCriteria";
|
||||
import { PublishData } from "@/data/auto/publishData";
|
||||
import { PublishPhotoSearchCriteria } from "@/data/auto/publishPhotoSearchCriteria";
|
||||
import { PublishVideoSearchCriteria } from "@/data/auto/publishVideoSearchCriteria";
|
||||
import { pageUrl } from "@/router/routes";
|
||||
import dmsFileService from "@/service/master/dmsFileService";
|
||||
import familyGroupService from "@/service/master/familyGroupService";
|
||||
import publishAudioService from "@/service/master/publishAudioService";
|
||||
import publishPhotoService from "@/service/master/publishPhotoService";
|
||||
import publishVideoService from "@/service/master/publishVideoService";
|
||||
import { useUploadQueueStore } from "@/stores/uploadQueue";
|
||||
import { chooseFiles } from "@/utils/chooseFiles";
|
||||
import { fetchFileBytes, saveFileBytes, toPlayableUrl } from "@/utils/dmsBytes";
|
||||
import { clearPlayback, setPlayback } from "@/utils/playbackBus";
|
||||
import { transferGlyph } from "@/utils/transferGlyph";
|
||||
|
||||
const { t } = useLocale();
|
||||
@@ -30,7 +44,7 @@ const { t } = useLocale();
|
||||
// 页面底渐变跟随主题(派生逻辑在 composables/useThemeGradient,多页共用)
|
||||
const { themeBackground } = useThemeGradient();
|
||||
|
||||
const { files, loading, errorText, pathStack, atRoot, init, openTrail, retry, openFolder, back, goTo, search } = useDmsFiles();
|
||||
const { files, records, loading, errorText, pathStack, atRoot, init, openTrail, retry, openFolder, back, goTo, search } = useDmsFiles();
|
||||
|
||||
// 上传队列(全局 store):入队在本页、胶囊读它的进行中计数、守卫回调消费它的面包屑导航请求
|
||||
const uploadQueue = useUploadQueueStore();
|
||||
@@ -167,6 +181,11 @@ function onSearch(value: string): void {
|
||||
}
|
||||
|
||||
function onQuickClick(key: string): void {
|
||||
// 回收站已落地(pages/trash);其余入口仍是占位提示
|
||||
if (key === "trash") {
|
||||
uni.navigateTo({ url: pageUrl("trash") });
|
||||
return;
|
||||
}
|
||||
const entry = quickEntries.value.find(e => e.key === key);
|
||||
toast(entry ? `${entry.label} · ${t("FilePage.todoTip")}` : t("FilePage.todoTip"));
|
||||
}
|
||||
@@ -266,6 +285,16 @@ const previewIcon = computed(() => {
|
||||
return pickedKind.value === "audio" ? "volume" : "eye";
|
||||
});
|
||||
|
||||
/** 预览按钮的分流动作 key:视频/音频走播放页(play),图片走相册连览(preview);
|
||||
* 文档没有移动端预览器,不显示该按钮 */
|
||||
const previewKey = computed(() => {
|
||||
const kind = pickedKind.value;
|
||||
if (kind === "video" || kind === "audio") {
|
||||
return "play";
|
||||
}
|
||||
return kind === "image" ? "preview" : "";
|
||||
});
|
||||
|
||||
/** 选中含文件即可下载(旧系统 onDownloadSelected 就是滤掉目录逐个循环下载) */
|
||||
const hasFileSelected = computed(() => selectedFiles.value.some(f => f.kind !== "folder"));
|
||||
|
||||
@@ -289,8 +318,8 @@ const multiActionsBottom = computed<MultiAction[]>(() => {
|
||||
const actions: MultiAction[] = [];
|
||||
const picked = selectedFiles.value;
|
||||
if (picked.length) {
|
||||
if (canSharePublish.value) {
|
||||
actions.push({ key: "preview", label: previewLabel.value, icon: previewIcon.value });
|
||||
if (canSharePublish.value && previewKey.value) {
|
||||
actions.push({ key: previewKey.value, label: previewLabel.value, icon: previewIcon.value });
|
||||
}
|
||||
if (picked.length === 1 && picked[0].kind !== "folder") {
|
||||
actions.push({ key: "rename", label: t("FilePage.actionRename"), icon: "edit-pen" });
|
||||
@@ -301,13 +330,13 @@ const multiActionsBottom = computed<MultiAction[]>(() => {
|
||||
return actions;
|
||||
});
|
||||
|
||||
/** 多选动作:取消直接退出多选,其余待真实接口接入后在此分流 */
|
||||
/** 多选动作:取消退出多选,其余与单选面板同一入口(runAction 按 key 分流) */
|
||||
function onMultiAction(key: string): void {
|
||||
if (key === "cancel") {
|
||||
onToggleSelect();
|
||||
return;
|
||||
}
|
||||
toast(`${key} · ${t("FilePage.todoTip")}`);
|
||||
runAction(key, selectedFiles.value);
|
||||
}
|
||||
|
||||
function onToggleSelectItem(id: number): void {
|
||||
@@ -321,13 +350,22 @@ function onToggleSelectItem(id: number): void {
|
||||
|
||||
// ===== 列表条目 / 悬浮按钮 =====
|
||||
async function onOpen(file: FileItem): Promise<void> {
|
||||
// 文件夹下钻走真实接口;打开文件(预览/播放)尚未实现,先占位
|
||||
// 文件夹下钻走真实接口;文件点开即预览/播放(旧系统 onViewPic 同款分流)
|
||||
if (await openFolder(file)) {
|
||||
keyword.value = "";
|
||||
selectedIds.value = [];
|
||||
return;
|
||||
}
|
||||
toast(`${file.name} · ${t("FilePage.todoTip")}`);
|
||||
if (file.kind === "image") {
|
||||
actionPreview([file]);
|
||||
}
|
||||
else if (file.kind === "video" || file.kind === "audio") {
|
||||
actionPlay(file);
|
||||
}
|
||||
else {
|
||||
// 文档没有移动端预览器,点开按下载处理(比占位 toast 实用)
|
||||
actionDownload([file]);
|
||||
}
|
||||
}
|
||||
|
||||
// 「…」单选操作面板:记目标条目即展开(面板内动作按 kind 派生,见 file-action-sheet)
|
||||
@@ -337,10 +375,436 @@ function onItemMore(file: FileItem): void {
|
||||
actionFile.value = file;
|
||||
}
|
||||
|
||||
/** 面板动作回调:真实接口逐个接入后在此分流,当前统一占位提示并收起面板 */
|
||||
/**
|
||||
* ===== 条目动作层(单选面板与多选栏共用,runAction 是唯一入口)=====
|
||||
* 调用链对齐旧系统 DmsFileEnquiry:删除 Delete、重命名 UpdateDmsFolder、
|
||||
* 移动 MoveDmsFolder、下载=分片取回拼整(utils/dmsBytes)、共享 CreateFamilyGroupFolder、
|
||||
* 发布 InsertPublishXxxDetail;目标记录从 useDmsFiles 的 records 拿全字段。
|
||||
*/
|
||||
|
||||
/** 动作在途标记:showLoading 全屏遮罩外再兑一层防连点 */
|
||||
const actionBusy = ref(false);
|
||||
|
||||
/** 包一层全屏 loading:接口异常统一 toast(业务异常主拦截器已弹模态,这里兜网络/流程错误) */
|
||||
async function withBusy(task: () => Promise<void>, tip?: string): Promise<void> {
|
||||
if (actionBusy.value) {
|
||||
return;
|
||||
}
|
||||
actionBusy.value = true;
|
||||
uni.showLoading({ title: tip || t("FilePage.pleaseWait"), mask: true });
|
||||
try {
|
||||
await task();
|
||||
}
|
||||
catch (error: any) {
|
||||
toast(error?.message || t("FilePage.actionFailed"));
|
||||
}
|
||||
finally {
|
||||
uni.hideLoading();
|
||||
actionBusy.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** showModal 包 Promise(删除确认) */
|
||||
function confirmDialog(title: string, content: string): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
uni.showModal({
|
||||
title,
|
||||
content,
|
||||
success: res => resolve(!!res.confirm),
|
||||
fail: () => resolve(false),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 列表原始记录(全字段):OrderKey/Url/系统 FileName 等隐藏字段只有它里有 */
|
||||
function recordOf(file: FileItem) {
|
||||
return records.value.get(file.id);
|
||||
}
|
||||
|
||||
// ----- 删除:旧系统 onDelete 逐条 Delete(软删,回收站可 RestoreDmsFile 恢复)-----
|
||||
async function actionDelete(targets: FileItem[]): Promise<void> {
|
||||
const ok = await confirmDialog(
|
||||
t("FilePage.actionDelete"),
|
||||
t("FilePage.deleteConfirmText").replace("{count}", String(targets.length)),
|
||||
);
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
await withBusy(async () => {
|
||||
for (const file of targets) {
|
||||
await dmsFileService.Delete(file.id);
|
||||
}
|
||||
toast(t("FilePage.deleteDone"));
|
||||
selectedIds.value = [];
|
||||
retry();
|
||||
});
|
||||
}
|
||||
|
||||
// ----- 重命名:命名对话框复用(预填当前名),提交 UpdateDmsFolder(目录同款,旧 onAmend)-----
|
||||
const renameVisible = ref(false);
|
||||
const renameSubmitting = ref(false);
|
||||
const renameTarget = ref<FileItem | null>(null);
|
||||
|
||||
function actionRename(targets: FileItem[]): void {
|
||||
const [first] = targets;
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
renameTarget.value = first;
|
||||
renameVisible.value = true;
|
||||
}
|
||||
|
||||
async function onRenameConfirm(name: string): Promise<void> {
|
||||
const target = renameTarget.value;
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
if (/[\\/]/.test(name)) {
|
||||
toast(t("FilePage.folderInvalid"));
|
||||
return;
|
||||
}
|
||||
const record = recordOf(target);
|
||||
renameSubmitting.value = true;
|
||||
try {
|
||||
const entity = new DmsFileEntity();
|
||||
entity.FileId = target.id;
|
||||
entity.FileName = name;
|
||||
// 旧系统取值:FilePath 是所在目录 FolderLocalName,根目录(CreatedBy==FolderLocalName)传空串
|
||||
const folderLocal = record?.FolderLocalName ?? "";
|
||||
entity.FilePath = record && record.CreatedBy === folderLocal ? "" : folderLocal;
|
||||
await dmsFileService.UpdateDmsFolder(entity);
|
||||
renameVisible.value = false;
|
||||
toast(t("FilePage.renameDone"));
|
||||
retry();
|
||||
}
|
||||
catch (error: any) {
|
||||
if (error?.message) {
|
||||
toast(error.message);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
renameSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ----- 移动:folder-move-picker 网盘式逐级下钻选目标;旧系统仅单选,这里多选批量逐个挪 -----
|
||||
const moveVisible = ref(false);
|
||||
const moveTargets = ref<FileItem[]>([]);
|
||||
|
||||
function actionMove(targets: FileItem[]): void {
|
||||
if (!targets.length) {
|
||||
return;
|
||||
}
|
||||
moveTargets.value = [...targets];
|
||||
moveVisible.value = true;
|
||||
}
|
||||
|
||||
/** 移动文件夹时把自己从候选里剔除(其子树随之不可见);文件无需剔除 */
|
||||
const moveExcludeName = computed(() => {
|
||||
const [first] = moveTargets.value;
|
||||
return first && first.kind === "folder" ? recordOf(first)?.FileName ?? "" : "";
|
||||
});
|
||||
|
||||
/** 单选且非目录时禁掉「原地移动」(多选来源目录可能不同,不做置灰) */
|
||||
const moveSourcePath = computed(() => {
|
||||
const [first] = moveTargets.value;
|
||||
return moveTargets.value.length === 1 ? recordOf(first)?.FilePath ?? "" : "";
|
||||
});
|
||||
|
||||
async function onMovePick(row: FolderRow): Promise<void> {
|
||||
moveVisible.value = false;
|
||||
const targets = moveTargets.value;
|
||||
await withBusy(async () => {
|
||||
for (const file of targets) {
|
||||
const record = recordOf(file);
|
||||
const entity = new DmsFileEntity();
|
||||
entity.FileId = file.id;
|
||||
// 旧系统 onMove:FileName 传 OrgFileName,FilePath 是目标目录系统名
|
||||
entity.FileName = record?.OrgFileName ?? file.name;
|
||||
entity.FilePath = row.folderName;
|
||||
await dmsFileService.MoveDmsFolder(entity);
|
||||
}
|
||||
toast(t("FilePage.moveDone"));
|
||||
selectedIds.value = [];
|
||||
retry();
|
||||
});
|
||||
}
|
||||
|
||||
// ----- 下载:视频 Url 直取、其余分片拼整;逐个串行(浏览器拦并发多文件)-----
|
||||
async function actionDownload(targets: FileItem[]): Promise<void> {
|
||||
const picked = targets.filter(f => f.kind !== "folder");
|
||||
if (!picked.length) {
|
||||
toast(t("FilePage.downloadNoFile"));
|
||||
return;
|
||||
}
|
||||
await withBusy(async () => {
|
||||
for (const file of picked) {
|
||||
const record = recordOf(file);
|
||||
if (record) {
|
||||
await saveFileBytes(await fetchFileBytes(record));
|
||||
}
|
||||
}
|
||||
toast(t("FilePage.downloadDone"));
|
||||
}, t("FilePage.downloading"));
|
||||
}
|
||||
|
||||
/** 图片预览:单选一张、多选多图相册连览。
|
||||
* 走原图拼整通道(旧 MyComputer @preview-image → GetOrgPictureFile 就是分片拼整);
|
||||
* DownloadFilePreview 是旧缩略图通道,依赖上传时生成的 scale 目录,本地库没有缩略图只会 400 */
|
||||
async function actionPreview(targets: FileItem[]): Promise<void> {
|
||||
const images = targets.filter(f => f.kind === "image");
|
||||
if (!images.length) {
|
||||
toast(t("FilePage.previewNoImage"));
|
||||
return;
|
||||
}
|
||||
await withBusy(async () => {
|
||||
const urls: string[] = [];
|
||||
for (const file of images) {
|
||||
const record = recordOf(file);
|
||||
if (record) {
|
||||
urls.push(toPlayableUrl(await fetchFileBytes(record)));
|
||||
}
|
||||
}
|
||||
if (urls.length) {
|
||||
uni.previewImage({ urls, current: urls[0] });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 视频/音频播放:视频有直链 Url 直接进播放页,否则字节拼整后本地播(H5 blob: 地址经 query 传递) */
|
||||
async function actionPlay(target: FileItem): Promise<void> {
|
||||
const record = recordOf(target);
|
||||
if (!record) {
|
||||
return;
|
||||
}
|
||||
if (target.kind === "video" && record.Url) {
|
||||
goPlay("video", target.name, record.Url);
|
||||
return;
|
||||
}
|
||||
await withBusy(async () => {
|
||||
const payload = await fetchFileBytes(record);
|
||||
goPlay(target.kind === "video" ? "video" : "audio", target.name, toPlayableUrl(payload));
|
||||
}, t("FilePage.loadingMedia"));
|
||||
}
|
||||
|
||||
function goPlay(type: "video" | "audio", name: string, url: string): void {
|
||||
// 播放地址走模块级总线传递,query 只带类型与展示名(blob 地址经 query 二次编码会跳转失败);
|
||||
// fail 不兜底就是静默不跳,这里把原因顶出来
|
||||
setPlayback({ type, name, url });
|
||||
uni.navigateTo({
|
||||
url: `${pageUrl("mediaPlay")}?type=${type}&name=${encodeURIComponent(name)}`,
|
||||
fail: (err: any) => {
|
||||
clearPlayback();
|
||||
toast(err?.errMsg || t("FilePage.actionFailed"));
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ----- 共享家人:选收藏夹里的家人目录,组装 FamilyGroupFolderEntity 批量建(旧 handleSelectShare)-----
|
||||
const shareVisible = ref(false);
|
||||
const shareLoading = ref(false);
|
||||
const shareItems = ref<PickerOption[]>([]);
|
||||
/** 展示用 items 之外的组装字段(FamilyName/TopFolderName 提交才需要) */
|
||||
const shareFolders = ref<Array<{ key: string; familyName: string; topFolderName: string }>>([]);
|
||||
const shareTargets = ref<FileItem[]>([]);
|
||||
|
||||
async function actionShare(targets: FileItem[]): Promise<void> {
|
||||
const picked = targets.filter(f => f.kind !== "folder");
|
||||
if (!picked.length) {
|
||||
toast(t("FilePage.shareNoFile"));
|
||||
return;
|
||||
}
|
||||
shareTargets.value = picked;
|
||||
shareVisible.value = true;
|
||||
shareLoading.value = true;
|
||||
try {
|
||||
const criteria = new FamilyGroupManagerUserSearchCriteria();
|
||||
criteria.Filter = [];
|
||||
criteria.limit = 999;
|
||||
criteria.page = 1;
|
||||
criteria.GridColumns = [];
|
||||
const data = await familyGroupService.FamilyFolderFavoriteBySearchCriteria(criteria);
|
||||
const favorites = data?.Favorites ?? [];
|
||||
shareFolders.value = favorites.map(f => ({
|
||||
key: f.FolderName,
|
||||
familyName: f.FamilyName,
|
||||
topFolderName: f.TopFolderName,
|
||||
}));
|
||||
shareItems.value = favorites.map(f => ({
|
||||
key: f.FolderName,
|
||||
label: `${f.FamilyLocalName} / ${f.FolderLocalName}`,
|
||||
sub: f.Notice,
|
||||
}));
|
||||
}
|
||||
catch (error: any) {
|
||||
toast(error?.message || t("FilePage.actionFailed"));
|
||||
}
|
||||
finally {
|
||||
shareLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function onShareConfirm(key: string): Promise<void> {
|
||||
const target = shareFolders.value.find(f => f.key === key);
|
||||
shareVisible.value = false;
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const filesToShare = shareTargets.value;
|
||||
await withBusy(async () => {
|
||||
const rows = filesToShare.map((file) => {
|
||||
const record = recordOf(file);
|
||||
const fgp = new FamilyGroupFolderEntity();
|
||||
fgp.FamilyName = target.familyName;
|
||||
fgp.TopFolderName = target.topFolderName;
|
||||
// 旧系统组装:FolderName 文件系统名、FolderLocalName 原始名、FilePath 目标共享目录
|
||||
fgp.FolderName = record?.FileName ?? file.path;
|
||||
fgp.FolderLocalName = record?.OrgFileName ?? file.name;
|
||||
fgp.FilePath = target.key;
|
||||
return fgp;
|
||||
});
|
||||
await dmsFileService.CreateFamilyGroupFolder(rows);
|
||||
toast(t("FilePage.shareDone"));
|
||||
selectedIds.value = [];
|
||||
});
|
||||
}
|
||||
|
||||
// ----- 发布:按媒体类型选专辑,InsertPublishXxxDetail 逐文件入集(旧 handleSelectPublish*)-----
|
||||
const publishVisible = ref(false);
|
||||
const publishLoading = ref(false);
|
||||
const publishItems = ref<PickerOption[]>([]);
|
||||
const publishTargets = ref<FileItem[]>([]);
|
||||
const publishKind = ref<"image" | "video" | "audio">("image");
|
||||
|
||||
/** 发布弹窗标题按媒体类型取相册/音乐集/影集名目(旧系统三个独立 Enquiry 弹窗) */
|
||||
const publishTitle = computed(() => {
|
||||
if (publishKind.value === "video") {
|
||||
return t("FilePage.publishPickVideo");
|
||||
}
|
||||
return publishKind.value === "audio" ? t("FilePage.publishPickAudio") : t("FilePage.publishPickPhoto");
|
||||
});
|
||||
|
||||
async function actionPublish(targets: FileItem[]): Promise<void> {
|
||||
const picked = targets.filter(f => f.kind !== "folder");
|
||||
if (!picked.length) {
|
||||
toast(t("FilePage.publishNoFile"));
|
||||
return;
|
||||
}
|
||||
const [first] = picked;
|
||||
if (first.kind !== "image" && first.kind !== "video" && first.kind !== "audio") {
|
||||
toast(t("FilePage.publishUnsupported"));
|
||||
return;
|
||||
}
|
||||
// 旧系统按同类型发布:混选只发与第一个文件同类型的条目
|
||||
publishKind.value = first.kind;
|
||||
publishTargets.value = picked.filter(f => f.kind === first.kind);
|
||||
publishVisible.value = true;
|
||||
publishLoading.value = true;
|
||||
try {
|
||||
let names: string[] = [];
|
||||
if (first.kind === "image") {
|
||||
const criteria = new PublishPhotoSearchCriteria();
|
||||
criteria.Filter = [];
|
||||
criteria.limit = 999;
|
||||
criteria.page = 1;
|
||||
criteria.GridColumns = [];
|
||||
const data = await publishPhotoService.FindBySearchCriteria(criteria);
|
||||
names = (data?.Publishs ?? []).map(p => p.PublishName);
|
||||
}
|
||||
else if (first.kind === "video") {
|
||||
const criteria = new PublishVideoSearchCriteria();
|
||||
criteria.Filter = [];
|
||||
criteria.limit = 999;
|
||||
criteria.page = 1;
|
||||
criteria.GridColumns = [];
|
||||
const data = await publishVideoService.FindBySearchCriteria(criteria);
|
||||
names = (data?.Datas ?? []).map(p => p.PublishName);
|
||||
}
|
||||
else {
|
||||
const criteria = new PublishAudioSearchCriteria();
|
||||
criteria.Filter = [];
|
||||
criteria.limit = 999;
|
||||
criteria.page = 1;
|
||||
criteria.GridColumns = [];
|
||||
const data = await publishAudioService.FindBySearchCriteria(criteria);
|
||||
names = (data?.Datas ?? []).map(p => p.PublishName);
|
||||
}
|
||||
publishItems.value = names.filter(Boolean).map(n => ({ key: n, label: n }));
|
||||
}
|
||||
catch (error: any) {
|
||||
toast(error?.message || t("FilePage.actionFailed"));
|
||||
}
|
||||
finally {
|
||||
publishLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function onPublishConfirm(name: string): Promise<void> {
|
||||
publishVisible.value = false;
|
||||
const targets = publishTargets.value;
|
||||
const kind = publishKind.value;
|
||||
await withBusy(async () => {
|
||||
for (const file of targets) {
|
||||
const data = new PublishData();
|
||||
data.FileId = file.id;
|
||||
data.PublishName = name;
|
||||
if (kind === "image") {
|
||||
await publishPhotoService.InsertPublishPhotoDetail(data);
|
||||
}
|
||||
else if (kind === "video") {
|
||||
await publishVideoService.InsertPublishVideoDetail(data);
|
||||
}
|
||||
else {
|
||||
await publishAudioService.InsertPublishAudioDetail(data);
|
||||
}
|
||||
}
|
||||
toast(t("FilePage.publishDone"));
|
||||
selectedIds.value = [];
|
||||
});
|
||||
}
|
||||
|
||||
// ----- 详情:只读卡片(旧系统信息抽屉的移动端简化)-----
|
||||
function actionDetail(file: FileItem): void {
|
||||
const record = recordOf(file);
|
||||
const lines = [
|
||||
`${t("FilePage.detailName")}: ${file.name}`,
|
||||
file.sizeText ? `${t("FilePage.detailSize")}: ${file.sizeText}` : "",
|
||||
`${t("FilePage.detailTime")}: ${file.modifiedAt}`,
|
||||
record?.OrderKey ? `${t("FilePage.detailOrder")}: ${record.OrderKey}` : "",
|
||||
].filter(Boolean);
|
||||
uni.showModal({ title: t("FilePage.actionDetail"), content: lines.join("\n"), showCancel: false });
|
||||
}
|
||||
|
||||
/** key -> 动作实现表:面板宫格/行式与多选栏的 key 全部从这里分发 */
|
||||
const actionRunners: Record<string, (targets: FileItem[]) => void> = {
|
||||
download: actionDownload,
|
||||
delete: actionDelete,
|
||||
rename: actionRename,
|
||||
move: actionMove,
|
||||
share: actionShare,
|
||||
publish: actionPublish,
|
||||
preview: actionPreview,
|
||||
play: targets => targets[0] && actionPlay(targets[0]),
|
||||
detail: targets => targets[0] && actionDetail(targets[0]),
|
||||
};
|
||||
|
||||
/** 面板/多选栏唯一动作入口:单选传 [file],多选传勾选集 */
|
||||
function runAction(key: string, targets: FileItem[]): void {
|
||||
if (!targets.length) {
|
||||
return;
|
||||
}
|
||||
const run = actionRunners[key];
|
||||
if (run) {
|
||||
run(targets);
|
||||
return;
|
||||
}
|
||||
toast(t("FilePage.todoTip"));
|
||||
}
|
||||
|
||||
/** 面板动作回调:收起面板后交给 runAction 分流 */
|
||||
function onSheetAction(key: string, file: FileItem): void {
|
||||
actionFile.value = null;
|
||||
toast(`${file.name} · ${key} · ${t("FilePage.todoTip")}`);
|
||||
runAction(key, [file]);
|
||||
}
|
||||
|
||||
// 新建文件夹命名对话框(CreateDmsFolder 在途标记防连点)
|
||||
@@ -411,9 +875,10 @@ const transferIconWhite = transferGlyph("#ffffff");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 操作面板/多选操作栏展开时收起 tabbar:tabbar 在页面 fixed 层叠上下文之外,
|
||||
<!-- 弹层(操作面板/抽屉/对话框)或多选态展开时收起 tabbar:tabbar 在页面 fixed 层叠上下文之外,
|
||||
弹层 z-index 再高也压不住它,直接隐藏最干净(主流云盘同款交互) -->
|
||||
<app-page :hide-nav="true" :show-tabbar="atRoot && !actionFile && !selectMode && !folderDialogVisible">
|
||||
<app-page :hide-nav="true"
|
||||
:show-tabbar="atRoot && !actionFile && !selectMode && !folderDialogVisible && !renameVisible && !moveVisible && !shareVisible && !publishVisible">
|
||||
<view class="file-page" :style="{ background: themeBackground }">
|
||||
<!-- 上半屏整体固定,只有文件列表所在的 scroll-view 内部滚动(整页不滚) -->
|
||||
<view class="file-page__head">
|
||||
@@ -494,6 +959,20 @@ const transferIconWhite = transferGlyph("#ffffff");
|
||||
@action="onSheetAction" />
|
||||
<folder-name-dialog :visible="folderDialogVisible" :submitting="folderSubmitting"
|
||||
@close="folderDialogVisible = false" @confirm="onCreateFolder" />
|
||||
<!-- 重命名:同一个命名对话框的第二个实例(预填当前名 + 重命名文案) -->
|
||||
<folder-name-dialog :visible="renameVisible" :submitting="renameSubmitting" :title="t('FilePage.renameTitle')"
|
||||
:confirm-label="t('FilePage.renameConfirm')" :initial-value="renameTarget?.name ?? ''"
|
||||
@close="renameVisible = false" @confirm="onRenameConfirm" />
|
||||
<!-- 移动:网盘式逐级下钻选择器(进层浏览,「移动到此」提交当前层后直接调移动接口) -->
|
||||
<folder-move-picker :visible="moveVisible" :exclude-name="moveExcludeName" :source-path="moveSourcePath"
|
||||
:selected-count="moveTargets.length" @close="moveVisible = false" @confirm="onMovePick" />
|
||||
<!-- 共享家人:选家人目录 -->
|
||||
<option-picker :visible="shareVisible" :title="t('FilePage.shareTitle')" :items="shareItems"
|
||||
:loading="shareLoading" :empty-text="t('FilePage.pickerEmpty')" @close="shareVisible = false"
|
||||
@confirm="onShareConfirm" />
|
||||
<!-- 发布:选专辑 -->
|
||||
<option-picker :visible="publishVisible" :title="publishTitle" :items="publishItems" :loading="publishLoading"
|
||||
:empty-text="t('FilePage.pickerEmpty')" @close="publishVisible = false" @confirm="onPublishConfirm" />
|
||||
<!-- 多选底部操作栏:常驻非模态(无遮罩),勾选与动作可交替进行;
|
||||
动作集是旧系统多选子集——共享/发布按同媒体类型条件,重命名/移动仅单选 -->
|
||||
<view v-if="selectMode" class="file-page__actions">
|
||||
|
||||
Reference in new issue
Block a user