diff --git a/components.d.ts b/components.d.ts index d6a42cc..fb754dd 100644 --- a/components.d.ts +++ b/components.d.ts @@ -21,6 +21,7 @@ declare module 'vue' { ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup'] ElCard: typeof import('element-plus/es')['ElCard'] ElCascader: typeof import('element-plus/es')['ElCascader'] + ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCol: typeof import('element-plus/es')['ElCol'] ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] ElContainer: typeof import('element-plus/es')['ElContainer'] diff --git a/docs/获取缓存数据.md b/docs/获取缓存数据.md index d9740e8..ef0e2fb 100644 --- a/docs/获取缓存数据.md +++ b/docs/获取缓存数据.md @@ -6,7 +6,7 @@ import { onMounted } from 'vue'; const authStore = useAuthStore(); // 获取租户ID -const tenantId = (authStore.user as any)?.tenant_id; +const tenantId = (authStore.user as any)?.tid; // 获取用户信息 const userInfo = authStore.user; diff --git a/src/api/erp.js b/src/api/erp.js index eafad9f..cde3598 100644 --- a/src/api/erp.js +++ b/src/api/erp.js @@ -101,7 +101,7 @@ export function getEmployeeList(tenantId) { return request({ url: '/admin/erp/getEmployee', method: 'get', - params: { tenant_id: tenantId } + params: { tid: tenantId } }); } diff --git a/src/api/file.js b/src/api/file.js index c08533e..24378cf 100644 --- a/src/api/file.js +++ b/src/api/file.js @@ -144,6 +144,18 @@ export function deleteFile(id) { }); } +/** + * 删除文件 + * @param {number|string} id 文件ID + * @returns {Promise} + */ +export function deleteFilePermanently(id) { + return request({ + url: `/admin/deleteFilePermanently/${id}`, + method: "delete", + }); +} + /** * 移动文件 * @param {number|string} id 文件ID @@ -159,37 +171,41 @@ export function moveFile(id, cate) { } /** - * 上传头像 - * @param {FormData} formData 文件数据 - * @param {Object} options 额外选项 - * @param {string} [options.cate] + * 批量删除文件 + * @param {Array} ids 文件ID数组 * @returns {Promise} */ -export function uploadAvatar(formData, options = {}) { - if (options.cate) { - formData.append('cate', options.cate); - } - +export function batchDeleteFiles(ids) { return request({ - url: "/admin/uploadavatar", + url: "/admin/batchDeleteFiles", method: "post", - data: formData, - headers: { - "Content-Type": "multipart/form-data" - } + data: { ids }, }); } /** - * 更新头像 - * @param {number|string} id 文件ID - * @param {Object} fileData 更新头像 + * 批量彻底删除文件 + * @param {Array} ids 文件ID数组 * @returns {Promise} */ -export function updateAvatar(id, fileData) { +export function batchDeleteFilesPermanently(ids) { return request({ - url: `/admin/uploadavatar/${id}`, + url: "/admin/batchDeleteFilesPermanently", method: "post", - data: fileData, + data: { ids }, + }); +} + +/** + * 批量移动文件 + * @param {Array} ids 文件ID数组 + * @param {number} cate 目标分类ID + * @returns {Promise} + */ +export function batchMoveFiles(ids, cate) { + return request({ + url: "/admin/batchMoveFiles", + method: "post", + data: { ids, cate }, }); } \ No newline at end of file diff --git a/src/stores/auth.js b/src/stores/auth.js index fad6a7e..f67adf2 100644 --- a/src/stores/auth.js +++ b/src/stores/auth.js @@ -40,7 +40,7 @@ export const useAuthStore = defineStore('auth', () => { account: userInfo.account || '', name: userInfo.name || '', group_id: userInfo.group_id || '', - tenant_id: userInfo.tenant_id || '', + tid: userInfo.tid || '', avatar: userInfo.avatar || '' } diff --git a/src/views/apps/cms/articles/category.vue b/src/views/apps/cms/articles/category.vue index 0c94e82..ea496cd 100644 --- a/src/views/apps/cms/articles/category.vue +++ b/src/views/apps/cms/articles/category.vue @@ -1,5 +1,19 @@ - +
+
+ +
+
+ +
-
+
file
+ + + - 移动 + - 下载 + - 删除 + + + +
@@ -211,7 +265,7 @@
-
+
+ + + + + + + + + + + + +
@@ -391,6 +472,7 @@ import { Files, Download, Delete, + DeleteFilled, Edit, ZoomIn, ZoomOut, @@ -404,7 +486,11 @@ import { createFileCate, renameFileCate, deleteFileCate, + deleteFilePermanently, deleteFile, + batchDeleteFiles, + batchDeleteFilesPermanently, + batchMoveFiles, } from "@/api/file"; import UploadFile from "./components/uploadFile.vue"; import MoveFile from "./components/moveFile.vue"; @@ -440,7 +526,7 @@ const loading = ref(false); // 分页相关 const currentPage = ref(1); -const pageSize = ref(24); +const pageSize = ref(20); const total = ref(0); // 上传对话框 @@ -450,6 +536,14 @@ const showUploadDialog = ref(false); const showMoveDialog = ref(false); const selectedFile = ref(null); +// 批量选择 +const selectedFileIds = ref([]); + +// 批量移动对话框 +const showBatchMoveDialog = ref(false); +const batchMoveTargetCate = ref(0); +const batchMoveLoading = ref(false); + // 图片预览 const showImagePreview = ref(false); const previewImageUrl = ref(""); @@ -500,7 +594,7 @@ const getFileUrl = (url: string) => { return `${import.meta.env.VITE_API_BASE_URL}${url}`; }; -//未分类分组(固定,不可编辑) +// 未分类分组(固定,不可编辑) const uncategorizedGroup = ref({ id: 0, name: "未分类", @@ -508,17 +602,9 @@ const uncategorizedGroup = ref({ description: "未分类的文件", }); -// 头像分组(固定,不可编辑) -const avatarGroup = ref({ - id: 999, - name: "头像", - total: 0, - description: "用户头像", -}); - // 过滤后的分组列表(未分类组置顶) const filteredGroups = computed(() => { - let result = [uncategorizedGroup.value, avatarGroup.value, ...groups.value]; + let result = [uncategorizedGroup.value, ...groups.value]; if (groupSearchQuery.value) { const query = groupSearchQuery.value.toLowerCase(); @@ -651,9 +737,6 @@ const loadFiles = async () => { if (selectedGroup.value.id === 0) { // 更新未分类组的文件数量 uncategorizedGroup.value.total = res.data.total || 0; - } else if (selectedGroup.value.id === 999) { - // 更新头像组的文件数量 - avatarGroup.value.total = res.data.total || 0; } else { // 更新普通分组的文件数量 const group = groups.value.find( @@ -965,6 +1048,30 @@ function handleDelete(row) { }); } +// 彻底删除文件 +function handlePermanentDelete(row) { + ElMessageBox.confirm("确定要彻底删除文件吗?此操作不可恢复,服务器上的文件也会被删除!", "警告", { + confirmButtonText: "确定删除", + cancelButtonText: "取消", + type: "error", + }).then(() => { + deleteFilePermanently(row.id).then((res) => { + const resp = + res && typeof res.code !== "undefined" + ? res + : res && res.data + ? res.data + : res; + if (resp.code === 200) { + ElMessage.success("彻底删除成功"); + loadFiles(); + } else { + ElMessage.error((resp && resp.message) || "彻底删除失败"); + } + }); + }); +} + const handleUpload = () => { if (!selectedGroup.value) { ElMessage.warning("请先选择一个分组"); @@ -979,6 +1086,158 @@ const handleUploadSuccess = () => { loadFiles(); }; +// 切换文件选择 +const toggleFileSelection = (file: FileItem) => { + const index = selectedFileIds.value.indexOf(file.id); + if (index > -1) { + selectedFileIds.value.splice(index, 1); + } else { + selectedFileIds.value.push(file.id); + } +}; + +// 全选/反选切换 +const toggleSelectAll = () => { + const allFileIds = files.value.map(file => file.id); + const isAllSelected = allFileIds.length === selectedFileIds.value.length && allFileIds.length > 0; + + if (isAllSelected) { + selectedFileIds.value = []; + } else { + selectedFileIds.value = allFileIds; + } +}; + +// 清除选择 +const clearSelection = () => { + selectedFileIds.value = []; +}; + +// 批量移动 +const handleBatchMove = () => { + if (selectedFileIds.value.length === 0) { + ElMessage.warning('请先选择要移动的文件'); + return; + } + batchMoveTargetCate.value = 0; + showBatchMoveDialog.value = true; +}; + +// 确认批量移动 +const confirmBatchMove = async () => { + if (selectedFileIds.value.length === 0) { + ElMessage.warning('请先选择要移动的文件'); + return; + } + if (batchMoveTargetCate.value === null || batchMoveTargetCate.value === undefined) { + ElMessage.warning('请选择目标分组'); + return; + } + + batchMoveLoading.value = true; + try { + const res = await batchMoveFiles(selectedFileIds.value, batchMoveTargetCate.value); + const resp = + res && typeof res.code !== 'undefined' + ? res + : res && res.data + ? res.data + : res; + if (resp.code === 200) { + ElMessage.success('批量移动成功'); + showBatchMoveDialog.value = false; + clearSelection(); + loadFiles(); + getUserCateData(); + } else { + ElMessage.error((resp && resp.message) || '批量移动失败'); + } + } catch (error) { + console.error('批量移动失败:', error); + ElMessage.error('批量移动失败'); + } finally { + batchMoveLoading.value = false; + } +}; + +// 批量删除 +const handleBatchDelete = () => { + if (selectedFileIds.value.length === 0) { + ElMessage.warning('请先选择要删除的文件'); + return; + } + + ElMessageBox.confirm( + `确定要删除选中的 ${selectedFileIds.value.length} 个文件吗?`, + '提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + } + ).then(async () => { + try { + const res = await batchDeleteFiles(selectedFileIds.value); + const resp = + res && typeof res.code !== 'undefined' + ? res + : res && res.data + ? res.data + : res; + if (resp.code === 200) { + ElMessage.success('批量删除成功'); + clearSelection(); + loadFiles(); + getUserCateData(); + } else { + ElMessage.error((resp && resp.message) || '批量删除失败'); + } + } catch (error) { + console.error('批量删除失败:', error); + ElMessage.error('批量删除失败'); + } + }); +}; + +// 批量永久删除 +const handleBatchDeletePermanently = () => { + if (selectedFileIds.value.length === 0) { + ElMessage.warning('请先选择要永久删除的文件'); + return; + } + + ElMessageBox.confirm( + `确定要永久删除选中的 ${selectedFileIds.value.length} 个文件吗?此操作不可恢复,服务器上的文件也会被删除!`, + '危险操作', + { + confirmButtonText: '确定永久删除', + cancelButtonText: '取消', + type: 'error', + } + ).then(async () => { + try { + const res = await batchDeleteFilesPermanently(selectedFileIds.value); + const resp = + res && typeof res.code !== 'undefined' + ? res + : res && res.data + ? res.data + : res; + if (resp.code === 200) { + ElMessage.success('批量永久删除成功'); + clearSelection(); + loadFiles(); + getUserCateData(); + } else { + ElMessage.error((resp && resp.message) || '批量永久删除失败'); + } + } catch (error) { + console.error('批量永久删除失败:', error); + ElMessage.error('批量永久删除失败'); + } + }); +}; + // 初始化 onMounted(() => { getUserCateData(); @@ -1111,9 +1370,23 @@ onMounted(() => { min-height: 400px; } + .file-grid-header { + margin-bottom: 12px; + display: flex; + justify-content: flex-start; + + .allbtns{ + color:#0081ff; + + &:hover{ + color:#0088ff; + } + } + } + .file-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); grid-auto-rows: auto; align-items: start; gap: 16px; @@ -1126,12 +1399,25 @@ onMounted(() => { // height: 100%; display: flex; flex-direction: column; + position: relative; &:hover { transform: translateY(-4px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } + &.file-card-selected { + border: 2px solid var(--el-color-primary); + box-shadow: 0 0 0 2px rgba(var(--el-color-primary-rgb), 0.2); + } + + .file-checkbox { + position: absolute; + top: 8px; + left: 8px; + z-index: 10; + } + :deep(.el-card__body) { padding: 16px; height: 100%; diff --git a/src/views/system/modules/index.vue b/src/views/system/modules/index.vue index 983b979..b757fb3 100644 --- a/src/views/system/modules/index.vue +++ b/src/views/system/modules/index.vue @@ -24,6 +24,7 @@ > +