chore: cleanup old frontend code and update project configs

This commit removes the deprecated frontend/web-old directory, updates various project configuration files including tsconfig, vite config, environment variables, and backend data models. It also fixes several API paths, adds tenant awareness to multiple models, and makes minor UI adjustments.
This commit is contained in:
zhangtao
2026-05-27 01:12:03 +08:00
parent eb18d85327
commit 53d2a9d13e
581 changed files with 14751 additions and 55305 deletions
@@ -1,35 +1,75 @@
<template>
<div v-loading="loading" class="min-h-0">
<ElEmpty
v-if="!loading && isEmpty"
:image-size="emptyImageSize"
:description="emptyDescription"
class="py-8"
/>
<div v-else class="min-h-0" :class="gridClass">
<slot />
<div class="fa-card-list">
<slot name="header" />
<div v-loading="loading">
<ElEmpty v-if="!loading && items.length === 0" :description="emptyText" />
<div v-else :class="gridClass">
<div
v-for="(item, index) in items"
:key="item[keyField] ?? index"
class="fa-card-item border border-g-300/60 dark:border-g-700 rounded-xl overflow-hidden bg-white dark:bg-g-800 hover:shadow-lg transition-all duration-300"
@click="$emit('itemClick', item)"
>
<slot name="card" :item="item" :index="index" />
</div>
</div>
<div v-if="total > pageSize" class="flex justify-center mt-6">
<ElPagination
:current-page="currentPage"
:total="total"
:page-size="pageSize"
@current-change="$emit('pageChange', $event)"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue";
defineOptions({ name: "FaCardList" });
interface FaCardListProps {
loading?: boolean;
isEmpty?: boolean;
emptyDescription?: string;
emptyImageSize?: number;
/** Tailwind/Grid class string for layout */
gridClass?: string;
}
const props = withDefaults(
defineProps<{
items: any[];
total?: number;
pageSize?: number;
currentPage?: number;
loading?: boolean;
columns?: number;
keyField?: string;
emptyText?: string;
}>(),
{
total: 0,
pageSize: 12,
currentPage: 1,
loading: false,
columns: 4,
keyField: "id",
emptyText: "暂无数据",
}
);
withDefaults(defineProps<FaCardListProps>(), {
loading: false,
isEmpty: false,
emptyDescription: "暂无数据",
emptyImageSize: 80,
gridClass:
"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 auto-rows-max content-start",
defineEmits<{
pageChange: [page: number];
itemClick: [item: any];
}>();
const gridClass = computed(() => {
const cols = {
2: "grid grid-cols-2 gap-4 max-lg:grid-cols-1",
3: "grid grid-cols-3 gap-4 max-xl:grid-cols-2 max-lg:grid-cols-1",
4: "grid grid-cols-4 gap-4 max-2xl:grid-cols-3 max-xl:grid-cols-2 max-lg:grid-cols-1",
5: "grid grid-cols-5 gap-5 max-2xl:grid-cols-4 max-xl:grid-cols-3 max-lg:grid-cols-2 max-sm:grid-cols-1",
};
return cols[props.columns as keyof typeof cols] || cols[4];
});
</script>
<style scoped>
.fa-card-item {
cursor: pointer;
}
</style>
@@ -25,12 +25,13 @@ const chartInstance = shallowRef<echarts.ECharts | null>(null);
const settingStore = useSettingsStore();
const { isDark } = storeToRefs(settingStore);
const props = withDefaults(defineProps<MapChartProps>(), {
const props = withDefaults(defineProps<MapChartProps & { dynamic?: boolean }>(), {
mapData: () => [],
selectedRegion: "",
showLabels: true,
showScatter: true,
isEmpty: false,
dynamic: false,
});
// 定义 emit
@@ -242,6 +243,7 @@ const resizeChart = () => {
};
let initTimer: ReturnType<typeof setTimeout> | null = null;
let dynamicTimer: ReturnType<typeof setInterval> | null = null;
// 处理组件销毁
const cleanupChart = () => {
@@ -249,6 +251,10 @@ const cleanupChart = () => {
clearTimeout(initTimer);
initTimer = null;
}
if (dynamicTimer !== null) {
clearInterval(dynamicTimer);
dynamicTimer = null;
}
if (chartInstance.value) {
chartInstance.value.off("click", handleMapClick);
chartInstance.value.dispose();
@@ -257,12 +263,29 @@ const cleanupChart = () => {
window.removeEventListener("resize", resizeChart);
};
const updateDynamicData = () => {
if (!chartInstance.value || chartInstance.value.isDisposed()) return;
const mapData = prepareMapData(chinaMapJson);
const scatterData = [
{ name: "北京", value: [116.405285, 39.904989, Math.round(50 + Math.random() * 200)] },
{ name: "上海", value: [121.472644, 31.231706, Math.round(50 + Math.random() * 200)] },
{ name: "深圳", value: [114.085947, 22.547, Math.round(50 + Math.random() * 200)] },
{ name: "成都", value: [104.065735, 30.659462, Math.round(30 + Math.random() * 150)] },
{ name: "武汉", value: [114.298572, 30.584355, Math.round(30 + Math.random() * 150)] },
{ name: "杭州", value: [120.153576, 30.287459, Math.round(30 + Math.random() * 150)] },
];
chartInstance.value.setOption({ series: [{ data: mapData }, { data: scatterData }] });
};
// 生命周期钩子
onMounted(() => {
if (!isEmpty.value) {
(async () => {
await initMap();
initTimer = setTimeout(resizeChart, 100);
if (props.dynamic) {
dynamicTimer = setInterval(updateDynamicData, 4000);
}
})();
}
window.addEventListener("resize", resizeChart);
@@ -81,7 +81,7 @@ const DEFAULT_UPLOAD_CONFIG = {
// 计算属性:上传服务器地址
const uploadServer = computed(
() => props.uploadConfig?.server || `${VITE_API_URL}/common/upload/wangeditor`
() => props.uploadConfig?.server || `${VITE_API_URL}/common/file/upload`
);
// 合并上传配置
@@ -205,14 +205,14 @@ const userStore = useUserStore();
const menuStore = useMenuStore();
const configStore = useConfigStore();
/** 与侧栏一致:参数配置 sys_web_logo / sys_web_title */
/** 租户配置:tenant_logo / tenant_name */
const headerLogoSrc = computed(() => {
const raw = configStore.configData.sys_web_logo?.config_value;
const raw = configStore.configData.tenant_logo?.config_value;
return typeof raw === "string" && raw.trim() ? raw.trim() : undefined;
});
const headerSystemName = computed(() => {
const raw = configStore.configData.sys_web_title?.config_value;
const raw = configStore.configData.tenant_name?.config_value;
if (typeof raw === "string" && raw.trim()) return raw.trim();
return AppConfig.systemInfo.name;
});
@@ -8,85 +8,6 @@
@close="onDrawerClosed"
>
<ElTabs v-model="activeTabRef" type="border-card">
<!-- 网站配置 -->
<ElTabPane label="网站配置" name="website">
<ElForm :model="configState" label-suffix=":" label-width="100px" label-position="right">
<!-- 系统配置 -->
<ElDivider>网站配置</ElDivider>
<div v-for="(item, key) in systemConfigs" :key="key">
<ElFormItem :label="item.config_name">
<span class="flex items-center gap-2 w-full">
<ElInput
v-model="item.config_value"
:placeholder="t('common.inputText')"
clearable
:style="'width: 100%'"
@input="markModified(key)"
/>
</span>
</ElFormItem>
</div>
<!-- logo配置 -->
<ElDivider>网站图标</ElDivider>
<div v-for="(item, key) in logoConfigs" :key="key">
<ElFormItem :label="item.config_name">
<div class="flex items-center gap-2 w-full">
<FaUpload
v-model="item.config_value"
:data="{ type: key }"
:name="'file'"
:max-file-size="item.maxFileSize"
:show-tip="true"
:enable-preview="true"
:enable-crop="true"
v-bind="logoCropBind(String(key))"
@success="(fileInfo: UploadFilePath) => handleUploadSuccess(fileInfo, key)"
@error="handleUploadError"
/>
</div>
</ElFormItem>
</div>
</ElForm>
</ElTabPane>
<ElTabPane label="安全隐私" name="securityPrivacy">
<ElForm :model="configState" label-suffix=":" label-width="100px" label-position="right">
<!-- 系统配置 -->
<ElDivider>安全隐私</ElDivider>
<div v-for="(item, key) in securityPrivacyConfigs" :key="key">
<ElFormItem :label="item.config_name">
<span class="flex items-center gap-2 w-full">
<ElInput
v-model="item.config_value"
:placeholder="t('common.inputText')"
clearable
:style="'width: 100%'"
@input="markModified(key)"
/>
</span>
</ElFormItem>
</div>
</ElForm>
</ElTabPane>
<ElTabPane label="用户协议" name="userAgreement">
<!-- 系统配置 -->
<ElForm :model="configState" label-suffix=":" label-width="100px" label-position="right">
<ElDivider>用户协议</ElDivider>
<div v-for="(item, key) in userAgreementConfigs" :key="key">
<ElFormItem :label="item.config_name">
<span class="flex items-center gap-2 w-full">
<ElInput
v-model="item.config_value"
:placeholder="t('common.inputText')"
clearable
:style="'width: 100%'"
@input="markModified(key)"
/>
</span>
</ElFormItem>
</div>
</ElForm>
</ElTabPane>
<ElTabPane label="接口白名单" name="apiWhitelist">
<ElForm :model="configState" label-suffix=":" label-width="100px" label-position="right">
<!-- 系统配置 -->
@@ -325,45 +246,7 @@ const drawerSize = computed(() => (appStore.device === DeviceEnum.DESKTOP ? "60%
const t = useI18n().t;
const configStore = useConfigStore();
/** 网站图标裁剪:favicon 方形、Logo 横版、登录背景 16:9 */
function logoCropBind(key: string) {
switch (key) {
case "sys_web_favicon":
return {
cropCutWidth: 64,
cropCutHeight: 64,
cropBoxWidth: 380,
cropBoxHeight: 320,
cropDialogTitle: "裁剪网站图标",
cropInnerTitle: "调整图标",
cropPreviewTitle: "预览",
};
case "sys_web_logo":
return {
cropCutWidth: 320,
cropCutHeight: 96,
cropBoxWidth: 520,
cropBoxHeight: 360,
cropDialogTitle: "裁剪站点 Logo",
cropInnerTitle: "调整 Logo",
cropPreviewTitle: "预览",
};
case "sys_login_background":
return {
cropCutWidth: 960,
cropCutHeight: 540,
cropBoxWidth: 560,
cropBoxHeight: 380,
cropDialogTitle: "裁剪登录背景",
cropInnerTitle: "调整背景图",
cropPreviewTitle: "预览",
cropFileType: "jpeg" as const,
};
default:
return {};
}
}
const activeTabRef = ref("website");
const activeTabRef = ref("apiWhitelist");
// 与父组件的 v-model 同步
const props = defineProps<{ modelValue: boolean }>();
@@ -443,17 +326,12 @@ const submitChanges = async () => {
});
}
// 4. 处理其他配置项
// 4. 处理其他配置项(已迁移到租户管理的配置不再处理)
const otherKeys = keysToSubmit.filter(
(key) => !["white_api_list_path", "ip_black_list", "ip_white_list"].includes(key)
);
const otherUpdatePromises = otherKeys.map((key) => {
const item =
systemConfigs.value[key as keyof typeof systemConfigs.value] ||
logoConfigs.value[key as keyof typeof logoConfigs.value] ||
securityPrivacyConfigs.value[key as keyof typeof securityPrivacyConfigs.value] ||
userAgreementConfigs.value[key as keyof typeof userAgreementConfigs.value] ||
demoConfigs.value[key as keyof typeof demoConfigs.value];
const item = demoConfigs.value[key as keyof typeof demoConfigs.value];
return item && item.id ? ParamsAPI.updateParams(item.id, { ...item }) : Promise.resolve();
});
await Promise.all(otherUpdatePromises);
@@ -483,24 +361,9 @@ const resetForm = async () => {
// 重置其他配置项
const keysToReset = Object.keys(modifiedFields);
for (const key of keysToReset) {
if (systemConfigs.value[key as keyof typeof systemConfigs.value]) {
systemConfigs.value[key as keyof typeof systemConfigs.value].config_value =
if (key !== "ip_white_list" && configStore.configData[key as keyof typeof configStore.configData]) {
(configStore.configData as Record<string, ConfigTable>)[key as string].config_value =
configStore.configData[key as keyof typeof configStore.configData]?.config_value || "";
} else if (logoConfigs.value[key as keyof typeof logoConfigs.value]) {
logoConfigs.value[key as keyof typeof logoConfigs.value].config_value =
configStore.configData[key as keyof typeof configStore.configData]?.config_value || "";
} else if (securityPrivacyConfigs.value[key as keyof typeof securityPrivacyConfigs.value]) {
securityPrivacyConfigs.value[key as keyof typeof securityPrivacyConfigs.value].config_value =
configStore.configData[key as keyof typeof configStore.configData]?.config_value || "";
} else if (userAgreementConfigs.value[key as keyof typeof userAgreementConfigs.value]) {
userAgreementConfigs.value[key as keyof typeof userAgreementConfigs.value].config_value =
configStore.configData[key as keyof typeof configStore.configData]?.config_value || "";
} else if (demoConfigs.value[key as keyof typeof demoConfigs.value]) {
// 除了IP白名单外的演示配置项
if (key !== "ip_white_list") {
demoConfigs.value[key as keyof typeof demoConfigs.value].config_value =
configStore.configData[key as keyof typeof configStore.configData]?.config_value || "";
}
}
delete modifiedFields[key];
}
@@ -517,27 +380,6 @@ async function onDrawerClosed() {
await resetForm();
}
// 系统配置项
const systemConfigs = computed(() => ({
sys_web_title: configStore.configData.sys_web_title,
sys_web_version: configStore.configData.sys_web_version,
sys_web_description: configStore.configData.sys_web_description,
}));
// 安全隐私配置项
const securityPrivacyConfigs = computed(() => ({
sys_help_doc: configStore.configData.sys_help_doc,
sys_git_code: configStore.configData.sys_git_code,
sys_keep_record: configStore.configData.sys_keep_record,
sys_web_copyright: configStore.configData.sys_web_copyright,
sys_web_privacy: configStore.configData.sys_web_privacy,
}));
// 用户协议配置项
const userAgreementConfigs = computed(() => ({
sys_web_clause: configStore.configData.sys_web_clause,
}));
// 接口白名单配置 - 动态管理
const apiWhitelistItems = ref<ListItem[]>([]);
// IP黑名单配置 - 动态管理
@@ -696,42 +538,6 @@ const demoConfigs = computed(() => ({
ip_white_list: configStore.configData.ip_white_list,
}));
// logo配置项
const logoConfigs = computed(() => ({
sys_web_logo: {
...configStore.configData.sys_web_logo,
maxFileSize: 5,
},
sys_web_favicon: {
...configStore.configData.sys_web_favicon,
maxFileSize: 5,
},
sys_login_background: {
...configStore.configData.sys_login_background,
maxFileSize: 10,
},
}));
// 图片上传成功的回调处理
const handleUploadSuccess = (fileInfo: UploadFilePath, type: string) => {
const fileUrl = fileInfo.file_url;
if (type in configStore.configData) {
(configStore.configData as Record<string, ConfigTable>)[type].config_value = fileUrl;
}
if (type in systemConfigs.value) {
(systemConfigs.value as Record<string, { config_value: string }>)[type].config_value = fileUrl;
} else if (type in logoConfigs.value) {
(logoConfigs.value as Record<string, { config_value: string }>)[type].config_value = fileUrl;
}
markModified(type);
};
// 图片上传失败的回调处理
const handleUploadError = (error: any) => {
console.error("上传失败:", error.message || "未知错误");
ElMessage.error(`上传失败:${error.message || "请稍后重试"}`);
};
onMounted(() => {
initializeLists();
configStore.getConfig(true);
@@ -56,6 +56,37 @@
</div>
</div>
<ul class="py-4 mt-3 border-t border-g-300/80">
<li
v-if="tenantList.length > 1"
class="btn-item flex-col! items-start! mb-4 p-2! bg-(--fa-gray-100) rounded-lg"
>
<span class="text-xs text-g-500 mb-2 block w-full">当前租户</span>
<ElDropdown trigger="click" @command="handleTenantSwitch">
<span
class="flex-c c-p w-full text-sm font-medium text-(--el-color-primary) hover:underline"
>
{{ currentTenantName }}
<FaSvgIcon icon="ri:arrow-down-s-line" class="ml-1 text-xs" />
</span>
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem
v-for="t in tenantList"
:key="t.id"
:command="t.id"
:class="{
'text-(--el-color-primary) font-medium': t.id === currentTenant?.id,
}"
>
<span class="flex-c justify-between gap-4">
<span>{{ t.name }}</span>
<FaSvgIcon v-if="t.id === currentTenant?.id" icon="ri:check-line" />
</span>
</ElDropdownItem>
</ElDropdownMenu>
</template>
</ElDropdown>
</li>
<li class="btn-item" @click="goPage('/fastlink/profile')">
<FaSvgIcon icon="ri:user-3-line" />
<span>{{ $t("topBar.user.userCenter") }}</span>
@@ -103,6 +134,7 @@ const { t } = useI18n();
const userStore = useUserStore();
const { info: userInfo } = storeToRefs(userStore);
const { tenantList, currentTenant } = storeToRefs(userStore);
const userMenuPopover = ref();
const paramDrawerVisible = ref(false);
@@ -120,6 +152,22 @@ const displayName = computed(
const displayEmail = computed(() => (userInfo.value as { email?: string })?.email || "");
const currentTenantName = computed(
() => currentTenant.value?.name || tenantList.value[0]?.name || "—"
);
async function handleTenantSwitch(tenantId: number) {
closeUserMenu();
try {
await userStore.selectTenant(tenantId);
setTimeout(() => {
window.location.reload();
}, 200);
} catch {
// silently fail
}
}
function openParamConfig(): void {
closeUserMenu();
paramDrawerVisible.value = true;
@@ -149,14 +149,14 @@ const router = useRouter();
const settingStore = useSettingsStore();
const configStore = useConfigStore();
/** 与旧版 layouts/old/components/AppLogo 一致:参数 sys_web_logo / sys_web_title */
/** 租户配置:tenant_logo / tenant_name */
const sidebarLogoSrc = computed(() => {
const raw = configStore.configData.sys_web_logo?.config_value;
const raw = configStore.configData.tenant_logo?.config_value;
return typeof raw === "string" && raw.trim() ? raw.trim() : undefined;
});
const sidebarTitle = computed(() => {
const raw = configStore.configData.sys_web_title?.config_value;
const raw = configStore.configData.tenant_name?.config_value;
if (typeof raw === "string" && raw.trim()) return raw.trim();
return AppConfig.systemInfo.name;
});
@@ -57,11 +57,11 @@
:key="index"
class="box-border flex-c px-3.5 py-3.5 c-p last:border-b-0 hover:bg-g-200/60"
>
<div class="w-9 h-9">
<img :src="item.avatar" class="w-full h-full rounded-lg" loading="eager" />
<div class="size-9 leading-9 text-center rounded-lg flex-cc bg-info/12 text-info">
<FaSvgIcon class="text-lg bg-transparent!" icon="ri:message-3-line" />
</div>
<div class="w-[calc(100%-45px)] ml-3.5">
<h4 class="text-xs font-normal leading-5.5">{{ item.title }}</h4>
<h4 class="text-sm font-normal leading-5.5 text-g-900">{{ item.title }}</h4>
<p class="mt-1.5 text-xs text-g-500">{{ item.time }}</p>
</div>
</li>
@@ -103,16 +103,13 @@
</template>
<script setup lang="ts">
import { computed, ref, watch, type Ref, type ComputedRef } from "vue";
import { computed, ref, watch, onMounted, type Ref, type ComputedRef } from "vue";
import { useI18n } from "vue-i18n";
// 导入头像图片
import avatar1 from "@imgs/avatar/avatar1.webp";
import avatar2 from "@imgs/avatar/avatar2.webp";
import avatar3 from "@imgs/avatar/avatar3.webp";
import avatar4 from "@imgs/avatar/avatar4.webp";
import avatar5 from "@imgs/avatar/avatar5.webp";
import avatar6 from "@imgs/avatar/avatar6.webp";
import { useRouter } from "vue-router";
import NoticeAPI, {
type NoticeTable,
type NotificationPanelMessage,
} from "@/api/module_system/notice";
defineOptions({ name: "FaNotification" });
@@ -130,8 +127,8 @@ interface MessageItem {
title: string;
/** 时间 */
time: string;
/** 头像 */
avatar: string;
/** 内容 */
content?: string;
}
interface PendingItem {
@@ -172,77 +169,53 @@ const visible = ref(false);
const barActiveIndex = ref(0);
const useNotificationData = () => {
// 通知数据
const noticeList = ref<NoticeItem[]>([
{
title: "新增国际化",
time: "2024-6-13 0:10",
type: "notice",
},
{
title: "冷月呆呆给你发了一条消息",
time: "2024-4-21 8:05",
type: "message",
},
{
title: "小肥猪关注了你",
time: "2020-3-17 21:12",
type: "collection",
},
{
title: "新增使用文档",
time: "2024-02-14 0:20",
type: "notice",
},
{
title: "小肥猪给你发了一封邮件",
time: "2024-1-20 0:15",
type: "email",
},
{
title: "菜单mock本地真实数据",
time: "2024-1-17 22:06",
type: "notice",
},
]);
// 消息数据
const msgList = ref<MessageItem[]>([
{
title: "池不胖 关注了你",
time: "2021-2-26 23:50",
avatar: avatar1,
},
{
title: "唐不苦 关注了你",
time: "2021-2-21 8:05",
avatar: avatar2,
},
{
title: "中小鱼 关注了你",
time: "2020-1-17 21:12",
avatar: avatar3,
},
{
title: "何小荷 关注了你",
time: "2021-01-14 0:20",
avatar: avatar4,
},
{
title: "誶誶淰 关注了你",
time: "2020-12-20 0:15",
avatar: avatar5,
},
{
title: "冷月呆呆 关注了你",
time: "2020-12-17 22:06",
avatar: avatar6,
},
]);
// 待办数据
const panelLoading = ref(false);
const noticeList = ref<NoticeItem[]>([]);
const msgList = ref<MessageItem[]>([]);
const pendingList = ref<PendingItem[]>([]);
const fetchPanel = async () => {
panelLoading.value = true;
try {
const res = await NoticeAPI.getNotificationPanel();
const data = (res.data as any)?.data ?? res.data ?? {};
// 通知
const notices: any[] = data.notices || [];
noticeList.value = notices.map((n: NoticeTable) => ({
title: n.notice_title || "",
time: n.created_time || "",
type: "notice" as NoticeType,
}));
// 消息
const messages: NotificationPanelMessage[] = data.messages || [];
msgList.value = messages.map((m) => ({
title: m.title || "",
time: m.time || "",
content: m.content || "",
}));
// 待办
const pendings: NotificationPanelMessage[] = data.pendings || [];
pendingList.value = pendings.map((p) => ({
title: p.title || "",
time: p.time || "",
}));
} catch {
noticeList.value = [];
msgList.value = [];
pendingList.value = [];
} finally {
panelLoading.value = false;
}
};
onMounted(() => fetchPanel());
watch(visible, (v) => {
if (v) fetchPanel();
});
// 标签栏数据
const barList = computed<BarItem[]>(() => [
{
@@ -375,17 +348,16 @@ const useTabManagement = (
// 业务逻辑处理
const useBusinessLogic = () => {
const handleNoticeAll = () => {
// TODO: 实现查看全部通知逻辑
console.info("[TODO] 查看全部通知");
// 跳转到公告管理页面
const router = useRouter();
router.push("/module_system/notice");
};
const handleMsgAll = () => {
// TODO: 实现查看全部消息逻辑
console.info("[TODO] 查看全部消息");
};
const handlePendingAll = () => {
// TODO: 实现查看全部待办逻辑
console.info("[TODO] 查看全部待办");
};
@@ -12,7 +12,7 @@
<!-- 普通菜单项 -->
<li
v-if="!item.children"
class="menu-item relative flex-c c-p select-none rounded text-xs transition-colors duration-150 hover:bg-g-200"
class="menu-item relative flex-c c-p select-none rounded text-xs transition-colors duration-150 hover:bg-(--el-color-primary-light-9)"
:class="{ 'is-disabled': item.disabled, 'has-line': item.showLine }"
:style="menuItemStyle"
@click="handleMenuClick(item)"
@@ -32,7 +32,7 @@
<!-- 子菜单 -->
<li
v-else
class="menu-item submenu relative flex-c c-p select-none rounded text-xs transition-colors duration-150 hover:bg-g-200"
class="menu-item submenu relative flex-c c-p select-none rounded text-xs transition-colors duration-150 hover:bg-(--el-color-primary-light-9)"
:style="menuItemStyle"
>
<div class="submenu-title flex-c w-full">
@@ -58,7 +58,7 @@
<li
v-for="child in item.children"
:key="child.key"
class="menu-item relative mx-1.5 flex-c c-p select-none rounded text-xs transition-colors duration-150 hover:bg-g-200"
class="menu-item relative mx-1.5 flex-c c-p select-none rounded text-xs transition-colors duration-150 hover:bg-(--el-color-primary-light-9)"
:class="{ 'is-disabled': child.disabled, 'has-line': child.showLine }"
:style="menuItemStyle"
@click="handleMenuClick(child)"
@@ -49,7 +49,7 @@
<slot v-if="$slots.default" />
<template #empty>
<div v-if="loading"></div>
<ElEmpty v-else :description="emptyText" :image-size="120" />
<ElEmpty v-else :description="emptyText" :image-size="80" />
</template>
</ElTable>
</VueDraggable>
@@ -164,15 +164,15 @@ const mainColors = AppConfig.systemMainColor;
const themeColorForCss = computed(() => systemThemeColor.value);
const webLogoSrc = computed(
() => configStore.configData.sys_web_logo?.config_value?.trim() || undefined
() => configStore.configData.tenant_logo?.config_value?.trim() || undefined
);
const siteTitle = computed(
() => configStore.configData.sys_web_title?.config_value?.trim() || AppConfig.systemInfo.name
() => configStore.configData.tenant_name?.config_value?.trim() || AppConfig.systemInfo.name
);
const displayVersion = computed(() => {
const raw = configStore.configData.sys_web_version?.config_value?.trim();
const raw = configStore.configData.tenant_version?.config_value?.trim();
const ver = raw || DEFAULT_APP_VERSION;
return ver.startsWith("v") || ver.startsWith("V") ? ver : `v${ver}`;
});
@@ -101,18 +101,18 @@ defineProps<{
const configStore = useConfigStore();
/** 接口 sys_web_logo,空则 FaLogo 内置默认图 */
/** 接口 tenant_logo,空则 FaLogo 内置默认图 */
const webLogoSrc = computed(
() => configStore.configData.sys_web_logo?.config_value?.trim() || undefined
() => configStore.configData.tenant_logo?.config_value?.trim() || undefined
);
const siteTitle = computed(
() => configStore.configData.sys_web_title?.config_value?.trim() || AppConfig.systemInfo.name
() => configStore.configData.tenant_name?.config_value?.trim() || AppConfig.systemInfo.name
);
const DEFAULT_APP_VERSION = "2.0.0";
const DEFAULT_APP_VERSION = "3.0.0";
const displayVersion = computed(() => {
const raw = configStore.configData.sys_web_version?.config_value?.trim();
const raw = configStore.configData.tenant_version?.config_value?.trim();
const ver = raw || DEFAULT_APP_VERSION;
return ver.startsWith("v") || ver.startsWith("V") ? ver : `v${ver}`;
});