mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 04:46:26 +00:00
feat: 增强用户界面配置选项
This commit is contained in:
@@ -1 +1,276 @@
|
||||
// 待开发
|
||||
import request from "@/utils/request";
|
||||
|
||||
export const ResourceAPI = {
|
||||
/**
|
||||
* 获取目录列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
getResourceList(query: ResourceListQuery) {
|
||||
return request<ApiResponse<ResourceItem[]>>({
|
||||
url: `/resource/resource/list`,
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 搜索资源
|
||||
* @param body 搜索条件
|
||||
*/
|
||||
searchResource(body: ResourceSearchQuery) {
|
||||
return request<ApiResponse<ResourceItem[]>>({
|
||||
url: `/resource/resource/search`,
|
||||
method: "post",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param formData 文件数据
|
||||
*/
|
||||
uploadFile(formData: FormData) {
|
||||
return request<ApiResponse<UploadFilePath>>({
|
||||
url: `/resource/resource/upload`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param path 文件路径
|
||||
*/
|
||||
downloadFile(path: string) {
|
||||
return request<Blob>({
|
||||
url: `/resource/resource/download`,
|
||||
method: "get",
|
||||
params: { path },
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除文件或目录
|
||||
* @param body 文件路径数组
|
||||
*/
|
||||
deleteResource(body: string[]) {
|
||||
return request<ApiResponse>({
|
||||
url: `/resource/resource/delete`,
|
||||
method: "delete",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 移动文件或目录
|
||||
* @param body 移动参数
|
||||
*/
|
||||
moveResource(body: ResourceMoveQuery) {
|
||||
return request<ApiResponse>({
|
||||
url: `/resource/resource/move`,
|
||||
method: "post",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 复制文件或目录
|
||||
* @param body 复制参数
|
||||
*/
|
||||
copyResource(body: ResourceCopyQuery) {
|
||||
return request<ApiResponse>({
|
||||
url: `/resource/resource/copy`,
|
||||
method: "post",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 重命名文件或目录
|
||||
* @param body 重命名参数
|
||||
*/
|
||||
renameResource(body: ResourceRenameQuery) {
|
||||
return request<ApiResponse>({
|
||||
url: `/resource/resource/rename`,
|
||||
method: "post",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建目录
|
||||
* @param body 创建目录参数
|
||||
*/
|
||||
createDirectory(body: ResourceCreateDirQuery) {
|
||||
return request<ApiResponse>({
|
||||
url: `/resource/resource/create-dir`,
|
||||
method: "post",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取资源统计信息
|
||||
*/
|
||||
getResourceStats() {
|
||||
return request<ApiResponse<ResourceStats>>({
|
||||
url: `/resource/resource/stats`,
|
||||
method: "get",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 导出资源列表
|
||||
* @param body 导出条件
|
||||
*/
|
||||
exportResource(body: ResourceSearchQuery) {
|
||||
return request<Blob>({
|
||||
url: `/resource/resource/export`,
|
||||
method: "post",
|
||||
data: body,
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default ResourceAPI;
|
||||
|
||||
/**
|
||||
* 资源列表查询参数
|
||||
*/
|
||||
export interface ResourceListQuery {
|
||||
/** 目录路径 */
|
||||
path: string;
|
||||
/** 递归获取 */
|
||||
recursive?: boolean;
|
||||
/** 包含隐藏文件 */
|
||||
include_hidden?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源搜索查询参数
|
||||
*/
|
||||
export interface ResourceSearchQuery {
|
||||
/** 关键词 */
|
||||
keyword?: string;
|
||||
/** 文件类型 */
|
||||
file_type?: string;
|
||||
/** 资源类型 */
|
||||
resource_type?: string;
|
||||
/** 最小文件大小 */
|
||||
min_size?: number;
|
||||
/** 最大文件大小 */
|
||||
max_size?: number;
|
||||
/** 开始时间 */
|
||||
start_date?: string;
|
||||
/** 结束时间 */
|
||||
end_date?: string;
|
||||
/** 文件扩展名 */
|
||||
extensions?: string[];
|
||||
/** 包含隐藏文件 */
|
||||
include_hidden?: boolean;
|
||||
/** 最大深度 */
|
||||
max_depth?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源项信息
|
||||
*/
|
||||
export interface ResourceItem {
|
||||
/** 文件/目录名称 */
|
||||
name: string;
|
||||
/** 完整路径 */
|
||||
path: string;
|
||||
/** 是否为目录 */
|
||||
is_directory: boolean;
|
||||
/** 文件大小(字节) */
|
||||
size?: number;
|
||||
/** 文件扩展名 */
|
||||
extension?: string;
|
||||
/** 修改时间 */
|
||||
modified_time: string;
|
||||
/** 创建时间 */
|
||||
created_time: string;
|
||||
/** 是否为隐藏文件 */
|
||||
is_hidden: boolean;
|
||||
/** 文件类型 */
|
||||
file_type?: string;
|
||||
/** 资源类型 */
|
||||
resource_type?: string;
|
||||
/** 文件URL(如果是图片等可预览文件) */
|
||||
file_url?: string;
|
||||
/** 缩略图URL */
|
||||
thumbnail_url?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源移动参数
|
||||
*/
|
||||
export interface ResourceMoveQuery {
|
||||
/** 源路径 */
|
||||
source_path: string;
|
||||
/** 目标路径 */
|
||||
target_path: string;
|
||||
/** 是否覆盖 */
|
||||
overwrite?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源复制参数
|
||||
*/
|
||||
export interface ResourceCopyQuery {
|
||||
/** 源路径 */
|
||||
source_path: string;
|
||||
/** 目标路径 */
|
||||
target_path: string;
|
||||
/** 是否覆盖 */
|
||||
overwrite?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源重命名参数
|
||||
*/
|
||||
export interface ResourceRenameQuery {
|
||||
/** 原路径 */
|
||||
old_path: string;
|
||||
/** 新名称 */
|
||||
new_name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录参数
|
||||
*/
|
||||
export interface ResourceCreateDirQuery {
|
||||
/** 父目录路径 */
|
||||
parent_path: string;
|
||||
/** 目录名称 */
|
||||
dir_name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源统计信息
|
||||
*/
|
||||
export interface ResourceStats {
|
||||
/** 总文件数 */
|
||||
total_files: number;
|
||||
/** 总目录数 */
|
||||
total_directories: number;
|
||||
/** 总大小(字节) */
|
||||
total_size: number;
|
||||
/** 按文件类型统计 */
|
||||
file_type_stats: Array<{
|
||||
file_type: string;
|
||||
count: number;
|
||||
size: number;
|
||||
}>;
|
||||
/** 按资源类型统计 */
|
||||
resource_type_stats: Array<{
|
||||
resource_type: string;
|
||||
count: number;
|
||||
size: number;
|
||||
}>;
|
||||
/** 最近修改的文件 */
|
||||
recent_files: ResourceItem[];
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ export const DICT_CACHE_KEY = "dict_cache";
|
||||
export const SHOW_TAGS_VIEW_KEY = "showTagsView";
|
||||
export const SHOW_APP_LOGO_KEY = "showAppLogo";
|
||||
export const SHOW_WATERMARK_KEY = "showWatermark";
|
||||
export const SHOW_SETTINGS_KEY = "showSettings";
|
||||
export const SHOW_DESKTOP_TOOLS_KEY = "showDesktopTools";
|
||||
export const LAYOUT_KEY = "layout";
|
||||
export const SIDEBAR_COLOR_SCHEME_KEY = "sidebarColorScheme";
|
||||
export const THEME_KEY = "theme";
|
||||
@@ -39,6 +41,8 @@ export const SETTINGS_KEYS = {
|
||||
SHOW_TAGS_VIEW: SHOW_TAGS_VIEW_KEY,
|
||||
SHOW_APP_LOGO: SHOW_APP_LOGO_KEY,
|
||||
SHOW_WATERMARK: SHOW_WATERMARK_KEY,
|
||||
SHOW_SETTINGS: SHOW_SETTINGS_KEY,
|
||||
SHOW_DESKTOP_TOOLS: SHOW_DESKTOP_TOOLS_KEY,
|
||||
SIDEBAR_COLOR_SCHEME: SIDEBAR_COLOR_SCHEME_KEY,
|
||||
LAYOUT: LAYOUT_KEY,
|
||||
THEME_COLOR: THEME_COLOR_KEY,
|
||||
|
||||
@@ -136,6 +136,14 @@ export default {
|
||||
profile: "User Profile",
|
||||
config: "Config Center",
|
||||
tour: "Project Tour",
|
||||
showSettings: "Show Project Settings",
|
||||
hideSettings: "Hide Project Settings",
|
||||
showDesktopTools: "Show Desktop Tools",
|
||||
hideDesktopTools: "Hide Desktop Tools",
|
||||
settingsEnabled: "Project settings button enabled",
|
||||
settingsDisabled: "Project settings button disabled",
|
||||
desktopToolsEnabled: "Desktop tools enabled",
|
||||
desktopToolsDisabled: "Desktop tools disabled",
|
||||
refresh: "Refresh",
|
||||
close: "Close",
|
||||
closeLeft: "Close Left",
|
||||
|
||||
@@ -136,6 +136,14 @@ export default {
|
||||
profile: "个人中心",
|
||||
config: "配置中心",
|
||||
tour: "项目引导",
|
||||
showSettings: "打开项目配置",
|
||||
hideSettings: "隐藏项目配置",
|
||||
showDesktopTools: "打开顶部工具",
|
||||
hideDesktopTools: "隐藏顶部工具",
|
||||
settingsEnabled: "已打开项目配置按钮",
|
||||
settingsDisabled: "已隐藏项目配置按钮",
|
||||
desktopToolsEnabled: "已打开顶部工具",
|
||||
desktopToolsDisabled: "已隐藏顶部工具",
|
||||
refresh: "刷新",
|
||||
close: "关闭",
|
||||
closeLeft: "关闭左侧",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div ref="navbar-actions" :class="['navbar-actions', navbarActionsClass]">
|
||||
<!-- 桌面端工具项 -->
|
||||
<template v-if="isDesktop">
|
||||
<template v-if="isDesktop && settingStore.showDesktopTools">
|
||||
<!-- 搜索 -->
|
||||
<div class="navbar-actions__item">
|
||||
<MenuSearch />
|
||||
@@ -67,6 +67,14 @@
|
||||
<el-icon><Position /></el-icon>
|
||||
{{ t("navbar.tour") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided @click="handleToggleSettings">
|
||||
<el-icon><Tools /></el-icon>
|
||||
{{ settingStore.showSettings ? t('navbar.hideSettings') : t('navbar.showSettings') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="handleToggleDesktopTools">
|
||||
<el-icon><Operation /></el-icon>
|
||||
{{ settingStore.showDesktopTools ? t('navbar.hideDesktopTools') : t('navbar.showDesktopTools') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided @click="handlelockScreen">
|
||||
<el-icon><Lock /></el-icon>
|
||||
{{ t("navbar.lock") }}
|
||||
@@ -179,6 +187,26 @@ function handleTourClick() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换项目配置显示状态
|
||||
*/
|
||||
function handleToggleSettings() {
|
||||
settingStore.updateSetting('showSettings', !settingStore.showSettings);
|
||||
ElMessage.success(
|
||||
settingStore.showSettings ? t('navbar.settingsEnabled') : t('navbar.settingsDisabled')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换桌面端工具项打开状态
|
||||
*/
|
||||
function handleToggleDesktopTools() {
|
||||
settingStore.updateSetting('showDesktopTools', !settingStore.showDesktopTools);
|
||||
ElMessage.success(
|
||||
settingStore.showDesktopTools ? t('navbar.desktopToolsEnabled') : t('navbar.desktopToolsDisabled')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁屏
|
||||
*/
|
||||
|
||||
@@ -68,16 +68,44 @@
|
||||
<!-- 系统主题 -->
|
||||
<section class="config-section">
|
||||
<el-divider>{{ t("settings.systemTheme") }}</el-divider>
|
||||
<div class="config-item flex-x-between">
|
||||
<span class="text-xs">{{ t("settings.themeColor") }}</span>
|
||||
<el-color-picker
|
||||
v-model="selectedThemeColor"
|
||||
:predefine="colorPresets"
|
||||
popper-class="theme-picker-dropdown"
|
||||
/>
|
||||
<div class="config-item">
|
||||
<div class="flex-x-between mb-3">
|
||||
<span class="text-xs">{{ t("settings.themeColor") }}</span>
|
||||
</div>
|
||||
<!-- 自定义主题颜色选择器 -->
|
||||
<div class="theme-color-selector">
|
||||
<div class="color-options">
|
||||
<!-- 预设颜色选项 -->
|
||||
<div
|
||||
v-for="(color, index) in displayColorPresets"
|
||||
:key="color"
|
||||
:class="[
|
||||
'color-option',
|
||||
{ 'is-active': selectedThemeColor === color }
|
||||
]"
|
||||
:style="{ backgroundColor: color }"
|
||||
@click="handleColorSelect(color)"
|
||||
>
|
||||
<div v-if="selectedThemeColor === color" class="color-check">
|
||||
<el-icon><Check /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 自定义颜色选择器 -->
|
||||
<div class="color-picker-wrapper">
|
||||
<el-color-picker
|
||||
v-model="selectedThemeColor"
|
||||
:predefine="allColorPresets"
|
||||
show-alpha
|
||||
size="small"
|
||||
class="custom-color-picker"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- 导航主题 -->
|
||||
<section v-if="!isDark" class="config-section ">
|
||||
<el-divider>{{ t("settings.navigation") }}</el-divider>
|
||||
@@ -157,7 +185,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DocumentCopy, RefreshLeft, Check } from "@element-plus/icons-vue";
|
||||
import { DocumentCopy, RefreshLeft, Check, Plus } from "@element-plus/icons-vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
import { LayoutMode, SidebarColor, ThemeMode } from "@/enums";
|
||||
@@ -192,9 +220,17 @@ const layoutOptions: LayoutOption[] = [
|
||||
|
||||
// 使用统一的颜色预设配置
|
||||
const colorPresets = themeColorPresets;
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
// 主题颜色选择器相关
|
||||
const displayColorPresets = computed(() => themeColorPresets.slice(0, 9)); // 只显示前9个预设颜色
|
||||
const allColorPresets = themeColorPresets; // 所有颜色预设,用于自定义颜色选择器
|
||||
|
||||
// 判断当前颜色是否为自定义颜色(不在前7个预设中)
|
||||
const isCustomColor = computed(() => {
|
||||
return !displayColorPresets.value.includes(selectedThemeColor.value);
|
||||
});
|
||||
|
||||
const isDark = ref<boolean>(settingsStore.theme === ThemeMode.DARK);
|
||||
const sidebarColor = ref(settingsStore.sidebarColorScheme);
|
||||
|
||||
@@ -237,6 +273,16 @@ const handleLayoutChange = (layout: LayoutMode) => {
|
||||
settingsStore.updateLayout(layout);
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理颜色选择
|
||||
*
|
||||
* @param color - 选中的颜色
|
||||
*/
|
||||
const handleColorSelect = (color: string) => {
|
||||
selectedThemeColor.value = color;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 复制当前配置
|
||||
*/
|
||||
@@ -618,6 +664,118 @@ const handleCloseDrawer = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/* 主题颜色选择器样式 */
|
||||
.theme-color-selector {
|
||||
.color-options {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.color-option {
|
||||
position: relative;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
cursor: pointer;
|
||||
border: 2px solid var(--el-border-color-light);
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px) scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
border-color: var(--el-color-primary-light-3);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-color: var(--el-color-primary);
|
||||
transform: translateY(-1px) scale(1.08);
|
||||
box-shadow: 0 4px 16px rgba(64, 128, 255, 0.3);
|
||||
}
|
||||
|
||||
.color-check {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: white;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 10px;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
}
|
||||
|
||||
.color-picker-wrapper {
|
||||
.custom-color-picker {
|
||||
:deep(.el-color-picker__trigger) {
|
||||
width: 22px !important;
|
||||
height: 22px !important;
|
||||
border: 2px solid var(--el-border-color-light) !important;
|
||||
border-radius: 6px !important;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px) scale(1.05) !important;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
|
||||
border-color: var(--el-color-primary-light-3) !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-color-picker__color) {
|
||||
border: none !important;
|
||||
border-radius: 3px !important;
|
||||
}
|
||||
|
||||
:deep(.el-color-picker__color-inner) {
|
||||
border-radius: 3px !important;
|
||||
}
|
||||
|
||||
:deep(.el-color-picker__icon) {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 深色模式适配 */
|
||||
.dark {
|
||||
.theme-color-selector {
|
||||
.color-option {
|
||||
border-color: var(--el-border-color);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary-light-3);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.color-picker-wrapper {
|
||||
.custom-color-picker {
|
||||
:deep(.el-color-picker__trigger) {
|
||||
border-color: var(--el-border-color) !important;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3) !important;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary-light-3) !important;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 复制配置对话框样式 */
|
||||
:deep(.copy-config-dialog) {
|
||||
.el-message-box__content {
|
||||
@@ -626,3 +784,4 @@ const handleCloseDrawer = () => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<!-- 新增:悬浮的系统设置按钮 -->
|
||||
<el-button
|
||||
v-if="defaultSettings.showSettings"
|
||||
v-if="settingStore.showSettings"
|
||||
class="floating-settings-button"
|
||||
type="primary"
|
||||
@click="handleSettingsClick"
|
||||
|
||||
@@ -12,6 +12,8 @@ export const defaultSettings: AppSettings = {
|
||||
version: pkg.version,
|
||||
// 是否显示设置
|
||||
showSettings: true,
|
||||
// 是否显示桌面端工具项
|
||||
showDesktopTools: true,
|
||||
// 是否显示标签视图
|
||||
showTagsView: true,
|
||||
// 是否显示应用Logo
|
||||
@@ -40,47 +42,50 @@ export const defaultSettings: AppSettings = {
|
||||
// 主题色预设 - 现代化配色方案
|
||||
// 注意:修改默认主题色时,需要同步修改 src/styles/variables.scss 中的 primary.base 值
|
||||
export const themeColorPresets = [
|
||||
// === 蓝色系 - 科技与专业 ===
|
||||
// === 精选常用颜色 - 多样化色系 ===
|
||||
"#4080FF", // Arco Design 蓝 - 现代感强
|
||||
"#1890FF", // Ant Design 蓝 - 经典商务
|
||||
"#52C41A", // 成功绿 - 活力清新
|
||||
"#722ED1", // 优雅紫 - 高端大气
|
||||
"#FA8C16", // 活力橙 - 温暖友好
|
||||
"#13C2C2", // 青色 - 科技感
|
||||
"#F5222D", // 警示红 - 醒目强烈
|
||||
"#EB2F96", // 品红 - 时尚个性
|
||||
"#EC4899", // 玫瑰粉 - 浪漫温馨
|
||||
"#10B981", // 翠绿色 - 清新自然
|
||||
|
||||
// === 蓝色系 - 科技与专业 ===
|
||||
"#409EFF", // Element Plus 蓝 - 清新自然
|
||||
"#2F54EB", // 深蓝 - 稳重专业
|
||||
"#1E40AF", // 深蓝色 - 商务精英
|
||||
"#1D4ED8", // 皇家蓝 - 高端商务
|
||||
|
||||
// === 绿色系 - 自然与活力 ===
|
||||
"#52C41A", // 成功绿 - 活力清新
|
||||
"#10B981", // 翠绿色 - 清新自然
|
||||
"#059669", // 森林绿 - 生态环保
|
||||
"#16A34A", // 草绿色 - 健康活力
|
||||
"#15803D", // 深绿色 - 稳重大气
|
||||
|
||||
// === 紫色系 - 创意与优雅 ===
|
||||
"#722ED1", // 优雅紫 - 高端大气
|
||||
"#7C3AED", // 紫罗兰 - 创意无限
|
||||
"#8B5CF6", // 浅紫色 - 时尚现代
|
||||
"#6D28D9", // 深紫色 - 神秘高端
|
||||
"#5B21B6", // 皇家紫 - 王者风范
|
||||
|
||||
// === 橙色系 - 温暖与活力 ===
|
||||
"#FA8C16", // 活力橙 - 温暖友好
|
||||
"#F97316", // 火橙色 - 热情奔放
|
||||
"#EA580C", // 深橙色 - 阳光活力
|
||||
"#DC2626", // 珊瑚红 - 温暖亲切
|
||||
|
||||
// === 青色系 - 科技与清新 ===
|
||||
"#13C2C2", // 青色 - 科技感
|
||||
"#0891B2", // 天蓝色 - 清新自然
|
||||
"#0E7490", // 深青色 - 专业科技
|
||||
"#06B6D4", // 青蓝色 - 海洋清新
|
||||
|
||||
// === 红色系 - 激情与警示 ===
|
||||
"#F5222D", // 警示红 - 醒目强烈
|
||||
"#DC2626", // 猩红色 - 激情四射
|
||||
"#B91C1C", // 深红色 - 庄重严肃
|
||||
|
||||
// === 粉色系 - 温柔与时尚 ===
|
||||
"#EB2F96", // 品红 - 时尚个性
|
||||
"#EC4899", // 玫瑰粉 - 浪漫温馨
|
||||
"#F472B6", // 浅粉色 - 柔美可爱
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ interface SettingsState {
|
||||
showTagsView: boolean;
|
||||
showAppLogo: boolean;
|
||||
showWatermark: boolean;
|
||||
showSettings: boolean;
|
||||
showDesktopTools: boolean;
|
||||
|
||||
// 布局设置
|
||||
layout: LayoutMode;
|
||||
@@ -42,6 +44,16 @@ export const useSettingsStore = defineStore("setting", () => {
|
||||
defaultSettings.showWatermark
|
||||
);
|
||||
|
||||
const showSettings = useStorage<boolean>(
|
||||
SETTINGS_KEYS.SHOW_SETTINGS,
|
||||
defaultSettings.showSettings
|
||||
);
|
||||
|
||||
const showDesktopTools = useStorage<boolean>(
|
||||
SETTINGS_KEYS.SHOW_DESKTOP_TOOLS,
|
||||
defaultSettings.showDesktopTools
|
||||
);
|
||||
|
||||
const sidebarColorScheme = useStorage<string>(
|
||||
SETTINGS_KEYS.SIDEBAR_COLOR_SCHEME,
|
||||
defaultSettings.sidebarColorScheme
|
||||
@@ -58,6 +70,8 @@ export const useSettingsStore = defineStore("setting", () => {
|
||||
showTagsView,
|
||||
showAppLogo,
|
||||
showWatermark,
|
||||
showSettings,
|
||||
showDesktopTools,
|
||||
sidebarColorScheme,
|
||||
layout,
|
||||
} as const;
|
||||
@@ -125,6 +139,8 @@ export const useSettingsStore = defineStore("setting", () => {
|
||||
showTagsView.value = defaultSettings.showTagsView;
|
||||
showAppLogo.value = defaultSettings.showAppLogo;
|
||||
showWatermark.value = defaultSettings.showWatermark;
|
||||
showSettings.value = defaultSettings.showSettings;
|
||||
showDesktopTools.value = defaultSettings.showDesktopTools;
|
||||
sidebarColorScheme.value = defaultSettings.sidebarColorScheme;
|
||||
layout.value = defaultSettings.layout as LayoutMode;
|
||||
themeColor.value = defaultSettings.themeColor;
|
||||
@@ -137,6 +153,8 @@ export const useSettingsStore = defineStore("setting", () => {
|
||||
showTagsView,
|
||||
showAppLogo,
|
||||
showWatermark,
|
||||
showSettings,
|
||||
showDesktopTools,
|
||||
sidebarColorScheme,
|
||||
layout,
|
||||
themeColor,
|
||||
|
||||
Vendored
+2
@@ -64,6 +64,8 @@ declare global {
|
||||
version: string;
|
||||
/** 是否显示设置 */
|
||||
showSettings: boolean;
|
||||
/** 是否显示桌面端工具项 */
|
||||
showDesktopTools: boolean;
|
||||
/** 是否显示多标签导航 */
|
||||
showTagsView: boolean;
|
||||
/** 是否显示应用Logo */
|
||||
|
||||
@@ -0,0 +1,756 @@
|
||||
<template>
|
||||
<div class="resource-management">
|
||||
<!-- 页面头部 -->
|
||||
<div class="page-header">
|
||||
<div class="header-left">
|
||||
<h2>资源管理</h2>
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item
|
||||
v-for="(item, index) in breadcrumbList"
|
||||
:key="index"
|
||||
@click="handleBreadcrumbClick(item, index)"
|
||||
:class="{ 'is-link': index < breadcrumbList.length - 1 }"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<el-button type="primary" @click="handleUpload">
|
||||
<el-icon><Upload /></el-icon>
|
||||
上传文件
|
||||
</el-button>
|
||||
<el-button @click="handleCreateDir">
|
||||
<el-icon><FolderAdd /></el-icon>
|
||||
新建文件夹
|
||||
</el-button>
|
||||
<el-button @click="handleRefresh">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索和筛选 -->
|
||||
<div class="search-section">
|
||||
<el-form :model="searchForm" inline>
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="请输入文件名或关键词"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="文件类型">
|
||||
<el-select v-model="searchForm.file_type" placeholder="请选择" clearable>
|
||||
<el-option label="图片" value="image" />
|
||||
<el-option label="文档" value="document" />
|
||||
<el-option label="视频" value="video" />
|
||||
<el-option label="音频" value="audio" />
|
||||
<el-option label="其他" value="other" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="文件大小">
|
||||
<el-input-number
|
||||
v-model="searchForm.min_size"
|
||||
placeholder="最小"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
/>
|
||||
<span style="margin: 0 8px">-</span>
|
||||
<el-input-number
|
||||
v-model="searchForm.max_size"
|
||||
placeholder="最大"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleResetSearch">
|
||||
<el-icon><RefreshLeft /></el-icon>
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 工具栏 -->
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<el-checkbox
|
||||
v-model="showHiddenFiles"
|
||||
@change="handleShowHiddenChange"
|
||||
>
|
||||
显示隐藏文件
|
||||
</el-checkbox>
|
||||
<el-checkbox
|
||||
v-model="recursiveMode"
|
||||
@change="handleRecursiveChange"
|
||||
>
|
||||
递归显示
|
||||
</el-checkbox>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<el-button-group>
|
||||
<el-button
|
||||
:type="viewMode === 'list' ? 'primary' : ''"
|
||||
@click="viewMode = 'list'"
|
||||
>
|
||||
<el-icon><List /></el-icon>
|
||||
</el-button>
|
||||
<el-button
|
||||
:type="viewMode === 'grid' ? 'primary' : ''"
|
||||
@click="viewMode = 'grid'"
|
||||
>
|
||||
<el-icon><Grid /></el-icon>
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文件列表 -->
|
||||
<div class="file-list">
|
||||
<el-table
|
||||
v-if="viewMode === 'list'"
|
||||
:data="fileList"
|
||||
v-loading="loading"
|
||||
@selection-change="handleSelectionChange"
|
||||
@row-dblclick="handleRowDoubleClick"
|
||||
row-key="path"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="名称" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<div class="file-name">
|
||||
<el-icon class="file-icon">
|
||||
<Folder v-if="row.is_directory" />
|
||||
<Document v-else />
|
||||
</el-icon>
|
||||
<span>{{ row.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="大小" width="120">
|
||||
<template #default="{ row }">
|
||||
<span v-if="!row.is_directory">{{ formatFileSize(row.size) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.file_type" size="small">{{ row.file_type }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="修改时间" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ formatDate(row.modified_time) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="!row.is_directory"
|
||||
type="primary"
|
||||
link
|
||||
@click="handleDownload(row)"
|
||||
>
|
||||
下载
|
||||
</el-button>
|
||||
<el-button type="primary" link @click="handleRename(row)">
|
||||
重命名
|
||||
</el-button>
|
||||
<el-button type="primary" link @click="handleMove(row)">
|
||||
移动
|
||||
</el-button>
|
||||
<el-button type="primary" link @click="handleCopy(row)">
|
||||
复制
|
||||
</el-button>
|
||||
<el-button type="danger" link @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 网格视图 -->
|
||||
<div v-else class="grid-view">
|
||||
<div
|
||||
v-for="item in fileList"
|
||||
:key="item.path"
|
||||
class="grid-item"
|
||||
@dblclick="handleItemDoubleClick(item)"
|
||||
>
|
||||
<div class="item-icon">
|
||||
<el-icon v-if="item.is_directory" size="48">
|
||||
<Folder />
|
||||
</el-icon>
|
||||
<el-icon v-else size="48">
|
||||
<Document />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="item-name">{{ item.name }}</div>
|
||||
<div class="item-size" v-if="!item.is_directory">
|
||||
{{ formatFileSize(item.size) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.page_no"
|
||||
v-model:page-size="pagination.page_size"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 上传对话框 -->
|
||||
<el-dialog
|
||||
v-model="uploadDialogVisible"
|
||||
title="上传文件"
|
||||
width="500px"
|
||||
:before-close="handleUploadClose"
|
||||
>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
:auto-upload="false"
|
||||
:multiple="true"
|
||||
:file-list="uploadFileList"
|
||||
@change="handleUploadChange"
|
||||
drag
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处,或<em>点击上传</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
支持多文件上传,单个文件不超过100MB
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<el-button @click="uploadDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleUploadConfirm" :loading="uploading">
|
||||
确定上传
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 新建文件夹对话框 -->
|
||||
<el-dialog
|
||||
v-model="createDirDialogVisible"
|
||||
title="新建文件夹"
|
||||
width="400px"
|
||||
>
|
||||
<el-form :model="createDirForm" label-width="80px">
|
||||
<el-form-item label="文件夹名" required>
|
||||
<el-input
|
||||
v-model="createDirForm.dir_name"
|
||||
placeholder="请输入文件夹名称"
|
||||
@keyup.enter="handleCreateDirConfirm"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="createDirDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleCreateDirConfirm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 重命名对话框 -->
|
||||
<el-dialog
|
||||
v-model="renameDialogVisible"
|
||||
title="重命名"
|
||||
width="400px"
|
||||
>
|
||||
<el-form :model="renameForm" label-width="80px">
|
||||
<el-form-item label="新名称" required>
|
||||
<el-input
|
||||
v-model="renameForm.new_name"
|
||||
placeholder="请输入新名称"
|
||||
@keyup.enter="handleRenameConfirm"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="renameDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleRenameConfirm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
Upload,
|
||||
FolderAdd,
|
||||
Refresh,
|
||||
Search,
|
||||
RefreshLeft,
|
||||
List,
|
||||
Grid,
|
||||
Folder,
|
||||
Document,
|
||||
UploadFilled
|
||||
} from '@element-plus/icons-vue'
|
||||
import { ResourceAPI, type ResourceItem, type ResourceListQuery, type ResourceSearchQuery } from '@/api/resource/resource'
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
const fileList = ref<ResourceItem[]>([])
|
||||
const selectedItems = ref<ResourceItem[]>([])
|
||||
const currentPath = ref('/')
|
||||
const breadcrumbList = ref([{ name: '根目录', path: '/' }])
|
||||
const showHiddenFiles = ref(false)
|
||||
const recursiveMode = ref(false)
|
||||
const viewMode = ref<'list' | 'grid'>('list')
|
||||
const total = ref(0)
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
page_no: 1,
|
||||
page_size: 20
|
||||
})
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive<ResourceSearchQuery>({
|
||||
keyword: '',
|
||||
file_type: '',
|
||||
min_size: undefined,
|
||||
max_size: undefined
|
||||
})
|
||||
|
||||
// 对话框状态
|
||||
const uploadDialogVisible = ref(false)
|
||||
const createDirDialogVisible = ref(false)
|
||||
const renameDialogVisible = ref(false)
|
||||
const uploading = ref(false)
|
||||
|
||||
// 上传相关
|
||||
const uploadRef = ref()
|
||||
const uploadFileList = ref<any[]>([])
|
||||
|
||||
// 表单数据
|
||||
const createDirForm = reactive({
|
||||
dir_name: ''
|
||||
})
|
||||
|
||||
const renameForm = reactive({
|
||||
new_name: '',
|
||||
old_path: ''
|
||||
})
|
||||
|
||||
// 计算属性
|
||||
const currentQuery = computed(() => ({
|
||||
path: currentPath.value,
|
||||
recursive: recursiveMode.value,
|
||||
include_hidden: showHiddenFiles.value
|
||||
}))
|
||||
|
||||
// 方法
|
||||
const loadFileList = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const response = await ResourceAPI.getResourceList(currentQuery.value)
|
||||
fileList.value = response.data.data || []
|
||||
total.value = fileList.value.length
|
||||
} catch (error) {
|
||||
ElMessage.error('加载文件列表失败')
|
||||
console.error('Load file list error:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleBreadcrumbClick = (item: any, index: number) => {
|
||||
if (index < breadcrumbList.value.length - 1) {
|
||||
currentPath.value = item.path
|
||||
updateBreadcrumb()
|
||||
loadFileList()
|
||||
}
|
||||
}
|
||||
|
||||
const updateBreadcrumb = () => {
|
||||
const pathParts = currentPath.value.split('/').filter(Boolean)
|
||||
breadcrumbList.value = [
|
||||
{ name: '根目录', path: '/' },
|
||||
...pathParts.map((part, index) => ({
|
||||
name: part,
|
||||
path: '/' + pathParts.slice(0, index + 1).join('/')
|
||||
}))
|
||||
]
|
||||
}
|
||||
|
||||
const handleRowDoubleClick = (row: ResourceItem) => {
|
||||
if (row.is_directory) {
|
||||
currentPath.value = row.path
|
||||
updateBreadcrumb()
|
||||
loadFileList()
|
||||
}
|
||||
}
|
||||
|
||||
const handleItemDoubleClick = (item: ResourceItem) => {
|
||||
if (item.is_directory) {
|
||||
currentPath.value = item.path
|
||||
updateBreadcrumb()
|
||||
loadFileList()
|
||||
}
|
||||
}
|
||||
|
||||
const handleSelectionChange = (selection: ResourceItem[]) => {
|
||||
selectedItems.value = selection
|
||||
}
|
||||
|
||||
const handleUpload = () => {
|
||||
uploadDialogVisible.value = true
|
||||
uploadFileList.value = []
|
||||
}
|
||||
|
||||
const handleUploadChange = (file: any, fileList: any[]) => {
|
||||
uploadFileList.value = fileList
|
||||
}
|
||||
|
||||
const handleUploadConfirm = async () => {
|
||||
if (uploadFileList.value.length === 0) {
|
||||
ElMessage.warning('请选择要上传的文件')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
uploading.value = true
|
||||
const formData = new FormData()
|
||||
uploadFileList.value.forEach((file: any) => {
|
||||
formData.append('file', file.raw)
|
||||
})
|
||||
formData.append('target_path', currentPath.value)
|
||||
|
||||
await ResourceAPI.uploadFile(formData)
|
||||
ElMessage.success('上传成功')
|
||||
uploadDialogVisible.value = false
|
||||
loadFileList()
|
||||
} catch (error) {
|
||||
ElMessage.error('上传失败')
|
||||
console.error('Upload error:', error)
|
||||
} finally {
|
||||
uploading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleUploadClose = () => {
|
||||
uploadDialogVisible.value = false
|
||||
uploadFileList.value = []
|
||||
}
|
||||
|
||||
const handleCreateDir = () => {
|
||||
createDirForm.dir_name = ''
|
||||
createDirDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleCreateDirConfirm = async () => {
|
||||
if (!createDirForm.dir_name.trim()) {
|
||||
ElMessage.warning('请输入文件夹名称')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await ResourceAPI.createDirectory({
|
||||
parent_path: currentPath.value,
|
||||
dir_name: createDirForm.dir_name.trim()
|
||||
})
|
||||
ElMessage.success('创建成功')
|
||||
createDirDialogVisible.value = false
|
||||
loadFileList()
|
||||
} catch (error) {
|
||||
ElMessage.error('创建失败')
|
||||
console.error('Create directory error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleRefresh = () => {
|
||||
loadFileList()
|
||||
}
|
||||
|
||||
const handleSearch = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const response = await ResourceAPI.searchResource(searchForm)
|
||||
fileList.value = response.data.data || []
|
||||
total.value = fileList.value.length
|
||||
} catch (error) {
|
||||
ElMessage.error('搜索失败')
|
||||
console.error('Search error:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleResetSearch = () => {
|
||||
Object.assign(searchForm, {
|
||||
keyword: '',
|
||||
file_type: '',
|
||||
min_size: undefined,
|
||||
max_size: undefined
|
||||
})
|
||||
loadFileList()
|
||||
}
|
||||
|
||||
const handleShowHiddenChange = () => {
|
||||
loadFileList()
|
||||
}
|
||||
|
||||
const handleRecursiveChange = () => {
|
||||
loadFileList()
|
||||
}
|
||||
|
||||
const handleDownload = async (item: ResourceItem) => {
|
||||
try {
|
||||
const response = await ResourceAPI.downloadFile(item.path)
|
||||
const blob = response.data
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = item.name
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
} catch (error) {
|
||||
ElMessage.error('下载失败')
|
||||
console.error('Download error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleRename = (item: ResourceItem) => {
|
||||
renameForm.old_path = item.path
|
||||
renameForm.new_name = item.name
|
||||
renameDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleRenameConfirm = async () => {
|
||||
if (!renameForm.new_name.trim()) {
|
||||
ElMessage.warning('请输入新名称')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await ResourceAPI.renameResource({
|
||||
old_path: renameForm.old_path,
|
||||
new_name: renameForm.new_name.trim()
|
||||
})
|
||||
ElMessage.success('重命名成功')
|
||||
renameDialogVisible.value = false
|
||||
loadFileList()
|
||||
} catch (error) {
|
||||
ElMessage.error('重命名失败')
|
||||
console.error('Rename error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleMove = (item: ResourceItem) => {
|
||||
// TODO: 实现移动功能
|
||||
ElMessage.info('移动功能待实现')
|
||||
}
|
||||
|
||||
const handleCopy = (item: ResourceItem) => {
|
||||
// TODO: 实现复制功能
|
||||
ElMessage.info('复制功能待实现')
|
||||
}
|
||||
|
||||
const handleDelete = async (item: ResourceItem) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要删除 ${item.name} 吗?`,
|
||||
'确认删除',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
|
||||
await ResourceAPI.deleteResource([item.path])
|
||||
ElMessage.success('删除成功')
|
||||
loadFileList()
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
ElMessage.error('删除失败')
|
||||
console.error('Delete error:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleSizeChange = (size: number) => {
|
||||
pagination.page_size = size
|
||||
loadFileList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (page: number) => {
|
||||
pagination.page_no = page
|
||||
loadFileList()
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
const formatFileSize = (size?: number) => {
|
||||
if (!size) return '-'
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
let unitIndex = 0
|
||||
let fileSize = size
|
||||
|
||||
while (fileSize >= 1024 && unitIndex < units.length - 1) {
|
||||
fileSize /= 1024
|
||||
unitIndex++
|
||||
}
|
||||
|
||||
return `${fileSize.toFixed(1)} ${units[unitIndex]}`
|
||||
}
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleString()
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
loadFileList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.resource-management {
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.header-left {
|
||||
h2 {
|
||||
margin: 0 0 10px 0;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px 20px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.file-list {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
|
||||
.file-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.file-icon {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-view {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
|
||||
.grid-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
border-color: #409eff;
|
||||
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
margin-bottom: 10px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.item-size {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-breadcrumb__item) {
|
||||
&.is-link {
|
||||
cursor: pointer;
|
||||
color: #409eff;
|
||||
|
||||
&:hover {
|
||||
color: #66b1ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,13 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>资源管理开发中,暂未提供</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user