k8s管理系统项目前端-workflow
k8s管理系统项目前端-workflow
<template>
<!-- 新的 div 容器,使用flex布局对齐内容 -->
<div class="deployment-header" style="flex: 1">
<div class="left-section">
<span>命名空间:</span>
<el-select v-model="selectedNamespace" placeholder="请选择">
<el-option
v-for="namespace in namespaces"
:key="namespace"
:label="namespace"
:value="namespace">
</el-option>
</el-select>
</div>
<!-- 移动的刷新按钮,保留在右侧 -->
<div class="right-group">
<el-button @click="refresh">
<el-icon><Refresh /></el-icon>
<span> 刷新 </span>
</el-button>
</div>
</div>
<div class="container">
<!-- 第一部分: 头部 -->
<!-- 步骤条展示,active属性控制到了哪一步 -->
<el-card class="workflow-head-card" shadow="never" :body-style="{padding:'30px 10px 20px 10px'}">
<el-steps :active="activeStep" align-center finish-status="success">
<el-step title="步骤1" description="选择工作流类型, ClusterIP NodePort Workflow"></el-step>
<el-step title="步骤2" description="填写Deployment Workflow Workflow表单"></el-step>
<el-step title="步骤3" description="创建Deployment Workflow Workflow"></el-step>
</el-steps>
</el-card>
<div class="header shadow">
<div class="left-group" >
<el-button type="primary" @click="showDrawer('drawer1')">
<el-icon><EditPen /></el-icon>
<span style="vertical-align: middle"> 创建工作流 </span>
</el-button>
<el-input placeholder="请输入内容" v-model="searchText" class="search-input" @input="handleInput">
<template #append>
<el-button type="primary" @click="search()">
<el-icon><Search /></el-icon>
</el-button>
</template>
</el-input>
</div>
</div>
<!-- 第二部分: 表格 -->
<div class="table-wrapper shadow">
<el-table :data="tableData" style="width: 100%" >
<el-table-column prop="ID" label="ID" align="center" width="60px">
<template #default="{ row }">
<div>
<span style="vertical-align: middle; color: #1395FF; cursor: pointer;" @click="handleRowClick(row)">
{{ row.ID }}
</span>
</div>
</template>
</el-table-column>
<el-table-column prop="Name" label="Name" align="center">
<template #default="{ row }">
<div>
<span style="vertical-align: middle; cursor: pointer;">
{{ row.Name }}
</span>
</div>
</template>
</el-table-column>
<el-table-column prop="Status" label="Type" align="center" >
<template #default="{ row }">
<div>
<el-tag class="ml-2" type="success">{{ row.Type }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="InsCount" label="实例数" align="center" >
<template #default="{ row }">
<div>
<el-tag class="ml-2" type="warning">{{row.Replicas}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="Deployment" label="Deployment" align="center" >
<template #default="{ row }">
<div>
<el-tag class="ml-2" type="warning">{{row.Deployment}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="Service" label="Service" align="center">
<template #default="{ row }">
<div>
<el-tag class="ml-2" type="warning">{{row.Service}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="Ingress" label="Ingress" align="center" >
<template #default="{ row }">
<div>
<el-tag class="ml-2" type="warning">{{row.Ingress}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" align="center" >
<template #default="{ row }">
<div>
<el-tag class="ml-2" type="warning">{{row.createTime}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="Operation" label="操作" align="center">
<template #default="{ row }">
<div class="button-group">
<el-button size="small" color="#F58D79" :dark="isDark" plain @click="restartDeployment(row)">
<el-icon><Tickets /></el-icon>
<span style="vertical-align: middle"> 详情 </span>
</el-button>
<el-button size="small" color="#F58D79" :dark="isDark" plain @click="confirmDelete(row)">
<el-icon><Tickets /></el-icon>
<span style="vertical-align: middle"> 删除 </span>
</el-button>
</div>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[5, 10, 20, 30]"
:small="small"
:disabled="disabled"
:background="background"
layout="total, sizes, prev, pager, next, jumper"
:total=workflowCount
class="paginations"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
<el-drawer
title="创建Workflow-步骤1"
v-model="drawers.drawer1"
direction="rtl"
>
<el-radio-group v-model="serviceType">
<el-radio label="clusterip">ClusterIP</el-radio>
<el-radio label="nodeport">NodePort</el-radio>
<el-radio label="ingress">Ingress</el-radio>
</el-radio-group>
<template #footer>
<el-button @click="closeDrawer('drawer1')">取消</el-button>
<el-button type="primary" @click="showDrawer('drawer2')">下一步</el-button>
</template>
</el-drawer>
<el-drawer
title="创建Workflow-步骤2"
v-model="drawers.drawer2"
direction="rtl"
>
<h3 style="margin-right: 10px;margin-bottom: 10px;">Deployment</h3>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules">
<div style="display: flex; align-items: center; margin-bottom: 20px;">
<span style="width: 100px; display: inline-block; text-align: right; margin-right: 10px;">名称:</span>
<el-form-item label="" prop="deploymentName">
<el-input v-model="formData.deploymentName" placeholder="请输入内容" style="width: 250px"></el-input>
</el-form-item>
</div>
<el-form-item label="" prop="createNamespace">
<div style="display: flex; align-items: center;margin-bottom: 20px">
<span style="width: 100px; display: inline-block; text-align: right; margin-right: 10px;">命名空间:</span>
<el-select v-model="formData.createNamespace" placeholder="请选择" style="width: 150px;">
<el-option
v-for="namespace in workNamespaces"
:key="namespace"
:label="namespace"
:value="namespace">
</el-option>
</el-select>
</div>
</el-form-item>
<el-form-item label="" prop="replicas">
<div style="display: flex; align-items: center;margin-bottom: 20px;">
<span style="width: 100px; display: inline-block; text-align: right; margin-right: 10px;">副本数:</span>
<el-input-number v-model="formData.replicas" :min="1" @change="handleChange" />
</div>
</el-form-item>
<div style="display: flex; align-items: center; margin-bottom: 20px;">
<span style="width: 100px; display: inline-block; text-align: right; margin-right: 10px;">镜像:</span>
<el-form-item label="" prop="images">
<el-input v-model="formData.images" placeholder="请输入内容" style="width: 250px"></el-input>
</el-form-item>
</div>
<div style="display: flex; align-items: center; margin-bottom: 20px;">
<span style="width: 100px; display: inline-block; text-align: right; margin-right: 10px;">标签:</span>
<el-form-item label="" prop="label">
<el-input v-model="formData.label" placeholder="请输入内容" style="width: 250px"></el-input>
</el-form-item>
</div>
<div style="display: flex; align-items: center; margin-bottom: 20px;">
<span style="width: 100px; display: inline-block; text-align: right; margin-right: 10px;">容器端口:</span>
<el-form-item label="" prop="port">
<el-input v-model="formData.port" placeholder="请输入内容" style="width: 250px"></el-input>
</el-form-item>
</div>
</el-form>
<!-- 更多输入项 -->
</div>
<template #footer>
<el-button @click="showDrawer('drawer1')">上一步</el-button>
<el-button @click="closeDrawer('drawer1')">取消</el-button>
<el-button type="primary" @click="validateForm">下一步</el-button>
</template>
</el-drawer>
<el-drawer
title="创建Workflow-步骤3"
v-model="drawers.drawer3"
direction="rtl"
>
<h3 style="margin-right: 10px;margin-bottom: 10px;">Service</h3>
<div>
<el-form ref="formRef2" :model="formData2" :rules="formRules2">
<!-- ClusterIP或NodePort时都显示 -->
<div style="display: flex; align-items: center;" v-if="serviceType === 'clusterip' || serviceType === 'nodeport'" >
<span style="width: 150px; display: inline-block; text-align: right; margin-right: 10px;margin-top: -13px">service端口:</span>
<el-form-item label="" prop="servicePort">
<el-input v-model="formData2.servicePort" placeholder="请输入内容" style="width: 250px"></el-input>
</el-form-item>
</div>
<!-- 仅当NodePort时显示 -->
<div style="display: flex; align-items: center;" v-if="serviceType === 'nodeport'" >
<span style="width: 150px; display: inline-block; text-align: right; margin-right: 10px;margin-top: -13px">NodePort端口:</span>
<el-form-item label="" prop="nodeport">
<el-input v-model="formData2.nodePort" placeholder="请输入内容" style="width: 250px"></el-input>
</el-form-item>
</div>
<!-- 仅当Ingress时显示 -->
<div style="display: flex; align-items: center;" v-if="serviceType === 'ingress'">
<span style="width: 150px; display: inline-block; text-align: right; margin-right: 10px;margin-top: -13px">service端口:</span>
<el-form-item label="" prop="servicePort">
<el-input v-model="formData2.servicePort" placeholder="请输入内容" style="width: 250px"></el-input>
</el-form-item>
</div>
<div style="display: flex; align-items: center;" v-if="serviceType === 'ingress'">
<el-divider />
</div>
<div style="display: flex; align-items: center;" v-if="serviceType === 'ingress'">
<h3 style="margin-right: 10px;margin-bottom: 10px;">Ingress</h3>
</div>
<div style="display: flex; align-items: center;" v-if="serviceType === 'ingress'">
<span style="width: 150px; display: inline-block; text-align: right; margin-right: 10px;margin-top: -13px">域名:</span>
<el-form-item label="" prop="domain">
<el-input v-model="formData2.domain" placeholder="请输入内容" style="width: 250px"></el-input>
</el-form-item>
</div>
<div style="display: flex; align-items: center;" v-if="serviceType === 'ingress'">
<span style="width: 150px; display: inline-block; text-align: right; margin-right: 10px;margin-top: -13px">PATH:</span>
<el-form-item label="" prop="path">
<el-input v-model="formData2.path" placeholder="请输入内容" style="width: 250px"></el-input>
</el-form-item>
</div>
<div style="display: flex; align-items: center;" v-if="serviceType === 'ingress'">
<span style="width: 150px; display: inline-block; text-align: right; margin-right: 10px;">匹配类型:</span>
<el-select v-model="formData2.type" placeholder="请选择" style="width: 150px;">
<el-option
v-for="type in workType"
:key="type.value"
:label="type.label"
:value="type.value">
</el-option>
</el-select>
</div>
</el-form>
</div>
<template #footer>
<el-button @click="showDrawer('drawer2')">上一步</el-button>
<el-button @click="closeDrawer()">取消</el-button>
<el-button type="primary" @click="validateForm2">创建</el-button>
</template>
</el-drawer>
</template>
<script setup>
import {onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, watch} from "vue";
import {DELETE_NAMESPACE, DEPLOYMENT_POD_LIST, NAMESPACE_LIST, WORKFLOW_LIST} from "../../../api/k8s.js";
import {ElDrawer, ElButton, ElRadio, ElRadioGroup, ElIcon, ElMessage, ElMessageBox} from 'element-plus';
import json2yaml from "json2yaml";
import {CREATE_WORKFLOW, DELETE_WORKFLOW} from "../../../api/workflow.js";
import _, {parseInt} from "lodash";
let currentPage = ref(1)
let pageSize = ref(5)
const small = ref(false)
const background = ref(false)
const disabled = ref(false)
var workflowCount = ref(0)
const activeStep = ref(0)
const namespaces = ref([]); // 用于存储命名空间列表
const selectedNamespace = ref('default'); // 用于存储用户选择的命名空间
const workNamespaces = ref([]); // 用于存储命名空间列表
const searchText = ref('');
const workType = ref([
{
value: 'Prefix',
label: 'Prefix'
},
{
value: 'Exact',
label: 'Exact'
},
{
value: 'ImplementationSpecific',
label: 'ImplementationSpecific'
}
]); // 用于存储命名空间列表
const handleSizeChange = (val) => {
console.log(`${val} items per page`)
namespaceAllParameters.page_size = val
localStorage.setItem('workpageSize',val)
getWorkflowData()
}
const confirmDelete = async (row)=> {
ElMessageBox.confirm(
`确定要删除workflow "${row.ID}" 吗?`,
'警告',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
).then(async () => {
try {
const id = row.ID
const resp = await DELETE_WORKFLOW(id)
console.log("————————————————",resp)
ElMessage({
type: 'success',
message: `workflow "${row.ID}" 已被删除`
});
getWorkflowData()
}catch (e){
console.log(e)
ElMessage.error(`删除失败: ${e}`);
}
}).catch(() => {
ElMessage({
type: 'info',
message: '已取消删除'
});
});
}
const search = async () => {
const workflowsearchparams = reactive({
filterName: searchText.value,
page_size:pageSize,
page_number:currentPage,
namespace: selectedNamespace.value,
})
try {
const resp = await WORKFLOW_LIST(workflowsearchparams)
console.log(resp)
if (resp && resp.data && Array.isArray(resp.data.Items)) {
// 创建一个对象来映射service_type到对应的字符串
const serviceTypeMapping = {
0: "Cluster_IP",
1: "NodePort",
2: "LoadBalancer",
3: "ExternalName", // 假设这是第四种类型
4: "Ingress"
};
tableData.value = resp.data.Items.map((item) => {
// 使用对象字典获得service_type的显示值
let serviceTypeDisplay = serviceTypeMapping[item.service_type] || "Unknown";
return {
ID: item.id,
Name: item.name,
Type: serviceTypeDisplay,
Replicas: item.replicas,
Deployment: item.deployment_name,
Service: item.service_name,
Ingress: item.ingress_name,
createTime: removeTimeZoneFromDateString(item.created_at)
// 其他属性...
};
});
}
// 更新部署计数
workflowCount.value = resp.data.count;
}catch (e){
console.log(e)
console.log("你吗")
}
}
const handleInput = _.debounce(async () => {
await search();
}, 500); // 使用 lodash 的 debounce 函数来防抖,防止搜索操作太频繁
const formData = ref({
deploymentName: '',
images:'',
replicas:1,
createNamespace:'default',
label:'',
port:''
// 初始化更多的表单数据
});
const formData2 = ref({
servicePort: '',
nodePort:'',
domain:'',
path:'',
type: 'Prefix',
// 初始化更多的表单数据
});
const handleChange = (value) => {
console.log(value)
}
const formRef = ref(null);
const formRef2 = ref(null);
const validateForm = async () => {
try {
await formRef.value.validate();
showDrawer('drawer3');
console.log(serviceType.value)
console.log(formData.value)
} catch (error) {
ElMessage.error('表单验证失败,请检查输入');
}
};
const validateForm2 = async () => {
try {
await formRef2.value.validate();
createWorkflow()
resetFormData2();
closeDrawer()
console.log(serviceType.value)
console.log(formData.value)
} catch (error) {
ElMessage.error('表单验证失败,请检查输入');
}
};
// 新的函数来重置 formData2
const resetFormData2 = () => {
formData2.value = {
servicePort: '',
nodePort: '',
domain: '',
path: '',
type: 'Prefix',
};
formData.value = {
deploymentName: '',
images:'',
replicas:1,
createNamespace:'default',
label:'',
port:''
}
};
const handleCurrentChange = (val) => {
console.log(`current page: ${val}`)
currentPage.value = val
namespaceAllParameters.page_number = val
localStorage.setItem('workcurrentPage', val); // 将当前页保存在 localStorage
getWorkflowData()
}
function parseLabels(labelString) {
const labelObject = {};
const labels = labelString.split(','); // 分割成数组
labels.forEach(label => {
const [key, value] = label.split('='); // 分割键值对
if (key && value) {
labelObject[key.trim()] = value.trim();
}
});
return labelObject;
}
const createWorkflow = async () => {
let typeValue;
console.log(serviceType,"================================")
if (serviceType.value === 'clusterip'){
typeValue = 0
}else if (serviceType.value === 'nodeport'){
typeValue = 1
}else if (serviceType.value === 'ingress'){
typeValue = 4
}
// 定义 createWLParameters 的基础结构
const baseParameters = {
workflowName: formData.value.deploymentName,
deployment: formData.value.deploymentName,
namespace: formData.value.createNamespace,
podName: formData.value.deploymentName,
type: typeValue,
replicas: formData.value.replicas,
image: formData.value.images,
label: parseLabels(formData.value.label),
containers: [{
name: formData.value.deploymentName,
image: formData.value.images,
ports: [parseInt(formData.value.port)]
}],
serviceName: formData.value.deploymentName,
servicePort: parseInt(formData2.value.servicePort),
serviceNodePort: parseInt(formData2.value.nodePort),
};
// 根据 serviceType 的值决定是否添加 ingress 相关的配置
if (serviceType.value === 'ingress') {
baseParameters.ingressName = formData.value.deploymentName;
baseParameters.rules = [{
host: formData2.value.domain,
paths: [{
path: formData2.value.path,
pathType: formData2.value.type,
serviceName: formData.value.deploymentName,
servicePort: parseInt(formData.value.port)
}]
}]
}
console.log(baseParameters,"_)_______")
try {
const resp = await CREATE_WORKFLOW(baseParameters);
console.log(resp)
if (resp && resp.code === 200) {
// 只返回命名空间的名称数组
getWorkflowData()
} else {
throw new Error('无法获取数据或数据格式不正确');
}
} catch (e) {
console.error(e);
return []; // 出错时返回空数组
}
}
const formRules = {
deploymentName: [
{ required: true, message: '请输入名称', trigger: 'blur' }
],
replicas: [
{ required: true, message: '请输入副本数', trigger: 'change' }
],
label: [
{ required: true, message: '请输入标签', trigger: 'blur' }
],
createNamespace: [
{ required: true, message: '请选择命名空间', trigger: 'change' }
],
images: [
{ required: true, message: '请输入镜像', trigger: 'blur' }
],
port: [
{ required: true, message: '请输入容器端口', trigger: 'blur' }
],
// 其他校验规则
};
const formRules2 = {
servicePort: [
{ required: true, message: '请输入servicePort', trigger: 'blur' }
],
nodePort: [
{ required: true, message: '请输入nodePort', trigger: 'change' }
],
domain: [
{ required: true, message: '请输入域名', trigger: 'blur' }
],
path: [
{ required: true, message: '请选择PATH', trigger: 'change' }
],
// 其他校验规则
};
const fetchNamespaces = async () => {
namespaces.value = await getNSData();
workNamespaces.value = await getNSData()
};
const namespaceAllParameters = reactive({
page_size: 100000,
page_number: 1,
})
// 设置watcher来监听selectedNamespace的改变
watch(selectedNamespace, (newNamespace) => {
// 更新deploymentParameters中的namespace
workflowparams.namespace = newNamespace;
// 重新调用getDeployData以获取新命名空间的部署数据
getWorkflowData();
});
const getNSData = async () => {
try {
const resp = await NAMESPACE_LIST(namespaceAllParameters);
if (resp && resp.code === 200 && resp.data && Array.isArray(resp.data.Items)) {
// 只返回命名空间的名称数组
return resp.data.Items.map(ns => ns.metadata.name);
} else {
throw new Error('无法获取数据或数据格式不正确');
}
} catch (e) {
console.error(e);
return []; // 出错时返回空数组
}
};
// 定义一个函数来移动到下一个步骤
const moveToNextStep = () => {
if (activeStep.value < 3) { // 假设有3个步骤
activeStep.value += 1; // 增加步骤
}
};
const drawers = ref({
drawer1: false,
drawer2: false,
drawer3: false,
// 初始化更多的drawer状态
});
const serviceType = ref('clusterip');
// 显示指定的drawer
const showDrawer = (drawerName) => {
console.log(drawerName)
if (drawerName === 'drawer1'){
activeStep.value = 0
}else if (drawerName === 'drawer2'){
activeStep.value = 1
}else if (drawerName === 'drawer3'){
activeStep.value = 2
}
for (const key in drawers.value) {
drawers.value[key] = false; // 先关闭所有drawer
}
drawers.value[drawerName] = true;
};
// 关闭指定的drawer
const closeDrawer = () => {
for (const key in drawers.value) {
drawers.value[key] = false; // 先关闭所有drawer
}
activeStep.value = 0
};
const tableData = ref([
{
ID: "",
Name: '',
Type: '-',
Replicas: '-',
Deployment: '',
Service:'',
Ingress: '',
createTime:"",
},
// ...其他数据项
]);
const workflowparams = reactive({
filterName: '',
page_size:pageSize,
page_number:currentPage,
namespace: selectedNamespace.value,
})
const getWorkflowData = async ()=>{
try {
const resp = await WORKFLOW_LIST(workflowparams)
console.log(resp)
if (resp && resp.data && Array.isArray(resp.data.Items)) {
// 创建一个对象来映射service_type到对应的字符串
const serviceTypeMapping = {
0: "Cluster_IP",
1: "NodePort",
2: "LoadBalancer",
3: "ExternalName", // 假设这是第四种类型
4: "Ingress"
};
tableData.value = resp.data.Items.map((item) => {
// 使用对象字典获得service_type的显示值
let serviceTypeDisplay = serviceTypeMapping[item.service_type] || "Unknown";
return {
ID: item.id,
Name: item.name,
Type: serviceTypeDisplay,
Replicas: item.replicas,
Deployment: item.deployment_name,
Service: item.service_name,
Ingress: item.ingress_name,
createTime: removeTimeZoneFromDateString(item.created_at)
// 其他属性...
};
});
}
// 更新部署计数
workflowCount.value = resp.data.count;
}catch (e){
console.log(e)
console.log("你吗")
}
}
function removeTimeZoneFromDateString(dateString) {
// 解析日期字符串为Date对象
var date = new Date(dateString);
// 将Date对象格式化为本地时间的字符串表示,不包含时区信息
return date.toLocaleString('zh-CN', { hour12: false }).replace(/\//g, '-').replace(/,/, '');
}
// 在setup函数中定义定时器
let refreshInterval = null;
const loadSelection = () => {
selectedNamespace.value = localStorage.getItem('selectedworkNamespace') || '';
};
onMounted(() => {
loadSelection();
refreshInterval = setInterval(() => {
getWorkflowData();
}, 30000); // 每30秒刷新一次
});
onBeforeMount(async ()=>{
clearInterval(refreshInterval);
// 尝试从 localStorage 中读取状态
const savedPageSize = localStorage.getItem('workpageSize');
const savedCurrentPage = localStorage.getItem('workcurrentPage');
// 如果存在则更新到响应式变量中
if (savedPageSize) {
pageSize.value = parseInt(savedPageSize, 10);
}
if (savedCurrentPage) {
currentPage.value = parseInt(savedCurrentPage, 10);
}
getWorkflowData()
await fetchNamespaces(); // 获取并更新命名空间列表
})
onBeforeUnmount(() => {
saveSelection(selectedNamespace.value);
});
const saveSelection = (value) => {
localStorage.setItem('selectedworkNamespace', value);
};
</script>
<style scoped>
.loading-icon {
width: 20px; /* 或者您希望的任何尺寸 */
height: 20px; /* 保持与宽度相同以保持图标的纵横比 */
margin-left: 5px; /* 添加一点空间在状态文本和图标之间 */
animation: loading-spin 2s linear infinite;
}
/* 如果需要,可以移除这个类,因为它可能会导致对齐问题 */
/* .loader {
animation: loader-spin 1s linear infinite;
} */
@keyframes loading-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.container {
margin: 10px;
background-color: #F2F2F2;
}
.deployment-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px;
margin: 10px;
margin-bottom: -10px;
background: #FFF;
border: 2px solid #ebeef5;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
/* 新增左侧部分样式,放置“命名空间:”文本和下拉框 */
.left-section {
display: flex;
align-items: center;
}
.header {
display: flex;
align-items: center;
justify-content: space-between; /* 添加此属性对子元素进行分散对齐 */
margin-bottom: 0px;
gap: 10px;
padding: 10px;
background: #FFF;
border: 2px solid #ebeef5;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.table-wrapper {
background: #FFF;
border: 2px solid #ebeef5; /* 浅色边框线 */
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.paginations {
margin-top: 10px;
margin-left: 20px;
margin-bottom: 10px;
}
/* 左侧组合样式,创建按钮和搜索框靠在一起 */
.left-group {
display: flex; /* 启用Flexbox */
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
height: 40px; /* 给予足够的高度,根据需要调整 */
gap: 10px; /* 或者您希望的任何值 */
}
/* Element Plus的步骤条组件特定样式 */
.el-steps {
flex-shrink: 0; /* 防止组件在容器太小的情况下缩小 */
}
/* 右侧刷新按钮 */
.right-group {
display: flex;
align-items: center;
}
.deployment-header {
display: flex;
align-items: center;
height: 30px;
padding: 10px;
margin: 10px;
margin-bottom: -10px;
background: #FFF;
border: 2px solid #ebeef5;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.container {
margin: 10px;
}
.input-row .el-input {
flex: 1;
}
.input-row label {
margin-left: 10px; /* 或者需要的任何值 */
}
.input-row .el-input .el-input__inner {
width: 100%; /* 让输入框充满其容器 */
}
.input-containername p {
margin-bottom: 10px; /* 为“CPU:”下方添加一些间隔 */
}
.button-group {
display: flex;
justify-content: center;
align-items: center;
gap: 10px; /* 调整按钮之间的间隔 */
}
.steps-center {
width: 100%;
}
/* 调整步骤条卡片的内边距以增加高度 */
.workflow-head-card {
padding: 40px 10px 20px 10px; /* 根据需要调整数值 */
/* 其他样式保持不变 */
}
/* 如果需要,还可以添加步骤条自己的内边距 */
.el-steps {
padding: 10px 0; /* 上下增加内边距,根据需要调整数值 */
/* 如果步骤条内部元素(比如标题或描述)需要调整间距,可以在这里设置 */
}
.header shadow {
height: 30px;
}
.search-input {
margin-right: 10px; /* 或者您希望的任何值 */
}
</style>
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 J.のblog!
评论