当前位置: 首页 > news >正文

vue3.0实现数组分组效果

  • 效果图

aca184ddf5a747d0ac36d62cd6912f61

e7cd1c7b5f6c4d969b961ed309575cb6

  • vue3.0+element-plus

  • 拖拽效果 vue-draggable-plus

  • 父组件使用

      <column-dialog :options="fields"v-model="content"></column-dialog>
    
  • 子组件 ColumnDialog/index.vue

      <template><div ><el-input placeholder="请选择"readonly@click="onVisible"v-model="valueName" /><el-dialog title="编辑显示列"width="600"v-model="visible"><div class="column_content"><div class="column_header"><div class="flex justify-between"><el-checkbox :indeterminate="indeterminate"@change="onCheckAllChange"v-model="checkAll">全选</el-checkbox><el-button size="default"@click="handleAddGroup"><el-icon><plus /></el-icon>新增分组</el-button></div></div><vue-draggable ghostClass="custom_drag_class"handle=".draggable_column"@end="onEndDraggable"@start="isDraggable = true"v-model="currentContent"><div v-for="(item,index) in currentContent":key="item.dataIndex"><!-- 分组类型 --><div v-if="item.children"class="group-container" ><div class="draggable_column "><div class="column_row flex justify-between column_child":class="{hideShadow: isDraggable}"><div><img alt=""class="drag_icon"src="@/assets/drag.svg"/><span class="column_title">{{ item.title }}</span></div><div class="field-actions"><el-button icon="Edit"size="small"title="重命名"@click="editField(index)"/><el-button icon="Delete"size="small"title="解散分组"@click="deleteField(index)"/></div></div></div><!-- 分组子字段拖拽容器--><div class="field-children"><vue-draggable class="child-list"ghostClass="dragging"handle=".child-drag-handle"v-model="item.children"><div v-for="ele in item.children":key="ele.dataIndex"><div class="child-drag-handle"><div class="column_row column_child"><img alt=""class="drag_icon"src="@/assets/drag.svg"/><el-checkbox @change="e => onChangeItem(e,ele)"v-model="ele.selectFlag"><span class="column_title">{{ ele.title }}</span></el-checkbox></div></div></div></vue-draggable></div></div><!-- 普通字段类型 --><div v-elseclass="draggable_column"><div class="column_row":class="{hideShadow: isDraggable}"><img alt=""class="drag_icon"src="@/assets/drag.svg"/><el-checkbox @change="e => onChangeItem(e,item)"v-model="item.selectFlag"><span class="column_title">{{ item.title }}</span></el-checkbox></div></div></div></vue-draggable></div><template #footer><el-button @click="closeFather">取消</el-button><el-button type="primary"@click="onSubmit">保存</el-button></template></el-dialog><el-dialog :close-on-click-modal="true"title="新增分组"width="600px"@close="closeGroupModal"v-model="groupModalVisible"><div class="modal-body"><el-form-item class="form-group"label="分组名称"><el-input placeholder="请输入分组名称"v-model.trim="groupName"/></el-form-item><el-form-item class="form-group"label="选择字段"><div class="fields-selection"><div v-for="(field, index) in nonGroupedFields":key="`select-field-${index}-${field.dataIndex}`"class="field-option":class="{ selected: selectedFieldIndexes.includes(index) }"><el-checkbox class="field-checkbox":value="index"v-model="selectedFieldIndexes"/><label>{{ field.title }}</label></div></div></el-form-item></div><template #footer><el-button @click="closeGroupModal">取消</el-button><el-button type="primary"@click="createGroup">确认</el-button></template></el-dialog></div></template><script setup lang="ts">import { VueDraggable } from "vue-draggable-plus";import _ from "lodash";const props = defineProps({title: { type: String, default: "请选择" },options: { type: Array as () => any[], default: () => [] },});// 定义emitsconst emit = defineEmits(["change"]);const model: any = defineModel();const indeterminate = ref(false);const checkAll = ref(false);const visible = ref(false);const List = ref([]);const checkData = ref([]); // 选中勾选的数据const isDraggable = ref(false);const currentContent = ref([]);const groupName = ref(""); // 分组名称const groupModalVisible = ref(false);const selectedFieldIndexes: any = ref([]); // 勾选的索引// 计算属性:非分组字段(用于模态框选择)const nonGroupedFields = computed(() => currentContent.value.filter(field => !field.children));watch(() => model.value,() => {const newList: any = _.cloneDeep(model.value);currentContent.value = newList;checkData.value = newList?.filter(el => el.selectFlag).map(el => el.dataIndex);isCheckAll();},{ immediate: true });// 监听options变化watch(() => props.options,newVal => {List.value = _.cloneDeep(newVal);isCheckAll();},{ immediate: true });function onVisible() {visible.value = true;}// 全选处理function onCheckAllChange(checked) {checkAll.value = checked;indeterminate.value = false;checkData.value = checked ? List.value.map(el => el.dataIndex) : [];currentContent.value.forEach(item => {item.selectFlag = checked;if (item?.children && item?.children.length > 0) {item.children.forEach(ele => {ele.selectFlag = checked;});}});}// 单选处理function onChangeItem(checked, item) {item.selectFlag = checked;const groupId = item.dataIndex;const checkIndex = _.findIndex(checkData.value, e => e === groupId);if (checked) {checkData.value.push(groupId);} else {checkData.value.splice(checkIndex, 1);}isCheckAll();}function isCheckAll() {indeterminate.value = Boolean(checkData.value?.length) && checkData.value?.length < List.value?.length;checkAll.value = checkData.value?.length === List.value?.length;}// 提交处理function onSubmit() {model.value = currentContent.value;emit("change");closeFather();}// 拖拽结束处理function onEndDraggable() {isDraggable.value = false;}// 关闭弹窗function closeFather() {visible.value = false;}// 计算显示的名称const valueName = computed(() => {if (!model.value || model.value.length === 0) {return "";}const list = [];model.value?.forEach(el => {if (el.selectFlag) {list.push(el.title);}if (el?.children) {el.children.forEach(ele => {if (ele.selectFlag) {list.push(ele.title);}});}});return list.map(el => el).join(", ");});// 新增分组function handleAddGroup() {groupName.value = "";selectedFieldIndexes.value = [];groupModalVisible.value = true;}function closeGroupModal() {groupModalVisible.value = false;groupName.value = "";selectedFieldIndexes.value = [];}function editField(index) {const field = currentContent.value[index];ElMessageBox.prompt("请输入新的名称", "编辑名称", {confirmButtonText: "确认",cancelButtonText: "取消",inputValue: field.title,inputPattern: /\S/,inputErrorMessage: "请输入新的分组名称",beforeClose: (action, instance, done) => {if (action === "confirm") {const inputValue = instance.inputValue?.trim();const newList = currentContent.value.filter((el, tmpIndex) => tmpIndex !== index);console.log(newList);const tmp = newList.some(el => el.title === inputValue);if (tmp) {ElMessage.warning("分组名称不能重复");} else {done();}} else {done();}},}).then(({ value }) => {const trimmedValue = value.trim();if (trimmedValue) {currentContent.value[index].title = trimmedValue;ElMessage.success("名称修改成功");}}).catch(() => {});}function deleteField(index) {const field = currentContent.value[index];ElMessageBox.confirm("确定要删除这个分组吗?分组内的字段将转为独立字段", "删除确认", {type: "warning",}).then(() => {const newList = [];if (field.children && field.children.length > 0) {field.children.forEach(child => {newList.push({...child,});});}currentContent.value.splice(index, 1, ...newList);ElMessage.success("删除成功");}).catch(() => {});}function createGroup() {const trimmedName = groupName.value;if (!trimmedName) {ElMessage.warning("请输入分组名称");return;}const tmp = currentContent.value.some(el => el.title === trimmedName);if (tmp) {ElMessage.warning("分组名称不能重复");return;}if (selectedFieldIndexes.value.length === 0) {ElMessage.warning("请至少选择一个字段");return;}// 新增分组逻辑const selectedFields = selectedFieldIndexes.value.map(index => nonGroupedFields.value[index]);const newGroup = { title: trimmedName, dataIndex: trimmedName, children: selectedFields };const selectedDataIndexes = selectedFields.map(f => f.dataIndex);// 更新字段数据currentContent.value = currentContent.value.filter(item => {if (item.children) {return !item.children.some(child => selectedDataIndexes.includes(child.dataIndex));}return !selectedDataIndexes.includes(item.dataIndex);});currentContent.value.unshift(newGroup);// 同步给父组件closeGroupModal();ElMessage.success("分组创建成功");}</script><style lang="scss" scoped>.column_content {padding: 12px;border: solid 1px #eeeeee;background-color: #f6f6f6;max-height: 60vh;overflow-y: auto;.column_header {padding: 10px 5px 10px 10px;}.group-container {margin-bottom: 10px;}.column_row {padding: 10px;cursor: url("@/assets/cursor.svg"), auto;border-bottom: solid 1px #eeeeee;font-size: 14px;background-color: #fff;margin-bottom: 10px;&.column_child {margin-bottom: 0;}&:hover {box-shadow: 0px 0px 20px 5px rgba(116, 116, 116, 0.11);background-color: transparent;}&:active {box-shadow: none;}.drag_icon {margin-right: 5px;width: 14px;height: 14px;display: inline-block;vertical-align: text-top;}.column_title {margin: 0px 10px;color: rgba(23, 26, 29, 0.6);text-align: center;}}.column_row.hideShadow {box-shadow: none;}.custom_drag_class {box-shadow: 0px 0px 20px 5px rgba(116, 116, 116, 0.11);border: 0.5px solid #0089f9;color: #0089f9;}.custom_drag_class .column_title {color: #0089f9;}}.modal-body {padding: 20px;max-height: 60vh;overflow-y: auto;.form-group {margin-bottom: 20px;.fields-selection {width: 100%;border: 1px solid #e9ecef;border-radius: 6px;max-height: 300px;overflow-y: auto;.field-option {padding: 12px;border-bottom: 1px solid #f1f3f4;display: flex;align-items: center;cursor: pointer;transition: background 0.3s ease;.field-checkbox {margin-right: 10px;}}.field-option:last-child {border-bottom: none;}.field-option:hover {background: #f8f9fa;}.field-option.selected {background: #e3f2fd;color: #1976d2;}}}}</style>
    
http://www.gsyq.cn/news/46231.html

相关文章:

  • 如何管理你的订阅服务?一个工具带来的财务新视角
  • 2025年深圳连锁门店神秘顾客暗访机构权威推荐榜单:调查汽车神秘顾客/门店巡检神秘顾客/汽车经销商暗访源头机构精选
  • 实用指南:Android studio 高效使用
  • 2025年11月中国抗衰老设备技术排行榜:前沿科技与健康衰老新选择
  • Windows 下安装 swoole 图文教程(php)
  • 银河麒麟高级服务器操作系统V10SP1(ARM)【auditd服务内存泄露】问题解决方法
  • 【2025-11-10】中年篮球
  • PRISMA 简介:系统综述和荟萃分析(meta分析)的首选报告项目
  • 2025年11月重庆互联网公司推荐排行榜:杰诚智享领跑榜单
  • 供应商协同平台有哪些?主要特征与优势是什么?
  • dongtai-java
  • 雷池 WAF 免费版实测:小白用 Nginx 搭环境,30 分钟护住跨境电商安全
  • 2025年大理石花岗岩生产厂家权威推荐榜单:天然石材/花岗岩/天然大理石源头厂家精选
  • 什么是文件摆渡系统?数据跨网“桥梁”与安全“卫士”!
  • OpenCL shader
  • 银河麒麟KylinV10操作系统清理操作系统中的缓存drop_caches
  • 转载,也要有转载的道德吧
  • 2025年机场无障碍盲道砖生产厂家权威推荐榜单:火车站盲道砖/圆点盲道砖/高铁站盲道砖源头厂家精选
  • C语言知识库 -- 完整C语言笔记目录,并且附带纯C项目源码《小鹅说 C 语言》
  • Day34(4)-F:\硕士阶段\Java\课程资料\2025最新版JavaWeb+AI\资料\02. 前端Web基础(JS+Vue+Ajax)\资料\03. 基础代码\JS-Vue-01~12
  • Linux内核进程管理子系统有什么第六十六回 —— 进程主结构详解(62) - 实践
  • HTML 01 【基础语法学习】 - 详解
  • 2025年浓硫酸订做厂家权威推荐榜单:液体硝酸/工业级盐酸/工业级盐酸源头厂家精选
  • vscode c语言 颜色设置
  • 2025年乌鲁木齐黄金回收权威推荐榜单:黄金上门回收/黄金首饰加工/打金店源头商家精选
  • 2025年华美月饼厂家权威推荐榜单:华美冰皮月饼/榴莲冰皮月饼/五仁月饼源头厂家及品牌代理精选
  • 转让发明专利
  • MySQL主从复制延迟诊断与GTID故障切换看我这篇就行了!
  • 2025研发效能制品库选型新思维:构建安全、高效与国产化兼容的研运基座
  • 网页调试和jmeter调试天气预报