updata #60

Merged
charles merged 1 commits from main into prod 2026-01-14 09:34:07 +08:00
5 changed files with 522 additions and 11 deletions

View File

@ -0,0 +1,20 @@
import request from '@/utils/request'
export function getListAPI(id) {
return request({
url: `/product/get_zhipu_0112_product.dspy`,
method: 'get',
params: {
userid: id
}
})
}
// 详情
export function getDetailAPI(keyword) {
return request({
url: `/product/get_zhipu_0112_product_detail.dspy`,
method: 'get',
params: {
keyword
}
})
}

View File

@ -467,10 +467,20 @@ export const asyncRoutes = [
meta: { title: "百度订单", fullPath: "/customer/orderManagement", noCache: true, }, meta: { title: "百度订单", fullPath: "/customer/orderManagement", noCache: true, },
}, },
{ {
path: "orderManagement", path: "HistoricalOrders",
component: () => import("@/views/customer/orderManagement/HistoricalOrders/index.vue"), component: () => import("@/views/customer/orderManagement/HistoricalOrders/index.vue"),
name: "OrderManagement", name: "HistoricalOrders",
meta: { title: "阿里云订单", fullPath: "/customer/orderManagement", noCache: true, }, meta: { title: "历史订单", fullPath: "/customer/orderManagement", noCache: true, requireUser: 'ZhipuHZ' },
requireUser: 'ZhipuHZ'
},
// 订单详情
{
path: "orderDetails",
component: () => import("@/views/customer/orderManagement/orderDetails/index.vue"),
name: "orderDetails",
meta: { title: "订单详情", fullPath: "/customer/orderManagement", noCache: true, requireUser: 'ZhipuHZ' },
hidden: true,
requireUser: 'ZhipuHZ'
} }
] ]
@ -901,10 +911,6 @@ export const asyncRoutes = [
}, },
{
},
// 退订管理 - 变为一级菜单(包含子菜单) // 退订管理 - 变为一级菜单(包含子菜单)
{ {
path: "/unsubscribeManagement", path: "/unsubscribeManagement",

View File

@ -1,3 +1,4 @@
// permission.js - 修改后的完整代码
import { asyncRoutes, constantRoutes } from "@/router"; import { asyncRoutes, constantRoutes } from "@/router";
// 获取用户代理字符串 // 获取用户代理字符串
@ -235,6 +236,10 @@ const actions = {
const userRoles = rootState.user.roles || JSON.parse(sessionStorage.getItem('roles') || '[]'); const userRoles = rootState.user.roles || JSON.parse(sessionStorage.getItem('roles') || '[]');
console.log("用户角色:", userRoles); console.log("用户角色:", userRoles);
// 获取用户名
const username = params.user || rootState.user.user || '';
console.log("当前用户名:", username, "检查是否是ZhipuHZ:", username === 'ZhipuHZ');
console.log("用户类型:", userType, "orgType:", orgType); console.log("用户类型:", userType, "orgType:", orgType);
// 确定设备类型 // 确定设备类型
@ -296,7 +301,55 @@ const actions = {
console.log("添加用户特定路由后的accessedRoutes:", accessedRoutes); console.log("添加用户特定路由后的accessedRoutes:", accessedRoutes);
} }
console.log("ACTION generateRoutes - calculated accessedRoutes:", accessedRoutes); // ========== 暴力过滤:直接修改 accessedRoutes ==========
// 遍历所有路由,找到 /orderManagement 路由,然后过滤它的子路由
accessedRoutes = accessedRoutes.map(route => {
if (route.path === "/orderManagement") {
console.log("找到订单管理路由,准备过滤子路由,用户名:", username);
// 创建路由副本
const newRoute = { ...route };
if (newRoute.children) {
// 如果不是 ZhipuHZ 用户,过滤掉 HistoricalOrders 和 orderDetails 路由
if (username !== 'ZhipuHZ') {
console.log(`用户 ${username} 不是 ZhipuHZ过滤订单管理子路由`);
newRoute.children = newRoute.children.filter(child =>
child.path !== 'HistoricalOrders' && child.path !== 'orderDetails'
);
console.log(`过滤后子路由:`, newRoute.children.map(c => c.path));
} else {
console.log(`用户 ${username} 是 ZhipuHZ保留所有子路由`);
}
}
return newRoute;
}
// 对于其他路由,保持原样
return route;
});
// 再次检查,确保没有遗漏的任何 orderManagement 路由
accessedRoutes.forEach(route => {
if (route.children) {
route.children = route.children.filter(child => {
// 如果子路由是 orderManagement也需要处理
if (child.path === "/orderManagement") {
console.log("在子路由中找到订单管理路由,准备过滤,用户名:", username);
if (child.children && username !== 'ZhipuHZ') {
child.children = child.children.filter(grandChild =>
grandChild.path !== 'HistoricalOrders' && grandChild.path !== 'orderDetails'
);
}
}
return true;
});
}
});
console.log("ACTION generateRoutes - 最终 calculated accessedRoutes:", accessedRoutes);
commit("SET_ROUTES", accessedRoutes); commit("SET_ROUTES", accessedRoutes);
resolve(accessedRoutes); resolve(accessedRoutes);

View File

@ -1,15 +1,116 @@
<template> <template>
<div> <div>
<el-card>
<el-table :data="orderList" style="width: 100%" border stripe v-loading="loading">
<!-- 产品名称 -->
<el-table-column prop="name" label="产品名称" min-width="250"></el-table-column>
<!-- 单价 -->
<el-table-column prop="price" label="单价" width="150"></el-table-column>
<!-- 数量 -->
<el-table-column prop="amount" label="数量" width="100"></el-table-column>
<!-- 月价格 -->
<el-table-column prop="price_month" label="月价格" width="150"></el-table-column>
<!-- 总价 -->
<el-table-column prop="total_price" label="总价" width="150"></el-table-column>
<!-- 硬件配置 -->
<el-table-column label="硬件配置" min-width="250">
<template slot-scope="scope">
<div v-if="scope.row.hardware">
<div v-for="(item, index) in scope.row.hardware" :key="index" style="margin: 2px 0;">
{{ item.name }}: {{ item.value }}
</div>
</div>
<div v-else>-</div>
</template>
</el-table-column>
<!-- 操作详情 -->
<el-table-column label="操作" width="150">
<template slot-scope="scope">
<!-- 根据产品名称判断路由 -->
<el-button
type="primary"
size="mini"
@click="goToDetail(scope.row)"
>
详情
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div> </div>
</template> </template>
<script> <script>
export default { import { getListAPI } from '@/api/HistoricalOrders';
export default {
data() {
return {
orderList: [],
loading: false,
}
},
created() {
this.getList();
},
methods: {
async getList() {
this.loading = true;
try {
const res = await getListAPI();
if (res.status) {
this.orderList = res.data;
} else {
this.$message.error(res.msg || '获取订单列表失败');
}
} catch (error) {
this.$message.error('获取订单列表失败');
} finally {
this.loading = false;
}
},
//
goToDetail(row) {
console.log(row);
// amount
if (row.amount === 96) {
this.$router.push({
path: '/orderManagement/orderDetails',
query: {
keyword: 'ygpu'
}
});
} else if (row.amount === 72) {
// keyword: hgpu
this.$router.push({
path: '/orderManagement/orderDetails',
query: {
keyword: 'hgpu'
}
});
} else {
//
this.$message.warning('该订单暂无详情');
}
}
} }
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.top {
margin-bottom: 20px;
.top-tit {
font-size: 20px;
font-weight: bold;
color: #333;
}
}
.el-card {
margin: 0 20px;
}
</style> </style>

View File

@ -0,0 +1,331 @@
<template>
<div>
<!-- 完善头部 -->
<div class="top">
<h1 class="top-title">服务器监控面板</h1>
<p class="top-desc">实时监控168台服务器状态</p>
</div>
<!-- 仅点击/回车触发搜索 -->
<div class="search">
<el-input
v-model="searchServerId"
placeholder="请输入服务器ID搜索示例CZ-YGPU-11"
class="search-input"
clearable
@keyup.enter="handleSearch"
@clear="handleClear"
></el-input>
<el-button type="primary" class="search-btn" @click="handleSearch">
搜索
</el-button>
</div>
<el-card>
<div class="card-box">
<div class="card-item">
<div class="item-title">在线服务器</div>
<div class="item-text">0</div>
</div>
<div class="card-item">
<div class="item-title">离线服务器</div>
<div class="item-text">0</div>
</div>
<div class="card-item">
<div class="item-title">告警服务器</div>
<div class="item-text">0</div>
</div>
<div class="card-item">
<div class="item-title">过期服务器</div>
<div class="item-text">168</div>
</div>
</div>
</el-card>
<div class="content">
<el-card>
<div class="card-top">
<span v-if="keyword === 'ygpu'">云算力调度系统服务算力运维服务型GPU ({{ filteredList.length }})</span>
<span v-else>高性能图形渲染服务并行数据处理服务型GPU ({{ filteredList.length }})</span>
</div>
<!-- 列表遍历手动筛选后的列表 -->
<div class="list">
<div class="list-item" v-for="server in filteredList" :key="server.device_id">
<div class="list-top">
<div class="name">{{ server.device_id }}</div>
</div>
<div class="base-info">
<div class="info-item">内网IP: {{ server.internal_ip }}</div>
<div class="info-item">公网IP: {{ server.public_ip }}</div>
<div class="info-item">设备周期: {{ server.service_period.start_date }} {{ server.service_period.end_date }}</div>
</div>
<div class="config-module">
<div class="module-title">硬件配置</div>
<div class="module-content">
<div class="content-item">GPU型号: {{ server.hardware_config.gpu.model }}</div>
<div class="content-item">显存: {{ server.hardware_config.gpu.memory }}</div>
<div class="content-item">CPU: {{ server.hardware_config.cpu }}</div>
<div class="content-item">内存: {{ server.hardware_config.ram }}</div>
<div class="content-item">存储: {{ server.hardware_config.storage }}</div>
</div>
</div>
<div class="config-module">
<div class="module-title">网络配置</div>
<div class="module-content">
<div class="content-item">带宽: {{ server.network_config.bandwidth }}</div>
</div>
</div>
<div class="config-module">
<div class="module-title">服务等级</div>
<div class="module-content">
<div class="content-item">SLA保障: {{ server.service_level.sla }}</div>
<div class="content-item">最大并发数: {{ server.service_level.max_concurrent_connections }}</div>
<div class="content-item">最大负载: {{ server.service_level.max_load_threshold }}</div>
<div class="content-item">计费方式: {{ formatBillingMethod(server.service_level.billing_method) }}</div>
</div>
</div>
<div class="config-module">
<div class="module-title">历史数据</div>
<div class="module-content">
<div class="content-item">运行率: {{ server.historical_stats.uptime_rate }}</div>
<div class="content-item">平均负载: {{ server.historical_stats.average_load }}</div>
<div class="content-item">峰值负载: {{ server.historical_stats.peak_load }}</div>
<div class="content-item">总使用时长: {{ server.historical_stats.total_usage_hours }}小时</div>
</div>
</div>
<div class="expired-tag">租用过期</div>
</div>
</div>
</el-card>
</div>
</div>
</template>
<script>
import { getDetailAPI } from '@/api/HistoricalOrders'
export default {
data() {
return {
serverList: [], //
filteredList: [], //
keyword: '',
searchServerId: '' //
}
},
created() {
this.keyword = this.$route.query.keyword
this.getDetail()
},
methods: {
async getDetail() {
console.log(this.keyword)
const res = await getDetailAPI(this.keyword)
if (res.status == true) {
this.serverList = res.data.servers
this.filteredList = res.data.servers //
console.log(res.data.servers)
}
},
formatBillingMethod(method) {
const billingMap = {
'monthly': '按月计费',
'hourly': '按小时计费',
'yearly': '按年计费'
}
return billingMap[method] || method
},
calculateAvgUptime() {
if (this.filteredList.length === 0) return 0
const total = this.filteredList.reduce((sum, server) => {
const uptime = parseFloat(server.historical_stats.uptime_rate)
return sum + (isNaN(uptime) ? 0 : uptime)
}, 0)
return (total / this.filteredList.length).toFixed(2)
},
calculateAvgLoad() {
if (this.filteredList.length === 0) return 0
const total = this.filteredList.reduce((sum, server) => {
const load = parseFloat(server.historical_stats.average_load)
return sum + (isNaN(load) ? 0 : load)
}, 0)
return (total / this.filteredList.length).toFixed(2)
},
// /
handleSearch() {
const searchVal = this.searchServerId.trim()
if (!searchVal) {
this.filteredList = this.serverList //
return
}
//
this.filteredList = this.serverList.filter(server =>
server.device_id && server.device_id.includes(searchVal)
)
},
//
handleClear() {
this.filteredList = this.serverList
}
}
}
</script>
<style lang="less" scoped>
/* 头部样式 */
.top {
text-align: center;
padding: 20px 0;
margin-bottom: 20px;
.top-title {
font-size: 28px;
color: #333;
font-weight: 700;
margin: 0 0 8px 0;
}
.top-desc {
font-size: 16px;
color: #666;
margin: 0;
}
}
/* 搜索样式 */
.search {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
gap: 12px;
.search-input {
width: 360px;
}
.search-btn {
height: 40px;
}
}
.card-box {
width: 100%;
display: flex;
justify-content: space-around;
flex-wrap: wrap;
align-items: center;
.card-item {
display: flex;
flex-direction: column;
align-items: center;
.item-title {
font-size: 20px;
color: #333;
margin-bottom: 10px;
font-weight: bold;
}
.item-text {
font-size: 20px;
font-weight: bold;
color: #000;
}
}
}
.content {
margin-top: 20px;
.card-top {
font-size: 24px;
color: #333;
font-weight: bold;
width: 100%;
padding-bottom: 20px;
border-bottom: 2px solid #eee;
}
}
/* 列表样式 */
.list {
width: 100%;
padding: 10px 0;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.list-item {
background-color: #ffffff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border-radius: 8px;
border: 1px solid #eee;
padding: 16px;
color: #333;
margin-bottom: 12px;
}
.list-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
.name {
font-size: 18px;
font-weight: 700;
color: #000;
}
}
.base-info {
.info-item {
font-size: 14px;
margin-bottom: 8px;
color: #666;
}
}
.config-module {
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid #eee;
.module-title {
font-size: 16px;
font-weight: 600;
color: #ff4d4f;
margin-bottom: 8px;
}
.module-content {
.content-item {
font-size: 14px;
margin-bottom: 6px;
color: #333;
}
}
}
.expired-tag {
margin-top: 12px;
font-size: 14px;
font-weight: 600;
color: #ff9c07;
}
</style>