feat: 新增公共上传接口,完善系统配置 Logo、favicon 上传

This commit is contained in:
Charles7c 2023-09-23 13:11:19 +08:00
parent adf7ec5dda
commit ff14ceb53f
6 changed files with 115 additions and 35 deletions

View File

@ -38,3 +38,7 @@ export function listRoleDict(params: RoleParam) {
export function listDict(code: string) { export function listDict(code: string) {
return axios.get<LabelValueState[]>(`${BASE_URL}/dict/${code}`); return axios.get<LabelValueState[]>(`${BASE_URL}/dict/${code}`);
} }
export function upload(data: FormData) {
return axios.post(`${BASE_URL}/file`, data);
}

View File

@ -0,0 +1,13 @@
export default function getFile(file: string | undefined) {
if (file) {
const baseUrl = import.meta.env.VITE_API_BASE_URL;
if (
!file.startsWith('http://') &&
!file.startsWith('https://') &&
!file.startsWith('blob:')
) {
return `${baseUrl}/file/${file}`;
}
}
return file;
}

View File

@ -16,6 +16,7 @@
<br /> <br />
<a-upload <a-upload
:file-list="faviconFile ? [faviconFile] : []" :file-list="faviconFile ? [faviconFile] : []"
accept="image/*"
:show-file-list="false" :show-file-list="false"
:custom-request="handleUploadFavicon" :custom-request="handleUploadFavicon"
@change="handleChangeFavicon" @change="handleChangeFavicon"
@ -32,7 +33,7 @@
v-if="faviconFile && faviconFile.url" v-if="faviconFile && faviconFile.url"
class="arco-upload-list-picture custom-upload-avatar favicon" class="arco-upload-list-picture custom-upload-avatar favicon"
> >
<img :src="faviconFile.url" /> <img :src="getFile(faviconFile.url)" />
<div <div
v-if="isEdit" v-if="isEdit"
class="arco-upload-list-picture-mask favicon" class="arco-upload-list-picture-mask favicon"
@ -59,6 +60,7 @@
<br /> <br />
<a-upload <a-upload
:file-list="logoFile ? [logoFile] : []" :file-list="logoFile ? [logoFile] : []"
accept="image/*"
:show-file-list="false" :show-file-list="false"
:custom-request="handleUploadLogo" :custom-request="handleUploadLogo"
@change="handleChangeLogo" @change="handleChangeLogo"
@ -75,7 +77,7 @@
v-if="logoFile && logoFile.url" v-if="logoFile && logoFile.url"
class="arco-upload-list-picture custom-upload-avatar logo" class="arco-upload-list-picture custom-upload-avatar logo"
> >
<img :src="logoFile.url" /> <img :src="getFile(logoFile.url)" />
<div <div
v-if="isEdit" v-if="isEdit"
class="arco-upload-list-picture-mask logo" class="arco-upload-list-picture-mask logo"
@ -109,8 +111,15 @@
> >
<a-input v-model="form.site_copyright" placeholder="请输入版权信息" /> <a-input v-model="form.site_copyright" placeholder="请输入版权信息" />
</a-form-item> </a-form-item>
<div> <div style="margin-top: 20px">
<a-space> <a-space>
<a-button
v-if="!isEdit"
v-permission="['system:config:reset']"
@click="toResetValue"
>
<template #icon><icon-redo /></template>恢复默认
</a-button>
<a-button <a-button
v-if="!isEdit" v-if="!isEdit"
v-permission="['system:config:update']" v-permission="['system:config:update']"
@ -119,13 +128,6 @@
> >
<template #icon><icon-edit /></template>修改 <template #icon><icon-edit /></template>修改
</a-button> </a-button>
<a-button
v-if="!isEdit"
v-permission="['system:config:reset']"
@click="toResetValue"
>
<template #icon><icon-redo /></template>恢复默认
</a-button>
<a-button <a-button
v-if="isEdit" v-if="isEdit"
v-permission="['system:config:update']" v-permission="['system:config:update']"
@ -166,6 +168,8 @@
save, save,
resetValue, resetValue,
} from '@/api/system/config'; } from '@/api/system/config';
import { upload } from '@/api/common';
import getFile from '@/utils/file';
const { proxy } = getCurrentInstance() as any; const { proxy } = getCurrentInstance() as any;
const dataList = ref<DataRecord[]>([]); const dataList = ref<DataRecord[]>([]);
@ -189,6 +193,20 @@
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/**
* 重置表单
*/
const reset = () => {
form.value = {
site_title: siteTitle.value?.value,
site_copyright: siteCopyright.value?.value,
site_logo: siteLogo.value?.value,
site_favicon: siteFavicon.value?.value,
};
logoFile.value.url = siteLogo.value?.value;
faviconFile.value.url = siteFavicon.value?.value;
};
/** /**
* 查询配置 * 查询配置
*/ */
@ -207,14 +225,7 @@
siteFavicon.value = dataList.value.find( siteFavicon.value = dataList.value.find(
(option) => option.code === 'site_favicon' (option) => option.code === 'site_favicon'
); );
form.value = { reset();
site_title: siteTitle.value?.value,
site_copyright: siteCopyright.value?.value,
site_logo: siteLogo.value?.value,
site_favicon: siteFavicon.value?.value,
};
logoFile.value.url = siteLogo.value?.value;
faviconFile.value.url = siteFavicon.value?.value;
}; };
getConfig(); getConfig();
@ -254,8 +265,28 @@
* @param options / * @param options /
*/ */
const handleUploadLogo = (options: RequestOption) => { const handleUploadLogo = (options: RequestOption) => {
console.log(options);
const controller = new AbortController(); const controller = new AbortController();
(async function requestWrap() {
const {
onProgress,
onError,
onSuccess,
fileItem,
name = 'file',
} = options;
onProgress(20);
const formData = new FormData();
formData.append(name as string, fileItem.file as Blob);
upload(formData)
.then((res) => {
onSuccess(res);
form.value.site_logo = res.data;
proxy.$message.success(res.msg);
})
.catch((error) => {
onError(error);
});
})();
return { return {
abort() { abort() {
controller.abort(); controller.abort();
@ -269,8 +300,28 @@
* @param options / * @param options /
*/ */
const handleUploadFavicon = (options: RequestOption) => { const handleUploadFavicon = (options: RequestOption) => {
console.log(options);
const controller = new AbortController(); const controller = new AbortController();
(async function requestWrap() {
const {
onProgress,
onError,
onSuccess,
fileItem,
name = 'file',
} = options;
onProgress(20);
const formData = new FormData();
formData.append(name as string, fileItem.file as Blob);
upload(formData)
.then((res) => {
onSuccess(res);
form.value.site_favicon = res.data;
proxy.$message.success(res.msg);
})
.catch((error) => {
onError(error);
});
})();
return { return {
abort() { abort() {
controller.abort(); controller.abort();
@ -287,7 +338,6 @@
const handleChangeLogo = (_: any, currentFile: any) => { const handleChangeLogo = (_: any, currentFile: any) => {
logoFile.value = { logoFile.value = {
...currentFile, ...currentFile,
// url: URL.createObjectURL(currentFile.file),
}; };
}; };
@ -300,7 +350,6 @@
const handleChangeFavicon = (_: any, currentFile: any) => { const handleChangeFavicon = (_: any, currentFile: any) => {
faviconFile.value = { faviconFile.value = {
...currentFile, ...currentFile,
// url: URL.createObjectURL(currentFile.file),
}; };
}; };
@ -335,13 +384,6 @@
const toEdit = () => { const toEdit = () => {
isEdit.value = true; isEdit.value = true;
}; };
/**
* 重置表单
*/
const reset = () => {
proxy.$refs.formRef?.resetFields();
};
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@ -359,7 +401,8 @@
line-height: 16px; line-height: 16px;
} }
.arco-form .image-item { .arco-form .image-item,
.input-item {
margin-bottom: 0; margin-bottom: 0;
} }

View File

@ -1,3 +1,3 @@
export default { export default {
'menu.system.config': '系统配置(开发中)', 'menu.system.config': '系统配置',
}; };

View File

@ -3,6 +3,7 @@
<a-space :size="54"> <a-space :size="54">
<a-upload <a-upload
:file-list="avatarList" :file-list="avatarList"
accept="image/*"
:show-file-list="false" :show-file-list="false"
list-type="picture-card" list-type="picture-card"
:show-upload-button="true" :show-upload-button="true"

View File

@ -16,12 +16,15 @@
package top.charles7c.cnadmin.webapi.controller.common; package top.charles7c.cnadmin.webapi.controller.common;
import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@ -31,21 +34,23 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import top.charles7c.cnadmin.common.base.BaseEnum; import top.charles7c.cnadmin.common.base.BaseEnum;
import top.charles7c.cnadmin.common.config.properties.LocalStorageProperties;
import top.charles7c.cnadmin.common.config.properties.ProjectProperties; import top.charles7c.cnadmin.common.config.properties.ProjectProperties;
import top.charles7c.cnadmin.common.constant.CacheConsts; import top.charles7c.cnadmin.common.constant.CacheConsts;
import top.charles7c.cnadmin.common.model.query.SortQuery; import top.charles7c.cnadmin.common.model.query.SortQuery;
import top.charles7c.cnadmin.common.model.vo.LabelValueVO; import top.charles7c.cnadmin.common.model.vo.LabelValueVO;
import top.charles7c.cnadmin.common.model.vo.R; import top.charles7c.cnadmin.common.model.vo.R;
import top.charles7c.cnadmin.common.util.FileUtils;
import top.charles7c.cnadmin.common.util.validate.CheckUtils;
import top.charles7c.cnadmin.common.util.validate.ValidationUtils;
import top.charles7c.cnadmin.monitor.annotation.Log; import top.charles7c.cnadmin.monitor.annotation.Log;
import top.charles7c.cnadmin.system.model.query.DeptQuery; import top.charles7c.cnadmin.system.model.query.DeptQuery;
import top.charles7c.cnadmin.system.model.query.MenuQuery; import top.charles7c.cnadmin.system.model.query.MenuQuery;
@ -72,6 +77,20 @@ public class CommonController {
private final RoleService roleService; private final RoleService roleService;
private final DictItemService dictItemService; private final DictItemService dictItemService;
private final ProjectProperties projectProperties; private final ProjectProperties projectProperties;
private final LocalStorageProperties localStorageProperties;
@Operation(summary = "上传文件", description = "上传文件")
@PostMapping("/file")
public R<String> upload(@NotNull(message = "文件不能为空") MultipartFile file) {
ValidationUtils.throwIf(file::isEmpty, "文件不能为空");
Long maxSizeInMb = localStorageProperties.getMaxSizeInMb();
CheckUtils.throwIf(file.getSize() > maxSizeInMb * 1024 * 1024, "请上传小于 {}MB 的文件", maxSizeInMb);
String filePath = localStorageProperties.getPath().getFile();
File newFile = FileUtils.upload(file, filePath, false);
CheckUtils.throwIfNull(newFile, "上传文件失败");
assert null != newFile;
return R.ok("上传成功", newFile.getName());
}
@Operation(summary = "查询部门树", description = "查询树结构的部门列表") @Operation(summary = "查询部门树", description = "查询树结构的部门列表")
@GetMapping("/tree/dept") @GetMapping("/tree/dept")