feat: oss

This commit is contained in:
dap 2024-10-05 16:38:47 +08:00
parent 8f66be32c1
commit a7512adcec
6 changed files with 502 additions and 62 deletions

View File

@ -0,0 +1,53 @@
import type { OssFile } from './model';
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
import { ContentTypeEnum } from '#/api/helper';
import { requestClient } from '#/api/request';
enum Api {
ossDownload = '/resource/oss/download',
ossInfo = '/resource/oss/listByIds',
ossList = '/resource/oss/list',
ossUpload = '/resource/oss/upload',
root = '/resource/oss',
}
export function ossList(params?: PageQuery) {
return requestClient.get<PageResult<OssFile>>(Api.ossList, { params });
}
export function ossInfo(ossIds: IDS) {
return requestClient.get<OssFile>(`${Api.ossInfo}/${ossIds}`);
}
/**
* @deprecated
* @param file
* @returns void
*/
export function ossUpload(file: any) {
const formData = new FormData();
formData.append('file', file);
return requestClient.postWithMsg(Api.ossUpload, formData, {
headers: { 'Content-Type': ContentTypeEnum.FORM_DATA },
timeout: 30 * 1000,
});
}
/**
*
* @param ossId ossId
* @returns blob
*/
export function ossDownload(ossId: ID) {
return requestClient.get<Blob>(`${Api.ossDownload}/${ossId}`, {
responseType: 'blob',
timeout: 30 * 1000,
isTransformResponse: false,
});
}
export function ossRemove(ossIds: IDS) {
return requestClient.deleteWithMsg<void>(`${Api.root}/${ossIds}`);
}

View File

@ -0,0 +1,28 @@
export interface OssFile {
ossId: string;
fileName: string;
originalName: string;
fileSuffix: string;
url: string;
createTime: string;
createBy: number;
createByName: string;
service: string;
}
export interface OssConfig {
ossConfigId: number;
configKey: string;
accessKey: string;
secretKey: string;
bucketName: string;
prefix: string;
endpoint: string;
domain: string;
isHttps: string;
region: string;
status: string;
ext1: string;
remark: string;
accessPolicy: string;
}

View File

@ -1,6 +1,8 @@
import { DictEnum } from '@vben/constants';
import { type FormSchemaGetter, z } from '#/adapter';
import { Tag } from 'ant-design-vue';
import { type FormSchemaGetter, type VxeGridProps, z } from '#/adapter';
import { getDictOptions } from '#/utils/dict';
const accessPolicyOptions = [
@ -30,6 +32,56 @@ export const querySchema: FormSchemaGetter = () => [
},
];
export const columns: VxeGridProps['columns'] = [
{ type: 'checkbox', width: 60 },
{
title: '配置名称',
field: 'configKey',
},
{
title: '访问站点',
field: 'endpoint',
showOverflow: true,
},
{
title: '桶名称',
field: 'bucketName',
},
{
title: '域',
field: 'region',
},
{
title: '权限桶类型',
field: 'accessPolicy',
slots: {
default: ({ row }) => {
const current = accessPolicyOptions.find(
(item) => item.value === row.accessPolicy,
);
if (current) {
return <Tag color={current.color}>{current.label}</Tag>;
}
return '未知类型';
},
},
},
{
title: '是否默认',
field: 'status',
slots: {
default: 'status',
},
},
{
field: 'action',
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
},
];
export const drawerSchema: FormSchemaGetter = () => [
{
component: 'Input',

View File

@ -1,51 +1,174 @@
<script setup lang="ts">
import { Page, useVbenDrawer } from '@vben/common-ui';
import { $t } from '@vben/locales';
import type { Recordable } from '@vben/types';
import { Card } from 'ant-design-vue';
import { useAccess } from '@vben/access';
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
import { useVbenForm } from '#/adapter';
import { Modal, Popconfirm, Space } from 'ant-design-vue';
import dayjs from 'dayjs';
import { querySchema } from './data';
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter';
import {
ossConfigChangeStatus,
ossConfigList,
ossConfigRemove,
} from '#/api/system/oss-config';
import { TableSwitch } from '#/components/table';
import { columns, querySchema } from './data';
import ossConfigDrawer from './oss-config-drawer.vue';
const formOptions: VbenFormProps = {
schema: querySchema(),
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
};
const gridOptions: VxeGridProps = {
checkboxConfig: {
//
highlight: true,
//
reserve: true,
//
// trigger: 'row',
},
columns,
height: 'auto',
keepSource: true,
pagerConfig: {},
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
//
if (formValues?.createTime) {
formValues.params = {
beginTime: dayjs(formValues.createTime[0]).format(
'YYYY-MM-DD 00:00:00',
),
endTime: dayjs(formValues.createTime[1]).format(
'YYYY-MM-DD 23:59:59',
),
};
Reflect.deleteProperty(formValues, 'createTime');
} else {
Reflect.deleteProperty(formValues, 'params');
}
return await ossConfigList({
pageNum: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
isHover: true,
keyField: 'ossConfigId',
},
round: true,
align: 'center',
showOverflow: true,
};
const [BasicTable, tableApi] = useVbenVxeGrid({ formOptions, gridOptions });
const [OssConfigDrawer, drawerApi] = useVbenDrawer({
connectedComponent: ossConfigDrawer,
});
function handleAdd() {
drawerApi.setData({ update: false });
drawerApi.setData({});
drawerApi.open();
}
const [QueryForm] = useVbenForm({
//
collapsed: false,
//
commonConfig: {
//
componentProps: {
class: 'w-full',
async function handleEdit(record: Recordable<any>) {
drawerApi.setData({ id: record.ossConfigId });
drawerApi.open();
}
async function handleDelete(row: Recordable<any>) {
await ossConfigRemove(row.ossConfigId);
await tableApi.reload();
}
function handleMultiDelete() {
const rows = tableApi.grid.getCheckboxRecords();
const ids = rows.map((row: any) => row.ossConfigId);
Modal.confirm({
title: '提示',
okType: 'danger',
content: `确认删除选中的${ids.length}条记录吗?`,
onOk: async () => {
await ossConfigRemove(ids);
await tableApi.reload();
},
},
schema: querySchema(),
//
showCollapseButton: true,
submitButtonOptions: {
text: '查询',
},
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
});
});
}
const { hasAccessByCodes } = useAccess();
</script>
<template>
<Page>
<Card>
<QueryForm />
</Card>
<a-button type="primary" @click="handleAdd">
{{ $t('pages.common.add') }}
</a-button>
<OssConfigDrawer />
<Page :auto-content-height="true">
<BasicTable>
<template #toolbar-actions>
<span class="pl-[7px] text-[16px]">oss配置列表</span>
</template>
<template #toolbar-tools>
<Space>
<a-button
danger
type="primary"
v-access:code="['system:ossConfig:remove']"
@click="handleMultiDelete"
>
{{ $t('pages.common.delete') }}
</a-button>
<a-button
type="primary"
v-access:code="['system:ossConfig:add']"
@click="handleAdd"
>
{{ $t('pages.common.add') }}
</a-button>
</Space>
</template>
<template #status="{ row }">
<TableSwitch
v-model="row.status"
:api="() => ossConfigChangeStatus(row)"
:disabled="!hasAccessByCodes(['system:ossConfig:edit'])"
:reload="() => tableApi.reload()"
/>
</template>
<template #action="{ row }">
<Space>
<a-button
size="small"
type="link"
v-access:code="['system:ossConfig:edit']"
@click="handleEdit(row)"
>
{{ $t('pages.common.edit') }}
</a-button>
<Popconfirm
placement="left"
title="确认删除?"
@confirm="handleDelete(row)"
>
<a-button
danger
size="small"
type="link"
v-access:code="['system:ossConfig:remove']"
@click.stop=""
>
{{ $t('pages.common.delete') }}
</a-button>
</Popconfirm>
</Space>
</template>
</BasicTable>
<OssConfigDrawer @reload="tableApi.reload()" />
</Page>
</template>

View File

@ -1,4 +1,4 @@
import type { FormSchemaGetter } from '#/adapter';
import type { FormSchemaGetter, VxeGridProps } from '#/adapter';
export const querySchema: FormSchemaGetter = () => [
{
@ -27,3 +27,46 @@ export const querySchema: FormSchemaGetter = () => [
label: '创建时间',
},
];
export const columns: VxeGridProps['columns'] = [
{ type: 'checkbox', width: 60 },
{
title: '文件名',
field: 'fileName',
showOverflow: true,
},
{
title: '文件原名',
field: 'originalName',
showOverflow: true,
},
{
title: '文件拓展名',
field: 'fileSuffix',
},
{
title: '文件预览',
field: 'url',
showOverflow: true,
slots: { default: 'url' },
},
{
title: '创建时间',
field: 'createTime',
},
{
title: '上传人',
field: 'createByName',
},
{
title: '服务商',
field: 'service',
},
{
field: 'action',
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
},
];

View File

@ -1,53 +1,194 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import { onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { Page } from '@vben/common-ui';
import { Page, type VbenFormProps } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { Card, Switch } from 'ant-design-vue';
import {
Image,
message,
Modal,
Popconfirm,
Space,
Switch,
Tooltip,
} from 'ant-design-vue';
import dayjs from 'dayjs';
import { useVbenForm } from '#/adapter';
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter';
import { configInfoByKey } from '#/api/system/config';
import { ossDownload, ossList, ossRemove } from '#/api/system/oss';
import { downloadByData } from '#/utils/file/download';
import { querySchema } from './data';
import { columns, querySchema } from './data';
const router = useRouter();
const formOptions: VbenFormProps = {
schema: querySchema(),
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
};
const [QueryForm] = useVbenForm({
//
collapsed: false,
//
commonConfig: {
//
componentProps: {
class: 'w-full',
const gridOptions: VxeGridProps = {
checkboxConfig: {
//
highlight: true,
//
reserve: true,
//
// trigger: 'row',
},
columns,
height: 'auto',
keepSource: true,
pagerConfig: {},
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
//
if (formValues?.createTime) {
formValues.params = {
beginTime: dayjs(formValues.createTime[0]).format(
'YYYY-MM-DD 00:00:00',
),
endTime: dayjs(formValues.createTime[1]).format(
'YYYY-MM-DD 23:59:59',
),
};
Reflect.deleteProperty(formValues, 'createTime');
} else {
Reflect.deleteProperty(formValues, 'params');
}
return await ossList({
pageNum: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
schema: querySchema(),
//
showCollapseButton: true,
submitButtonOptions: {
text: '查询',
rowConfig: {
isHover: true,
keyField: 'ossId',
},
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
});
round: true,
align: 'center',
showOverflow: true,
};
const [BasicTable, tableApi] = useVbenVxeGrid({ formOptions, gridOptions });
async function handleDownload(row: Recordable<any>) {
const hideLoading = message.loading($t('pages.common.downloadLoading'), 0);
try {
const data = await ossDownload(row.ossId);
downloadByData(data, row.originalName);
} finally {
hideLoading();
}
}
async function handleDelete(row: Recordable<any>) {
await ossRemove(row.ossId);
await tableApi.reload();
}
function handleMultiDelete() {
const rows = tableApi.grid.getCheckboxRecords();
const ids = rows.map((row: any) => row.ossId);
Modal.confirm({
title: '提示',
okType: 'danger',
content: `确认删除选中的${ids.length}条记录吗?`,
onOk: async () => {
await ossRemove(ids);
await tableApi.reload();
},
});
}
const router = useRouter();
function handleToSettings() {
router.push('/system/oss-config');
}
const preview = ref(false);
onMounted(async () => {
const resp = await configInfoByKey('sys.oss.previewListResource');
preview.value = Boolean(resp);
});
function isImageFile(ext: string) {
const supportList = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
return supportList.some((item) => ext.toLocaleLowerCase().includes(item));
}
</script>
<template>
<Page>
<Card>
<QueryForm />
</Card>
<div class="flex items-center gap-2">
<span>预览图片: </span>
<Switch v-model:checked="preview" />
</div>
<a-button @click="() => router.push('/system/oss-config')">配置</a-button>
<Page :auto-content-height="true">
<BasicTable>
<template #toolbar-actions>
<span class="pl-[7px] text-[16px]">文件列表</span>
</template>
<template #toolbar-tools>
<Space>
<Tooltip title="预览图片">
<Switch v-model:checked="preview" />
</Tooltip>
<a-button
v-access:code="['system:ossConfig:list']"
@click="handleToSettings"
>
配置管理
</a-button>
<a-button
danger
type="primary"
v-access:code="['system:oss:delete']"
@click="handleMultiDelete"
>
{{ $t('pages.common.delete') }}
</a-button>
<a-button> 上传 没做 </a-button>
</Space>
</template>
<template #url="{ row }">
<Image
v-if="preview && isImageFile(row.url)"
:src="row.url"
height="50px"
/>
<span v-else>{{ row.url }}</span>
</template>
<template #action="{ row }">
<Space>
<a-button
size="small"
type="link"
v-access:code="['system:oss:edit']"
@click="handleDownload(row)"
>
{{ $t('pages.common.download') }}
</a-button>
<Popconfirm
placement="left"
title="确认删除?"
@confirm="handleDelete(row)"
>
<a-button
danger
size="small"
type="link"
v-access:code="['system:oss:delete']"
@click.stop=""
>
{{ $t('pages.common.delete') }}
</a-button>
</Popconfirm>
</Space>
</template>
</BasicTable>
</Page>
</template>