新增工作区下载列表
This commit is contained in:
@@ -380,3 +380,164 @@
|
|||||||
.workspace-footer .iconfont {
|
.workspace-footer .iconfont {
|
||||||
font-size: 40rpx !important;
|
font-size: 40rpx !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== 标签切换 ===== */
|
||||||
|
.workspace-tabs {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 18rpx 0;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #999;
|
||||||
|
border-bottom: 4rpx solid transparent;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active {
|
||||||
|
color: #6c63ff;
|
||||||
|
border-bottom-color: #6c63ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-badge {
|
||||||
|
margin-left: 8rpx;
|
||||||
|
background: #ff5e57;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20rpx;
|
||||||
|
padding: 2rpx 10rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 下载列表 ===== */
|
||||||
|
.download-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-empty {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 200rpx;
|
||||||
|
color: #bbb;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-empty .iconfont {
|
||||||
|
font-size: 140rpx;
|
||||||
|
color: #ddd;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-list {
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12rpx 8rpx 20rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-btn {
|
||||||
|
color: #ff5e57;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 4rpx 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24rpx 20rpx;
|
||||||
|
background: rgba(255, 255, 255, 0.85);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-item:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.di-icon {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(108, 99, 255, 0.08);
|
||||||
|
border-radius: 18rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.di-icon .iconfont {
|
||||||
|
font-size: 40rpx;
|
||||||
|
color: #6c63ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.di-info {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6rpx;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.di-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.di-meta {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.di-path {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #bbb;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.di-delete {
|
||||||
|
width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.di-delete .iconfont {
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #ccc;
|
||||||
|
transition: color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.di-delete:active .iconfont {
|
||||||
|
color: #ff5e57;
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,8 +66,20 @@
|
|||||||
<view v-if="isSearchFocus" class="cancel-search" @click="handleSearchFocus">取消</view>
|
<view v-if="isSearchFocus" class="cancel-search" @click="handleSearchFocus">取消</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 标签切换 -->
|
||||||
|
<view class="workspace-tabs">
|
||||||
|
<view class="tab-item" :class="{ active: activeTab === 'workspace' }" @click="switchTab('workspace')">
|
||||||
|
工作区
|
||||||
</view>
|
</view>
|
||||||
<view class="workspace-content">
|
<view class="tab-item" :class="{ active: activeTab === 'downloads' }" @click="switchTab('downloads')">
|
||||||
|
下载列表
|
||||||
|
<text v-if="downloadList.length > 0" class="tab-badge">{{ downloadList.length }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- ===== 工作区文件 ===== -->
|
||||||
|
<view class="workspace-content" v-if="activeTab === 'workspace'">
|
||||||
<view class="folder-null" v-if="currentFolderList.length===0">
|
<view class="folder-null" v-if="currentFolderList.length===0">
|
||||||
<view class="iconfont icon-wenjianjia"></view>
|
<view class="iconfont icon-wenjianjia"></view>
|
||||||
文件夹为空
|
文件夹为空
|
||||||
@@ -89,6 +101,34 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- ===== 下载列表 ===== -->
|
||||||
|
<view class="download-content" v-if="activeTab === 'downloads'">
|
||||||
|
<view v-if="downloadList.length === 0" class="download-empty">
|
||||||
|
<text class="iconfont icon-xiazai"></text>
|
||||||
|
<text>暂无下载文件</text>
|
||||||
|
</view>
|
||||||
|
<view v-else class="download-list">
|
||||||
|
<view class="download-header">
|
||||||
|
<text>已下载 {{ downloadList.length }} 个文件</text>
|
||||||
|
<text class="clear-btn" @click="handleClearDownloads">清空</text>
|
||||||
|
</view>
|
||||||
|
<view class="download-item" v-for="item in downloadList" :key="item.id" @click="openDownloadFile(item)">
|
||||||
|
<view class="di-icon">
|
||||||
|
<text class="iconfont" :class="getFileIcon(item.name)"></text>
|
||||||
|
</view>
|
||||||
|
<view class="di-info">
|
||||||
|
<text class="di-name">{{ item.name }}</text>
|
||||||
|
<text class="di-meta">{{ formatDownloadTime(item.time) }} · {{ formatDownloadSize(item.size) }}</text>
|
||||||
|
<text class="di-path">{{ item.displayPath || item.path }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="di-delete" @click.stop="handleDeleteDownload(item.id)">
|
||||||
|
<text class="iconfont icon-del"></text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view v-if="isSelectFolder" class="workspace-footer" :class="{'folder-actions':selectFileList.length>0}">
|
<view v-if="isSelectFolder" class="workspace-footer" :class="{'folder-actions':selectFileList.length>0}">
|
||||||
<view class="iconfont icon-shangchuan" @click="handleUploadFile"></view>
|
<view class="iconfont icon-shangchuan" @click="handleUploadFile"></view>
|
||||||
<!-- <view class="iconfont icon-fuzhi"></view> -->
|
<!-- <view class="iconfont icon-fuzhi"></view> -->
|
||||||
@@ -122,11 +162,21 @@
|
|||||||
updateWorkspaceFileName
|
updateWorkspaceFileName
|
||||||
} from '@/utils/cloud-api.js'
|
} from '@/utils/cloud-api.js'
|
||||||
import { openFile as openFileNative } from '@/utils/fileOpener.js'
|
import { openFile as openFileNative } from '@/utils/fileOpener.js'
|
||||||
|
import {
|
||||||
|
getDownloadList,
|
||||||
|
saveDownload,
|
||||||
|
deleteDownload,
|
||||||
|
clearAllDownloads,
|
||||||
|
formatFileSize,
|
||||||
|
formatTime
|
||||||
|
} from '@/utils/downloadManager.js'
|
||||||
|
|
||||||
const UserToken = ref('')
|
const UserToken = ref('')
|
||||||
const workspaceId = ref('')
|
const workspaceId = ref('')
|
||||||
const navbarBeforeTitle = ref('');
|
const navbarBeforeTitle = ref('');
|
||||||
const navbarTitle = ref('工作区');
|
const navbarTitle = ref('工作区');
|
||||||
|
const activeTab = ref('workspace')
|
||||||
|
const downloadList = ref([])
|
||||||
|
|
||||||
// 选择文件模式
|
// 选择文件模式
|
||||||
const isSelectFolder = ref(false);
|
const isSelectFolder = ref(false);
|
||||||
@@ -395,6 +445,7 @@ import { openFile as openFileNative } from '@/utils/fileOpener.js'
|
|||||||
|
|
||||||
const downloadUrl = fileInfo.url;
|
const downloadUrl = fileInfo.url;
|
||||||
const fileName = folder.name;
|
const fileName = folder.name;
|
||||||
|
const fileSize = fileInfo.size || 0;
|
||||||
|
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
title: '下载中...'
|
title: '下载中...'
|
||||||
@@ -407,8 +458,14 @@ import { openFile as openFileNative } from '@/utils/fileOpener.js'
|
|||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
if (downloadRes.statusCode === 200) {
|
if (downloadRes.statusCode === 200) {
|
||||||
const tempPath = downloadRes.tempFilePath;
|
const tempPath = downloadRes.tempFilePath;
|
||||||
// 下载成功,直接打开文件
|
// 持久化保存到本地
|
||||||
console.log("// 下载成功,文件下载路径为:",tempPath);
|
saveDownload(tempPath, fileName, fileSize).then((record) => {
|
||||||
|
downloadList.value.unshift(record);
|
||||||
|
uni.showToast({ title: '下载成功', icon: 'success' });
|
||||||
|
}).catch(() => {
|
||||||
|
// 保存失败不影响打开
|
||||||
|
});
|
||||||
|
// 下载成功后打开文件
|
||||||
openFile(tempPath);
|
openFile(tempPath);
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@@ -443,6 +500,102 @@ import { openFile as openFileNative } from '@/utils/fileOpener.js'
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ===== 下载列表相关 =====
|
||||||
|
const switchTab = (tab) => {
|
||||||
|
activeTab.value = tab
|
||||||
|
if (tab === 'downloads') {
|
||||||
|
loadDownloads()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadDownloads = () => {
|
||||||
|
downloadList.value = getDownloadList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const openDownloadFile = (item) => {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
// 检查公共目录下文件是否存在
|
||||||
|
plus.io.resolveLocalFileSystemURL(
|
||||||
|
item.path,
|
||||||
|
() => {
|
||||||
|
// 文件存在,直接打开
|
||||||
|
openFile(item.path)
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// 文件已被手动删除
|
||||||
|
uni.showToast({ title: '文件已失效,将移除记录', icon: 'none' })
|
||||||
|
handleDeleteDownload(item.id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
uni.getSavedFileList({
|
||||||
|
success: (res) => {
|
||||||
|
const found = res.fileList.find(f => f.filePath === item.path)
|
||||||
|
if (found) {
|
||||||
|
openFile(item.path)
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '文件已失效,将移除记录', icon: 'none' })
|
||||||
|
handleDeleteDownload(item.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: () => openFile(item.path)
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeleteDownload = (id) => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '删除文件',
|
||||||
|
content: '确定要删除此下载文件吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
deleteDownload(id).then(() => {
|
||||||
|
downloadList.value = getDownloadList()
|
||||||
|
uni.showToast({ title: '已删除', icon: 'success' })
|
||||||
|
}).catch(() => {
|
||||||
|
uni.showToast({ title: '删除失败', icon: 'error' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClearDownloads = () => {
|
||||||
|
if (downloadList.value.length === 0) return
|
||||||
|
uni.showModal({
|
||||||
|
title: '清空下载',
|
||||||
|
content: '确定要清空所有下载文件吗?此操作不可撤销。',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
clearAllDownloads().then(() => {
|
||||||
|
downloadList.value = []
|
||||||
|
uni.showToast({ title: '已清空', icon: 'success' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatDownloadSize = formatFileSize
|
||||||
|
const formatDownloadTime = formatTime
|
||||||
|
|
||||||
|
// const getFileIcon = (fileName) => {
|
||||||
|
// const ext = fileName.split('.').pop().toLowerCase()
|
||||||
|
// const iconMap = {
|
||||||
|
// pdf: 'icon-PDF',
|
||||||
|
// doc: 'icon-word', docx: 'icon-word',
|
||||||
|
// xls: 'icon-excel', xlsx: 'icon-excel',
|
||||||
|
// ppt: 'icon-PPD', pptx: 'icon-PPD',
|
||||||
|
// zip: 'icon-yasuobao', rar: 'icon-yasuobao', '7z': 'icon-yasuobao',
|
||||||
|
// jpg: 'icon-tupian', jpeg: 'icon-tupian', png: 'icon-tupian', gif: 'icon-tupian',
|
||||||
|
// mp3: 'icon-yinle', wav: 'icon-yinle', flac: 'icon-yinle',
|
||||||
|
// mp4: 'icon-shipin', avi: 'icon-shipin', mov: 'icon-shipin',
|
||||||
|
// txt: 'icon-txt', md: 'icon-txt',
|
||||||
|
// }
|
||||||
|
// return iconMap[ext] || 'icon-weizhiwenjian'
|
||||||
|
// }
|
||||||
|
|
||||||
// WYXD 查看器入口
|
// WYXD 查看器入口
|
||||||
const openWyxdViewer = (folder) => {
|
const openWyxdViewer = (folder) => {
|
||||||
const filePath = folder.path.startsWith('./') ? folder.path.slice(2) : folder.path;
|
const filePath = folder.path.startsWith('./') ? folder.path.slice(2) : folder.path;
|
||||||
@@ -666,6 +819,7 @@ import { openFile as openFileNative } from '@/utils/fileOpener.js'
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initCurrentFolderList();
|
initCurrentFolderList();
|
||||||
|
loadDownloads();
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
420
unpackage/dist/dev/app-plus/app-service.js
vendored
420
unpackage/dist/dev/app-plus/app-service.js
vendored
@@ -5837,7 +5837,7 @@ This will fail in production.`);
|
|||||||
const openFile$1 = (url) => {
|
const openFile$1 = (url) => {
|
||||||
downloadAndOpen$1(url);
|
downloadAndOpen$1(url);
|
||||||
};
|
};
|
||||||
const formatFileSize = (size) => {
|
const formatFileSize2 = (size) => {
|
||||||
if (!size)
|
if (!size)
|
||||||
return "0KB";
|
return "0KB";
|
||||||
if (size < 1) {
|
if (size < 1) {
|
||||||
@@ -6696,7 +6696,7 @@ This will fail in production.`);
|
|||||||
onUnload(() => {
|
onUnload(() => {
|
||||||
socketStore.disconnect();
|
socketStore.disconnect();
|
||||||
});
|
});
|
||||||
const __returned__ = { friendSocketStore, handleFriendConnect, socketStore, ChatType, wyxdWorkspaceId, wyxdFilePath, wyxdFileName, wyxdUserToken, hasWyxdFile, convertMarkdownTable, renderTable, escapeLoneUnderscores, pareseMarkdown, sanitizeContent, previewImage, openFile: openFile$1, formatFileSize, parseFileInfo, selectNormalChat, showNewChatModal, closeNewChatModal, isChatSidebar, handleChatSidebar, goWorkSpace, goWyxdViewer, goCloudDatabase, logOut, previewFileArray, uploadPhoto, deleteImage, fileList, uploadFile, handleConnect, isThinking, isSelfSent, isUploading, isLoadingMessages, uploadedFiles, streamingMessageId, showMessageModal, detailLinks, handleChatContentClick, extractLinks, extractFileNameFromUrl, showMessageDetail, closeMessageDetail, downloadToast, get downloadToastTimer() {
|
const __returned__ = { friendSocketStore, handleFriendConnect, socketStore, ChatType, wyxdWorkspaceId, wyxdFilePath, wyxdFileName, wyxdUserToken, hasWyxdFile, convertMarkdownTable, renderTable, escapeLoneUnderscores, pareseMarkdown, sanitizeContent, previewImage, openFile: openFile$1, formatFileSize: formatFileSize2, parseFileInfo, selectNormalChat, showNewChatModal, closeNewChatModal, isChatSidebar, handleChatSidebar, goWorkSpace, goWyxdViewer, goCloudDatabase, logOut, previewFileArray, uploadPhoto, deleteImage, fileList, uploadFile, handleConnect, isThinking, isSelfSent, isUploading, isLoadingMessages, uploadedFiles, streamingMessageId, showMessageModal, detailLinks, handleChatContentClick, extractLinks, extractFileNameFromUrl, showMessageDetail, closeMessageDetail, downloadToast, get downloadToastTimer() {
|
||||||
return downloadToastTimer;
|
return downloadToastTimer;
|
||||||
}, set downloadToastTimer(v) {
|
}, set downloadToastTimer(v) {
|
||||||
downloadToastTimer = v;
|
downloadToastTimer = v;
|
||||||
@@ -7451,6 +7451,177 @@ This will fail in production.`);
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
const PagesChatChat = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["render", _sfc_render$9], ["__scopeId", "data-v-5eb7b895"], ["__file", "D:/Projects/uniapp/app-test/test1/pages/Chat/Chat.vue"]]);
|
const PagesChatChat = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["render", _sfc_render$9], ["__scopeId", "data-v-5eb7b895"], ["__file", "D:/Projects/uniapp/app-test/test1/pages/Chat/Chat.vue"]]);
|
||||||
|
const STORAGE_KEY = "localDownloads";
|
||||||
|
const SUB_DIR = "AppDownloads";
|
||||||
|
function getPublicDownloadDir() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
plus.io.requestFileSystem(
|
||||||
|
plus.io.PUBLIC_DOWNLOADS,
|
||||||
|
(fs) => {
|
||||||
|
fs.root.getDirectory(
|
||||||
|
SUB_DIR,
|
||||||
|
{ create: true },
|
||||||
|
(dirEntry) => {
|
||||||
|
resolve(dirEntry.fullPath);
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
formatAppLog("error", "at utils/downloadManager.js:27", "getDirectory failed:", err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
formatAppLog("error", "at utils/downloadManager.js:33", "requestFileSystem failed:", err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getDisplayPath(fullPath) {
|
||||||
|
const idx = fullPath.indexOf("/Download/");
|
||||||
|
if (idx !== -1) {
|
||||||
|
return "下载/" + fullPath.slice(idx + 10);
|
||||||
|
}
|
||||||
|
return fullPath;
|
||||||
|
}
|
||||||
|
function getDownloadList() {
|
||||||
|
try {
|
||||||
|
return uni.getStorageSync(STORAGE_KEY) || [];
|
||||||
|
} catch (e2) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function saveDownload(tempFilePath, fileName, fileSize) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
getPublicDownloadDir().then((dirPath) => {
|
||||||
|
const destPathForUrl = plus.io.convertLocalFileSystemURL(dirPath + "/" + fileName);
|
||||||
|
plus.io.resolveLocalFileSystemURL(
|
||||||
|
tempFilePath,
|
||||||
|
(fileEntry) => {
|
||||||
|
plus.io.resolveLocalFileSystemURL(
|
||||||
|
dirPath,
|
||||||
|
(dirEntry) => {
|
||||||
|
fileEntry.copyTo(
|
||||||
|
dirEntry,
|
||||||
|
fileName,
|
||||||
|
() => {
|
||||||
|
const record = {
|
||||||
|
id: Date.now().toString(),
|
||||||
|
name: fileName,
|
||||||
|
path: destPathForUrl,
|
||||||
|
displayPath: getDisplayPath(destPathForUrl),
|
||||||
|
size: fileSize || 0,
|
||||||
|
time: Date.now()
|
||||||
|
};
|
||||||
|
const list = getDownloadList();
|
||||||
|
const exists = list.some((item) => item.name === fileName);
|
||||||
|
if (exists) {
|
||||||
|
const idx = list.findIndex((item) => item.name === fileName);
|
||||||
|
if (idx !== -1)
|
||||||
|
list.splice(idx, 1);
|
||||||
|
}
|
||||||
|
list.unshift(record);
|
||||||
|
try {
|
||||||
|
uni.setStorageSync(STORAGE_KEY, list);
|
||||||
|
} catch (e2) {
|
||||||
|
list.pop();
|
||||||
|
uni.setStorageSync(STORAGE_KEY, list);
|
||||||
|
}
|
||||||
|
resolve(record);
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
formatAppLog("error", "at utils/downloadManager.js:114", "copyTo failed:", err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
reject
|
||||||
|
);
|
||||||
|
},
|
||||||
|
reject
|
||||||
|
);
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function deleteDownload(id) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const list = getDownloadList();
|
||||||
|
const record = list.find((item) => item.id === id);
|
||||||
|
if (!record) {
|
||||||
|
reject(new Error("记录不存在"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
plus.io.resolveLocalFileSystemURL(
|
||||||
|
record.path,
|
||||||
|
(fileEntry) => {
|
||||||
|
fileEntry.remove(
|
||||||
|
() => {
|
||||||
|
const newList = list.filter((item) => item.id !== id);
|
||||||
|
uni.setStorageSync(STORAGE_KEY, newList);
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
const newList = list.filter((item) => item.id !== id);
|
||||||
|
uni.setStorageSync(STORAGE_KEY, newList);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
const newList = list.filter((item) => item.id !== id);
|
||||||
|
uni.setStorageSync(STORAGE_KEY, newList);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function clearAllDownloads() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const list = getDownloadList();
|
||||||
|
if (list.length === 0) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let count = 0;
|
||||||
|
const done = () => {
|
||||||
|
count++;
|
||||||
|
if (count >= list.length) {
|
||||||
|
uni.setStorageSync(STORAGE_KEY, []);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
list.forEach((record) => {
|
||||||
|
plus.io.resolveLocalFileSystemURL(
|
||||||
|
record.path,
|
||||||
|
(fileEntry) => {
|
||||||
|
fileEntry.remove(done, done);
|
||||||
|
},
|
||||||
|
done
|
||||||
|
);
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.setStorageSync(STORAGE_KEY, []);
|
||||||
|
resolve();
|
||||||
|
}, 5e3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function formatFileSize(bytes) {
|
||||||
|
if (!bytes || bytes <= 0)
|
||||||
|
return "未知大小";
|
||||||
|
const units = ["B", "KB", "MB", "GB"];
|
||||||
|
let i = 0;
|
||||||
|
let size = bytes;
|
||||||
|
while (size >= 1024 && i < units.length - 1) {
|
||||||
|
size /= 1024;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return size.toFixed(i === 0 ? 0 : 1) + " " + units[i];
|
||||||
|
}
|
||||||
|
function formatTime(timestamp) {
|
||||||
|
const d = new Date(timestamp);
|
||||||
|
const pad = (n2) => String(n2).padStart(2, "0");
|
||||||
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||||
|
}
|
||||||
const _sfc_main$9 = {
|
const _sfc_main$9 = {
|
||||||
__name: "WorkSpace",
|
__name: "WorkSpace",
|
||||||
setup(__props, { expose: __expose }) {
|
setup(__props, { expose: __expose }) {
|
||||||
@@ -7459,6 +7630,8 @@ This will fail in production.`);
|
|||||||
const workspaceId = vue.ref("");
|
const workspaceId = vue.ref("");
|
||||||
const navbarBeforeTitle = vue.ref("");
|
const navbarBeforeTitle = vue.ref("");
|
||||||
const navbarTitle = vue.ref("工作区");
|
const navbarTitle = vue.ref("工作区");
|
||||||
|
const activeTab = vue.ref("workspace");
|
||||||
|
const downloadList = vue.ref([]);
|
||||||
const isSelectFolder = vue.ref(false);
|
const isSelectFolder = vue.ref(false);
|
||||||
const selectFileList = vue.ref([]);
|
const selectFileList = vue.ref([]);
|
||||||
const handleSelectFolder = () => {
|
const handleSelectFolder = () => {
|
||||||
@@ -7501,7 +7674,7 @@ This will fail in production.`);
|
|||||||
await doUploadWorkspaceFiles(files);
|
await doUploadWorkspaceFiles(files);
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:177", "选择文件失败:", err);
|
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:227", "选择文件失败:", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -7538,7 +7711,7 @@ This will fail in production.`);
|
|||||||
initCurrentFolderList();
|
initCurrentFolderList();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:230", "上传文件失败:", error);
|
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:280", "上传文件失败:", error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: typeof error === "string" ? error : "上传失败,请重试",
|
title: typeof error === "string" ? error : "上传失败,请重试",
|
||||||
icon: "error"
|
icon: "error"
|
||||||
@@ -7578,7 +7751,7 @@ This will fail in production.`);
|
|||||||
closeNewFolderModal();
|
closeNewFolderModal();
|
||||||
initCurrentFolderList();
|
initCurrentFolderList();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:276", "创建文件夹失败:", error);
|
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:326", "创建文件夹失败:", error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "创建失败,请重试",
|
title: "创建失败,请重试",
|
||||||
icon: "error"
|
icon: "error"
|
||||||
@@ -7649,7 +7822,7 @@ This will fail in production.`);
|
|||||||
initCurrentFolderList();
|
initCurrentFolderList();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:354", "重命名失败:", error);
|
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:404", "重命名失败:", error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: typeof error === "string" ? error : "重命名失败,请重试",
|
title: typeof error === "string" ? error : "重命名失败,请重试",
|
||||||
icon: "error"
|
icon: "error"
|
||||||
@@ -7684,6 +7857,7 @@ This will fail in production.`);
|
|||||||
}
|
}
|
||||||
const downloadUrl = fileInfo.url;
|
const downloadUrl = fileInfo.url;
|
||||||
const fileName = folder.name;
|
const fileName = folder.name;
|
||||||
|
const fileSize = fileInfo.size || 0;
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
title: "下载中..."
|
title: "下载中..."
|
||||||
});
|
});
|
||||||
@@ -7694,7 +7868,11 @@ This will fail in production.`);
|
|||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
if (downloadRes.statusCode === 200) {
|
if (downloadRes.statusCode === 200) {
|
||||||
const tempPath = downloadRes.tempFilePath;
|
const tempPath = downloadRes.tempFilePath;
|
||||||
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:411", "// 下载成功,文件下载路径为:", tempPath);
|
saveDownload(tempPath, fileName, fileSize).then((record) => {
|
||||||
|
downloadList.value.unshift(record);
|
||||||
|
uni.showToast({ title: "下载成功", icon: "success" });
|
||||||
|
}).catch(() => {
|
||||||
|
});
|
||||||
openFile$1(tempPath);
|
openFile$1(tempPath);
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@@ -7705,7 +7883,7 @@ This will fail in production.`);
|
|||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:422", "下载文件失败:", err);
|
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:479", "下载文件失败:", err);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "下载失败,请重试",
|
title: "下载失败,请重试",
|
||||||
icon: "error"
|
icon: "error"
|
||||||
@@ -7714,7 +7892,7 @@ This will fail in production.`);
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:431", "获取下载链接失败:", error);
|
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:488", "获取下载链接失败:", error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "获取下载链接失败,请重试",
|
title: "获取下载链接失败,请重试",
|
||||||
icon: "error"
|
icon: "error"
|
||||||
@@ -7726,12 +7904,67 @@ This will fail in production.`);
|
|||||||
uni.showToast({ title: "无法打开此文件", icon: "error" });
|
uni.showToast({ title: "无法打开此文件", icon: "error" });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const switchTab = (tab) => {
|
||||||
|
activeTab.value = tab;
|
||||||
|
if (tab === "downloads") {
|
||||||
|
loadDownloads();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const loadDownloads = () => {
|
||||||
|
downloadList.value = getDownloadList();
|
||||||
|
};
|
||||||
|
const openDownloadFile = (item) => {
|
||||||
|
plus.io.resolveLocalFileSystemURL(
|
||||||
|
item.path,
|
||||||
|
() => {
|
||||||
|
openFile$1(item.path);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
uni.showToast({ title: "文件已失效,将移除记录", icon: "none" });
|
||||||
|
handleDeleteDownload(item.id);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const handleDeleteDownload = (id) => {
|
||||||
|
uni.showModal({
|
||||||
|
title: "删除文件",
|
||||||
|
content: "确定要删除此下载文件吗?",
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
deleteDownload(id).then(() => {
|
||||||
|
downloadList.value = getDownloadList();
|
||||||
|
uni.showToast({ title: "已删除", icon: "success" });
|
||||||
|
}).catch(() => {
|
||||||
|
uni.showToast({ title: "删除失败", icon: "error" });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handleClearDownloads = () => {
|
||||||
|
if (downloadList.value.length === 0)
|
||||||
|
return;
|
||||||
|
uni.showModal({
|
||||||
|
title: "清空下载",
|
||||||
|
content: "确定要清空所有下载文件吗?此操作不可撤销。",
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
clearAllDownloads().then(() => {
|
||||||
|
downloadList.value = [];
|
||||||
|
uni.showToast({ title: "已清空", icon: "success" });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const formatDownloadSize = formatFileSize;
|
||||||
|
const formatDownloadTime = formatTime;
|
||||||
const openWyxdViewer = (folder) => {
|
const openWyxdViewer = (folder) => {
|
||||||
const filePath = folder.path.startsWith("./") ? folder.path.slice(2) : folder.path;
|
const filePath = folder.path.startsWith("./") ? folder.path.slice(2) : folder.path;
|
||||||
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:449", "解析文件路径:", filePath);
|
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:602", "解析文件路径:", filePath);
|
||||||
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:450", "folder", folder);
|
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:603", "folder", folder);
|
||||||
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:451", "encode", encodeURIComponent(filePath));
|
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:604", "encode", encodeURIComponent(filePath));
|
||||||
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:452", "encode", encodeURIComponent(folder.name));
|
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:605", "encode", encodeURIComponent(folder.name));
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: "/pages/WYXD/WYXD?workspaceId=" + encodeURIComponent(workspaceId.value) + "&filePath=" + encodeURIComponent(filePath) + "&fileName=" + encodeURIComponent(folder.name) + "&userToken=" + encodeURIComponent(UserToken.value)
|
url: "/pages/WYXD/WYXD?workspaceId=" + encodeURIComponent(workspaceId.value) + "&filePath=" + encodeURIComponent(filePath) + "&fileName=" + encodeURIComponent(folder.name) + "&userToken=" + encodeURIComponent(UserToken.value)
|
||||||
});
|
});
|
||||||
@@ -7750,7 +7983,7 @@ This will fail in production.`);
|
|||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
try {
|
try {
|
||||||
const filePath = `./${folder.path}`;
|
const filePath = `./${folder.path}`;
|
||||||
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:482", "删除的是:", filePath);
|
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:635", "删除的是:", filePath);
|
||||||
await daleteWorkspace(UserToken.value, workspaceId.value, filePath);
|
await daleteWorkspace(UserToken.value, workspaceId.value, filePath);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "删除成功",
|
title: "删除成功",
|
||||||
@@ -7758,7 +7991,7 @@ This will fail in production.`);
|
|||||||
});
|
});
|
||||||
initCurrentFolderList();
|
initCurrentFolderList();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:490", "删除失败:", error);
|
formatAppLog("error", "at pages/WorkSpace/WorkSpace.vue:643", "删除失败:", error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "删除失败,请重试",
|
title: "删除失败,请重试",
|
||||||
icon: "error"
|
icon: "error"
|
||||||
@@ -7826,7 +8059,7 @@ This will fail in production.`);
|
|||||||
const currentFolderList = vue.ref([]);
|
const currentFolderList = vue.ref([]);
|
||||||
const getCurrentFolderList = () => {
|
const getCurrentFolderList = () => {
|
||||||
var _a, _b;
|
var _a, _b;
|
||||||
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:571", "当前路径为:", JSON.stringify(folderStack.value));
|
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:724", "当前路径为:", JSON.stringify(folderStack.value));
|
||||||
const currentPath = folderStack.value.length > 0 ? folderStack.value[folderStack.value.length - 1].path : ".";
|
const currentPath = folderStack.value.length > 0 ? folderStack.value[folderStack.value.length - 1].path : ".";
|
||||||
if (currentPath === ".") {
|
if (currentPath === ".") {
|
||||||
return ((_a = allFoldersData.value) == null ? void 0 : _a.children) || [];
|
return ((_a = allFoldersData.value) == null ? void 0 : _a.children) || [];
|
||||||
@@ -7850,7 +8083,7 @@ This will fail in production.`);
|
|||||||
const initCurrentFolderList = async () => {
|
const initCurrentFolderList = async () => {
|
||||||
workspaceId.value = getWorkspaceId();
|
workspaceId.value = getWorkspaceId();
|
||||||
UserToken.value = getToken();
|
UserToken.value = getToken();
|
||||||
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:598", "getWorkspaceId:", workspaceId.value);
|
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:751", "getWorkspaceId:", workspaceId.value);
|
||||||
allFoldersData.value = await getWorkspaceList(UserToken.value, workspaceId.value);
|
allFoldersData.value = await getWorkspaceList(UserToken.value, workspaceId.value);
|
||||||
currentFolderList.value = getCurrentFolderList();
|
currentFolderList.value = getCurrentFolderList();
|
||||||
};
|
};
|
||||||
@@ -7897,7 +8130,7 @@ This will fail in production.`);
|
|||||||
uni.navigateBack({
|
uni.navigateBack({
|
||||||
delta: 1,
|
delta: 1,
|
||||||
fail() {
|
fail() {
|
||||||
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:658", "返回失败,进入兜底跳转");
|
formatAppLog("log", "at pages/WorkSpace/WorkSpace.vue:811", "返回失败,进入兜底跳转");
|
||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
url: "/pages/Chat/Chat"
|
url: "/pages/Chat/Chat"
|
||||||
});
|
});
|
||||||
@@ -7907,8 +8140,9 @@ This will fail in production.`);
|
|||||||
};
|
};
|
||||||
vue.onMounted(() => {
|
vue.onMounted(() => {
|
||||||
initCurrentFolderList();
|
initCurrentFolderList();
|
||||||
|
loadDownloads();
|
||||||
});
|
});
|
||||||
const __returned__ = { UserToken, workspaceId, navbarBeforeTitle, navbarTitle, isSelectFolder, selectFileList, handleSelectFolder, selectFolder, showNewFolder, isNFNFocused, newFolderName, closeNewFolderModal, newWorkspaceFolder, handleUploadFile, doUploadWorkspaceFiles, currentFolderPath, folderPathPreview, confirmCreateFolder, handleLongPress, handleRename, handleDownload, openFile: openFile$1, openWyxdViewer, handleAiModify, handleDelete, isMenuOpen, handleMenu, searchKeyword, isSearchFocus, handleSearchFocus, getFileIcon, allFoldersData, folderStack, currentFolderList, getCurrentFolderList, initCurrentFolderList, handleFolderClick, handleSelectAll, handleBackOrCheck, onMounted: vue.onMounted, ref: vue.ref, computed: vue.computed, get getWorkspaceId() {
|
const __returned__ = { UserToken, workspaceId, navbarBeforeTitle, navbarTitle, activeTab, downloadList, isSelectFolder, selectFileList, handleSelectFolder, selectFolder, showNewFolder, isNFNFocused, newFolderName, closeNewFolderModal, newWorkspaceFolder, handleUploadFile, doUploadWorkspaceFiles, currentFolderPath, folderPathPreview, confirmCreateFolder, handleLongPress, handleRename, handleDownload, openFile: openFile$1, switchTab, loadDownloads, openDownloadFile, handleDeleteDownload, handleClearDownloads, formatDownloadSize, formatDownloadTime, openWyxdViewer, handleAiModify, handleDelete, isMenuOpen, handleMenu, searchKeyword, isSearchFocus, handleSearchFocus, getFileIcon, allFoldersData, folderStack, currentFolderList, getCurrentFolderList, initCurrentFolderList, handleFolderClick, handleSelectAll, handleBackOrCheck, onMounted: vue.onMounted, ref: vue.ref, computed: vue.computed, get getWorkspaceId() {
|
||||||
return getWorkspaceId;
|
return getWorkspaceId;
|
||||||
}, get getToken() {
|
}, get getToken() {
|
||||||
return getToken;
|
return getToken;
|
||||||
@@ -7930,6 +8164,18 @@ This will fail in production.`);
|
|||||||
return updateWorkspaceFileName;
|
return updateWorkspaceFileName;
|
||||||
}, get openFileNative() {
|
}, get openFileNative() {
|
||||||
return openFile;
|
return openFile;
|
||||||
|
}, get getDownloadList() {
|
||||||
|
return getDownloadList;
|
||||||
|
}, get saveDownload() {
|
||||||
|
return saveDownload;
|
||||||
|
}, get deleteDownload() {
|
||||||
|
return deleteDownload;
|
||||||
|
}, get clearAllDownloads() {
|
||||||
|
return clearAllDownloads;
|
||||||
|
}, get formatFileSize() {
|
||||||
|
return formatFileSize;
|
||||||
|
}, get formatTime() {
|
||||||
|
return formatTime;
|
||||||
} };
|
} };
|
||||||
Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
|
Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
|
||||||
return __returned__;
|
return __returned__;
|
||||||
@@ -8097,9 +8343,46 @@ This will fail in production.`);
|
|||||||
class: "cancel-search",
|
class: "cancel-search",
|
||||||
onClick: $setup.handleSearchFocus
|
onClick: $setup.handleSearchFocus
|
||||||
}, "取消")) : vue.createCommentVNode("v-if", true)
|
}, "取消")) : vue.createCommentVNode("v-if", true)
|
||||||
|
]),
|
||||||
|
vue.createElementVNode("view", { class: "workspace-tabs" }, [
|
||||||
|
vue.createElementVNode(
|
||||||
|
"view",
|
||||||
|
{
|
||||||
|
class: vue.normalizeClass(["tab-item", { active: $setup.activeTab === "workspace" }]),
|
||||||
|
onClick: _cache[9] || (_cache[9] = ($event) => $setup.switchTab("workspace"))
|
||||||
|
},
|
||||||
|
" 工作区 ",
|
||||||
|
2
|
||||||
|
/* CLASS */
|
||||||
|
),
|
||||||
|
vue.createElementVNode(
|
||||||
|
"view",
|
||||||
|
{
|
||||||
|
class: vue.normalizeClass(["tab-item", { active: $setup.activeTab === "downloads" }]),
|
||||||
|
onClick: _cache[10] || (_cache[10] = ($event) => $setup.switchTab("downloads"))
|
||||||
|
},
|
||||||
|
[
|
||||||
|
vue.createTextVNode(" 下载列表 "),
|
||||||
|
$setup.downloadList.length > 0 ? (vue.openBlock(), vue.createElementBlock(
|
||||||
|
"text",
|
||||||
|
{
|
||||||
|
key: 0,
|
||||||
|
class: "tab-badge"
|
||||||
|
},
|
||||||
|
vue.toDisplayString($setup.downloadList.length),
|
||||||
|
1
|
||||||
|
/* TEXT */
|
||||||
|
)) : vue.createCommentVNode("v-if", true)
|
||||||
|
],
|
||||||
|
2
|
||||||
|
/* CLASS */
|
||||||
|
)
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
vue.createElementVNode("view", { class: "workspace-content" }, [
|
$setup.activeTab === "workspace" ? (vue.openBlock(), vue.createElementBlock("view", {
|
||||||
|
key: 0,
|
||||||
|
class: "workspace-content"
|
||||||
|
}, [
|
||||||
$setup.currentFolderList.length === 0 ? (vue.openBlock(), vue.createElementBlock("view", {
|
$setup.currentFolderList.length === 0 ? (vue.openBlock(), vue.createElementBlock("view", {
|
||||||
key: 0,
|
key: 0,
|
||||||
class: "folder-null"
|
class: "folder-null"
|
||||||
@@ -8163,11 +8446,94 @@ This will fail in production.`);
|
|||||||
/* KEYED_FRAGMENT */
|
/* KEYED_FRAGMENT */
|
||||||
))
|
))
|
||||||
])) : vue.createCommentVNode("v-if", true)
|
])) : vue.createCommentVNode("v-if", true)
|
||||||
|
])) : vue.createCommentVNode("v-if", true),
|
||||||
|
$setup.activeTab === "downloads" ? (vue.openBlock(), vue.createElementBlock("view", {
|
||||||
|
key: 1,
|
||||||
|
class: "download-content"
|
||||||
|
}, [
|
||||||
|
$setup.downloadList.length === 0 ? (vue.openBlock(), vue.createElementBlock("view", {
|
||||||
|
key: 0,
|
||||||
|
class: "download-empty"
|
||||||
|
}, [
|
||||||
|
vue.createElementVNode("text", { class: "iconfont icon-xiazai" }),
|
||||||
|
vue.createElementVNode("text", null, "暂无下载文件")
|
||||||
|
])) : (vue.openBlock(), vue.createElementBlock("view", {
|
||||||
|
key: 1,
|
||||||
|
class: "download-list"
|
||||||
|
}, [
|
||||||
|
vue.createElementVNode("view", { class: "download-header" }, [
|
||||||
|
vue.createElementVNode(
|
||||||
|
"text",
|
||||||
|
null,
|
||||||
|
"已下载 " + vue.toDisplayString($setup.downloadList.length) + " 个文件",
|
||||||
|
1
|
||||||
|
/* TEXT */
|
||||||
|
),
|
||||||
|
vue.createElementVNode("text", {
|
||||||
|
class: "clear-btn",
|
||||||
|
onClick: $setup.handleClearDownloads
|
||||||
|
}, "清空")
|
||||||
]),
|
]),
|
||||||
|
(vue.openBlock(true), vue.createElementBlock(
|
||||||
|
vue.Fragment,
|
||||||
|
null,
|
||||||
|
vue.renderList($setup.downloadList, (item) => {
|
||||||
|
return vue.openBlock(), vue.createElementBlock("view", {
|
||||||
|
class: "download-item",
|
||||||
|
key: item.id,
|
||||||
|
onClick: ($event) => $setup.openDownloadFile(item)
|
||||||
|
}, [
|
||||||
|
vue.createElementVNode("view", { class: "di-icon" }, [
|
||||||
|
vue.createElementVNode(
|
||||||
|
"text",
|
||||||
|
{
|
||||||
|
class: vue.normalizeClass(["iconfont", $setup.getFileIcon(item.name)])
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
/* CLASS */
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
vue.createElementVNode("view", { class: "di-info" }, [
|
||||||
|
vue.createElementVNode(
|
||||||
|
"text",
|
||||||
|
{ class: "di-name" },
|
||||||
|
vue.toDisplayString(item.name),
|
||||||
|
1
|
||||||
|
/* TEXT */
|
||||||
|
),
|
||||||
|
vue.createElementVNode(
|
||||||
|
"text",
|
||||||
|
{ class: "di-meta" },
|
||||||
|
vue.toDisplayString($setup.formatDownloadTime(item.time)) + " · " + vue.toDisplayString($setup.formatDownloadSize(item.size)),
|
||||||
|
1
|
||||||
|
/* TEXT */
|
||||||
|
),
|
||||||
|
vue.createElementVNode(
|
||||||
|
"text",
|
||||||
|
{ class: "di-path" },
|
||||||
|
vue.toDisplayString(item.displayPath || item.path),
|
||||||
|
1
|
||||||
|
/* TEXT */
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
vue.createElementVNode("view", {
|
||||||
|
class: "di-delete",
|
||||||
|
onClick: vue.withModifiers(($event) => $setup.handleDeleteDownload(item.id), ["stop"])
|
||||||
|
}, [
|
||||||
|
vue.createElementVNode("text", { class: "iconfont icon-del" })
|
||||||
|
], 8, ["onClick"])
|
||||||
|
], 8, ["onClick"]);
|
||||||
|
}),
|
||||||
|
128
|
||||||
|
/* KEYED_FRAGMENT */
|
||||||
|
))
|
||||||
|
]))
|
||||||
|
])) : vue.createCommentVNode("v-if", true),
|
||||||
$setup.isSelectFolder ? (vue.openBlock(), vue.createElementBlock(
|
$setup.isSelectFolder ? (vue.openBlock(), vue.createElementBlock(
|
||||||
"view",
|
"view",
|
||||||
{
|
{
|
||||||
key: 0,
|
key: 2,
|
||||||
class: vue.normalizeClass(["workspace-footer", { "folder-actions": $setup.selectFileList.length > 0 }])
|
class: vue.normalizeClass(["workspace-footer", { "folder-actions": $setup.selectFileList.length > 0 }])
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@@ -9388,7 +9754,7 @@ This will fail in production.`);
|
|||||||
__name: "TemplateSpace",
|
__name: "TemplateSpace",
|
||||||
setup(__props, { expose: __expose }) {
|
setup(__props, { expose: __expose }) {
|
||||||
__expose();
|
__expose();
|
||||||
const formatTime = (timestamp) => {
|
const formatTime2 = (timestamp) => {
|
||||||
if (!timestamp)
|
if (!timestamp)
|
||||||
return "";
|
return "";
|
||||||
const seconds = Math.floor(timestamp);
|
const seconds = Math.floor(timestamp);
|
||||||
@@ -9693,7 +10059,7 @@ This will fail in production.`);
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const __returned__ = { formatTime, formatSize, handleLikeTemplate, templateName, editTemplate, checkTemplate, currentTemplateList, checkShowTemplate, navbarBeforeTitle, navbarTitle, isSelectFolder, selectFileList, handleSelectFolder, selectFolder, handleLongPress, handleRename, handleDownload, handleDelete, isMenuOpen, handleMenu, searchKeyword, isSearchFocus, handleSearchFocus, allTemplate, handleSelectAll, handleBackOrCheck, onMounted: vue.onMounted, ref: vue.ref };
|
const __returned__ = { formatTime: formatTime2, formatSize, handleLikeTemplate, templateName, editTemplate, checkTemplate, currentTemplateList, checkShowTemplate, navbarBeforeTitle, navbarTitle, isSelectFolder, selectFileList, handleSelectFolder, selectFolder, handleLongPress, handleRename, handleDownload, handleDelete, isMenuOpen, handleMenu, searchKeyword, isSearchFocus, handleSearchFocus, allTemplate, handleSelectAll, handleBackOrCheck, onMounted: vue.onMounted, ref: vue.ref };
|
||||||
Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
|
Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
|
||||||
return __returned__;
|
return __returned__;
|
||||||
}
|
}
|
||||||
@@ -10077,7 +10443,7 @@ This will fail in production.`);
|
|||||||
return "icon-wenjianyasuo";
|
return "icon-wenjianyasuo";
|
||||||
return "icon-qitawenjian";
|
return "icon-qitawenjian";
|
||||||
};
|
};
|
||||||
const formatFileSize = (bytes) => {
|
const formatFileSize2 = (bytes) => {
|
||||||
if (!bytes)
|
if (!bytes)
|
||||||
return "";
|
return "";
|
||||||
if (bytes < 1024)
|
if (bytes < 1024)
|
||||||
@@ -10878,7 +11244,7 @@ This will fail in production.`);
|
|||||||
await fetchTeamList();
|
await fetchTeamList();
|
||||||
await fetchDbList();
|
await fetchDbList();
|
||||||
});
|
});
|
||||||
const __returned__ = { userToken, userId, workspaceId, currentCategory, switchCategory, navbarTitle, isMenuOpen, isSelectFolder, selectFileList, handleMenu, handleSelectFolder, parentFolderName, handleBackOrUp, rawPersonalFileData, rawTeamFileData, personalFiles, teamFiles, currentFolderPath, isFolder, getFileExtension, getFileIcon, formatFileSize, currentFolderDisplayPath, personalFileCount, findFolderByPath, loadCurrentFiles, fetchFileList, fetchTeamFileList, handleFileClick, handleFileLongPress, handleDownload, openFile: openFile$1, openFileWithMenu, handleRename, handleDeleteFile, refreshCurrentFileList, showNewFolder, newFolderName, openNewFolderModal, closeNewFolderModal, confirmCreateFolder, showUploadPathInput, uploadPath, handleUploadFile, closeUploadPathInput, confirmUploadPath, chooseAndUploadFile, doUploadFiles, allDatabases, isDbLoading, personalDatabases, teamDatabases, showNewDb, newDbName, fetchDbList, openNewDbModal, closeNewDbModal, confirmCreateDb, showUploadDb, uploadDbFileName, uploadDbFilePath, openUploadDbModal, closeUploadDbModal, handlePickDbFile, confirmUploadDb, handleDeleteDb, handleDbClick, formatDbTime, getAccessLevelLabel, showTeamManage, teamGroups, currentTeamId, currentTeamMembers, inviteKeyword, currentTeamDescription, currentTeamType, currentUserRoleInTeam, isTeamAdmin, fetchTeamList, selectTeam, openTeamManage, closeTeamManage, showEditTeam, editTeamName, editTeamDesc, editTeamType, openEditTeamModal, closeEditTeamModal, confirmEditTeam, toggleMemberRole, removeTeamMemberHandler, refreshTeamMembers, searchResultList, searched, searchTeamMember, addTeamMemberHandler, handleDeleteTeam, onMounted: vue.onMounted, ref: vue.ref, computed: vue.computed, get getToken() {
|
const __returned__ = { userToken, userId, workspaceId, currentCategory, switchCategory, navbarTitle, isMenuOpen, isSelectFolder, selectFileList, handleMenu, handleSelectFolder, parentFolderName, handleBackOrUp, rawPersonalFileData, rawTeamFileData, personalFiles, teamFiles, currentFolderPath, isFolder, getFileExtension, getFileIcon, formatFileSize: formatFileSize2, currentFolderDisplayPath, personalFileCount, findFolderByPath, loadCurrentFiles, fetchFileList, fetchTeamFileList, handleFileClick, handleFileLongPress, handleDownload, openFile: openFile$1, openFileWithMenu, handleRename, handleDeleteFile, refreshCurrentFileList, showNewFolder, newFolderName, openNewFolderModal, closeNewFolderModal, confirmCreateFolder, showUploadPathInput, uploadPath, handleUploadFile, closeUploadPathInput, confirmUploadPath, chooseAndUploadFile, doUploadFiles, allDatabases, isDbLoading, personalDatabases, teamDatabases, showNewDb, newDbName, fetchDbList, openNewDbModal, closeNewDbModal, confirmCreateDb, showUploadDb, uploadDbFileName, uploadDbFilePath, openUploadDbModal, closeUploadDbModal, handlePickDbFile, confirmUploadDb, handleDeleteDb, handleDbClick, formatDbTime, getAccessLevelLabel, showTeamManage, teamGroups, currentTeamId, currentTeamMembers, inviteKeyword, currentTeamDescription, currentTeamType, currentUserRoleInTeam, isTeamAdmin, fetchTeamList, selectTeam, openTeamManage, closeTeamManage, showEditTeam, editTeamName, editTeamDesc, editTeamType, openEditTeamModal, closeEditTeamModal, confirmEditTeam, toggleMemberRole, removeTeamMemberHandler, refreshTeamMembers, searchResultList, searched, searchTeamMember, addTeamMemberHandler, handleDeleteTeam, onMounted: vue.onMounted, ref: vue.ref, computed: vue.computed, get getToken() {
|
||||||
return getToken;
|
return getToken;
|
||||||
}, get chooseFile() {
|
}, get chooseFile() {
|
||||||
return chooseFile;
|
return chooseFile;
|
||||||
@@ -13056,7 +13422,7 @@ This will fail in production.`);
|
|||||||
const openFile$1 = (url) => {
|
const openFile$1 = (url) => {
|
||||||
downloadAndOpen(url);
|
downloadAndOpen(url);
|
||||||
};
|
};
|
||||||
const formatFileSize = (size) => {
|
const formatFileSize2 = (size) => {
|
||||||
if (!size)
|
if (!size)
|
||||||
return "0KB";
|
return "0KB";
|
||||||
if (size < 1) {
|
if (size < 1) {
|
||||||
@@ -13888,7 +14254,7 @@ This will fail in production.`);
|
|||||||
onUnload(() => {
|
onUnload(() => {
|
||||||
socketStore.disconnect();
|
socketStore.disconnect();
|
||||||
});
|
});
|
||||||
const __returned__ = { friendSocketStore, handleFriendConnect, socketStore, ChatType, convertMarkdownTable, renderTable, escapeLoneUnderscores, pareseMarkdown, sanitizeContent, previewImage, openFile: openFile$1, formatFileSize, parseFileInfo, selectNormalChat, showNewChatModal, closeNewChatModal, isChatSidebar, handleChatSidebar, goWorkSpace, goCloudDatabase, logOut, previewFileArray, uploadPhoto, deleteImage, fileList, uploadFile, handleConnect, isThinking, isSelfSent, isUploading, uploadedFiles, streamingMessageId, showMessageModal, detailLinks, handleChatContentClick, extractLinks, extractFileNameFromUrl, showMessageDetail, closeMessageDetail, downloadToast, get downloadToastTimer() {
|
const __returned__ = { friendSocketStore, handleFriendConnect, socketStore, ChatType, convertMarkdownTable, renderTable, escapeLoneUnderscores, pareseMarkdown, sanitizeContent, previewImage, openFile: openFile$1, formatFileSize: formatFileSize2, parseFileInfo, selectNormalChat, showNewChatModal, closeNewChatModal, isChatSidebar, handleChatSidebar, goWorkSpace, goCloudDatabase, logOut, previewFileArray, uploadPhoto, deleteImage, fileList, uploadFile, handleConnect, isThinking, isSelfSent, isUploading, uploadedFiles, streamingMessageId, showMessageModal, detailLinks, handleChatContentClick, extractLinks, extractFileNameFromUrl, showMessageDetail, closeMessageDetail, downloadToast, get downloadToastTimer() {
|
||||||
return downloadToastTimer;
|
return downloadToastTimer;
|
||||||
}, set downloadToastTimer(v) {
|
}, set downloadToastTimer(v) {
|
||||||
downloadToastTimer = v;
|
downloadToastTimer = v;
|
||||||
|
|||||||
140
unpackage/dist/dev/app-plus/pages/WYXD/WYXD.css
vendored
140
unpackage/dist/dev/app-plus/pages/WYXD/WYXD.css
vendored
@@ -333,6 +333,146 @@
|
|||||||
.workspace-footer .iconfont[data-v-507487c4] {
|
.workspace-footer .iconfont[data-v-507487c4] {
|
||||||
font-size: 1.25rem !important;
|
font-size: 1.25rem !important;
|
||||||
}
|
}
|
||||||
|
/* ===== 标签切换 ===== */
|
||||||
|
.workspace-tabs[data-v-507487c4] {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 0.75rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
gap: 0.625rem;
|
||||||
|
}
|
||||||
|
.tab-item[data-v-507487c4] {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5625rem 0;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #999;
|
||||||
|
border-bottom: 0.125rem solid transparent;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.tab-item.active[data-v-507487c4] {
|
||||||
|
color: #6c63ff;
|
||||||
|
border-bottom-color: #6c63ff;
|
||||||
|
}
|
||||||
|
.tab-badge[data-v-507487c4] {
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
background: #ff5e57;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.625rem;
|
||||||
|
padding: 0.0625rem 0.3125rem;
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
/* ===== 下载列表 ===== */
|
||||||
|
.download-content[data-v-507487c4] {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.download-empty[data-v-507487c4] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 6.25rem;
|
||||||
|
color: #bbb;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.download-empty .iconfont[data-v-507487c4] {
|
||||||
|
font-size: 4.375rem;
|
||||||
|
color: #ddd;
|
||||||
|
margin-bottom: 0.625rem;
|
||||||
|
}
|
||||||
|
.download-list[data-v-507487c4] {
|
||||||
|
padding: 0.625rem 0.75rem;
|
||||||
|
}
|
||||||
|
.download-header[data-v-507487c4] {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.375rem 0.25rem 0.625rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.clear-btn[data-v-507487c4] {
|
||||||
|
color: #ff5e57;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
}
|
||||||
|
.download-item[data-v-507487c4] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.75rem 0.625rem;
|
||||||
|
background: rgba(255, 255, 255, 0.85);
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
.download-item[data-v-507487c4]:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
}
|
||||||
|
.di-icon[data-v-507487c4] {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(108, 99, 255, 0.08);
|
||||||
|
border-radius: 0.5625rem;
|
||||||
|
margin-right: 0.625rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.di-icon .iconfont[data-v-507487c4] {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: #6c63ff;
|
||||||
|
}
|
||||||
|
.di-info[data-v-507487c4] {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.1875rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.di-name[data-v-507487c4] {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.di-meta[data-v-507487c4] {
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.di-path[data-v-507487c4] {
|
||||||
|
font-size: 0.625rem;
|
||||||
|
color: #bbb;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.di-delete[data-v-507487c4] {
|
||||||
|
width: 1.75rem;
|
||||||
|
height: 1.75rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.di-delete .iconfont[data-v-507487c4] {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
color: #ccc;
|
||||||
|
transition: color 0.15s ease;
|
||||||
|
}
|
||||||
|
.di-delete:active .iconfont[data-v-507487c4] {
|
||||||
|
color: #ff5e57;
|
||||||
|
}
|
||||||
.wyxd-container[data-v-507487c4] {
|
.wyxd-container[data-v-507487c4] {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -333,6 +333,146 @@
|
|||||||
.workspace-footer .iconfont[data-v-769edd25] {
|
.workspace-footer .iconfont[data-v-769edd25] {
|
||||||
font-size: 1.25rem !important;
|
font-size: 1.25rem !important;
|
||||||
}
|
}
|
||||||
|
/* ===== 标签切换 ===== */
|
||||||
|
.workspace-tabs[data-v-769edd25] {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 0.75rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
gap: 0.625rem;
|
||||||
|
}
|
||||||
|
.tab-item[data-v-769edd25] {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5625rem 0;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #999;
|
||||||
|
border-bottom: 0.125rem solid transparent;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.tab-item.active[data-v-769edd25] {
|
||||||
|
color: #6c63ff;
|
||||||
|
border-bottom-color: #6c63ff;
|
||||||
|
}
|
||||||
|
.tab-badge[data-v-769edd25] {
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
background: #ff5e57;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.625rem;
|
||||||
|
padding: 0.0625rem 0.3125rem;
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
/* ===== 下载列表 ===== */
|
||||||
|
.download-content[data-v-769edd25] {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.download-empty[data-v-769edd25] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 6.25rem;
|
||||||
|
color: #bbb;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.download-empty .iconfont[data-v-769edd25] {
|
||||||
|
font-size: 4.375rem;
|
||||||
|
color: #ddd;
|
||||||
|
margin-bottom: 0.625rem;
|
||||||
|
}
|
||||||
|
.download-list[data-v-769edd25] {
|
||||||
|
padding: 0.625rem 0.75rem;
|
||||||
|
}
|
||||||
|
.download-header[data-v-769edd25] {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.375rem 0.25rem 0.625rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.clear-btn[data-v-769edd25] {
|
||||||
|
color: #ff5e57;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
}
|
||||||
|
.download-item[data-v-769edd25] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.75rem 0.625rem;
|
||||||
|
background: rgba(255, 255, 255, 0.85);
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
.download-item[data-v-769edd25]:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
}
|
||||||
|
.di-icon[data-v-769edd25] {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(108, 99, 255, 0.08);
|
||||||
|
border-radius: 0.5625rem;
|
||||||
|
margin-right: 0.625rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.di-icon .iconfont[data-v-769edd25] {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: #6c63ff;
|
||||||
|
}
|
||||||
|
.di-info[data-v-769edd25] {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.1875rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.di-name[data-v-769edd25] {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.di-meta[data-v-769edd25] {
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.di-path[data-v-769edd25] {
|
||||||
|
font-size: 0.625rem;
|
||||||
|
color: #bbb;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.di-delete[data-v-769edd25] {
|
||||||
|
width: 1.75rem;
|
||||||
|
height: 1.75rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.di-delete .iconfont[data-v-769edd25] {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
color: #ccc;
|
||||||
|
transition: color 0.15s ease;
|
||||||
|
}
|
||||||
|
.di-delete:active .iconfont[data-v-769edd25] {
|
||||||
|
color: #ff5e57;
|
||||||
|
}
|
||||||
/* 编辑模板弹窗 */
|
/* 编辑模板弹窗 */
|
||||||
.edit-template-wrapper[data-v-769edd25] {
|
.edit-template-wrapper[data-v-769edd25] {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -865,3 +865,143 @@
|
|||||||
.workspace-footer .iconfont[data-v-c1a60359] {
|
.workspace-footer .iconfont[data-v-c1a60359] {
|
||||||
font-size: 1.25rem !important;
|
font-size: 1.25rem !important;
|
||||||
}
|
}
|
||||||
|
/* ===== 标签切换 ===== */
|
||||||
|
.workspace-tabs[data-v-c1a60359] {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 0.75rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
gap: 0.625rem;
|
||||||
|
}
|
||||||
|
.tab-item[data-v-c1a60359] {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5625rem 0;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #999;
|
||||||
|
border-bottom: 0.125rem solid transparent;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.tab-item.active[data-v-c1a60359] {
|
||||||
|
color: #6c63ff;
|
||||||
|
border-bottom-color: #6c63ff;
|
||||||
|
}
|
||||||
|
.tab-badge[data-v-c1a60359] {
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
background: #ff5e57;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.625rem;
|
||||||
|
padding: 0.0625rem 0.3125rem;
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
/* ===== 下载列表 ===== */
|
||||||
|
.download-content[data-v-c1a60359] {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.download-empty[data-v-c1a60359] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 6.25rem;
|
||||||
|
color: #bbb;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.download-empty .iconfont[data-v-c1a60359] {
|
||||||
|
font-size: 4.375rem;
|
||||||
|
color: #ddd;
|
||||||
|
margin-bottom: 0.625rem;
|
||||||
|
}
|
||||||
|
.download-list[data-v-c1a60359] {
|
||||||
|
padding: 0.625rem 0.75rem;
|
||||||
|
}
|
||||||
|
.download-header[data-v-c1a60359] {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.375rem 0.25rem 0.625rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.clear-btn[data-v-c1a60359] {
|
||||||
|
color: #ff5e57;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
}
|
||||||
|
.download-item[data-v-c1a60359] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.75rem 0.625rem;
|
||||||
|
background: rgba(255, 255, 255, 0.85);
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
.download-item[data-v-c1a60359]:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
}
|
||||||
|
.di-icon[data-v-c1a60359] {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(108, 99, 255, 0.08);
|
||||||
|
border-radius: 0.5625rem;
|
||||||
|
margin-right: 0.625rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.di-icon .iconfont[data-v-c1a60359] {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: #6c63ff;
|
||||||
|
}
|
||||||
|
.di-info[data-v-c1a60359] {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.1875rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.di-name[data-v-c1a60359] {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.di-meta[data-v-c1a60359] {
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.di-path[data-v-c1a60359] {
|
||||||
|
font-size: 0.625rem;
|
||||||
|
color: #bbb;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.di-delete[data-v-c1a60359] {
|
||||||
|
width: 1.75rem;
|
||||||
|
height: 1.75rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.di-delete .iconfont[data-v-c1a60359] {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
color: #ccc;
|
||||||
|
transition: color 0.15s ease;
|
||||||
|
}
|
||||||
|
.di-delete:active .iconfont[data-v-c1a60359] {
|
||||||
|
color: #ff5e57;
|
||||||
|
}
|
||||||
|
|||||||
284
utils/downloadManager.js
Normal file
284
utils/downloadManager.js
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
/**
|
||||||
|
* 下载管理工具
|
||||||
|
* 文件保存到公共下载目录,用户可通过文件管理器访问
|
||||||
|
*/
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'localDownloads'
|
||||||
|
const SUB_DIR = 'AppDownloads' // 公共下载目录下的子文件夹
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取公共下载目录路径
|
||||||
|
* @returns {Promise<string>} 下载目录绝对路径
|
||||||
|
*/
|
||||||
|
function getPublicDownloadDir() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
plus.io.requestFileSystem(
|
||||||
|
plus.io.PUBLIC_DOWNLOADS,
|
||||||
|
(fs) => {
|
||||||
|
// PUBLIC_DOWNLOADS 对应 /storage/emulated/0/Download/
|
||||||
|
fs.root.getDirectory(
|
||||||
|
SUB_DIR,
|
||||||
|
{ create: true },
|
||||||
|
(dirEntry) => {
|
||||||
|
resolve(dirEntry.fullPath)
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error('getDirectory failed:', err)
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error('requestFileSystem failed:', err)
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
reject(new Error('仅APP端支持'))
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从完整路径中提取可读的显示路径
|
||||||
|
* 如 /storage/emulated/0/Download/AppDownloads/xxx.pdf → 下载/AppDownloads/xxx.pdf
|
||||||
|
*/
|
||||||
|
function getDisplayPath(fullPath) {
|
||||||
|
const idx = fullPath.indexOf('/Download/')
|
||||||
|
if (idx !== -1) {
|
||||||
|
return '下载/' + fullPath.slice(idx + 10)
|
||||||
|
}
|
||||||
|
return fullPath
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有下载记录
|
||||||
|
* @returns {Array} 下载记录列表
|
||||||
|
*/
|
||||||
|
export function getDownloadList() {
|
||||||
|
try {
|
||||||
|
return uni.getStorageSync(STORAGE_KEY) || []
|
||||||
|
} catch (e) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存下载记录(复制到公共下载目录)
|
||||||
|
* @param {string} tempFilePath 临时文件路径(uni.downloadFile 返回的)
|
||||||
|
* @param {string} fileName 文件名
|
||||||
|
* @param {number} fileSize 文件大小(字节,可选)
|
||||||
|
*/
|
||||||
|
export function saveDownload(tempFilePath, fileName, fileSize) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
getPublicDownloadDir().then((dirPath) => {
|
||||||
|
const destPathForUrl = plus.io.convertLocalFileSystemURL(dirPath + '/' + fileName)
|
||||||
|
|
||||||
|
plus.io.resolveLocalFileSystemURL(
|
||||||
|
tempFilePath,
|
||||||
|
(fileEntry) => {
|
||||||
|
plus.io.resolveLocalFileSystemURL(
|
||||||
|
dirPath,
|
||||||
|
(dirEntry) => {
|
||||||
|
fileEntry.copyTo(
|
||||||
|
dirEntry,
|
||||||
|
fileName,
|
||||||
|
() => {
|
||||||
|
const record = {
|
||||||
|
id: Date.now().toString(),
|
||||||
|
name: fileName,
|
||||||
|
path: destPathForUrl,
|
||||||
|
displayPath: getDisplayPath(destPathForUrl),
|
||||||
|
size: fileSize || 0,
|
||||||
|
time: Date.now()
|
||||||
|
}
|
||||||
|
const list = getDownloadList()
|
||||||
|
const exists = list.some(item => item.name === fileName)
|
||||||
|
if (exists) {
|
||||||
|
const idx = list.findIndex(item => item.name === fileName)
|
||||||
|
if (idx !== -1) list.splice(idx, 1)
|
||||||
|
}
|
||||||
|
list.unshift(record)
|
||||||
|
try {
|
||||||
|
uni.setStorageSync(STORAGE_KEY, list)
|
||||||
|
} catch (e) {
|
||||||
|
list.pop()
|
||||||
|
uni.setStorageSync(STORAGE_KEY, list)
|
||||||
|
}
|
||||||
|
resolve(record)
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.error('copyTo failed:', err)
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
reject
|
||||||
|
)
|
||||||
|
},
|
||||||
|
reject
|
||||||
|
)
|
||||||
|
}).catch(reject)
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
// 非 APP 端降级使用 uni.saveFile
|
||||||
|
uni.saveFile({
|
||||||
|
tempFilePath: tempFilePath,
|
||||||
|
success: (saveRes) => {
|
||||||
|
const record = {
|
||||||
|
id: Date.now().toString(),
|
||||||
|
name: fileName,
|
||||||
|
path: saveRes.savedFilePath,
|
||||||
|
size: fileSize || 0,
|
||||||
|
time: Date.now()
|
||||||
|
}
|
||||||
|
const list = getDownloadList()
|
||||||
|
list.unshift(record)
|
||||||
|
uni.setStorageSync(STORAGE_KEY, list)
|
||||||
|
resolve(record)
|
||||||
|
},
|
||||||
|
fail: reject
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除单条下载记录及其文件
|
||||||
|
* @param {string} id 记录 ID
|
||||||
|
*/
|
||||||
|
export function deleteDownload(id) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const list = getDownloadList()
|
||||||
|
const record = list.find(item => item.id === id)
|
||||||
|
if (!record) {
|
||||||
|
reject(new Error('记录不存在'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
// 删除公共目录下的文件
|
||||||
|
plus.io.resolveLocalFileSystemURL(
|
||||||
|
record.path,
|
||||||
|
(fileEntry) => {
|
||||||
|
fileEntry.remove(
|
||||||
|
() => {
|
||||||
|
const newList = list.filter(item => item.id !== id)
|
||||||
|
uni.setStorageSync(STORAGE_KEY, newList)
|
||||||
|
resolve()
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// 文件不存在也清除记录
|
||||||
|
const newList = list.filter(item => item.id !== id)
|
||||||
|
uni.setStorageSync(STORAGE_KEY, newList)
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// 无法解析路径,直接清除记录
|
||||||
|
const newList = list.filter(item => item.id !== id)
|
||||||
|
uni.setStorageSync(STORAGE_KEY, newList)
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
uni.removeSavedFile({
|
||||||
|
filePath: record.path,
|
||||||
|
success: () => {
|
||||||
|
const newList = list.filter(item => item.id !== id)
|
||||||
|
uni.setStorageSync(STORAGE_KEY, newList)
|
||||||
|
resolve()
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
const newList = list.filter(item => item.id !== id)
|
||||||
|
uni.setStorageSync(STORAGE_KEY, newList)
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空所有下载记录和文件
|
||||||
|
*/
|
||||||
|
export function clearAllDownloads() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const list = getDownloadList()
|
||||||
|
if (list.length === 0) {
|
||||||
|
resolve()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
let count = 0
|
||||||
|
const done = () => {
|
||||||
|
count++
|
||||||
|
if (count >= list.length) {
|
||||||
|
uni.setStorageSync(STORAGE_KEY, [])
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list.forEach(record => {
|
||||||
|
plus.io.resolveLocalFileSystemURL(
|
||||||
|
record.path,
|
||||||
|
(fileEntry) => {
|
||||||
|
fileEntry.remove(done, done)
|
||||||
|
},
|
||||||
|
done
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.setStorageSync(STORAGE_KEY, [])
|
||||||
|
resolve()
|
||||||
|
}, 5000)
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
let count = 0
|
||||||
|
list.forEach(record => {
|
||||||
|
uni.removeSavedFile({
|
||||||
|
filePath: record.path,
|
||||||
|
success: () => { count++; if (count >= list.length) { uni.setStorageSync(STORAGE_KEY, []); resolve() } },
|
||||||
|
fail: () => { count++; if (count >= list.length) { uni.setStorageSync(STORAGE_KEY, []); resolve() } }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if (list.length === 0) resolve()
|
||||||
|
setTimeout(() => { uni.setStorageSync(STORAGE_KEY, []); resolve() }, 3000)
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化文件大小为可读字符串
|
||||||
|
* @param {number} bytes 字节数
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function formatFileSize(bytes) {
|
||||||
|
if (!bytes || bytes <= 0) return '未知大小'
|
||||||
|
const units = ['B', 'KB', 'MB', 'GB']
|
||||||
|
let i = 0
|
||||||
|
let size = bytes
|
||||||
|
while (size >= 1024 && i < units.length - 1) {
|
||||||
|
size /= 1024
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return size.toFixed(i === 0 ? 0 : 1) + ' ' + units[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化时间戳为可读日期
|
||||||
|
* @param {number} timestamp
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function formatTime(timestamp) {
|
||||||
|
const d = new Date(timestamp)
|
||||||
|
const pad = n => String(n).padStart(2, '0')
|
||||||
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user