批量更新租户规则

This commit is contained in:
扫地僧 2026-03-09 22:55:27 +08:00
parent 108ec50978
commit a5a02eb8c9
19 changed files with 399 additions and 66 deletions

1
components.d.ts vendored
View File

@ -21,6 +21,7 @@ declare module 'vue' {
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup'] ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
ElCard: typeof import('element-plus/es')['ElCard'] ElCard: typeof import('element-plus/es')['ElCard']
ElCascader: typeof import('element-plus/es')['ElCascader'] ElCascader: typeof import('element-plus/es')['ElCascader']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCol: typeof import('element-plus/es')['ElCol'] ElCol: typeof import('element-plus/es')['ElCol']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
ElContainer: typeof import('element-plus/es')['ElContainer'] ElContainer: typeof import('element-plus/es')['ElContainer']

View File

@ -6,7 +6,7 @@ import { onMounted } from 'vue';
const authStore = useAuthStore(); const authStore = useAuthStore();
// 获取租户ID // 获取租户ID
const tenantId = (authStore.user as any)?.tenant_id; const tenantId = (authStore.user as any)?.tid;
// 获取用户信息 // 获取用户信息
const userInfo = authStore.user; const userInfo = authStore.user;

View File

@ -101,7 +101,7 @@ export function getEmployeeList(tenantId) {
return request({ return request({
url: '/admin/erp/getEmployee', url: '/admin/erp/getEmployee',
method: 'get', method: 'get',
params: { tenant_id: tenantId } params: { tid: tenantId }
}); });
} }

View File

@ -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 * @param {number|string} id 文件ID
@ -159,37 +171,41 @@ export function moveFile(id, cate) {
} }
/** /**
* 上传头像 * 批量删除文件
* @param {FormData} formData 文件数据 * @param {Array} ids 文件ID数组
* @param {Object} options 额外选项
* @param {string} [options.cate]
* @returns {Promise} * @returns {Promise}
*/ */
export function uploadAvatar(formData, options = {}) { export function batchDeleteFiles(ids) {
if (options.cate) {
formData.append('cate', options.cate);
}
return request({ return request({
url: "/admin/uploadavatar", url: "/admin/batchDeleteFiles",
method: "post", method: "post",
data: formData, data: { ids },
headers: {
"Content-Type": "multipart/form-data"
}
}); });
} }
/** /**
* 更新头像 * 批量彻底删除文件
* @param {number|string} id 文件ID * @param {Array} ids 文件ID数组
* @param {Object} fileData 更新头像
* @returns {Promise} * @returns {Promise}
*/ */
export function updateAvatar(id, fileData) { export function batchDeleteFilesPermanently(ids) {
return request({ return request({
url: `/admin/uploadavatar/${id}`, url: "/admin/batchDeleteFilesPermanently",
method: "post", 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 },
}); });
} }

View File

@ -40,7 +40,7 @@ export const useAuthStore = defineStore('auth', () => {
account: userInfo.account || '', account: userInfo.account || '',
name: userInfo.name || '', name: userInfo.name || '',
group_id: userInfo.group_id || '', group_id: userInfo.group_id || '',
tenant_id: userInfo.tenant_id || '', tid: userInfo.tid || '',
avatar: userInfo.avatar || '' avatar: userInfo.avatar || ''
} }

View File

@ -1,5 +1,19 @@
<template> <template>
<div class="category-manager"> <div class="category-manager">
<!-- 页面头部 -->
<div class="page-header">
<div class="page-title">
<h3>文章分类</h3>
<p> {{ totalCount }} 个分类</p>
</div>
<div class="page-actions">
<el-button type="primary" :icon="Plus" @click="handleAdd">
新增分类
</el-button>
<el-button :icon="Refresh" @click="handleRefresh">刷新</el-button>
</div>
</div>
<div class="category-list" v-loading="loading"> <div class="category-list" v-loading="loading">
<div v-if="filteredCategories.length === 0" class="empty-state"> <div v-if="filteredCategories.length === 0" class="empty-state">
<el-empty description="暂无文章分类" /> <el-empty description="暂无文章分类" />
@ -55,6 +69,13 @@ const currentEdit = ref(null);
const searchText = ref(""); const searchText = ref("");
const categories = ref([]); const categories = ref([]);
//
const addCategory = ref(null);
const handleAdd = () => {
addCategory.value = { parentId: 0 };
dialogVisible.value = true;
}
// //
const totalCount = computed(() => categories.value.length); const totalCount = computed(() => categories.value.length);
@ -178,6 +199,8 @@ onMounted(() => {
<style lang="less" scoped> <style lang="less" scoped>
.category-manager { .category-manager {
min-height: 100vh; min-height: 100vh;
padding: 20px;
background-color: var(--el-bg-color-page);
// //
.page-header { .page-header {

View File

@ -8,7 +8,7 @@
<div class="search-bar"> <div class="search-bar">
<el-input <el-input
v-model="searchQuery" v-model="searchQuery"
placeholder="搜索文章标题/作者" placeholder="搜索文章标题"
clearable clearable
@clear="handleSearch" @clear="handleSearch"
> >

View File

@ -0,0 +1,3 @@
<template></template>
<script setup lang="ts"></script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,3 @@
<template></template>
<script setup lang="ts"></script>
<style lang="less" scoped></style>

View File

@ -79,7 +79,7 @@ const total = ref(0);
const editRef = ref<any>(null); const editRef = ref<any>(null);
const authStore = useAuthStore(); const authStore = useAuthStore();
const tenantId = (authStore.user as any)?.tenant_id; const tenantId = (authStore.user as any)?.tid;
// console.log(tenantId); // console.log(tenantId);

View File

@ -90,7 +90,7 @@ const loading = ref(false)
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
const isEdit = ref(false) const isEdit = ref(false)
const authStore = useAuthStore() const authStore = useAuthStore()
const tenantId = (authStore.user as any)?.tenant_id const tenantId = (authStore.user as any)?.tid
const leaderList = ref([]) const leaderList = ref([])
const formData = reactive({ const formData = reactive({
id: 0, id: 0,
@ -192,7 +192,7 @@ const handleSubmit = async () => {
} }
}) })
if (tenantId) { if (tenantId) {
submitData.tenant_id = tenantId submitData.tid = tenantId
} }
res = await editOrganization(formData.id, submitData) res = await editOrganization(formData.id, submitData)
} else { } else {
@ -205,7 +205,7 @@ const handleSubmit = async () => {
} }
}) })
if (tenantId) { if (tenantId) {
submitData.append('tenant_id', String(tenantId)) submitData.append('tid', String(tenantId))
} }
res = await createOrganization(submitData) res = await createOrganization(submitData)
} }

View File

@ -117,7 +117,7 @@ import { getOrganizationList, getOrganizationDetail, deleteOrganization } from '
// //
interface TreeNode { interface TreeNode {
id: number id: number
tenant_id?: number tid?: number
org_name: string org_name: string
org_code?: string org_code?: string
parent_id?: number parent_id?: number
@ -153,7 +153,7 @@ const buildTree = (list: any[]): TreeNode[] => {
list.forEach(item => { list.forEach(item => {
const node: TreeNode = { const node: TreeNode = {
id: item.id, id: item.id,
tenant_id: item.tenant_id, tid: item.tid,
org_name: item.org_name, org_name: item.org_name,
org_code: item.org_code, org_code: item.org_code,
parent_id: item.parent_id, parent_id: item.parent_id,
@ -193,7 +193,7 @@ const loadOrganizationDetail = async (id: number) => {
if (data) { if (data) {
selectedNode.value = { selectedNode.value = {
id: data.id, id: data.id,
tenant_id: data.tenant_id, tid: data.tid,
org_name: data.org_name, org_name: data.org_name,
org_code: data.org_code, org_code: data.org_code,
parent_id: data.parent_id, parent_id: data.parent_id,

View File

@ -56,7 +56,7 @@
详情 详情
</el-button> </el-button>
<el-button <el-button
v-if="row.default !== 1 && row.default !== 2" v-if="userInfo?.id === 1"
size="small" size="small"
type="primary" type="primary"
link link
@ -66,7 +66,7 @@
编辑 编辑
</el-button> </el-button>
<el-button <el-button
v-if="row.id !== 1" v-if="userInfo?.id === 1"
size="small" size="small"
type="danger" type="danger"
link link

View File

@ -88,7 +88,7 @@ const submitForm = async () => {
try { try {
const submitData = { const submitData = {
...formData, ...formData,
tenant_id: currentTenantId.value tid: currentTenantId.value
}; };
const res = await addUser(submitData); const res = await addUser(submitData);

View File

@ -72,7 +72,7 @@
const formRef = ref(); const formRef = ref();
const formData = reactive({ const formData = reactive({
tenant_id: null, tid: null,
type: '', type: '',
file_url: '', file_url: '',
expire_time: '', expire_time: '',
@ -89,7 +89,7 @@
const open = (row: any) => { const open = (row: any) => {
visible.value = true; visible.value = true;
tenantName.value = row.tenant_name; tenantName.value = row.tenant_name;
formData.tenant_id = row.id; formData.tid = row.id;
// //
// fetchDetail(row.id); // fetchDetail(row.id);

View File

@ -113,7 +113,7 @@ const fetchDomains = async () => {
loading.value = true loading.value = true
try { try {
// //
const res = await getMyDomains({ tid: authStore.user.tenant_id }) const res = await getMyDomains({ tid: authStore.user.tid })
if (res.code === 200) { if (res.code === 200) {
tableData.value = res.data || [] tableData.value = res.data || []
} }
@ -133,7 +133,7 @@ const handleSubmit = async () => {
submitLoading.value = true submitLoading.value = true
try { try {
const res = await applyTenantDomain({ const res = await applyTenantDomain({
tenant_id: authStore.user.tenant_id, tid: authStore.user.tid,
main_domain: form.main_domain, main_domain: form.main_domain,
sub_domain: form.sub_domain sub_domain: form.sub_domain
}) })

View File

@ -55,9 +55,9 @@
<el-tag size="small">{{ row.dict_code }}</el-tag> <el-tag size="small">{{ row.dict_code }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="tenant_id" label="租户ID" width="100" align="center"> <el-table-column prop="tid" label="租户ID" width="100" align="center">
<template #default="{ row }"> <template #default="{ row }">
<span>{{ row.tenant_id || 0 }}</span> <span>{{ row.tid || 0 }}</span>
</template> </template>
</el-table-column> </el-table-column>
<!-- <el-table-column prop="parent_id" label="父级ID" width="100" align="center"> <!-- <el-table-column prop="parent_id" label="父级ID" width="100" align="center">

View File

@ -57,7 +57,7 @@
</div> </div>
<div class="group-count">{{ group.total }} 个文件</div> <div class="group-count">{{ group.total }} 个文件</div>
</div> </div>
<div class="group-actions" v-if="group.id !== 0 && group.id !== 999"> <div class="group-actions" v-if="group.id !== 0">
<div class="action-buttons" @click.stop> <div class="action-buttons" @click.stop>
<el-button <el-button
type="primary" type="primary"
@ -128,11 +128,46 @@
> >
上传文件 上传文件
</el-button> </el-button>
<el-button
type="warning"
:icon="FolderOpened"
@click="handleBatchMove"
:disabled="selectedFileIds.length === 0"
>
批量移动 ({{ selectedFileIds.length }})
</el-button>
<el-button
type="danger"
:icon="Delete"
@click="handleBatchDelete"
:disabled="selectedFileIds.length === 0"
>
批量删除 ({{ selectedFileIds.length }})
</el-button>
<el-button
type="danger"
plain
@click="handleBatchDeletePermanently"
:disabled="selectedFileIds.length === 0"
>
<el-icon><DeleteFilled /></el-icon>
批量永久删除 ({{ selectedFileIds.length }})
</el-button>
<el-button
type="info"
@click="clearSelection"
:disabled="selectedFileIds.length === 0"
>
取消选择
</el-button>
</div> </div>
</div> </div>
<!-- 文件卡片网格 --> <!-- 文件卡片网格 -->
<div class="file-grid-container"> <div class="file-grid-container">
<div class="file-grid-header" v-if="files.length > 0">
<button class="allbtns" @click="toggleSelectAll">全选/反选</button>
</div>
<div <div
v-loading="loading" v-loading="loading"
class="file-grid" class="file-grid"
@ -142,14 +177,14 @@
v-for="file in files" v-for="file in files"
:key="file.id" :key="file.id"
shadow="hover" shadow="hover"
class="file-card" :class="['file-card', { 'file-card-selected': selectedFileIds.includes(file.id) }]"
@click="handleFileClick(file)" @click="toggleFileSelection(file)"
> >
<div class="file-checkbox">
<el-checkbox :model-value="selectedFileIds.includes(file.id)" />
</div>
<div class="file-icon"> <div class="file-icon">
<div <div v-if="isImage(file)">
v-if="isImage(file)"
@click.stop="handleImagePreview(file)"
>
<img <img
:src="getFileUrl(file.url)" :src="getFileUrl(file.url)"
alt="file" alt="file"
@ -179,27 +214,46 @@
}}</span> }}</span>
</div> </div>
<div class="file-actions"> <div class="file-actions">
<el-button
v-if="isImage(file)"
type="primary"
link
@click.stop="handleImagePreview(file)"
title="预览"
>
<i class="fa-solid fa-eye"></i>
</el-button>
<el-button <el-button
type="primary" type="primary"
link link
@click.stop="handleMoveClick(file)" @click.stop="handleMoveClick(file)"
title="移动" title="移动"
> >
移动 <i class="fa-solid fa-arrows-up-down-left-right"></i>
</el-button> </el-button>
<el-button <el-button
type="primary" type="primary"
link link
@click.stop="handleDownload(file)" @click.stop="handleDownload(file)"
title="下载"
> >
下载 <i class="fa-solid fa-download"></i>
</el-button> </el-button>
<el-button <el-button
type="danger" type="danger"
link link
@click.stop="handleDelete(file)" @click.stop="handleDelete(file)"
title="删除"
> >
删除 <i class="fa-regular fa-trash-can"></i>
</el-button>
<el-button
type="danger"
link
@click.stop="handlePermanentDelete(file)"
title="彻底删除"
>
<i class="fa-solid fa-trash"></i>
</el-button> </el-button>
</div> </div>
</el-card> </el-card>
@ -211,7 +265,7 @@
</div> </div>
<!-- 分页组件 --> <!-- 分页组件 -->
<div class="pagination-container" v-if="total > 0"> <div class="pagination-container">
<el-pagination <el-pagination
v-model:current-page="currentPage" v-model:current-page="currentPage"
v-model:page-size="pageSize" v-model:page-size="pageSize"
@ -371,6 +425,33 @@
@success="handleCreateCategorySuccess" @success="handleCreateCategorySuccess"
@close="handleCreateCategoryClose" @close="handleCreateCategoryClose"
/> />
<!-- 批量移动文件对话框 -->
<el-dialog
v-model="showBatchMoveDialog"
title="批量移动文件"
width="400px"
>
<el-form label-width="80px">
<el-form-item label="目标分组">
<el-select v-model="batchMoveTargetCate" placeholder="请选择目标分组">
<el-option :value="0" label="未分类" />
<el-option
v-for="group in groups"
:key="group.id"
:value="group.id"
:label="group.name"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showBatchMoveDialog = false">取消</el-button>
<el-button type="primary" @click="confirmBatchMove" :loading="batchMoveLoading">
确定
</el-button>
</template>
</el-dialog>
</div> </div>
</template> </template>
@ -391,6 +472,7 @@ import {
Files, Files,
Download, Download,
Delete, Delete,
DeleteFilled,
Edit, Edit,
ZoomIn, ZoomIn,
ZoomOut, ZoomOut,
@ -404,7 +486,11 @@ import {
createFileCate, createFileCate,
renameFileCate, renameFileCate,
deleteFileCate, deleteFileCate,
deleteFilePermanently,
deleteFile, deleteFile,
batchDeleteFiles,
batchDeleteFilesPermanently,
batchMoveFiles,
} from "@/api/file"; } from "@/api/file";
import UploadFile from "./components/uploadFile.vue"; import UploadFile from "./components/uploadFile.vue";
import MoveFile from "./components/moveFile.vue"; import MoveFile from "./components/moveFile.vue";
@ -440,7 +526,7 @@ const loading = ref(false);
// //
const currentPage = ref(1); const currentPage = ref(1);
const pageSize = ref(24); const pageSize = ref(20);
const total = ref(0); const total = ref(0);
// //
@ -450,6 +536,14 @@ const showUploadDialog = ref(false);
const showMoveDialog = ref(false); const showMoveDialog = ref(false);
const selectedFile = ref<FileItem | null>(null); const selectedFile = ref<FileItem | null>(null);
//
const selectedFileIds = ref<number[]>([]);
//
const showBatchMoveDialog = ref(false);
const batchMoveTargetCate = ref(0);
const batchMoveLoading = ref(false);
// //
const showImagePreview = ref(false); const showImagePreview = ref(false);
const previewImageUrl = ref(""); const previewImageUrl = ref("");
@ -508,17 +602,9 @@ const uncategorizedGroup = ref<Group>({
description: "未分类的文件", description: "未分类的文件",
}); });
//
const avatarGroup = ref<Group>({
id: 999,
name: "头像",
total: 0,
description: "用户头像",
});
// //
const filteredGroups = computed(() => { const filteredGroups = computed(() => {
let result = [uncategorizedGroup.value, avatarGroup.value, ...groups.value]; let result = [uncategorizedGroup.value, ...groups.value];
if (groupSearchQuery.value) { if (groupSearchQuery.value) {
const query = groupSearchQuery.value.toLowerCase(); const query = groupSearchQuery.value.toLowerCase();
@ -651,9 +737,6 @@ const loadFiles = async () => {
if (selectedGroup.value.id === 0) { if (selectedGroup.value.id === 0) {
// //
uncategorizedGroup.value.total = res.data.total || 0; uncategorizedGroup.value.total = res.data.total || 0;
} else if (selectedGroup.value.id === 999) {
//
avatarGroup.value.total = res.data.total || 0;
} else { } else {
// //
const group = groups.value.find( 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 = () => { const handleUpload = () => {
if (!selectedGroup.value) { if (!selectedGroup.value) {
ElMessage.warning("请先选择一个分组"); ElMessage.warning("请先选择一个分组");
@ -979,6 +1086,158 @@ const handleUploadSuccess = () => {
loadFiles(); 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(() => { onMounted(() => {
getUserCateData(); getUserCateData();
@ -1111,9 +1370,23 @@ onMounted(() => {
min-height: 400px; min-height: 400px;
} }
.file-grid-header {
margin-bottom: 12px;
display: flex;
justify-content: flex-start;
.allbtns{
color:#0081ff;
&:hover{
color:#0088ff;
}
}
}
.file-grid { .file-grid {
display: 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; grid-auto-rows: auto;
align-items: start; align-items: start;
gap: 16px; gap: 16px;
@ -1126,12 +1399,25 @@ onMounted(() => {
// height: 100%; // height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative;
&:hover { &:hover {
transform: translateY(-4px); transform: translateY(-4px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 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) { :deep(.el-card__body) {
padding: 16px; padding: 16px;
height: 100%; height: 100%;

View File

@ -24,6 +24,7 @@
> >
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column prop="id" label="ID" width="80" align="center" /> <el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="mid" label="MID" width="80" align="center" />
<el-table-column <el-table-column
prop="name" prop="name"
label="模块名称" label="模块名称"