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>