vue中el-dialog弹窗出现时页面整体左移

文章描述了在使用Vue.js的el-dialog组件时遇到的body宽度变化问题。当弹窗出现前后,body的宽度产生差异。解决方法是在App.vue全局样式中添加CSS代码,设置body的宽度为calc(100%),以确保宽度不变。

el-dialog弹窗出现前的body:

el-dialog弹窗出现后的body:

解决方法:在App.vue中添加如下代码:

body{
  width:calc(100%) !important;
}

 

 

<template> <el-dialog :append-to-body="true" title="配置人员" v-if="hasLoaded" :close-on-click-modal="false" v-model="dialogVisible" width="800px" center class="dialog" @close="handleDialogVisible" > <div class="line"></div> <el-form ref="form" :model="form" label-width="140px" size="default" :rules="rules"> <el-form-item label="有效期" :style="{ width: '30%' }" prop="signatureTime"> <el-date-picker v-model="form.signatureTime" clearable type="daterange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" align="right" class="time-picker" /> </el-form-item> <!-- 树状选择区域 --> <div class="merchant-select-area"> <div style="text-align: center; position: relative"> <h3>选择人员</h3> <div class="selectablebox" style="margin-right: -2px"> <el-input placeholder="请输入名称/ID" v-model="merchantSearch" style="margin-bottom: 10px; width: 220px" class="merchant-search-input" clearable /> <Search class="small-search-icon" /> <el-tree ref="merchantTree" :data="filteredTreeData" show-checkbox node-key="id" :props="treeProps" :filter-node-method="filterNode" @check-change="handleTreeCheck" default-expand-all ></el-tree> </div> </div> <!-- 中间:穿梭箭头 --> <div class="arrow-column"> <el-button class="btn" @click="moveToSelected" :disabled="selectedTreeNodes.length === 0"> <el-icon class="icons"> <ArrowRight /> <ArrowRight /> </el-icon> </el-button> <el-button class="btn" @click="moveToSelectable" :disabled="selectedMerchantsToRemove.length === 0"> <el-icon class="icons"> <ArrowLeft /> <ArrowLeft /> </el-icon> </el-button> </div> <!-- 右侧:已选列表 --> <div style="text-align: center" class="boxes"> <h3>已选中人员</h3> <div class="selectablebox"> <div class="selected-column"> <el-checkbox-group v-model="selectedMerchantsToRemove"> <el-checkbox v-for="merchant in selectedMerchants" :key="merchant.id" :label="merchant" style="display: block; margin-bottom: 5px" class="merchant-checkbox" > {{ merchant.name }} </el-checkbox> </el-checkbox-group> </div> </div> </div> </div> <div class="dialog-footer"> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary" @click="handleSave">保存</el-button> </div> </el-form> </el-dialog> </template> <script> import _ from 'lodash'; import { ElMessageBox, ElMessage } from 'element-plus'; import { Validator } from '/@/config/utils'; import { ArrowRight, ArrowLeft } from '@element-plus/icons-vue'; export default { components: { ArrowRight, ArrowLeft }, props: { id: { type: String, default: '', }, }, data() { return { form: { signatureTime: [], templateName: '5', }, dialogVisible: true, hasLoaded: false, merchantSearch: '', treeData: [], // 原始树数据 filteredTreeData: [], // 过滤后显示的树数据 treeProps: { children: 'children', label: 'name' }, selectedMerchants: [], // 已选中的人员(右侧列表) selectedMerchantsToRemove: [], // 右侧待移除的人员 selectedTreeNodes: [], // 左侧树中勾选的节点(待右移) }; }, created() { this.getDetail(); }, watch: { merchantSearch(val) { this.$refs.merchantTree.filter(val); }, }, methods: { async getDetail() { this.$http .get(this.$api.settlement.userConfigDetail, { params: { rateId: this.id }, }) .then(({ data }) => { this.form.signatureTime = [data.validStartTime, data.validEndTime]; this.treeData = [...data.allUserList]; this.selectedMerchants = [...(data.mappingUserList || [])]; this.filterTreeData(); this.hasLoaded = true; }) .catch((err) => { console.error('数据加载错误:', err); }); }, // 递归提取一个节点及其所有子节点(扁平化为数组) flattenNodeWithChildren(node) { const result = []; const traverse = (n) => { result.push({ ...n }); // 浅拷贝避免引用污染 if (n.children && Array.isArray(n.children)) { n.children.forEach(traverse); } }; traverse(node); return result; }, // 扁平化整棵树的所有节点(用于比对) flattenTree(tree) { let flat = []; tree.forEach((node) => { flat.push(...this.flattenNodeWithChildren(node)); }); return flat; }, // 过滤树数据(隐藏已选节点及其子节点) filterTreeData() { const selectedIds = new Set(this.selectedMerchants.map((m) => m.id)); this.filteredTreeData = this.deepFilterTree(JSON.parse(JSON.stringify(this.treeData)), selectedIds); }, deepFilterTree(tree, excludedIds) { return tree .map((node) => { if (excludedIds.has(node.id)) return null; if (node.children && node.children.length > 0) { const filteredChildren = this.deepFilterTree(node.children, excludedIds).filter((child) => child !== null); if (filteredChildren.length > 0) { return { ...node, children: filteredChildren }; } return null; } return node; }) .filter((node) => node !== null); }, // 树节点搜索过滤 filterNode(value, data) { if (!value) return true; return data.name.includes(value) || (data.id && data.id.toString().includes(value)); }, // 处理勾选变化:获取所有被勾选的节点(包含父节点和子节点) handleTreeCheck(data, checked) { this.$nextTick(() => { // getCheckedNodes(true) 返回所有被勾选的节点(不含半选) const checkedNodes = this.$refs.merchantTree.getCheckedNodes(true); const allSelectedNodes = []; // 遍历每个勾选的节点,并将其自身+所有子节点扁平化加入 checkedNodes.forEach((node) => { allSelectedNodes.push(...this.flattenNodeWithChildren(node)); }); // 去重(按 id) this.selectedTreeNodes = _.uniqBy(allSelectedNodes, 'id'); }); }, // 右移:将勾选的节点(含其所有子节点)移到右侧 moveToSelected() { if (this.selectedTreeNodes.length === 0) return; // 合并新选中的节点(去重) this.selectedMerchants = _.unionBy(this.selectedMerchants, this.selectedTreeNodes, 'id'); // 清空当前选择 & 更新树显示 this.selectedTreeNodes = []; this.$refs.merchantTree.setCheckedKeys([]); // 清除 UI 上的勾选 this.filterTreeData(); // 刷新左侧树(隐藏已选) }, // 左移:从右侧移除选中的项 moveToSelectable() { if (this.selectedMerchantsToRemove.length === 0) return; // 移除指定元素 this.selectedMerchants = this.selectedMerchants.filter((m) => !this.selectedMerchantsToRemove.some((rm) => rm.id === m.id)); this.selectedMerchantsToRemove = []; this.filterTreeData(); // 重新显示被移回的节点 }, handleSave() { let vm = this; let { form } = this; var validator = new Validator(); // validator.add(form.signatureTime, [{ strategy: 'isNonEmpty', errorMsg: '请选择有效期' }]); if (!Array.isArray(form.signatureTime) || form.signatureTime.length !== 2 || !form.signatureTime[0] || !form.signatureTime[1]) { return ElMessage.error('请选择完整的有效期范围'); } // validator.add(vm.selectedMerchants, [{ strategy: 'isNonEmpty', errorMsg: '请至少选择一个人员' }]); // 手动验证商户选择 if (vm.selectedMerchants.length === 0) { return ElMessage.error('请至少选择一个人员'); } const errorMsg = validator.check(); if (errorMsg) return ElMessage.error(errorMsg); console.log('最终选中的人员:', this.selectedMerchants); const submitData = { // // 有效期:拆分为开始日期和结束日期(根据接口要求调整) rateId: this.id, validStartTime: form.signatureTime[0] + ' 00:00:00', validEndTime: form.signatureTime[1] + ' 23:59:59', userIds: this.selectedMerchants.map((item) => item.userId), // 已选中的人员数组 }; console.log('88888888888', submitData); this.$http .post(this.$api.settlement.saveUserRateConfig, submitData) .then((data) => { console.log('配置已保存:', data); if (data.code == 0) { this.$message.success('配置已保存'); this.$emit('onSuccess', submitData); this.dialogVisible = false; } else { this.$message.error(data.msg || '保存失败'); } }) .catch((err) => { //this.$message.error('保存失败,请重试') console.error('保存错误:', err); }); }, handleDialogVisible() { this.$emit('onClose'); }, }, }; </script> <style lang="scss" scoped> ::v-deep .el-input { width: 350px; } .dialog-footer { display: flex; justify-content: center; margin-top: 20px; } .line { margin-top: -20px; margin-bottom: 23px; border-bottom: 1px solid #3333 !important; } ::v-deep .el-dialog__title { text-align: center; font-size: 18px; } ::v-deep .el-date-editor { min-width: 350px !important; } .merchant-select-area { display: flex; justify-content: space-between; margin-top: 43px; max-height: 400px; overflow: hidden; overflow-x: auto; } .boxes { margin-bottom: 10px; display: flex; flex-direction: column; align-items: center; width: 26-px; } .selectablebox { margin-top: 10px; //width: 316px; width: 266px; border: 1px solid #3333; border-radius: 5px; padding: 10px; height: 350px; } .selectablebox > div { width: 100%; } .arrow-column { display: flex; flex-direction: column; justify-content: center; align-items: center; } .arrow-column .el-button { margin: 5px 0; } ::v-deep .el-tree { width: 100%; max-height: 260px; overflow-y: auto; overflow-x: auto; padding: 10px; } .selected-column { flex: 1; width: 90%; max-height: 350px; overflow-y: auto; overflow-x: auto; border-radius: 5px; margin-top: 10px; } ::v-deep .el-tree::-webkit-scrollbar, .selected-column::-webkit-scrollbar { width: 6px; } ::v-deep .el-tree::-webkit-scrollbar-thumb, .selected-column::-webkit-scrollbar-thumb { background-color: #ddd; border-radius: 3px; } .merchant-search-input ::v-deep .el-input__wrapper { border-radius: 30px; } .merchant-checkbox ::v-deep.el-checkbox { width: 100%; height: 37px; line-height: 37px; padding-left: 10px; margin-top: 10px; } .btn { color: var(--el-color-primary); border: 1px solid var(--el-color-primary); } :deep(.small-search-icon) { font-size: 12px !important; color: #666 !important; cursor: pointer; position: absolute; top: 52px; right: 69px; width: 12px !important; height: 12px !important; } :deep(.el-checkbox-group) { display: flex !important; flex-wrap: wrap; height: 300px; } :deep(.el-checkbox-group .el-checkbox) { width: 30% !important; box-sizing: border-box; } :deep(.el-button:not(.is-circle) i.el-icon), :deep(.el-button i.fa), :deep(.el-button--default i.iconfont), :deep(.el-button--default i.fa) { font-size: 32px !important; } </style> 选择人员下面数据框的数据,超宽度,为什么没有现滚动条
最新发布
10-29
<think>好的,我现在需要回答用户关于如何在Vue中使用Element UI的el-dialog组件打开弹窗的问题。首先,我需要回忆Element UI的文档以及之前的经验。 用户提到了el-dialog,我记得el-dialog的基本用法是通过控制visible属性来显示或隐藏弹窗。可能需要使用v-model或者.sync修饰符来双向绑定这个属性。然后,用户需要示例代码,所以得提供一个简单的例子,展示如何通过按钮触发弹窗的打开和关闭。 接下来,考虑到用户可能还想知道如何自定义标题栏,因为在系统级指令里引用了使用slot="title"重写标题栏的内容[^1]。所以应该在示例中包含如何使用插槽来自定义标题,比如添加放大缩小按钮。这部分可能需要覆盖默认的标题栏,替换成自定义的div,并添加按钮和事件处理函数。 另外,用户提供的引用里还提到了z-index的问题[^2]。虽然用户的问题主要是打开弹窗,但可能需要提及如何调整弹窗的层级,比如设置z-index属性。不过根据问题描述,用户可能暂时不需要这个,但可以作为补充信息。 然后,确保代码部分符合要求,使用正确的格式,行内数学表达式用$...$,独立公式用$$...$$。不过在这个问题中可能用不到数学公式,但需要注意代码块的使用。 接下来,需要按照用户的结构要求,先给步骤和示例,再生成相关问题。步骤应该包括安装Element UI、基础用法、自定义标题和关闭逻辑。示例代码要正确展示visible属性的绑定,以及按钮的点击事件。 可能还需要解释每个部分的作用,比如:visible.sync的作用,插槽的使用方法,以及如何在methods里定义handleClose函数来处理关闭事件。此外,自定义标题栏时,要提到使用slot="title",并添加需要的按钮,比如放大缩小,这里可能需要调用Element的方法或者调整样式,但示例中可以简化,只展示结构。 最后,生成的相关问题需要围绕el-dialog的其他功能,比如控制层级、自定义内容、事件处理等,参考用户提供的引用内容,可能涉及zIndex设置或其它弹窗组件如el-drawer的比较。 需要检查是否所有引用都正确标注,比如在提到自定义标题时引用[^1],在提到z-index时引用[^2]。同时确保回答结构清晰,分步骤说明,代码示例正确,语言使用中文,符合用户的要求。</think>### Vue Element UI el-dialog 打开弹窗示例 以下是使用 `el-dialog` 组件实现弹窗的基础步骤和代码示例: #### 1. 安装并引入 Element UI 首先确保已安装 Element UI: ```bash npm install element-ui ``` 在 Vue 入口文件(如 `main.js`)中全局引入: ```javascript import Vue from 'vue'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); ``` #### 2. 基础弹窗实现 通过 `v-model` 或 `:visible.sync` 控制弹窗显示: ```vue <template> <div> <el-button @click="dialogVisible = true">打开弹窗</el-button> <el-dialog title="基础弹窗" :visible.sync="dialogVisible" width="30%" @close="handleClose"> <span>这是弹窗内容</span> <span slot="footer" class="dialog-footer"> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary" @click="dialogVisible = false">确认</el-button> </span> </el-dialog> </div> </template> <script> export default { data() { return { dialogVisible: false }; }, methods: { handleClose() { console.log('弹窗已关闭'); } } }; </script> ``` #### 3. 自定义标题栏 通过插槽 `slot="title"` 覆盖默认标题栏,添加自定义功能(如放大缩小): ```vue <el-dialog :visible.sync="dialogVisible"> <template slot="title"> <div class="custom-title"> <span>自定义标题</span> <el-button @click="toggleZoom">放大</el-button> </div> </template> <span>弹窗内容</span> </el-dialog> ``` #### 4. 关键属性说明 - `:visible.sync`:双向绑定弹窗显示状态。 - `width`:控制弹窗宽度(支持百分比或像素)。 - `@close`:弹窗关闭时触发的事件。 - 通过 `z-index` 属性可调整弹窗层级。
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值