新增前端vue

This commit is contained in:
2025-12-19 17:34:24 +08:00
parent 065469cc90
commit ba7bcc3da9
12 changed files with 199 additions and 83 deletions

View File

@@ -1,11 +0,0 @@
package com.jeesite.modules.app.utils;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
public class DateUtils {
}

View File

@@ -42,17 +42,12 @@ public class FileDownloadUtils {
// 1. 构建完整文件路径 // 1. 构建完整文件路径
Path fullFilePath = Paths.get(orgFileName); Path fullFilePath = Paths.get(orgFileName);
File file = fullFilePath.toFile(); File file = fullFilePath.toFile();
// 2. 校验文件合法性 // 2. 校验文件合法性
validateFile(file); validateFile(file);
// 3. 清理并编码文件名(避免特殊字符导致的解析异常) // 3. 清理并编码文件名(避免特殊字符导致的解析异常)
String encodedFileName = encodeFileName(fileName); String encodedFileName = encodeFileName(fileName);
// 4. 设置响应头(核心修复:解决下划线/乱码问题) // 4. 设置响应头(核心修复:解决下划线/乱码问题)
setDownloadResponseHeader(response, encodedFileName, file.length()); setDownloadResponseHeader(response, encodedFileName, file.length());
// 5. 读取文件并写入响应输出流try-with-resources自动关闭流
try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(fullFilePath)); try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(fullFilePath));
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) { OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
FileCopyUtils.copy(inputStream, outputStream); FileCopyUtils.copy(inputStream, outputStream);
@@ -74,16 +69,12 @@ public class FileDownloadUtils {
// 1. 构建完整文件路径 // 1. 构建完整文件路径
Path fullFilePath = Paths.get(orgFileName); Path fullFilePath = Paths.get(orgFileName);
File file = fullFilePath.toFile(); File file = fullFilePath.toFile();
// 2. 校验文件合法性 // 2. 校验文件合法性
validateFile(file); validateFile(file);
// 3. 读取文件字节数组 // 3. 读取文件字节数组
byte[] fileBytes = Files.readAllBytes(fullFilePath); byte[] fileBytes = Files.readAllBytes(fullFilePath);
// 4. 清理并编码文件名 // 4. 清理并编码文件名
String encodedFileName = encodeFileName(fileName); String encodedFileName = encodeFileName(fileName);
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
// 核心修复使用RFC 5987标准移除多余双引号避免下划线问题 // 核心修复使用RFC 5987标准移除多余双引号避免下划线问题
headers.set(HttpHeaders.CONTENT_DISPOSITION, headers.set(HttpHeaders.CONTENT_DISPOSITION,
@@ -149,14 +140,8 @@ public class FileDownloadUtils {
* @return 编码后的文件名 * @return 编码后的文件名
*/ */
private static String encodeFileName(String fileName) { private static String encodeFileName(String fileName) {
try { return URLEncoder.encode(fileName, StandardCharsets.UTF_8)
// 修复:将空格编码为%20而非+,避免浏览器解析为下划线 .replace("+", "%20");
return URLEncoder.encode(fileName, StandardCharsets.UTF_8.name())
.replace("+", "%20");
} catch (UnsupportedEncodingException e) {
// 理论上UTF-8不会抛出此异常兜底返回原文件名
return fileName;
}
} }
} }

View File

@@ -14,7 +14,7 @@ import java.util.concurrent.Executors;
public class MailSendUtils { public class MailSendUtils {
private static final ExecutorService MAIL_EXECUTOR = Executors.newFixedThreadPool(5); private static final ExecutorService MAIL_EXECUTOR = Executors.newFixedThreadPool(5);
private static String FILE_PATH = "/ogsapp/files"; private static final String FILE_PATH = "/ogsapp/files";
/** /**
* 同步发送HTML格式邮件 * 同步发送HTML格式邮件

View File

@@ -0,0 +1,48 @@
package com.jeesite.modules.app.utils;
import java.nio.file.*;
import java.text.DecimalFormat;
public class MyFileUtils {
private static final long UNIT_BASE = 1024;
private static final String[] UNITS = {"B", "KB", "MB", "GB", "TB"};
private static final LoggerUtils logger = LoggerUtils.getInstance();
public static String formatFileSize(long sizeInBytes, int decimalPlaces) {
if (sizeInBytes == 0) {
return "0 " + UNITS[0];
}
long absSize = Math.abs(sizeInBytes);
String negativeFlag = sizeInBytes < 0 ? "-" : "";
int validDecimalPlaces = Math.max(0, Math.min(decimalPlaces, 4));
String formatPattern = validDecimalPlaces == 0 ? "#0" : "#0." + "0".repeat(validDecimalPlaces);
DecimalFormat df = new DecimalFormat(formatPattern);
int unitIndex = 0;
double formattedSize = absSize;
while (formattedSize >= UNIT_BASE && unitIndex < UNITS.length - 1) {
formattedSize /= UNIT_BASE;
unitIndex++;
}
return negativeFlag + df.format(formattedSize) + " " + UNITS[unitIndex];
}
public static void moveFile(String sourceName, String targetName, boolean overwrite) {
try {
Path sourcePath = Paths.get(sourceName);
Path targetPath = Paths.get(targetName);
CopyOption[] options = overwrite
? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE}
: new CopyOption[]{StandardCopyOption.ATOMIC_MOVE};
Files.move(sourcePath, targetPath, options);
} catch (Exception e) {
logger.error(e.getMessage());
}
}
}

View File

@@ -32,4 +32,17 @@ public class vId {
long rand = Math.abs(RAND.nextLong()) % (long) Math.pow(3, 6); long rand = Math.abs(RAND.nextLong()) % (long) Math.pow(3, 6);
return tm + String.format("%04d", rand); return tm + String.format("%04d", rand);
} }
public static long getLongId() {
String tm = LocalDateTime.now().format(DF);
int rand = RAND.nextInt(100);
String randStr = String.format("%02d", rand);
String cidStr = tm + randStr;
try {
return Long.parseLong(cidStr);
} catch (NumberFormatException e) {
// 兜底处理:若意外超出范围,抛出明确异常
throw new RuntimeException("生成的CID超出long类型范围" + cidStr, e);
}
}
} }

View File

@@ -139,6 +139,12 @@ public class BizFoldersController extends BaseController {
return renderResult(Global.TRUE, text("删除文件夹信息成功!")); return renderResult(Global.TRUE, text("删除文件夹信息成功!"));
} }
@RequestMapping(value = "listAll")
@ResponseBody
public List<BizFolders> listAll(BizFolders bizFolders) {
return bizFoldersService.findList(bizFolders);
}
@RequestMapping(value = "foldersAll") @RequestMapping(value = "foldersAll")
@ResponseBody @ResponseBody
public List<FolderItem> foldersAll(BizFolders bizFolders) { public List<FolderItem> foldersAll(BizFolders bizFolders) {

View File

@@ -1,7 +1,12 @@
package com.jeesite.modules.biz.web; package com.jeesite.modules.biz.web;
import java.util.Date;
import java.util.List; import java.util.List;
import cn.hutool.core.net.multipart.UploadFile;
import com.jeesite.modules.app.utils.MyFileUtils;
import com.jeesite.modules.app.utils.vId;
import com.jeesite.modules.file.entity.FileUpload;
import com.jeesite.modules.file.utils.FileUploadUtils; import com.jeesite.modules.file.utils.FileUploadUtils;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
@@ -36,6 +41,8 @@ import com.jeesite.modules.biz.service.BizMyfilesService;
@RequestMapping(value = "${adminPath}/biz/myfiles") @RequestMapping(value = "${adminPath}/biz/myfiles")
public class BizMyfilesController extends BaseController { public class BizMyfilesController extends BaseController {
private static final String FILE_PATH = "/ogsapp/files";
private final BizMyfilesService bizMyfilesService; private final BizMyfilesService bizMyfilesService;
public BizMyfilesController(BizMyfilesService bizMyfilesService) { public BizMyfilesController(BizMyfilesService bizMyfilesService) {
@@ -85,8 +92,19 @@ public class BizMyfilesController extends BaseController {
@PostMapping(value = "save") @PostMapping(value = "save")
@ResponseBody @ResponseBody
public String save(@Validated BizMyfiles bizMyfiles) { public String save(@Validated BizMyfiles bizMyfiles) {
// bizMyfilesService.save(bizMyfiles);
FileUploadUtils.saveFileUpload(bizMyfiles, bizMyfiles.getId(), "bizMyfiles_file"); FileUploadUtils.saveFileUpload(bizMyfiles, bizMyfiles.getId(), "bizMyfiles_file");
List<FileUpload> fileList = FileUploadUtils.findFileUpload(bizMyfiles.getId(), "bizMyfiles_file");
for (FileUpload fileUpload : fileList) {
bizMyfiles.setCreateTime(new Date());
bizMyfiles.setTid(vId.getLongId());
bizMyfiles.setFileName(fileUpload.getFileName());
bizMyfiles.setFilePath(FILE_PATH + fileUpload.getFileUrl());
bizMyfiles.setFileHash(fileUpload.getFileEntity().getFileMd5());
bizMyfiles.setFileSize(MyFileUtils.formatFileSize(fileUpload.getFileEntity().getFileSize(), 2));
bizMyfiles.setFileExt(fileUpload.getFileEntity().getFileExtension());
bizMyfiles.setMimeType(fileUpload.getFileEntity().getFileContentType());
bizMyfilesService.save(bizMyfiles);
}
return renderResult(Global.TRUE, text("保存文件信息成功!")); return renderResult(Global.TRUE, text("保存文件信息成功!"));
} }

View File

@@ -37,7 +37,10 @@ export interface FolderItem {
export const bizFoldersList = (params?: BizFolders | any) => export const bizFoldersList = (params?: BizFolders | any) =>
defHttp.get<BizFolders>({ url: adminPath + '/biz/folders/list', params }); defHttp.get<BizFolders>({ url: adminPath + '/biz/folders/list', params });
export const bizFoldersListAll = (params?: BizFolders | any) =>
defHttp.get<BizFolders[]>({ url: adminPath + '/biz/folders/listAll', params });
export const bizFolderItemAll = (params?: BizFolders | any) => export const bizFolderItemAll = (params?: BizFolders | any) =>
defHttp.get<FolderItem[]>({ url: adminPath + '/biz/folders/foldersAll', params }); defHttp.get<FolderItem[]>({ url: adminPath + '/biz/folders/foldersAll', params });

View File

@@ -51,11 +51,11 @@
field: 'provinceCode', field: 'provinceCode',
component: 'Select', component: 'Select',
componentProps: { componentProps: {
api: bizProvinceListAll, api: bizProvinceListAll,
params: {}, params: {},
fieldNames: { label: 'provinceName', value: 'provinceCode' }, fieldNames: { label: 'provinceName', value: 'provinceCode' },
immediate: true, immediate: true,
allowClear: true, allowClear: true,
onChange: (value: string) => { onChange: (value: string) => {
provListParams.value.provinceCode = value; provListParams.value.provinceCode = value;
}, },

View File

@@ -119,8 +119,8 @@ export default defineComponent({
return { return {
listData, listData,
register, register,
openModal, openModal,
getUsageClass, getUsageClass,
getProgressClass, getProgressClass,
}; };

View File

@@ -17,8 +17,10 @@
import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form'; import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form';
import { useMessage } from '@jeesite/core/hooks/web/useMessage'; import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { BasicModal, useModalInner } from '@jeesite/core/components/Modal'; import { BasicModal, useModalInner } from '@jeesite/core/components/Modal';
import { BizMyfiles, bizMyfilesSave, bizMyfilesForm } from '@jeesite/biz/api/biz/myfiles'; import { BizMyfiles, bizMyfilesSave, bizMyfilesForm } from '@jeesite/biz/api/biz/myfiles';
import { FolderItem, bizFoldersListAll } from '@jeesite/biz/api/biz/folders';
export default defineComponent({ export default defineComponent({
components: { BasicModal, BasicForm }, components: { BasicModal, BasicForm },
emits: ['modalClose'], emits: ['modalClose'],
@@ -29,28 +31,56 @@
const record = ref<BizMyfiles>({} as BizMyfiles); const record = ref<BizMyfiles>({} as BizMyfiles);
const inputFormSchemas: FormSchema<BizMyfiles>[] = [ const inputFormSchemas: FormSchema<BizMyfiles>[] = [
{
label: t('上级目录'),
field: 'parentId',
component: 'Select',
componentProps: {
api: bizFoldersListAll,
params: { parentId : '0' },
fieldNames: { label: 'folderName', value: 'folderId' },
immediate: true,
allowClear: true,
},
required: true,
dynamicDisabled: true,
},
{ {
label: t('文件夹标识'), label: t('目录名称'),
field: 'folderId', field: 'folderId',
component: 'Input', component: 'Select',
componentProps: { componentProps: {
maxlength: 52, api: bizFoldersListAll,
params: {},
fieldNames: { label: 'folderName', value: 'folderId' },
immediate: true,
allowClear: true,
}, },
required: true, required: true,
dynamicDisabled: true, dynamicDisabled: true,
}, },
{
label: t('用户编码'),
field: 'loginCode',
component: 'Input',
componentProps: {
maxlength: 52,
},
required: true,
dynamicDisabled: true,
},
{
label: t('用户名称'),
field: 'userName',
component: 'Input',
componentProps: {
maxlength: 52,
},
required: true,
dynamicDisabled: true,
},
{ {
label: t('文件夹名称'), label: t('上传文件'),
field: 'folderName',
component: 'Input',
componentProps: {
maxlength: 52,
},
required: true,
dynamicDisabled: true,
},
{
label: t('附件上传'),
field: 'dataMap', field: 'dataMap',
component: 'Upload', component: 'Upload',
componentProps: { componentProps: {
@@ -59,6 +89,7 @@
bizType: 'bizMyfiles_file', bizType: 'bizMyfiles_file',
uploadType: 'all', uploadType: 'all',
}, },
required: true,
colProps: { md: 24, lg: 24 }, colProps: { md: 24, lg: 24 },
}, },
]; ];
@@ -72,36 +103,40 @@
const { createMessage } = useMessage(); const { createMessage } = useMessage();
const [register, { closeModal }] = useModalInner(async (data: any) => { const [register, { setModalProps, closeModal }] = useModalInner(async (data: any) => {
const dddd = { setModalProps({ loading: true });
... data await resetFields();
} record.value = (data || {}) as BizMyfiles;
console.log(dddd) record.value.__t = new Date().getTime();
resetFields();
if (data) { if (data) {
setFieldsValue({ ...data }); await setFieldsValue(record.value);
} }
setModalProps({ loading: false });
}); });
async function handleSubmit() { async function handleSubmit() {
try { try {
const data = await validate(); const data = await validate();
setModalProps({ confirmLoading: true });
const params: any = { const params: any = {
isNewRecord: true, isNewRecord: true,
tid : Date.now(),
}; };
const res = await bizMyfilesSave(params, data); const res = await bizMyfilesSave(params, data);
showMessage(res.message); showMessage(res.message);
handleCancel(data); handleCancel();
} catch (error: any) { } catch (error: any) {
console.error('上传文件保存失败:', error); console.error('上传文件保存失败:', error);
} } finally {
setModalProps({ confirmLoading: false });
}
} }
// 取消按钮逻辑 // 取消按钮逻辑
const handleCancel = (data: any) => { const handleCancel = () => {
resetFields(); // 取消时重置表单 resetFields();
emit('modalClose',data); emit('modalClose');
closeModal(); // 关闭模态框 closeModal();
}; };
return { return {
@@ -110,11 +145,11 @@
handleCancel, handleCancel,
registerForm, registerForm,
inputFormSchemas, inputFormSchemas,
handleSubmit handleSubmit,
}; };
}, },
}); });
</script> </script>
<style> <style>
</style> </style>

View File

@@ -68,8 +68,8 @@
v-model:value="fileSearchText" v-model:value="fileSearchText"
placeholder="文件搜索框" placeholder="文件搜索框"
class="file-search-input" class="file-search-input"
enter-button enter-button
@search="onSearch" @search="onSearch"
/> />
<div class="header-btn-group"> <div class="header-btn-group">
<a-button type="default" class="header-btn" @click="openfolderModal(true, ParamsFolders)"><Icon icon="ant-design:folder-add-outlined" />新建文件夹</a-button> <a-button type="default" class="header-btn" @click="openfolderModal(true, ParamsFolders)"><Icon icon="ant-design:folder-add-outlined" />新建文件夹</a-button>
@@ -88,7 +88,7 @@
<div class="card-inner-content"> <div class="card-inner-content">
<div class="file-card-header"> <div class="file-card-header">
<div class="file-name-wrap"> <div class="file-name-wrap">
<span class="file-card-icon">📄</span> <span class="file-card-icon"><Icon icon="i-svg:moon" size="24" /></span>
<span class="file-card-name" :title="file.fileName">{{ file.fileName }}</span> <span class="file-card-name" :title="file.fileName">{{ file.fileName }}</span>
</div> </div>
<span class="file-card-size">{{ file.fileSize }}</span> <span class="file-card-size">{{ file.fileSize }}</span>
@@ -105,7 +105,7 @@
</div> </div>
</div> </div>
</div> </div>
<UploadModal @register="uploadregister" /> <UploadModal @register="uploadregister" @modalClose="getFileList({ params: ParamsFolders})"/>
<FolderModal @register="folderregister" /> <FolderModal @register="folderregister" />
</template> </template>
@@ -121,6 +121,10 @@ import FolderModal from './components/folder.vue';
import { FolderItem, bizFolderItemAll } from '@jeesite/biz/api/biz/folders'; import { FolderItem, bizFolderItemAll } from '@jeesite/biz/api/biz/folders';
import { BizMyfiles, bizMyfilesListAll } from '@jeesite/biz/api/biz/myfiles'; import { BizMyfiles, bizMyfilesListAll } from '@jeesite/biz/api/biz/myfiles';
import { useUserStore } from '@jeesite/core/store/modules/user';
const userStore = useUserStore();
const userinfo = computed(() => userStore.getUserInfo);
const [uploadregister, { openModal: openUploadModal }] = useModal(); const [uploadregister, { openModal: openUploadModal }] = useModal();
const [folderregister, { openModal: openfolderModal }] = useModal(); const [folderregister, { openModal: openfolderModal }] = useModal();
@@ -217,34 +221,47 @@ watch(folderSearchText, (newVal) => {
searchAndExpandParents(newVal); searchAndExpandParents(newVal);
}); });
const getDataList = async () => { const getDataList = async (params: {}) => {
try { try {
const result = await bizFolderItemAll(); const reqParams = {
... params,
loginCode :userinfo.value.loginCode,
}
const result = await bizFolderItemAll(reqParams);
const folders = (result || []) as FolderItem[]; const folders = (result || []) as FolderItem[];
folders.forEach(folder => { folders.forEach(folder => {
folder.expanded = false; folder.expanded = false;
}); });
bindParentReferences(folders); // 绑定父级引用 bindParentReferences(folders); // 绑定父级引用
folderList.value = folders; folderList.value = folders;
getFileList({}); getFileList({});
} catch (error) { } catch (error) {
console.error('获取文件夹信息失败:', error); console.error('获取文件夹信息失败:', error);
folderList.value = []; folderList.value = [];
} }
}; };
const getFileList = async (params: {}) => { const getFileList = async (params: {}) => {
try { try {
const result = await bizMyfilesListAll(params); const reqParams = {
fileList.value = result || []; ... params,
folderId: ParamsFolders?.value?.folderId || '',
loginCode :userinfo.value.loginCode,
}
const result = await bizMyfilesListAll(reqParams);
fileList.value = result || [];
} catch (error) { } catch (error) {
console.error('获取文件信息失败:', error); console.error('获取文件信息失败:', error);
fileList.value = []; fileList.value = [];
} }
}; };
const onSearch = (searchValue: string) => { const onSearch = (searchValue: string) => {
UploadFile = true;
ParamsFolders.value = {};
const params = { const params = {
fileName: searchValue, fileName: searchValue,
} }
@@ -275,6 +292,8 @@ const handleChildClick = (child: ChildFolder) => {
ParamsFolders.value = child; ParamsFolders.value = child;
const params = { const params = {
folderId: child.id, folderId: child.id,
userName: userinfo.value.userName,
loginCode :userinfo.value.loginCode,
} }
getFileList(params); getFileList(params);
}; };
@@ -289,7 +308,7 @@ watch(folderSearchText, () => {
}); });
onMounted(() => { onMounted(() => {
getDataList(); getDataList({});
}); });
</script> </script>
@@ -687,4 +706,4 @@ onMounted(() => {
* { * {
box-sizing: border-box; box-sizing: border-box;
} }
</style> </style>