feat: 增加支付宝在线支付、套餐订购弹窗、会员权益、支持模型展示等

This commit is contained in:
Gsh
2025-08-14 00:26:39 +08:00
parent 48d8c528f6
commit ee6b4827fa
11 changed files with 1564 additions and 38 deletions

View File

@@ -56,6 +56,7 @@ async function showPopover() {
// 点击
// 处理模型点击
function handleModelClick(item: GetSessionListVO) {
console.log('modelStore.modelList', modelStore.modelList);
if (!isModelAvailable(item)) {
ElMessageBox.confirm(
`

View File

@@ -0,0 +1,947 @@
<script setup lang="ts">
import { ElMessage } from 'element-plus';
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { createOrder, getOrderStatus } from '@/api';
import SupportModelList from '@/components/userPersonalCenter/components/SupportModelList.vue';
import ProductPage from '@/pages/products/index.vue';
const emit = defineEmits(['close']);
const visible = ref(true);
const activeTab = ref('member');
const selectedId = ref<number | null>(null);
const selectedPrice = ref(0);
const selectPackageObject = ref<any>(null);
const showDetails = ref(false);
const isMobile = ref(false);
const isLoading = ref(false);
const paymentWindow = ref<Window | null>(null);
const pollInterval = ref<NodeJS.Timeout | null>(null);
function checkMobile() {
isMobile.value = window.innerWidth < 768;
}
onMounted(() => {
checkMobile();
window.addEventListener('resize', checkMobile);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', checkMobile);
cleanupPayment();
});
function cleanupPayment() {
if (pollInterval.value) {
clearInterval(pollInterval.value);
pollInterval.value = null;
}
// if (paymentWindow.value && !paymentWindow.value.closed) {
// paymentWindow.value.close();
// }
// 清除轮询定时器
if (pollInterval.value) {
clearInterval(pollInterval.value);
pollInterval.value = null;
}
// 关闭支付窗口(如果还在)
// if (paymentWindow.value && !paymentWindow.value.closed) {
// paymentWindow.value.close();
// paymentWindow.value = null;
// }
retryCount = 0; // 重置重试计数器
}
const tabs = [
{ key: 'member', label: '会员套餐' },
// { key: 'token', label: 'Token 套餐' },
];
const packagesData = {
member: [
{ id: 1, name: '10个月', desc: '', price: 199, perMonth: 19.9, tag: '超高性价比', discount: '长期省心', key: 10 },
{ id: 2, name: '6个月', desc: '', price: 143.9, perMonth: 23.9, tag: '年度热销', key: 6 },
{ id: 3, name: '3个月', desc: '', price: 80.7, perMonth: 26.9, tag: '短期体验', discount: '', key: 3 },
{ id: 4, name: '1个月', desc: '', price: 29.9, originalPrice: 49.9, tag: '灵活选择', discount: '', key: 1 },
// { id: 5, name: '测试', desc: '', price: 0.01, originalPrice: 9.9, tag: '测试使用', discount: '', key: 0 },
],
token: [
{ id: 6, name: '10M 输入Token', desc: '', price: 49.9, tag: '轻量用户', discount: '' },
{ id: 7, name: '20M 输入Token', desc: '', price: 79.9, tag: '中等使用', discount: '' },
{ id: 8, name: '30M 输入Token', desc: '', price: 99.9, tag: '量大管饱', discount: '' },
{ id: 9, name: '联系站长', desc: '', price: 0, tag: '企业级需求', discount: '' },
],
};
const benefitsData = {
member: [
{ name: '基础+高级模型访问', value: '' },
{ name: 'AI专线超级加速', value: '' },
{ name: '售后微信群支持', value: '' },
{ name: '专属Api服务', value: '' },
],
token: [
{ name: 'Token 用途', value: '用于调用 API 或模型生成内容' },
{ name: '灵活计费', value: '按调用量扣费,更加自由' },
{ name: '支持多模型', value: '适配多种模型调用需求' },
],
};
const fullBenefitsData = {
member: [
{ category: 'YiXinAI AI Pro', free: '1项', vip: '5项', value: '价值68/月' },
{ category: '基础对话', free: '3条/天', vip: '不限条款', value: 'DeepSeek-R1 32b蒸馏版、AI-4o mini' },
{ category: '高级对话', free: '-', vip: '400条/月', value: 'DeepSeek-R1 671b满血版、4o、Cd3.5s、Code、文档对话' },
{ category: 'AI基础绘画', free: '-', vip: '500条/月', value: '' },
{ category: 'AI高级绘画', free: '-', vip: '50条/月', value: '' },
{ category: 'AI-4.0联网搜索', free: '-', vip: '支持', value: '' },
{ category: 'AI PPT', free: '1项', vip: '12项', value: '价值99/月' },
{ category: 'AI 思维导图', free: '1项', vip: '8项', value: '价值99/月' },
{ category: '文档对话', free: '0项', vip: '6项', value: '价值99/月' },
{ category: 'AI 绘图', free: '0项', vip: '5项', value: '价值99/月' },
{ category: 'AI 写作', free: '1项', vip: '6项', value: '价值99/月' },
],
};
const currentPackages = computed(() => packagesData[activeTab.value]);
const currentBenefits = computed(() => benefitsData[activeTab.value]);
function selectPackage(pkg: any) {
selectedId.value = pkg.id;
selectedPrice.value = pkg.price;
selectPackageObject.value = pkg;
}
async function pay() {
if (!selectedId.value) {
ElMessage.warning('请选择一个套餐');
return;
}
isLoading.value = true;
try {
const params = {
goodsType: selectPackageObject.value?.key,
};
const response = await createOrder(params);
console.log('订单创建成功:', response);
if (response.data.paymentPageHtml) {
handlePaymentPage(response.data.paymentPageHtml, response.data.orderId, response.data.outTradeNo);
}
else {
throw new Error('未获取到支付页面');
}
}
catch (error: any) {
console.error('支付失败:', error);
ElMessage.error(`支付失败: ${error.message || '未知错误'}`);
}
finally {
isLoading.value = false;
}
}
function handlePaymentPage(html: string, orderId: string, outTradeNo: string) {
// 关闭当前弹窗(如果有)
close();
// 创建支付窗口
paymentWindow.value = window.open('', '_blank');
if (!paymentWindow.value) {
ElMessage.error('无法打开支付窗口,请检查浏览器弹窗设置或允许弹窗');
return;
}
// 写入支付页面HTML并自动提交
paymentWindow.value.document.open();
paymentWindow.value.document.write(html);
// paymentWindow.value.document.close();
// 3秒后开始轮询支付状态给支付页面加载时间
setTimeout(() => {
startPolling(outTradeNo);
}, 3000);
}
function startPolling(outTradeNo: string) {
// 先清理之前的轮询任务
cleanupPayment();
// 立即检查一次状态(避免等待第一个间隔)
checkPaymentStatus(outTradeNo);
// 设置轮询任务每3秒检查一次
pollInterval.value = setInterval(() => {
checkPaymentStatus(outTradeNo);
}, 3000);
}
let retryCount = 0; // 错误重试计数器
async function checkPaymentStatus(outTradeNo: string) {
try {
const result = await getOrderStatus(outTradeNo);
console.log('订单状态检查结果:', result);
if (result.data.tradeStatus === 'TRADE_SUCCESS') {
// 支付成功处理
cleanupPayment();
ElMessage.success('支付成功!');
close(); // 关闭弹窗
// 可以在这里添加跳转到成功页面的逻辑
// window.location.href = '/pay/success?order=' + outTradeNo;
}
else if (result.data.tradeStatus === 'TRADE_CLOSED'
|| result.data.tradeStatus === 'TRADE_FAILED') {
// 支付失败处理
cleanupPayment();
ElMessage.warning(`支付失败: ${result.data.tradeStatusDesc || '未知原因'}`);
}
// 其他状态(如待支付)不做处理,继续轮询
}
catch (error) {
console.error('检查订单状态失败:', error);
// 网络错误等情况,可以重试几次后停止
if (retryCount > 3) {
cleanupPayment();
ElMessage.error('检查支付状态失败,请手动刷新页面确认');
}
retryCount++;
}
}
const router = useRouter();
function toggleDetails() {
showDetails.value = !showDetails.value;
}
function close() {
visible.value = false;
emit('close');
}
function onClose() {
emit('close');
}
</script>
<template>
<el-dialog
v-model="visible"
:width="isMobile ? '90%' : '980px'"
:fullscreen="isMobile && showDetails"
:show-close="false"
destroy-on-close
class="product-package-dialog"
@close="onClose"
>
<!-- 详情页 -->
<div v-if="showDetails" class="details-view">
<!-- 顶部标题和返回按钮 -->
<div class="flex items-center mb-6 sticky top-0 bg-white z-10 pt-2 pb-4">
<el-button text circle size="small" class="mr-2" @click="toggleDetails">
</el-button>
<div class="text-xl font-bold">
YiXinAI会员详细权益
</div>
</div>
<ProductPage />
<!-- 权益详情表格 -->
<div v-if="false" class="benefits-table">
<div class="table-header">
<div class="table-cell">
服务项
</div>
<div class="table-cell">
免费用户
</div>
<div class="table-cell">
AI大会员
</div>
</div>
<div v-for="(item, index) in fullBenefitsData.member" :key="index" class="table-row">
<div class="table-cell font-medium">
{{ item.category }}
</div>
<div class="table-cell">
{{ item.free }}
</div>
<div class="table-cell">
<div>{{ item.vip }}</div>
<div v-if="item.value" class="text-gray-500 text-xs">
{{ item.value }}
</div>
</div>
</div>
</div>
</div>
<!-- 主页面 -->
<div v-else>
<!-- 顶部标题和关闭按钮 -->
<div class="flex justify-between items-center mb-4">
<div class="text-xl font-bold">
购买套餐
</div>
<el-button circle size="small" @click="close">
</el-button>
</div>
<!-- Tab 切换 -->
<div class="flex border-b mb-6 overflow-x-auto">
<div
v-for="tab in tabs"
:key="tab.key"
class="cursor-pointer px-5 py-2 -mb-px border-b-2 transition whitespace-nowrap"
:class="activeTab === tab.key
? 'border-orange-500 text-orange-500 font-semibold'
: 'border-transparent text-gray-500 hover:text-orange-500'"
@click="activeTab = tab.key"
>
{{ tab.label }}
</div>
</div>
<!-- 移动端布局 -->
<div v-if="isMobile" class="mobile-layout">
<!-- 套餐卡片列表 -->
<div class="package-list">
<div
v-for="pkg in currentPackages"
:key="pkg.id"
class="package-card"
:class="{ selected: pkg.id === selectedId }"
@click="selectPackage(pkg)"
>
<!-- 标签 -->
<div v-if="pkg.discount" class="discount-tag">
{{ pkg.discount }}
</div>
<div v-if="pkg.tag" class="tag">
{{ pkg.tag }}
</div>
<!-- 套餐信息 -->
<div class="package-info">
<div class="package-name">
{{ pkg.name }}
</div>
<div class="package-desc">
{{ pkg.desc }}
</div>
</div>
<!-- 价格 -->
<div class="package-price">
<span class="price">¥{{ pkg.price }}</span>
<span v-if="pkg.perMonth" class="per-month">
¥{{ pkg.perMonth }}/
</span>
<div v-if="pkg.originalPrice" class="original-price">
原价¥{{ pkg.originalPrice }}
</div>
</div>
</div>
</div>
<!-- 权益预览 -->
<div class="benefits-preview max-h-200px overflow-y-auto">
<div class="section-title">
专属权益
</div>
<ul class="benefits-list">
<li
v-for="(b, index) in currentBenefits"
:key="index"
class="benefit-item"
>
<span class="dot"></span>
<span>
<span class="benefit-name">{{ b.name }}</span>
<span v-if="b.value">{{ b.value }}</span>
</span>
</li>
</ul>
<SupportModelList />
</div>
<!-- 支付区域 -->
<div class="payment-area">
<div v-if="false" class="agreement-text">
登录和注册都代表同意YiXinAI的会员协议
</div>
<div class="payment-info">
<div class="actual-payment">
<span>实际支付</span>
<span class="price">¥{{ selectedPrice || 0 }}</span>
</div>
<el-button
text
type="primary"
class="view-details-btn"
@click="toggleDetails"
>
了解更多
</el-button>
<el-button
type="primary"
:disabled="!selectedId || isLoading"
:loading="isLoading"
class="pay-button"
@click="pay"
>
立即支付
</el-button>
</div>
<div class="note-text">
可叠加购买次数过期时间以最后订单为准<br>
最终解释权归YiXinAI所有
</div>
</div>
</div>
<!-- 桌面端布局 -->
<div v-else class="flex gap-6 desktop-layout">
<!-- 左栏 套餐卡片 + 支付 -->
<div class="w-[60%] flex flex-col justify-between">
<!-- 套餐卡片列表 -->
<div class="flex flex-wrap gap-4">
<div
v-for="pkg in currentPackages"
:key="pkg.id"
class="package-card"
:class="{ selected: pkg.id === selectedId }"
@click="selectPackage(pkg)"
>
<!-- 标签 -->
<div v-if="pkg.discount" class="discount-tag">
{{ pkg.discount }}
</div>
<div v-if="pkg.tag" class="tag">
{{ pkg.tag }}
</div>
<!-- 套餐信息 -->
<div class="package-info">
<div class="package-name">
{{ pkg.name }}
</div>
<div class="package-desc">
{{ pkg.desc }}
</div>
</div>
<!-- 价格 -->
<div class="package-price">
<span class="price">¥{{ pkg.price }}</span>
<span v-if="pkg.perMonth" class="per-month">
¥{{ pkg.perMonth }}/
</span>
<div v-if="pkg.originalPrice" class="original-price">
原价¥{{ pkg.originalPrice }}
</div>
</div>
</div>
</div>
<!-- 支付按钮 -->
<div class="payment-section">
<div v-if="false" class="agreement-text">
登录和注册都代表同意YiXinAI的会员协议
</div>
<div class="payment-action">
<div class="actual-payment">
<span>实际支付</span>
<span class="price">¥{{ selectedPrice || 0 }}</span>
</div>
<div>
<el-button class="pay-button" text type="primary" @click="toggleDetails">
了解更多
</el-button>
<el-button
type="primary"
size="large"
:disabled="!selectedId || isLoading"
:loading="isLoading"
class="pay-button"
@click="pay"
>
立即支付
</el-button>
</div>
</div>
<div class="note-text">
可叠加购买次数过期时间以最后订单为准<br>
最终解释权归YiXinAI所有
</div>
</div>
</div>
<!-- 右栏 套餐权益 + 详情 -->
<div class="w-[40%] flex flex-col justify-between right-panel max-h-400px overflow-y-auto">
<div>
<div class="section-title">
会员权益
</div>
<ul class="benefits-list ">
<li
v-for="(b, index) in currentBenefits"
:key="index"
class="benefit-item"
>
<span class="dot"></span>
<span>
<span class="benefit-name">{{ b.name }}</span>
<span v-if="b.value">{{ b.value }}</span>
</span>
</li>
</ul>
<!-- 额外描述 -->
<div v-if="activeTab === 'member'" class="extra-description ">
<SupportModelList />
<!-- <div class="description-card"> -->
<!-- <div class="title"> -->
<!-- 前沿模型AI对话 -->
<!-- </div> -->
<!-- <div class="subtext"> -->
<!-- DP-RI深度思考精准解答 -->
<!-- </div> -->
<!-- <div class="subtext"> -->
<!-- AI写作文档对话AI思维导图等赋能职场 -->
<!-- </div> -->
<!-- </div> -->
<!-- <div class="description-card"> -->
<!-- <div class="title"> -->
<!-- AI绘图与设计能力 -->
<!-- </div> -->
<!-- <div class="subtext"> -->
<!-- 视觉吸睛赋能 -->
<!-- </div> -->
<!-- <div class="subtext"> -->
<!-- "AI+办公!"解锁300+工具箱会员权益 -->
<!-- </div> -->
<!-- </div> -->
</div>
</div>
<!-- 查看详情 -->
<div class="view-details">
<!-- <el-button text type="primary" @click="toggleDetails"> -->
<!-- 查看详情 -->
<!-- </el-button> -->
</div>
</div>
</div>
</div>
</el-dialog>
</template>
<style scoped lang="scss">
.product-package-dialog {
.el-dialog__header {
display: none;
}
.details-view {
height: 600px;
overflow-y: auto;
padding-right: 8px;
.benefits-table {
display: table;
width: 100%;
border-collapse: collapse;
.table-header, .table-row {
display: table-row;
}
.table-cell {
display: table-cell;
padding: 12px 16px;
border-bottom: 1px solid #eee;
vertical-align: middle;
}
.table-header {
font-weight: bold;
background-color: #f8f8f8;
.table-cell {
border-bottom: 2px solid #ddd;
}
}
}
}
/* 移动端样式 */
.mobile-layout {
display: flex;
flex-direction: column;
gap: 24px;
.package-list {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
}
.package-card {
position: relative;
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 16px;
transition: all 0.3s;
&.selected {
border-color: #f97316;
background-color: #fff7ed;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.discount-tag {
position: absolute;
top: -6px;
left: 8px;
background-color: #ef4444;
color: white;
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
}
.tag {
position: absolute;
top: -6px;
right: 8px;
background-color: #f97316;
color: white;
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
}
.package-info {
margin-bottom: 12px;
.package-name {
font-size: 16px;
font-weight: 600;
}
.package-desc {
font-size: 12px;
color: #6b7280;
}
}
.package-price {
.price {
font-size: 20px;
font-weight: 700;
color: #f97316;
}
.per-month {
font-size: 12px;
color: #6b7280;
margin-left: 4px;
}
.original-price {
font-size: 12px;
color: #9ca3af;
text-decoration: line-through;
}
}
}
.benefits-preview {
background-color: #f9fafb;
border-radius: 8px;
padding: 16px;
.section-title {
font-weight: 600;
margin-bottom: 12px;
}
.benefits-list {
list-style: none;
padding: 0;
margin: 0;
.benefit-item {
display: flex;
align-items: flex-start;
margin-bottom: 8px;
font-size: 14px;
color: #4b5563;
.dot {
color: #f97316;
margin-right: 8px;
}
.benefit-name {
font-weight: 500;
}
}
}
.view-details-btn {
width: 100%;
margin-top: 16px;
justify-content: flex-end;
}
}
.payment-area {
border-top: 1px solid #e5e7eb;
padding-top: 16px;
.agreement-text {
font-size: 12px;
color: #6b7280;
margin-bottom: 12px;
}
.payment-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
.actual-payment {
.price {
font-size: 18px;
font-weight: 700;
color: #f97316;
}
}
.pay-button {
width: 120px;
}
}
.note-text {
font-size: 12px;
color: #9ca3af;
}
}
}
/* 桌面端样式 */
.desktop-layout {
.package-card {
position: relative;
width: calc(50% - 0.5rem);
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 16px;
transition: all 0.3s;
display: flex;
flex-direction: column;
justify-content: space-between;
&.selected {
border-color: #f97316;
background-color: #fff7ed;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.discount-tag {
position: absolute;
top: -6px;
left: 8px;
background-color: #ef4444;
color: white;
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
}
.tag {
position: absolute;
top: -6px;
right: 8px;
background-color: #f97316;
color: white;
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
}
.package-info {
margin-bottom: 12px;
.package-name {
font-size: 16px;
font-weight: 600;
}
.package-desc {
font-size: 12px;
color: #6b7280;
}
}
.package-price {
.price {
font-size: 20px;
font-weight: 700;
color: #f97316;
}
.per-month {
font-size: 12px;
color: #6b7280;
margin-left: 4px;
}
.original-price {
font-size: 12px;
color: #9ca3af;
text-decoration: line-through;
}
}
}
.payment-section {
border-top: 1px solid #e5e7eb;
padding-top: 16px;
margin-top: 16px;
.agreement-text {
font-size: 12px;
color: #6b7280;
margin-bottom: 12px;
}
.payment-action {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
.actual-payment {
.price {
font-size: 18px;
font-weight: 700;
color: #f97316;
}
}
}
.note-text {
font-size: 12px;
color: #9ca3af;
}
}
.right-panel {
.section-title {
font-weight: 600;
margin-bottom: 12px;
}
.benefits-list {
list-style: none;
padding: 0;
margin: 0;
.benefit-item {
display: flex;
align-items: flex-start;
margin-bottom: 8px;
font-size: 14px;
color: #4b5563;
.dot {
color: #f97316;
margin-right: 8px;
}
.benefit-name {
font-weight: 500;
}
}
}
.extra-description {
margin-top: 24px;
.description-card {
background-color: #f9fafb;
border-radius: 8px;
padding: 12px;
margin-bottom: 12px;
.title {
font-weight: 600;
margin-bottom: 4px;
}
.subtext {
font-size: 12px;
color: #6b7280;
line-height: 1.4;
}
}
}
.view-details {
border-top: 1px solid #e5e7eb;
padding-top: 16px;
margin-top: 16px;
}
}
}
/* 响应式调整 */
@media (max-width: 768px) {
.el-dialog {
margin-top: 20px !important;
margin-bottom: 20px !important;
}
.details-view {
height: auto;
max-height: 80vh;
.benefits-table {
display: block;
overflow-x: auto;
white-space: nowrap;
.table-header, .table-row {
display: table;
width: 100%;
table-layout: fixed;
}
}
}
}
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-thumb {
background-color: #ccc;
border-radius: 3px;
}
}
</style>

View File

@@ -0,0 +1,202 @@
<script lang="ts" setup>
import { useModelStore } from '@/stores/modules/model';
// interface Model {
// id: string;
// category: string;
// modelId: string;
// modelName: string;
// modelDescribe: string;
// modelPrice: number;
// modelType: string;
// modelShow: string;
// systemPrompt: string | null;
// apiHost: string | null;
// apiKey: string | null;
// remark: string;
// }
// 从store获取模型列表
/*
const modelList = ref<Model[]>([
{
id: '077be103-1456-a4bb-409c-3a15f04e1ad9',
category: 'chat',
modelId: 'DeepSeek-R1-0528',
modelName: 'DeepSeek-R1',
modelDescribe: '国产开源,深度思索模式,不过幻读问题比较大,同时具备思考响应链,在开源模型中永远的神!',
modelPrice: 0,
modelType: '1',
modelShow: '0',
systemPrompt: null,
apiHost: null,
apiKey: null,
remark: '国产开源,深度思索模式,不过幻读问题比较大,同时具备思考响应链,在开源模型中永远的神!',
},
{
id: '077be103-1456-a4bb-409c-3a15f04e1ab7',
category: 'chat',
modelId: 'DeepSeek-V3-0324',
modelName: 'DeepSeek-V3',
modelDescribe: '国产开源,简单聊天模式,对于中文文章语义体验较好,但响应速度一般',
modelPrice: 0,
modelType: '1',
modelShow: '0',
systemPrompt: null,
apiHost: null,
apiKey: null,
remark: '国产开源,简单聊天模式,对于中文文章语义体验较好,但响应速度一般',
},
]);
*/
// 实际使用时您可以从store中获取模型列表
// import { useModelStore } from '@/stores/model';
const modelStore = useModelStore();
const modelList = computed(() => modelStore.modelList);
console.log('modelList---', modelList);
</script>
<template>
<div class="model-container">
<div class="model-header">
支持的模型
</div>
<div class="model-grid">
<div v-for="model in modelList" :key="model.id" class="model-card">
<div class="model-card-header">
<h3 class="model-name">
{{ model.modelName }}
</h3>
<div class="model-price">
<template v-if="model.modelPrice === 0">
<span class="free-tag">{{ model.modelId === 'DeepSeek-R1-0528' ? '免费' : 'Vip专享' }}</span>
</template>
<template v-else>
<span class="price">{{ model.modelPrice }}/</span>
<span class="per-token">{{ model.modelPrice * 100 }}/百万Token</span>
</template>
</div>
</div>
<div class="model-description">
{{ model.modelDescribe }}
</div>
<div class="model-footer">
<span class="model-id">{{ model.modelId }}</span>
<!-- <el-tag v-if="model.category === 'chat'" size="small" type="success"> -->
<!-- 对话 -->
<!-- </el-tag> -->
<!-- <el-tag v-else size="small" type="info"> -->
<!-- 其他 -->
<!-- </el-tag> -->
</div>
</div>
</div>
</div>
</template>
<style scoped>
.model-container {
//padding: 10px;
max-width: 300px;
margin: 10px 0 ;
}
.model-header {
font-size: 14px;
margin-bottom: 24px;
color: #333;
font-weight: 600;
}
.model-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
}
.model-card {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
padding: 10px;
transition: all 0.3s ease;
border: 1px solid #ebeef5;
}
.model-card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.1);
}
.model-card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.model-name {
font-size: 14px;
font-weight: 600;
margin: 0;
color: #333;
}
.model-price {
text-align: right;
}
.free-tag {
background: #f0f9eb;
color: #67c23a;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.price {
display: block;
font-size: 13px;
font-weight: 600;
color: #f56c6c;
}
.per-token {
font-size: 12px;
color: #909399;
}
.model-description {
color: #606266;
line-height: 1.6;
margin-bottom: 16px;
min-height: 60px;
}
.model-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 12px;
border-top: 1px solid #f0f0f0;
}
.model-id {
font-size: 12px;
color: #909399;
}
@media (max-width: 768px) {
.model-grid {
grid-template-columns: 1fr;
}
.model-card {
padding: 16px;
}
}
</style>