This commit is contained in:
2026-09-16 10:37:08 +08:00
32 changed files with 3475 additions and 72 deletions
+138 -8
View File
@@ -75,7 +75,10 @@
@click="handleNavigate(module)"
>
<div class="card-header">
<span class="card-title">{{ module.name }}</span>
<span class="card-title">
{{ module.name }}
<el-icon v-if="module.locked" class="lock-badge"><Lock /></el-icon>
</span>
<div class="card-icon-wrapper" v-html="module.icon"></div>
</div>
<div class="card-divider"></div>
@@ -117,7 +120,10 @@
@click="handleNavigate(module)"
>
<div class="card-header">
<span class="card-title">{{ module.name }}</span>
<span class="card-title">
{{ module.name }}
<el-icon v-if="module.locked" class="lock-badge"><Lock /></el-icon>
</span>
<div class="card-icon-wrapper" v-html="module.icon"></div>
</div>
<div class="card-divider"></div>
@@ -159,7 +165,10 @@
@click="handleNavigate(module)"
>
<div class="card-header">
<span class="card-title">{{ module.name }}</span>
<span class="card-title">
{{ module.name }}
<el-icon v-if="module.locked" class="lock-badge"><Lock /></el-icon>
</span>
<div class="card-icon-wrapper" v-html="module.icon"></div>
</div>
<div class="card-divider"></div>
@@ -206,7 +215,7 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import { ElMessage, ElMessageBox } from "element-plus";
import {
ArrowDown,
@@ -218,6 +227,7 @@ import {
Moon,
OfficeBuilding,
Lock,
Lock,
} from "@element-plus/icons-vue";
import { getTenantList } from "@/api/modules";
@@ -243,15 +253,22 @@ const router = useRouter();
const authStore = useAuthStore();
const menuStore = useMenuStore();
// 已开通模块排在前面,套餐未开通(锁定)的模块沉到后方
function sortUnlockedFirst(list: ModuleItem[]): ModuleItem[] {
return [...list].sort(
(a, b) => Number(!!a.locked) - Number(!!b.locked)
);
}
// 根据type分组模块
const basicModules = computed(() =>
moduleList.value.filter((item) => item.type === 1)
sortUnlockedFirst(moduleList.value.filter((item) => item.type === 1))
);
const systemModules = computed(() =>
moduleList.value.filter((item) => item.type === 2)
sortUnlockedFirst(moduleList.value.filter((item) => item.type === 2))
);
const uncategorizedModules = computed(() =>
moduleList.value.filter((item) => item.type === 0)
sortUnlockedFirst(moduleList.value.filter((item) => item.type === 0))
);
const moduleList = ref<ModuleItem[]>([]);
@@ -327,6 +344,25 @@ const fetchUserProfile = async () => {
// 当前租户(企业)名称,来自后端 /backend/getCurrentUser 或登录接口返回的 tenant_name
const tenantName = computed(() => authStore.user?.tenant_name || "");
// 当前租户生效套餐名称(用于未开通提示)
const packageName = ref("");
// 套餐购买链接(平台端配置,为空时点击「购买」仅提示联系管理员)
const purchaseUrl = ref("");
// 套餐未开通模块:不跳转,提示购买套餐后使用
function showLockedTip(module: ModuleItem) {
const pkg = packageName.value ? `「${packageName.value}」` : "当前";
ElMessageBox.alert(
`${pkg}套餐未包含「${module.name}」功能模块,请购买或升级套餐后使用。如有疑问请联系平台管理员。`,
"功能未开通",
{
confirmButtonText: "我知道了",
type: "warning",
customClass: "module-locked-tip"
}
).catch(() => {});
}
// 处理导航跳转
function handleNavigate(module: ModuleItem) {
if (module.enabled === false) {
@@ -657,7 +693,7 @@ onMounted(() => {
}
.module-card {
height: 180px;
min-height: 180px;
background: #fff;
border-radius: 12px;
padding: 30px;
@@ -683,9 +719,17 @@ onMounted(() => {
}
.card-title {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 18px;
font-weight: 600;
color: @text-main;
.lock-badge {
font-size: 14px;
color: #c0c4cc;
}
}
}
@@ -708,6 +752,79 @@ onMounted(() => {
}
}
.card-lock-tip {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-top: 12px;
font-size: 12px;
color: #c0c4cc;
.buy-btn {
flex-shrink: 0;
padding: 2px 10px;
border: 1px solid #3973ff;
border-radius: 12px;
color: #3973ff;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
&:hover {
background: #3973ff;
border-color: #3973ff;
color: #fff;
}
}
}
// 套餐未开通模块:置灰 + 禁止点击效果
&.is-locked {
background: #fafafa;
border-style: dashed;
border-color: #dcdfe6;
box-shadow: none;
cursor: not-allowed;
filter: grayscale(1);
opacity: 0.8;
.card-title {
color: #909399;
}
.card-desc {
color: #a8abb2;
}
.card-icon-wrapper {
color: #c0c4cc;
}
.card-divider {
border-color: #e4e7ed;
}
&:hover {
transform: none;
background: #fafafa;
box-shadow: none;
.card-title,
.card-desc {
color: #909399;
}
.card-icon-wrapper {
color: #c0c4cc;
}
.card-divider {
opacity: 1;
}
}
}
&:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(102, 126, 234, 0.15);
@@ -1140,6 +1257,19 @@ onMounted(() => {
.modules-section .module-card .card-desc {
color: #8c8c8c !important;
}
.modules-section .module-card.is-locked {
background: #1f1f1f;
.card-title {
color: #8c8c8c !important;
font-weight: 600;
}
.card-desc {
color: #6b6b6b !important;
}
}
}
}
</style>