uniapp+v3开发小程序拖拽排序功能

server/2025/3/15 2:02:04/
项目需求:需要根据用户喜好手动排序(这里只需要上下排序)

排序前(左图) => 排序时(右图)

拖动演示

思路: 
1.创建一个拖动的元素,当拖动元素与其他元素触碰时更换位置重排列表
2.长按元素记录起点位置与下标,并将目标元素赋值与拖动元素
3.移动时处理触碰逻辑
4.松手时清空拖动元素
5.全部代码如下
1.getPos方法获取每个元素的位置top/left,便于交互后重新排列的元素定位
2.getIntersectRow方法根据拖动元素的top及bottom判断与那个元素相交,可能不止一个,取相交部分最多一个
<template><view class="realTime"><uni-nav-bar dark left-icon="left" left-text="" @clickLeft="prePage" :fixed="true" shadowbackground-color="#3B45FF" status-bar title="实时参数" /><view class="wrap boxSize" @touchmove="moveHandle" @touchend="endHandle"><view class="paramsIt boxSize" @longpress="e => startHandle(e, index, item)" :class="{'sortIt': isSort}":style="{'top':item.top + 'px', 'left': item.left + 'px'}" v-for="(item,index) in paramsList":key="item.key"><view class="left"><image v-if="isSort" src="@/static/svg/sort.svg" mode=""></image><view class="name">{{item.key}}</view></view><view class="val" v-if="!isSort">{{`${item.val}${item.unit}`}}</view></view><view class="paramsIt boxSize sortIt active" v-if="activeItem":style="{'top':activeItem.top + 'px', 'left': activeItem.left + 'px'}"><view class="left"><image src="@/static/svg/sort.svg" mode=""></image><view class="name">{{activeItem.key}}</view></view></view></view><view class="btnBox boxSize"><button type="default" style="color:#ffffff;backgroundColor:#3B45FF;borderColor:#1AAD19" class="btn"@tap="sortHandle">{{isSort ? '确认' : '参数排序'}}</button></view></view>
</template><script setup>import {getCurrentInstance,nextTick,reactive,toRefs} from 'vue'import {prePage,goPage} from '@/utils/util'import {request} from '@/utils/api'import {onLoad} from "@dcloudio/uni-app"onLoad(config => {openFunction(config)})let state = reactive({routeParams: {},isSort: false,activeIdx: null,activeItem: null,copyItem: null,itemHeight: 65,positionList: [],startY: '',paramsList: []})let {isSort,activeIdx,positionList,paramsList,activeItem} = toRefs(state)let pageInstance = getCurrentInstance()let openFunction = (config) => {state.routeParams = JSON.parse(JSON.stringify(config))getParams()}//获取所有参数let getParams = async () => {let res = await request('device/FieldOrder/current_data/', 'GET', {pond_id: state.routeParams.pond_id,user_id: JSON.parse(uni.getStorageSync('userInfo')).id})if (res) {state.paramsList = JSON.parse(JSON.stringify(res.data))}}//获取所有元素的定位信息let getPos = () => {let query = uni.createSelectorQuery(pageInstance.proxy)query.selectAll('.paramsIt').boundingClientRect().exec(res => {let arr = []res[0].forEach((it, idx) => {let obj = {}obj.top = (it.top - 88) + 12 * idxobj.left = it.leftarr.push(obj)})state.positionList = JSON.parse(JSON.stringify(arr))state.paramsList.forEach((it, idx) => {it.top = state.positionList[idx].topit.left = state.positionList[idx].left})})}//开始排序let sortHandle = async () => {if (!state.isSort) {getPos()state.isSort = truereturn}let paramsList = state.paramsList.map(it => ({key: it.key,val: it.val,unit: it.unit,status: it.status}))let res = await request('device/FieldOrder/', 'POST', {pond_id: state.routeParams.pond_id,user_id: JSON.parse(uni.getStorageSync('userInfo')).id,result: paramsList})if (res) {uni.showToast({icon: 'none',title: '操作成功!'})state.isSort = falsegetParams()}}//按下let startHandle = (e, index, item) => {if (!state.isSort) returnstate.activeIdx = indexstate.activeItem = JSON.parse(JSON.stringify(item))state.startY = e.touches[0].clientY}//移动let moveHandle = (e, index) => {if (!state.activeItem) return//元素跟随鼠标移动let diffY = e.touches[0].clientY - state.startYstate.activeItem.top = state.activeItem.top + diffYstate.startY = e.touches[0].clientY//判断交叉元素let dragTop = state.activeItem.toplet dragBtm = dragTop + state.itemHeightlet intersectRow = getIntersectRow(dragTop, dragBtm)if (!intersectRow) returnlet intersectIdx = state.positionList.findIndex(i => i.top == intersectRow.top)//其他元素变换位置let copyParams = [...state.paramsList]if (intersectIdx !== state.activeIdx) {copyParams.splice(state.activeIdx, 1)copyParams.splice(intersectIdx, 0, state.paramsList[state.activeIdx])copyParams.forEach((it, idx) => {it.top = state.positionList[idx].topit.left = state.positionList[idx].left})state.activeIdx = intersectIdxnextTick(() => {state.paramsList = [...copyParams]})}}//抬起let endHandle = (e, index) => {state.activeIdx = nullstate.activeItem = null}//交叉元素let getIntersectRow = (dragTop, dragBtm) => {let filter = state.positionList.filter(it => {let acmeFlag = it.top < dragTop && (it.top + state.itemHeight) > dragToplet lowFlag = it.top < dragBtm && (it.top + state.itemHeight) > dragBtmreturn acmeFlag || lowFlag})//取最近一个let midY = dragTop + state.itemHeight / 2filter.forEach(i => i.diffY = Math.abs((i.top + state.itemHeight / 2) - midY))filter.sort((a, b) => a.diffY - b.diffY)return filter[0] || null}
</script><style lang="scss" scoped>.realTime {width: 100%;height: 100%;.wrap {width: 100%;height: calc(100% - 160px);padding: 12px 18px;overflow-y: auto;border-radius: 8px;position: relative;.paramsIt {position: relative;width: 100%;height: 65px;padding: 0 20px;display: flex;align-items: center;justify-content: space-between;background-color: #fff;transition: all 0.3s ease; // 添加过渡效果.left {display: flex;align-items: center;image {width: 24px;height: 24px;margin-right: 10px;}.name {font-family: PingFang SC;font-size: 18px;font-weight: 500;line-height: normal;letter-spacing: 0px;font-variation-settings: "opsz" auto;/* 正文色/正文色 */color: #1A1A1A;}}.val {font-family: PingFang SC;font-size: 18px;font-weight: normal;line-height: normal;text-align: right;letter-spacing: 0px;font-variation-settings: "opsz" auto;/* 字体/次要文字 */color: #909399;}}.sortIt {position: absolute;width: calc(100% - 36px);border-radius: 8px;margin-bottom: 12px;}.active {box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);/* 添加阴影 */opacity: 0.8;/* 降低透明度 */// 确保拖拽元素在最上层z-index: 2;// 拖拽元素禁用过渡transition: none !important;}}.btnBox {width: 100%;padding: 0 18px;display: flex;align-items: center;justify-content: space-between;.btn {width: 100%;}}}
</style>

注意: 拖动后再给原数组赋值时需加上nextTick函数,如需多行多列拖动只需再次基础上加部分逻辑即可


http://www.ppmy.cn/server/175029.html

相关文章

制造业数字化转型,汽车装备制造企业数字化转型案例,智能制造数字化传统制造业数字化制造业数字化转型案例

《某制造业企业信息化整体解决方案》PPT展示了一个汽车装备企业的整体信息化解决方案&#xff0c;阐述了该企业的业务特点和现状&#xff0c;主要包括按订单生产、多级计划和产品跟踪等&#xff0c;分析了信息化建设的主要困难&#xff0c;如信息管理手工化、过程数据追溯困难、…

快速学习Bootstrap前端框架

什么是 Bootstrap? Bootstrap 是一个开源的前端框架,用于快速开发响应式(Responsive)和美观的网页。它包含: ✅ HTML 组件(导航栏、按钮、表单等) ✅ CSS 样式(网格系统、排版、颜色等) ✅ JavaScript 交互(模态框、轮播图、工具提示等) 官网:Bootstrap The mo…

Java后端序列化工具 Jackson 和 FastJSON

1. Jackson&#xff08;Spring Boot 默认支持&#xff0c;无需额外依赖&#xff09; 1.1 添加依赖&#xff08;如果使用 Spring Boot&#xff0c;默认已有&#xff0c;无需添加&#xff09; 如果你不是 Spring Boot 项目&#xff0c;需要手动添加 Jackson 依赖&#xff1a; …

Java Web大文件下载:从卡顿到丝滑的优化之旅

文章目录 Java Web大文件下载&#xff1a;从卡顿到丝滑的优化之旅一、引言二、优化前的困境&#xff08;一&#xff09;性能瓶颈初现&#xff08;二&#xff09;内存之殇&#xff08;三&#xff09;网络拥堵&#xff08;四&#xff09;代码示例&#xff1a;基本下载实现 三、优…

正则表达式(复习)

文章目录 一、[]: 一个字符集合二、{}: 重复次数三、特殊符号四、(): 分组五、python代码示例六、注意 正则表达式(regular expression)描述了一种字符串匹配的模式&#xff08;pattern&#xff09;&#xff0c;可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个…

计算机操作系统(一) 什么是操作系统

计算机操作系统&#xff08;一&#xff09; 什么是操作系统 前言一、什么是操作系统二、操作系统的作用三、推动操作系统发展的主要动力总结&#xff08;核心概念速记&#xff09;&#xff1a; 前言 当你打开电脑、点击应用、播放音乐时&#xff0c;是谁在背后默默协调这一切&…

css实现标题跑马灯效果

css实现标题跑马灯效果 <div class"topBar"><span class"scrolling-text">滚动字幕</span></div>keyframes marquee {0% {transform: translateX(300%);}100% {transform: translateX(-300%);} }.topBar {width:100%;height: 45px…

系统架构设计师-第6章 系统配置与性能评价

【本章学习建议】 根据考试大纲&#xff0c;本章主要考查系统架构设计师单选题&#xff0c;预计考1分左右&#xff0c;对应第二版教材2.9节&#xff0c;内容较少&#xff0c;较为简单&#xff0c;容易拿分。 6.1 性能指标 1. 计算机的性能指标 对计算机评价的主要性能指标有…