sass警告
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<!-- 会员权限拦截弹窗 — 无权限页面点击时弹出 -->
|
||||
<Teleport to="body">
|
||||
<div v-if="modelValue" class="member-access-dialog-overlay" @click="$emit('update:modelValue', false)">
|
||||
<div class="member-access-dialog" @click.stop>
|
||||
<!-- 提示图标 -->
|
||||
<div class="member-access-dialog__icon">
|
||||
<svg viewBox="0 0 48 48" fill="none" class="member-access-dialog__lock-svg">
|
||||
<rect x="10" y="22" width="28" height="20" rx="3" stroke="currentColor" stroke-width="2.5"/>
|
||||
<path d="M16 22V16a8 8 0 1116 0v6" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/>
|
||||
<circle cx="24" cy="33" r="3" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 提示文字 -->
|
||||
<p class="member-access-dialog__text">该功能仅对会员开放,升级会员,享受更强大的AI求职功能</p>
|
||||
<!-- 操作按钮 -->
|
||||
<div class="member-access-dialog__actions">
|
||||
<button class="member-access-dialog__btn member-access-dialog__btn--cancel" @click="$emit('update:modelValue', false)">
|
||||
先不升级
|
||||
</button>
|
||||
<button class="member-access-dialog__btn member-access-dialog__btn--confirm" @click="handleViewDetail">
|
||||
了解详情
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 会员权限拦截弹窗
|
||||
* 当用户点击无权限(accessible=false)的菜单时弹出
|
||||
* "了解详情"按钮打开会员购买组件 MemberDialog
|
||||
*/
|
||||
|
||||
/** 组件 Props */
|
||||
defineProps<{ modelValue: boolean }>()
|
||||
|
||||
/** 组件 Emits */
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'openMember'): void
|
||||
}>()
|
||||
|
||||
/** 点击了解详情 — 关闭当前弹窗,通知父组件打开会员购买弹窗 */
|
||||
function handleViewDetail() {
|
||||
emit('update:modelValue', false)
|
||||
emit('openMember')
|
||||
}
|
||||
</script>
|
||||
@@ -92,7 +92,7 @@ const inviteCode = computed(() => store.state.userInfo?.inviteCode || '')
|
||||
/** 邀请链接文案 */
|
||||
const inviteText = computed(() => {
|
||||
const code = inviteCode.value
|
||||
return `https://www.offerpai.com.cn/invite_code=${code}`
|
||||
return `https://www.offerpai.com.cn?invite_code=${code}`
|
||||
})
|
||||
|
||||
/** 复制链接到剪贴板 */
|
||||
|
||||
@@ -123,6 +123,10 @@
|
||||
<SettingsDialog v-model="showSettingsDialog" :initial-tab="store.state.settingsTab" />
|
||||
<!-- 邀请注册送会员弹窗 -->
|
||||
<SettingsInviteDialog v-model="showShareDialog" />
|
||||
<!-- 会员权限拦截弹窗 -->
|
||||
<MemberAccessDialog v-model="showMemberAccessDialog" @open-member="showMemberDialog = true" />
|
||||
<!-- 会员购买弹窗 -->
|
||||
<MemberDialog v-model="showMemberDialog" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -131,6 +135,8 @@ import { computed, ref, watch, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import SettingsDialog from '@/components/SettingsDialog.vue'
|
||||
import MemberAccessDialog from '@/components/MemberAccessDialog.vue'
|
||||
import MemberDialog from '@/components/MemberDialog.vue'
|
||||
import { checkLogin } from '@/api/auth'
|
||||
import { fetchMessageList, fetchUnreadCount, markMessageRead } from '@/api/message'
|
||||
import { timestampToLocalDateTime } from '@/utils/time'
|
||||
@@ -155,6 +161,10 @@ interface MenuItem {
|
||||
iconImg: string
|
||||
label: string
|
||||
badge?: string
|
||||
/** 排序字段 */
|
||||
sortOrder: number
|
||||
/** 是否有使用权限 */
|
||||
accessible: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,15 +183,17 @@ const iconMap: Record<string, string> = {
|
||||
/**
|
||||
* 静态菜单 — 不需要登录就能看到的导航项(比如"职位")
|
||||
* 这些菜单始终显示,不依赖后端返回
|
||||
* sortOrder 设为 0 确保在未获取到后端数据时也能正常显示
|
||||
*/
|
||||
const staticMenus: MenuItem[] = [
|
||||
{ name: 'Jobs', path: '/jobs', iconImg: navJobsIcon, label: '职位' },
|
||||
{ name: 'Jobs', path: '/jobs', iconImg: navJobsIcon, label: '职位', sortOrder: 0, accessible: true },
|
||||
]
|
||||
|
||||
/**
|
||||
* 动态菜单 — 从 store.state.dynamicMenus(后端返回的数据)转换而来
|
||||
* 登录后才会有数据,登出后自动清空
|
||||
* 过滤掉 position === 'footer' 的项(如"设置"),它们显示在底部区域
|
||||
* 按 sortOrder 排序,routeName(meta.label)作为显示名称
|
||||
*/
|
||||
const dynamicMenuItems = computed<MenuItem[]>(() => {
|
||||
return store.state.dynamicMenus
|
||||
@@ -192,19 +204,30 @@ const dynamicMenuItems = computed<MenuItem[]>(() => {
|
||||
iconImg: iconMap[item.meta?.icon] || '',
|
||||
label: item.meta?.label || item.name,
|
||||
badge: item.meta?.badge,
|
||||
sortOrder: item.sortOrder ?? 99,
|
||||
accessible: item.accessible !== false, // 默认为 true
|
||||
}))
|
||||
.sort((a: MenuItem, b: MenuItem) => a.sortOrder - b.sortOrder)
|
||||
})
|
||||
|
||||
/**
|
||||
* 最终渲染的主菜单 = 静态菜单 + 动态菜单
|
||||
* 最终渲染的主菜单 = 静态菜单 + 动态菜单,按 sortOrder 统一排序
|
||||
* 如果后端返回了 Jobs 的数据,用后端数据覆盖静态菜单的 sortOrder
|
||||
*/
|
||||
const mainMenus = computed<MenuItem[]>(() => {
|
||||
return [...staticMenus, ...dynamicMenuItems.value]
|
||||
// 动态菜单中已包含后端返回的 Jobs(如果有的话),此时不再使用静态的
|
||||
const dynamicNames = dynamicMenuItems.value.map(m => m.name)
|
||||
const filteredStatic = staticMenus.filter(s => !dynamicNames.includes(s.name))
|
||||
return [...filteredStatic, ...dynamicMenuItems.value].sort((a, b) => a.sortOrder - b.sortOrder)
|
||||
})
|
||||
|
||||
const showShareDialog = ref(false)
|
||||
const showMessageDialog = ref(false)
|
||||
const showFeedbackDialog = ref(false)
|
||||
/** 会员权限拦截弹窗 */
|
||||
const showMemberAccessDialog = ref(false)
|
||||
/** 会员购买弹窗 */
|
||||
const showMemberDialog = ref(false)
|
||||
const showSettingsDialog = computed({
|
||||
get: () => store.state.showSettings,
|
||||
set: (val: boolean) => store.commit('SET_SHOW_SETTINGS', val),
|
||||
@@ -420,15 +443,25 @@ async function handleSettingsNav() {
|
||||
/**
|
||||
* 导航点击处理:
|
||||
* - 静态页面(Jobs)直接跳转
|
||||
* - accessible 为 false 的菜单 → 弹出会员权限拦截弹窗
|
||||
* - 动态页面通过 checkLogin 接口验证,未登录则弹登录框
|
||||
*/
|
||||
const staticNames = staticMenus.map(m => m.name)
|
||||
|
||||
async function handleNav(item: MenuItem) {
|
||||
// 无权限菜单 — 弹出会员权限拦截弹窗
|
||||
if (!item.accessible) {
|
||||
showMemberAccessDialog.value = true
|
||||
return
|
||||
}
|
||||
|
||||
// Jobs 等静态页面 — 无需登录验证,直接跳转
|
||||
if (staticNames.includes(item.name)) {
|
||||
router.push(item.path)
|
||||
return
|
||||
}
|
||||
|
||||
// 动态页面 — 需要登录验证
|
||||
try {
|
||||
const res = await checkLogin()
|
||||
if (res.code === '0' && res.data === true) {
|
||||
|
||||
Reference in New Issue
Block a user