Browse Source

feat(oss): add bulk delete

pull/449/head
cKey 4 years ago
parent
commit
acba916b30
  1. 6
      apps/vue/src/api/oss-management/model/ossModel.ts
  2. 9
      apps/vue/src/api/oss-management/oss.ts
  3. 38
      apps/vue/src/views/account/center/FileList.vue
  4. 18
      apps/vue/src/views/account/center/ShareList.vue
  5. 54
      apps/vue/src/views/account/center/data.ts
  6. 41
      apps/vue/src/views/oss-management/objects/components/OssTable.vue
  7. 54
      apps/vue/src/views/oss-management/objects/datas/TableData.ts
  8. 5
      aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Domain.Shared/LINGYUN/Abp/OssManagement/Localization/Resources/en.json
  9. 5
      aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Domain.Shared/LINGYUN/Abp/OssManagement/Localization/Resources/zh-Hans.json
  10. 2
      aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/OssObjectController.cs
  11. 2
      aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Properties/launchSettings.json

6
apps/vue/src/api/oss-management/model/ossModel.ts

@ -57,6 +57,12 @@ export interface OssCopyOrMove {
toName?: string; toName?: string;
} }
export interface OssObjectBulkDelete {
bucket: string;
path?: string;
objects: string[];
}
export interface FileShareInput { export interface FileShareInput {
name: string; name: string;
path?: string; path?: string;

9
apps/vue/src/api/oss-management/oss.ts

@ -2,6 +2,7 @@ import { defAbpHttp } from '/@/utils/http/abp';
import { import {
OssObject, OssObject,
OssObjectCreate, OssObjectCreate,
OssObjectBulkDelete,
OssContainer, OssContainer,
GetOssObjectRequest, GetOssObjectRequest,
GetOssObjectPagedRequest, GetOssObjectPagedRequest,
@ -17,6 +18,7 @@ import { UploadFileParams } from '/#/axios';
enum Api { enum Api {
CreateObject = '/api/oss-management/objects', CreateObject = '/api/oss-management/objects',
DeleteObject = '/api/oss-management/objects', DeleteObject = '/api/oss-management/objects',
BulkDeleteObject = '/api/oss-management/objects/bulk-delete',
GetObject = '/api/oss-management/objects', GetObject = '/api/oss-management/objects',
GetObjects = '/api/oss-management/containes/objects', GetObjects = '/api/oss-management/containes/objects',
CreateContainer = '/api/oss-management/containes/{name}', CreateContainer = '/api/oss-management/containes/{name}',
@ -179,6 +181,13 @@ export const deleteObject = (input: GetOssObjectRequest) => {
); );
}; };
export const bulkDeleteObject = (input: OssObjectBulkDelete) => {
return defAbpHttp.post<void>({
url: Api.BulkDeleteObject,
params: input,
});
}
export const getObject = (input: GetOssObjectRequest) => { export const getObject = (input: GetOssObjectRequest) => {
return defAbpHttp.get<OssObject>({ return defAbpHttp.get<OssObject>({
url: Api.GetObject, url: Api.GetObject,

38
apps/vue/src/views/account/center/FileList.vue

@ -6,11 +6,11 @@
:stop-button-propagation="true" :stop-button-propagation="true"
:actions="[ :actions="[
{ {
color: 'error', auth: 'AbpOssManagement.OssObject.Download',
label: L('Delete'), ifShow: !record.isFolder,
icon: 'ant-design:delete-outlined', label: L('Objects:Download'),
ifShow: deleteEnabled, icon: 'ant-design:download-outlined',
onClick: handleDelete.bind(null, record), onClick: handleDownload.bind(null, record),
}, },
{ {
label: L('Share'), label: L('Share'),
@ -18,6 +18,13 @@
ifShow: shareEnabled, ifShow: shareEnabled,
onClick: handleShare.bind(null, record), onClick: handleShare.bind(null, record),
}, },
{
color: 'error',
label: L('Delete'),
icon: 'ant-design:delete-outlined',
ifShow: deleteEnabled,
onClick: handleDelete.bind(null, record),
},
]" ]"
/> />
</template> </template>
@ -29,7 +36,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineProps, defineEmits, watchEffect, nextTick } from 'vue'; import { computed, watchEffect, nextTick } from 'vue';
import { useLocalization } from '/@/hooks/abp/useLocalization'; import { useLocalization } from '/@/hooks/abp/useLocalization';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { BasicTable, TableAction, useTable } from '/@/components/Table'; import { BasicTable, TableAction, useTable } from '/@/components/Table';
@ -43,6 +50,7 @@
import { getList as getPrivates } from '/@/api/oss-management/private'; import { getList as getPrivates } from '/@/api/oss-management/private';
import { getList as getPublices } from '/@/api/oss-management/public'; import { getList as getPublices } from '/@/api/oss-management/public';
import { share } from '/@/api/oss-management/private'; import { share } from '/@/api/oss-management/private';
import { generateOssUrl } from '/@/api/oss-management/oss';
const props = defineProps({ const props = defineProps({
selectGroup: { selectGroup: {
@ -82,7 +90,7 @@
immediate: false, immediate: false,
rowSelection: { type: 'checkbox' }, rowSelection: { type: 'checkbox' },
actionColumn: { actionColumn: {
width: 180, width: 240,
title: L('Actions'), title: L('Actions'),
dataIndex: 'action', dataIndex: 'action',
slots: { customRender: 'action' }, slots: { customRender: 'action' },
@ -92,6 +100,13 @@
const shareEnabled = computed(() => { const shareEnabled = computed(() => {
return props.selectGroup === 'private'; return props.selectGroup === 'private';
}); });
const bucket = computed(() => {
switch (props.selectGroup) {
case 'private': return 'users';
case 'public': return 'public';
default: return '';
}
})
const [registershareForm, { validate, setFieldsValue, resetFields }] = useForm({ const [registershareForm, { validate, setFieldsValue, resetFields }] = useForm({
labelAlign: 'left', labelAlign: 'left',
@ -117,6 +132,15 @@
}); });
} }
function handleDownload(record) {
const link = document.createElement('a');
link.style.display = 'none';
link.href = generateOssUrl(bucket.value, record.path, record.name);
link.setAttribute('download', record.name);
document.body.appendChild(link);
link.click();
}
function handleDelete(record) { function handleDelete(record) {
createConfirm({ createConfirm({
iconType: 'warning', iconType: 'warning',

18
apps/vue/src/views/account/center/ShareList.vue

@ -4,6 +4,11 @@
<TableAction <TableAction
:stop-button-propagation="true" :stop-button-propagation="true"
:actions="[ :actions="[
{
label: L('CopyLink'),
icon: 'ant-design:copy-outlined',
onClick: handleCopyLink.bind(null, record),
},
{ {
color: 'error', color: 'error',
label: L('Delete'), label: L('Delete'),
@ -18,9 +23,10 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineProps, defineEmits, watchEffect } from 'vue'; import { watchEffect } from 'vue';
import { useLocalization } from '/@/hooks/abp/useLocalization'; import { useLocalization } from '/@/hooks/abp/useLocalization';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { copyTextToClipboard } from '/@/hooks/web/useCopyToClipboard';
import { BasicTable, TableAction, useTable } from '/@/components/Table'; import { BasicTable, TableAction, useTable } from '/@/components/Table';
import { getShareDataColumns } from './data'; import { getShareDataColumns } from './data';
import { getShareList as getShares } from '/@/api/oss-management/private'; import { getShareList as getShares } from '/@/api/oss-management/private';
@ -40,7 +46,7 @@
const emit = defineEmits(['delete:file:share']); const emit = defineEmits(['delete:file:share']);
const { L } = useLocalization('AbpOssManagement', 'AbpUi'); const { L } = useLocalization('AbpOssManagement', 'AbpUi');
const { createConfirm } = useMessage(); const { createConfirm, createMessage } = useMessage();
const [registerTable, { setTableData }] = useTable({ const [registerTable, { setTableData }] = useTable({
rowKey: 'url', rowKey: 'url',
columns: getShareDataColumns(), columns: getShareDataColumns(),
@ -75,6 +81,14 @@
}); });
} }
function handleCopyLink(record) {
let url = window.location.origin
url += '/api/files/share/' + record.url
if (copyTextToClipboard(url)) {
createMessage.success(L('Successful'))
}
}
function handleDelete(record) { function handleDelete(record) {
createConfirm({ createConfirm({
iconType: 'warning', iconType: 'warning',

54
apps/vue/src/views/account/center/data.ts

@ -19,32 +19,6 @@ export function getDataColumns(): BasicColumn[] {
width: 300, width: 300,
sorter: true, sorter: true,
}, },
{
title: L('DisplayName:CreationDate'),
dataIndex: 'creationDate',
align: 'left',
width: 160,
sorter: true,
format: (text) => {
if (text) {
return formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss');
}
return text;
},
},
{
title: L('DisplayName:LastModifiedDate'),
dataIndex: 'lastModifiedDate',
align: 'left',
width: 160,
sorter: true,
format: (text) => {
if (text) {
return formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss');
}
return text;
},
},
{ {
title: L('DisplayName:FileType'), title: L('DisplayName:FileType'),
dataIndex: 'isFolder', dataIndex: 'isFolder',
@ -59,7 +33,7 @@ export function getDataColumns(): BasicColumn[] {
title: L('DisplayName:Size'), title: L('DisplayName:Size'),
dataIndex: 'size', dataIndex: 'size',
align: 'left', align: 'left',
width: 'auto', width: 150,
sorter: true, sorter: true,
format: (text) => { format: (text) => {
const size = Number(text); const size = Number(text);
@ -84,6 +58,32 @@ export function getDataColumns(): BasicColumn[] {
return kb + ' KB'; return kb + ' KB';
}, },
}, },
{
title: L('DisplayName:CreationDate'),
dataIndex: 'creationDate',
align: 'left',
width: 160,
sorter: true,
format: (text) => {
if (text) {
return formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss');
}
return text;
},
},
{
title: L('DisplayName:LastModifiedDate'),
dataIndex: 'lastModifiedDate',
align: 'left',
width: 'auto',
sorter: true,
format: (text) => {
if (text) {
return formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss');
}
return text;
},
},
]; ];
} }

41
apps/vue/src/views/oss-management/objects/components/OssTable.vue

@ -48,6 +48,14 @@
@click="handleUpload" @click="handleUpload"
>{{ L('Objects:UploadFile') }}</a-button >{{ L('Objects:UploadFile') }}</a-button
> >
<a-button
v-if="hasPermission('AbpOssManagement.OssObject.Delete')"
:disabled="selectCount <= 0"
type="primary"
danger
@click="handleBulkDelete"
>{{ L('Objects:BulkDelete') }}</a-button
>
</template> </template>
<template #action="{ record }"> <template #action="{ record }">
<TableAction <TableAction
@ -87,12 +95,12 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, unref } from 'vue'; import { computed, defineComponent, unref } from 'vue';
import { Card, Modal, Tree, Select } from 'ant-design-vue'; import { Card, Modal, Tree, Select, message } from 'ant-design-vue';
import { useLocalization } from '/@/hooks/abp/useLocalization'; import { useLocalization } from '/@/hooks/abp/useLocalization';
import { usePermission } from '/@/hooks/web/usePermission'; import { usePermission } from '/@/hooks/web/usePermission';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import { BasicTable, TableAction, useTable } from '/@/components/Table'; import { BasicTable, TableAction, useTable } from '/@/components/Table';
import { getObjects, deleteObject, generateOssUrl } from '/@/api/oss-management/oss'; import { getObjects, deleteObject, bulkDeleteObject, generateOssUrl } from '/@/api/oss-management/oss';
import { getDataColumns } from '../datas/TableData'; import { getDataColumns } from '../datas/TableData';
import { useObjects } from '../hooks/useObjects'; import { useObjects } from '../hooks/useObjects';
import OssUploadModal from './OssUploadModal.vue'; import OssUploadModal from './OssUploadModal.vue';
@ -132,7 +140,7 @@
beforeFetch, beforeFetch,
handleContainerChange, handleContainerChange,
} = useObjects(); } = useObjects();
const [registerTable, { reload, setPagination }] = useTable({ const [registerTable, { reload, setPagination, getSelectRowKeys }] = useTable({
rowKey: 'name', rowKey: 'name',
title: L('DisplayName:OssObject'), title: L('DisplayName:OssObject'),
columns: getDataColumns(), columns: getDataColumns(),
@ -148,6 +156,9 @@
striped: false, striped: false,
useSearchForm: false, useSearchForm: false,
showTableSetting: true, showTableSetting: true,
// ,
clickToRowSelect: false,
clearSelectOnPageChange: true,
tableSetting: { tableSetting: {
redo: false, redo: false,
}, },
@ -166,6 +177,9 @@
const lockTree = computed(() => { const lockTree = computed(() => {
return unref(bucket) ? false : true; return unref(bucket) ? false : true;
}); });
const selectCount = computed(() => {
return getSelectRowKeys().length;
})
function handleSelectFolder(folders, e) { function handleSelectFolder(folders, e) {
path.value = e.node.dataRef.path + folders[0]; path.value = e.node.dataRef.path + folders[0];
@ -187,6 +201,25 @@
object: record.name, object: record.name,
}).then(() => { }).then(() => {
reload(); reload();
message.success(L('Successful'));
});
},
});
}
function handleBulkDelete() {
Modal.warning({
title: L('AreYouSure'),
content: L('Objects:WillBeBulkDeletedMessage'),
okCancel: true,
onOk: () => {
bulkDeleteObject({
bucket: unref(bucket),
path: unref(path),
objects: getSelectRowKeys(),
}).then(() => {
reload();
message.success(L('Successful'));
}); });
}, },
}); });
@ -227,6 +260,7 @@
L, L,
folders, folders,
lockTree, lockTree,
selectCount,
expandedKeys, expandedKeys,
fetchFolders, fetchFolders,
handleSelectFolder, handleSelectFolder,
@ -238,6 +272,7 @@
hasPermission, hasPermission,
registerTable, registerTable,
handleDelete, handleDelete,
handleBulkDelete,
handleDownload, handleDownload,
registerFolderModal, registerFolderModal,
registerPreviewModal, registerPreviewModal,

54
apps/vue/src/views/oss-management/objects/datas/TableData.ts

@ -19,32 +19,6 @@ export function getDataColumns(): BasicColumn[] {
customRender: 'enable', customRender: 'enable',
}, },
}, },
{
title: L('DisplayName:CreationDate'),
dataIndex: 'creationDate',
align: 'left',
width: 160,
sorter: true,
format: (text) => {
if (text) {
return formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss');
}
return text;
},
},
{
title: L('DisplayName:LastModifiedDate'),
dataIndex: 'lastModifiedDate',
align: 'left',
width: 160,
sorter: true,
format: (text) => {
if (text) {
return formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss');
}
return text;
},
},
{ {
title: L('DisplayName:FileType'), title: L('DisplayName:FileType'),
dataIndex: 'isFolder', dataIndex: 'isFolder',
@ -59,7 +33,7 @@ export function getDataColumns(): BasicColumn[] {
title: L('DisplayName:Size'), title: L('DisplayName:Size'),
dataIndex: 'size', dataIndex: 'size',
align: 'left', align: 'left',
width: 'auto', width: 150,
sorter: true, sorter: true,
format: (text) => { format: (text) => {
const size = Number(text); const size = Number(text);
@ -84,5 +58,31 @@ export function getDataColumns(): BasicColumn[] {
return kb + ' KB'; return kb + ' KB';
}, },
}, },
{
title: L('DisplayName:CreationDate'),
dataIndex: 'creationDate',
align: 'left',
width: 160,
sorter: true,
format: (text) => {
if (text) {
return formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss');
}
return text;
},
},
{
title: L('DisplayName:LastModifiedDate'),
dataIndex: 'lastModifiedDate',
align: 'left',
width: 'auto',
sorter: true,
format: (text) => {
if (text) {
return formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss');
}
return text;
},
},
]; ];
} }

5
aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Domain.Shared/LINGYUN/Abp/OssManagement/Localization/Resources/en.json

@ -65,6 +65,7 @@
"PublicDocument": "Public Document", "PublicDocument": "Public Document",
"MyShare": "My Share", "MyShare": "My Share",
"FileList": "Files", "FileList": "Files",
"CopyLink": "Copy Link",
"Share": "Share", "Share": "Share",
"Containers": "Containers", "Containers": "Containers",
"Containers:Create": "Create Container", "Containers:Create": "Create Container",
@ -87,6 +88,8 @@
"Objects:Download": "Download", "Objects:Download": "Download",
"Objects:FileSystem": "File System", "Objects:FileSystem": "File System",
"Objects:Preview": "Preview", "Objects:Preview": "Preview",
"Objects:Root": "Root" "Objects:Root": "Root",
"Objects:BulkDelete": "Bulk Delete",
"Objects:WillBeBulkDeletedMessage": "Multiple selected storage objects will be deleted!"
} }
} }

5
aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Domain.Shared/LINGYUN/Abp/OssManagement/Localization/Resources/zh-Hans.json

@ -65,6 +65,7 @@
"PublicDocument": "公共文档", "PublicDocument": "公共文档",
"MyShare": "我的分享", "MyShare": "我的分享",
"FileList": "文件列表", "FileList": "文件列表",
"CopyLink": "复制链接",
"Share": "分享", "Share": "分享",
"Containers": "容器", "Containers": "容器",
"Containers:Create": "创建容器", "Containers:Create": "创建容器",
@ -87,6 +88,8 @@
"Objects:Download": "下载", "Objects:Download": "下载",
"Objects:FileSystem": "文件管理", "Objects:FileSystem": "文件管理",
"Objects:Preview": "预览", "Objects:Preview": "预览",
"Objects:Root": "根目录" "Objects:Root": "根目录",
"Objects:BulkDelete": "批量删除",
"Objects:WillBeBulkDeletedMessage": "选择的多个存储对象将被删除!"
} }
} }

2
aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/OssObjectController.cs

@ -40,7 +40,7 @@ namespace LINGYUN.Abp.OssManagement
await FileUploader.UploadAsync(input); await FileUploader.UploadAsync(input);
} }
[HttpDelete] [HttpPost]
[Route("bulk-delete")] [Route("bulk-delete")]
public virtual async Task BulkDeleteAsync(BulkDeleteOssObjectInput input) public virtual async Task BulkDeleteAsync(BulkDeleteOssObjectInput input)
{ {

2
aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Properties/launchSettings.json

@ -14,7 +14,7 @@
"launchBrowser": false, "launchBrowser": false,
"applicationUrl": "http://127.0.0.1:30010", "applicationUrl": "http://127.0.0.1:30010",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Production" "ASPNETCORE_ENVIRONMENT": "Development"
} }
} }
} }

Loading…
Cancel
Save