element-ui的form居中, label自适应宽度,el-select和el-input一样宽,整体在dialog居中,el-select默认选中第一项

本文介绍了如何在Vue.js应用中实现组件内部的form元素居中显示,设置label宽度自适应,使el-select与el-input宽度一致,并默认选中el-select的第一项。通过使用`::v-deep`穿透scoped样式,调整了el-dialog和el-form的布局,确保表单元素在dialog中居中。同时,展示了如何通过v-model和数据绑定实现el-select的默认选中状态。

效果图
在这里插入图片描述

代码部分:
1.整体form在dialog中居中

::v-deep .el-dialog .el-dialog__body{
      display: flex;
      justify-content: center;
      align-items: center;
}

为什么要使用 ::v-deep来穿透样式?
因为是scoped样式lang=“scss” 写了 /deep/不生效 以后就写::v-deep

注意点:
::v-deep与后面写的选择器中间要有空格

2.label宽度自适应
在这里插入图片描述
设置label-width=“auto”

3.el-select和el-input一样宽
直接给el-select加上 style=“width:100%;”

<el-form-item label="是否有效">
    <el-select v-model="roleForm.status" style="width:100%;">
        <el-option 
        v-for="item in statusList" 
        :key="item.id" 
        :label="item.name" 
        :value="item.id"></el-option>
    </el-select>
</el-form-item>

4.el-select默认选中第一项

  • 静态获取数据
<el-form-item label="是否有效">
    <el-select v-model="roleForm.status" style="width:100%;">
        <el-option 
        v-for="item in statusList" 
        :key="item.id" 
        :label="item.name" 
        :value="item.id"></el-option>
    </el-select>
</el-form-item>
statusList:[
    {id:1,name:'有效'},
    {id:2,name:'无效'}
]
created(){
    //select默认选中第一个
    this.roleForm.status = this.statusList[0].id;
},

完整代码:

<el-dialog
    title="添加角色"
    :visible.sync="roleDialogVisible"
    width="30%"
    >
    <el-form 
    size="small"
    style="width:70%"
    :model="roleForm" 
    class="demo-form-inline" 
    label-width="auto">
        <el-form-item label="角色编码">
            <el-input v-model="roleForm.roleCode"></el-input>
        </el-form-item>
        <el-form-item label="角色名称">
            <el-input v-model="roleForm.roleName"></el-input>
        </el-form-item>
        <el-form-item label="角色类型">
            <el-select v-model="roleForm.roleType" placeholder="请选择" style="width:100%;">
                <el-option 
                v-for="item in roleTypes"
                :key="item.id"
                :label="item.name" 
                :value="item.value"></el-option>
            </el-select>
        </el-form-item>
        <el-form-item label="是否有效">
            <el-select v-model="roleForm.status" style="width:100%;">
                <el-option 
                v-for="item in statusList" 
                :key="item.id" 
                :label="item.name" 
                :value="item.id"></el-option>
            </el-select>
        </el-form-item>
        <el-form-item label="描述">
            <el-input v-model="roleForm.roleDescription"></el-input>
        </el-form-item>
    </el-form>
    <span slot="footer" class="dialog-footer">
        <el-button @click="roleDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="roleDialogVisible = false">确 定</el-button>
    </span>
</el-dialog>
<template> <!-- 顶部搜索区域 --> <div class="filter-container"> <el-form :inline="true" :model="searchForm"> <!-- 问卷标题搜索 --> <el-form-item label="问卷标题"> <el-input v-model="searchForm.dcWjTitle" placeholder="输入问卷标题" clearable /> </el-form-item> <!-- 被测评人ID搜索 --> <el-form-item label="被测评人ID"> <el-input v-model="searchForm.dcid" placeholder="输入被测评人ID" clearable /> </el-form-item> <!-- 部门选择 --> <el-form-item label="人员部门"> <el-select v-model="searchForm.dcDept" placeholder="选择部门" clearable > <el-option v-for="dept in departments" :key="dept.value" :label="dept.label" :value="dept.value" /> </el-select> </el-form-item> <!-- 状态选择 --> <el-form-item label="提交状态"> <el-select v-model="searchForm.state" placeholder="选择状态" clearable > <el-option label="已提交" value="1" /> <el-option label="未提交" value="0" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" @click="handleSearch" icon="el-icon-search" >搜索</el-button> </el-form-item> </el-form> </div> <!-- 数据表格 --> <el-table :data="filteredData"> <!-- 表格列定义 --> </el-table> </template> <script> export default { data() { return { searchForm: { dcWjTitle: '', dcid: '', dcDept: '', state: '' }, rawData: [], // 原始数据 departments: [ // 部门选项 { label: '技术部', value: 'tech' }, { label: '市场部', value: 'market' } ] } }, computed: { // 过滤后的数据 filteredData() { return this.rawData.filter(item => (!this.searchForm.dcWjTitle || item.title.includes(this.searchForm.dcWjTitle)) && (!this.searchForm.dcid || item.dcid === this.searchForm.dcid) && (!this.searchForm.dcDept || item.dept === this.searchForm.dcDept) && (!this.searchForm.state || item.state === this.searchForm.state) ) } }, methods: { // 触发搜索 handleSearch() { this.fetchFilteredData() }, // 从后端获取数据 async fetchFilteredData() { try { const params = { title: this.searchForm.dcWjTitle, dcid: this.searchForm.dcid, dept: this.searchForm.dcDept, state: this.searchForm.state } // 调用后端API(示例) const { data } = await axios.get('/api/questionnaires', { params }) this.rawData = data } catch (error) { console.error('获取数据失败', error) } } } } </script> 帮我优化一下布局,适用于移动端
07-15
<style scoped> /* 基础响应式 */ .filter-container { padding: 10px; } /* 移动端表单项优化 */ @media (max-width: 768px) { .el-form-item { margin-bottom: 15px; } .el-form-item__label { float: none; display: block; text-align: left; padding: 0 0 8px 0; } .el-form-item__content { margin-left: 0 !important; } .mobile-full-width { width: 100%; } /* 解决iOS Safari渲染问题 */ :deep(.el-select) { width: 100%; } } /* 对话框移动端适配 */ :deep(.el-dialog) { @media (max-width: 768px) { width: 90% !important; margin-top: 5vh !important; } } </style> <template> <div class="filter-container"> <el-form :model="searchForm"> <el-row :gutter="15"> <!-- 问卷标题搜索 --> <el-col :xs="24" :sm="12" :md="6"> <el-form-item label="问卷标题"> <el-input v-model="searchForm.dcWjTitle" placeholder="输入问卷标题" clearable /> </el-form-item> </el-col> <!-- 被测评人ID搜索 --> <el-col :xs="24" :sm="12" :md="6"> <el-form-item label="被测评人ID"> <el-input v-model="searchForm.dcid" placeholder="输入被测评人ID" clearable /> </el-form-item> </el-col> <!-- 部门选择 --> <el-col :xs="24" :sm="12" :md="6"> <el-form-item label="人员部门"> <el-select v-model="searchForm.dcDept" placeholder="选择部门" clearable > ... </el-select> </el-form-item> </el-col> <!-- 状态选择 --> <el-col :xs="24" :sm="12" :md="6"> <el-form-item label="提交状态"> <el-select v-model="searchForm.state" placeholder="选择状态" clearable > ... </el-select> </el-form-item> </el-col> <!-- 搜索按钮 --> <el-col :xs="24" :sm="24" :md="6"> <el-form-item> <el-button type="primary" @click="handleSearch" icon="el-icon-search" class="mobile-full-width" >搜索</el-button> </el-form-item> </el-col> </el-row> </el-form> </div> </template> <script> export default { data() { return { searchForm: { dcWjTitle: '', dcid: '', dcDept: '', state: '' }, rawData: [], // 原始数据 departments: [ // 部门选项 { label: '技术部', value: 'tech' }, { label: '市场部', value: 'market' } ] } }, computed: { // 过滤后的数据 filteredData() { return this.rawData.filter(item => (!this.searchForm.dcWjTitle || item.title.includes(this.searchForm.dcWjTitle)) && (!this.searchForm.dcid || item.dcid === this.searchForm.dcid) && (!this.searchForm.dcDept || item.dept === this.searchForm.dcDept) && (!this.searchForm.state || item.state === this.searchForm.state) ) } }, methods: { // 触发搜索 handleSearch() { this.fetchFilteredData() }, // 从后端获取数据 async fetchFilteredData() { try { const params = { title: this.searchForm.dcWjTitle, dcid: this.searchForm.dcid, dept: this.searchForm.dcDept, state: this.searchForm.state } // 调用后端API const { data } = await axios.get('http://172.26.26.43/dev-api/wjdc/wj/listTx?', { params }) this.rawData = data } catch (error) { console.error('获取数据失败', error) } } } } </script> 我在台式电脑上运行这个代码页面,问卷标题、被测评人ID、人员部门、提交状态都在一行,我想每个单独一行,这样输入框就不会被挤压显得很小
07-15
<template> <el-container style="height: 100vh; overflow: hidden;"> <!-- 侧边栏 --> <el-aside width="200px" style="background-color: #304156;"> <el-menu default-active="1" class="el-menu-vertical-demo" background-color="#304156" text-color="#bfcbd9" active-text-color="#409EFF"> <el-menu-item index="1"> <i class="el-icon-user"></i> <span>员工管理</span> </el-menu-item> </el-menu> </el-aside> <!-- 主内容区 --> <el-container> <!-- 头部 --> <el-header class="app-header"> <h2 class="app-title">员工管理系统</h2> </el-header> <!-- 主要内容 --> <el-main class="app-content"> <!-- 查询条件 --> <div class="query-section"> <el-row :gutter="20"> <el-col :span="4"> <el-input v-model="queryParams.name" placeholder="请输入姓名" clearable /> </el-col> <el-col :span="4"> <el-select v-model="queryParams.gender" placeholder="请选择性别" clearable> <el-option label="男" :value="0" /> <el-option label="女" :value="1" /> </el-select> </el-col> <el-col :span="4"> <el-select v-model="queryParams.job" placeholder="请选择职位" clearable> <el-option v-for="item in jobOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-col> <el-col :span="6"> <el-date-picker v-model="queryParams.dateRange" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /> </el-col> <el-col :span="6" class="action-buttons"> <el-button type="primary" @click="fetchData" icon="el-icon-search">搜索</el-button> <el-button type="success" @click="showAddDialog" icon="el-icon-plus">新增</el-button> <el-button type="danger" @click="batchDelete" icon="el-icon-delete">批量删除</el-button> </el-col> </el-row> </div> <!-- 数据表格 --> <div class="table-section"> <el-table :data="tableData" border stripe style="width: 100%" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55" align="center" /> <el-table-column prop="id" label="ID" width="80" align="center" /> <el-table-column prop="name" label="姓名" width="120" /> <el-table-column label="头像" width="100" align="center"> <template #default="scope"> <el-avatar v-if="scope.row.image" :src="scope.row.image" :size="50" shape="square" :preview-src-list="[scope.row.image]" /> <el-avatar v-else :size="50" shape="square"> <i class="el-icon-user-solid" style="font-size: 24px;" /> </el-avatar> </template> </el-table-column> <el-table-column prop="gender" label="性别" width="80" align="center"> <template #default="scope"> <el-tag :type="scope.row.gender === 0 ? 'primary' : 'danger'"> {{ scope.row.gender === 0 ? '男' : '女' }} </el-tag> </template> </el-table-column> <el-table-column prop="job" label="职位" width="150"> <template #default="scope"> <el-tag :type="getJobTagType(scope.row.job)"> {{ jobMap[scope.row.job] || '未知' }} </el-tag> </template> </el-table-column> <el-table-column prop="entrydate" label="入职时间" width="120" align="center" /> <el-table-column prop="createTime" label="创建时间" width="180" align="center" /> <el-table-column label="操作" width="180" align="center" fixed="right"> <template #default="scope"> <el-button size="small" type="primary" icon="el-icon-edit" @click="handleEdit(scope.row)" /> <el-button size="small" type="danger" icon="el-icon-delete" @click="handleDelete(scope.row.id)" /> </template> </el-table-column> </el-table> </div> <!-- 分页组件 --> <div class="pagination-section"> <el-pagination background layout="total, prev, pager, next, sizes" :total="total" :page-sizes="[5, 10, 20, 50]" :page-size="queryParams.pageSize" v-model:current-page="queryParams.pageNum" @current-change="fetchData" @size-change="handleSizeChange" /> </div> </el-main> </el-container> </el-container> <!-- 新增/编辑对话框 --> <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px" :close-on-click-modal="false"> <el-form :model="form" ref="formRef" :rules="formRules" label-width="100px" label-position="left"> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="姓名" prop="name"> <el-input v-model="form.name" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="用户名" prop="username"> <el-input v-model="form.username" /> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="性别" prop="gender"> <el-select v-model="form.gender" placeholder="请选择性别"> <el-option label="男" :value="0" /> <el-option label="女" :value="1" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="职位" prop="job"> <el-select v-model="form.job" placeholder="请选择职位"> <el-option v-for="item in jobOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="密码" prop="password" v-if="!isEdit"> <el-input v-model="form.password" show-password /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="入职时间" prop="entrydate"> <el-date-picker v-model="form.entrydate" type="date" placeholder="选择日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width: 100%" /> </el-form-item> </el-col> </el-row> <el-form-item label="头像链接" prop="image"> <el-input v-model="form.image" placeholder="请输入图片URL" /> <div class="avatar-preview" v-if="form.image"> <el-image :src="form.image" fit="cover" style="width: 100px; height: 100px; margin-top: 10px;" :preview-src-list="[form.image]" /> </div> </el-form-item> <el-form-item class="form-actions"> <el-button type="primary" @click="submitForm">提交</el-button> <el-button @click="dialogVisible = false">取消</el-button> </el-form-item> </el-form> </el-dialog> </template> <script setup> import { ref, reactive, onMounted } from 'vue'; import axios from 'axios'; import { ElMessage, ElMessageBox } from 'element-plus'; // 设置 axios 实例 const apiClient = axios.create({ baseURL: 'http://localhost:8080/emps', timeout: 5000, }); // 表格数据 const tableData = ref([]); const total = ref(0); const selectedRows = ref([]); const formRef = ref(null); // 查询参数 const queryParams = reactive({ pageNum: 1, pageSize: 5, name: '', gender: null, job: null, dateRange: [] }); // 表单数据 const form = reactive({ id: null, username: '', password: '', name: '', gender: null, job: null, image: '', entrydate: '' }); // 表单验证规则 const formRules = { name: [{ required: true, message: '请输入姓名', trigger: 'blur' }], username: [{ required: true, message: '请输入用户名', trigger: 'blur' }], gender: [{ required: true, message: '请选择性别', trigger: 'change' }], job: [{ required: true, message: '请选择职位', trigger: 'change' }], password: [{ required: true, message: '请输入密码', trigger: 'blur' }], entrydate: [{ required: true, message: '请选择入职日期', trigger: 'change' }] }; // 对话框控制 const dialogVisible = ref(false); const dialogTitle = ref('新增员工'); const isEdit = ref(false); // 职位映射表 const jobMap = { 1: '班主任', 2: '讲师', 3: '学工主管', 4: '教研主管', 5: '咨询师' }; // 职位选项 const jobOptions = [ { label: '班主任', value: 1 }, { label: '讲师', value: 2 }, { label: '学工主管', value: 3 }, { label: '教研主管', value: 4 }, { label: '咨询师', value: 5 } ]; // 获取职位标签类型 const getJobTagType = (job) => { const types = ['', 'success', 'warning', 'danger', 'info', 'primary']; return types[job] || 'info'; }; // 获取数据 const fetchData = async () => { const params = { page: queryParams.pageNum, pageSize: queryParams.pageSize, name: queryParams.name, gender: queryParams.gender, job: queryParams.job, begin: queryParams.dateRange[0] || '', end: queryParams.dateRange[1] || '' }; try { const res = await apiClient.get('', { params }); if (res.data.code === 1) { tableData.value = res.data.data.rows; total.value = res.data.data.total; } else { ElMessage.error('获取数据失败:' + res.data.msg); } } catch (error) { console.error('请求出错:', error); ElMessage.error('网络请求失败,请检查后端是否运行正常'); } }; // 显示新增对话框 const showAddDialog = () => { dialogTitle.value = '新增员工'; isEdit.value = false; Object.assign(form, { id: null, username: '', password: '', name: '', gender: null, job: null, image: '', entrydate: '' }); dialogVisible.value = true; }; // 显示编辑对话框 const handleEdit = (row) => { dialogTitle.value = '编辑员工'; isEdit.value = true; Object.assign(form, { ...row }); dialogVisible.value = true; }; // 提交表单 const submitForm = async () => { try { await formRef.value.validate(); if (isEdit.value) { await apiClient.put('', form); ElMessage.success('员工信息更新成功'); } else { await apiClient.post('', form); ElMessage.success('员工添加成功'); } dialogVisible.value = false; fetchData(); } catch (error) { if (error.name !== 'Error') { console.error('保存失败:', error); ElMessage.error('操作失败:' + (error.response?.data?.message || error.message)); } } }; // 删除员工 const handleDelete = async (id) => { try { await ElMessageBox.confirm(`确认删除ID为 ${id} 的员工吗?`, '删除确认', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }); await apiClient.delete(`/${[id]}`); ElMessage.success('删除成功'); fetchData(); } catch (error) { if (error !== 'cancel') { console.error('删除失败:', error); ElMessage.error('删除失败:' + (error.response?.data?.message || error.message)); } } }; // 批量删除 const batchDelete = async () => { if (selectedRows.value.length === 0) { ElMessage.warning('请至少选择一条记录'); return; } try { const ids = selectedRows.value.map(item => item.id); await ElMessageBox.confirm(`确认删除选中的 ${ids.length} 条员工吗?`, '批量删除确认', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }); await apiClient.delete(`/${ids}`); ElMessage.success(`成功删除 ${ids.length} 条记录`); fetchData(); } catch (error) { if (error !== 'cancel') { console.error('批量删除失败:', error); ElMessage.error('删除失败:' + (error.response?.data?.message || error.message)); } } }; // 表格选择监听 const handleSelectionChange = (rows) => { selectedRows.value = rows; }; // 处理分页大小变化 const handleSizeChange = (size) => { queryParams.pageSize = size; fetchData(); }; // 初始化加载数据 onMounted(() => { fetchData(); }); </script> <style scoped> /* 全局样式 */ .app-header { background-color: #fff; box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); display: flex; align-items: center; padding: 0 20px; z-index: 1; } .app-title { margin: 0; font-size: 18px; font-weight: 600; color: #333; } .app-content { padding: 20px; background-color: #f0f2f5; height: calc(100vh - 60px); overflow-y: auto; } .query-section { background: #fff; padding: 20px; border-radius: 4px; margin-bottom: 20px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .table-section { background: #fff; padding: 20px; border-radius: 4px; margin-bottom: 20px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .pagination-section { display: flex; justify-content: center; background: #fff; padding: 15px 0; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .action-buttons { display: flex; justify-content: flex-end; } .form-actions { display: flex; justify-content: flex-end; margin-top: 20px; } .avatar-preview { display: flex; justify-content: center; margin-top: 10px; } .el-avatar { background-color: #f5f7fa; display: flex; align-items: center; justify-content: center; } .el-avatar i { color: #909399; } </style> 操作按钮的文字并未显示检查下什么问题 表单里面添加一个最后修改时间的列,后端响应给前端的字段如下 private LocalDateTime updateTime; 并且优化一下表单样式,现在未铺满
07-15
<template> <div class="operation-container"> <div class="header"> <h2>{{ $t('config.title') }}</h2> <div> <el-button type="success" size="default" @click="showAddDialog" style="margin-bottom: 10px"> {{ $t('config.addConfig') }} </el-button> </div> </div> <div class="filter-container"> <el-form :model="filter" class="filter-form" label-width="150px"> <el-row :gutter="20" style="margin-bottom: 20px"> <el-col :span="6"> <!-- 主机应用选择 --> <el-form-item :label="$t('config.hostApp')"> <el-select v-model="filter.hostApp" :placeholder="$t('config.selectHostApp')" clearable filterable style="width: 300px" @change="handleFilter" > <el-option v-for="app in hostAppOptions" :key="app" :label="getHostAppLabel(app)" :value="app" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <!-- 卡位置选择 --> <el-form-item :label="$t('config.cardPosition')"> <el-select v-model="filter.cardPosition" :placeholder="$t('config.selectCardPosition')" clearable filterable style="width: 300px" @change="handleFilter" > <el-option v-for="position in cardPositionOptions" :key="position" :label="getCardPositionLabel(position)" :value="position" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <!-- 设备类型选择 --> <el-form-item :label="$t('config.deviceType')"> <el-select v-model="filter.deviceType" :placeholder="$t('config.selectDeviceType')" clearable filterable style="width: 300px" @change="handleFilter" > <el-option v-for="item in deviceTypeOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <!-- 卡片名称查询 --> <el-form-item :label="$t('management.cardName')" prop="cardName"> <el-select v-model="filter.cardName" :placeholder="$t('common.selectCardName')" clearable filterable @change="handleFilter" style="width: 300px" > <el-option v-for="item in cardAddOptions" :key="item.cardName" :label="cardTitleMap[item.cardName] + '-' + item.cardName" :value="item.cardName" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="24"> <el-row type="flex" justify="end"> <el-button @click="resetFields">{{ $t('common.reset') }}</el-button> <el-button type="primary" @click="handleFilter">{{ $t('common.search') }}</el-button> </el-row> </el-col> </el-row> </el-form> </div> <!-- 批量操作工具栏 --> <div class="batch-toolbar"> <el-button type="warning" size="small" @click="betaOrStopOrFullPublish('batchRuleSet')" icon="CircleCheck" > {{ $t('config.batchConfigCardRules') }} </el-button> <el-button type="success" size="small" @click="betaOrStopOrFullPublish('full')" icon="CircleCheck" > {{ $t('config.batchFullPublish') }} </el-button> <el-button type="danger" size="small" @click="betaOrStopOrFullPublish('stop')" icon="CircleCheck" > {{ $t('config.batchStopPublish') }} </el-button> </div> <el-table :data="tableData" :fit="true" border style="width: 100%" :empty-text="$t('common.noData')" v-loading="loading" class="github-table" ref="tableRef" @expand-change="handleExpandChange" :expand-row-keys="defaultExpandedRows" row-key="id" > <!-- 展开/收起列 --> <el-table-column type="expand" width="60"> <template #header> <el-icon :size="13" class="expand-icon" @click.stop="toggleExpandAll"> <component :is="isAllExpanded ? 'ArrowDown' : 'ArrowRight'" /> </el-icon> </template> <template #default="{ row }"> <div class="version-container"> <el-table :data="versionTablesData[row.id]?.tableData || []" :fit="true" border stripe style="width: 100%" :empty-text="$t('common.noData')" v-loading="versionTablesData[row.id]?.loading" class="github-table" :ref="`versionTable_${row.id}`" :row-key="(row) => row.cardVer" > <el-table-column width="40" type="selection" reserve-selection /> <el-table-column prop="cardVer" :label="$t('version.version')" align="center" /> <el-table-column prop="status" :label="$t('management.cardPackageStatus')" align="center" > <template #default="{ row }"> <el-tag :type="row.status === 0 ? 'success' : 'danger'"> {{ row.status === 0 ? $t('management.listed') : $t('management.unlisted') }} </el-tag> </template> </el-table-column> <el-table-column prop="cardTags" :label="$t('version.tags')" align="center"> <template #default="{ row }"> <div :class="{ 'tag-cell': row.cardTags?.slice(0, 3)?.length > 1 }"> <!-- 显示前N个可见标签 --> <div v-for="tag in row.cardTags?.slice(0, 3)" :key="tag"> <el-tag v-if="tag.length > 10" class="table-tag" @click="handleTagClick(tag)"> {{ truncateText(tag, 10) }} </el-tag> <el-tag v-else> {{ tag }} </el-tag> </div> <!-- 折叠剩余标签 --> <el-popover v-if="row.cardTags?.slice(3)?.length > 0" placement="bottom" trigger="hover" :width="300" > <template #reference> <el-tag class="more-tag">+{{ row.cardTags?.slice(3)?.length }}</el-tag> </template> <div class="hidden-tags-container"> <el-tag v-for="tag in row.cardTags?.slice(3)" :key="tag" class="hidden-tag"> {{ tag }} </el-tag> </div> </el-popover> </div> </template> </el-table-column> <el-table-column prop="deviceTypes" :label="$t('version.supportedDevices')" align="center" > <template #default="{ row }"> <div class="vertical-tags-container"> <el-tag v-for="type in row.deviceTypes.join(',').split(',')" :key="type" class="content-fit-tag" > {{ deviceTypeFilter(type) }} </el-tag> </div> </template> </el-table-column> <el-table-column prop="minorsMode" :label="$t('version.minorsMode')" align="center"> <template #default="{ row }"> <el-tag :type="row.minorsMode === 1 ? 'success' : 'warning'"> {{ row.minorsMode === 1 ? $t('version.available') : $t('version.unavailable') }} </el-tag> </template> </el-table-column> <el-table-column :label="$t('version.multiLangDesc')" width="150" align="center"> <template #default="{ row }"> <div> <el-button size="small" @click="handleShowJson(row.cardMultiLang, $t('version.multiLangDesc'))" > {{ $t('version.viewJson') }} </el-button> </div> </template> </el-table-column> <el-table-column :label="$t('version.capabilities')" width="150" align="center"> <template #default="{ row }"> <div> <el-button size="small" @click="handleShowJson(row.services, $t('version.capabilities'))" > {{ $t('version.viewJson') }} </el-button> </div> </template> </el-table-column> <el-table-column prop="publishStatus" :label="$t('config.releaseStatus')" align="center" > <template #default="{ row }"> <div v-if="row.publishStatus === 0"> {{ $t('config.releaseStatusEnum.toBeReleased') }} </div> <div v-if="row.publishStatus === 1"> {{ $t('config.releaseStatusEnum.crowdtestingRelease') }} </div> <div v-if="row.publishStatus === 2"> {{ $t('config.releaseStatusEnum.fullRelease') }} </div> </template> </el-table-column> <el-table-column :label="$t('common.actions')" width="300" fixed="right" align="center" > <template #default="{ row: version }"> <div style="flex-wrap: wrap; align-items: center; gap: 4px; min-height: 32px"> <el-button size="small" @click="actionsTo(row.id, 'edit', version)" >{{ $t('common.edit') }} </el-button> <el-button type="danger" style="margin-left: 9px" size="small" @click="actionsTo(row.id, 'stop', version)" v-if="version.publishStatus !== 0" >{{ $t('crowdTesting.stopPublish') }} </el-button> <el-button type="success" style="margin-left: 9px" size="small" @click="actionsTo(row.id, 'full', version)" v-if="version.publishStatus === 1" >{{ $t('crowdTesting.fullPublish') }} </el-button> </div> </template> </el-table-column> </el-table> <div> <el-pagination size="default" @size-change="(val) => handleVersionSizeChange(row.id, val)" @current-change="(val) => handleVersionCurrentChange(row.id, val)" :current-page="versionCurrentPage" :page-sizes="[10, 20, 50, 100]" :page-size="versionPageSize" layout="total, prev, pager, next,sizes, jumper" :total="versionTablesData[row.id]?.total" /> </div> </div> </template> </el-table-column> <!-- 选择框列 --> <el-table-column prop="hostApp" :label="$t('config.hostApp')" align="center" width="300px"> <template #default="scope"> <el-tooltip :content="scope.row.hostApp"> <span class="text-ellipsis">{{ getHostAppLabel(scope.row.hostApp) }}</span> </el-tooltip> </template> </el-table-column> <el-table-column prop="cardPosition" :label="$t('config.cardPosition')" align="center"> <template #default="scope"> <el-tooltip :content="scope.row.cardPosition"> <span class="text-ellipsis">{{ getCardPositionLabel(scope.row.cardPosition) }}</span> </el-tooltip> </template> </el-table-column> <el-table-column prop="deviceType" :label="$t('config.deviceType')" align="center" width="150" > <template #default="scope"> {{ getDeviceType(scope.row.deviceType) }} </template> </el-table-column> <el-table-column prop="cardName" :label="$t('management.cardName')" align="center"> <template #default="scope"> <el-tooltip :content="scope.row.cardName"> <span class="text-ellipsis">{{ cardTitleMap[scope.row.cardName] || scope.row.cardName }}</span> </el-tooltip> </template> </el-table-column> <!-- 索引列 --> <el-table-column prop="index" :label="$t('config.sortIndex')" align="center" width="150"> <template #default="scope"> {{ basicCardObj[scope.row.cardName]?.cardType === 2 ? '/' : scope.row.index }} </template> </el-table-column> <el-table-column :label="$t('common.actions')" fixed="right" align="center"> <template #default="{ row, $index }"> <div class="action-buttons"> <el-button type="primary" size="small" @click="batchUpdateIndex($index, row)" v-if="basicCardObj[row.cardName]?.cardType !== 2" > {{ $t('config.modifyCardOrder') }} </el-button> <el-button size="small" type="danger" @click="handleDelete($index, row)" >{{ $t('common.delete') }} </el-button> </div> </template> </el-table-column> </el-table> <div> <el-pagination size="default" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" layout="total, prev, pager, next,sizes, jumper" :total="total" > </el-pagination> </div> <AddCardConfig :dialogVisible="dialogVisible" :title="dialogTitle" :form-data="form" :host-app-options="hostAppOptions" :card-position-options="cardPositionOptions" :device-type-options="deviceTypeOptions" :card-options="cardAddOptions" :card-obj="basicCardObj" :is-edit="currentIndex !== -1" :cancel="handleCancel" v-if="dialogVisible" :cardTitleMap="cardTitleMap" :getHostAppLabel="getHostAppLabel" :getCardPositionLabel="getCardPositionLabel" /> <!-- JSON查看对话框 --> <JsonViewerDialog :jsonViewerVisible="jsonViewerVisible" :jsonViewerTitle="jsonViewerTitle" :formattedJson="formattedJson" :close="closeShowJson" /> <EditCardIndex :config="rowData" :dialogVisible="batchUpdateIndexDialogVisible" v-if="batchUpdateIndexDialogVisible" :cancel="cancel" :cardTitleMap="cardTitleMap" :getHostAppLabel="getHostAppLabel" :getCardPositionLabel="getCardPositionLabel" /> <BatchSetRule :dialogVisible="batchRuleSetDialogVisible" :cancel="cancel" :cardOptions="cardAddOptions" :cardTitleMap="cardTitleMap" :ruleDtoList="ruleDtoList" /> <EditCardVersionCrowdTesting :visible="showDialog" v-model:visible="showDialog" :testRowData="testRowData" :getHostAppLabel="getHostAppLabel" :getCardPositionLabel="getCardPositionLabel" /> </div> </template> <script setup> import { useGlobalMaps } from '@/utils/util.ts' const { maps, initGlobalMaps } = useGlobalMaps() const getHostAppLabel = (app) => maps.value.hostAppMap[app] || app const getCardPositionLabel = (app) => maps.value.cardPositionMap[app] || app </script> <script> import { ElMessage, ElMessageBox } from 'element-plus' import enumInfo from '@/constants/EnumInfo.ts' import { requestHandler } from '@/utils/request' import axios from 'axios' import EditCardVersionCrowdTesting from './components/EditCardVersionCrowdTesting.vue' import AddCardConfig from './components/AddCardConfig.vue' import EditCardIndex from './components/EditCardIndex.vue' import BatchSetRule from './components/BatchSetRule.vue' import JsonViewerDialog from '@/components/JsonViewerDialog.vue' import { initGlobalMaps, useGlobalMaps } from '@/utils/util.ts'; import { useStore } from '@/store' import { getCardOptions } from '@/service' const store = useStore(); export default { components: { EditCardVersionCrowdTesting, EditCardIndex, AddCardConfig, BatchSetRule, JsonViewerDialog }, data() { return { loading: false, dialogVisible: false, batchRuleSetDialogVisible: false, batchUpdateIndexDialogVisible: false, rowData: {}, dialogTitle: this.$t('config.addOperationConfig'), currentIndex: -1, tableData: [], versionTablesData: {}, // Changed from versionTables to store version data by card ID cardOptions: [], cardAddOptions: [], defaultExpandedRows: [], basicCardObj: {}, currentPage: 1, pageSize: 10, total: 0, basicCardObject: {}, jsonViewerVisible: false, jsonViewerTitle: '', formattedJson: '', filter: { hostApp: '', cardPosition: '', deviceType: '', cardName: '', publishStatus: '' }, form: { hostApp: '', cardPosition: '', deviceType: 'phone', cardName: '', index: 1 }, versionLoading: false, versionCurrentPage: 1, versionPageSize: 10, versionTotal: 0, tagVisible: false, showDialog: false, tagContent: '', testRowData: {}, basic: { cardName: '', provider: '', dimension: '', disable: null }, isAllExpanded: false, cardTitleMap: new Map(), ruleDtoList: [] } }, computed: { hostAppOptions() { return [...new Set(this.cardOptions.map((item) => item.hostApp))] }, cardPositionOptions() { return [...new Set(this.cardOptions.map((item) => item.cardPosition))] }, deviceTypeOptions() { return enumInfo.deviceType() } }, async mounted() { await initGlobalMaps() this.queryCardOptions() this.fetchCardList() this.cardAddOptions = await getCardOptions() this.basicCardObj = this.cardAddOptions.reduce((map, obj) => { map[obj.cardName] = obj return map }, {}) const targetLang = store.language this.cardTitleMap = this.cardAddOptions.reduce((map, card) => { // 先设置一个作为兜底 map[card.cardName] = card.cardName if (card.cardMultiLang && card.cardMultiLang.length > 0) { // 将cardMultiLang数组转换为{cardName: cardTitle}的对象 for (const langItem of card.cardMultiLang) { if (langItem.language === targetLang) { map[card.cardName] = langItem.cardTitle || card.cardName } } } return map }, {}) }, methods: { fetchCardVersionConfig(cardConfigId, config) { if (!this.versionTablesData[cardConfigId]) { this.versionTablesData[cardConfigId] = { loading: false, tableData: [], currentPage: 1, pageSize: 10, total: 0 } } this.versionTablesData[cardConfigId].loading = true const queryData = { cardName: config.cardName, hostApp: config.hostApp, deviceType: config.deviceType, cardOpId: cardConfigId, pageNumber: this.versionTablesData[cardConfigId].currentPage || 1, pageSize: this.versionTablesData[cardConfigId].pageSize || 10, pageInfo: { pageSize: this.versionTablesData[cardConfigId].pageSize || 10, offset: this.versionTablesData[cardConfigId].currentPage || 1 } } axios .post('/v1/card/version-op/page-query', queryData) .then((response) => { if (response?.data?.code === 0) { this.versionTablesData[cardConfigId]['tableData'] = response.data.opList?.map((item) => ({ ...item, hostApp: config.hostApp, deviceType: config.deviceType, cardPosition: config.cardPosition })) || [] this.versionTablesData[cardConfigId]['total'] = response.data.count || 0 this.versionTablesData[cardConfigId]['config'] = config } else { ElMessage.error(response?.data?.message || this.$t('common.getListFailed')) } }) .finally(() => { this.versionTablesData[cardConfigId]['loading'] = false }) }, cancel() { this.batchUpdateIndexDialogVisible = false this.batchRuleSetDialogVisible = false }, deviceTypeFilter(input) { if (!input) return null const found = enumInfo.deviceType().find((ent) => ent.value === input) return found ? found.label : '' }, truncateText(text, maxLength = 6) { return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text }, handleTagClick(tag) { this.tagContent = tag this.tagVisible = true }, actionsTo(cardConfigId, type, row) { if (type === 'beta' && !row.testRules) { ElMessage.error(this.$t('config.checkRules')) } else { let tempRow = JSON.parse(JSON.stringify(row)) this.testRowData = { action: type, hostApp: this.versionTablesData[cardConfigId].config.hostApp, cardName: this.versionTablesData[cardConfigId].config.cardName, cardVer: tempRow.cardVer, testRules: tempRow.testRules, cardOpId: cardConfigId, publishStatus: tempRow.publishStatus } this.showDialog = true } }, handleVersionSizeChange(cardConfigId, val) { this.versionTablesData[cardConfigId].pageSize = val this.versionTablesData[cardConfigId].currentPage = 1 this.versionPageSize = val this.fetchCardVersionConfig(cardConfigId, this.versionTablesData[cardConfigId].config) }, handleVersionCurrentChange(cardConfigId, val) { this.versionTablesData[cardConfigId].currentPage = val this.versionCurrentPage = val this.fetchCardVersionConfig(cardConfigId, this.versionTablesData[cardConfigId].config) }, toggleExpandAll() { this.isAllExpanded = !this.isAllExpanded this.tableData.forEach((row) => { this.$refs.tableRef.toggleRowExpansion(row, this.isAllExpanded) }) }, handleExpandChange(row, expandedRows) { if (expandedRows.includes(row) && !row.versionDataLoaded) { row.versionDataLoaded = true this.versionTablesData[row.id] = { ...this.versionTablesData[row.id], config: row } this.fetchCardVersionConfig(row.id, row) } }, betaOrStopOrFullPublish(type) { const ruleDtoList = [] Object.keys(this.versionTablesData).forEach((cardConfigId) => { const table = this.$refs[`versionTable_${cardConfigId}`] if (table && table.getSelectionRows) { ruleDtoList.push( ...table.getSelectionRows().map((item) => ({ cardOpId: cardConfigId, hostApp: item.hostApp, cardPosition: item.cardPosition, deviceType: item.deviceType, cardName: item.cardName, cardTitle: this.cardTitleMap[item.cardName] || item.cardName, cardVer: item.cardVer, status: item.publishStatus, testRules: item.testRules })) ) } }) if (ruleDtoList.length === 0) { ElMessage.error(this.$t('config.selectAtLeastOneRecord')) return } if (ruleDtoList.length > 20) { ElMessage.error(this.$t('config.selectMaxRecords')) return } // 众测发布 if (type === 'batchRuleSet') { const firstRecord = ruleDtoList[0] const inconsistentFields = [] if (ruleDtoList.some((item) => item.hostApp !== firstRecord.hostApp)) { inconsistentFields.push(this.$t('config.hostApp')) } if (ruleDtoList.some((item) => item.cardPosition !== firstRecord.cardPosition)) { inconsistentFields.push(this.$t('config.cardPosition')) } if (ruleDtoList.some((item) => item.deviceType !== firstRecord.deviceType)) { inconsistentFields.push(this.$t('config.deviceType')) } if (inconsistentFields.length > 0) { const fieldsText = inconsistentFields.join('、') ElMessage.error( this.$t('config.inconsistentFieldsError', { fields: fieldsText }) ) return } // 筛选既不是众测发布也不是待发布状态的条数 const invalidRecords = ruleDtoList.filter((item) => item.status !== 0 && item.status !== 1) if (invalidRecords.length) { // 提取不符合要求的卡片名版本号 const invalidItems = invalidRecords .map((item) => `${item.cardTitle}(${item.cardVer})`) .join('、') ElMessage.error( this.$t('config.invalidRecordsBatchConfigCardRule', { count: invalidRecords.length, items: invalidItems, requiredStatus: `${this.$t('config.betaRelease')}${ store.isEnglish ? ' ' : '' }${this.$t('common.or')}${store.isEnglish ? ' ' : ''}${this.$t( 'config.pendingRelease' )}` }) ) return } this.batchRuleSetDialogVisible = true this.ruleDtoList = ruleDtoList return } if (type === 'stop') { // 获取状态为0(不符合要求)的记录 const invalidRecords = ruleDtoList.filter((item) => item.status === 0) if (invalidRecords.length) { // 提取不符合要求的卡片名版本号 const invalidItems = invalidRecords .map((item) => `${item.cardTitle}(${item.cardVer})`) .join('、') ElMessage.error( this.$t('config.invalidRecordsForStop', { count: invalidRecords.length, items: invalidItems, requiredStatus: `${this.$t('config.betaRelease')}${ store.isEnglish ? ' ' : '' }${this.$t('common.or')}${store.isEnglish ? ' ' : ''}${this.$t( 'config.fullRelease' )}` }) ) return } } if (type === 'full') { // 获取状态不为1(不符合全量发布要求)的记录 const invalidRecords = ruleDtoList.filter((item) => item.status !== 1) if (invalidRecords.length) { // 提取不符合要求的卡片名版本号 const invalidItems = invalidRecords .map((item) => `${item.cardTitle}(${item.cardVer})`) .join('、') ElMessage.error( this.$t('config.invalidRecordsForFullRelease', { count: invalidRecords.length, items: invalidItems, requiredStatus: this.$t('config.betaRelease') }) ) return } } let status if (type === 'beta') { status = 1 } else if (type === 'full') { status = 2 } else { status = 0 } requestHandler({ url: '/v1/card/version-op/batch-update', data: { ruleDtoList: ruleDtoList, status: status } }) }, resetFields() { Object.assign(this.filter, { hostApp: '', cardPosition: '', deviceType: '', cardName: '', publishStatus: '' }) this.handleFilter() }, getDeviceType(type) { if (type === '') { return null } const _deviceType = this.deviceTypeOptions.find((ent) => { return ent.value === type }) if (_deviceType) { return _deviceType.label } return '' }, fetchCardList() { this.tableData = [] this.loading = true const queryData = { hostApp: this.filter.hostApp || null, cardPosition: this.filter.cardPosition || null, deviceType: this.filter.deviceType || null, cardName: this.filter.cardName || null, publishStatus: this.filter.publishStatus, pageNumber: this.currentPage, pageSize: this.pageSize } axios .post('/v1/card/operation/page-query', queryData) .then((response) => { if (response && response.data && response.data.code === 0) { this.tableData = response.data.cardOpConfigs || [] this.total = response.data.count || 0 // 默认展开第一行(取第一行的row-key值) if (this.tableData.length > 0) { this.defaultExpandedRows = [this.tableData[0].id] this.fetchCardVersionConfig(this.tableData[0].id, this.tableData[0]) } } else { ElMessage.error(response.data.message || this.$t('common.getListFailed')) } }) .finally(() => { this.loading = false }) }, queryCardOptions() { const queryData = { pageNumber: 1, pageSize: 1000 } axios.post('/v1/card/operation/page-query', queryData).then((response) => { if (response && response.data && response.data.code === 0) { this.cardOptions = response.data.cardOpConfigs } }) }, showAddDialog() { this.dialogTitle = this.$t('config.addOperationConfig') this.dialogVisible = true this.currentIndex = -1 this.form = {} }, handleEdit(index, row) { this.dialogTitle = this.$t('config.editOperationConfig') Object.assign(this.form, row) this.currentIndex = index this.dialogVisible = true }, handleCancel() { this.dialogVisible = false }, batchUpdateIndex(index, row) { Object.assign(this.form, row) this.currentIndex = index this.batchUpdateIndexDialogVisible = true this.rowData = row }, handleDelete(index, row) { ElMessageBox.confirm(this.$t('common.confirmDelete'), this.$t('common.warning'), { confirmButtonText: this.$t('common.confirm'), cancelButtonText: this.$t('common.cancel'), type: 'warning' }).then(() => { requestHandler({ url: '/v1/card/operation/delete', data: { id: row.id } }) }) }, handleSizeChange(val) { this.pageSize = val this.currentPage = 1 this.fetchCardList() }, handleCurrentChange(val) { this.currentPage = val this.fetchCardList() }, handleFilter() { this.currentPage = 1 this.fetchCardList() this.isAllExpanded = false }, handleShowJson(jsonStr, title) { try { this.formattedJson = jsonStr this.jsonViewerTitle = title this.jsonViewerVisible = true } catch (e) { ElMessage.error(this.$t('common.jsonFormatError') + e) } }, closeShowJson() { this.jsonViewerVisible = false } } } </script> <style scoped> .operation-container { padding: 20px; background-color: #fff; border-radius: 6px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } .filter-container { margin-bottom: 20px; display: flex; flex-wrap: wrap; gap: 10px; } .github-table { --el-table-border-color: #e1e4e8; --el-table-header-bg-color: #f6f8fa; --el-table-row-hover-bg-color: #f6f8fa; } .github-table :deep(.el-table__header th) { background-color: #f6f8fa; color: #24292e; font-weight: 600; } .github-table :deep(.el-table__body td) { padding: 12px 0; } .filter-container { padding: 20px; margin-bottom: 20px; border-radius: 4px; } .batch-toolbar { margin-bottom: 15px; display: flex; align-items: center; gap: 10px; } .batch-toolbar { margin-bottom: 15px; display: flex; align-items: center; gap: 12px; } /* 修改行的样式 */ :deep(.el-table .modified-row) { --el-table-tr-bg-color: rgba(var(--el-color-success-rgb), 0.08); } .expand-icon { vertical-align: middle; cursor: pointer; color: rgb(96, 98, 102); } /* 垂直排列容器 - 完全居中方案 */ .vertical-tags-container { display: grid; justify-items: center; /* 水平居中 */ gap: 6px; padding: 2px 0; width: 100%; } /* 自适应宽度标签 */ .content-fit-tag { width: max-content; /* 宽度严格包裹内容 */ margin: 0 auto !important; /* 双重居中保障 */ padding: 0 10px; display: block; /* 使margin:auto生效 */ } .version-container { padding: 20px; background-color: #fff; border-radius: 6px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } .github-table { --el-table-border-color: #e1e4e8; --el-table-header-bg-color: #f6f8fa; --el-table-row-hover-bg-color: #f6f8fa; } .github-table :deep(.el-table__header th) { background-color: #f6f8fa; color: #24292e; font-weight: 600; } .github-table :deep(.el-table__body td) { padding: 12px 0; } /* 表格标签专用样式 */ .tag-cell { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-height: 32px; } .table-tag { max-width: 80px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; transition: all 0.2s; } .more-tag { background: var(--el-color-info-light-8); border: none; color: var(--el-color-info); } .hidden-tags-container { display: flex; flex-wrap: wrap; gap: 8px; max-height: 200px; overflow-y: auto; padding: 8px; } .tag-cell { display: flex; justify-content: center; /* 水平居中 */ align-items: center; /* 垂直居中 */ flex-wrap: wrap; /* 允许标签换行 */ gap: 4px; /* 标签间距 */ width: 100%; /* 确保占据全部宽度 */ } </style> 这个是我的CardConfig <template> <el-dialog v-model="visible" :title="title" width="60%" :before-close="resetForm"> <el-steps :active="activeStep" simple style="margin-bottom: 20px" v-if="!isEdit"> <el-step :title="title" /> <el-step :title="$t('config.crowdTestRules')" /> </el-steps> <!-- Step 1: Card Configuration --> <el-form v-show="activeStep === 0" ref="cardFormRef" :model="form" :rules="rules" label-width="150px" label-position="left" > <el-form-item :label="$t('config.hostApp')" prop="hostApp"> <el-select filterable v-model="form.hostApp" :placeholder="$t('config.selectHostApp')" style="width: 100%" allow-create :disabled="isEdit" > <el-option v-for="app in hostAppOptions" :key="app" :label="getHostAppLabel(app)" :value="app" /> </el-select> </el-form-item> <el-form-item :label="$t('config.cardPosition')" prop="cardPosition"> <el-select filterable v-model="form.cardPosition" :placeholder="$t('config.selectCardPosition')" style="width: 100%" :disabled="isEdit" allow-create > <el-option v-for="position in cardPositionOptions" :key="position" :label="getCardPositionLabel(position)" :value="position" /> </el-select> </el-form-item> <el-form-item :label="$t('management.cardName')" prop="cardName"> <el-select v-model="form.cardName" :placeholder="$t('common.selectCardName')" style="width: 100%" filterable :disabled="isEdit" @change="selectCard" popper-class="wider-select-popper" > <el-option v-for="item in cardOptions" :key="item.cardName" :label="`${cardTitleMap[item.cardName]}-${item.cardName}`" :value="item.cardName" style="width: 800px" /> </el-select> </el-form-item> <el-form-item :label="$t('config.deviceType')" prop="deviceType"> <el-select filterable v-model="form.deviceType" :placeholder="$t('config.selectDeviceType')" :disabled="isEdit" style="width: 100%" > <el-option v-for="item in deviceTypeOptions.filter( (item) => availableDeviceTypeOptions.indexOf(item.value) !== -1 )" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> <el-form-item :label="$t('config.sortIndex')" prop="index" v-if="form.cardType !== 2"> <el-input-number v-model="form.index" :min="1" :max="100" size="default" style="width: 100%" /> </el-form-item> </el-form> <!-- Step 2: Crowd Testing Rules --> <el-form v-show="activeStep === 1" v-if="!isEdit" ref="testRulesFormRef" :model="testRules" :rules="testRulesRules" label-width="150px" label-position="left" > <el-form-item :label="$t('version.cardVer')" prop="cardVer" required> <el-select v-model="testRules.cardVer" :placeholder="$t('version.cardVer')" style="width: 100%" filterable > <el-option v-for="version in cardVersionOptions" :key="version" :label="version" :value="version" /> </el-select> </el-form-item> <el-form-item :label="$t('config.configMethod')" prop="configMethod" required> <el-radio-group v-model="testRules.configMethod" @change="changeConfigMethod"> <el-radio value="template">{{ $t('config.useTemplate') }}</el-radio> <el-radio value="manual">{{ $t('config.manualSetup') }}</el-radio> </el-radio-group> </el-form-item> <el-form-item v-if="testRules.configMethod === 'template'" :label="$t('config.ruleTemplate')" prop="templateId" > <el-select v-model="testRules.templateId" :placeholder="$t('config.selectTemplate')" style="width: 100%" filterable @change="handleTemplateChange" > <el-option v-for="template in ruleTemplateOptions" :key="template.id" :label="template.ruleName" :value="template.id" /> </el-select> </el-form-item> <CardRules :action="'edit'" :formData="group" ref="cardRulesFormRef" @rules-changed="handleRulesChanged" /> </el-form> <template #footer> <el-button @click="resetForm()">{{ $t('common.cancel') }}</el-button> <el-button v-if="activeStep === 0" @click="submitForm" type="primary"> {{ $t('common.save') }} </el-button> <el-button v-if="activeStep > 0" @click="prevStep">{{ $t('common.prev') }}</el-button> <el-button v-if="activeStep < 1 && !isEdit" type="primary" @click="nextStep"> {{ $t('common.next') }} </el-button> <el-button v-if="activeStep === 1" @click="saveAndPublish(1)" type="primary"> {{ $t('common.saveAndBetaPublish') }} </el-button> </template> </el-dialog> </template> <script> import { ElMessage } from 'element-plus' import validator from '@/utils/ValidateUtil.ts' import { requestHandler } from '@/utils/request' import axios from 'axios' import CardRules from '../../testrule/components/CardRules.vue' import { useGlobalMaps } from '@/utils/util.ts' import { reactive } from 'vue' export default { setup() { const maps = reactive({ hostAppMap: {}, cardPositionMap: {}, providerMap: {} }) const getHostAppLabel = (app) => maps.hostAppMap[app] || app const getCardPositionLabel = (app) => maps.cardPositionMap[app] || app return { getHostAppLabel, getCardPositionLabel, maps } }, components: { CardRules }, props: { cancel: Function, dialogVisible: { type: Boolean, default: false }, title: { type: String, default: '' }, formData: Object, hostAppOptions: { type: Array, default: () => [] }, cardPositionOptions: { type: Array, default: () => [] }, deviceTypeOptions: { type: Array, default: () => [] }, cardOptions: { type: Array, default: () => [] }, isEdit: { type: Boolean, default: false }, getHostAppLabel: Function, getCardPositionLabel: Function, cardTitleMap: Object }, emits: ['update:visible', 'success'], computed: { visible: function () { return this.dialogVisible } }, data() { return { formRef: {}, testRulesFormRef: {}, activeStep: 0, form: { hostApp: this.formData.hostApp, cardPosition: this.formData.cardPosition, deviceType: this.formData.deviceType, cardName: this.formData.cardName, id: this.formData.id, cardType: 0, index: this.formData.index || 1 }, group: { testRules: [ { id: Date.now(), items: [ { op: 'equal', key: '', values: [] } ] } ] }, testRules: { cardVer: '', configMethod: 'template', templateId: '' }, rules: { hostApp: [ { required: true, message: this.$t('config.selectHostApp'), trigger: 'change' }, { pattern: validator.hostApp, message: this.$t('config.validHostApp'), trigger: ['blur', 'change', 'input'] } ], cardPosition: [ { required: true, message: this.$t('config.selectCardPosition'), trigger: 'change' }, { pattern: validator.cardPosition, message: this.$t('config.validPosition'), trigger: ['blur', 'change'] } ], deviceType: [ { required: true, message: this.$t('config.selectDeviceType'), trigger: 'change' } ], cardName: [ { required: true, message: this.$t('common.selectCardName'), trigger: 'change' } ], index: [ { required: true, message: this.$t('config.enterCardIndex'), trigger: 'blur' }, { pattern: validator.integer, message: this.$t('common.validNumber'), trigger: 'blur' }, { validator: (_, val) => val <= 2147483647, message: this.$t('common.validNumber') } ] }, testRulesRules: { cardVer: [{ required: true, message: this.$t('version.cardVer'), trigger: 'change' }], templateId: [ { required: true, message: this.$t('config.selectTemplate'), trigger: 'change', when: () => this.testRules.configMethod === 'template' } ] }, cardVersionOptions: [], availableDeviceTypeOptions: [], ruleTemplateOptions: [] } }, watch: { formData() { this.form = JSON.parse(JSON.stringify(this.formData)) }, isEdit() {}, 'form.cardName'(newVal) { if (newVal && this.activeStep === 1) { this.loadCardVersions() } } }, created() { this.initMaps() // 组件创建时初始化 }, methods: { selectCard() { // 设置卡片类型 const item = this.cardOptions.find((ent) => { return ent.cardName === this.form.cardName }) this.form.cardType = item.cardType // 查询版本 const queryData = { pageNumber: 1, pageSize: 1000, cardName: this.form.cardName } axios.post('/v2/card/version/page-query', queryData).then((response) => { if (response && response.data && response.data.code === 0) { this.availableDeviceTypeOptions = [ ...new Set(response.data.cardVersions.flatMap((item) => item.deviceTypes)) ] this.form.deviceType = null } }) }, // 新增处理规则变化的方法 handleRulesChanged(newRules) { this.group.testRules = newRules }, async nextStep() { try { await this.$refs.cardFormRef.validate() this.activeStep++ this.loadCardVersions() this.loadRuleTemplates() } catch (error) { console.error('Validation failed:', error) } }, prevStep() { this.activeStep-- }, handleTemplateChange(selectedId) { // 从 ruleTemplateOptions 中找到选中的 template const selectedTemplate = this.ruleTemplateOptions.find( (template) => template.id === selectedId ) if (selectedTemplate) { this.group = { testRules: selectedTemplate.testRules } } }, changeConfigMethod(configMethod) { if (configMethod === 'manual') { this.group = { testRules: [ { id: Date.now(), items: [ { op: 'equal', key: '', values: [] } ] } ] } } else { this.handleTemplateChange(this.testRules.templateId) } }, loadCardVersions() { const queryData = { pageNumber: 1, pageSize: 1000, cardName: this.form.cardName, deviceTypes: [this.form.deviceType] } axios.post('/v2/card/version/page-query', queryData).then((response) => { if (response && response.data && response.data.code === 0) { this.cardVersionOptions = [ ...new Set(response.data.cardVersions.map((item) => item.cardVer)) ] } }) }, loadRuleTemplates() { const queryData = { pageNumber: 1, pageSize: 1000, hostApp: this.form.hostApp } axios.post('/v1/card/test-rule/page-query', queryData).then((response) => { if (response && response.data && response.data.code === 0) { this.ruleTemplateOptions = response.data.testRuleDtos } }) }, async submitForm() { try { await Promise.all([this.$refs.cardFormRef.validate()]) const cardNameToIndex = {} const index = this.form.cardType === 2 ? 0 : this.form.index || 1 this.form.index = index cardNameToIndex[this.form.cardName] = index const requestData = { ...this.form, testRules: this.testRules, cardIndexMap: cardNameToIndex } const url = this.isEdit ? '/v1/card/operation/batch-update-index' : '/v1/card/operation/insert' await requestHandler({ url, data: requestData }) this.$emit('success') this.cancel() } catch (error) { // 处理后端返回的重复错误 (code 100001) if (error.response?.data.code === 100001) { ElMessage.error(this.$t('config.cardDuplicateIndexError')) return } if (error.message) { ElMessage.error(error.message || this.$t('common.operationFailed')) } } }, async saveAndPublish(status) { try { await Promise.all([ this.$refs.cardFormRef.validate(), this.$refs.testRulesFormRef.validate(), this.$refs.cardRulesFormRef.validate() ]) this.form.index = this.form.cardType === 2 ? 0 : this.form.index || 1 const requestData = { ...this.form, next: { cardName: this.form.cardName, cardVer: this.testRules.cardVer, status: status, testRules: this.group.testRules } } const url = '/v1/card/operation/insert' await requestHandler({ url, data: requestData }) this.$emit('success') this.cancel() } catch (error) { console.error('Submit failed:', error) } }, resetForm() { this.cancel() this.activeStep = 0 if (this.$refs.cardFormRef) { this.$refs.cardFormRef.resetFields() } if (this.$refs.testRulesFormRef) { this.$refs.testRulesFormRef.resetFields() } this.form = { hostApp: '', cardPosition: '', deviceType: 'phone', cardName: '', index: 1 } this.testRules = { cardVer: '', configMethod: 'template', templateId: '' } this.group = null }, async initMaps() { const { maps, initGlobalMaps } = useGlobalMaps() await initGlobalMaps() // 手动将 ref 值赋给 data(失去响应性,需注意) this.maps = maps.value } } } </script> <style scoped> /* 扩大 el-select 下拉框的宽度 */ .wider-select-popper .el-select-dropdown__list { min-width: 600px !important; width: 100% !important; max-width: 800px; } </style> 这个是我的addCardConfig 我怎么修改才能修改下拉框背景宽度
最新发布
11-07
<div class="process-table"> <!-- 表头列优化 --> <div class="table-header"> <div class="col">工序名称</div> <div class="col">英语名称</div> <div class="col">流程卡内容</div> <div class="col">工艺等待时间(h)</div> <div class="col">机器运行时间(H)</div> <div class="col">set_up时间</div> <div class="col">人工时间</div> <div class="col">单位</div> <div class="col">数量</div> <div class="col">router状况</div> <div class="col">工序代码</div> <div class="col">操作</div> </div> <!-- 数据行 - 与表头相同的列设置 --> <div class="table-body"> <div v-for="(item, index) in formList" :key="index" class="table-row"> <!-- 名称 --> <div class="col"> <el-form-item> <el-input v-model="item.routerName" placeholder="工序名称" /> </el-form-item> </div> <!-- 英语名称 --> <div class="col"> <el-form-item > <el-input v-model="item.englishName" placeholder="English Name" /> </el-form-item> </div> <!-- 流程卡内容 --> <div class="col"> <el-form-item > <el-input v-model="item.routercont" type="textarea" :autosize="{ minRows: 1, maxRows: 3}" class="scrollable-textarea" /> </el-form-item> </div> <!-- 时间参数组 --> <div class="col"> <el-form-item> <el-input v-model="item.processWatitime" /> </el-form-item> </div> <!-- 运行(H) --> <div class="col"> <el-form-item> <el-input v-model="item.machineRuntime" /> </el-form-item> </div> <!-- set_up --> <div class="col"> <el-form-item> <el-input v-model="item.setuptime" placeholder="min/pcs" /> </el-form-item> </div> <!-- 人工 --> <div class="col"> <el-form-item> <el-input v-model="item.labortime" placeholder="min/pcs" /> </el-form-item> </div> <!-- 单位 --> <div class="col"> <el-form-item> <el-input v-model="item.routerUnit" /> </el-form-item> </div> <!-- 数量 --> <div class="col"> <el-form-item> <el-input v-model="item.quantity" /> </el-form-item> </div> <!-- 状况 --> <div class="col"> <el-form-item> <el-select v-model="item.routerStatus" placeholder="状态"> <el-option v-for="dict in dict.type.router_status" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> </div> <!-- 代码 --> <div class="col"> <el-form-item> <el-input v-model="item.processCode" /> </el-form-item> </div> <!-- 操作 --> <div class="col"> <el-button type="danger" icon="el-icon-delete" circle @click="removeProcess(index)" /> </div> </div> </div>表头下面内容不对齐,且内容框太小无法看见输入内容
09-29
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值