sass警告

This commit is contained in:
2026-06-02 14:21:13 +08:00
parent dde72be9de
commit 557a6f30f2
19 changed files with 353 additions and 48 deletions
+51
View File
@@ -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>
+1 -1
View File
@@ -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}`
})
/** 复制链接到剪贴板 */
+36 -3
View File
@@ -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 排序,routeNamemeta.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) {