feat(web): 升级至 v3.0 并重构组件与引导流程

- 提取应用引导逻辑至 useAppBootstrap 组合式函数
- 增强日期选择器组件,支持全类型与格式自动推导
- 修复资源泄漏:清理 RAF、定时器、事件与 mitt 监听
- 修复退出登录逻辑与异步导出流程
- 迁移 ::v-deep 至 Vue 3 标准 :deep 语法
- 移除调试日志并改进类型定义与注释文档
This commit is contained in:
zhangtao
2026-05-11 23:37:11 +08:00
parent 7e7a5c97cb
commit 9a231aa58c
120 changed files with 2282 additions and 1931 deletions
@@ -22,8 +22,8 @@ defineEmits<{ link: [] }>();
<style scoped lang="scss">
.login-auth-link-row {
::v-deep(.el-link),
::v-deep(.el-link__inner) {
:deep(.el-link),
:deep(.el-link__inner) {
line-height: inherit;
}
}
@@ -246,11 +246,11 @@ html.dark {
/* EP 全局规则:相邻 .el-button { margin-left: 12px },纵向叠放时仍会命中,导致第二颗按钮横向错位 */
.login-mobile-actions {
::v-deep(.el-button + .el-button) {
:deep(.el-button + .el-button) {
margin-left: 0 !important;
}
::v-deep(.el-button) {
:deep(.el-button) {
box-sizing: border-box;
width: 100%;
min-width: 0;
@@ -78,7 +78,7 @@ const qrPayload = computed(() => {
/* EP:相邻按钮横向间距;单列返回按钮无需处理 */
.login-mobile-actions {
::v-deep(.el-button) {
:deep(.el-button) {
box-sizing: border-box;
width: 100%;
min-width: 0;
@@ -546,12 +546,14 @@ const showVoteNotification = () => {
});
};
let voteTimer: ReturnType<typeof setTimeout> | null = null;
onMounted(async () => {
setupAccount("super");
await configStore.getConfig();
await tryConsumeOAuthCallback();
getCaptcha();
setTimeout(showVoteNotification, 500);
voteTimer = setTimeout(showVoteNotification, 500);
});
onActivated(() => {
@@ -561,6 +563,7 @@ onActivated(() => {
});
onBeforeUnmount(() => {
if (voteTimer !== null) clearTimeout(voteTimer);
notificationInstance?.close();
notificationInstance = null;
});
@@ -661,68 +664,68 @@ async function submitForget() {
<style lang="scss" scoped>
.login-page-panel {
::v-deep(.btn) {
:deep(.btn) {
border-radius: 8px;
}
}
html.dark .login-page-panel {
::v-deep(.text-g-800) {
:deep(.text-g-800) {
color: rgb(255 255 255 / 78%) !important;
}
}
/* 副标题与表单块间距(login-page-form 在子组件内,须 :deep) */
::v-deep(.login-page-form) {
:deep(.login-page-form) {
margin-top: 1rem;
}
::v-deep(.login-page-form .el-form-item) {
:deep(.login-page-form .el-form-item) {
margin-bottom: 1.1rem;
}
::v-deep(.el-select__wrapper) {
:deep(.el-select__wrapper) {
min-height: 42px !important;
}
/* 仅账号登录底部「手机号 / 扫码」双列按钮需要 42px;手机号登录页的返回钮与主按钮同为 h-11,勿全局压高度 */
::v-deep(.login-secondary-actions .login-secondary-btn) {
:deep(.login-secondary-actions .login-secondary-btn) {
height: 42px;
border-radius: 8px;
}
html.dark {
::v-deep(.login-page-form .el-input__wrapper) {
:deep(.login-page-form .el-input__wrapper) {
background-color: rgb(255 255 255 / 6%) !important;
box-shadow: 0 0 0 1px rgb(255 255 255 / 10%) inset !important;
}
::v-deep(.login-page-form .el-input__inner) {
:deep(.login-page-form .el-input__inner) {
color: rgb(255 255 255 / 92%);
}
::v-deep(.login-page-form .el-input__inner::placeholder) {
:deep(.login-page-form .el-input__inner::placeholder) {
color: rgb(255 255 255 / 35%);
}
::v-deep(.login-page-form .el-select .el-select__wrapper) {
:deep(.login-page-form .el-select .el-select__wrapper) {
background-color: rgb(255 255 255 / 6%) !important;
box-shadow: 0 0 0 1px rgb(255 255 255 / 10%) inset !important;
}
::v-deep(.login-page-form .el-select__placeholder) {
:deep(.login-page-form .el-select__placeholder) {
color: rgb(255 255 255 / 40%);
}
::v-deep(.login-page-form .el-select__selected-item) {
:deep(.login-page-form .el-select__selected-item) {
color: rgb(255 255 255 / 92%);
}
::v-deep(.login-remember .el-checkbox__label) {
:deep(.login-remember .el-checkbox__label) {
color: rgb(255 255 255 / 65%);
}
::v-deep(.login-secondary-btn) {
:deep(.login-secondary-btn) {
color: rgb(255 255 255 / 85%) !important;
background: transparent !important;
border-color: rgb(255 255 255 / 22%) !important;
@@ -735,7 +738,7 @@ html.dark {
}
}
::v-deep(.login-secondary-actions .login-secondary-btn) {
:deep(.login-secondary-actions .login-secondary-btn) {
height: 42px;
}
}
@@ -310,20 +310,21 @@ async function loadDeptData() {
}
}
function deleteDeptRow(id: number) {
ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await DeptAPI.deleteDept([id]);
await userStore.getUserInfo();
ElMessage.success("删除成功");
selectedRows.value = [];
await loadDeptData();
})
.catch(() => {});
async function deleteDeptRow(id: number) {
try {
await ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await DeptAPI.deleteDept([id]);
await userStore.getUserInfo();
ElMessage.success("删除成功");
selectedRows.value = [];
await loadDeptData();
} catch {
// 用户取消
}
}
const opCtx = {
@@ -545,50 +546,46 @@ async function handleSubmit() {
});
}
function handleBatchDelete() {
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await DeptAPI.deleteDept(ids);
await userStore.getUserInfo();
ElMessage.success("删除成功");
selectedRows.value = [];
await loadDeptData();
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await DeptAPI.deleteDept(ids);
await userStore.getUserInfo();
ElMessage.success("删除成功");
selectedRows.value = [];
await loadDeptData();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
function handleMoreClick(status: string) {
async function handleMoreClick(status: string) {
const ids = selectedIds.value;
if (!ids.length) {
ElMessage.warning("请先选择要操作的数据");
return;
}
ElMessageBox.confirm(`确认${status === "0" ? "启用" : "停用"}该项数据?`, "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
await DeptAPI.batchDept({ ids, status });
await loadDeptData();
await userStore.getUserInfo();
} catch (error: unknown) {
console.error(error);
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确认${status === "0" ? "启用" : "停用"}该项数据?`, "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await DeptAPI.batchDept({ ids, status });
await loadDeptData();
await userStore.getUserInfo();
} catch {
// 用户取消或操作失败
}
}
function toggleExpand() {
@@ -614,15 +611,15 @@ onMounted(() => {
</script>
<style scoped lang="scss">
.crud-dialog-art-form ::v-deep(.el-row > .el-col:last-child) {
.crud-dialog-art-form :deep(.el-row > .el-col:last-child) {
display: none;
}
.crud-dialog-art-form ::v-deep(.el-form-item__content) {
.crud-dialog-art-form :deep(.el-form-item__content) {
max-width: 100%;
}
::v-deep(.dept-table-actions .inline-flex) {
:deep(.dept-table-actions .inline-flex) {
vertical-align: middle;
}
</style>
@@ -786,71 +786,66 @@ function formatDictDataOperationCell(row: DictDataTable) {
});
}
function deleteDictDataRow(id: number) {
ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await DictAPI.deleteDictData([id]);
dictStore.clearDictData();
if (props.dictType) {
await dictStore.getDict([props.dictType]);
}
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
})
.catch(() => {});
async function deleteDictDataRow(id: number) {
try {
await ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await DictAPI.deleteDictData([id]);
dictStore.clearDictData();
if (props.dictType) await dictStore.getDict([props.dictType]);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
}
}
function handleBatchDelete() {
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await DictAPI.deleteDictData(ids);
dictStore.clearDictData();
if (props.dictType) {
await dictStore.getDict([props.dictType]);
}
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await DictAPI.deleteDictData(ids);
dictStore.clearDictData();
if (props.dictType) await dictStore.getDict([props.dictType]);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
function handleMoreClick(status: string) {
async function handleMoreClick(status: string) {
const ids = selectedIds.value;
if (!ids.length) {
ElMessage.warning("请先选择要操作的数据");
return;
}
ElMessageBox.confirm(`确认${status === "0" ? "启用" : "停用"}该项数据?`, "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await DictAPI.batchDictData({ ids, status });
await refreshData();
dictStore.clearDictData();
if (props.dictType) {
await dictStore.getDict([props.dictType]);
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确认${status === "0" ? "启用" : "停用"}该项数据?`, "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await DictAPI.batchDictData({ ids, status });
await refreshData();
dictStore.clearDictData();
if (props.dictType) await dictStore.getDict([props.dictType]);
} catch {
// 用户取消
}
}
function openExportModal() {
@@ -890,11 +885,11 @@ function openExportModal() {
border-color: var(--el-border-color-lighter);
}
.crud-dialog-art-form ::v-deep(.el-row > .el-col:last-child) {
.crud-dialog-art-form :deep(.el-row > .el-col:last-child) {
display: none;
}
.crud-dialog-art-form ::v-deep(.el-form-item__content) {
.crud-dialog-art-form :deep(.el-form-item__content) {
max-width: 100%;
}
</style>
@@ -560,74 +560,69 @@ async function handleSubmit() {
});
}
function deleteDictTypeRow(id: number) {
ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await DictAPI.deleteDictType([id]);
dictStore.clearDictData();
const dictTypes = Object.keys(dictStore.dictData);
if (dictTypes.length > 0) {
await dictStore.getDict(dictTypes);
}
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
})
.catch(() => {});
async function deleteDictTypeRow(id: number) {
try {
await ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await DictAPI.deleteDictType([id]);
dictStore.clearDictData();
const dictTypes = Object.keys(dictStore.dictData);
if (dictTypes.length > 0) await dictStore.getDict(dictTypes);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
}
}
function handleBatchDelete() {
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await DictAPI.deleteDictType(ids);
dictStore.clearDictData();
const dictTypes = Object.keys(dictStore.dictData);
if (dictTypes.length > 0) {
await dictStore.getDict(dictTypes);
}
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await DictAPI.deleteDictType(ids);
dictStore.clearDictData();
const dictTypes = Object.keys(dictStore.dictData);
if (dictTypes.length > 0) await dictStore.getDict(dictTypes);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
function handleMoreClick(status: string) {
async function handleMoreClick(status: string) {
const ids = selectedIds.value;
if (!ids.length) {
ElMessage.warning("请先选择要操作的数据");
return;
}
ElMessageBox.confirm(`确认${status === "0" ? "启用" : "停用"}该项数据?`, "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await DictAPI.batchDictType({ ids, status });
await refreshData();
dictStore.clearDictData();
const dictTypes = Object.keys(dictStore.dictData);
if (dictTypes.length > 0) {
await dictStore.getDict(dictTypes);
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确认${status === "0" ? "启用" : "停用"}该项数据?`, "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await DictAPI.batchDictType({ ids, status });
await refreshData();
dictStore.clearDictData();
const dictTypes = Object.keys(dictStore.dictData);
if (dictTypes.length > 0) await dictStore.getDict(dictTypes);
} catch {
// 用户取消
}
}
function openExportModal() {
@@ -636,11 +631,11 @@ function openExportModal() {
</script>
<style scoped lang="scss">
.crud-dialog-art-form ::v-deep(.el-row > .el-col:last-child) {
.crud-dialog-art-form :deep(.el-row > .el-col:last-child) {
display: none;
}
.crud-dialog-art-form ::v-deep(.el-form-item__content) {
.crud-dialog-art-form :deep(.el-form-item__content) {
max-width: 100%;
}
</style>
@@ -461,19 +461,20 @@ async function handleOpenDialog(id: number) {
dialogVisible.value.visible = true;
}
function deleteLogRow(id: number) {
ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await LogAPI.deleteLog([id]);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
})
.catch(() => {});
async function deleteLogRow(id: number) {
try {
await ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await LogAPI.deleteLog([id]);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
}
}
function buildLogRowActions(row: LogTable): TableOperationAction[] {
@@ -507,26 +508,25 @@ function formatLogOperationCell(row: LogTable) {
});
}
function handleBatchDelete() {
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await LogAPI.deleteLog(ids);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await LogAPI.deleteLog(ids);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
function openExportModal() {
@@ -436,7 +436,7 @@
<ElFormItem v-if="formData.type !== MenuTypeEnum.BUTTON" label="图标" prop="icon">
<!-- 图标选择器 -->
<icon-select v-model="formData.icon" />
<FaIconSelect v-model="formData.icon" />
</ElFormItem>
<ElFormItem
@@ -527,6 +527,7 @@ import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vu
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
import FaDrawer from "@/components/modal/fa-drawer/index.vue";
import FaIconSelect from "@/components/others/fa-icon-select/index.vue";
import { ElMessage, ElMessageBox, ElTag, ElTooltip } from "element-plus";
import { useAuth } from "@/hooks/core/useAuth";
import { renderTableOperationCell, type TableOperationAction } from "@utils/table";
@@ -820,20 +821,22 @@ function toggleExpand() {
});
}
function deleteMenuRow(id: number) {
ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await MenuAPI.deleteMenu([id]);
await userStore.getUserInfo();
ElMessage.success("删除成功");
selectedRows.value = [];
await loadMenuData();
})
.catch(() => {});
async function deleteMenuRow(id: number) {
try {
await ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await MenuAPI.deleteMenu([id]);
await userStore.getUserInfo();
ElMessage.success("删除成功");
selectedRows.value = [];
await loadMenuData();
} catch {
// 用户取消
}
}
const opCtx = {
@@ -1040,11 +1043,11 @@ async function handleOpenDialog(
Object.assign(detailFormData.value, response.data.data ?? {});
} else if (type === "update") {
dialogVisible.title = "修改菜单";
Object.assign(formData, response.data.data);
Object.assign(formData.value, response.data.data);
}
} else {
dialogVisible.title = "新增菜单";
Object.assign(formData, initialFormData);
formData.value = { ...initialFormData };
if (parentRow?.id != null) {
formData.value.parent_id = parentRow.id;
formData.value.client = (parentRow.client as MenuClientEnum) || menuClientTab.value;
@@ -1102,27 +1105,26 @@ async function handleSubmit() {
});
}
function handleBatchDelete() {
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await MenuAPI.deleteMenu(ids);
await userStore.getUserInfo();
ElMessage.success("删除成功");
selectedRows.value = [];
await loadMenuData();
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await MenuAPI.deleteMenu(ids);
await userStore.getUserInfo();
ElMessage.success("删除成功");
selectedRows.value = [];
await loadMenuData();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
async function handleMoreClick(status: string) {
@@ -1135,16 +1137,18 @@ async function handleMoreClick(status: string) {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
await MenuAPI.batchMenu({ ids, status });
await loadMenuData();
} catch (error: unknown) {
console.error(error);
}
})
.catch(() => {});
});
try {
await ElMessageBox.confirm("确认启用或停用该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await MenuAPI.batchMenu({ ids, status });
await loadMenuData();
} catch {
// 用户取消
}
}
onMounted(() => {
@@ -1153,7 +1157,7 @@ onMounted(() => {
</script>
<style scoped lang="scss">
::v-deep(.menu-table-actions .inline-flex) {
:deep(.menu-table-actions .inline-flex) {
vertical-align: middle;
}
</style>
@@ -637,20 +637,22 @@ async function handleSubmit() {
});
}
function deleteNoticeRow(id: number) {
ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await NoticeAPI.deleteNotice([id]);
await noticeStore.getNotice();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
})
.catch(() => {});
async function deleteNoticeRow(id: number) {
try {
await ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await NoticeAPI.deleteNotice([id]);
await noticeStore.getNotice();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
}
}
function buildNoticeRowActions(row: NoticeTable): TableOperationAction[] {
@@ -694,30 +696,29 @@ function formatNoticeOperationCell(row: NoticeTable) {
});
}
function handleBatchDelete() {
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await NoticeAPI.deleteNotice(ids);
await noticeStore.getNotice();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await NoticeAPI.deleteNotice(ids);
await noticeStore.getNotice();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
function handleMoreClick(status: string) {
async function handleMoreClick(status: string) {
const ids = selectedIds.value;
if (!ids.length) {
ElMessage.warning("请先选择要操作的数据");
@@ -727,13 +728,14 @@ function handleMoreClick(status: string) {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await NoticeAPI.batchNotice({ ids, status });
await refreshData();
await noticeStore.getNotice();
})
.catch(() => {});
});
try {
await NoticeAPI.batchNotice({ ids, status });
await refreshData();
await noticeStore.getNotice();
} catch {
// 用户取消
}
}
function openExportModal() {
@@ -747,11 +749,11 @@ onMounted(async () => {
<style scoped lang="scss">
/* FaForm 底部预留的操作栏列在弹窗内不需要占位 */
.crud-dialog-art-form ::v-deep(.el-row > .el-col:last-child) {
.crud-dialog-art-form :deep(.el-row > .el-col:last-child) {
display: none;
}
.crud-dialog-art-form ::v-deep(.el-form-item__content) {
.crud-dialog-art-form :deep(.el-form-item__content) {
max-width: 100%;
}
@@ -773,27 +775,27 @@ onMounted(async () => {
color: var(--el-text-color-placeholder);
}
.notice-html-preview ::v-deep(h1),
.notice-html-preview ::v-deep(h2),
.notice-html-preview ::v-deep(h3) {
.notice-html-preview :deep(h1),
.notice-html-preview :deep(h2),
.notice-html-preview :deep(h3) {
margin: 12px 0 8px;
}
.notice-html-preview ::v-deep(p) {
.notice-html-preview :deep(p) {
margin: 8px 0;
line-height: 1.6;
}
.notice-html-preview ::v-deep(table) {
.notice-html-preview :deep(table) {
margin: 12px 0;
}
.notice-html-preview ::v-deep(table th),
.notice-html-preview ::v-deep(table td) {
.notice-html-preview :deep(table th),
.notice-html-preview :deep(table td) {
padding: 8px 12px;
}
.notice-html-preview ::v-deep(pre) {
.notice-html-preview :deep(pre) {
padding: 12px;
margin: 12px 0;
overflow-x: auto;
@@ -801,14 +803,14 @@ onMounted(async () => {
border-radius: 4px;
}
.notice-html-preview ::v-deep(blockquote) {
.notice-html-preview :deep(blockquote) {
padding-left: 16px;
margin: 12px 0;
color: var(--el-text-color-regular);
border-left: 4px solid var(--el-color-primary);
}
.notice-html-preview ::v-deep(img) {
.notice-html-preview :deep(img) {
max-width: 100%;
height: auto;
}
@@ -1,3 +1,4 @@
<!-- 参数配置 -->
<template>
<FaDrawer
v-model="drawerVisible"
@@ -716,12 +717,12 @@ const logoConfigs = computed(() => ({
const handleUploadSuccess = (fileInfo: UploadFilePath, type: string) => {
const fileUrl = fileInfo.file_url;
if (type in configStore.configData) {
(configStore.configData as any)[type].config_value = fileUrl;
(configStore.configData as Record<string, ConfigTable>)[type].config_value = fileUrl;
}
if (type in systemConfigs.value) {
(systemConfigs.value as any)[type].config_value = fileUrl;
(systemConfigs.value as Record<string, { config_value: string }>)[type].config_value = fileUrl;
} else if (type in logoConfigs.value) {
(logoConfigs.value as any)[type].config_value = fileUrl;
(logoConfigs.value as Record<string, { config_value: string }>)[type].config_value = fileUrl;
}
markModified(type);
};
@@ -510,21 +510,23 @@ async function handleSubmit() {
});
}
function deleteParamRow(id: number) {
ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await ParamsAPI.deleteParams([id]);
configStore.isConfigLoaded = false;
await configStore.getConfig();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
})
.catch(() => {});
async function deleteParamRow(id: number) {
try {
await ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await ParamsAPI.deleteParams([id]);
configStore.isConfigLoaded = false;
await configStore.getConfig();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
}
}
function buildParamRowActions(row: ConfigTable): TableOperationAction[] {
@@ -568,28 +570,27 @@ function formatParamOperationCell(row: ConfigTable) {
});
}
function handleBatchDelete() {
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await ParamsAPI.deleteParams(ids);
configStore.isConfigLoaded = false;
await configStore.getConfig();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await ParamsAPI.deleteParams(ids);
configStore.isConfigLoaded = false;
await configStore.getConfig();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
function openExportModal() {
@@ -598,11 +599,11 @@ function openExportModal() {
</script>
<style scoped lang="scss">
.crud-dialog-art-form ::v-deep(.el-row > .el-col:last-child) {
.crud-dialog-art-form :deep(.el-row > .el-col:last-child) {
display: none;
}
.crud-dialog-art-form ::v-deep(.el-form-item__content) {
.crud-dialog-art-form :deep(.el-form-item__content) {
max-width: 100%;
}
</style>
@@ -403,20 +403,22 @@ function onTableSelectionChange(rows: PositionTable[]) {
selectedRows.value = rows;
}
function deletePositionRow(id: number) {
ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await PositionAPI.deletePosition([id]);
await userStore.getUserInfo();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
})
.catch(() => {});
async function deletePositionRow(id: number) {
try {
await ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await PositionAPI.deletePosition([id]);
await userStore.getUserInfo();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
}
}
const opCtx = {
@@ -680,63 +682,59 @@ async function handleSubmit() {
});
}
function handleBatchDelete() {
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await PositionAPI.deletePosition(ids);
await userStore.getUserInfo();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await PositionAPI.deletePosition(ids);
await userStore.getUserInfo();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
function handleMoreClick(status: string) {
async function handleMoreClick(status: string) {
const ids = selectedIds.value;
if (!ids.length) {
ElMessage.warning("请先选择要操作的数据");
return;
}
ElMessageBox.confirm(`确认${status === "0" ? "启用" : "停用"}该项数据?`, "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
await PositionAPI.batchPosition({ ids, status });
await refreshData();
await userStore.getUserInfo();
} catch (error: unknown) {
console.error(error);
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确认${status === "0" ? "启用" : "停用"}该项数据?`, "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await PositionAPI.batchPosition({ ids, status });
await refreshData();
await userStore.getUserInfo();
} catch {
// 用户取消
}
}
</script>
<style scoped lang="scss">
.crud-dialog-art-form ::v-deep(.el-row > .el-col:last-child) {
.crud-dialog-art-form :deep(.el-row > .el-col:last-child) {
display: none;
}
.crud-dialog-art-form ::v-deep(.el-form-item__content) {
.crud-dialog-art-form :deep(.el-form-item__content) {
max-width: 100%;
}
::v-deep(.position-table-actions .inline-flex) {
:deep(.position-table-actions .inline-flex) {
vertical-align: middle;
}
</style>
@@ -409,21 +409,22 @@ function handleOpenAssignPermDialog(roleId: number, roleName: string) {
drawerVisible.value = true;
}
function deleteRoleRow(id: number) {
ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await RoleAPI.deleteRole([id]);
const userStore = useUserStore();
await userStore.getUserInfo();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
})
.catch(() => {});
async function deleteRoleRow(id: number) {
try {
await ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await RoleAPI.deleteRole([id]);
const userStore = useUserStore();
await userStore.getUserInfo();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
}
}
const opCtx = {
@@ -716,48 +717,48 @@ async function handleSubmit() {
});
}
function handleBatchDelete() {
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await RoleAPI.deleteRole(ids);
const userStore = useUserStore();
await userStore.getUserInfo();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await RoleAPI.deleteRole(ids);
const userStore = useUserStore();
await userStore.getUserInfo();
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
function handleMoreClick(status: string) {
async function handleMoreClick(status: string) {
const ids = selectedIds.value;
if (!ids.length) {
ElMessage.warning("请先选择要操作的数据");
return;
}
ElMessageBox.confirm(`确认${status === "0" ? "启用" : "停用"}该项数据?`, "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await RoleAPI.batchRole({ ids, status });
await refreshData();
const userStore = useUserStore();
await userStore.getUserInfo();
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确认${status === "0" ? "启用" : "停用"}该项数据?`, "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await RoleAPI.batchRole({ ids, status });
await refreshData();
const userStore = useUserStore();
await userStore.getUserInfo();
} catch {
// 用户取消
}
}
function openExportModal() {
@@ -766,15 +767,15 @@ function openExportModal() {
</script>
<style scoped lang="scss">
.crud-dialog-art-form ::v-deep(.el-row > .el-col:last-child) {
.crud-dialog-art-form :deep(.el-row > .el-col:last-child) {
display: none;
}
.crud-dialog-art-form ::v-deep(.el-form-item__content) {
.crud-dialog-art-form :deep(.el-form-item__content) {
max-width: 100%;
}
::v-deep(.role-table-actions .inline-flex) {
:deep(.role-table-actions .inline-flex) {
vertical-align: middle;
}
</style>
@@ -364,19 +364,21 @@ function onTableSelectionChange(rows: TenantTable[]) {
selectedRows.value = rows;
}
function deleteTenantRow(id: number) {
ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await TenantAPI.deleteTenant([id]);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
})
.catch(() => {});
async function deleteTenantRow(id: number) {
try {
await ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await TenantAPI.deleteTenant([id]);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
}
}
const opCtx = {
@@ -648,39 +650,38 @@ async function handleSubmit() {
});
}
function handleBatchDelete() {
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await TenantAPI.deleteTenant(ids);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await TenantAPI.deleteTenant(ids);
ElMessage.success("删除成功");
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
</script>
<style scoped lang="scss">
.crud-dialog-art-form ::v-deep(.el-row > .el-col:last-child) {
.crud-dialog-art-form :deep(.el-row > .el-col:last-child) {
display: none;
}
.crud-dialog-art-form ::v-deep(.el-form-item__content) {
.crud-dialog-art-form :deep(.el-form-item__content) {
max-width: 100%;
}
::v-deep(.tenant-table-actions .inline-flex) {
:deep(.tenant-table-actions .inline-flex) {
vertical-align: middle;
}
</style>
@@ -152,15 +152,14 @@ const queryFormData = reactive<DeptPageQuery>({
const loading = ref(true);
onBeforeMount(() => {
onBeforeMount(async () => {
loading.value = true;
DeptAPI.listDept(queryFormData)
.then((response) => {
deptOptions.value = formatTree(response.data.data);
})
.finally(() => {
loading.value = false;
});
try {
const response = await DeptAPI.listDept(queryFormData);
deptOptions.value = formatTree(response.data.data);
} finally {
loading.value = false;
}
});
</script>
@@ -54,7 +54,7 @@ const selectConfig: ISelectConfig = {
],
},
],
indexAction(params: UserPageQuery) {
async indexAction(params: UserPageQuery) {
// 映射查询参数到后端接口
const query: any = { ...params };
// 清理空字符串/空值,避免后端 422
@@ -70,12 +70,11 @@ const selectConfig: ISelectConfig = {
else if (query.status === "false") query.status = false;
}
// 请求用户分页列表并适配 TableSelect 需要的结构
return UserAPI.listUser(query).then((res: any) => {
return {
total: res.data.data.total,
list: res.data.data.items,
};
});
const res = await UserAPI.listUser(query);
return {
total: res.data.data.total,
list: res.data.data.items,
};
},
tableColumns: [
{ type: "selection", width: 50, align: "center" },
@@ -545,41 +545,43 @@ function onTableSelectionChange(rows: UserInfo[]) {
selectedRows.value = rows;
}
function handleResetPassword(row: UserInfo) {
ElMessageBox.prompt(`请输入用户【${row.username ?? ""}】的新密码`, "重置密码", {
confirmButtonText: "确定",
cancelButtonText: "取消",
}).then(
async ({ value }) => {
if (!value || value.length < 6) {
ElMessage.warning("密码至少需要6位字符,请重新输入");
return;
}
await UserAPI.resetUserPassword({ id: row.id!, password: value });
ElMessage.success("密码已重置");
},
() => {}
);
async function handleResetPassword(row: UserInfo) {
try {
const { value } = await ElMessageBox.prompt(
`请输入用户【${row.username ?? ""}】的新密码`,
"重置密码",
{ confirmButtonText: "确定", cancelButtonText: "取消" }
);
if (!value || value.length < 6) {
ElMessage.warning("密码至少需要6位字符,请重新输入");
return;
}
await UserAPI.resetUserPassword({ id: row.id!, password: value });
ElMessage.success("密码已重置");
} catch {
// 用户取消
}
}
function deleteUserRow(id: number) {
ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await UserAPI.deleteUser([id]);
const idSet = [id];
if (userStore.basicInfo.id && idSet.includes(userStore.basicInfo.id)) {
userStore.clearUserInfo();
} else {
ElMessage.success("删除成功");
}
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
})
.catch(() => {});
async function deleteUserRow(id: number) {
try {
await ElMessageBox.confirm("确认删除该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
await UserAPI.deleteUser([id]);
const idSet = [id];
if (userStore.basicInfo.id && idSet.includes(userStore.basicInfo.id)) {
userStore.clearUserInfo();
} else {
ElMessage.success("删除成功");
}
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
}
}
const opCtx = {
@@ -708,7 +710,7 @@ const exportQueryParams = computed(() => {
const userImportContentConfig = computed<IContentConfig>(() => ({
permPrefix: "module_system:user",
cols: userCrudCols.value,
indexAction: async () => ({}) as any,
indexAction: async () => ({}),
importTemplate: () => UserAPI.downloadTemplateUser(),
}));
@@ -839,25 +841,23 @@ function openExportModal() {
exportModalVisible.value = true;
}
function handleImportUpload(formDataUpload: FormData) {
async function handleImportUpload(formDataUpload: FormData) {
uploadLoading.value = true;
UserAPI.importUser(formDataUpload)
.then(async (response) => {
if (response.data.code === ResultEnum.SUCCESS) {
ElMessage.success(`${response.data.msg},${response.data.data}`);
importModalVisible.value = false;
await refreshData();
} else {
ElMessage.error(response.data.msg || "导入失败");
}
})
.catch((error: unknown) => {
console.error(error);
ElMessage.error("上传失败");
})
.finally(() => {
uploadLoading.value = false;
});
try {
const response = await UserAPI.importUser(formDataUpload);
if (response.data.code === ResultEnum.SUCCESS) {
ElMessage.success(`${response.data.msg},${response.data.data}`);
importModalVisible.value = false;
await refreshData();
} else {
ElMessage.error(response.data.msg || "导入失败");
}
} catch (error: unknown) {
console.error(error);
ElMessage.error("上传失败");
} finally {
uploadLoading.value = false;
}
}
async function resetForm() {
@@ -950,61 +950,57 @@ async function handleSubmit() {
});
}
function handleBatchDelete() {
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await UserAPI.deleteUser(ids);
if (userStore.basicInfo.id && ids.includes(userStore.basicInfo.id)) {
userStore.clearUserInfo();
} else {
ElMessage.success("删除成功");
}
selectedRows.value = [];
await refreshRemove();
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 条数据吗?`, "批量删除", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await UserAPI.deleteUser(ids);
if (userStore.basicInfo.id && ids.includes(userStore.basicInfo.id)) {
userStore.clearUserInfo();
} else {
ElMessage.success("删除成功");
}
selectedRows.value = [];
await refreshRemove();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
function handleMoreClick(status: string) {
async function handleMoreClick(status: string) {
const ids = selectedIds.value;
if (!ids.length) {
ElMessage.warning("请先选择要操作的数据");
return;
}
ElMessageBox.confirm("确认启用或停用该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
batchDeleting.value = true;
await UserAPI.batchUser({ ids, status });
await refreshData();
} catch (error: unknown) {
console.error(error);
} finally {
batchDeleting.value = false;
}
})
.catch(() => {});
try {
await ElMessageBox.confirm("确认启用或停用该项数据?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
batchDeleting.value = true;
await UserAPI.batchUser({ ids, status });
await refreshData();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
</script>
<style lang="scss" scoped>
/* 左侧部门树内容区:card body 纵向 flex + 滚动条占满剩余高度(布局在 index,内边距在 DeptTree) */
.tree-card ::v-deep(.el-card__body) {
.tree-card :deep(.el-card__body) {
display: flex;
flex: 1;
flex-direction: column;