k8s管理系统项目前端-user

一、userView

<template>

  <div class="container">
    <!-- 第一部分: 头部 -->
    <div class="header shadow">
      <div class="left-group">
        <el-button disabled  type="primary" @click="createUser">
          <span style="vertical-align: middle"> 创建 </span>
        </el-button>
        <el-button   type="primary" @click="transferDialogVisible = true">
          <span style="vertical-align: middle"> 添加到组 </span>
        </el-button>
        <el-button   type="primary" @click="DeleteUser(row)">
          <span style="vertical-align: middle"> 删除</span>
        </el-button>
        <el-button disabled  type="primary" @click="UnlockUser">
          <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%"      ref="multipleTable" @selection-change="handleSelectionChange">
        <el-table-column type="selection" width="55" />
        <el-table-column prop="id" label="Id"  align="center">
          <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; color: #1395FF; cursor: pointer;" @click="handleRowClick(row)">
                {{ row.name }}
              </span>
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="phone" label="手机号" align="center">
          <template #default="{ row }">
            <div>
                {{ row.phone }}
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="email" label="邮箱" align="center">
          <template #default="{ row }">
            <div>
              {{ row.email }}
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="type" label="账号类型" align="center" >
          <template #default="{ row }">
            <div>
              <p style="color: #4795EE; margin: 0;">{{ row.type }}</p>
            </div>
          </template>
        </el-table-column>


        <el-table-column prop="status" label="状态" width="100px" align="center">
          <template #default="{ row }">
            <el-tag :type="row.statusType" class="ml-2">{{ row.status }}</el-tag>
          </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="#5AABFF" :dark="isDark" plain @click="addUserToGroup(row)">
                <el-icon><EditPen /></el-icon>
                <span style="vertical-align: middle"> 添加到组 </span>
              </el-button>
              <el-button size="small"  color="#F58D79" :dark="isDark" plain @click="DeleteUser(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=deploymentCount
          class="paginations"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
      />
    </div>
  </div>
  <el-dialog v-model="transferDialogVisible" title="请选择组">
    <el-transfer
        v-model="targetKeys"
        :data="groupData"
        :titles="['可选组', '已选组']"
        class="transfer-custom"
    ></el-transfer>
    <span slot="footer" class="dialog-footer">
    <el-button @click="transferDialogVisible = false">取消</el-button>
    <el-button type="primary" @click="handleTransferConfirm">确定</el-button>
  </span>
  </el-dialog>

</template>

<script setup>
import {nextTick, onBeforeMount, reactive, ref, watch} from 'vue';
import 'element-plus/dist/index.css';
import {  EditPen, Search } from '@element-plus/icons-vue';
import {
  DELETE_SECRET,
  PV_DETAIL, PV_LIST,
} from "../../../api/k8s.js";

import _, {parseInt} from 'lodash';
import json2yaml from 'json2yaml'
import router from "@/router/index.js";
import {ADDUSER_GROUP, DELETE_USER, GROUP_LIST, USER_LIST} from "../../../api/user.js";
import {ElMessage, ElMessageBox} from "element-plus";
let currentPage = ref(1)
let pageSize = ref(5)
const small = ref(false)
const background = ref(false)
const disabled = ref(false)
const selectedIds = ref([])
var deploymentCount = ref(0)
const multipleTable = ref()
const handleSelectionChange = (selection)=> {

  selectedIds.value = selection.map(item => item.id)
  console.log(selectedIds.value)
}
const transferDialogVisible = ref(false);
const groupData = ref([]); // 这里应该是一个包含所有组的数组
const targetKeys = ref([]); // 这里是穿梭框右侧的key数组
const renderFunc = (h, option) => {
  return h('span', option.label);
};

const handleTransferConfirm = async () => {
  console.log('已选组:', targetKeys.value);
  console.log(selectedIds.value)
  const addUser_group = reactive({
    user_id: selectedIds.value,
    group_id: targetKeys.value
  })
  console.log(addUser_group.value)
  try {
    const resp = await ADDUSER_GROUP(addUser_group);
    console.log("添加用户到组 ", resp);

  } catch (e) {
    console.error(e);
  }
  // 此处可以添加关联组的逻辑
  transferDialogVisible.value = false;
  // 调用 API 关联组或者执行其他操作
};

const handleSizeChange = (val) => {
  console.log(`${val} items per page`)
  // namespaceAllParameters.page_size = val
  localStorage.setItem('servicepageSize',val)
  getUserData()
}
const handleCurrentChange = (val) => {
  console.log(`current page: ${val}`)
  currentPage.value = val
  // namespaceAllParameters.page_number = val
  localStorage.setItem('servicecurrentPage', val); // 将当前页保存在 localStorage

  getUserData()
}
// 在模板中可以直接使用 Refresh, EditPen, 和 Search
// 辅助函数,用于隐藏手机号中间四位
const hidePhoneMiddleDigits = (phone) => {
  if (phone && phone.length === 11) {
    return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
  }
  return phone; // 如果格式不正确,返回原始值
};


const createUser = ()=>{

}
const addUserToGroup = (params)=>{
    selectedIds.value = [params.id]
  console.log(selectedIds.value)
}
const DeleteUser = async ()=>{
  ElMessageBox.confirm(
      `确定要删除用户吗?`,
      '警告',
      {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      }
  ).then(async () => {
    const user_params = reactive({
      users_id: selectedIds.value
    })
    try {
      const resp = await DELETE_USER(user_params);
      console.log("————————————————",resp)
      ElMessage({
        type: 'success',
        message: `用户已被删除`
      });
      getUserData()

    }catch (e){
      console.log(e)
      ElMessage.error(`删除失败: ${e}`);
    }
  }).catch(() => {
    ElMessage({
      type: 'info',
      message: '已取消删除'
    });
  });

}
const UnlockUser = ()=>{

}
const handleInput = ()=> {

}
const handleRowClick = (row) => {
  router.push({
    name: 'userDetail',
    params: { id: row.id}
  });

};

const createDialogVisible = ref(false);

// fetchNamespaces 函数使用 getNSData 的返回值来更新命名空间的下拉菜单
// getNSData 函数获取命名空间并返回名称数组




const formatDateTime = (dateTimeString) => {
  return dateTimeString.replace('T', ' ').replace('Z', '');
};
// 示例数据和方法
const searchText = ref('');
const tableData = ref([
  {
    id: "",
    name: "",
    nickName: '',
    phone: '',
    email: '',
    type: '',
    status: '',
    createTime:"",
    Operation:"",
  },
  // ...其他数据项
]);

const search = async () => {
  console.log('执行搜索:', searchText.value);
  const searchParameters = reactive({
    page_size: pageSize,
    page_number: currentPage,
    filterName: searchText.value,
    namespace: ''
  })
  try {
    const resp = await PV_LIST(searchParameters);

    if (resp && resp.data && Array.isArray(resp.data.items)) {
      tableData.value = resp.data.items.map((item) => {
        console.log(item.spec.claimRef,'握草无情')
        let pvc
        if (item.spec.claimRef === undefined) {
          pvc = '-'
        }else {
          pvc = item.spec.claimRef.name
        }
        // 根据后端数据结构提取所需信息
        return {
          name: item.metadata.name, // PersistentVolume的名字
          status: item.status.phase, // PersistentVolume的状态
          mode: item.spec.volumeMode, // PersistentVolume的模式
          capacity: item.spec.capacity.storage, // PersistentVolume的容量
          pvc: pvc, // PVC信息,根据实际情况填写或调整
          accessModes: item.spec.accessModes.join(', '), // PersistentVolume的访问模式
          reclaimPolicy: item.spec.persistentVolumeReclaimPolicy, // PersistentVolume的回收策略
          createTime: formatDateTime(item.metadata.creationTimestamp), // 创建时间,假设您已定义formatDateTime函数
          // 其他属性可以根据需要添加
        };
      });
    }
    // 更新部署计数
    deploymentCount.value = resp.data.count;
  } catch (e) {
    console.error(e);
    // 处理错误情况,可能要向用户显示错误信息或日志输出
  }
};

const refresh = () => {
  getUserData()
  console.log('刷新表格数据');
};
const updateParameters = reactive({
  name: "",
  namespace: '',
  content: ""
})


const user_params = reactive({
  page_size: pageSize,
  page_number: currentPage,
})
const group_params = reactive({
  page_size: pageSize,
  page_number: currentPage,
})
const getGroupsList = async () => {
  try {
    const resp = await GROUP_LIST(group_params);
    console.log("组啊===== ", resp);
    if (resp && resp.data && resp.data.items) {
      // Map items to the expected data structure for el-transfer
      groupData.value = resp.data.items.map(item => ({
        key: item.id,       // 假设id是唯一的
        label: item.name,   // label是显示在列表中的文本
        disabled: false     // 可以根据需要设置
      }));
    }
  } catch (e) {
    console.error(e);
  }
}

const getUserData = async () => {
  try {
    const resp = await USER_LIST(user_params);
    console.log("用户", resp);
    if (resp && resp.data && Array.isArray(resp.data.items)) {
      tableData.value = resp.data.items.map((item) => {
        // 用户类型判断
        const userType = item.username === 'admin' ? '管理员' : '一般用户';
        // 用户状态判断
        let userStatus = '';
        let userStatusType = '';
        switch (item.status) {
          case 0:
            userStatus = '正常';
            userStatusType = 'success';
            break;
          case 1:
            userStatus = '临时锁定';
            userStatusType = 'error';
            break;
          case 2:
            userStatus = '永久锁定';
            userStatusType = 'error';
            break;
            // 默认情况,您可以根据需要添加其他状态
          default:
            userStatus = '未知';
            userStatusType = 'info';
            break;
        }

        return {
          id: item.id,
          name: item.username,
          phone: hidePhoneMiddleDigits(item.phone),
          nickName: item.nickname,
          email: item.email,
          type: userType,
          status: userStatus,
          statusType: userStatusType, // 新增属性,用于标识状态类型
          createTime: formatDateTime(item.created_at),
        };
      });
    }
    // 更新部署计数
    deploymentCount.value = resp.data.count;
  } catch (e) {
    console.error(e);
  }
};
// const getDeployDetailData = async (serviceName)=>{
//   try {
//     const DeploymentDetailparams = reactive({
//       name: serviceName,
//       namespace: selectedNamespace.value
//     })
//     console.log("ddddddddd",DeploymentDetailparams)
//     const resp = await PV_DETAIL(DeploymentDetailparams)
//     console.log("cccccccc",resp)
//     console.log("yaml =======",json2yaml.stringify(resp.data))
//     yamlContent.value = json2yaml.stringify(resp.data); // 确保将数据赋值给 yamlContent
//     currentDeployName.value = resp.data.metadata.name
//     updateParameters.content = yamlContent.value
//     await  nextTick()
//     dialogVisible.value = true;
//
//   }catch (e){
//     console.log(e)
//     console.log("你吗")
//   }
// }
// 在setup函数中定义定时器
let refreshInterval = null;



onBeforeMount( async ()=> {
  clearInterval(refreshInterval);
  // 尝试从 localStorage 中读取状态
  const savedPageSize = localStorage.getItem('nspageSize');
  const savedCurrentPage = localStorage.getItem('nscurrentPage');

  // 如果存在则更新到响应式变量中
  if (savedPageSize) {
    pageSize.value = parseInt(savedPageSize, 10);
  }
  if (savedCurrentPage) {
    currentPage.value = parseInt(savedCurrentPage, 10);
  }
  getUserData()
  getGroupsList()
  // 假设这是从 API 获取的组列表
  groupData.value = [
    { key: 1, label: '组1' },
    { key: 2, label: '组2' },
    // ...更多组
  ];

});
</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;
}

/* 确保下拉框和文本在同一行 */
.el-select {
  margin-left: 10px; /* 为下拉框添加左侧间隔 */
}
.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);
}

.search-input {
  /*flex-grow: 1;*/
  width: 200px;
}

.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;
  align-items: center;
  gap: 10px; /* You can adjust the gap as needed */
}

/* 右侧刷新按钮 */
.right-group {
  display: flex;
  align-items: center;
}
.yaml-content {
  background-color: #f5f5f5;
  border-left: 3px solid #4795EE;
  padding: 15px;
  white-space: pre-wrap;
  text-align: left;
  margin: 20px 0;
  overflow-x: auto;
}
.status-active {
  color: #67C23A;
}

.status-inactive {
  color: red;
}
.dialog-footer {
  /*text-align: right;*/
  display: flex;
  justify-content: flex-end;
  padding: 8px;
}




.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);
}



.form-item-with-help {
  margin-top: 10px;
  margin-left: 20px;
}
.form-item-with-resource {
  margin-top: 10px;
  margin-left: 20px;
}
.form-item {
  flex-wrap: wrap; /* 确保换行 */
  /*align-items: center; !* 如果需要垂直居中 *!*/
  margin-bottom: 10px; /* 保持垂直间距 */
}

.input-item {
  width: 200px;
  margin-right: 10px;
}
.help-text {
  display: block; /* 保证帮助文本在新的行显示 */
  font-size: 12px;
  width: 1000px;
  color: #999999;
  margin-top: 5px;
}
.input-with-help {
  margin-top: 20px;
  display: flex;
  flex-direction: column;
}
.input-deployname {
  width: 300px; /* 限制这个div的宽度 */
  display: flex;
  flex-direction: column; /* 让子元素垂直排列 */
}
.input-containername {
  width: 250px; /* 限制这个div的宽度 */
  display: flex;
  margin-left: -50px;
  flex-direction: column; /* 让子元素垂直排列 */
}

.add-button {
  margin-top: -20px; /* 添加按钮与输入行之间的空间 */
  margin-left: 200px;
}

.input-container-cpu {
  display: flex;
  flex-direction: column;
  /*padding-left: -20px; !* 往右移动整个容器 *!*/

}

.cpu-label {
  margin-bottom: 5px; /* 调整为所需的间距 */
  margin-left: -50px;
}


.container {
  margin: 10px;
}
.item-container {
  margin-bottom: 10px;
}
.content-container {
  background-color: #F2F2F2;
  padding: 10px;
  display: flex;
  width: 700px;
  justify-content: space-between;
  align-items: center;
  transition: height 0.3s;
  overflow: hidden;
  position: relative;

}
.content-container.collapsed {
  height: 40px;
}
.info {
  display: flex;
  align-items: center;
}
.actions {
  position: absolute;
  right: 10px; /* 调整为需要的值 */
  top: 10px; /* 调整为需要的值 */
}



.input-row {
  display: flex;
  align-items: center;
  gap: 10px;
  /*margin-left: -50px;*/
}
.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:”下方添加一些间隔 */
}

.form-item-resource {
  margin-left: 20px; /* 整个表单项向右移动 20px */
  margin-top: 10px;
}
.custom-form-item {
  height: auto;
  overflow: auto;
}
.check-method-container,
.execution-command-container {
  display: flex;
  align-items: center;
  margin-bottom: 10px; /* 添加一些垂直间距 */
}

.method-label {
  width: 100px;
  margin: 10px;
  margin-right: 20px; /* 文本与选择框/输入框之间的间隔 */
}
.command-label {
  /*margin-right: 20px; !* 文本与输入框之间的间隔 *!*/
  width: 100px;
  margin: 10px;

}
.livenessdiv {
  background-color: white;
  width: 500px;
}
.port-range {
  margin-left: 10px;
}
.input-healthContainer {
  margin-left: -50px;
}
.el-select,
.el-input {
  /*flex-grow: 1; !* 输入框和选择框将占据剩余空间 *!*/
  max-width: calc(100% - 100px); /* 限制最大宽度,这里的100px是示例,可以根据实际情况调整 */
}
.button-group {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px; /* 调整按钮之间的间隔 */
}
.variables-header {
  display: flex;
  justify-content: space-between;
  padding-bottom: 10px;
}
.variable-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
}
.variable-name, .variable-value {
  width: 100px;
}
.variable-input {
  flex: 1;
  margin-right: 10px;
}
.el-input {
  max-width: 95%;
}
/* CSS */
.transfer-custom {
  flex: 1; /* 使两个面板均匀分配空间 */
  margin-left: 120px;
}

.transfer-custom .el-transfer {
  display: flex;
  align-items: stretch; /* 确保两个面板高度相同 */
}


</style>

二、userInfoView

<template>

  <div class="container">
    <!-- 第一部分: 头部 -->
    <div class="header shadow">
      <el-page-header @back="goBack">
        <template #content>
          <span style="font-size: 14px"> 用户详情 </span>
        </template>
      </el-page-header>
    </div>

    <div class="info shadow">
      <h4 class="InfoHeader">用户信息</h4>
      <div class="user-info">
        <p><span class="label">用户名:</span><span class="value" id="username-value">{{userInfo.username}}</span></p>
        <p><span class="label">昵称:</span><span class="value" id="nickname-value">{{userInfo.nickname}}</span></p>
        <p><span class="label">手机号:</span><span class="value" id="phone-value">{{userInfo.phone}}</span></p>
        <p><span class="label">邮箱:</span><span class="value" id="email-value">{{userInfo.email}}</span></p>
      </div>
    </div>
    <div class="roleInfo shadow">
      <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
        <el-tab-pane label="已关联的权限" name="first">
          <el-button disabled  type="primary" @click="addUserPermission">
            <span style="vertical-align: middle">关联策略  </span>
          </el-button>
          <el-button  disabled  type="primary" @click="deleteUserPermission">
            <span style="vertical-align: middle"> 解除策略 </span>
          </el-button>
          <el-table
              ref="multipleTable"
              :data="permissionData"
              style="width: 100%;margin-top: 10px"
              @selection-change="permissionDataChange"
          >
            <el-table-column type="selection" width="55" />
            <el-table-column prop="id" label="ID"  align="center" style="50px">
              <template #default="{ row }">
                <div>
                  <span style="vertical-align: middle; color: #1395FF; cursor: pointer;">
                    {{ row.id }}
                  </span>
                </div>
              </template>
            </el-table-column>
            <el-table-column prop="resource" label="资源"  align="center">
              <template #default="{ row }">
                <div>
              <span style="vertical-align: middle; color: #1395FF; cursor: pointer;">
                {{ row.resource }}
              </span>
                </div>
              </template>
            </el-table-column>
            <el-table-column prop="operation" label="操作"  align="center">
              <template #default="{ row }">
                <div>
              <span style="vertical-align: middle; color: #1395FF; cursor: pointer;">
                {{ row.operation }}
              </span>
                </div>
              </template>
            </el-table-column>
            <el-table-column prop="description" label="描述"  align="center">
              <template #default="{ row }">
                <div>
              <span style="vertical-align: middle; color: #1395FF; cursor: pointer;">
                {{ row.description }}
              </span>
                </div>
              </template>
            </el-table-column>
          </el-table>
        </el-tab-pane>
        <el-tab-pane label="已加入的组" name="second">
          <el-table
              ref="multipleTables"
              :data="groupData"
              style="width: 100%;margin-top: 10px"
              @selection-change="groupDataChange"
          >
            <el-table-column type="selection" width="55" />
            <el-table-column prop="id" label="ID"  align="center">
              <template #default="{ row }">
                <div>
                  <span style="vertical-align: middle; color: #1395FF; cursor: pointer;">
                    {{ row.id }}
                  </span>
                </div>
              </template>
            </el-table-column>
            <el-table-column prop="name" label="名称"  align="center">
              <template #default="{ row }">
                <div>
              <span style="vertical-align: middle; color: #1395FF; cursor: pointer;">
                {{ row.name }}
              </span>
                </div>
              </template>
            </el-table-column>
            <el-table-column prop="description" label="描述"  align="center">
              <template #default="{ row }">
                <div>
              <span style="vertical-align: middle; color: #1395FF; cursor: pointer;">
                {{ row.description }}
              </span>
                </div>
              </template>
            </el-table-column>
            <el-table-column prop="created_at" label="创建时间"  align="center">
              <template #default="{ row }">
                <div>
              <span style="vertical-align: middle; color: #1395FF; cursor: pointer;">
                {{ row.created_at }}
              </span>
                </div>
              </template>
            </el-table-column>
          </el-table>

        </el-tab-pane>
      </el-tabs>
    </div>
  </div>

</template>

<script setup>
import router from "@/router/index.js";
import {onBeforeMount, reactive, ref} from "vue";
import {ADDUSER_GROUP, USER_DETAIL, USER_GROUP_LIST, USER_PERMISSION_LIST} from "../../../api/user.js";
const activeName = ref('first')
const multipleTable = ref()

const handleClick = (tab, event) => {
  console.log(tab, event)
}
const permissionData = ref([{
  id:'',
  resource: '',
  operation: '',
  description: '',
},])

const groupData = ref([{
  id:'',
  name: '',
  description: '',
  created_at: '',
},])
const permissionDataChange = (val) => {
  console.log(val)
  // multipleSelection.value = val
}
const groupDataChange = (val) => {
  console.log(val)
  // multipleSelection.value = val
}
const { id } = defineProps({
  id: String,
});
const addUserPermission = ()=>{

}
const deleteUserPermission = ()=>{

}
const getUserPermissions = async ()=>{
  try {
    console.log(id)
    const resp = await USER_PERMISSION_LIST(id)
    console.log("用户的权限:", resp);
    if (resp && resp.data && Array.isArray(resp.data.items)) {
      permissionData.value = resp.data.items.map((item) => ({
        id: item.id,
        resource: item.resource,
        operation: item.operation,
        description: item.description,
      }));
    }
  } catch (e) {
    console.error(e);
  }
}
const getUserGroups = async ()=>{
  try {
    console.log(id)
    const resp = await USER_GROUP_LIST(id)
    console.log("用户的组:", resp);
    if (resp && resp.data && Array.isArray(resp.data)) {
      groupData.value = resp.data.map((item) => ({
        id: item.id,
        name: item.name,
        created_at: item.created_at,
        description: item.description,
      }));
    }
  } catch (e) {
    console.error(e);
  }
}
const goBack = () => {
  router.go(-1)
}
const userInfo = ref({
  username: '',
  nickname: '',
  phone: '',
  email: ''
});

const getUserDetail = async () => {
  try {
    const resp = await USER_DETAIL(id);
    console.log("用户信息", resp);
    if (resp.code === 200) {
      userInfo.value.nickname = resp.data.nickname
      userInfo.value.username = resp.data.username
      userInfo.value.phone = resp.data.phone
      userInfo.value.email = resp.data.email
    }
  } catch (e) {
    console.error(e);
  }
  // 调用 API 关联组或者执行其他操作
};
onBeforeMount(()=>{
  getUserDetail()
  getUserPermissions()
  getUserGroups()
})

</script>

<style scoped>
.container {
  margin: 10px;
}
/* 左侧组合样式,创建按钮和搜索框靠在一起 */
.left-group {
  display: flex;
  align-items: center;
  gap: 10px; /* You can adjust the gap as needed */
}
.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);
}
.info {
  /*display: flex;*/
  /*align-items: center;*/
  /*justify-content: space-between; !* 添加此属性对子元素进行分散对齐 *!*/
  /*gap: 10px;*/
  padding: 10px;
  height: 250px;
  background: #FFF;
  border: 2px solid #ebeef5;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.roleInfo {
  /*display: flex;*/
  /*align-items: center;*/
  /*justify-content: space-between; !* 添加此属性对子元素进行分散对齐 *!*/
  /*gap: 10px;*/
  padding: 10px;
  height: 400px;
  background: #FFF;
  border: 2px solid #ebeef5;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
/* CSS */
.label {
  color: grey;
  margin-left: 20px;
  font-size: 13px;
}

.shadow {
  /* 阴影效果的样式 */
}
.InfoHeader {
  color: #333; /* 标题颜色 */
  margin-bottom: 10px; /* 标题下边距 */
}

.label {
  color: grey; /* 标签颜色 */
  font-size: 14px; /* 标签字体大小 */
  margin-right: 5px; /* 标签右边距 */
}

.value {
  color: #333; /* 值的字体颜色 */
  font-size: 14px; /* 值的字体大小 */
}
.demo-tabs > .el-tabs__content {
  padding: 32px;
  color: #6b778c;
  font-size: 32px;
  font-weight: 600;
}
</style>

三、groupView

<template>

  <div class="container">
    <!-- 第一部分: 头部 -->
    <div class="header shadow">
      <div class="left-group">
        <el-button disabled  type="primary" @click="createUser">
          <span style="vertical-align: middle"> 创建 </span>
        </el-button>
        <el-button   type="primary" @click="transferDialogVisible = true">
          <span style="vertical-align: middle"> 添加到组 </span>
        </el-button>
        <el-button   type="primary" @click="DeleteUser(row)">
          <span style="vertical-align: middle"> 删除</span>
        </el-button>
        <el-button disabled  type="primary" @click="UnlockUser">
          <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%"      ref="multipleTable" @selection-change="handleSelectionChange">
        <el-table-column type="selection" width="55" />
        <el-table-column prop="id" label="Id"  align="center">
          <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; color: #1395FF; cursor: pointer;" @click="handleRowClick(row)">
                {{ row.name }}
              </span>
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="phone" label="手机号" align="center">
          <template #default="{ row }">
            <div>
              {{ row.phone }}
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="email" label="邮箱" align="center">
          <template #default="{ row }">
            <div>
              {{ row.email }}
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="type" label="账号类型" align="center" >
          <template #default="{ row }">
            <div>
              <p style="color: #4795EE; margin: 0;">{{ row.type }}</p>
            </div>
          </template>
        </el-table-column>


        <el-table-column prop="status" label="状态" width="100px" align="center">
          <template #default="{ row }">
            <el-tag :type="row.statusType" class="ml-2">{{ row.status }}</el-tag>
          </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="#5AABFF" :dark="isDark" plain @click="addUserToGroup(row)">
                <el-icon><EditPen /></el-icon>
                <span style="vertical-align: middle"> 添加到组 </span>
              </el-button>
              <el-button size="small"  color="#F58D79" :dark="isDark" plain @click="DeleteUser(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=deploymentCount
          class="paginations"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
      />
    </div>
  </div>
  <el-dialog v-model="transferDialogVisible" title="请选择组">
    <el-transfer
        v-model="targetKeys"
        :data="groupData"
        :titles="['可选组', '已选组']"
        class="transfer-custom"
    ></el-transfer>
    <span slot="footer" class="dialog-footer">
    <el-button @click="transferDialogVisible = false">取消</el-button>
    <el-button type="primary" @click="handleTransferConfirm">确定</el-button>
  </span>
  </el-dialog>

</template>

<script setup>
import {nextTick, onBeforeMount, reactive, ref, watch} from 'vue';
import 'element-plus/dist/index.css';
import {  EditPen, Search } from '@element-plus/icons-vue';
import {
  DELETE_SECRET,
  PV_DETAIL, PV_LIST,
} from "../../../api/k8s.js";

import _, {parseInt} from 'lodash';
import json2yaml from 'json2yaml'
import router from "@/router/index.js";
import {ADDUSER_GROUP, DELETE_USER, GROUP_LIST, USER_LIST} from "../../../api/user.js";
import {ElMessage, ElMessageBox} from "element-plus";
let currentPage = ref(1)
let pageSize = ref(5)
const small = ref(false)
const background = ref(false)
const disabled = ref(false)
const selectedIds = ref([])
var deploymentCount = ref(0)
const multipleTable = ref()
const handleSelectionChange = (selection)=> {

  selectedIds.value = selection.map(item => item.id)
  console.log(selectedIds.value)
}
const transferDialogVisible = ref(false);
const groupData = ref([]); // 这里应该是一个包含所有组的数组
const targetKeys = ref([]); // 这里是穿梭框右侧的key数组
const renderFunc = (h, option) => {
  return h('span', option.label);
};

const handleTransferConfirm = async () => {
  console.log('已选组:', targetKeys.value);
  console.log(selectedIds.value)
  const addUser_group = reactive({
    user_id: selectedIds.value,
    group_id: targetKeys.value
  })
  console.log(addUser_group.value)
  try {
    const resp = await ADDUSER_GROUP(addUser_group);
    console.log("添加用户到组 ", resp);

  } catch (e) {
    console.error(e);
  }
  // 此处可以添加关联组的逻辑
  transferDialogVisible.value = false;
  // 调用 API 关联组或者执行其他操作
};

const handleSizeChange = (val) => {
  console.log(`${val} items per page`)
  // namespaceAllParameters.page_size = val
  localStorage.setItem('servicepageSize',val)
  getUserData()
}
const handleCurrentChange = (val) => {
  console.log(`current page: ${val}`)
  currentPage.value = val
  // namespaceAllParameters.page_number = val
  localStorage.setItem('servicecurrentPage', val); // 将当前页保存在 localStorage

  getUserData()
}
// 在模板中可以直接使用 Refresh, EditPen, 和 Search
// 辅助函数,用于隐藏手机号中间四位
const hidePhoneMiddleDigits = (phone) => {
  if (phone && phone.length === 11) {
    return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
  }
  return phone; // 如果格式不正确,返回原始值
};


const createUser = ()=>{

}
const addUserToGroup = (params)=>{
  selectedIds.value = [params.id]
  console.log(selectedIds.value)
}
const DeleteUser = async ()=>{
  ElMessageBox.confirm(
      `确定要删除用户吗?`,
      '警告',
      {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      }
  ).then(async () => {
    const user_params = reactive({
      users_id: selectedIds.value
    })
    try {
      const resp = await DELETE_USER(user_params);
      console.log("————————————————",resp)
      ElMessage({
        type: 'success',
        message: `用户已被删除`
      });
      getUserData()

    }catch (e){
      console.log(e)
      ElMessage.error(`删除失败: ${e}`);
    }
  }).catch(() => {
    ElMessage({
      type: 'info',
      message: '已取消删除'
    });
  });

}
const UnlockUser = ()=>{

}
const handleInput = ()=> {

}
const handleRowClick = (row) => {
  router.push({
    name: 'userDetail',
    params: { id: row.id}
  });

};

const createDialogVisible = ref(false);

// fetchNamespaces 函数使用 getNSData 的返回值来更新命名空间的下拉菜单
// getNSData 函数获取命名空间并返回名称数组




const formatDateTime = (dateTimeString) => {
  return dateTimeString.replace('T', ' ').replace('Z', '');
};
// 示例数据和方法
const searchText = ref('');
const tableData = ref([
  {
    id: "",
    name: "",
    nickName: '',
    phone: '',
    email: '',
    type: '',
    status: '',
    createTime:"",
    Operation:"",
  },
  // ...其他数据项
]);

const search = async () => {
  console.log('执行搜索:', searchText.value);
  const searchParameters = reactive({
    page_size: pageSize,
    page_number: currentPage,
    filterName: searchText.value,
    namespace: ''
  })
  try {
    const resp = await PV_LIST(searchParameters);

    if (resp && resp.data && Array.isArray(resp.data.items)) {
      tableData.value = resp.data.items.map((item) => {
        console.log(item.spec.claimRef,'握草无情')
        let pvc
        if (item.spec.claimRef === undefined) {
          pvc = '-'
        }else {
          pvc = item.spec.claimRef.name
        }
        // 根据后端数据结构提取所需信息
        return {
          name: item.metadata.name, // PersistentVolume的名字
          status: item.status.phase, // PersistentVolume的状态
          mode: item.spec.volumeMode, // PersistentVolume的模式
          capacity: item.spec.capacity.storage, // PersistentVolume的容量
          pvc: pvc, // PVC信息,根据实际情况填写或调整
          accessModes: item.spec.accessModes.join(', '), // PersistentVolume的访问模式
          reclaimPolicy: item.spec.persistentVolumeReclaimPolicy, // PersistentVolume的回收策略
          createTime: formatDateTime(item.metadata.creationTimestamp), // 创建时间,假设您已定义formatDateTime函数
          // 其他属性可以根据需要添加
        };
      });
    }
    // 更新部署计数
    deploymentCount.value = resp.data.count;
  } catch (e) {
    console.error(e);
    // 处理错误情况,可能要向用户显示错误信息或日志输出
  }
};

const refresh = () => {
  getUserData()
  console.log('刷新表格数据');
};
const updateParameters = reactive({
  name: "",
  namespace: '',
  content: ""
})


const user_params = reactive({
  page_size: pageSize,
  page_number: currentPage,
})
const group_params = reactive({
  page_size: pageSize,
  page_number: currentPage,
})
const getGroupsList = async () => {
  try {
    const resp = await GROUP_LIST(group_params);
    console.log("组啊===== ", resp);
    if (resp && resp.data && resp.data.items) {
      // Map items to the expected data structure for el-transfer
      groupData.value = resp.data.items.map(item => ({
        key: item.id,       // 假设id是唯一的
        label: item.name,   // label是显示在列表中的文本
        disabled: false     // 可以根据需要设置
      }));
    }
  } catch (e) {
    console.error(e);
  }
}

const getUserData = async () => {
  try {
    const resp = await USER_LIST(user_params);
    console.log("用户", resp);
    if (resp && resp.data && Array.isArray(resp.data.items)) {
      tableData.value = resp.data.items.map((item) => {
        // 用户类型判断
        const userType = item.username === 'admin' ? '管理员' : '一般用户';
        // 用户状态判断
        let userStatus = '';
        let userStatusType = '';
        switch (item.status) {
          case 0:
            userStatus = '正常';
            userStatusType = 'success';
            break;
          case 1:
            userStatus = '临时锁定';
            userStatusType = 'error';
            break;
          case 2:
            userStatus = '永久锁定';
            userStatusType = 'error';
            break;
            // 默认情况,您可以根据需要添加其他状态
          default:
            userStatus = '未知';
            userStatusType = 'info';
            break;
        }

        return {
          id: item.id,
          name: item.username,
          phone: hidePhoneMiddleDigits(item.phone),
          nickName: item.nickname,
          email: item.email,
          type: userType,
          status: userStatus,
          statusType: userStatusType, // 新增属性,用于标识状态类型
          createTime: formatDateTime(item.created_at),
        };
      });
    }
    // 更新部署计数
    deploymentCount.value = resp.data.count;
  } catch (e) {
    console.error(e);
  }
};
// const getDeployDetailData = async (serviceName)=>{
//   try {
//     const DeploymentDetailparams = reactive({
//       name: serviceName,
//       namespace: selectedNamespace.value
//     })
//     console.log("ddddddddd",DeploymentDetailparams)
//     const resp = await PV_DETAIL(DeploymentDetailparams)
//     console.log("cccccccc",resp)
//     console.log("yaml =======",json2yaml.stringify(resp.data))
//     yamlContent.value = json2yaml.stringify(resp.data); // 确保将数据赋值给 yamlContent
//     currentDeployName.value = resp.data.metadata.name
//     updateParameters.content = yamlContent.value
//     await  nextTick()
//     dialogVisible.value = true;
//
//   }catch (e){
//     console.log(e)
//     console.log("你吗")
//   }
// }
// 在setup函数中定义定时器
let refreshInterval = null;



onBeforeMount( async ()=> {
  clearInterval(refreshInterval);
  // 尝试从 localStorage 中读取状态
  const savedPageSize = localStorage.getItem('nspageSize');
  const savedCurrentPage = localStorage.getItem('nscurrentPage');

  // 如果存在则更新到响应式变量中
  if (savedPageSize) {
    pageSize.value = parseInt(savedPageSize, 10);
  }
  if (savedCurrentPage) {
    currentPage.value = parseInt(savedCurrentPage, 10);
  }
  getUserData()
  getGroupsList()
  // 假设这是从 API 获取的组列表
  groupData.value = [
    { key: 1, label: '组1' },
    { key: 2, label: '组2' },
    // ...更多组
  ];

});
</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;
}

/* 确保下拉框和文本在同一行 */
.el-select {
  margin-left: 10px; /* 为下拉框添加左侧间隔 */
}
.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);
}

.search-input {
  /*flex-grow: 1;*/
  width: 200px;
}

.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;
  align-items: center;
  gap: 10px; /* You can adjust the gap as needed */
}

/* 右侧刷新按钮 */
.right-group {
  display: flex;
  align-items: center;
}
.yaml-content {
  background-color: #f5f5f5;
  border-left: 3px solid #4795EE;
  padding: 15px;
  white-space: pre-wrap;
  text-align: left;
  margin: 20px 0;
  overflow-x: auto;
}
.status-active {
  color: #67C23A;
}

.status-inactive {
  color: red;
}
.dialog-footer {
  /*text-align: right;*/
  display: flex;
  justify-content: flex-end;
  padding: 8px;
}




.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);
}



.form-item-with-help {
  margin-top: 10px;
  margin-left: 20px;
}
.form-item-with-resource {
  margin-top: 10px;
  margin-left: 20px;
}
.form-item {
  flex-wrap: wrap; /* 确保换行 */
  /*align-items: center; !* 如果需要垂直居中 *!*/
  margin-bottom: 10px; /* 保持垂直间距 */
}

.input-item {
  width: 200px;
  margin-right: 10px;
}
.help-text {
  display: block; /* 保证帮助文本在新的行显示 */
  font-size: 12px;
  width: 1000px;
  color: #999999;
  margin-top: 5px;
}
.input-with-help {
  margin-top: 20px;
  display: flex;
  flex-direction: column;
}
.input-deployname {
  width: 300px; /* 限制这个div的宽度 */
  display: flex;
  flex-direction: column; /* 让子元素垂直排列 */
}
.input-containername {
  width: 250px; /* 限制这个div的宽度 */
  display: flex;
  margin-left: -50px;
  flex-direction: column; /* 让子元素垂直排列 */
}

.add-button {
  margin-top: -20px; /* 添加按钮与输入行之间的空间 */
  margin-left: 200px;
}

.input-container-cpu {
  display: flex;
  flex-direction: column;
  /*padding-left: -20px; !* 往右移动整个容器 *!*/

}

.cpu-label {
  margin-bottom: 5px; /* 调整为所需的间距 */
  margin-left: -50px;
}


.container {
  margin: 10px;
}
.item-container {
  margin-bottom: 10px;
}
.content-container {
  background-color: #F2F2F2;
  padding: 10px;
  display: flex;
  width: 700px;
  justify-content: space-between;
  align-items: center;
  transition: height 0.3s;
  overflow: hidden;
  position: relative;

}
.content-container.collapsed {
  height: 40px;
}
.info {
  display: flex;
  align-items: center;
}
.actions {
  position: absolute;
  right: 10px; /* 调整为需要的值 */
  top: 10px; /* 调整为需要的值 */
}



.input-row {
  display: flex;
  align-items: center;
  gap: 10px;
  /*margin-left: -50px;*/
}
.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:”下方添加一些间隔 */
}

.form-item-resource {
  margin-left: 20px; /* 整个表单项向右移动 20px */
  margin-top: 10px;
}
.custom-form-item {
  height: auto;
  overflow: auto;
}
.check-method-container,
.execution-command-container {
  display: flex;
  align-items: center;
  margin-bottom: 10px; /* 添加一些垂直间距 */
}

.method-label {
  width: 100px;
  margin: 10px;
  margin-right: 20px; /* 文本与选择框/输入框之间的间隔 */
}
.command-label {
  /*margin-right: 20px; !* 文本与输入框之间的间隔 *!*/
  width: 100px;
  margin: 10px;

}
.livenessdiv {
  background-color: white;
  width: 500px;
}
.port-range {
  margin-left: 10px;
}
.input-healthContainer {
  margin-left: -50px;
}
.el-select,
.el-input {
  /*flex-grow: 1; !* 输入框和选择框将占据剩余空间 *!*/
  max-width: calc(100% - 100px); /* 限制最大宽度,这里的100px是示例,可以根据实际情况调整 */
}
.button-group {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px; /* 调整按钮之间的间隔 */
}
.variables-header {
  display: flex;
  justify-content: space-between;
  padding-bottom: 10px;
}
.variable-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
}
.variable-name, .variable-value {
  width: 100px;
}
.variable-input {
  flex: 1;
  margin-right: 10px;
}
.el-input {
  max-width: 95%;
}
/* CSS */
.transfer-custom {
  flex: 1; /* 使两个面板均匀分配空间 */
  margin-left: 120px;
}

.transfer-custom .el-transfer {
  display: flex;
  align-items: stretch; /* 确保两个面板高度相同 */
}


</style>