updata
This commit is contained in:
parent
5f8dce4fe5
commit
c7014b3d1c
@ -11,7 +11,7 @@ import {getHomePath} from "@/views/setting/tools";
|
|||||||
|
|
||||||
NProgress.configure({showSpinner: false}); // NProgress Configuration
|
NProgress.configure({showSpinner: false}); // NProgress Configuration
|
||||||
|
|
||||||
const whiteList = ["product","/login", "/homePage", "/registrationPage", "/shoppingCart", "/homePageImage","/h5HomePage",'/H5about','/modelProductDetail','/ncmatchHome']; // no redirect whitelist
|
const whiteList = ["product", "/tokenMarket", "/modelDetail", "/modelApiDocument", "/login", "/homePage", "/registrationPage", "/shoppingCart", "/homePageImage","/h5HomePage",'/H5about','/modelProductDetail','/ncmatchHome']; // no redirect whitelist
|
||||||
|
|
||||||
// 获取用户代理字符串
|
// 获取用户代理字符串
|
||||||
const userAgent = window.navigator.userAgent;
|
const userAgent = window.navigator.userAgent;
|
||||||
|
|||||||
@ -62,6 +62,16 @@ export const constantRoutes = [
|
|||||||
component: () => import('@/views/modelProductDetail/index.vue'),
|
component: () => import('@/views/modelProductDetail/index.vue'),
|
||||||
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/tokenMarket",
|
||||||
|
name: "PublicTokenMarket",
|
||||||
|
component: () => import('@/views/product/allProduct/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
title: "TOKEN市集",
|
||||||
|
noCache: true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/h5HomePage',
|
path: '/h5HomePage',
|
||||||
name: 'H5HomePage',
|
name: 'H5HomePage',
|
||||||
@ -392,6 +402,28 @@ export const constantRoutes = [
|
|||||||
meta: { title: "立即支付页面" },
|
meta: { title: "立即支付页面" },
|
||||||
}, {
|
}, {
|
||||||
path: "/auth-redirect", component: () => import("@/views/login/auth-redirect"), hidden: true,
|
path: "/auth-redirect", component: () => import("@/views/login/auth-redirect"), hidden: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/modelDetail",
|
||||||
|
component: () => import('@/views/modelManagement/ModelDetail.vue'),
|
||||||
|
hidden: true,
|
||||||
|
name: 'modelDetail',
|
||||||
|
meta: {
|
||||||
|
title: "模型详情",
|
||||||
|
fullPath: "/modelDetail",
|
||||||
|
noCache: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/modelApiDocument",
|
||||||
|
component: () => import('@/views/modelManagement/ApiDocument.vue'),
|
||||||
|
hidden: true,
|
||||||
|
name: 'modelApiDocument',
|
||||||
|
meta: {
|
||||||
|
title: "API文档",
|
||||||
|
fullPath: "/modelApiDocument",
|
||||||
|
noCache: true
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
path: "/404", component: () => import("@/views/error-page/404"), hidden: true,
|
path: "/404", component: () => import("@/views/error-page/404"), hidden: true,
|
||||||
}, {
|
}, {
|
||||||
@ -480,6 +512,30 @@ export const asyncRoutes = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
// Token用量 - 一级菜单(所有登录用户都能看到)
|
||||||
|
{
|
||||||
|
path: "/tokenUsage",
|
||||||
|
component: Layout,
|
||||||
|
meta: {
|
||||||
|
title: "Token用量",
|
||||||
|
fullPath: "/tokenUsage",
|
||||||
|
noCache: true,
|
||||||
|
icon: "el-icon-data-line"
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: () => import('@/views/tokenUsage/index.vue'),
|
||||||
|
name: 'TokenUsage',
|
||||||
|
meta: {
|
||||||
|
title: "Token用量",
|
||||||
|
fullPath: "/tokenUsage",
|
||||||
|
noCache: true,
|
||||||
|
icon: "el-icon-data-line"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
// 模型体验
|
// 模型体验
|
||||||
{
|
{
|
||||||
path: "/modelExperience",
|
path: "/modelExperience",
|
||||||
@ -492,30 +548,6 @@ export const asyncRoutes = [
|
|||||||
noCache: true
|
noCache: true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 模型详情
|
|
||||||
{
|
|
||||||
path: "/modelDetail",
|
|
||||||
component: () => import('@/views/modelManagement/ModelDetail.vue'),
|
|
||||||
hidden: true,
|
|
||||||
name: 'modelDetail',
|
|
||||||
meta: {
|
|
||||||
title: "模型详情",
|
|
||||||
fullPath: "/modelDetail",
|
|
||||||
noCache: true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// API文档
|
|
||||||
{
|
|
||||||
path: "/modelApiDocument",
|
|
||||||
component: () => import('@/views/modelManagement/ApiDocument.vue'),
|
|
||||||
hidden: true,
|
|
||||||
name: 'modelApiDocument',
|
|
||||||
meta: {
|
|
||||||
title: "API文档",
|
|
||||||
fullPath: "/modelApiDocument",
|
|
||||||
noCache: true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/overview",
|
path: "/overview",
|
||||||
component: Layout,
|
component: Layout,
|
||||||
@ -1056,24 +1088,24 @@ export const asyncRoutes = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
// 信息完善 - 变为一级菜单
|
// 信息完善 - 变为一级菜单
|
||||||
{
|
// {
|
||||||
path: "/informationPerfect",
|
// path: "/informationPerfect",
|
||||||
component: Layout,
|
// component: Layout,
|
||||||
meta: {
|
// meta: {
|
||||||
title: "信息完善",
|
// title: "信息完善",
|
||||||
icon: "el-icon-edit-outline",
|
// icon: "el-icon-edit-outline",
|
||||||
noCache: true,
|
// noCache: true,
|
||||||
fullPath: "/informationPerfect"
|
// fullPath: "/informationPerfect"
|
||||||
},
|
// },
|
||||||
children: [
|
// children: [
|
||||||
{
|
// {
|
||||||
path: "index",
|
// path: "index",
|
||||||
component: () => import('@/views/customer/ncApprove/index.vue'),
|
// component: () => import('@/views/customer/ncApprove/index.vue'),
|
||||||
name: "InformationPerfect",
|
// name: "InformationPerfect",
|
||||||
meta: { title: "信息完善", fullPath: "/informationPerfect/index" },
|
// meta: { title: "信息完善", fullPath: "/informationPerfect/index" },
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
},
|
// },
|
||||||
// 充值管理 - 变为一级菜单
|
// 充值管理 - 变为一级菜单
|
||||||
{
|
{
|
||||||
path: "/rechargeManagement",
|
path: "/rechargeManagement",
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const SPECIAL_ORDER_USER = 'ZhipuHZ';
|
|||||||
const SUPER_ADMIN_ROUTE_PATH = '/superAdministrator';
|
const SUPER_ADMIN_ROUTE_PATH = '/superAdministrator';
|
||||||
|
|
||||||
// 所有登录用户都能访问的公共路由,不依赖后端 auths 和角色。hidden 路由不会显示在菜单里。
|
// 所有登录用户都能访问的公共路由,不依赖后端 auths 和角色。hidden 路由不会显示在菜单里。
|
||||||
const COMMON_ROUTE_PATHS = ['/product', '/tokenManagement', '/modelExperience', '/modelDetail', '/modelApiDocument'];
|
const COMMON_ROUTE_PATHS = ['/product', '/tokenManagement', '/tokenUsage', '/modelExperience', '/modelDetail', '/modelApiDocument'];
|
||||||
|
|
||||||
// 运营角色需要额外补出来的菜单。
|
// 运营角色需要额外补出来的菜单。
|
||||||
const OPERATION_EXTRA_ROUTE_PATHS = ['/modelManagement', '/operationReport'];
|
const OPERATION_EXTRA_ROUTE_PATHS = ['/modelManagement', '/operationReport'];
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
<p @mouseleave="sildeOut" @mouseenter="sildeIn(product_service)">
|
<p @mouseleave="sildeOut" @mouseenter="sildeIn(product_service)">
|
||||||
<a>基础云</a>
|
<a>基础云</a>
|
||||||
</p>
|
</p>
|
||||||
<p class="nav-hover" @click="handleModelSquareClick">token市集</p>
|
<p class="nav-hover" :class="{ active: isActiveTokenMarket }" @click="handleModelSquareClick">token市集</p>
|
||||||
<p class="nav-hover" @click="goYuanjing">元境</p>
|
<p class="nav-hover" @click="goYuanjing">元境</p>
|
||||||
<!-- 供需广场 -->
|
<!-- 供需广场 -->
|
||||||
<p :class="{ active: $route.path.includes('/supply') }">
|
<p :class="{ active: $route.path.includes('/supply') }">
|
||||||
@ -367,6 +367,11 @@ export default Vue.extend({
|
|||||||
return this.$route.path.includes('/homePage/index');
|
return this.$route.path.includes('/homePage/index');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isActiveTokenMarket() {
|
||||||
|
const category = this.$route.query.category || this.$route.query.tab || ''
|
||||||
|
return this.$route.path === '/tokenMarket' ||
|
||||||
|
(this.$route.path === '/product' && ['TOKEN市集', 'Token市集', 'token市集'].includes(category))
|
||||||
|
},
|
||||||
aiPanelStyle() {
|
aiPanelStyle() {
|
||||||
if (this.aiPanelPosition.left === null || this.aiPanelPosition.top === null) {
|
if (this.aiPanelPosition.left === null || this.aiPanelPosition.top === null) {
|
||||||
return {}
|
return {}
|
||||||
@ -381,19 +386,30 @@ export default Vue.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 点击模型广场前校验登录状态
|
// 点击 TOKEN 市集:未登录也允许进入,但按单页面模式展示。
|
||||||
handleModelSquareClick() {
|
handleModelSquareClick() {
|
||||||
if (!this.loginState) {
|
if (!this.loginState) {
|
||||||
this.$message.warning('请先登录哦~')
|
this.$router.push({
|
||||||
|
path: '/tokenMarket',
|
||||||
|
query: {
|
||||||
|
category: 'TOKEN市集',
|
||||||
|
single: '1'
|
||||||
|
}
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.$router.push('/product')
|
this.$router.push({
|
||||||
|
path: '/product',
|
||||||
|
query: {
|
||||||
|
category: 'TOKEN市集'
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 跳转元境
|
// 跳转元境
|
||||||
async goYuanjing() {
|
async goYuanjing() {
|
||||||
if (!this.loginState) {
|
if (!this.loginState) {
|
||||||
this.$message.warning('请先登录哦~')
|
window.open('https://ai.opencomputing.cn/', '_blank')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1143,6 +1159,11 @@ export default Vue.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.nav-hover.active {
|
||||||
|
color: #1E6FFF;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-size: 18px !important;
|
font-size: 18px !important;
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<div class="hero-inner">
|
<div class="hero-inner">
|
||||||
<p class="hero-slogan">好用还省钱,Token 就上开元云</p>
|
<p class="hero-slogan">好用还省钱,Token 就上开元云</p>
|
||||||
<h1>石景山<span>OPC</span>公共服务平台</h1>
|
<h1>数智开物<span>OPC</span>公共服务平台</h1>
|
||||||
<p class="hero-subtitle">
|
<p class="hero-subtitle">
|
||||||
为 <strong>OPC</strong> 而生,极致性价比一站式模型平台
|
为 <strong>OPC</strong> 而生,极致性价比一站式模型平台
|
||||||
</p>
|
</p>
|
||||||
@ -220,7 +220,14 @@ export default Vue.extend({
|
|||||||
methods: {
|
methods: {
|
||||||
goTokenMarket() {
|
goTokenMarket() {
|
||||||
if (!this.loginState) {
|
if (!this.loginState) {
|
||||||
this.$message.warning('请先登录哦~')
|
this.$router.push({
|
||||||
|
path: '/tokenMarket',
|
||||||
|
query: {
|
||||||
|
category: 'TOKEN市集',
|
||||||
|
from: 'home',
|
||||||
|
single: '1'
|
||||||
|
}
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
@ -264,7 +271,7 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
async goCreativeWorkshop() {
|
async goCreativeWorkshop() {
|
||||||
if (!this.loginState) {
|
if (!this.loginState) {
|
||||||
this.$message.warning('请先登录哦~')
|
window.open('https://ai.opencomputing.cn/', '_blank')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -43,10 +43,27 @@
|
|||||||
|
|
||||||
<!-- 登录容器 -->
|
<!-- 登录容器 -->
|
||||||
<div class="login-container login-form-style">
|
<div class="login-container login-form-style">
|
||||||
<el-form ref="loginForm" :model="loginForm" :rules="rules" class="login-form-l" autocomplete="on"
|
<div class="login-mode-tabs">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
:class="{ active: loginMode === 'password' }"
|
||||||
|
@click="switchLoginMode('password')"
|
||||||
|
>
|
||||||
|
密码登录
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
:class="{ active: loginMode === 'mobile' }"
|
||||||
|
@click="switchLoginMode('mobile')"
|
||||||
|
>
|
||||||
|
验证码登录
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-form ref="loginForm" :model="loginForm" :rules="activeRules" class="login-form-l" autocomplete="on"
|
||||||
label-position="left">
|
label-position="left">
|
||||||
|
|
||||||
<el-form-item prop="username" style="background-color: white; border: 1px solid #d9d9d9;width: 300px;">
|
<el-form-item v-if="loginMode === 'password'" prop="username" style="background-color: white; border: 1px solid #d9d9d9;width: 300px;">
|
||||||
<div class="user-input">
|
<div class="user-input">
|
||||||
<span class="svg-container icon-box">
|
<span class="svg-container icon-box">
|
||||||
<img src="../../assets/kyy/用户.svg" alt="">
|
<img src="../../assets/kyy/用户.svg" alt="">
|
||||||
@ -56,7 +73,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-tooltip v-model="capsTooltip" style="margin-bottom: 7px" content="Caps lock is On" placement="right"
|
<el-tooltip v-if="loginMode === 'password'" v-model="capsTooltip" style="margin-bottom: 7px" content="Caps lock is On" placement="right"
|
||||||
manual>
|
manual>
|
||||||
<el-form-item prop="password" class="password-mobile"
|
<el-form-item prop="password" class="password-mobile"
|
||||||
style="background-color: white; border: 1px solid #d9d9d9;width: 300px;">
|
style="background-color: white; border: 1px solid #d9d9d9;width: 300px;">
|
||||||
@ -66,7 +83,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<el-input id="passwordId" :key="passwordType" ref="password" v-model="loginForm.password"
|
<el-input id="passwordId" :key="passwordType" ref="password" v-model="loginForm.password"
|
||||||
:type="passwordType" placeholder="请输入密码" name="password" @keyup.native="checkCapslock"
|
:type="passwordType" placeholder="请输入密码" name="password" @keyup.native="checkCapslock"
|
||||||
@blur="capsTooltip = false" @keyup.enter.native="handleLogin" />
|
@blur="capsTooltip = false" @keyup.enter.native="handleLogin('loginForm')" />
|
||||||
<span class="show-pwd" @click="showPwd">
|
<span class="show-pwd" @click="showPwd">
|
||||||
<svg-icon style="margin-right: 10px;cursor: pointer"
|
<svg-icon style="margin-right: 10px;cursor: pointer"
|
||||||
:icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
|
:icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
|
||||||
@ -75,7 +92,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
||||||
<el-form-item prop="mobile" style="background-color: white; border: 1px solid #d9d9d9;width: 300px;">
|
<el-form-item v-if="loginMode === 'mobile'" prop="mobile" style="background-color: white; border: 1px solid #d9d9d9;width: 300px;">
|
||||||
<div class="user-input">
|
<div class="user-input">
|
||||||
<span class="svg-container icon-box">
|
<span class="svg-container icon-box">
|
||||||
<img src="../../assets/kyy/用户.svg" alt="">
|
<img src="../../assets/kyy/用户.svg" alt="">
|
||||||
@ -85,7 +102,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<div style="display:flex;margin-top:20px;">
|
<div v-if="loginMode === 'mobile'" style="display:flex;margin-top:20px;">
|
||||||
<el-form-item prop="vcode" class="invitecode"
|
<el-form-item prop="vcode" class="invitecode"
|
||||||
style="background-color: white; border: 1px solid #d9d9d9;">
|
style="background-color: white; border: 1px solid #d9d9d9;">
|
||||||
<div class="user-input">
|
<div class="user-input">
|
||||||
@ -201,6 +218,7 @@ export default {
|
|||||||
// 对话框和加载状态
|
// 对话框和加载状态
|
||||||
loading: false, // 登录按钮加载状态
|
loading: false, // 登录按钮加载状态
|
||||||
forgotPasswordVisible: false, // 忘记密码弹窗显示状态
|
forgotPasswordVisible: false, // 忘记密码弹窗显示状态
|
||||||
|
loginMode: 'password', // 登录模式:password-账号密码,mobile-手机号验证码
|
||||||
|
|
||||||
// 登录表单数据
|
// 登录表单数据
|
||||||
loginForm: {
|
loginForm: {
|
||||||
@ -247,6 +265,19 @@ export default {
|
|||||||
// 检查当前域名是否为ncmatch.cn
|
// 检查当前域名是否为ncmatch.cn
|
||||||
isNcmatchDomain() {
|
isNcmatchDomain() {
|
||||||
return window.location.hostname.includes('ncmatch.cn');
|
return window.location.hostname.includes('ncmatch.cn');
|
||||||
|
},
|
||||||
|
activeRules() {
|
||||||
|
if (this.loginMode === 'mobile') {
|
||||||
|
return {
|
||||||
|
mobile: this.rules.mobile,
|
||||||
|
vcode: this.rules.vcode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
username: this.rules.username,
|
||||||
|
password: this.rules.password
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -693,6 +724,16 @@ export default {
|
|||||||
console.log(tab, event);
|
console.log(tab, event);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 切换登录方式,并清理上一种登录方式的校验提示。
|
||||||
|
switchLoginMode(mode) {
|
||||||
|
if (this.loginMode === mode) return;
|
||||||
|
this.loginMode = mode;
|
||||||
|
this.capsTooltip = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.loginForm && this.$refs.loginForm.clearValidate();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// 检查大写锁定状态
|
// 检查大写锁定状态
|
||||||
checkCapslock(e) {
|
checkCapslock(e) {
|
||||||
const { key } = e;
|
const { key } = e;
|
||||||
@ -703,15 +744,7 @@ export default {
|
|||||||
handleLogin(formName) {
|
handleLogin(formName) {
|
||||||
this.$refs[formName].validate((valid) => {
|
this.$refs[formName].validate((valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
const loginParams = {
|
const loginParams = this.buildLoginParams()
|
||||||
username: this.loginForm.username,
|
|
||||||
domain_name: this.photosUrl?.domain_name ? this.photosUrl.domain_name : '',
|
|
||||||
password: this.passwordEncryption(this.loginForm.password),
|
|
||||||
mobile: this.loginForm.mobile,
|
|
||||||
vcode: this.loginForm.vcode,
|
|
||||||
codeid: this.loginForm.codeid,
|
|
||||||
wechat_openid: this.wechat_openid
|
|
||||||
}
|
|
||||||
|
|
||||||
logintypeAPI(loginParams).then(res => {
|
logintypeAPI(loginParams).then(res => {
|
||||||
if (res.status == true) {
|
if (res.status == true) {
|
||||||
@ -803,16 +836,38 @@ export default {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.$message({
|
this.$message({
|
||||||
message: res.msg,
|
message: "请完善登录信息",
|
||||||
|
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
this.$refs.loginForm.resetFields();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 根据当前登录模式生成提交参数,避免把另一种模式的空字段带给后端。
|
||||||
|
buildLoginParams() {
|
||||||
|
const commonParams = {
|
||||||
|
domain_name: this.photosUrl?.domain_name ? this.photosUrl.domain_name : '',
|
||||||
|
wechat_openid: this.wechat_openid
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.loginMode === 'mobile') {
|
||||||
|
return {
|
||||||
|
...commonParams,
|
||||||
|
mobile: this.loginForm.mobile,
|
||||||
|
vcode: this.loginForm.vcode,
|
||||||
|
codeid: this.loginForm.codeid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...commonParams,
|
||||||
|
username: this.loginForm.username,
|
||||||
|
password: this.passwordEncryption(this.loginForm.password)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
// 显示/隐藏密码
|
// 显示/隐藏密码
|
||||||
showPwd() {
|
showPwd() {
|
||||||
if (this.passwordType === "password") {
|
if (this.passwordType === "password") {
|
||||||
@ -838,7 +893,9 @@ export default {
|
|||||||
$cursor: black;
|
$cursor: black;
|
||||||
$light_gray: #eee;
|
$light_gray: #eee;
|
||||||
$dark_gray: #889aa4;
|
$dark_gray: #889aa4;
|
||||||
|
.el-form{
|
||||||
|
padding:20px 0;
|
||||||
|
}
|
||||||
.main-box {
|
.main-box {
|
||||||
background: url('/static/img/banner.0798e703.png') no-repeat center center;
|
background: url('/static/img/banner.0798e703.png') no-repeat center center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
@ -886,24 +943,60 @@ $dark_gray: #889aa4;
|
|||||||
|
|
||||||
/* 统一登录表单容器 */
|
/* 统一登录表单容器 */
|
||||||
.login-form {
|
.login-form {
|
||||||
padding: 40px 50px;
|
width: 420px;
|
||||||
|
padding: 24px 60px 22px;
|
||||||
mix-blend-mode: normal;
|
mix-blend-mode: normal;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
box-shadow: 0 18px 46px rgba(34, 47, 96, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 登录标题统一间距 */
|
/* 登录标题统一间距 */
|
||||||
.login-text {
|
.login-text {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 12px;
|
||||||
font-family: PingFang SC;
|
font-family: PingFang SC;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-mode-tabs {
|
||||||
|
display: flex;
|
||||||
|
width: 300px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 16px 0;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #d8e6ff;
|
||||||
|
border-radius: 999px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
height: 36px;
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 999px;
|
||||||
|
transition: all 0.22s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #1e6fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 600;
|
||||||
|
background: linear-gradient(135deg, #1e6fff, #4aa3ff);
|
||||||
|
box-shadow: 0 8px 18px rgba(30, 111, 255, 0.28);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 统一输入框容器样式 */
|
/* 统一输入框容器样式 */
|
||||||
.user-input {
|
.user-input {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -925,7 +1018,7 @@ $dark_gray: #889aa4;
|
|||||||
|
|
||||||
/* 统一表单项目样式 + 边距 */
|
/* 统一表单项目样式 + 边距 */
|
||||||
::v-deep .el-form-item {
|
::v-deep .el-form-item {
|
||||||
margin-bottom: 24px !important;
|
margin-bottom: 15px !important;
|
||||||
/* 给错误提示预留空间 */
|
/* 给错误提示预留空间 */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1010,8 +1103,8 @@ $dark_gray: #889aa4;
|
|||||||
height: 42px;
|
height: 42px;
|
||||||
line-height: 42px;
|
line-height: 42px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-top: 10px !important;
|
margin-top: 0 !important;
|
||||||
margin-bottom: 20px !important;
|
margin-bottom: 12px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 底部按钮区域统一 */
|
/* 底部按钮区域统一 */
|
||||||
@ -1024,7 +1117,7 @@ $dark_gray: #889aa4;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.forgot-password {
|
.forgot-password {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 12px;
|
||||||
color: #409eff;
|
color: #409eff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|||||||
@ -182,6 +182,10 @@ print(response.json())`
|
|||||||
},
|
},
|
||||||
goBack() {
|
goBack() {
|
||||||
if (this.$route.query.from === 'tokenMarket') {
|
if (this.$route.query.from === 'tokenMarket') {
|
||||||
|
if (this.$route.query.single === '1') {
|
||||||
|
this.$router.push({ path: '/tokenMarket', query: { category: 'TOKEN市集', single: '1' } })
|
||||||
|
return
|
||||||
|
}
|
||||||
this.$router.push({ path: '/product', query: { category: 'TOKEN市集' } })
|
this.$router.push({ path: '/product', query: { category: 'TOKEN市集' } })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -272,6 +272,10 @@ export default {
|
|||||||
return value !== undefined && value !== null && value !== '' && value !== '-'
|
return value !== undefined && value !== null && value !== '' && value !== '-'
|
||||||
},
|
},
|
||||||
goTokenMarket() {
|
goTokenMarket() {
|
||||||
|
if (this.$route.query.single === '1') {
|
||||||
|
this.$router.push({ path: '/tokenMarket', query: { category: 'TOKEN市集', single: '1' } })
|
||||||
|
return
|
||||||
|
}
|
||||||
this.$router.push({ path: '/product', query: { category: 'TOKEN市集' } })
|
this.$router.push({ path: '/product', query: { category: 'TOKEN市集' } })
|
||||||
},
|
},
|
||||||
goBack() {
|
goBack() {
|
||||||
@ -286,7 +290,8 @@ export default {
|
|||||||
id: this.$route.query.id,
|
id: this.$route.query.id,
|
||||||
model_id: this.$route.query.model_id || this.$route.query.id,
|
model_id: this.$route.query.model_id || this.$route.query.id,
|
||||||
from: 'tokenMarket',
|
from: 'tokenMarket',
|
||||||
category: 'TOKEN市集'
|
category: 'TOKEN市集',
|
||||||
|
single: this.$route.query.single
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
goApiDocument() {
|
goApiDocument() {
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="product-service-page">
|
<div class="product-service-page" :class="{ 'single-product-page': isSinglePageMode }">
|
||||||
|
<TopBox v-if="isSinglePageMode" class="single-page-topbox" />
|
||||||
|
|
||||||
<!-- 产品分类导航 -->
|
<!-- 产品分类导航 -->
|
||||||
<div class="category-nav">
|
<div v-if="!isSinglePageMode" class="category-nav">
|
||||||
<div v-for="category in panelData"
|
<div v-for="category in panelData"
|
||||||
:key="category.firTitle"
|
:key="category.firTitle"
|
||||||
class="nav-item"
|
class="nav-item"
|
||||||
@ -194,9 +195,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import { reqNavList, reqNewHomeSync, reqNewHomeFestival } from "@/api/newHome";
|
import { reqNavList, reqNewHomeSync, reqNewHomeFestival } from "@/api/newHome";
|
||||||
import { gotoYuanJingAPI } from '@/api/gotoYuanJing'
|
import { gotoYuanJingAPI } from '@/api/gotoYuanJing'
|
||||||
|
import TopBox from '@/views/homePage/components/topBox/index.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ProductServicePage",
|
name: "ProductServicePage",
|
||||||
|
components: {
|
||||||
|
TopBox
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
panelData: [],
|
panelData: [],
|
||||||
@ -216,6 +221,9 @@ export default {
|
|||||||
isTokenMarketActive() {
|
isTokenMarketActive() {
|
||||||
return this.isTokenMarketCategory(this.activeCategory);
|
return this.isTokenMarketCategory(this.activeCategory);
|
||||||
},
|
},
|
||||||
|
isSinglePageMode() {
|
||||||
|
return this.$route.path === '/tokenMarket' || this.$route.query.single === '1' || this.$route.query.single === 'true';
|
||||||
|
},
|
||||||
currentSubcategories() {
|
currentSubcategories() {
|
||||||
if (!this.activeCategory || !this.panelData.length) return [];
|
if (!this.activeCategory || !this.panelData.length) return [];
|
||||||
const category = this.panelData.find(item => item.firTitle === this.activeCategory);
|
const category = this.panelData.find(item => item.firTitle === this.activeCategory);
|
||||||
@ -356,7 +364,8 @@ export default {
|
|||||||
model_id: model.id,
|
model_id: model.id,
|
||||||
llmid: model.llmid || model.model_name || model.id,
|
llmid: model.llmid || model.model_name || model.id,
|
||||||
from: 'tokenMarket',
|
from: 'tokenMarket',
|
||||||
category: 'TOKEN市集'
|
category: 'TOKEN市集',
|
||||||
|
single: this.isSinglePageMode ? '1' : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -370,13 +379,18 @@ export default {
|
|||||||
id: model.id,
|
id: model.id,
|
||||||
model_id: model.id,
|
model_id: model.id,
|
||||||
from: 'tokenMarket',
|
from: 'tokenMarket',
|
||||||
category: 'TOKEN市集'
|
category: 'TOKEN市集',
|
||||||
|
single: this.isSinglePageMode ? '1' : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// TOKEN 市集体验按钮:进入模型体验页。
|
// TOKEN 市集体验按钮:进入模型体验页。
|
||||||
goModelExperience(model) {
|
goModelExperience(model) {
|
||||||
|
if (!this.loginState) {
|
||||||
|
this.$message.warning('请先登录再进行体验哦')
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.cacheTokenMarketModel(model);
|
this.cacheTokenMarketModel(model);
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'modelExperience',
|
name: 'modelExperience',
|
||||||
@ -548,7 +562,7 @@ export default {
|
|||||||
async goYuanjing() {
|
async goYuanjing() {
|
||||||
const userId = sessionStorage.getItem('userId')
|
const userId = sessionStorage.getItem('userId')
|
||||||
if (!userId || userId === 'null' || userId === '') {
|
if (!userId || userId === 'null' || userId === '') {
|
||||||
this.$message.warning('请先登录哦~')
|
window.open('https://ai.opencomputing.cn/', '_blank')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -778,6 +792,30 @@ export default {
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
min-height: calc(100vh - 100px);
|
min-height: calc(100vh - 100px);
|
||||||
|
|
||||||
|
&.single-product-page {
|
||||||
|
height: 100vh;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 28px;
|
||||||
|
background: linear-gradient(180deg, #f3f7ff 0%, #ffffff 100%);
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
.single-page-topbox {
|
||||||
|
width: 100vw;
|
||||||
|
margin: -28px calc(50% - 50vw) 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-content {
|
||||||
|
max-width: 1180px;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-content {
|
||||||
|
padding-bottom: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.category-nav {
|
.category-nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
|
|||||||
246
f/web-kboss/src/views/tokenUsage/index.vue
Normal file
246
f/web-kboss/src/views/tokenUsage/index.vue
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
<template>
|
||||||
|
<div class="token-usage-page">
|
||||||
|
<div class="usage-shell">
|
||||||
|
<div class="page-header">
|
||||||
|
<div>
|
||||||
|
<div class="title-line">
|
||||||
|
<span class="title-icon">
|
||||||
|
<i class="el-icon-data-line"></i>
|
||||||
|
</span>
|
||||||
|
<h2>Token用量</h2>
|
||||||
|
</div>
|
||||||
|
<p>查看模型调用 Token 消耗、调用次数和费用趋势。</p>
|
||||||
|
</div>
|
||||||
|
<el-button size="small" icon="el-icon-refresh" class="refresh-btn">刷新</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-row">
|
||||||
|
<div v-for="item in statCards" :key="item.label" class="stat-card" :class="item.type">
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
<strong>{{ item.value }}</strong>
|
||||||
|
<em>{{ item.desc }}</em>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-card">
|
||||||
|
<div class="card-header">
|
||||||
|
<div>
|
||||||
|
<h3>用量明细</h3>
|
||||||
|
<p>按模型维度统计 Token 输入、输出与费用。</p>
|
||||||
|
</div>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
type="daterange"
|
||||||
|
size="small"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="usageList" class="usage-table" style="width: 100%">
|
||||||
|
<el-table-column prop="modelName" label="模型名称" min-width="180"></el-table-column>
|
||||||
|
<el-table-column prop="calls" label="调用次数" width="120"></el-table-column>
|
||||||
|
<el-table-column prop="inputTokens" label="输入Token" min-width="140"></el-table-column>
|
||||||
|
<el-table-column prop="outputTokens" label="输出Token" min-width="140"></el-table-column>
|
||||||
|
<el-table-column prop="totalTokens" label="总Token" min-width="140"></el-table-column>
|
||||||
|
<el-table-column prop="cost" label="预估费用" width="120"></el-table-column>
|
||||||
|
<el-table-column prop="updatedAt" label="更新时间" min-width="170"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'TokenUsage',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dateRange: [],
|
||||||
|
statCards: [
|
||||||
|
{ label: '总消耗 Token', value: '1,286,400', desc: '较昨日 +12.6%', type: 'primary' },
|
||||||
|
{ label: '调用次数', value: '3,482', desc: '今日累计调用', type: 'success' },
|
||||||
|
{ label: '预估费用', value: '¥ 128.64', desc: '按当前单价估算', type: 'warning' },
|
||||||
|
{ label: '活跃模型', value: '8', desc: '最近 7 天有调用', type: 'purple' }
|
||||||
|
],
|
||||||
|
usageList: [
|
||||||
|
{
|
||||||
|
modelName: 'DeepSeek-V4',
|
||||||
|
calls: 1260,
|
||||||
|
inputTokens: '420,000',
|
||||||
|
outputTokens: '318,000',
|
||||||
|
totalTokens: '738,000',
|
||||||
|
cost: '¥ 73.80',
|
||||||
|
updatedAt: '2026-05-25 10:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelName: '通义千问-Plus',
|
||||||
|
calls: 856,
|
||||||
|
inputTokens: '210,400',
|
||||||
|
outputTokens: '156,000',
|
||||||
|
totalTokens: '366,400',
|
||||||
|
cost: '¥ 38.20',
|
||||||
|
updatedAt: '2026-05-25 09:42:16'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelName: '腾讯混元-Pro',
|
||||||
|
calls: 342,
|
||||||
|
inputTokens: '102,000',
|
||||||
|
outputTokens: '80,000',
|
||||||
|
totalTokens: '182,000',
|
||||||
|
cost: '¥ 16.64',
|
||||||
|
updatedAt: '2026-05-25 09:18:31'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.token-usage-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 24px;
|
||||||
|
background: linear-gradient(180deg, #f3f7ff 0%, #f7f9fc 48%, #ffffff 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-shell {
|
||||||
|
min-height: calc(100vh - 48px);
|
||||||
|
padding: 24px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #edf1f7;
|
||||||
|
border-radius: 18px;
|
||||||
|
box-shadow: 0 12px 30px rgba(31, 45, 61, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header,
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 0;
|
||||||
|
color: #1f2d3d;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
color: #409eff;
|
||||||
|
font-size: 18px;
|
||||||
|
background: #eef5ff;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header p,
|
||||||
|
.card-header p {
|
||||||
|
margin: 0;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-btn {
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid #edf1f7;
|
||||||
|
background: #ffffff;
|
||||||
|
|
||||||
|
span,
|
||||||
|
em {
|
||||||
|
display: block;
|
||||||
|
color: #909399;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
display: block;
|
||||||
|
margin: 10px 0 8px;
|
||||||
|
color: #1f2d3d;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background: #eef5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background: #f0fdf4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
background: #fff7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.purple {
|
||||||
|
background: #f5f3ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card {
|
||||||
|
padding: 22px;
|
||||||
|
border: 1px solid #edf1f7;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
color: #1f2d3d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-table {
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.stat-row {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.stat-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header,
|
||||||
|
.card-header {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user