feat(File): 在文件分页查询中添加文件路径筛选

- 在 PageFileInput 类中新增 FilePath 属性,用于筛选文件路径
- 这个改动允许用户在分页查询时根据文件路径进行筛选,提高了文件查询的灵活性和效率
This commit is contained in:
master 2024-11-07 11:33:52 +08:00
parent 6438995d47
commit 729c0c60f8
6 changed files with 1018 additions and 722 deletions

View File

@ -16,6 +16,11 @@ public class PageFileInput : BasePageInput
/// </summary> /// </summary>
public string FileName { get; set; } public string FileName { get; set; }
/// <summary>
/// 文件路径
/// </summary>
public string FilePath { get; set; }
/// <summary> /// <summary>
/// 文件后缀 /// 文件后缀
/// </summary> /// </summary>

View File

@ -51,6 +51,7 @@ public class SysFileService : IDynamicApiController, ITransient
// 合并公开和私有附件并分页 // 合并公开和私有附件并分页
return await _sysFileRep.Context.UnionAll(publicList, privateList) return await _sysFileRep.Context.UnionAll(publicList, privateList)
.WhereIF(!string.IsNullOrWhiteSpace(input.FileName), u => u.FileName.Contains(input.FileName.Trim())) .WhereIF(!string.IsNullOrWhiteSpace(input.FileName), u => u.FileName.Contains(input.FileName.Trim()))
.WhereIF(!string.IsNullOrWhiteSpace(input.FilePath), u => u.FilePath.Contains(input.FilePath.Trim()))
.WhereIF(!string.IsNullOrWhiteSpace(input.StartTime.ToString()) && !string.IsNullOrWhiteSpace(input.EndTime.ToString()), .WhereIF(!string.IsNullOrWhiteSpace(input.StartTime.ToString()) && !string.IsNullOrWhiteSpace(input.EndTime.ToString()),
u => u.CreateTime >= input.StartTime && u.CreateTime <= input.EndTime) u => u.CreateTime >= input.StartTime && u.CreateTime <= input.EndTime)
.OrderBy(u => u.CreateTime, OrderByType.Desc) .OrderBy(u => u.CreateTime, OrderByType.Desc)
@ -273,6 +274,7 @@ public class SysFileService : IDynamicApiController, ITransient
/// </summary> /// </summary>
/// <param name="id"></param> /// <param name="id"></param>
/// <returns></returns> /// <returns></returns>
[ApiDescriptionSettings(Name = "File"), HttpGet]
[DisplayName("获取文件")] [DisplayName("获取文件")]
public async Task<SysFile> GetFile([FromQuery] long id) public async Task<SysFile> GetFile([FromQuery] long id)
{ {
@ -280,6 +282,36 @@ public class SysFileService : IDynamicApiController, ITransient
return file ?? throw Oops.Oh(ErrorCodeEnum.D8000); return file ?? throw Oops.Oh(ErrorCodeEnum.D8000);
} }
/// <summary>
/// 获取文件路径 🔖
/// </summary>
/// <returns></returns>
[ApiDescriptionSettings(Name = "Folder"), HttpGet]
[DisplayName("获取文件路径")]
public async Task<dynamic> GetFolder()
{
var files = await _sysFileRep.AsQueryable().ToListAsync();
var folders = files.GroupBy(x => x.FilePath).Select(g => g.First().FilePath).ToList();
var result = folders
.GroupBy(p => p.Split('/').First())
.Select((g, index) => new
{
id = index + 1, // 组的索引加1作为id
pid = 0,
name = g.Key,
children = g.Select((item, subIndex) => new
{
id = (index + 1 * 100) + subIndex + 1, // 子项的索引加1作为id
pid = index + 1,
name = item.Split('/').Last(),
children = new List<string>()
})
.ToList()
})
.ToList();
return result;
}
/// <summary> /// <summary>
/// 上传文件 🔖 /// 上传文件 🔖
/// </summary> /// </summary>

View File

@ -20,6 +20,7 @@ import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } fr
import { AccountTypeEnum } from '../models'; import { AccountTypeEnum } from '../models';
import { AdminResultIActionResult } from '../models'; import { AdminResultIActionResult } from '../models';
import { AdminResultListSysFile } from '../models'; import { AdminResultListSysFile } from '../models';
import { AdminResultObject } from '../models';
import { AdminResultSqlSugarPagedListSysFile } from '../models'; import { AdminResultSqlSugarPagedListSysFile } from '../models';
import { AdminResultString } from '../models'; import { AdminResultString } from '../models';
import { AdminResultSysFile } from '../models'; import { AdminResultSysFile } from '../models';
@ -231,6 +232,49 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
options: localVarRequestOptions, options: localVarRequestOptions,
}; };
}, },
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
apiSysFileFolderGet: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/api/sysFile/folder`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication Bearer required
// http bearer authentication required
if (configuration && configuration.accessToken) {
const accessToken = typeof configuration.accessToken === 'function'
? await configuration.accessToken()
: await configuration.accessToken;
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
}
const query = new URLSearchParams(localVarUrlObj.search);
for (const key in localVarQueryParameter) {
query.set(key, localVarQueryParameter[key]);
}
for (const key in options.params) {
query.set(key, options.params[key]);
}
localVarUrlObj.search = (new URLSearchParams(query)).toString();
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
options: localVarRequestOptions,
};
},
/** /**
* *
* @summary 🔖 * @summary 🔖
@ -1909,6 +1953,19 @@ export const SysFileApiFp = function(configuration?: Configuration) {
return axios.request(axiosRequestArgs); return axios.request(axiosRequestArgs);
}; };
}, },
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysFileFolderGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultObject>>> {
const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).apiSysFileFolderGet(options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
return axios.request(axiosRequestArgs);
};
},
/** /**
* *
* @summary 🔖 * @summary 🔖
@ -2316,6 +2373,15 @@ export const SysFileApiFactory = function (configuration?: Configuration, basePa
async apiSysFileFileGet(id?: number, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysFile>> { async apiSysFileFileGet(id?: number, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysFile>> {
return SysFileApiFp(configuration).apiSysFileFileGet(id, options).then((request) => request(axios, basePath)); return SysFileApiFp(configuration).apiSysFileFileGet(id, options).then((request) => request(axios, basePath));
}, },
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysFileFolderGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultObject>> {
return SysFileApiFp(configuration).apiSysFileFolderGet(options).then((request) => request(axios, basePath));
},
/** /**
* *
* @summary 🔖 * @summary 🔖
@ -2692,6 +2758,16 @@ export class SysFileApi extends BaseAPI {
public async apiSysFileFileGet(id?: number, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysFile>> { public async apiSysFileFileGet(id?: number, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysFile>> {
return SysFileApiFp(this.configuration).apiSysFileFileGet(id, options).then((request) => request(this.axios, this.basePath)); return SysFileApiFp(this.configuration).apiSysFileFileGet(id, options).then((request) => request(this.axios, this.basePath));
} }
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SysFileApi
*/
public async apiSysFileFolderGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultObject>> {
return SysFileApiFp(this.configuration).apiSysFileFolderGet(options).then((request) => request(this.axios, this.basePath));
}
/** /**
* *
* @summary 🔖 * @summary 🔖

View File

@ -90,6 +90,14 @@ export interface PageFileInput {
*/ */
fileName?: string | null; fileName?: string | null;
/**
*
*
* @type {string}
* @memberof PageFileInput
*/
filePath?: string | null;
/** /**
* *
* *

View File

@ -0,0 +1,148 @@
<template lang="">
<el-card class="box-card" shadow="hover" body-style="height:100%; overflow:auto;padding:5px;">
<template #header>
<div class="card-header">
<div class="tree-h-flex">
<div class="tree-h-left">
<el-input :prefix-icon="Search" v-model="filterText" placeholder="文件夹名称" />
</div>
<div class="tree-h-right">
<el-dropdown @command="handleCommand">
<el-button style="margin-left: 8px; width: 34px">
<el-icon class="el-icon--center">
<MoreFilled />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="expandAll">全部展开</el-dropdown-item>
<el-dropdown-item command="collapseAll">全部折叠</el-dropdown-item>
<el-dropdown-item command="rootNode">根节点</el-dropdown-item>
<el-dropdown-item command="refresh">刷新</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</div>
</template>
<div style="margin-bottom: 45px" v-loading="state.loading">
<el-tree
ref="treeRef"
class="filter-tree"
:data="state.folderData"
node-key="id"
:props="{ children: 'children', label: 'name' }"
:filter-node-method="filterNode"
@node-click="nodeClick"
highlight-current
check-strictly
accordion
lazy
:load="loadNode"
/>
</div>
</el-card>
</template>
<script lang="ts" setup>
import { onMounted, reactive, ref, watch } from 'vue';
import type { ElTree } from 'element-plus';
import { Search, MoreFilled } from '@element-plus/icons-vue';
import { getAPI } from '/@/utils/axios-utils';
import { SysFileApi } from '/@/api-services/api';
const filterText = ref('');
const treeRef = ref<InstanceType<typeof ElTree>>();
const state = reactive({
loading: false,
folderData: [] as any,
});
onMounted(() => {
initTreeData();
});
watch(filterText, (val) => {
treeRef.value!.filter(val);
});
const initTreeData = async () => {
state.loading = true;
var res = await getAPI(SysFileApi).apiSysFileFolderGet();
state.folderData = res.data.result ?? [];
state.loading = false;
};
const loadNode = async (node: any, resolve: any) => {
console.log(node);
if (node.data == undefined || Array.isArray(node.data)) return;
state.loading = true;
var data = state.folderData.find(u => u.id == node.data.id);
state.loading = false;
console.log(data);
if (data)
resolve(data.children);
else
resolve([]);
};
//
const getCheckedKeys = () => {
return treeRef.value!.getCheckedKeys();
};
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.name.includes(value);
};
const handleCommand = async (command: string | number | object) => {
if ('expandAll' == command) {
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
treeRef.value!.store._getAllNodes()[i].expanded = true;
}
} else if ('collapseAll' == command) {
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
treeRef.value!.store._getAllNodes()[i].expanded = false;
}
} else if ('refresh' == command) {
initTreeData();
} else if ('rootNode' == command) {
emits('node-click', { id: 0, name: '' });
}
};
//
const emits = defineEmits(['node-click']);
const nodeClick = (node: any) => {
emits('node-click', { id: node.id, name: node.name });
};
//
defineExpose({ initTreeData, getCheckedKeys });
</script>
<style lang="scss" scoped>
.box-card {
flex: 1;
> :deep(.el-card__header) {
padding: 5px;
}
}
.tree-h-flex {
display: flex;
}
.tree-h-left {
flex: 1;
width: 100%;
}
.tree-h-right {
width: 42px;
min-width: 42px;
}
</style>

View File

@ -1,5 +1,11 @@
<template> <template>
<div class="sys-file-container"> <div class="sys-file-container">
<splitpanes class="default-theme">
<pane size="20" style="display: flex">
<FolderTree ref="folderTreeRef" @node-click="handleNodeChange" />
</pane>
<pane size="80" style="display: flex; flex-direction: column">
<el-card shadow="hover" :body-style="{ padding: '5px 5px 0 5px', display: 'flex', width: '100%', height: '100%', alignItems: 'start' }"> <el-card shadow="hover" :body-style="{ padding: '5px 5px 0 5px', display: 'flex', width: '100%', height: '100%', alignItems: 'start' }">
<el-form :model="state.queryParams" ref="queryForm" :show-message="false" :inlineMessage="true" label-width="auto" style="flex: 1 1 0%"> <el-form :model="state.queryParams" ref="queryForm" :show-message="false" :inlineMessage="true" label-width="auto" style="flex: 1 1 0%">
<el-row :gutter="10"> <el-row :gutter="10">
@ -114,6 +120,8 @@
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</pane>
</splitpanes>
<el-drawer :title="state.fileName" v-model="state.docxVisible" size="70%" destroy-on-close> <el-drawer :title="state.fileName" v-model="state.docxVisible" size="70%" destroy-on-close>
<vue-office-docx :src="state.docxUrl" style="height: calc(100vh - 37px)" @rendered="handleRendered" @error="handleError" /> <vue-office-docx :src="state.docxUrl" style="height: calc(100vh - 37px)" @rendered="handleRendered" @error="handleError" />
@ -135,13 +143,19 @@ import { ElMessageBox, ElMessage, UploadInstance } from 'element-plus';
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table'; import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook'; import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
import { Local } from '/@/utils/storage'; import { Local } from '/@/utils/storage';
import { Splitpanes, Pane } from 'splitpanes';
import 'splitpanes/dist/splitpanes.css';
import { downloadByUrl } from '/@/utils/download'; import { downloadByUrl } from '/@/utils/download';
import VueOfficeDocx from '@vue-office/docx'; import VueOfficeDocx from '@vue-office/docx';
import VueOfficeExcel from '@vue-office/excel'; import VueOfficeExcel from '@vue-office/excel';
import VueOfficePdf from '@vue-office/pdf'; import VueOfficePdf from '@vue-office/pdf';
import '@vue-office/docx/lib/index.css'; import '@vue-office/docx/lib/index.css';
import '@vue-office/excel/lib/index.css'; import '@vue-office/excel/lib/index.css';
import FolderTree from '/@/views/system/file/component/folderTree.vue';
import EditFile from '/@/views/system/file/component/editFile.vue'; import EditFile from '/@/views/system/file/component/editFile.vue';
import { getAPI } from '/@/utils/axios-utils'; import { getAPI } from '/@/utils/axios-utils';
@ -152,9 +166,12 @@ const xGrid = ref<VxeGridInstance>();
// const baseUrl = window.__env__.VITE_API_URL; // const baseUrl = window.__env__.VITE_API_URL;
const uploadRef = ref<UploadInstance>(); const uploadRef = ref<UploadInstance>();
const editRef = ref<InstanceType<typeof EditFile>>(); const editRef = ref<InstanceType<typeof EditFile>>();
const folderTreeRef = ref<InstanceType<typeof FolderTree>>();
const state = reactive({ const state = reactive({
queryParams: { queryParams: {
fileName: undefined, fileName: undefined,
filePath: undefined,
startTime: undefined, startTime: undefined,
endTime: undefined, endTime: undefined,
}, },
@ -239,6 +256,7 @@ const handleQuery = async (reset = false) => {
// //
const resetQuery = async () => { const resetQuery = async () => {
state.queryParams.fileName = undefined; state.queryParams.fileName = undefined;
state.queryParams.filePath = undefined;
state.queryParams.startTime = undefined; state.queryParams.startTime = undefined;
state.queryParams.endTime = undefined; state.queryParams.endTime = undefined;
await xGrid.value?.commitProxy('reload'); await xGrid.value?.commitProxy('reload');
@ -301,6 +319,15 @@ const gridEvents: VxeGridListeners<SysFile> = {
}, },
}; };
//
const handleNodeChange = async (node: any) => {
state.queryParams.fileName = undefined;
state.queryParams.filePath = node.name;
state.queryParams.startTime = undefined;
state.queryParams.endTime = undefined;
await handleQuery();
};
// Pdf // Pdf
const showPreviewDialog = async (row: any) => { const showPreviewDialog = async (row: any) => {
if (row.suffix == '.pdf') { if (row.suffix == '.pdf') {