Sfoglia il codice sorgente

花束排序实现中,先保存第一版

shizhongqi 1 anno fa
parent
commit
03fedf1634

+ 1 - 20
.cursor/rules/coding-standards.mdc

@@ -22,8 +22,7 @@ alwaysApply: false
   <!-- 模板内容 -->
 </template>
 
-<script>
-// 或 <script setup lang="ts">
+<script> // 或 <script setup lang="ts">
 export default {
   // 组件选项
 }
@@ -48,15 +47,6 @@ props: {
     default: () => []
   }
 }
-
-// Vue 3 Composition API
-interface Props {
-  title: string
-  list?: any[]
-}
-const props = withDefaults(defineProps<Props>(), {
-  list: () => []
-})
 ```
 
 #### API 请求规范
@@ -266,15 +256,6 @@ props: {
     default: () => []
   }
 }
-
-// Vue 3 Composition API
-interface Props {
-  title: string
-  list?: any[]
-}
-const props = withDefaults(defineProps<Props>(), {
-  list: () => []
-})
 ```
 
 #### API 请求规范

+ 4 - 5
.cursor/rules/general-rules.mdc

@@ -1,7 +1,7 @@
 ---
 alwaysApply: true
 ---
-你是一名顶尖的 uni-app 开发专家,精通 Vue.js (同时支持 Vue 2 和 Vue 3 语法)、JavaScript 和 TypeScript。
+你是一名顶尖的 uni-app 开发专家,精通 Vue.js (只支持 Vue2 )、JavaScript 和 TypeScript。
 你的首要任务是编写高效、可维护、跨端兼容性强的 uni-app 代码。
 
 ### 核心准则
@@ -13,7 +13,7 @@ alwaysApply: true
 6. **样式单位**: 在编写 CSS/SCSS/LESS 时,默认使用 `rpx` 作为响应式尺寸单位,以确保在不同尺寸的设备上表现一致。
 7. **项目结构**: 认知并遵循标准的 uni-app 项目结构,例如 `pages` 目录存放页面,`components` 存放可复用组件,`static` 存放静态资源, `store` 存放状态管理。
 8. **性能优化**: 在生成代码时,考虑到小程序的性能限制,例如避免在视图层进行复杂的运算,合理使用 `v-if` 和 `v-show`,避免频繁的 setData 操作。
-9. **Vue 语法**: 如果项目使用 Vue 3,优先使用 Composition API (`<script setup>`) 以获得更好的类型支持和代码组织。如果项目是 Vue 2,则使用 Options API。
+9. **Vue 语法**: 本项目使用的是 Vue 2,请使用 Options API。
 
 ### 跨平台兼容性
 - **小程序限制**: 注意小程序的包大小限制(主包2M,总包20M),合理使用分包加载
@@ -22,7 +22,6 @@ alwaysApply: true
 - **平台差异**: 使用条件编译处理平台特有功能,如支付、分享、定位等
 
 ### 代码质量要求
-- **类型安全**: 在 TypeScript 项目中,确保类型定义完整且准确
 - **错误处理**: 实现完善的错误处理机制,包括网络请求失败、权限拒绝等场景
 - **用户体验**: 添加适当的加载状态、空状态和错误状态处理
 - **可维护性**: 代码结构清晰,命名规范,注释充分
@@ -42,6 +41,6 @@ alwaysApply: true
 
 **其他项目**
 - jdApp, mallApp -- 其他业务线的移动端应用
-- jdadmin -- 管理后台
+- jdadmin -- 基地管理后台
 
-**重要说明**: 各个项目是相互独立的,修改代码时请确认在正确的项目目录中操作
+**重要说明**: 各个项目是相互独立的,修改代码时请确认在对应项目目录中操作,不跨项目目录

+ 1 - 1
.cursor/rules/project-structure.mdc

@@ -56,7 +56,7 @@ projectName/
 ```
 
 ### 操作指导
-1. **路径确认**: 在修改代码前,必须确认当前操作的项目路径
+1. **路径确认**: 在修改代码前,必须确认当前操作的项目路径,防止跨项目目录分析与工作
 2. **分包识别**: 注意项目中的分包结构 (pages开头的目录)
 3. **配置文件**: 重点关注 `pages.json`, `manifest.json`, `config.js` 等配置文件
 4. **环境区分**: 注意开发环境相关的配置文件如 `ext.json`, `ext-production.json`

+ 1 - 0
hdApp/.gitignore

@@ -24,3 +24,4 @@ yarn-error.log*
 *.njsproj
 *.sln
 *.sw*
+CLAUDE.md

+ 8 - 2
hdApp/src/admin/goods/add.vue

@@ -240,7 +240,7 @@ export default {
         this.form.catIds.push(item.id);
         
         // 如果是第一个选中的分类且是"花束",则将分类名称添加到商品名称中
-        if (!this.firstCategorySelected && item.categoryName === "花束") {
+        if (!this.firstCategorySelected) {
           this.form.name = item.categoryName + (this.form.name || "");
           this.firstCategorySelected = true;
         }
@@ -268,7 +268,7 @@ export default {
         this.form.stock = "";
       }
       if (status === '0') {
-        this.form.stock = 999999999;
+        this.form.stock = 9999;
       }
     },
     // 节日涨价
@@ -335,7 +335,13 @@ export default {
           rule: ["required"],
           msg: ["请输入价格"]
         });
+        
+        // 检查价格是否为有效数字且大于0
+        if (this.form.price !== "" && (isNaN(parseFloat(this.form.price)) || parseFloat(this.form.price) <= 0)) {
+          this.$msg('价格必须是大于0的数字!');
+          return false;
         }
+      }
       
       let formData = e.detail.value;
       let checkRes = form.validation(formData, rules);

+ 465 - 0
hdApp/src/admin/goods/categorySort.vue

@@ -0,0 +1,465 @@
+<template>
+	<view class="app-content">
+		<view class="list-wrap">
+			<block v-if="!$util.isEmpty(list.data)">
+				<block v-for="(item, index) in list.data" :key="item.id">
+					<view 
+						class="list"
+						:class="{ 'dragging': draggedIndex === index }"
+						:style="{ 
+							transform: transformStyle(index), 
+							transition: isDragging ? 'none' : 'transform 0.3s ease', 
+							zIndex: draggedIndex === index ? 999 : 1 
+					}">
+						<view class="list-img" @click="navigateToGoodsSort(item.id)">
+							<image :src="item.img" mode="aspectFit" ></image>
+						</view>
+						<view class="list-det" @click="navigateToGoodsSort(item.id)">
+							<view class="app-size-28 app-color-0" style="font-size:30upx;font-weight:bold;">{{ item.categoryName}}</view>						
+						</view>
+
+						<!-- <text style="position:absolute;top:65upx;right:410upx;color:#CCCCCC;">{{item.inTurn}}</text> -->
+
+						<text v-if="index > 0" style="right:335upx;" class="move-btn" @click.stop="topFn(item.id)">↑</text>
+						<text v-if="index > 0" style="right:350upx;line-height: 10upx;" class="move-btn-line">-</text>
+
+						<text v-if="index < list.data.length - 1" style="right:255upx;" class="move-btn" @click.stop="bottomFn(item.id)">↓</text>
+						<text v-if="index < list.data.length - 1" style="right:272upx;line-height: 68upx;" class="move-btn-line">-</text>
+
+						<text style="right:170upx;" class="move-btn" @click.stop="upFn(item.id)">↑</text>
+						<text style="right:95upx;" class="move-btn" @click.stop="downFn(item.id)">↓</text>
+
+						<view
+						style="position:absolute;top:58upx;right:28upx;cursor:move;"
+						class="drag-handle"
+						:data-index="index" 
+						:data-id="item.id"
+						@touchstart="dragStart" 
+						@touchmove="dragMove" 
+						@touchend="dragEnd">
+							<text style="color:#333333;font-size:45upx;">≡</text>
+						</view>
+					</view>
+				</block>
+			</block>
+			<block v-else>
+				<app-wrapper-empty title="暂无数据" :is-empty="$util.isEmpty(list.data)" />
+			</block>
+		</view>
+	</view>
+</template>
+<script>
+import ModalModule from '@/components/plugin/modal'
+import AppUploader from '@/components/app-uploader'
+import OperateModule from '@/admin/home/components/module/operate'
+import AppWrapperEmpty from '@/components/app-wrapper-empty'
+import { list } from '@/mixins'
+import { categoryListInfo, categoryAddB, categoryUpdateB } from '@/api/goods'
+import { sort } from '@/api/category'
+export default {
+	name: 'category',
+	components: {
+		ModalModule,
+		AppUploader,
+		OperateModule,
+		AppWrapperEmpty
+	},
+	mixins: [list],
+	data() {
+		return {
+			modifyModal: false,
+
+			draggedIndex: -1,
+			draggedItem: null,
+			dragOffsetY: 0,
+			isDragging: false,
+			itemHeight: 160, // 列表项高度,单位upx
+			startY: 0,
+		}
+	},
+	computed: {
+	},
+	onPullDownRefresh() {
+		this.resetList()
+		this._list().then(res => {
+			uni.stopPullDownRefresh()
+		})
+	},
+	onReachBottom() {
+		if (!this.list.finished) {
+			this._list().then(res => {
+				uni.stopPullDownRefresh()
+			})
+		} else {
+			uni.stopPullDownRefresh()
+		}
+	},
+	onLoad() {
+	},
+	methods: {
+		// 跳转到商品排序页面
+		navigateToGoodsSort(categoryId) {
+			uni.navigateTo({
+				url: '/admin/goods/goodsSort?id=' + categoryId
+			})
+		},
+		radioChange(e){
+			this.flower = e.detail.value
+		},
+		// edit(item){
+		// 	this.modifyModal = true
+		// 	this.modifyTitle = '编辑'
+		// 	this.modifyForm = { categoryId:item.id, categoryName: item.categoryName, inTurn: item.inTurn, status: item.status }
+		// 	this.flower = parseInt(item.flower)
+		// 	this.catDisabled = true
+		// },
+		
+		
+		async init() {
+			this._list()
+		},
+		_list() {
+			return categoryListInfo().then(res => {
+				let data = { code: res.code, msg: res.msg, data: { list: res.data } }
+				this.completes(data)
+			})
+		},
+		// _categoryAddOrEditB() {
+		// 	if (!this.modifyForm.categoryName) {
+		// 		this.$msg('请输入分类名称')
+		// 		return false
+		// 	}
+		// 	let form = JSON.parse(JSON.stringify(this.modifyForm))
+		// 	let host = categoryAddB
+		// 	if(form.categoryId && form.categoryId > 0){
+		// 		host =  categoryUpdateB
+		// 	}
+		// 	if(Number(this.flower) == 2){
+		// 		this.$msg('请选择属性')
+		// 		return false
+		// 	}
+		// 	host({ ...form,flower:this.flower }).then(res => {
+		// 		this.$msg('操作成功!')
+		// 		this.modalCancel()
+		// 		setTimeout(() => {
+		// 			this.resetList()
+		// 			this._list()
+		// 		}, 1000)
+		// 	})
+		// },
+		// modifyModalClick(e) {
+		// 	if (e.index === 0) {
+		// 		this.modalCancel()
+		// 	} else {
+		// 		this._categoryAddOrEditB()
+		// 	}
+		// },
+		// modalCancel() {
+		// 	this.delModal = false
+		// 	this.modifyModal = false
+		// },
+		switchChangeFn(e) {
+			this.modifyForm.status = e.detail.value ? 1 : 0
+		},
+		
+		// 拖拽相关方法
+		transformStyle(index) {
+			if (index === this.draggedIndex) {
+				return `translateY(${this.dragOffsetY}upx)`
+			}
+			return 'translateY(0upx)'
+		},
+		
+		dragStart(e) {
+			const index = parseInt(e.currentTarget.dataset.index)
+			this.draggedIndex = index
+			this.draggedItem = this.list.data[index]
+			this.startY = e.touches[0].clientY
+			this.isDragging = true
+			
+			// 添加拖拽时的视觉反馈
+			// 设置当前拖拽项的样式
+			// uni.createSelectorQuery()
+			// 	.selectAll('.list')
+			// 	.boundingClientRect(rects => {
+			// 		if (rects && rects[this.draggedIndex]) {
+			// 			this.itemHeight = rects[0].height
+			// 			// 通过数据绑定方式设置样式,不直接操作DOM
+			// 			this.$set(this.list.data[this.draggedIndex], 'isDragging', true)
+			// 			// 震动反馈
+			// 			uni.vibrateShort({
+			// 				success: () => {
+			// 					console.log('震动反馈成功')
+			// 				}
+			// 			})
+			// 		}
+			// 	}).exec()
+			
+		},
+		
+		dragMove(e) {
+			if (!this.isDragging || this.draggedIndex === -1) return
+			
+			const currentY = e.touches[0].clientY
+			const deltaY = currentY - this.startY
+			
+			// 根据设备像素比例转换为upx单位,确保在不同设备上表现一致
+			const pixelRatio = uni.getSystemInfoSync().pixelRatio || 2
+			const deltaYupx = deltaY * (750 / uni.getSystemInfoSync().windowWidth)
+			
+			this.dragOffsetY = deltaYupx
+			
+			// 更精确地计算目标位置
+			const itemHeightUpx = this.itemHeight * (750 / uni.getSystemInfoSync().windowWidth)
+			
+			// 使用更精确的计算方法确定移动的项目数
+			const moveDistance = Math.abs(deltaYupx)
+			const moveDirection = deltaYupx > 0 ? 1 : -1
+			const moveItems = Math.floor(moveDistance / (itemHeightUpx * 0.5)) * moveDirection
+			
+			// 确保目标索引在有效范围内
+			const targetIndex = Math.max(0, Math.min(this.list.data.length - 1, this.draggedIndex + moveItems))
+			
+			
+			if (targetIndex !== this.draggedIndex) {
+				// 交换元素位置
+				const newData = [...this.list.data]
+				const draggedItem = newData[this.draggedIndex]
+				newData.splice(this.draggedIndex, 1)
+				newData.splice(targetIndex, 0, draggedItem)
+				
+				// 更新数据和拖拽状态
+				this.list.data = newData
+				this.draggedIndex = targetIndex
+				this.startY = currentY
+				this.dragOffsetY = 0
+				
+				// 提供触感反馈
+				// uni.vibrateShort()
+			}
+		},
+		
+		dragEnd(e) {
+			console.log('dragEnd')
+			if (!this.isDragging) return
+			
+			// 重置拖拽状态
+			this.draggedIndex = -1
+			this.draggedItem = null
+			this.dragOffsetY = 0
+			this.isDragging = false
+			this.startY = 0
+			
+			// 在UniApp中不需要手动移除视觉反馈,因为状态已重置
+			// 移除视觉反馈
+			// const items = document.querySelectorAll('.list')
+			// items.forEach(item => {
+			// 	item.style.opacity = '1'
+			// 	item.style.boxShadow = 'none'
+			// })
+			// 更新排序值并发送到后端
+			this.updateSortOrder()
+		},
+		
+		updateSortOrder() {
+			// 更新每个项目的inTurn值,从大到小排序(值越大越靠前)
+			const sortData = this.list.data.map((item, index) => ({
+				id: item.id,
+				inTurn: this.list.data.length - index // 反向索引,使第一个项目有最大值
+			}))
+
+			console.log(sortData)
+			
+			// 调用API更新排序
+			sort({ items: sortData }).then(res => {
+				if (res.code === 1) {
+					this.$msg('排序更新成功')
+				} else {
+					this.$msg('排序更新失败')
+				}
+			}).catch(err => {
+				console.error('更新排序失败:', err)
+				this.$msg('排序更新失败')
+			})
+		},
+		
+		// 原有的上移下移方法
+		upFn(id) {
+			console.log('upFn: ' + id)
+			
+			const index = this.list.data.findIndex(item => item.id === id)
+			if (index > 0) {
+				const newData = [...this.list.data]
+				const temp = newData[index]
+				newData[index] = newData[index - 1]
+				newData[index - 1] = temp
+				this.list.data = newData
+				this.updateSortOrder()
+			}
+		},
+		
+		downFn(id) {
+			console.log('downFn: ' + id)
+			
+			const index = this.list.data.findIndex(item => item.id === id)
+			if (index < this.list.data.length - 1) {
+				const newData = [...this.list.data]
+				const temp = newData[index]
+				newData[index] = newData[index + 1]
+				newData[index + 1] = temp
+				this.list.data = newData
+				this.updateSortOrder()
+			}
+		},
+
+		// 置顶
+		topFn(id) {
+			console.log('topFn: ' + id)
+			
+			const index = this.list.data.findIndex(item => item.id === id)
+			if (index > 0) {
+				const newData = [...this.list.data]
+				const targetItem = newData[index]
+				
+				// 将目标项移到第一位
+				newData.splice(index, 1)
+				newData.unshift(targetItem)
+				
+				this.list.data = newData
+				this.updateSortOrder()
+			}
+		},
+		// 置底
+		bottomFn(id) {
+			console.log('bottomFn: ' + id)
+			
+			const index = this.list.data.findIndex(item => item.id === id)
+			if (index < this.list.data.length - 1) {
+				const newData = [...this.list.data]
+				const targetItem = newData[index]
+				
+				// 将目标项移到最后一位
+				newData.splice(index, 1)
+				newData.push(targetItem)
+				
+				this.list.data = newData
+				this.updateSortOrder()
+			}
+		}
+	}
+}
+</script>
+<style lang="scss" scoped>
+.app-content {
+	min-height: calc(100vh - 100upx);
+	padding-bottom: 100upx;
+}
+.list-wrap {
+	position: relative;
+	background-color: #fff;
+	.list {
+		height: 160upx;
+		@include disFlex(center, flex-start);
+		padding: 30upx 0;
+		margin: 0 30upx;
+		color: $fontColor3;
+		border-bottom: 1px solid $borderColor;
+		position:relative;
+		.list-index {
+			width: 50upx;
+		}
+		.list-img {
+			width: 130upx;
+			height: 130upx;
+			image {
+				width:130upx;
+				height:130upx;
+				border-radius: 20upx;
+			}
+		}
+		.list-det {
+			margin-left: 20upx;
+			& > div {
+				margin-top: 14upx;
+				&:first-child {
+					margin-top: 0;
+				}
+			}
+			.list-operate {
+				width:480upx;
+				margin-top:10upx;
+				@include disFlex(center, space-between);
+				.operate-det {
+					width:490upx;
+					padding-left:120upx;
+					& > span {
+						border:1px solid #ccc;
+						border-radius:25upx;
+						padding:5upx 20upx 5upx 20upx;
+						font-size:25upx;
+						margin-left:35upx;
+						&:first-child {
+							margin-left: 0;
+						}
+					}
+				}
+			}
+		}
+	}
+}
+.app-footer {
+	justify-content: flex-end;
+	.admin-button-com {
+		width: 200upx;
+		margin-right: 30upx;
+	}
+}
+.modify-modal {
+	.prompt-text {
+		color: red;
+		position: relative;
+		right: 52upx;
+		bottom: 24upx;
+		// margin-bottom: 10upx;
+	}
+	.line-label {
+		width: 140upx;
+		flex-shrink: 0;
+	}
+	.switch-wrap {
+		margin-top: 30upx;
+	}
+}
+.move-btn {
+	position: absolute;
+	top:65upx;
+	color:blue;
+	border:1px solid #CCCCCC;
+	padding:5upx 10upx;
+	border-radius:50%;
+}
+.move-btn-line {
+	position: absolute;
+	top:65upx;
+	color:blue;
+}
+
+// 拖拽相关样式
+.list.dragging {
+	border: 2px dashed #007AFF !important;
+	box-shadow: 0 4px 20px rgba(0, 122, 255, 0.3) !important;
+	background-color: rgba(0, 122, 255, 0.05) !important;
+	opacity: 0.85;
+	border-radius: 12upx;
+	transform: scale(1.05);
+}
+
+.drag-handle {
+	transition: all 0.2s ease;
+}
+
+.drag-handle:active {
+	transform: scale(1.1);
+	opacity: 0.8;
+}
+</style>

+ 9 - 3
hdApp/src/admin/goods/detail.vue

@@ -259,12 +259,12 @@ export default {
       this.form.stockSet = status;
       if (status === '1') {
         // 如果当前库存是默认的大数值,清空让用户输入
-        if (this.form.stock >= 999999999) {
+        if (this.form.stock >= 9999) {
           this.form.stock = "";
         }
       }
       if (status === '0') {
-        this.form.stock = 999999999;
+        this.form.stock = 9999;
       }
     },
     // 仅支持自取
@@ -310,7 +310,7 @@ export default {
         // 设置库存状态与库存数量
         if (res.data.stockSet == "0") {
           this.form.stockSet = "0";
-          this.form.stock = 999999999;
+          this.form.stock = 9999;
         } else {
           this.form.stockSet = "1";
           this.form.stock = res.data.stock || "";
@@ -365,6 +365,12 @@ export default {
           rule: ["required"],
           msg: ["请输入价格"]
         });
+
+        // 检查价格是否为有效数字且大于0
+        if (this.form.price !== "" && (isNaN(parseFloat(this.form.price)) || parseFloat(this.form.price) <= 0)) {
+          this.$msg('价格必须是大于0的数字!');
+          return false;
+        }
       }
       
       let formData = e.detail.value;

+ 390 - 0
hdApp/src/admin/goods/goodsSort.vue

@@ -0,0 +1,390 @@
+<template>
+	<view class="app-content">
+		<view class="list-wrap">
+			<block v-if="!$util.isEmpty(list.data)">
+				<block v-for="(item, index) in list.data" :key="item.id">
+					<view 
+						class="list"
+						:class="{ 'dragging': draggedIndex === index }"
+						:style="{ 
+							transform: transformStyle(index), 
+							transition: isDragging ? 'none' : 'transform 0.3s ease', 
+							zIndex: draggedIndex === index ? 999 : 1 
+						}">
+						<view class="list-img">
+							<image :src="item.cover" mode="aspectFit" ></image>
+						</view>
+						<view class="list-det">
+							<view class="app-size-28 app-color-0" style="font-size:30upx;font-weight:bold;">{{ item.name || item.goodsName}}</view>						
+						</view>
+
+						<text v-if="index > 0" style="right:335upx;" class="move-btn" @click.stop="topFn(item.id)">↑</text>
+						<text v-if="index > 0" style="right:350upx;line-height: 12upx;" class="move-btn-line">-</text>
+
+						<text v-if="index < list.data.length - 1" style="right:255upx;" class="move-btn" @click.stop="bottomFn(item.id)">↓</text>
+						<text v-if="index < list.data.length - 1" style="right:272upx;line-height: 72upx;" class="move-btn-line">-</text>
+
+						<text style="right:170upx;" class="move-btn" @click.stop="upFn(item.id)">↑</text>
+						<text style="right:95upx;" class="move-btn" @click.stop="downFn(item.id)">↓</text>
+
+						<view
+						style="position:absolute;top:58upx;right:28upx;cursor:move;"
+						class="drag-handle"
+						:data-index="index" 
+						:data-id="item.id"
+						@touchstart="dragStart" 
+						@touchmove="dragMove" 
+						@touchend="dragEnd">
+							<text style="color:#333333;font-size:45upx;">≡</text>
+						</view>
+					</view>
+				</block>
+			</block>
+			<block v-else>
+				<app-wrapper-empty title="暂无数据" :is-empty="$util.isEmpty(list.data)" />
+			</block>
+		</view>
+	</view>
+</template>
+<script>
+import AppWrapperEmpty from '@/components/app-wrapper-empty'
+import { list } from '@/mixins'
+import { getListB, sort } from '@/api/goods'
+export default {
+	name: 'goodsSort',
+	components: {
+		AppWrapperEmpty
+	},
+	mixins: [list],
+	data() {
+		return {
+			categoryId: '', // 分类ID
+			draggedIndex: -1,
+			draggedItem: null,
+			dragOffsetY: 0,
+			isDragging: false,
+			itemHeight: 160, // 列表项高度,单位upx
+			startY: 0,
+		}
+	},
+	onPullDownRefresh() {
+		this.resetList()
+		this._list().then(res => {
+			uni.stopPullDownRefresh()
+		})
+	},
+	onReachBottom() {
+		if (!this.list.finished) {
+			this._list().then(res => {
+				uni.stopPullDownRefresh()
+			})
+		} else {
+			uni.stopPullDownRefresh()
+		}
+	},
+	onLoad(options) {
+		// 接收传递过来的分类ID参数
+		if (options && options.id) {
+			this.categoryId = options.id
+		}
+		//this.init()
+	},
+	methods: {
+		async init() {
+			// 确保list.data是数组
+			if (!Array.isArray(this.list.data)) {
+				this.list.data = []
+			}
+			this._list()
+		},
+		_list() {
+			// 传递分类ID参数和分页参数到API
+			const params = {
+				categoryId: this.categoryId,
+				page: this.list.page,
+				pageSize: this.list.pageSize
+			}
+			console.log('请求参数:', params)
+			console.log('当前list.data:', this.list.data)
+			console.log('list.data类型:', Array.isArray(this.list.data) ? 'Array' : typeof this.list.data)
+			
+			return getListB(params).then(res => {
+				console.log('API返回数据:', res)
+				
+				// 确保返回的数据结构符合预期
+				const responseData = {
+					code: res.code,
+					msg: res.msg,
+					data: {
+						list: res.data && res.data.list ? res.data.list : [],
+						finished: res.moreData,
+						totalPage: res.totalPage || 1
+					}
+				}
+				
+				console.log('处理后的数据:', responseData)
+				console.log('处理后的数据 -- :', res)
+
+				// 使用mixin提供的completes方法处理分页数据
+				this.completes(res)
+				
+				console.log('最终列表数据:', this.list.data)
+				console.log('列表长度:', this.list.data.length)
+				console.log('是否还有更多数据:', this.list.finished)
+			}).catch(err => {
+				console.error('API请求失败:', err)
+				this.list.loading = false
+			})
+		},
+		
+		// 拖拽相关方法
+		transformStyle(index) {
+			if (index === this.draggedIndex) {
+				return `translateY(${this.dragOffsetY}upx)`
+			}
+			return 'translateY(0upx)'
+		},
+		
+		dragStart(e) {
+			const index = parseInt(e.currentTarget.dataset.index)
+			this.draggedIndex = index
+			this.draggedItem = this.list.data[index]
+			this.startY = e.touches[0].clientY
+			this.isDragging = true
+		},
+		
+		dragMove(e) {
+			if (!this.isDragging || this.draggedIndex === -1) return
+			
+			const currentY = e.touches[0].clientY
+			const deltaY = currentY - this.startY
+			
+			// 根据设备像素比例转换为upx单位,确保在不同设备上表现一致
+			const pixelRatio = uni.getSystemInfoSync().pixelRatio || 2
+			const deltaYupx = deltaY * (750 / uni.getSystemInfoSync().windowWidth)
+			
+			this.dragOffsetY = deltaYupx
+			
+			// 更精确地计算目标位置
+			const itemHeightUpx = this.itemHeight * (750 / uni.getSystemInfoSync().windowWidth)
+			
+			// 使用更精确的计算方法确定移动的项目数
+			const moveDistance = Math.abs(deltaYupx)
+			const moveDirection = deltaYupx > 0 ? 1 : -1
+			const moveItems = Math.floor(moveDistance / (itemHeightUpx * 0.5)) * moveDirection
+			
+			// 确保目标索引在有效范围内
+			const targetIndex = Math.max(0, Math.min(this.list.data.length - 1, this.draggedIndex + moveItems))
+			
+			if (targetIndex !== this.draggedIndex) {
+				// 交换元素位置
+				const newData = [...this.list.data]
+				const draggedItem = newData[this.draggedIndex]
+				newData.splice(this.draggedIndex, 1)
+				newData.splice(targetIndex, 0, draggedItem)
+				
+				// 更新数据和拖拽状态
+				this.list.data = newData
+				this.draggedIndex = targetIndex
+				this.startY = currentY
+				this.dragOffsetY = 0
+			}
+		},
+		
+		dragEnd(e) {
+			console.log('dragEnd')
+			if (!this.isDragging) return
+			
+			// 重置拖拽状态
+			this.draggedIndex = -1
+			this.draggedItem = null
+			this.dragOffsetY = 0
+			this.isDragging = false
+			this.startY = 0
+			
+			// 更新排序值并发送到后端
+			this.updateSortOrder()
+		},
+		
+		updateSortOrder() {
+			// 更新每个项目的inTurn值,从大到小排序(值越大越靠前)
+			const sortData = this.list.data.map((item, index) => ({
+				id: item.id,
+				inTurn: this.list.data.length - index // 反向索引,使第一个项目有最大值
+			}))
+
+			console.log(sortData)
+			
+			// 调用API更新排序
+			sort({ items: sortData }).then(res => {
+				if (res.code === 1) {
+					this.$msg('排序更新成功')
+				} else {
+					this.$msg('排序更新失败')
+				}
+			}).catch(err => {
+				console.error('更新排序失败:', err)
+				this.$msg('排序更新失败')
+			})
+		},
+		
+		// 原有的上移下移方法
+		upFn(id) {
+			console.log('upFn: ' + id)
+			
+			const index = this.list.data.findIndex(item => item.id === id)
+			if (index > 0) {
+				const newData = [...this.list.data]
+				const temp = newData[index]
+				newData[index] = newData[index - 1]
+				newData[index - 1] = temp
+				this.list.data = newData
+				this.updateSortOrder()
+			}
+		},
+		
+		downFn(id) {
+			console.log('downFn: ' + id)
+			
+			const index = this.list.data.findIndex(item => item.id === id)
+			if (index < this.list.data.length - 1) {
+				const newData = [...this.list.data]
+				const temp = newData[index]
+				newData[index] = newData[index + 1]
+				newData[index + 1] = temp
+				this.list.data = newData
+				this.updateSortOrder()
+			}
+		},
+
+		// 置顶
+		topFn(id) {
+			console.log('topFn: ' + id)
+			
+			const index = this.list.data.findIndex(item => item.id === id)
+			if (index > 0) {
+				const newData = [...this.list.data]
+				const targetItem = newData[index]
+				
+				// 将目标项移到第一位
+				newData.splice(index, 1)
+				newData.unshift(targetItem)
+				
+				this.list.data = newData
+				this.updateSortOrder()
+			}
+		},
+		// 置底
+		bottomFn(id) {
+			console.log('bottomFn: ' + id)
+			
+			const index = this.list.data.findIndex(item => item.id === id)
+			if (index < this.list.data.length - 1) {
+				const newData = [...this.list.data]
+				const targetItem = newData[index]
+				
+				// 将目标项移到最后一位
+				newData.splice(index, 1)
+				newData.push(targetItem)
+				
+				this.list.data = newData
+				this.updateSortOrder()
+			}
+		}
+	}
+}
+</script>
+<style lang="scss" scoped>
+.app-content {
+	min-height: calc(100vh - 100upx);
+	padding-bottom: 100upx;
+}
+.list-wrap {
+	position: relative;
+	background-color: #fff;
+	.list {
+		height: 160upx;
+		@include disFlex(center, flex-start);
+		padding: 30upx 0;
+		margin: 0 30upx;
+		color: $fontColor3;
+		border-bottom: 1px solid $borderColor;
+		position:relative;
+		.list-index {
+			width: 50upx;
+		}
+		.list-img {
+			width: 130upx;
+			height: 130upx;
+			image {
+				width:130upx;
+				height:130upx;
+				border-radius: 20upx;
+			}
+		}
+		.list-det {
+			margin-left: 20upx;
+			& > div {
+				margin-top: 14upx;
+				&:first-child {
+					margin-top: 0;
+				}
+			}
+			.list-operate {
+				width:480upx;
+				margin-top:10upx;
+				@include disFlex(center, space-between);
+				.operate-det {
+					width:490upx;
+					padding-left:120upx;
+					& > span {
+						border:1px solid #ccc;
+						border-radius:25upx;
+						padding:5upx 20upx 5upx 20upx;
+						font-size:25upx;
+						margin-left:35upx;
+						&:first-child {
+							margin-left: 0;
+						}
+					}
+				}
+			}
+		}
+	}
+}
+.move-btn {
+	position: absolute;
+	top:65upx;
+	color:blue;
+	border:1px solid #CCCCCC;
+	padding: 6upx 12upx;
+	border-radius:50%;
+    z-index: 9999;
+}
+.move-btn-line {
+	position: absolute;
+	top:65upx;
+	color:blue;
+    font-size: 26upx;
+    z-index: 1;
+}
+
+// 拖拽相关样式
+.list.dragging {
+	border: 2px dashed #007AFF !important;
+	box-shadow: 0 4px 20px rgba(0, 122, 255, 0.3) !important;
+	background-color: rgba(0, 122, 255, 0.05) !important;
+	opacity: 0.85;
+	border-radius: 12upx;
+	transform: scale(1.05);
+}
+
+.drag-handle {
+	transition: all 0.2s ease;
+}
+
+.drag-handle:active {
+	transform: scale(1.1);
+	opacity: 0.8;
+}
+</style>

+ 1 - 0
hdApp/src/admin/home/apply.vue

@@ -45,6 +45,7 @@ export default {
                 { name: "花束品种", img: `${this.$constant.hostUrl}/image/ghs/home/hcgl2.png`, url: "/admin/goods/kind",pf:1},
                 { name: "商城分类", img: `${this.$constant.hostUrl}/image/ghs/home/hcgl2.png`, url: "/admin/goods/category",pf:1},
                 { name: "涨价设置", img: `${this.$constant.hostUrl}/image/ghs/home/gj2.png`, url: "/admin/goods/price-increase",pf:1},
+                { name: "分类排序", img: `${this.$constant.hostUrl}/image/ghs/home/hcgl2.png`, url: "/admin/goods/categorySort",pf:1},
             ]
         },
         {

+ 7 - 0
hdApp/src/api/category/index.js

@@ -50,3 +50,10 @@ export const classUpdateB = data => {
 export const changeStatusB = data => {
 	return https.get('/category/change-status', data)
 }
+
+/** *
+ * 商品分类-排序
+ */
+export const sort = data => {
+	return https.post('/category/sort', data)
+}

+ 47 - 34
hdApp/src/api/goods/index.js

@@ -40,13 +40,6 @@ export const getPlantCgDetail = async data => {
 	return https.get("/plant-cg/detail", data);
 };
 
-/** *
- * 获取分类的商品 b
- */
-export const getCategoryToGoods = data => {
-	return https.get('/category/goods-list', data)
-}
-
 /** *
  * 商品-列表 b
  */
@@ -110,6 +103,53 @@ export const usageListB = data => {
 	return https.get('/usage/list', data)
 }
 
+/** *
+ * 商品管理-运费设置 b
+ */
+export const freightSet = data => {
+	return https.get('/merchant/freight-setting', data)
+}
+
+/** *
+ * 商品管理-涨价设置 b
+ */
+export const getRise = data => {
+	return https.get('/goods/setting', data)
+}
+
+/** *
+ * 商品管理-更新涨价 b
+ */
+export const updateRise = data => {
+	return https.post('/goods/update-rise', data)
+}
+/** *
+ * 保存花材组合
+ */
+export const saveItemGroup = data => {
+	return https.post('/goods/save-item-group', data)
+}
+
+/**
+ * 商品排序
+ * @param {*} data 
+ * @returns 
+ */
+export const sort = data => {
+	return https.post('/goods/sort', data)
+}
+
+
+
+
+
+/** *
+ * 获取分类的商品 b
+ */
+export const getCategoryToGoods = data => {
+	return https.get('/category/goods-list', data)
+}
+
 /** *
  * 商品分类-列表(无分页) b
  */
@@ -151,30 +191,3 @@ export const categoryDelB = data => {
 export const categoryStatusB = data => {
 	return https.get('/category/change-status', data)
 }
-
-/** *
- * 商品管理-运费设置 b
- */
-export const freightSet = data => {
-	return https.get('/merchant/freight-setting', data)
-}
-
-/** *
- * 商品管理-涨价设置 b
- */
-export const getRise = data => {
-	return https.get('/goods/setting', data)
-}
-
-/** *
- * 商品管理-更新涨价 b
- */
-export const updateRise = data => {
-	return https.post('/goods/update-rise', data)
-}
-/** *
- * 保存花材组合
- */
-export const saveItemGroup = data => {
-	return https.post('/goods/save-item-group', data)
-}

+ 3 - 1
hdApp/src/pages.json

@@ -292,7 +292,9 @@
 				{ "path": "pdList", "style": { "navigationBarTitleText": "绿植盘点记录" } },
 				{ "path": "pdGoods", "style": { "navigationBarTitleText": "选择商品" } },
 				{ "path": "pdGoodsConfirm", "style": { "navigationBarTitleText": "确认盘点" } },
-				{ "path": "pdGoodsDetail", "style": { "navigationBarTitleText": "详情" } }
+				{ "path": "pdGoodsDetail", "style": { "navigationBarTitleText": "详情" } },
+				{ "path": "categorySort", "style": { "navigationBarTitleText": "分类排序" } },
+				{ "path": "goodsSort", "style": { "navigationBarTitleText": "商品排序" } }
 			]
 		},
 		{