fix: 增加用户角色标识与优化产品页
This commit is contained in:
@@ -31,6 +31,9 @@ const models = [
|
||||
意心AI 为您集成市面上热门模型,订阅即享多模型使用权限,无需额外购买,真正做到一步到位。
|
||||
</p>
|
||||
|
||||
<h2 class="text-2xl font-semibold mb-4 text-center">
|
||||
热门大模型价格排行榜
|
||||
</h2>
|
||||
<div class="bg-white shadow rounded-2xl overflow-hidden">
|
||||
<table class="w-full table-auto border-collapse">
|
||||
<thead class="bg-gray-100">
|
||||
@@ -39,7 +42,7 @@ const models = [
|
||||
模型
|
||||
</th>
|
||||
<th class="px-4 py-2 text-left text-sm font-semibold text-gray-700">
|
||||
市价(元/月)
|
||||
市价( 美元/M token)
|
||||
</th>
|
||||
<th class="px-4 py-2 text-left text-sm font-semibold text-gray-700">
|
||||
简介
|
||||
@@ -62,6 +65,24 @@ const models = [
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mt-16">
|
||||
<h2 class="text-2xl font-semibold mb-4 text-center">
|
||||
查看更多大模型价格实时排行榜
|
||||
</h2>
|
||||
<div class="rounded-2xl shadow-lg overflow-hidden border border-gray-200">
|
||||
<iframe
|
||||
src="https://easyllm.site/static/models.html"
|
||||
width="100%"
|
||||
height="700"
|
||||
class="w-full"
|
||||
loading="lazy"
|
||||
sandbox="allow-scripts allow-same-origin allow-popups"
|
||||
/>
|
||||
</div>
|
||||
<p class="text-sm text-center text-gray-500 mt-2">
|
||||
来源:LMSYS Chatbot Arena 排行榜
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-8 text-center">
|
||||
<h2 class="text-2xl font-semibold mb-2">
|
||||
一口价订阅,仅需 <span class="text-red-500 text-3xl font-bold">49.9元/月</span>
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
<!-- 头像 -->
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import Popover from '@/components/Popover/index.vue';
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||
import { useUserStore } from '@/stores';
|
||||
import { useSessionStore } from '@/stores/modules/session';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const userStore = useUserStore();
|
||||
const sessionStore = useSessionStore();
|
||||
const userRole = computed(() => {
|
||||
const roles = userStore.userInfo?.roles ?? [];
|
||||
return roles.some(role => role.roleCode === 'YiXinAi-Vip') ? 'vip' : 'user';
|
||||
});
|
||||
|
||||
const src = computed(
|
||||
() => userStore.userInfo?.avatar ?? 'https://avatars.githubusercontent.com/u/76239030',
|
||||
);
|
||||
@@ -87,41 +95,135 @@ function handleClick(item: any) {
|
||||
}
|
||||
}
|
||||
|
||||
function openVipGuide() {
|
||||
const isVip = userRole.value === 'vip';
|
||||
|
||||
ElMessageBox.confirm(
|
||||
`
|
||||
<div class="text-center leading-relaxed">
|
||||
<h3 class="text-lg font-bold mb-3">${isVip ? 'YiXinAI-VIP 会员' : '成为 YiXinAI-VIP'}</h3>
|
||||
<p class="mb-2">
|
||||
${
|
||||
isVip
|
||||
? '您已是尊贵会员,享受全部 AI 模型与专属服务。感谢您的支持!'
|
||||
: '解锁所有 AI 模型,无限加速,专属客服,尽享尊贵体验。'
|
||||
}
|
||||
</p>
|
||||
${
|
||||
isVip
|
||||
? '<p class="text-sm text-gray-500">您可随时访问产品页面查看更多特权内容。</p>'
|
||||
: '<p class="text-sm text-gray-500">点击下方按钮,立即升级为 VIP 会员!</p>'
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
isVip ? '会员状态' : '会员尊享',
|
||||
{
|
||||
confirmButtonText: '前往产品页面',
|
||||
cancelButtonText: '关闭',
|
||||
dangerouslyUseHTMLString: true,
|
||||
type: 'info',
|
||||
center: true,
|
||||
roundButton: true,
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
router.push({
|
||||
name: 'products', // 使用命名路由
|
||||
query: { from: userRole.value }, // 可选:添加来源标识
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
// 点击右上角关闭或“关闭”按钮,不执行任何操作
|
||||
});
|
||||
}
|
||||
|
||||
/* 弹出面板 结束 */
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="avatar-container">
|
||||
<Popover
|
||||
ref="popoverRef"
|
||||
placement="bottom-end"
|
||||
trigger="clickTarget"
|
||||
:trigger-style="{ cursor: 'pointer' }"
|
||||
popover-class="popover-content"
|
||||
:popover-style="popoverStyle"
|
||||
>
|
||||
<!-- 触发元素插槽 -->
|
||||
<template #trigger>
|
||||
<el-avatar :src="src" :size="28" fit="fit" shape="circle" />
|
||||
</template>
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- 用户信息区域 -->
|
||||
<div class=" cursor-pointer flex flex-col text-right mr-2 leading-tight" @click="openVipGuide">
|
||||
<div class="text-sm font-semibold text-gray-800">
|
||||
{{ userStore.userInfo?.user.nick ?? '未登录用户' }}
|
||||
</div>
|
||||
|
||||
<div class="popover-content-box shadow-lg">
|
||||
<div v-for="item in popoverList" :key="item.key" class="popover-content-box-items h-full">
|
||||
<div
|
||||
v-if="!item.divider"
|
||||
class="popover-content-box-item flex items-center h-full gap-8px p-8px pl-10px pr-12px rounded-lg hover:cursor-pointer hover:bg-[rgba(0,0,0,.04)]"
|
||||
@click="handleClick(item)"
|
||||
>
|
||||
<SvgIcon :name="item.icon!" size="16" class-name="flex-none" />
|
||||
<div class="popover-content-box-item-text font-size-14px text-overflow max-h-120px">
|
||||
{{ item.title }}
|
||||
<!-- 角色展示 -->
|
||||
<div>
|
||||
<span
|
||||
v-if="userRole === 'vip'"
|
||||
class="inline-block px-2 py-0.5 text-xs text-yellow-700 bg-yellow-100 rounded-full font-semibold"
|
||||
>
|
||||
YiXinAI-VIP
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-else
|
||||
class="inline-block px-2 py-0.5 text-xs text-gray-600 bg-gray-100 rounded-full cursor-pointer hover:bg-yellow-50 hover:text-yellow-700 transition"
|
||||
>
|
||||
普通用户 · 开通 VIP
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 头像区域 -->
|
||||
<div class="avatar-container">
|
||||
<Popover
|
||||
ref="popoverRef"
|
||||
placement="bottom-end"
|
||||
trigger="clickTarget"
|
||||
:trigger-style="{ cursor: 'pointer' }"
|
||||
popover-class="popover-content"
|
||||
:popover-style="popoverStyle"
|
||||
>
|
||||
<template #trigger>
|
||||
<el-avatar :src="src" :size="28" fit="fit" shape="circle" />
|
||||
</template>
|
||||
|
||||
<div class="popover-content-box shadow-lg">
|
||||
<!-- 用户信息 -->
|
||||
<div class="user-info-box flex items-center gap-8px p-8px rounded-lg mb-2">
|
||||
<el-avatar :src="src" :size="32" fit="fit" shape="circle" />
|
||||
<div class="flex flex-col text-sm">
|
||||
<div class="font-semibold text-gray-800">
|
||||
{{ userStore.userInfo?.user.nick ?? '未登录用户' }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500">
|
||||
<span
|
||||
v-if="userRole === 'vip'"
|
||||
class="inline-block px-2 py-0.5 text-xs text-yellow-700 bg-yellow-100 rounded-full font-semibold"
|
||||
>
|
||||
YiXinAI-VIP
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-else
|
||||
class="inline-block px-2 py-0.5 text-xs text-gray-600 bg-gray-100 rounded-full cursor-pointer hover:bg-yellow-50 hover:text-yellow-700 transition"
|
||||
>
|
||||
普通用户 · 开通 VIP
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divder h-1px bg-gray-200 my-4px" />
|
||||
|
||||
<div v-if="item.divider" class="divder h-1px bg-gray-200 my-4px" />
|
||||
<div v-for="item in popoverList" :key="item.key" class="popover-content-box-items h-full">
|
||||
<div
|
||||
v-if="!item.divider"
|
||||
class="popover-content-box-item flex items-center h-full gap-8px p-8px pl-10px pr-12px rounded-lg hover:cursor-pointer hover:bg-[rgba(0,0,0,.04)]"
|
||||
@click="handleClick(item)"
|
||||
>
|
||||
<SvgIcon :name="item.icon!" size="16" class-name="flex-none" />
|
||||
<div class="popover-content-box-item-text font-size-14px text-overflow max-h-120px">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="item.divider" class="divder h-1px bg-gray-200 my-4px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popover>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ const pricing = [
|
||||
'AI超级加速',
|
||||
'无限制使用',
|
||||
'售后微信群支持',
|
||||
'可用29.9元优惠券',
|
||||
'可用20元优惠券',
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -27,7 +27,6 @@ const pricing = [
|
||||
'AI超级加速',
|
||||
'无限制使用',
|
||||
'售后微信群支持',
|
||||
'不支持优惠券',
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -39,8 +38,9 @@ const pricing = [
|
||||
'基础+高级模型访问',
|
||||
'AI超级加速',
|
||||
'无限制使用',
|
||||
'售后微信群支持',
|
||||
'售后微信群优先处理',
|
||||
'可用49.9元优惠券',
|
||||
'可用20元优惠券',
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -149,7 +149,7 @@ function openContact() {
|
||||
{{ plan.period }}
|
||||
</p>
|
||||
<el-divider />
|
||||
<ul class="text-left space-y-2 w-full">
|
||||
<ul class="text-left space-y-2 w-full min-h-200px">
|
||||
<li v-for="(feature, i) in plan.features" :key="i" class="flex items-start gap-2">
|
||||
<el-icon><CircleCheck /></el-icon>
|
||||
<span>{{ feature }}</span>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { LoginUser } from '@/api/auth/types';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
@@ -9,9 +8,6 @@ export const useUserStore = defineStore(
|
||||
const refreshToken = ref<string | undefined>();
|
||||
const router = useRouter();
|
||||
const setToken = (value: string, refreshValue?: string) => {
|
||||
// 让接口报401
|
||||
// token.value = `${value}cdsfds`;
|
||||
|
||||
token.value = value;
|
||||
if (refreshValue) {
|
||||
refreshToken.value = refreshValue;
|
||||
@@ -22,9 +18,10 @@ export const useUserStore = defineStore(
|
||||
refreshToken.value = void 0;
|
||||
};
|
||||
|
||||
const userInfo = ref<LoginUser>();
|
||||
const userInfo = ref<any>();
|
||||
const setUserInfo = (value: any) => {
|
||||
userInfo.value = value;
|
||||
console.log('userInfo', userInfo.value);
|
||||
};
|
||||
const clearUserInfo = () => {
|
||||
userInfo.value = void 0;
|
||||
|
||||
@@ -75,13 +75,22 @@ function jwtPlugin(): HookFetchPlugin<BaseResponse> {
|
||||
return {
|
||||
name: 'jwt',
|
||||
beforeRequest: async (config) => {
|
||||
config.headers = new Headers(config.headers);
|
||||
if (userStore.refreshToken) {
|
||||
config.headers.set('refresh_token', `Bearer ${userStore.refreshToken}`);
|
||||
}
|
||||
if (userStore.token) {
|
||||
config.headers = new Headers(config.headers);
|
||||
config.headers.set('authorization', `Bearer ${userStore.token}`);
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
afterResponse: async (response) => {
|
||||
afterResponse: async (response: any) => {
|
||||
console.log('response.response.headers---', response.response.headers);
|
||||
if (response.response.headers?.refresh_token) {
|
||||
userStore.setToken(response.response.headers?.access_token, response.response.headers?.refresh_token);
|
||||
}
|
||||
|
||||
if (response.result?.code === 200)
|
||||
return response;
|
||||
|
||||
|
||||
3
Yi.Ai.Vue3/types/components.d.ts
vendored
3
Yi.Ai.Vue3/types/components.d.ts
vendored
@@ -26,6 +26,9 @@ declare module 'vue' {
|
||||
ElImage: typeof import('element-plus/es')['ElImage']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElMain: typeof import('element-plus/es')['ElMain']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||
FilesSelect: typeof import('./../src/components/FilesSelect/index.vue')['default']
|
||||
IconSelect: typeof import('./../src/components/IconSelect/index.vue')['default']
|
||||
|
||||
Reference in New Issue
Block a user