mirror of
https://github.com/kuaifan/dootask.git
synced 2026-09-10 22:19:02 +00:00
feat(file): sync collaboration download status
This commit is contained in:
parent
2a7d635729
commit
59aff7a356
20
electron/electron-down.js
vendored
20
electron/electron-down.js
vendored
@ -63,7 +63,7 @@ function initialize(onStarted = null, onChanged = null) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// IPC
|
// IPC
|
||||||
ipcMain.handle('downloadManager', async (event, {action, path, msgId}) => {
|
ipcMain.handle('downloadManager', async (event, {action, path, msgId, file, files}) => {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "get": {
|
case "get": {
|
||||||
return {
|
return {
|
||||||
@ -93,12 +93,14 @@ function initialize(onStarted = null, onChanged = null) {
|
|||||||
case "remove": {
|
case "remove": {
|
||||||
downloadManager.remove(path);
|
downloadManager.remove(path);
|
||||||
syncDownloadItems();
|
syncDownloadItems();
|
||||||
|
notifyChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "removeAll": {
|
case "removeAll": {
|
||||||
downloadManager.removeAll();
|
downloadManager.removeAll();
|
||||||
syncDownloadItems();
|
syncDownloadItems();
|
||||||
|
notifyChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +132,22 @@ function initialize(onStarted = null, onChanged = null) {
|
|||||||
shell.showItemInFolder(file.path);
|
shell.showItemInFolder(file.path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "fileStatuses": {
|
||||||
|
if (!Array.isArray(files)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return downloadManager.getFileStatuses(files.slice(0, 500));
|
||||||
|
}
|
||||||
|
|
||||||
|
case "showFile": {
|
||||||
|
const localFile = downloadManager.getFileStatus(file);
|
||||||
|
if (localFile.status !== 'available') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
shell.showItemInFolder(localFile.path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
93
electron/lib/download-manager.js
vendored
93
electron/lib/download-manager.js
vendored
@ -103,12 +103,27 @@ class DownloadManager {
|
|||||||
* @returns {{status: 'available'|'downloading'|'missing', path?: string}}
|
* @returns {{status: 'available'|'downloading'|'missing', path?: string}}
|
||||||
*/
|
*/
|
||||||
getMessageFileStatus(msgId) {
|
getMessageFileStatus(msgId) {
|
||||||
const targetId = parseInt(msgId, 10);
|
return this.getFileStatus({msgId});
|
||||||
if (!targetId) {
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取会话附件对应的下载状态。
|
||||||
|
*
|
||||||
|
* @param {{msgId?: number|string, attachmentId?: number|string}} reference
|
||||||
|
* @returns {{status: 'available'|'downloading'|'missing', path?: string}}
|
||||||
|
*/
|
||||||
|
getFileStatus(reference = {}) {
|
||||||
|
const msgId = parseInt(reference.msgId, 10) || 0;
|
||||||
|
const attachmentId = parseInt(reference.attachmentId, 10) || 0;
|
||||||
|
if (!msgId && !attachmentId) {
|
||||||
return {status: 'missing'};
|
return {status: 'missing'};
|
||||||
}
|
}
|
||||||
|
|
||||||
const items = this.downloadHistory.filter(item => this.getMessageFileId(item) === targetId);
|
const items = this.downloadHistory.filter(item => {
|
||||||
|
const file = this.getFileReference(item);
|
||||||
|
return (msgId > 0 && file.msgId === msgId)
|
||||||
|
|| (attachmentId > 0 && file.attachmentId === attachmentId);
|
||||||
|
});
|
||||||
const downloading = items.some(item => item.state === 'progressing' && !item.paused);
|
const downloading = items.some(item => item.state === 'progressing' && !item.paused);
|
||||||
if (downloading) {
|
if (downloading) {
|
||||||
return {status: 'downloading'};
|
return {status: 'downloading'};
|
||||||
@ -118,6 +133,58 @@ class DownloadManager {
|
|||||||
return available ? {status: 'available', path: available.path} : {status: 'missing'};
|
return available ? {status: 'available', path: available.path} : {status: 'missing'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量获取会话附件的下载状态,只扫描一次下载历史。
|
||||||
|
*
|
||||||
|
* @param {Array<{key: string, msgId?: number|string, attachmentId?: number|string}>} references
|
||||||
|
* @returns {Object<string, 'available'|'downloading'|'missing'>}
|
||||||
|
*/
|
||||||
|
getFileStatuses(references = []) {
|
||||||
|
const statuses = {};
|
||||||
|
const msgKeys = new Map();
|
||||||
|
const attachmentKeys = new Map();
|
||||||
|
const addKey = (map, id, key) => {
|
||||||
|
if (!id) return;
|
||||||
|
if (!map.has(id)) map.set(id, []);
|
||||||
|
map.get(id).push(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
references.forEach(reference => {
|
||||||
|
if (!reference || typeof reference !== 'object') return;
|
||||||
|
const key = `${reference.key || ''}`;
|
||||||
|
if (!key) return;
|
||||||
|
const msgId = parseInt(reference.msgId, 10) || 0;
|
||||||
|
const attachmentId = parseInt(reference.attachmentId, 10) || 0;
|
||||||
|
statuses[key] = 'missing';
|
||||||
|
addKey(msgKeys, msgId, key);
|
||||||
|
addKey(attachmentKeys, attachmentId, key);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.downloadHistory.forEach(item => {
|
||||||
|
const file = this.getFileReference(item);
|
||||||
|
const keys = new Set([
|
||||||
|
...(msgKeys.get(file.msgId) || []),
|
||||||
|
...(attachmentKeys.get(file.attachmentId) || []),
|
||||||
|
]);
|
||||||
|
if (!keys.size) return;
|
||||||
|
|
||||||
|
let status = '';
|
||||||
|
if (item.state === 'progressing' && !item.paused) {
|
||||||
|
status = 'downloading';
|
||||||
|
} else if (item.state === 'completed' && item.path && fs.existsSync(item.path)) {
|
||||||
|
status = 'available';
|
||||||
|
}
|
||||||
|
if (!status) return;
|
||||||
|
|
||||||
|
keys.forEach(key => {
|
||||||
|
if (status === 'downloading' || statuses[key] === 'missing') {
|
||||||
|
statuses[key] = status;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return statuses;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从下载地址中识别聊天文件消息 ID。
|
* 从下载地址中识别聊天文件消息 ID。
|
||||||
*
|
*
|
||||||
@ -125,6 +192,17 @@ class DownloadManager {
|
|||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
getMessageFileId(item) {
|
getMessageFileId(item) {
|
||||||
|
return this.getFileReference(item).msgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从下载地址中识别会话消息或协作附件 ID。
|
||||||
|
*
|
||||||
|
* @param {Object} item
|
||||||
|
* @returns {{msgId: number, attachmentId: number}}
|
||||||
|
*/
|
||||||
|
getFileReference(item) {
|
||||||
|
const reference = {msgId: 0, attachmentId: 0};
|
||||||
const urls = [...(Array.isArray(item.urls) ? item.urls : []), item.url].filter(Boolean);
|
const urls = [...(Array.isArray(item.urls) ? item.urls : []), item.url].filter(Boolean);
|
||||||
for (const value of urls) {
|
for (const value of urls) {
|
||||||
try {
|
try {
|
||||||
@ -132,14 +210,19 @@ class DownloadManager {
|
|||||||
if (url.pathname.endsWith('/api/dialog/msg/download')) {
|
if (url.pathname.endsWith('/api/dialog/msg/download')) {
|
||||||
const msgId = parseInt(url.searchParams.get('msg_id'), 10);
|
const msgId = parseInt(url.searchParams.get('msg_id'), 10);
|
||||||
if (msgId > 0) {
|
if (msgId > 0) {
|
||||||
return msgId;
|
reference.msgId = msgId;
|
||||||
|
}
|
||||||
|
} else if (url.pathname.endsWith('/api/file/collaboration/download')) {
|
||||||
|
const attachmentId = parseInt(url.searchParams.get('attachment_id'), 10);
|
||||||
|
if (attachmentId > 0) {
|
||||||
|
reference.attachmentId = attachmentId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore malformed history URLs.
|
// Ignore malformed history URLs.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -34,6 +34,8 @@ last_verified: v1.8.89
|
|||||||
|
|
||||||
文件消息和粘贴、插入到聊天正文中的图片都会汇总到协作文件。一条聊天消息包含多张图片时,每张图片会作为一条独立记录展示。图片会在列表和宫格中直接显示缩略图;缩略图不可用时显示对应的文件类型图标。
|
文件消息和粘贴、插入到聊天正文中的图片都会汇总到协作文件。一条聊天消息包含多张图片时,每张图片会作为一条独立记录展示。图片会在列表和宫格中直接显示缩略图;缩略图不可用时显示对应的文件类型图标。
|
||||||
|
|
||||||
|
Electron 桌面客户端会同步会话文件与协作文件的本地下载状态:未下载时显示下载图标,下载中显示加载图标;下载完成且本地文件仍存在时显示文件夹图标,点击可在资源管理器或 Finder 中定位文件。清除下载历史,或移动、改名、删除本地文件后,操作会恢复为下载图标。正文图片等独立附件也支持相同操作。
|
||||||
|
|
||||||
## 权限与范围
|
## 权限与范围
|
||||||
- 只展示当前用户仍有权访问的来源;退出会话、项目或任务权限变化后,对应文件不再显示。
|
- 只展示当前用户仍有权访问的来源;退出会话、项目或任务权限变化后,对应文件不再显示。
|
||||||
- 已撤回或删除的文件消息、正文图片不显示;编辑消息移除图片后,对应图片也不再显示。
|
- 已撤回或删除的文件消息、正文图片不显示;编辑消息移除图片后,对应图片也不再显示。
|
||||||
|
|||||||
@ -99,8 +99,14 @@
|
|||||||
<span class="time-text">{{formatTime(item.created_at)}}</span>
|
<span class="time-text">{{formatTime(item.created_at)}}</span>
|
||||||
<span class="size-text">{{$A.bytesToSize(item.size)}}</span>
|
<span class="size-text">{{$A.bytesToSize(item.size)}}</span>
|
||||||
<div class="row-actions">
|
<div class="row-actions">
|
||||||
<ETooltip :content="$L('打开来源')"><button @click="locateMessage(item)"><Icon type="md-open"/></button></ETooltip>
|
<ETooltip :content="$L('打开来源')"><button @click="locateMessage(item)"><CollaborationSourceIcon/></button></ETooltip>
|
||||||
<ETooltip :content="$L('下载')"><button @click="download(item)"><Icon type="md-download"/></button></ETooltip>
|
<ETooltip :content="localActionTitle(item)">
|
||||||
|
<button
|
||||||
|
:disabled="localFileStatus(item) === 'downloading'"
|
||||||
|
@click="handleLocalAction(item)">
|
||||||
|
<LocalFileStatusIcon :status="localFileStatus(item)"/>
|
||||||
|
</button>
|
||||||
|
</ETooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -123,8 +129,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid-meta">{{item.sender.nickname || $L('未知成员')}} · {{formatTime(item.created_at)}}</div>
|
<div class="grid-meta">{{item.sender.nickname || $L('未知成员')}} · {{formatTime(item.created_at)}}</div>
|
||||||
<div class="grid-actions" @click.stop>
|
<div class="grid-actions" @click.stop>
|
||||||
<button :title="$L('打开来源')" @click="locateMessage(item)"><Icon type="md-open"/></button>
|
<button :title="$L('打开来源')" @click="locateMessage(item)"><CollaborationSourceIcon/></button>
|
||||||
<button :title="$L('下载')" @click="download(item)"><Icon type="md-download"/></button>
|
<button
|
||||||
|
:title="localActionTitle(item)"
|
||||||
|
:disabled="localFileStatus(item) === 'downloading'"
|
||||||
|
@click="handleLocalAction(item)">
|
||||||
|
<LocalFileStatusIcon :status="localFileStatus(item)"/>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -142,11 +153,14 @@
|
|||||||
<script>
|
<script>
|
||||||
import {mapState} from "vuex";
|
import {mapState} from "vuex";
|
||||||
import {openFileInClient} from "../../../utils/file";
|
import {openFileInClient} from "../../../utils/file";
|
||||||
|
import CollaborationSourceIcon from "./CollaborationSourceIcon.vue";
|
||||||
|
import LocalFileStatusIcon from "./DialogView/LocalFileStatusIcon.vue";
|
||||||
|
|
||||||
const CACHE_VERSION = 1;
|
const CACHE_VERSION = 1;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CollaborationFileList",
|
name: "CollaborationFileList",
|
||||||
|
components: {CollaborationSourceIcon, LocalFileStatusIcon},
|
||||||
props: {
|
props: {
|
||||||
searchKey: {
|
searchKey: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -169,6 +183,8 @@ export default {
|
|||||||
initializing: true,
|
initializing: true,
|
||||||
searchTimer: null,
|
searchTimer: null,
|
||||||
requestId: 0,
|
requestId: 0,
|
||||||
|
localFileStatuses: {},
|
||||||
|
removeDownloadListener: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -223,11 +239,20 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.dispatch('getProjects').catch(() => {});
|
this.$store.dispatch('getProjects').catch(() => {});
|
||||||
|
if (this.$Electron) {
|
||||||
|
this.removeDownloadListener = $A.Electron.listener('downloadItemsChanged', () => {
|
||||||
|
this.refreshLocalFileStatuses();
|
||||||
|
});
|
||||||
|
this.refreshLocalFileStatuses();
|
||||||
|
}
|
||||||
this.refresh();
|
this.refresh();
|
||||||
this.initializing = false;
|
this.initializing = false;
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
clearTimeout(this.searchTimer);
|
clearTimeout(this.searchTimer);
|
||||||
|
if (typeof this.removeDownloadListener === 'function') {
|
||||||
|
this.removeDownloadListener();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setScope(scope) {
|
setScope(scope) {
|
||||||
@ -256,6 +281,7 @@ export default {
|
|||||||
this.loading = 0;
|
this.loading = 0;
|
||||||
if (!keepItems) {
|
if (!keepItems) {
|
||||||
this.items = [];
|
this.items = [];
|
||||||
|
this.localFileStatuses = {};
|
||||||
}
|
}
|
||||||
this.cursor = 0;
|
this.cursor = 0;
|
||||||
this.hasMore = false;
|
this.hasMore = false;
|
||||||
@ -286,6 +312,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.items.push(...data.list);
|
this.items.push(...data.list);
|
||||||
}
|
}
|
||||||
|
this.refreshLocalFileStatuses(data.list);
|
||||||
this.cursor = data.next_cursor;
|
this.cursor = data.next_cursor;
|
||||||
this.hasMore = data.has_more;
|
this.hasMore = data.has_more;
|
||||||
if (replace && !this.searchKey.trim()) {
|
if (replace && !this.searchKey.trim()) {
|
||||||
@ -368,6 +395,40 @@ export default {
|
|||||||
itemKey(item) {
|
itemKey(item) {
|
||||||
return item.attachment_id ? `attachment-${item.attachment_id}` : `message-${item.msg_id}`;
|
return item.attachment_id ? `attachment-${item.attachment_id}` : `message-${item.msg_id}`;
|
||||||
},
|
},
|
||||||
|
fileReference(item) {
|
||||||
|
return {
|
||||||
|
key: this.itemKey(item),
|
||||||
|
msgId: item.msg_id,
|
||||||
|
attachmentId: item.attachment_id,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
localFileStatus(item) {
|
||||||
|
if (!this.$Electron) return 'missing';
|
||||||
|
return this.localFileStatuses[this.itemKey(item)] || 'missing';
|
||||||
|
},
|
||||||
|
localActionTitle(item) {
|
||||||
|
if (this.localFileStatus(item) === 'available') {
|
||||||
|
return this.$L('在文件夹中显示');
|
||||||
|
}
|
||||||
|
return this.$L('下载');
|
||||||
|
},
|
||||||
|
async refreshLocalFileStatuses(items = this.items) {
|
||||||
|
if (!this.$Electron || !items.length) return;
|
||||||
|
const references = items.map(item => this.fileReference(item));
|
||||||
|
const batches = [];
|
||||||
|
for (let index = 0; index < references.length; index += 500) {
|
||||||
|
batches.push(references.slice(index, index + 500));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const results = await Promise.all(batches.map(files => $A.Electron.sendAsync('downloadManager', {
|
||||||
|
action: 'fileStatuses',
|
||||||
|
files,
|
||||||
|
})));
|
||||||
|
this.localFileStatuses = Object.assign({}, this.localFileStatuses, ...results);
|
||||||
|
} catch {
|
||||||
|
// Keep the download action available when local history cannot be read.
|
||||||
|
}
|
||||||
|
},
|
||||||
showThumbnail(item) {
|
showThumbnail(item) {
|
||||||
return !!item.image_url && !item._thumbnailError;
|
return !!item.image_url && !item._thumbnailError;
|
||||||
},
|
},
|
||||||
@ -437,16 +498,45 @@ export default {
|
|||||||
search_msg_id: item.msg_id,
|
search_msg_id: item.msg_id,
|
||||||
}).catch(({msg}) => msg && $A.modalError(msg));
|
}).catch(({msg}) => msg && $A.modalError(msg));
|
||||||
},
|
},
|
||||||
download(item) {
|
downloadUrl(item) {
|
||||||
const url = item.attachment_id
|
if (item.attachment_source === 'file_message') {
|
||||||
|
return `dialog/msg/download?msg_id=${item.msg_id}`;
|
||||||
|
}
|
||||||
|
return item.attachment_id
|
||||||
? `file/collaboration/download?attachment_id=${item.attachment_id}`
|
? `file/collaboration/download?attachment_id=${item.attachment_id}`
|
||||||
: `dialog/msg/download?msg_id=${item.msg_id}`;
|
: `dialog/msg/download?msg_id=${item.msg_id}`;
|
||||||
|
},
|
||||||
|
async handleLocalAction(item) {
|
||||||
|
if (!this.$Electron) {
|
||||||
|
this.download(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const status = this.localFileStatus(item);
|
||||||
|
if (status === 'downloading') return;
|
||||||
|
if (status === 'available') {
|
||||||
|
try {
|
||||||
|
const shown = await $A.Electron.sendAsync('downloadManager', {
|
||||||
|
action: 'showFile',
|
||||||
|
file: this.fileReference(item),
|
||||||
|
});
|
||||||
|
if (shown) return;
|
||||||
|
} catch {
|
||||||
|
// Refresh the action when the local file cannot be revealed.
|
||||||
|
}
|
||||||
|
this.$set(this.localFileStatuses, this.itemKey(item), 'missing');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$set(this.localFileStatuses, this.itemKey(item), 'downloading');
|
||||||
|
this.$store.dispatch('downUrl', $A.apiUrl(this.downloadUrl(item)));
|
||||||
|
setTimeout(() => this.refreshLocalFileStatuses([item]), 500);
|
||||||
|
},
|
||||||
|
download(item) {
|
||||||
$A.modalConfirm({
|
$A.modalConfirm({
|
||||||
language: false,
|
language: false,
|
||||||
title: this.$L('下载文件'),
|
title: this.$L('下载文件'),
|
||||||
okText: this.$L('立即下载'),
|
okText: this.$L('立即下载'),
|
||||||
content: `${this.fileName(item)} (${$A.bytesToSize(item.size)})`,
|
content: `${this.fileName(item)} (${$A.bytesToSize(item.size)})`,
|
||||||
onOk: () => this.$store.dispatch('downUrl', $A.apiUrl(url)),
|
onOk: () => this.$store.dispatch('downUrl', $A.apiUrl(this.downloadUrl(item))),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -724,12 +814,17 @@ export default {
|
|||||||
.ivu-tooltip-rel button {
|
.ivu-tooltip-rel button {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
color: $primary-text-color;
|
color: $primary-text-color;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover { color: $primary-color; background: rgba($primary-color, 0.08); }
|
&:hover { color: $primary-color; background: rgba($primary-color, 0.08); }
|
||||||
|
&:disabled { cursor: default; }
|
||||||
|
.common-loading { width: 16px; height: 16px; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.collaboration-grid {
|
.collaboration-grid {
|
||||||
@ -784,7 +879,19 @@ export default {
|
|||||||
top: 10px;
|
top: 10px;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
button { width: 28px; height: 28px; border: 0; color: $primary-text-color; background: transparent; cursor: pointer; }
|
button {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 0;
|
||||||
|
color: $primary-text-color;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
&:disabled { cursor: default; }
|
||||||
|
.common-loading { width: 16px; height: 16px; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.empty-state {
|
.empty-state {
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
aria-hidden="true">
|
||||||
|
<path
|
||||||
|
d="M14 5H19V10"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.8"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
<path
|
||||||
|
d="M19 5L11 13"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.8"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
<path
|
||||||
|
d="M19 14V17A2 2 0 0 1 17 19H7A2 2 0 0 1 5 17V7A2 2 0 0 1 7 5H10"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.8"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "CollaborationSourceIcon",
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<span class="local-file-status-icon" aria-hidden="true">
|
||||||
|
<Loading v-if="status === 'downloading'"/>
|
||||||
|
<svg
|
||||||
|
v-else-if="status === 'available'"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M3.5 16V6A2 2 0 0 1 5.5 4H8.2A2 2 0 0 1 9.6 4.6L11 6H18.5A2 2 0 0 1 20.5 8V9"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.8"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
<path
|
||||||
|
d="M5.2 19.5H17.7A2 2 0 0 0 19.6 18.1L21.2 12.8A1.5 1.5 0 0 0 19.8 10.8H8.3A2 2 0 0 0 6.4 12.2L3.6 17A1.7 1.7 0 0 0 5.2 19.5Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.8"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
v-else
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M12 3V15M7.5 10.5L12 15L16.5 10.5M5 20H19"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.8"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "LocalFileStatusIcon",
|
||||||
|
props: {
|
||||||
|
status: {
|
||||||
|
type: String,
|
||||||
|
default: 'missing',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.local-file-status-icon {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex: none;
|
||||||
|
|
||||||
|
.common-loading {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -16,7 +16,7 @@
|
|||||||
<div v-else class="file-box" @click="downFile">
|
<div v-else class="file-box" @click="downFile">
|
||||||
<img class="file-thumb" :src="msg.thumb"/>
|
<img class="file-thumb" :src="msg.thumb"/>
|
||||||
<div class="file-info">
|
<div class="file-info">
|
||||||
<div class="file-name">{{ msg.name }}</div>
|
<div class="file-name" :title="msg.name">{{ msg.name }}</div>
|
||||||
<div class="file-size">{{ $A.bytesToSize(msg.size) }}</div>
|
<div class="file-size">{{ $A.bytesToSize(msg.size) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@ -27,9 +27,7 @@
|
|||||||
:aria-label="localActionTitle"
|
:aria-label="localActionTitle"
|
||||||
:disabled="localFileStatus === 'downloading'"
|
:disabled="localFileStatus === 'downloading'"
|
||||||
@click.stop="handleLocalAction">
|
@click.stop="handleLocalAction">
|
||||||
<Loading v-if="localFileStatus === 'downloading'"/>
|
<LocalFileStatusIcon :status="localFileStatus"/>
|
||||||
<Icon v-else-if="localFileStatus === 'available'" type="ios-folder-open-outline"/>
|
|
||||||
<Icon v-else type="md-download"/>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="msg.percentage" class="file-percentage">
|
<div v-if="msg.percentage" class="file-percentage">
|
||||||
@ -40,7 +38,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import LocalFileStatusIcon from "./LocalFileStatusIcon.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {LocalFileStatusIcon},
|
||||||
props: {
|
props: {
|
||||||
msgId: {
|
msgId: {
|
||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user