Compare commits
2 Commits
2d0d6eb59b
...
9e6da05991
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e6da05991 | ||
|
|
f6ece66b1a |
2
.env.dev
2
.env.dev
@ -2,5 +2,5 @@
|
|||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
|
||||||
# api
|
# api
|
||||||
VITE_API_BASIC="http://10.10.1.30:8888"
|
VITE_API_BASIC="http://10.10.1.11:8888"
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>GxDL算力云</title>
|
<title>FAST亼算云</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@ -52,3 +52,8 @@ export const exchangePoint=(params:any)=>request.put('/v1/balance/exchange_point
|
|||||||
//创建实例
|
//创建实例
|
||||||
export const createHost=(params:any)=>request.post('/v1/host/host_case',params)
|
export const createHost=(params:any)=>request.post('/v1/host/host_case',params)
|
||||||
|
|
||||||
|
//释放实例
|
||||||
|
export const releaseCase=(caseId:string)=>request.post(`/v1/host_case/release_case/${caseId}`)
|
||||||
|
//重置实例
|
||||||
|
export const reStartCase=(caseId:string)=>request.post(`/v1/host_case/restart_case/${caseId}`)
|
||||||
|
|
||||||
|
|||||||
7
src/utils/debounce.js
Normal file
7
src/utils/debounce.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export function debounce(func, delay = 500) {
|
||||||
|
let timer;
|
||||||
|
return function (...args) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => func.apply(this, args), delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
15
src/utils/dict.ts
Normal file
15
src/utils/dict.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export const payTypeDic = new Map([
|
||||||
|
["PayOnTime", "按量计费"],
|
||||||
|
["PayOnDay", "包日"],
|
||||||
|
["PayOnWeek", "包周"],
|
||||||
|
["PayOnMonth", "包月"],
|
||||||
|
["PayOnYear", "包年"],
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const instanceStatus = new Map([
|
||||||
|
["RUNNING", { text: "运行中", color: "#67C23A" }],
|
||||||
|
["STOPPED", { text: "已停止", color: "#909399" }],
|
||||||
|
["CREATING", { text: "创建中", color: "#E6A23C" }],
|
||||||
|
["RESTARTING", { text: "重启中", color: "#E6A23C" }],
|
||||||
|
["RELEASED", { text: "已释放", color: "#F56C6C" }],
|
||||||
|
]);
|
||||||
@ -199,6 +199,7 @@ import {
|
|||||||
} from '@ant-design/icons-vue'
|
} from '@ant-design/icons-vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
|
import { debounce } from '@/utils/debounce.js'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
// 实例卡片的开关状态
|
// 实例卡片的开关状态
|
||||||
const switch1 = ref(true)
|
const switch1 = ref(true)
|
||||||
@ -246,10 +247,14 @@ const fetchUserAssets = () => {
|
|||||||
// 生成邀请链接
|
// 生成邀请链接
|
||||||
const generateInviteLink = () => {
|
const generateInviteLink = () => {
|
||||||
// 这里应该是调用API生成邀请链接的逻辑
|
// 这里应该是调用API生成邀请链接的逻辑
|
||||||
message.success('邀请链接生成成功!')
|
debouncedSubmit()
|
||||||
// 实际开发中应该调用API,然后显示生成的链接
|
// 实际开发中应该调用API,然后显示生成的链接
|
||||||
// const link = await api.generateInviteLink()
|
// const link = await api.generateInviteLink()
|
||||||
}
|
}
|
||||||
|
const debouncedSubmit = debounce(() => {
|
||||||
|
message.success('邀请链接生成成功!')
|
||||||
|
}, 800)
|
||||||
|
|
||||||
|
|
||||||
// 新增:跳转到活动规则页面
|
// 新增:跳转到活动规则页面
|
||||||
const goToRules = () => {
|
const goToRules = () => {
|
||||||
@ -405,8 +410,8 @@ const goToInvoice = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.invite-actions {
|
.invite-actions {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.generate-btn {
|
.generate-btn {
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<span class="page-title">容器实例</span>
|
<span class="page-title">容器实例</span>
|
||||||
<span class="warning-tip">
|
<span class="warning-tip">
|
||||||
<exclamation-circle-outlined class="warning-icon" />
|
<!-- <exclamation-circle-outlined class="warning-icon" /> -->
|
||||||
实例连续关机15天会释放实例,实例释放会导致数据清空且不可恢复,释放前实例数据仍在。
|
<!-- 实例连续关机15天会释放实例,实例释放会导致数据清空且不可恢复,释放前实例数据仍在。 -->
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -23,10 +23,8 @@
|
|||||||
<div class="header-filter">
|
<div class="header-filter">
|
||||||
<a-select v-model:value="filterStatus" placeholder="筛选状态" style="width: 160px;" size="large"
|
<a-select v-model:value="filterStatus" placeholder="筛选状态" style="width: 160px;" size="large"
|
||||||
@change="handleSearch">
|
@change="handleSearch">
|
||||||
<a-select-option value="all">全部状态</a-select-option>
|
<a-select-option value="">全部状态</a-select-option>
|
||||||
<a-select-option value="running">运行中</a-select-option>
|
<a-select-option v-for="[key, config] in instanceStatus" :value="key">{{ config.text }}</a-select-option>
|
||||||
<a-select-option value="stopped">已停止</a-select-option>
|
|
||||||
<a-select-option value="error">异常</a-select-option>
|
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-input-search v-model:value="searchKeyword" placeholder="搜索实例名称/ID" style="width: 240px; margin-left: 12px;"
|
<a-input-search v-model:value="searchKeyword" placeholder="搜索实例名称/ID" style="width: 240px; margin-left: 12px;"
|
||||||
size="large" @search="handleSearch" />
|
size="large" @search="handleSearch" />
|
||||||
@ -41,9 +39,8 @@
|
|||||||
<!-- 状态列 -->
|
<!-- 状态列 -->
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'status'">
|
<template v-if="column.key === 'status'">
|
||||||
<span :class="`status-${record.status}`">
|
<span :style="{ color: instanceStatus.get(record.status)?.color }">{{ instanceStatus.get(record.status)?.text
|
||||||
{{ getStatusText(record.status) }}
|
}}</span>
|
||||||
</span>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 规格详情列 -->
|
<!-- 规格详情列 -->
|
||||||
@ -64,7 +61,11 @@
|
|||||||
{{ getHealthStatusText(record.health_status) }}
|
{{ getHealthStatusText(record.health_status) }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="column.key === 'price_type'">
|
||||||
|
<span>
|
||||||
|
{{ payTypeDic.get(record.price_type) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
<!-- 操作列 -->
|
<!-- 操作列 -->
|
||||||
<template v-else-if="column.key === 'actions'">
|
<template v-else-if="column.key === 'actions'">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
@ -147,14 +148,10 @@
|
|||||||
<p style="color: #666;">• 实例配置和网络设置保持不变</p>
|
<p style="color: #666;">• 实例配置和网络设置保持不变</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #okText>
|
|
||||||
<span style="color: #ff4d4f;">确认重置</span>
|
|
||||||
</template>
|
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<!-- 释放确认模态框 -->
|
<!-- 释放确认模态框 -->
|
||||||
<a-modal v-model:open="releaseModalVisible" title="确认释放" @ok="confirmRelease" @cancel="releaseModalVisible = false"
|
<a-modal v-model:open="releaseModalVisible" title="确认释放" @ok="confirmRelease" @cancel="releaseModalVisible = false" >
|
||||||
:ok-button-props="{ danger: true }">
|
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<exclamation-circle-outlined class="warning-icon"
|
<exclamation-circle-outlined class="warning-icon"
|
||||||
style="color: #ff4d4f; font-size: 24px; margin-right: 12px;" />
|
style="color: #ff4d4f; font-size: 24px; margin-right: 12px;" />
|
||||||
@ -173,15 +170,17 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, computed } from 'vue'
|
import { ref, onMounted, computed } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import {ExclamationCircleOutlined,ReloadOutlined,EyeOutlined,DownOutlined,RedoOutlined,DeleteOutlined} from '@ant-design/icons-vue'
|
import { ExclamationCircleOutlined, ReloadOutlined, EyeOutlined, DownOutlined, RedoOutlined, DeleteOutlined } from '@ant-design/icons-vue'
|
||||||
import type { TableColumnType } from 'ant-design-vue'
|
import type { TableColumnType } from 'ant-design-vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { hostCaseList } from '@/apis/admin'
|
import { hostCaseList } from '@/apis/admin'
|
||||||
|
import { releaseCase, reStartCase } from '@/apis/home'
|
||||||
import { get } from 'http'
|
import { get } from 'http'
|
||||||
|
import { payTypeDic, instanceStatus } from '@/utils/dict'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
// 状态管理
|
// 状态管理
|
||||||
const filterStatus = ref('all')
|
const filterStatus = ref('')
|
||||||
const searchKeyword = ref('')
|
const searchKeyword = ref('')
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const specModalVisible = ref(false)
|
const specModalVisible = ref(false)
|
||||||
@ -217,7 +216,6 @@ const columns = ref<TableColumnType[]>([
|
|||||||
{ title: '名称', dataIndex: 'name', key: 'name', width: 180 },
|
{ title: '名称', dataIndex: 'name', key: 'name', width: 180 },
|
||||||
{ title: '状态', dataIndex: 'status', key: 'status', width: 100 },
|
{ title: '状态', dataIndex: 'status', key: 'status', width: 100 },
|
||||||
{ title: '规格详情', dataIndex: 'spec', key: 'spec', width: 200 },
|
{ title: '规格详情', dataIndex: 'spec', key: 'spec', width: 200 },
|
||||||
{ title: '本地磁盘', dataIndex: 'system_disk', key: 'system_disk', width: 120 },
|
|
||||||
{ title: '健康状态', dataIndex: 'health_status', key: 'health_status', width: 120 },
|
{ title: '健康状态', dataIndex: 'health_status', key: 'health_status', width: 120 },
|
||||||
{ title: '付费方式', dataIndex: 'price_type', key: 'price_type', width: 120 },
|
{ title: '付费方式', dataIndex: 'price_type', key: 'price_type', width: 120 },
|
||||||
{ title: '释放时间', dataIndex: 'release_at', key: 'release_at', width: 120 },
|
{ title: '释放时间', dataIndex: 'release_at', key: 'release_at', width: 120 },
|
||||||
@ -232,7 +230,9 @@ const getDataList = async () => {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
const params = {
|
const params = {
|
||||||
page_num: paginationState.value.current,
|
page_num: paginationState.value.current,
|
||||||
page_size: paginationState.value.pageSize
|
page_size: paginationState.value.pageSize,
|
||||||
|
status: filterStatus.value,
|
||||||
|
name: searchKeyword.value
|
||||||
}
|
}
|
||||||
const res = await hostCaseList(params)
|
const res = await hostCaseList(params)
|
||||||
mockData.value = res.data
|
mockData.value = res.data
|
||||||
@ -247,23 +247,11 @@ const getDataList = async () => {
|
|||||||
getDataList()
|
getDataList()
|
||||||
|
|
||||||
|
|
||||||
// 状态文本映射
|
|
||||||
const getStatusText = (status: string) => {
|
|
||||||
const map: Record<string, any> = {
|
|
||||||
"RUNNING": '运行中',
|
|
||||||
"STOPPED": '已停止',
|
|
||||||
"CREATING": '创建中',
|
|
||||||
"RESTARTING": '重启中',
|
|
||||||
}
|
|
||||||
return map[status] || status
|
|
||||||
}
|
|
||||||
|
|
||||||
// 健康状态文本映射
|
// 健康状态文本映射
|
||||||
const getHealthStatusText = (status: string) => {
|
const getHealthStatusText = (status: string) => {
|
||||||
const map: Record<string, string> = {
|
const map: Record<string, string> = {
|
||||||
healthy: '健康',
|
Normal: '正常',
|
||||||
warning: '警告',
|
Abnormal: '异常'
|
||||||
error: '异常'
|
|
||||||
}
|
}
|
||||||
return map[status] || status
|
return map[status] || status
|
||||||
}
|
}
|
||||||
@ -325,32 +313,32 @@ const handleMenuClick = (key: string, record: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 确认重置
|
// 确认重置
|
||||||
const confirmReset = () => {
|
const confirmReset = async () => {
|
||||||
if (!currentInstance.value) return
|
if (!currentInstance.value) return
|
||||||
|
try {
|
||||||
message.loading({ content: '正在重置实例...', key: 'reset', duration: 0 })
|
const res = await reStartCase(currentInstance.value.id)
|
||||||
setTimeout(() => {
|
|
||||||
// 模拟重置操作
|
|
||||||
currentInstance.value.health_status = 'healthy'
|
|
||||||
message.success({ content: '实例重置成功', key: 'reset' })
|
message.success({ content: '实例重置成功', key: 'reset' })
|
||||||
resetModalVisible.value = false
|
resetModalVisible.value = false
|
||||||
}, 2000)
|
getDataList()
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error)
|
||||||
|
resetModalVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确认释放
|
// 确认释放
|
||||||
const confirmRelease = () => {
|
const confirmRelease = async () => {
|
||||||
if (!currentInstance.value) return
|
if (!currentInstance.value) return
|
||||||
|
try {
|
||||||
message.loading({ content: '正在释放实例...', key: 'release', duration: 0 })
|
const res = await releaseCase(currentInstance.value.id)
|
||||||
setTimeout(() => {
|
|
||||||
// 从数据中移除
|
|
||||||
const index = mockData.value.findIndex(item => item.id === currentInstance.value.id)
|
|
||||||
if (index !== -1) {
|
|
||||||
mockData.value.splice(index, 1)
|
|
||||||
}
|
|
||||||
message.success({ content: '实例释放成功', key: 'release' })
|
message.success({ content: '实例释放成功', key: 'release' })
|
||||||
releaseModalVisible.value = false
|
releaseModalVisible.value = false
|
||||||
}, 2000)
|
getDataList()
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error)
|
||||||
|
releaseModalVisible.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量续费
|
// 批量续费
|
||||||
@ -366,6 +354,7 @@ const refreshData = () => {
|
|||||||
// 搜索
|
// 搜索
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
paginationState.value.current = 1
|
paginationState.value.current = 1
|
||||||
|
getDataList()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页变化
|
// 分页变化
|
||||||
@ -379,7 +368,7 @@ const onTableChange = (pag: any) => {
|
|||||||
|
|
||||||
// 新建实例
|
// 新建实例
|
||||||
const handleRent = () => {
|
const handleRent = () => {
|
||||||
router.push('/layout/admin/instanceCreate')
|
router.push('/layout/market')
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@ -572,7 +561,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 健康状态样式
|
// 健康状态样式
|
||||||
.health-healthy {
|
.health-Normal {
|
||||||
color: #52c41a;
|
color: #52c41a;
|
||||||
background: #f6ffed;
|
background: #f6ffed;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
@ -581,16 +570,16 @@ onMounted(() => {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.health-warning {
|
// .health-Abnormal {
|
||||||
color: #faad14;
|
// color: #faad14;
|
||||||
background: #fffbe6;
|
// background: #fffbe6;
|
||||||
padding: 4px 8px;
|
// padding: 4px 8px;
|
||||||
border-radius: 4px;
|
// border-radius: 4px;
|
||||||
font-size: 12px;
|
// font-size: 12px;
|
||||||
display: inline-block;
|
// display: inline-block;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.health-error {
|
.health-Abnormal {
|
||||||
color: #ff4d4f;
|
color: #ff4d4f;
|
||||||
background: #fff2f0;
|
background: #fff2f0;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
|
|||||||
@ -15,12 +15,13 @@
|
|||||||
<a-card class="card" title="计费方式">
|
<a-card class="card" title="计费方式">
|
||||||
<div class="billing-content">
|
<div class="billing-content">
|
||||||
<a-radio-group v-model:value="billingType" button-style="solid" @change="handleBillingTypeChange">
|
<a-radio-group v-model:value="billingType" button-style="solid" @change="handleBillingTypeChange">
|
||||||
<a-radio-button value="payg">按量计费</a-radio-button>
|
<a-radio-button value="PayOnTime">按量计费</a-radio-button>
|
||||||
<a-radio-button value="daily">包日</a-radio-button>
|
<a-radio-button value="PayOnDay">包日</a-radio-button>
|
||||||
<a-radio-button value="weekly">包周</a-radio-button>
|
<a-radio-button value="PayOnWeek">包周</a-radio-button>
|
||||||
<a-radio-button value="monthly">包月</a-radio-button>
|
<a-radio-button value="PayOnMonth">包月</a-radio-button>
|
||||||
|
<a-radio-button value="PayOnYear">包年</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
<a href="/docs/price/" target="_blank" class="billing-link">计费规则</a>
|
<!-- <a href="/docs/price/" target="_blank" class="billing-link">计费规则</a> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="note">
|
<div class="note">
|
||||||
创建完主机后仍然可以转换计费方式。如选择按量计费,价格发生变动以实例开机时的价格为准
|
创建完主机后仍然可以转换计费方式。如选择按量计费,价格发生变动以实例开机时的价格为准
|
||||||
@ -38,7 +39,7 @@
|
|||||||
<a-radio-button v-for="region in regions" :key="region.value" :value="region.value">
|
<a-radio-button v-for="region in regions" :key="region.value" :value="region.value">
|
||||||
{{ region.label }}
|
{{ region.label }}
|
||||||
<a-tag v-if="region.tag" :color="region.tag.color" class="region-tag">{{ region.tag.text
|
<a-tag v-if="region.tag" :color="region.tag.color" class="region-tag">{{ region.tag.text
|
||||||
}}</a-tag>
|
}}</a-tag>
|
||||||
</a-radio-button>
|
</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</div>
|
</div>
|
||||||
@ -249,7 +250,7 @@
|
|||||||
<span class="original-total-price">¥{{ originalTotalPrice }}/{{ billingUnit }}</span>
|
<span class="original-total-price">¥{{ originalTotalPrice }}/{{ billingUnit }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a-button size="large" @click="handleCancel" class="cancel-btn">取消</a-button>
|
<a-button size="large" @click="handleCancel" class="cancel-btn">取消</a-button>
|
||||||
@ -320,7 +321,7 @@ interface Coupon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const billingType = ref('weekly')
|
const billingType = ref('PayOnTime')
|
||||||
const selectedRegion = ref('beijingDC2')
|
const selectedRegion = ref('beijingDC2')
|
||||||
const selectedZone = ref('')
|
const selectedZone = ref('')
|
||||||
const selectedGpuModels = ref<string[]>(['RTX 5090'])
|
const selectedGpuModels = ref<string[]>(['RTX 5090'])
|
||||||
@ -1041,173 +1042,174 @@ const showPriceBreakdown = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.footer-actions {
|
.footer-actions {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: linear-gradient(135deg, #ffffff 0%, #fafafa 100%);
|
background: linear-gradient(135deg, #ffffff 0%, #fafafa 100%);
|
||||||
padding: 20px 32px;
|
padding: 20px 32px;
|
||||||
border-top: 1px solid #e8e8e8;
|
border-top: 1px solid #e8e8e8;
|
||||||
box-shadow: 0 -2px 16px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 -2px 16px rgba(0, 0, 0, 0.08);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
margin: 0 -24px -24px;
|
margin: 0 -24px -24px;
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-summary {
|
.price-summary {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 32px;
|
gap: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-main {
|
.price-main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-label {
|
.total-label {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #666;
|
color: #666;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-display {
|
.price-display {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-price {
|
.total-price {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #f7412d;
|
color: #f7412d;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-unit {
|
.price-unit {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #999;
|
color: #999;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.original-price-container {
|
.original-price-container {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.original-total-price {
|
.original-total-price {
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
color: #999;
|
color: #999;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-breakdown {
|
.price-breakdown {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
background: #f8f9fa;
|
background: #f8f9fa;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid #e9ecef;
|
border: 1px solid #e9ecef;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breakdown-item {
|
.breakdown-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breakdown-label {
|
.breakdown-label {
|
||||||
color: #666;
|
color: #666;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breakdown-value {
|
.breakdown-value {
|
||||||
color: #333;
|
color: #333;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breakdown-value.discount {
|
.breakdown-value.discount {
|
||||||
color: #52c41a;
|
color: #52c41a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cancel-btn {
|
.cancel-btn {
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
border: 1px solid #d9d9d9;
|
border: 1px solid #d9d9d9;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cancel-btn:hover {
|
.cancel-btn:hover {
|
||||||
border-color: #1890ff;
|
border-color: #1890ff;
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-btn {
|
.create-btn {
|
||||||
padding: 0 32px;
|
padding: 0 32px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
|
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.3);
|
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.3);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-btn:hover {
|
.create-btn:hover {
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.4);
|
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-btn:disabled {
|
.create-btn:disabled {
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
color: #d9d9d9;
|
color: #d9d9d9;
|
||||||
transform: none;
|
transform: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 响应式设计 */
|
/* 响应式设计 */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.footer-actions {
|
.footer-actions {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
padding: 16px 20px;
|
padding: 16px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-summary {
|
.price-summary {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-breakdown {
|
.price-breakdown {
|
||||||
display: none; /* 移动端隐藏价格明细 */
|
display: none;
|
||||||
}
|
/* 移动端隐藏价格明细 */
|
||||||
|
}
|
||||||
.action-buttons {
|
|
||||||
width: 100%;
|
.action-buttons {
|
||||||
}
|
width: 100%;
|
||||||
|
}
|
||||||
.cancel-btn,
|
|
||||||
.create-btn {
|
.cancel-btn,
|
||||||
flex: 1;
|
.create-btn {
|
||||||
}
|
flex: 1;
|
||||||
|
}
|
||||||
.total-price {
|
|
||||||
font-size: 28px;
|
.total-price {
|
||||||
}
|
font-size: 28px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-unit {
|
.price-unit {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="gx_layout">
|
<div class="gx_layout">
|
||||||
<div :class="isHome ? 'gx_layout_header_home' : 'gx_layout_header_noHome'" class="gx_layout_header">
|
<div :class="isHome ? 'gx_layout_header_home' : 'gx_layout_header_noHome'" class="gx_layout_header">
|
||||||
<div class="logo">GxDL算力云</div>
|
<div class="logo">FAST亼算云</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 60px;width: 100%;"></div>
|
<div style="height: 60px;width: 100%;"></div>
|
||||||
<div class="gx_layout_content">
|
<div class="gx_layout_content">
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="one">
|
<div class="one">
|
||||||
<div v-for="item in oneList" style="display: flex;justify-content: center;align-items: center;cursor: pointer;"
|
<div v-for="item in oneList" style="display: flex;justify-content: center;align-items: center;cursor: pointer;"
|
||||||
@click="router.push(item.path)">
|
@click="router.push(item.link_url)">
|
||||||
<div style="margin-right: 10px;"><img :src="item.img" alt="" srcset="" width="50" height="50"></div>
|
<div style="margin-right: 10px;"><img :src="item.img" alt="" srcset="" width="50" height="50"></div>
|
||||||
<div>
|
<div>
|
||||||
<h3>{{ item.title }}</h3>
|
<h3>{{ item.title }}</h3>
|
||||||
@ -179,39 +179,39 @@ const fetchBannerList = async () => {
|
|||||||
};
|
};
|
||||||
// 请求轮播
|
// 请求轮播
|
||||||
const getOneList = async () => {
|
const getOneList = async () => {
|
||||||
try {
|
// try {
|
||||||
const res: any = await getApiOneList();
|
// const res: any = await getApiOneList();
|
||||||
// 处理返回的数据
|
// // 处理返回的数据
|
||||||
if (res && Array.isArray(res) && res.length > 0) {
|
// if (res && Array.isArray(res) && res.length > 0) {
|
||||||
oneList.value = res;
|
// oneList.value = res;
|
||||||
} else {
|
// } else {
|
||||||
// API返回空数据或无效数据,使用默认图片
|
// // API返回空数据或无效数据,使用默认图片
|
||||||
oneList.value = [{ id: 0, image_url: defaultBanner }];
|
// oneList.value = [{ id: 0, image_url: defaultBanner }];
|
||||||
}
|
// }
|
||||||
} catch (error: any) {
|
// } catch (error: any) {
|
||||||
console.error('轮播图请求失败:', error);
|
// console.error('轮播图请求失败:', error);
|
||||||
// 请求失败时使用默认图片
|
// 请求失败时使用默认图片
|
||||||
oneList.value = [
|
oneList.value = [
|
||||||
{
|
{
|
||||||
img: youhuiquan,
|
img: youhuiquan,
|
||||||
title: '算力免费领',
|
title: '算力免费领',
|
||||||
description: '完成认证/问卷获得算力券',
|
description: '完成认证/问卷获得算力券',
|
||||||
path: '/active/newUser'
|
link_url: '/active/newUser'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
img: yaoqinghaoyou,
|
img: yaoqinghaoyou,
|
||||||
title: '邀约优算友',
|
title: '邀约优算友',
|
||||||
description: '邀请好友可获得算力券',
|
description: '邀请好友可获得算力券',
|
||||||
path: '/active/invite'
|
link_url: '/active/invite'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
img: goumaijilu,
|
img: goumaijilu,
|
||||||
title: '开业重磅福利',
|
title: '开业重磅福利',
|
||||||
description: '购算力,享豪礼',
|
description: '购算力,享豪礼',
|
||||||
path: '/active/newUser'
|
link_url: '/active/newUser'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
// }
|
||||||
};
|
};
|
||||||
// 请求GPU列表
|
// 请求GPU列表
|
||||||
const getGPUList = async () => {
|
const getGPUList = async () => {
|
||||||
@ -231,17 +231,17 @@ const getAdvantageList = async () => {
|
|||||||
const res: any = await getAdvantage();
|
const res: any = await getAdvantage();
|
||||||
const list = res || [];
|
const list = res || [];
|
||||||
// 给每个 item 添加 img 字段
|
// 给每个 item 添加 img 字段
|
||||||
// list.forEach((item: any, index: number) => {
|
list.forEach((item: any, index: number) => {
|
||||||
// if (index === 0) {
|
if (index === 0) {
|
||||||
// item.img = one;
|
item.img = one;
|
||||||
// } else if (index === 1) {
|
} else if (index === 1) {
|
||||||
// item.img = two;
|
item.img = two;
|
||||||
// } else if (index === 2) {
|
} else if (index === 2) {
|
||||||
// item.img = three;
|
item.img = three;
|
||||||
// } else {
|
} else {
|
||||||
// item.img = firth;
|
item.img = firth;
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
|
|
||||||
advantageList.value = list;
|
advantageList.value = list;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@ -1,101 +1,122 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="instance-create-container">
|
<a-spin :spinning="spinning">
|
||||||
<!-- 服务器选择 -->
|
<div class="instance-create-container">
|
||||||
<a-card class="card select-server" title="服务器选择">
|
<!-- 服务器选择 -->
|
||||||
<div class="list-filter">
|
<a-card class="card select-server" title="服务器选择">
|
||||||
<div class="filter-item">
|
<div class="list-filter">
|
||||||
<span class="filter-label">实例名称:</span>
|
<div class="filter-item">
|
||||||
<div class="filter-content">
|
<span class="filter-label">实例名称:</span>
|
||||||
<a-input v-model:value="instanceName"></a-input>
|
<div class="filter-content">
|
||||||
|
<a-input v-model:value="instanceName"></a-input>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="filter-item">
|
||||||
<div class="filter-item">
|
<span class="filter-label">计费方式<span style="color: #ff4d4f;margin: 0 2px">*</span>:</span>
|
||||||
<span class="filter-label">计费方式<span style="color: #ff4d4f;margin: 0 2px">*</span>:</span>
|
<div class="filter-content">
|
||||||
<div class="filter-content">
|
<a-radio-group v-model:value="billingType" button-style="solid">
|
||||||
<a-radio-group v-model:value="billingType" button-style="solid">
|
<a-radio-button value="PayOnTime">按量计费</a-radio-button>
|
||||||
<a-radio-button value="PayOnTime">按量计费</a-radio-button>
|
<a-radio-button value="PayOnDay">包日</a-radio-button>
|
||||||
<a-radio-button value="PayOnDay">包日</a-radio-button>
|
<a-radio-button value="PayOnWeek">包周</a-radio-button>
|
||||||
<a-radio-button value="PayOnWeek">包周</a-radio-button>
|
<a-radio-button value="PayOnMonth">包月</a-radio-button>
|
||||||
<a-radio-button value="PayOnMonth">包月</a-radio-button>
|
<a-radio-button value="PayOnYear">包年</a-radio-button>
|
||||||
<a-radio-button value="PayOnYear">包年</a-radio-button>
|
|
||||||
</a-radio-group>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- GPU数量 -->
|
|
||||||
<div class="filter-item">
|
|
||||||
<span class="filter-label">GPU数量<span style="color: #ff4d4f;margin: 0 2px">*</span><span>:</span></span>
|
|
||||||
<div class="filter-content">
|
|
||||||
<a-radio-group v-model:value="gpuCount" button-style="solid">
|
|
||||||
<a-radio-button v-for="count in gpuCountOptions" :key="count" :value="count">
|
|
||||||
{{ count }}
|
|
||||||
</a-radio-button>
|
|
||||||
</a-radio-group>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="filter-item">
|
|
||||||
<span class="filter-label">镜像<span style="color: #ff4d4f;margin: 0 2px;">*</span>:</span>
|
|
||||||
<a-form layout="vertical">
|
|
||||||
<a-form-item class="image-type">
|
|
||||||
<a-radio-group v-model:value="imageType" button-style="solid"
|
|
||||||
@change="getServiceImages()">
|
|
||||||
<a-radio-button value="SYSTEM">基础镜像</a-radio-button>
|
|
||||||
<a-radio-button value="USER">我的镜像</a-radio-button>
|
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="filter-item" v-if="billingType != 'PayOnTime'">
|
||||||
|
<span class="filter-label">租用时长<span style="color: #ff4d4f;margin: 0 2px">*</span>:</span>
|
||||||
|
<div class="filter-content">
|
||||||
|
<a-select ref="select" v-model:value="priceCount" style="width: 300px">
|
||||||
|
<a-select-option v-for="item in priceCounts" :value="item.id">{{ item.label
|
||||||
|
}}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- GPU数量 -->
|
||||||
|
<div class="filter-item">
|
||||||
|
<span class="filter-label">GPU数量<span
|
||||||
|
style="color: #ff4d4f;margin: 0 2px">*</span><span>:</span></span>
|
||||||
|
<div class="filter-content">
|
||||||
|
<a-radio-group v-model:value="gpuCount" button-style="solid">
|
||||||
|
<a-radio-button v-for="count in gpuCountOptions" :key="count" :value="count">
|
||||||
|
{{ count }}
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</a-form-item>
|
<div class="filter-item">
|
||||||
|
<span class="filter-label">镜像<span style="color: #ff4d4f;margin: 0 2px;">*</span>:</span>
|
||||||
|
<a-form layout="vertical">
|
||||||
|
<a-form-item class="image-type">
|
||||||
|
<a-radio-group v-model:value="imageType" button-style="solid"
|
||||||
|
@change="getServiceImages()">
|
||||||
|
<a-radio-button value="SYSTEM">基础镜像</a-radio-button>
|
||||||
|
<a-radio-button value="USER">我的镜像</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<div class="basic-image" style="margin-top: 15px;">
|
|
||||||
<a-select ref="select" v-model:value="selectedImage" style="width: 300px"
|
<a-form-item>
|
||||||
@change="handleChange">
|
<div class="basic-image" style="margin-top: 15px;">
|
||||||
<a-select-option v-for="item in imageOptions" :value="item.id">{{ item.label
|
<a-select ref="select" v-model:value="selectedImage" style="width: 300px"
|
||||||
|
@change="handleChange">
|
||||||
|
<a-select-option v-for="item in imageOptions" :value="item.id">{{ item.label
|
||||||
}}</a-select-option>
|
}}</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 主机表格 -->
|
<!-- 主机表格 -->
|
||||||
<div class="machine-table">
|
<div class="machine-table">
|
||||||
<a-descriptions layout="vertical" bordered :column="8">
|
<a-descriptions layout="vertical" bordered :column="8">
|
||||||
|
|
||||||
<a-descriptions-item label="主机ID">{{ serviceInfo.id }}</a-descriptions-item>
|
<a-descriptions-item label="主机ID">{{ serviceInfo.id }}</a-descriptions-item>
|
||||||
<a-descriptions-item label="算力型号/显存">{{ serviceInfo.gpuType + '/' + serviceInfo.vram + 'GB'
|
<a-descriptions-item label="算力型号/显存">{{ serviceInfo.gpuType + '/' + serviceInfo.vram + 'GB'
|
||||||
}}</a-descriptions-item>
|
}}</a-descriptions-item>
|
||||||
<a-descriptions-item label="空闲GPU">{{ serviceInfo.gpuAvailableNum }}</a-descriptions-item>
|
<a-descriptions-item label="空闲GPU">{{ serviceInfo.gpuAvailableNum }}</a-descriptions-item>
|
||||||
<a-descriptions-item label="每GPU分配">
|
<a-descriptions-item label="每GPU分配">
|
||||||
<span>{{ 'CPU:' + serviceInfo.cpuNum + '核' }}</span><br>
|
<span>{{ 'CPU:' + serviceInfo.cpuNum + '核' }}</span><br>
|
||||||
<span>{{ '内存:' + serviceInfo.memory + "GB" }}</span>
|
<span>{{ '内存:' + serviceInfo.memory + "GB" }}</span>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="CPU型号">{{ serviceInfo.cpuType }}</a-descriptions-item>
|
<a-descriptions-item label="CPU型号">{{ serviceInfo.cpuType }}</a-descriptions-item>
|
||||||
<a-descriptions-item label="硬盘">
|
<a-descriptions-item label="硬盘">
|
||||||
<span>{{ '数据盘:' + serviceInfo.dataDisk + 'GB' }}</span><br>
|
<span>{{ '数据盘:' + serviceInfo.dataDisk + 'GB' }}</span><br>
|
||||||
<span>{{ '系统盘:' + serviceInfo.systemDisk + "GB" }}</span>
|
<span>{{ '系统盘:' + serviceInfo.systemDisk + "GB" }}</span>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="驱动/CUDA">{{ serviceInfo.gpuDriver + '/' + serviceInfo.cudaVersion
|
<a-descriptions-item label="驱动/CUDA">{{ serviceInfo.gpuDriver + '/' +
|
||||||
}}</a-descriptions-item>
|
serviceInfo.cudaVersion
|
||||||
<a-descriptions-item label="消耗算力点(单卡)">{{ serviceInfo.price }}</a-descriptions-item>
|
}}</a-descriptions-item>
|
||||||
</a-descriptions>
|
<a-descriptions-item label="消耗算力点(单卡)">{{ serviceInfo.price }}</a-descriptions-item>
|
||||||
</div>
|
</a-descriptions>
|
||||||
</a-card>
|
</div>
|
||||||
<!-- 优惠券 -->
|
</a-card>
|
||||||
<a-card class="card coupon-selection">
|
<!-- 优惠券 -->
|
||||||
<a-checkbox v-model:checked="checked">是否使用算力券<span style="color:#f34646;">{{ ' ( 可用点数:'+couponTotal+' 点 )' }}</span></a-checkbox>
|
<a-card class="card coupon-selection">
|
||||||
</a-card>
|
<div style="display: flex;align-items: center;">
|
||||||
<!-- 底部操作栏 -->
|
<div>算力券:</div>
|
||||||
</div>
|
<a-select ref="select" v-model:value="voucherId" style="width:300px" @change="handleChange">
|
||||||
|
<a-select-option :value="''">不使用算力券</a-select-option>
|
||||||
|
<a-select-option v-for="value in voucherList" :value="value.id">{{ value.name + ' - '+'¥'+value.amount }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</a-card>
|
||||||
|
<!-- 底部操作栏 -->
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
<div class="footer-actions">
|
<div class="footer-actions">
|
||||||
|
|
||||||
<div class="price-summary">
|
<div class="price-summary">
|
||||||
<div>消耗算力点:{{totalMoney }}</div>
|
<div>消耗算力点:{{ totalMoney }}</div>
|
||||||
<!-- 账户余额信息 -->
|
<!-- 账户余额信息 -->
|
||||||
<div class="account-info" style="display: flex;align-items: center;">
|
<div class="account-info" style="display: flex;align-items: center;">
|
||||||
<div class="balance-label" style="margin-right: 10px;">可用算力点:{{ userInfo.computingPowerPoint+' 点' }}</div>
|
<div class="balance-label" style="margin-right: 10px;">可用算力点:{{ userInfo.computingPowerPoint + ' 点'
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
<!-- <div v-if="isShow" style="color: #ff4d4f;font-size: 12px;cursor: pointer;"
|
<!-- <div v-if="isShow" style="color: #ff4d4f;font-size: 12px;cursor: pointer;"
|
||||||
@click="router.push('/layout/admin/balance')">
|
@click="router.push('/layout/admin/balance')">
|
||||||
算力点不足去充值</div> -->
|
算力点不足去充值</div> -->
|
||||||
@ -115,12 +136,12 @@
|
|||||||
取消
|
取消
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="primary" size="large" @click="handleCreate" :loading="creating"
|
<a-button type="primary" size="large" @click="handleCreate" :loading="creating"
|
||||||
:disabled="selectedImage==''" class="create-btn">
|
:disabled="selectedImage == ''" class="create-btn">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</template>
|
</template>
|
||||||
立即创建
|
立即创建
|
||||||
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -132,10 +153,10 @@
|
|||||||
import { ref, computed, reactive, watch, onMounted } from 'vue'
|
import { ref, computed, reactive, watch, onMounted } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import type { TableColumnsType } from 'ant-design-vue'
|
import type { TableColumnsType } from 'ant-design-vue'
|
||||||
import { useRoute,useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { getImages } from "@/apis/home"
|
import { getImages, getRecommendList } from "@/apis/home"
|
||||||
import { getMyCouponTotal, getTotalCost, getHostInfo,createHost } from "@/apis/home"
|
import { getMyCouponTotal, getTotalCost, getHostInfo, createHost } from "@/apis/home"
|
||||||
const router=useRouter()
|
const router = useRouter()
|
||||||
interface GpuModelOption {
|
interface GpuModelOption {
|
||||||
value: string
|
value: string
|
||||||
label: string
|
label: string
|
||||||
@ -143,10 +164,12 @@ interface GpuModelOption {
|
|||||||
total: number
|
total: number
|
||||||
regions: string[]
|
regions: string[]
|
||||||
}
|
}
|
||||||
|
const voucherList = ref<any[]>([])
|
||||||
const instanceName=ref('')
|
const instanceName = ref('')
|
||||||
const checked = ref(false)
|
const checked = ref(false)
|
||||||
const totalMoney = ref(0)
|
const totalMoney = ref(0)
|
||||||
|
const priceCount = ref(1)
|
||||||
|
const voucherId = ref('')
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const billingType = ref('PayOnTime')
|
const billingType = ref('PayOnTime')
|
||||||
const gpuCount = ref(5)
|
const gpuCount = ref(5)
|
||||||
@ -156,25 +179,33 @@ const selectedImage = ref<string>("")
|
|||||||
const creating = ref(false)
|
const creating = ref(false)
|
||||||
const serviceInfo = ref<any>({})
|
const serviceInfo = ref<any>({})
|
||||||
const userInfo = ref<any>({})
|
const userInfo = ref<any>({})
|
||||||
const HostId=ref(0)
|
const HostId = ref(0)
|
||||||
const computingPowerPoint=ref(0)
|
const computingPowerPoint = ref(0)
|
||||||
const couponTotal=ref(0)
|
const couponTotal = ref(0)
|
||||||
onMounted(() => {
|
const spinning = ref(false)
|
||||||
|
onMounted(async () => {
|
||||||
|
spinning.value = true
|
||||||
const userInfoStr = localStorage.getItem("userInfo")
|
const userInfoStr = localStorage.getItem("userInfo")
|
||||||
if (userInfoStr) {
|
if (userInfoStr) {
|
||||||
userInfo.value = JSON.parse(userInfoStr)
|
userInfo.value = JSON.parse(userInfoStr)
|
||||||
}
|
}
|
||||||
serviceInfo.value = useRoute().query
|
serviceInfo.value = useRoute().query
|
||||||
HostId.value=serviceInfo.value.id
|
HostId.value = serviceInfo.value.id
|
||||||
console.log('1111',serviceInfo.value)
|
instanceName.value = serviceInfo.value.name
|
||||||
gpuCountOptions.value = Array.from({ length: Number(serviceInfo.value.gpuAvailableNum) }, (_, i) => i + 1)
|
gpuCountOptions.value = Array.from({ length: Number(serviceInfo.value.gpuAvailableNum) }, (_, i) => i + 1)
|
||||||
gpuCount.value = gpuCountOptions.value[0]
|
gpuCount.value = gpuCountOptions.value[0]
|
||||||
getServiceImages()
|
await getServiceImages()
|
||||||
getCoupon()
|
await getCoupon()
|
||||||
fetchTotalCost()
|
await fetchTotalCost()
|
||||||
|
await getVoucherList()
|
||||||
|
spinning.value = false
|
||||||
})
|
})
|
||||||
|
async function getVoucherList() {
|
||||||
|
const res = await getRecommendList()
|
||||||
|
voucherList.value = res.data
|
||||||
|
}
|
||||||
async function getCoupon() {
|
async function getCoupon() {
|
||||||
const res:any=await getMyCouponTotal()
|
const res: any = await getMyCouponTotal()
|
||||||
couponTotal.value = res;
|
couponTotal.value = res;
|
||||||
}
|
}
|
||||||
// 封装请求
|
// 封装请求
|
||||||
@ -182,14 +213,26 @@ async function fetchTotalCost() {
|
|||||||
const res: any = await getTotalCost({
|
const res: any = await getTotalCost({
|
||||||
billingMethod: billingType.value,
|
billingMethod: billingType.value,
|
||||||
gpuNum: gpuCount.value,
|
gpuNum: gpuCount.value,
|
||||||
host_id: HostId.value
|
host_id: HostId.value,
|
||||||
|
price_count:priceCount.value
|
||||||
})
|
})
|
||||||
totalMoney.value = res.amount
|
totalMoney.value = res.amount
|
||||||
computingPowerPoint.value=res.computingPowerPoint
|
computingPowerPoint.value = res.computingPowerPoint
|
||||||
}
|
}
|
||||||
|
const dayList = ref([{ id: 1, label: '1日' }, { id: 2, label: '2日' }, { id: 3, label: '3日' }, { id: 4, label: '4日' }, { id: 5, label: '5日' }, { id: 6, label: '6日' }])
|
||||||
|
const weekList = ref([{ id: 1, label: '1周' }, { id: 2, label: '2周' }, { id: 3, label: '3周' }])
|
||||||
|
const weekMonth = ref([{ id: 1, label: '1个月' }, { id: 2, label: '2个月' }, { id: 3, label: '3个月' }, { id: 3, label: '4个月' }, { id: 3, label: '5个月' }, { id: 3, label: '6个月' }, { id: 3, label: '7个月' }, { id: 3, label: '8个月' }, { id: 3, label: '9个月' }, { id: 3, label: '10个月' }, { id: 3, label: '11个月' }])
|
||||||
|
const priceCounts = computed(() => {
|
||||||
|
if (billingType.value === 'PayOnDay') {
|
||||||
|
return dayList.value
|
||||||
|
} else if (billingType.value === 'PayOnWeek') {
|
||||||
|
return weekList.value
|
||||||
|
} else if (billingType.value === 'PayOnMonth') {
|
||||||
|
return weekMonth.value
|
||||||
|
}
|
||||||
|
})
|
||||||
// 监听依赖项变化
|
// 监听依赖项变化
|
||||||
watch([billingType, gpuCount], () => {
|
watch([billingType, gpuCount,priceCount], () => {
|
||||||
fetchTotalCost()
|
fetchTotalCost()
|
||||||
getSingleHost()
|
getSingleHost()
|
||||||
}) // 组件加载时也执行一次
|
}) // 组件加载时也执行一次
|
||||||
@ -222,23 +265,23 @@ const handleChange = (e: any) => {
|
|||||||
console.log(e)
|
console.log(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isShow=computed(()=>{
|
const isShow = computed(() => {
|
||||||
return userInfo.value.balance<computingPowerPoint.value
|
return userInfo.value.balance < computingPowerPoint.value
|
||||||
})
|
})
|
||||||
const handleCreate = async () => {
|
const handleCreate = async () => {
|
||||||
creating.value = true
|
creating.value = true
|
||||||
try {
|
try {
|
||||||
const params={
|
const params = {
|
||||||
host_id:HostId.value,
|
host_id: HostId.value,
|
||||||
billingMethod:billingType.value,
|
billingMethod: billingType.value,
|
||||||
gpuNum:gpuCount.value,
|
gpuNum: gpuCount.value,
|
||||||
case_name:instanceName.value,
|
case_name: instanceName.value,
|
||||||
image_id:selectedImage.value,
|
image_id: selectedImage.value,
|
||||||
is_use_voucher:checked.value
|
voucher_id: voucherId.value
|
||||||
}
|
}
|
||||||
const res=await createHost(params)
|
const res = await createHost(params)
|
||||||
message.success('实例创建成功!')
|
message.success('实例创建成功!')
|
||||||
creating.value=false
|
creating.value = false
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('创建失败,请重试')
|
message.error('创建失败,请重试')
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="gx_layout">
|
<div class="gx_layout">
|
||||||
<div :class="isHome ? 'gx_layout_header_home' : 'gx_layout_header_noHome'" class="gx_layout_header">
|
<div :class="isHome ? 'gx_layout_header_home' : 'gx_layout_header_noHome'" class="gx_layout_header">
|
||||||
<div class="logo">GxDL算力云</div>
|
<div class="logo">FAST亼算云</div>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<a-menu v-model:selectedKeys="current" :class="isHome ? 'custom-menu' : ''" mode="horizontal"
|
<a-menu v-model:selectedKeys="current" :class="isHome ? 'custom-menu' : ''" mode="horizontal"
|
||||||
:items="leftRoutes" @click="({ key }) => handleMenuClick(key)" />
|
:items="leftRoutes" @click="({ key }) => handleMenuClick(key)" />
|
||||||
|
|||||||
@ -1,118 +1,121 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="instance-create-container">
|
<div class="instance-create-container">
|
||||||
<div class="header">
|
<a-spin :spinning="spinning">
|
||||||
<h1>算力中心</h1>
|
<div class="header">
|
||||||
<h2>聚焦高效算力服务,为数字产业、智能应用提供强支撑</h2>
|
<h1>算力中心</h1>
|
||||||
</div>
|
<h2>聚焦高效算力服务,为数字产业、智能应用提供强支撑</h2>
|
||||||
<div class="instance-create-body">
|
</div>
|
||||||
<!-- 筛选区域(保持不变) -->
|
<div class="instance-create-body">
|
||||||
<a-card class="card select-server">
|
<!-- 筛选区域(保持不变) -->
|
||||||
<div class="list-filter">
|
<a-card class="card select-server">
|
||||||
<div class="filter-item"> <span class="filter-label">计费方式:</span>
|
<div class="list-filter">
|
||||||
<div class="filter-content"> <a-radio-group v-model:value="billingType" button-style="solid"
|
<div class="filter-item"> <span class="filter-label">计费方式:</span>
|
||||||
@change="getHostList"> <a-radio-button v-for="type in billingTypeOptions" :key="type.value"
|
<div class="filter-content"> <a-radio-group v-model:value="billingType" button-style="solid"
|
||||||
:value="type.value"> {{ type.label }} </a-radio-button> </a-radio-group> </div>
|
@change="getHostList"> <a-radio-button v-for="type in billingTypeOptions" :key="type.value"
|
||||||
</div> <!-- 选择地区 -->
|
:value="type.value"> {{ type.label }} </a-radio-button> </a-radio-group> </div>
|
||||||
<div class="filter-item"> <span class="filter-label">选择地区:</span>
|
</div> <!-- 选择地区 -->
|
||||||
<div class="filter-content"> <a-radio-group v-model:value="selectedRegion" button-style="solid"
|
<div class="filter-item"> <span class="filter-label">选择地区:</span>
|
||||||
@change="getHostList">
|
<div class="filter-content"> <a-radio-group v-model:value="selectedRegion" button-style="solid"
|
||||||
<a-radio-button v-for="region in areaList" :key="region.center_id" :value="region.center_id"> {{
|
@change="getHostList">
|
||||||
region.center_name }}
|
<a-radio-button v-for="region in areaList" :key="region.center_id" :value="region.center_id"> {{
|
||||||
</a-radio-button> </a-radio-group>
|
region.center_name }}
|
||||||
|
</a-radio-button> </a-radio-group>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="filter-item"> <span class="filter-label">GPU型号:</span>
|
||||||
<div class="filter-item"> <span class="filter-label">GPU型号:</span>
|
<div class="filter-content">
|
||||||
<div class="filter-content">
|
<a-radio-group v-model:value="selectedGpuModels" button-style="solid" @change="getHostList">
|
||||||
<a-radio-group v-model:value="selectedGpuModels" button-style="solid" @change="getHostList">
|
<a-radio-button v-for="gpuItem in gpuList" :key="gpuItem.gpuType" :value="gpuItem.gpuType">
|
||||||
<a-radio-button v-for="gpuItem in gpuList" :key="gpuItem.gpuType" :value="gpuItem.gpuType">
|
{{ gpuItem.gpuType }}
|
||||||
{{ gpuItem.gpuType }}
|
<span v-if="gpuItem.gpuType !== '全部'">{{ "(" + gpuItem.gpuAvailableNum + "/" + gpuItem.gpuNum + ")"
|
||||||
<span v-if="gpuItem.gpuType !== '全部'">{{ "(" + gpuItem.gpuAvailableNum + "/" + gpuItem.gpuNum + ")"
|
|
||||||
}}</span>
|
}}</span>
|
||||||
</a-radio-button> </a-radio-group>
|
</a-radio-button> </a-radio-group>
|
||||||
|
</div>
|
||||||
|
</div> <!-- GPU数量 -->
|
||||||
|
<div class="filter-item"> <span class="filter-label">GPU数量:</span>
|
||||||
|
<div class="filter-content"> <a-select :size="'large'" default-value="1" style="width: 200px"
|
||||||
|
@change="handleGpuCountChange"> <a-select-option v-for="count in gpuCountOptions" :key="count"
|
||||||
|
:value="count"> {{ count }} </a-select-option> </a-select> </div>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- GPU数量 -->
|
|
||||||
<div class="filter-item"> <span class="filter-label">GPU数量:</span>
|
|
||||||
<div class="filter-content"> <a-select :size="'large'" default-value="1" style="width: 200px"
|
|
||||||
@change="handleGpuCountChange"> <a-select-option v-for="count in gpuCountOptions" :key="count"
|
|
||||||
:value="count"> {{ count }} </a-select-option> </a-select> </div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a-card>
|
||||||
</a-card>
|
|
||||||
|
|
||||||
<!-- 机器列表 -->
|
<!-- 机器列表 -->
|
||||||
<a-card class="card machine-list">
|
<a-card class="card machine-list">
|
||||||
<template v-if="allMachineList.length > 0">
|
<template v-if="allMachineList.length > 0">
|
||||||
<div class="image-card-container" style="min-height:300px;">
|
<a-spin :spinning="tableLoading">
|
||||||
|
<div class="image-card-container" style="min-height:300px;">
|
||||||
|
<div v-for="record in allMachineList" :key="record.id" class="image-card"
|
||||||
|
:class="{ selected: selectedMachineId === record.id }" @click="handleSelectMachine(record.id)">
|
||||||
|
<!-- 顶部 -->
|
||||||
|
<div class="image-card-header">
|
||||||
|
<div class="gpu-title">
|
||||||
|
{{ record.name }}
|
||||||
|
</div>
|
||||||
|
<div class="gpu-availability">
|
||||||
|
空闲 / 总量
|
||||||
|
<strong style="font-size: 18px;">{{ record.gpuAvailableNum }}</strong> / {{ record.gpuNum }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-for="record in allMachineList" :key="record.id" class="image-card"
|
<!-- 中部信息 -->
|
||||||
:class="{ selected: selectedMachineId === record.id }" @click="handleSelectMachine(record.id)">
|
<div class="image-card-info">
|
||||||
<!-- 顶部 -->
|
<div class="info-item">
|
||||||
<div class="image-card-header">
|
<span class="info-item-name">CPU</span>
|
||||||
<div class="gpu-title">
|
<span class="info-item-account">{{ record.cpuNum }} 核</span>
|
||||||
{{ record.name }}
|
</div>
|
||||||
</div>
|
<div class="info-item">
|
||||||
<div class="gpu-availability">
|
<span class="info-item-name">内存</span>
|
||||||
空闲 / 总量
|
<span class="info-item-account">{{ record.memory }} GB</span>
|
||||||
<strong style="font-size: 18px;">{{ record.gpuAvailableNum }}</strong> / {{ record.gpuNum }}
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-item-name">显存</span>
|
||||||
|
<span class="info-item-account">{{ record.vram }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-item-name">系统盘</span>
|
||||||
|
<span class="info-item-account">30 GB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部价格 -->
|
||||||
|
<div class="image-card-footer">
|
||||||
|
<div class="price">
|
||||||
|
<span class="price-num">{{ record.price }}</span>
|
||||||
|
<span class="price-unit">元/卡/小时</span>
|
||||||
|
</div>
|
||||||
|
<a-button type="primary" class="buy-btn" :disabled="record.freeGpu === 0"
|
||||||
|
@click.stop="handleRentMachine(record)">
|
||||||
|
立即创建
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</a-spin>
|
||||||
<!-- 中部信息 -->
|
</template>
|
||||||
<div class="image-card-info">
|
<template v-else>
|
||||||
<div class="info-item">
|
<a-empty />
|
||||||
<span class="info-item-name">CPU</span>
|
</template>
|
||||||
<span class="info-item-account">{{ record.cpuNum }} 核</span>
|
<!-- 分页 -->
|
||||||
</div>
|
<div class="pagination-container">
|
||||||
<div class="info-item">
|
<a-pagination v-model:current="currentPage" v-model:pageSize="pageSize" :total="hostTotal" show-size-changer
|
||||||
<span class="info-item-name">内存</span>
|
show-quick-jumper />
|
||||||
<span class="info-item-account">{{ record.memory }} GB</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-item-name">显存</span>
|
|
||||||
<span class="info-item-account">{{ record.vram }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-item-name">系统盘</span>
|
|
||||||
<span class="info-item-account">30 GB</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 底部价格 -->
|
|
||||||
<div class="image-card-footer">
|
|
||||||
<div class="price">
|
|
||||||
<span class="price-num">{{ record.price }}</span>
|
|
||||||
<span class="price-unit">元/卡/小时</span>
|
|
||||||
</div>
|
|
||||||
<a-button type="primary" class="buy-btn" :disabled="record.freeGpu === 0"
|
|
||||||
@click.stop="handleRentMachine(record)">
|
|
||||||
立即购买
|
|
||||||
</a-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</a-card>
|
||||||
<template v-else>
|
</div>
|
||||||
<a-empty />
|
</a-spin>
|
||||||
</template>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<div class="pagination-container">
|
|
||||||
<a-pagination v-model:current="currentPage" v-model:pageSize="pageSize" :total="hostTotal"
|
|
||||||
show-size-changer show-quick-jumper />
|
|
||||||
</div>
|
|
||||||
</a-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, reactive, watch } from 'vue'
|
import { ref, computed, reactive, watch, onMounted } from 'vue'
|
||||||
import { hostList, getAreaListApi, getGpuListApi } from '@/apis/market'
|
import { hostList, getAreaListApi, getGpuListApi } from '@/apis/market'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import type { TableColumnsType } from 'ant-design-vue'
|
import type { TableColumnsType } from 'ant-design-vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
const spinning = ref(false)
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const hostTotal = ref(0)
|
const hostTotal = ref(0)
|
||||||
@ -127,9 +130,14 @@ const tableLoading = ref(false)
|
|||||||
// 分页相关
|
// 分页相关
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const pageSize = ref(10)
|
const pageSize = ref(10)
|
||||||
getHostList()
|
onMounted(async () => {
|
||||||
getAreaList()
|
spinning.value = true
|
||||||
getGpuList()
|
await getHostList()
|
||||||
|
await getAreaList()
|
||||||
|
await getGpuList()
|
||||||
|
spinning.value = false
|
||||||
|
})
|
||||||
|
|
||||||
async function getHostList() {
|
async function getHostList() {
|
||||||
tableLoading.value = true
|
tableLoading.value = true
|
||||||
try {
|
try {
|
||||||
@ -187,13 +195,13 @@ const handleSelectMachine = (id: string) => {
|
|||||||
}
|
}
|
||||||
// 一卡可租
|
// 一卡可租
|
||||||
const handleRentMachine = (record: any) => {
|
const handleRentMachine = (record: any) => {
|
||||||
router.push({path:"/layout/create",query:record})
|
router.push({ path: "/layout/create", query: record })
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleGpuCountChange = (e:any) => {
|
const handleGpuCountChange = (e: any) => {
|
||||||
// console.log(e)
|
// console.log(e)
|
||||||
gpuCount.value=e
|
gpuCount.value = e
|
||||||
getHostList()
|
getHostList()
|
||||||
selectedMachineId.value = ''
|
selectedMachineId.value = ''
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export default defineConfig({
|
|||||||
open: true, // 可选:启动时自动打开浏览器
|
open: true, // 可选:启动时自动打开浏览器
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://10.10.1.34:8888', // 目标服务器的地址
|
target: 'http://10.10.1.11:8888', // 目标服务器的地址
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user