commit
377082156c
@ -54,6 +54,24 @@
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">购物车空</div>
|
||||
<div class="code-name">&#xe600;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">icon_关机-开机</div>
|
||||
<div class="code-name">&#xe609;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">icon_arrow_left</div>
|
||||
<div class="code-name">&#xe62e;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">发送</div>
|
||||
@ -138,9 +156,9 @@
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1776735138822') format('woff2'),
|
||||
url('iconfont.woff?t=1776735138822') format('woff'),
|
||||
url('iconfont.ttf?t=1776735138822') format('truetype');
|
||||
src: url('iconfont.woff2?t=1781579680075') format('woff2'),
|
||||
url('iconfont.woff?t=1781579680075') format('woff'),
|
||||
url('iconfont.ttf?t=1781579680075') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
@ -166,6 +184,33 @@
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-gouwuchekong"></span>
|
||||
<div class="name">
|
||||
购物车空
|
||||
</div>
|
||||
<div class="code-name">.icon-gouwuchekong
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-icon_guanji-kaiji"></span>
|
||||
<div class="name">
|
||||
icon_关机-开机
|
||||
</div>
|
||||
<div class="code-name">.icon-icon_guanji-kaiji
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-icon_arrow_left"></span>
|
||||
<div class="name">
|
||||
icon_arrow_left
|
||||
</div>
|
||||
<div class="code-name">.icon-icon_arrow_left
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-fasong"></span>
|
||||
<div class="name">
|
||||
@ -292,6 +337,30 @@
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-gouwuchekong"></use>
|
||||
</svg>
|
||||
<div class="name">购物车空</div>
|
||||
<div class="code-name">#icon-gouwuchekong</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-icon_guanji-kaiji"></use>
|
||||
</svg>
|
||||
<div class="name">icon_关机-开机</div>
|
||||
<div class="code-name">#icon-icon_guanji-kaiji</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-icon_arrow_left"></use>
|
||||
</svg>
|
||||
<div class="name">icon_arrow_left</div>
|
||||
<div class="code-name">#icon-icon_arrow_left</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-fasong"></use>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 5043107 */
|
||||
src: url('iconfont.woff2?t=1776735138822') format('woff2'),
|
||||
url('iconfont.woff?t=1776735138822') format('woff'),
|
||||
url('iconfont.ttf?t=1776735138822') format('truetype');
|
||||
src: url('iconfont.woff2?t=1781579680075') format('woff2'),
|
||||
url('iconfont.woff?t=1781579680075') format('woff'),
|
||||
url('iconfont.ttf?t=1781579680075') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@ -13,6 +13,18 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-gouwuchekong:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.icon-icon_guanji-kaiji:before {
|
||||
content: "\e609";
|
||||
}
|
||||
|
||||
.icon-icon_arrow_left:before {
|
||||
content: "\e62e";
|
||||
}
|
||||
|
||||
.icon-fasong:before {
|
||||
content: "\e60d";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -5,6 +5,27 @@
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "1306",
|
||||
"name": "购物车空",
|
||||
"font_class": "gouwuchekong",
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
},
|
||||
{
|
||||
"icon_id": "11518201",
|
||||
"name": "icon_关机-开机",
|
||||
"font_class": "icon_guanji-kaiji",
|
||||
"unicode": "e609",
|
||||
"unicode_decimal": 58889
|
||||
},
|
||||
{
|
||||
"icon_id": "18418399",
|
||||
"name": "icon_arrow_left",
|
||||
"font_class": "icon_arrow_left",
|
||||
"unicode": "e62e",
|
||||
"unicode_decimal": 58926
|
||||
},
|
||||
{
|
||||
"icon_id": "12719937",
|
||||
"name": "发送",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -337,6 +337,18 @@ export const constantRoutes = [
|
||||
name: "homePageIndex",
|
||||
hidden: true,
|
||||
meta: { title: "首页", onCache: true },
|
||||
}, {
|
||||
path: "computeMarket",
|
||||
component: () => import("@/views/homePage/computeMarket/index.vue"),
|
||||
name: "computeMarket",
|
||||
hidden: true,
|
||||
meta: { title: "算力市场", onCache: true },
|
||||
}, {
|
||||
path: "computeMarket/createInstance",
|
||||
component: () => import("@/views/homePage/computeMarket/createInstance.vue"),
|
||||
name: "computeMarketCreateInstance",
|
||||
hidden: true,
|
||||
meta: { title: "创建实例", onCache: true },
|
||||
}, {
|
||||
path: "detail",
|
||||
component: () => import("@/views/homePage/detail/index.vue"),
|
||||
@ -1127,6 +1139,33 @@ export const asyncRoutes = [
|
||||
},
|
||||
|
||||
|
||||
// 容器实例 - 客户角色可见
|
||||
{
|
||||
path: "/containerInstance",
|
||||
component: Layout,
|
||||
redirect: "/containerInstance/index",
|
||||
meta: {
|
||||
title: "容器实例",
|
||||
icon: "el-icon-box",
|
||||
noCache: true,
|
||||
fullPath: "/containerInstance",
|
||||
roles: ["客户"]
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "index",
|
||||
component: () => import("@/views/customer/containerInstance"),
|
||||
name: "ContainerInstance",
|
||||
meta: {
|
||||
title: "容器实例",
|
||||
fullPath: "/containerInstance/index",
|
||||
roles: ["客户"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
// 退订管理 - 变为一级菜单(包含子菜单)
|
||||
{
|
||||
path: "/unsubscribeManagement",
|
||||
|
||||
@ -28,6 +28,7 @@ const BASE_USER_ROUTE_PATHS = ['/orderManagement', '/resourceManagement'];
|
||||
|
||||
// 客户角色额外能看到的一级菜单。
|
||||
const CUSTOMER_EXTRA_ROUTE_PATHS = [
|
||||
'/containerInstance',
|
||||
'/unsubscribeManagement',
|
||||
'/informationPerfect',
|
||||
'/rechargeManagement',
|
||||
|
||||
500
f/web-kboss/src/views/customer/containerInstance/index.vue
Normal file
500
f/web-kboss/src/views/customer/containerInstance/index.vue
Normal file
@ -0,0 +1,500 @@
|
||||
<template>
|
||||
<div class="container-instance-page">
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1>容器实例</h1>
|
||||
<p>管理训练、推理和实验环境中的容器实例</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="stat-grid">
|
||||
<div v-for="item in statCards" :key="item.label" class="stat-card">
|
||||
<div class="stat-card__top">
|
||||
<span class="stat-icon" :class="item.type">
|
||||
<i :class="item.icon"></i>
|
||||
</span>
|
||||
<span>{{ item.label }}</span>
|
||||
</div>
|
||||
<strong>{{ item.value }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-card">
|
||||
<el-table :data="containerList" stripe class="container-table">
|
||||
<el-table-column width="48">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.checked"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="容器名称" min-width="220">
|
||||
<template slot-scope="scope">
|
||||
<div class="name-cell">
|
||||
<strong>{{ scope.row.name }}</strong>
|
||||
<span>Pod: {{ scope.row.pod }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="状态" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span class="status-badge" :class="scope.row.statusType">
|
||||
<i></i>{{ scope.row.status }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="命名空间" width="130">
|
||||
<template slot-scope="scope">
|
||||
<span class="namespace-tag">{{ scope.row.namespace }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="镜像" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
<div class="image-cell">
|
||||
<strong>{{ scope.row.image }}</strong>
|
||||
<span>{{ scope.row.gpu }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="disk" label="本地磁盘" width="100"></el-table-column>
|
||||
<el-table-column prop="spec" label="CPU/内存" width="130"></el-table-column>
|
||||
|
||||
<el-table-column label="付费方式" width="110">
|
||||
<template slot-scope="scope">
|
||||
<span class="pay-tag" :class="scope.row.payType">{{ scope.row.payText }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="releaseTime" label="释放时间/停机时间" min-width="150"></el-table-column>
|
||||
|
||||
<el-table-column label="登录指令/密码" min-width="210">
|
||||
<template slot-scope="scope">
|
||||
<div class="login-cell">
|
||||
<span>SSH: <em>{{ scope.row.ssh }}</em></span>
|
||||
<span>密码: <em>{{ scope.row.password }}</em></span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="快捷工具" width="170">
|
||||
<template slot-scope="scope">
|
||||
<div class="tool-cell">
|
||||
<el-button
|
||||
size="mini"
|
||||
:disabled="scope.row.statusType === 'pending'"
|
||||
@click="handleTool('Jupyter', scope.row)"
|
||||
>
|
||||
Jupyter
|
||||
</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="success"
|
||||
plain
|
||||
:disabled="scope.row.statusType === 'pending'"
|
||||
@click="handleTool('实例监测', scope.row)"
|
||||
>
|
||||
实例监测
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="210" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<div class="action-cell">
|
||||
<el-button
|
||||
v-if="scope.row.statusType === 'stopped' || scope.row.statusType === 'error'"
|
||||
type="text"
|
||||
class="success-text"
|
||||
@click="handleAction('启动', scope.row)"
|
||||
>
|
||||
启动
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
type="text"
|
||||
class="warning-text"
|
||||
:disabled="scope.row.statusType === 'pending'"
|
||||
@click="handleAction('停止', scope.row)"
|
||||
>
|
||||
停止
|
||||
</el-button>
|
||||
<el-button type="text" @click="handleAction('重启', scope.row)">重启</el-button>
|
||||
<el-button type="text" @click="handleAction('日志', scope.row)">日志</el-button>
|
||||
<el-button type="text" class="danger-text" @click="handleAction('删除', scope.row)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ContainerInstance',
|
||||
data() {
|
||||
return {
|
||||
statCards: [
|
||||
{ label: '运行中', value: 5, icon: 'el-icon-check', type: 'success' },
|
||||
{ label: 'Pending', value: 1, icon: 'el-icon-time', type: 'warning' },
|
||||
{ label: '异常/重启', value: 0, icon: 'el-icon-close', type: 'danger' },
|
||||
{ label: 'GPU使用', value: '18 / 32 GB', icon: 'el-icon-cpu', type: 'primary' }
|
||||
],
|
||||
containerList: [
|
||||
{
|
||||
name: 'train-bert-finetune-7b8f9c',
|
||||
pod: 'train-bert-finetune-7b8f9c-xkq2m',
|
||||
status: 'Running',
|
||||
statusType: 'running',
|
||||
namespace: 'ml-training',
|
||||
image: 'pytorch/pytorch:2.1.0',
|
||||
gpu: 'GPU: A100 40GB x 1',
|
||||
disk: '50GB',
|
||||
spec: '4 Core / 16 GB',
|
||||
payText: '按量计费',
|
||||
payType: 'hourly',
|
||||
releaseTime: '2025-02-18 10:30',
|
||||
ssh: 'ssh root@10.0.0.1',
|
||||
password: 'Vm**k9#2'
|
||||
},
|
||||
{
|
||||
name: 'llama2-inference-3d2e1a',
|
||||
pod: 'llama2-inference-3d2e1a-pqr8n',
|
||||
status: 'Running',
|
||||
statusType: 'running',
|
||||
namespace: 'ml-inference',
|
||||
image: 'meta-llama/llama2:7b-chat',
|
||||
gpu: 'GPU: RTX 4090 x 1',
|
||||
disk: '100GB',
|
||||
spec: '8 Core / 32 GB',
|
||||
payText: '包月',
|
||||
payType: 'monthly',
|
||||
releaseTime: '2025-02-15 09:15',
|
||||
ssh: 'ssh root@10.0.0.2',
|
||||
password: 'Ab**c3$5'
|
||||
},
|
||||
{
|
||||
name: 'sd-training-exp-9f4b2c',
|
||||
pod: 'sd-training-exp-9f4b2c-zlm3p',
|
||||
status: 'Pending',
|
||||
statusType: 'pending',
|
||||
namespace: 'ml-training',
|
||||
image: 'stabilityai/stable-diffusion:fp16',
|
||||
gpu: 'GPU: A100 80GB x 2',
|
||||
disk: '200GB',
|
||||
spec: '16 Core / 64 GB',
|
||||
payText: '按量计费',
|
||||
payType: 'hourly',
|
||||
releaseTime: '-',
|
||||
ssh: '等待分配',
|
||||
password: '-'
|
||||
},
|
||||
{
|
||||
name: 'glm-finetune-pod-5e6d7f',
|
||||
pod: 'glm-finetune-pod-5e6d7f-abc9h',
|
||||
status: 'Error',
|
||||
statusType: 'error',
|
||||
namespace: 'ml-tuning',
|
||||
image: 'chatglm2-6b:fp16',
|
||||
gpu: 'GPU: V100 32GB x 1',
|
||||
disk: '80GB',
|
||||
spec: '8 Core / 32 GB',
|
||||
payText: '按量计费',
|
||||
payType: 'hourly',
|
||||
releaseTime: '异常停机',
|
||||
ssh: 'ssh root@10.0.0.4',
|
||||
password: 'Xy**z8#1'
|
||||
},
|
||||
{
|
||||
name: 'qwen-infer-srv-2c3d4e',
|
||||
pod: 'qwen-infer-srv-2c3d4e-hjk7m',
|
||||
status: 'Stopped',
|
||||
statusType: 'stopped',
|
||||
namespace: 'ml-inference',
|
||||
image: 'qwen-7b-chat:beta',
|
||||
gpu: 'GPU: A10G x 1',
|
||||
disk: '50GB',
|
||||
spec: '4 Core / 16 GB',
|
||||
payText: '包周',
|
||||
payType: 'weekly',
|
||||
releaseTime: '2025-01-24 14:20',
|
||||
ssh: 'ssh root@10.0.0.5',
|
||||
password: 'Pq**r6$2'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCreate() {
|
||||
this.$router.push('/homePage/computeMarket')
|
||||
},
|
||||
handleTool(name, row) {
|
||||
this.$message.info(`${name}:${row.name}`)
|
||||
},
|
||||
handleAction(action, row) {
|
||||
this.$message.info(`${action}:${row.name}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container-instance-page {
|
||||
min-height: calc(100vh - 84px);
|
||||
padding: 24px;
|
||||
background: #f5f7fb;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
color: #1f2937;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 8px 0 0;
|
||||
color: #8a94a6;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.create-btn {
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 18px rgba(64, 158, 255, 0.18);
|
||||
}
|
||||
|
||||
.stat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 18px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.stat-card,
|
||||
.table-card {
|
||||
background: #fff;
|
||||
border: 1px solid #edf1f7;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 28px rgba(31, 72, 135, 0.06);
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
padding: 22px;
|
||||
|
||||
strong {
|
||||
display: block;
|
||||
color: #1f2937;
|
||||
font-size: 26px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-card__top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
color: #667085;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 12px;
|
||||
|
||||
&.success {
|
||||
color: #059669;
|
||||
background: #dff8ee;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
color: #d97706;
|
||||
background: #fef3c7;
|
||||
}
|
||||
|
||||
&.danger {
|
||||
color: #dc2626;
|
||||
background: #fee2e2;
|
||||
}
|
||||
|
||||
&.primary {
|
||||
color: #1e6fff;
|
||||
background: #e8f1ff;
|
||||
}
|
||||
}
|
||||
|
||||
.table-card {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.container-table {
|
||||
::v-deep th {
|
||||
color: #667085;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
::v-deep td {
|
||||
color: #5f6b7a;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.name-cell,
|
||||
.image-cell,
|
||||
.login-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
strong {
|
||||
color: #1f2937;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #98a2b3;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-cell em {
|
||||
color: #1e6fff;
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
|
||||
i {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&.running {
|
||||
color: #059669;
|
||||
background: #dff8ee;
|
||||
|
||||
i {
|
||||
background: #10b981;
|
||||
}
|
||||
}
|
||||
|
||||
&.pending {
|
||||
color: #d97706;
|
||||
background: #fef3c7;
|
||||
|
||||
i {
|
||||
background: #f59e0b;
|
||||
}
|
||||
}
|
||||
|
||||
&.error {
|
||||
color: #dc2626;
|
||||
background: #fee2e2;
|
||||
|
||||
i {
|
||||
background: #ef4444;
|
||||
}
|
||||
}
|
||||
|
||||
&.stopped {
|
||||
color: #667085;
|
||||
background: #eef2f7;
|
||||
|
||||
i {
|
||||
background: #98a2b3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.namespace-tag,
|
||||
.pay-tag {
|
||||
display: inline-flex;
|
||||
padding: 4px 8px;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.namespace-tag {
|
||||
color: #1e6fff;
|
||||
background: #eef5ff;
|
||||
}
|
||||
|
||||
.pay-tag.hourly {
|
||||
color: #d97706;
|
||||
background: #fef3c7;
|
||||
}
|
||||
|
||||
.pay-tag.monthly,
|
||||
.pay-tag.weekly {
|
||||
color: #1e6fff;
|
||||
background: #eef5ff;
|
||||
}
|
||||
|
||||
.tool-cell,
|
||||
.action-cell {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.success-text {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.warning-text {
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.danger-text {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.stat-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.container-instance-page {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.stat-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -19,9 +19,11 @@
|
||||
<a @click="goHome">首页</a>
|
||||
</p>
|
||||
<!-- 产品与服务:鼠标移入显示子菜单 -->
|
||||
<p @mouseleave="sildeOut" @mouseenter="sildeIn(product_service)">
|
||||
<a>基础云</a>
|
||||
</p>
|
||||
<!-- <p @mouseleave="sildeOut" @mouseenter="sildeIn(product_service)">
|
||||
<a>算力云</a>
|
||||
</p> -->
|
||||
<!-- 算力市场 -->
|
||||
<p class="nav-hover" :class="{ active: $route.path.includes('/computeMarket') }" @click="$router.push('/homePage/computeMarket')">算力市场</p>
|
||||
<p class="nav-hover" :class="{ active: isActiveTokenMarket }" @click="handleModelSquareClick">token市集</p>
|
||||
<!-- 训推平台 -->
|
||||
<p class="nav-hover" @click="goTrainPlatform">训推平台</p>
|
||||
@ -410,6 +412,11 @@ export default Vue.extend({
|
||||
})
|
||||
},
|
||||
|
||||
// 跳转算力市场
|
||||
goComputeMarket() {
|
||||
this.$router.push('/homePage/computeMarket')
|
||||
},
|
||||
|
||||
// 跳转训推平台
|
||||
goTrainPlatform() {
|
||||
window.open('http://101.200.145.167:8923/', '_blank')
|
||||
|
||||
713
f/web-kboss/src/views/homePage/computeMarket/createInstance.vue
Normal file
713
f/web-kboss/src/views/homePage/computeMarket/createInstance.vue
Normal file
@ -0,0 +1,713 @@
|
||||
<template>
|
||||
<div class="create-instance-page">
|
||||
<main class="create-main">
|
||||
<div class="breadcrumb-row">
|
||||
<div class="breadcrumb">
|
||||
<span class="link" @click="$router.push('/homePage/computeMarket')"> <i class="iconfont icon-icon_arrow_left"></i> 返回算力市场</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="content-card">
|
||||
<div class="card-header">
|
||||
<h2>计费方式:</h2>
|
||||
<button type="button" class="link-btn">计费规则</button>
|
||||
</div>
|
||||
<div class="option-list">
|
||||
<button
|
||||
v-for="option in billingOptions"
|
||||
:key="option.value"
|
||||
type="button"
|
||||
class="option-btn"
|
||||
:class="{ active: billingType === option.value }"
|
||||
@click="billingType = option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</button>
|
||||
</div>
|
||||
<p class="card-tip">创建完主机后仍然可以转换计费方式。如选择按量计费,价格发生变动以实例开机时的价格为准</p>
|
||||
</section>
|
||||
|
||||
<section class="content-card">
|
||||
<div class="card-header">
|
||||
<h2>选择主机:</h2>
|
||||
</div>
|
||||
|
||||
<div class="host-table-wrap">
|
||||
<table class="host-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>主机ID</th>
|
||||
<th>算力型号/显存</th>
|
||||
<th>空闲GPU</th>
|
||||
<th>每GPU分配</th>
|
||||
<th>CPU型号</th>
|
||||
<th>硬盘</th>
|
||||
<th>驱动/CUDA</th>
|
||||
<th>价格(单卡)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="host in hosts"
|
||||
:key="host.id"
|
||||
:class="{ selected: selectedHostId === host.id }"
|
||||
@click="selectedHostId = host.id"
|
||||
>
|
||||
<td>
|
||||
<span class="radio-dot"></span>
|
||||
</td>
|
||||
<td class="host-name">{{ host.name }}</td>
|
||||
<td>
|
||||
<strong class="gpu-name">{{ host.gpu }}</strong>
|
||||
<span class="gpu-memory"> {{ host.memory }}</span>
|
||||
</td>
|
||||
<td><strong class="free-count">{{ host.freeGpu }}</strong></td>
|
||||
<td>{{ host.allocation }}</td>
|
||||
<td class="muted small">{{ host.cpu }}</td>
|
||||
<td class="muted small">{{ host.disk }}</td>
|
||||
<td class="muted small">{{ host.driver }}</td>
|
||||
<td>
|
||||
<strong class="price">¥{{ host.price }}/时</strong>
|
||||
<span class="origin-price">¥{{ host.originalPrice }}/时</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="field-block">
|
||||
<h3>GPU数量:</h3>
|
||||
<div class="option-list option-list--compact">
|
||||
<button
|
||||
v-for="count in gpuCounts"
|
||||
:key="count"
|
||||
type="button"
|
||||
class="option-btn gpu-count-btn"
|
||||
:class="{ active: gpuCount === count }"
|
||||
@click="gpuCount = count"
|
||||
>
|
||||
{{ count }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="disk-row">
|
||||
<span>数据盘: 免费50GB</span>
|
||||
<label>
|
||||
<input v-model="needExpandDisk" type="checkbox">
|
||||
<span>需要扩容</span>
|
||||
</label>
|
||||
<div v-if="needExpandDisk" class="disk-input">
|
||||
<input v-model.number="expandDiskSize" type="number" min="50">
|
||||
<span>GB</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="spec-bar">
|
||||
<strong>实例规格:</strong>
|
||||
<span>{{ specSummary }}</span>
|
||||
</section>
|
||||
|
||||
<section class="content-card">
|
||||
<div class="card-header">
|
||||
<h2>镜像:</h2>
|
||||
<button type="button" class="link-btn">没有我要的环境?</button>
|
||||
</div>
|
||||
<div class="option-list">
|
||||
<button
|
||||
v-for="option in imageTypes"
|
||||
:key="option.value"
|
||||
type="button"
|
||||
class="option-btn image-option"
|
||||
:class="{ active: imageType === option.value }"
|
||||
@click="imageType = option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
<em v-if="option.hot">HOT</em>
|
||||
</button>
|
||||
</div>
|
||||
<p class="card-tip">基础镜像包含常用基本软件,如:深度学习框架、Miniconda等。如需其他软件可创建后安装</p>
|
||||
<div class="select-wrap">
|
||||
<select v-model="selectedEnv">
|
||||
<option value="">请选择框架名称/框架版本/Python版本/CUDA版本</option>
|
||||
<option value="pytorch">PyTorch 2.1.0 / Python 3.10 / CUDA 12.1</option>
|
||||
<option value="tensorflow">TensorFlow 2.14.0 / Python 3.10 / CUDA 12.1</option>
|
||||
<option value="paddle">PaddlePaddle 2.5.1 / Python 3.10 / CUDA 11.8</option>
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="content-card">
|
||||
<div class="card-header">
|
||||
<h2>优惠券:</h2>
|
||||
</div>
|
||||
<div class="select-wrap">
|
||||
<select v-model="coupon">
|
||||
<option value="">请选择</option>
|
||||
<option value="10">新人专享满100减10元</option>
|
||||
<option value="50">充值满500减50元</option>
|
||||
<option value="100">VIP用户满1000减100元</option>
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside class="submit-bar">
|
||||
<div class="submit-bar__inner">
|
||||
<div class="submit-costs">
|
||||
<span>日常费用: ¥0.00/日</span>
|
||||
<span>
|
||||
配置费用
|
||||
<strong>¥{{ estimatedPrice }}/时</strong>
|
||||
</span>
|
||||
<button type="button" class="detail-btn">费用明细</button>
|
||||
</div>
|
||||
<div class="submit-actions">
|
||||
<!-- <span class="balance">账户余额 ¥0.00 <em>余额不足去充值</em></span> -->
|
||||
<button type="button" class="cancel-btn" @click="$router.push('/homePage/computeMarket')">取消</button>
|
||||
<button type="button" class="create-btn" @click="submitCreate">
|
||||
<i class="iconfont icon-icon_guanji-kaiji"></i>
|
||||
创建并开机
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'ComputeMarketCreateInstance',
|
||||
data() {
|
||||
return {
|
||||
billingType: 'hourly',
|
||||
selectedHostId: 'D42',
|
||||
gpuCount: 1,
|
||||
needExpandDisk: false,
|
||||
expandDiskSize: 100,
|
||||
imageType: 'basic',
|
||||
selectedEnv: '',
|
||||
coupon: '',
|
||||
billingOptions: [
|
||||
{ label: '按量计费', value: 'hourly' },
|
||||
{ label: '包日', value: 'daily' },
|
||||
{ label: '包周', value: 'weekly' },
|
||||
{ label: '包月', value: 'monthly' },
|
||||
{ label: '包年', value: 'yearly' }
|
||||
],
|
||||
gpuCounts: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
imageTypes: [
|
||||
{ label: '基础镜像', value: 'basic' },
|
||||
{ label: '社区镜像', value: 'community', hot: true },
|
||||
{ label: '我的镜像', value: 'custom' }
|
||||
],
|
||||
hosts: [
|
||||
{
|
||||
id: 'D42',
|
||||
name: 'D42机',
|
||||
gpu: 'RTX PRO 6000',
|
||||
memory: '96GB',
|
||||
freeGpu: '1 / 8',
|
||||
allocation: 'CPU: 25核 内存: 120GB',
|
||||
cpu: 'Xeon(R) Platinum 8470Q',
|
||||
disk: '数据盘: 50GB 可扩容: 1622GB',
|
||||
driver: '驱动: 595.58.03 CUDA: 13.2',
|
||||
price: 5.98,
|
||||
originalPrice: 7.97
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
loginStateVuex: state => state.login.loginState
|
||||
}),
|
||||
loginState() {
|
||||
const userId = sessionStorage.getItem('userId')
|
||||
return this.loginStateVuex || (userId !== null && userId !== 'null' && userId !== '')
|
||||
},
|
||||
selectedHost() {
|
||||
return this.hosts.find(host => host.id === this.selectedHostId) || this.hosts[0]
|
||||
},
|
||||
dataDiskText() {
|
||||
return this.needExpandDisk ? `数据盘:${this.expandDiskSize}GB SSD` : '数据盘:免费50GB SSD'
|
||||
},
|
||||
specSummary() {
|
||||
const host = this.selectedHost
|
||||
return `GPU型号:${host.gpu} *${this.gpuCount}卡 | CPU:25核心 | 内存:120GB | 系统盘:30GB | ${this.dataDiskText}`
|
||||
},
|
||||
estimatedPrice() {
|
||||
return (Number(this.selectedHost.price) * this.gpuCount).toFixed(2)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const gpu = this.$route.query.gpu
|
||||
if (gpu) {
|
||||
this.hosts[0].gpu = String(gpu).split('/')[0].trim()
|
||||
this.hosts[0].memory = (String(gpu).split('/')[1] || '96 GB').trim()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submitCreate() {
|
||||
if (!this.loginState) {
|
||||
this.$message.warning('请先登录后再创建实例')
|
||||
this.$router.push({
|
||||
path: '/login',
|
||||
query: {
|
||||
fromPath: 'computeMarketCreateInstance',
|
||||
redirect: this.$route.fullPath
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.selectedEnv) {
|
||||
this.$message.warning('请先选择镜像环境')
|
||||
return
|
||||
}
|
||||
this.$message.success('实例创建请求已提交')
|
||||
this.$router.push('/containerInstance/index')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.create-instance-page {
|
||||
min-height: 100vh;
|
||||
padding: 28px 0 88px;
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(30, 111, 255, 0.12), transparent 30%),
|
||||
linear-gradient(180deg, #f7fbff 0%, #f5f7fb 100%);
|
||||
}
|
||||
|
||||
.create-main {
|
||||
width: min(1160px, calc(100% - 48px));
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.breadcrumb-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: #98a2b3;
|
||||
font-size: 14px;
|
||||
|
||||
.link {
|
||||
color: #1e6fff;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: #344054;
|
||||
}
|
||||
}
|
||||
|
||||
.content-card,
|
||||
.spec-bar,
|
||||
.submit-bar {
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
border: 1px solid rgba(219, 234, 254, 0.78);
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 12px 34px rgba(31, 72, 135, 0.08);
|
||||
}
|
||||
aside{
|
||||
margin: 0;
|
||||
}
|
||||
.content-card {
|
||||
padding: 24px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
color: #1f2937;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.link-btn {
|
||||
color: #1e6fff;
|
||||
font-size: 14px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.option-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.option-list--compact {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.option-btn {
|
||||
min-width: 88px;
|
||||
height: 40px;
|
||||
padding: 0 18px;
|
||||
color: #5f6b7a;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
border: 2px solid #e3e8f0;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&.active,
|
||||
&:hover {
|
||||
color: #1e6fff;
|
||||
border-color: #1e6fff;
|
||||
background: #eef5ff;
|
||||
}
|
||||
}
|
||||
|
||||
.image-option em {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -10px;
|
||||
padding: 2px 7px;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
font-style: normal;
|
||||
background: #ef4444;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.card-tip {
|
||||
margin: 0;
|
||||
color: #8a94a6;
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.host-table-wrap {
|
||||
overflow-x: auto;
|
||||
margin-bottom: 22px;
|
||||
border: 1px solid #edf1f7;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.host-table {
|
||||
width: 100%;
|
||||
min-width: 1060px;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
|
||||
th {
|
||||
padding: 14px 12px;
|
||||
color: #667085;
|
||||
text-align: left;
|
||||
font-weight: 700;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 16px 12px;
|
||||
color: #5f6b7a;
|
||||
border-top: 1px solid #edf1f7;
|
||||
}
|
||||
|
||||
tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
tr.selected {
|
||||
background: #f2f7ff;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-dot {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #1e6fff;
|
||||
border-radius: 50%;
|
||||
box-shadow: inset 0 0 0 4px #fff;
|
||||
background: #1e6fff;
|
||||
}
|
||||
|
||||
.host-name {
|
||||
color: #1f2937;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.gpu-name {
|
||||
color: #1e6fff;
|
||||
}
|
||||
|
||||
.gpu-memory,
|
||||
.muted {
|
||||
color: #8a94a6;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.free-count {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.price {
|
||||
display: block;
|
||||
color: #ef4444;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.origin-price {
|
||||
display: block;
|
||||
color: #98a2b3;
|
||||
font-size: 12px;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.field-block {
|
||||
margin-bottom: 18px;
|
||||
|
||||
h3 {
|
||||
margin: 0 0 12px;
|
||||
color: #1f2937;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.gpu-count-btn {
|
||||
min-width: 44px;
|
||||
padding: 0 14px;
|
||||
}
|
||||
|
||||
.disk-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 18px;
|
||||
color: #5f6b7a;
|
||||
font-size: 14px;
|
||||
|
||||
label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
accent-color: #1e6fff;
|
||||
}
|
||||
}
|
||||
|
||||
.disk-input {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
input {
|
||||
width: 92px;
|
||||
height: 34px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #dfe5ef;
|
||||
border-radius: 10px;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.spec-bar {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
padding: 18px 20px;
|
||||
margin-bottom: 18px;
|
||||
background: #eef5ff;
|
||||
|
||||
strong {
|
||||
flex-shrink: 0;
|
||||
color: #1e6fff;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #344054;
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
}
|
||||
|
||||
.select-wrap {
|
||||
position: relative;
|
||||
|
||||
select {
|
||||
width: 100%;
|
||||
height: 46px;
|
||||
padding: 0 14px;
|
||||
color: #667085;
|
||||
background: #fff;
|
||||
border: 1px solid #dfe5ef;
|
||||
border-radius: 12px;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.submit-bar {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0 !important;
|
||||
left: 0;
|
||||
z-index: 30;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border: none;
|
||||
border-top: 1px solid rgba(223, 229, 239, 0.95);
|
||||
border-radius: 0;
|
||||
box-shadow: 0 -10px 30px rgba(31, 72, 135, 0.08);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.submit-bar__inner {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
width: min(1600px, calc(100% - 48px));
|
||||
min-height: 86px;
|
||||
margin: 0 auto;
|
||||
padding: 18px 0;
|
||||
}
|
||||
|
||||
.submit-costs,
|
||||
.submit-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.submit-costs span,
|
||||
.balance {
|
||||
color: #667085;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.submit-costs strong {
|
||||
color: #ef4444;
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.detail-btn {
|
||||
color: #1e6fff;
|
||||
font-size: 13px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.balance em {
|
||||
margin-left: 6px;
|
||||
color: #ef4444;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.cancel-btn,
|
||||
.create-btn {
|
||||
height: 48px;
|
||||
padding: 0 26px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
color: #667085;
|
||||
background: #fff;
|
||||
border: 1px solid #dfe5ef;
|
||||
|
||||
&:hover {
|
||||
color: #1e6fff;
|
||||
border-color: #1e6fff;
|
||||
}
|
||||
}
|
||||
|
||||
.create-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, #1e6fff, #43a3ff);
|
||||
border: none;
|
||||
box-shadow: 0 10px 20px rgba(30, 111, 255, 0.24);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 12px 24px rgba(30, 111, 255, 0.3);
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.create-main {
|
||||
width: calc(100% - 24px);
|
||||
}
|
||||
|
||||
.content-card {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.card-header,
|
||||
.spec-bar {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-bar__inner {
|
||||
width: calc(100% - 24px);
|
||||
min-height: auto;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.submit-costs,
|
||||
.submit-actions {
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.create-btn {
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1010
f/web-kboss/src/views/homePage/computeMarket/index.vue
Normal file
1010
f/web-kboss/src/views/homePage/computeMarket/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user