merge: fix/country-mode-good-reorder-dev into develop (local only, not pushed)

This commit is contained in:
yeuimu
2026-08-28 21:37:27 +08:00
+88 -1
View File
@@ -675,6 +675,78 @@ async function onLeftTreeDragEnd(dragNode: any, dropNode: any, position: string)
} catch { ElMessage.error('调整失败'); await reloadCategories() } } catch { ElMessage.error('调整失败'); await reloadCategories() }
} }
// ─── 国家模式:商品在国家分组内拖拽排序(保序重排优先级) ───
const treeDragGoodId = ref<string | null>(null)
const treeDragOver = ref<{ id: string; pos: 'before' | 'after' } | null>(null)
function onTreeGoodDragStart(event: DragEvent, data: any) {
if (mode.value !== 'country') return
treeDragGoodId.value = data.goodId
if (event.dataTransfer) {
event.dataTransfer.effectAllowed = 'move'
event.dataTransfer.setData('text/plain', String(data.goodId))
}
}
function onTreeGoodDragOver(event: DragEvent, data: any) {
if (mode.value !== 'country' || !treeDragGoodId.value || treeDragGoodId.value === data.goodId) return
event.preventDefault()
event.stopPropagation()
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect()
treeDragOver.value = {
id: data.goodId,
pos: event.clientY < rect.top + rect.height / 2 ? 'before' : 'after',
}
}
function onTreeGoodDragLeave(_event: DragEvent, data: any) {
if (treeDragOver.value?.id === data.goodId) treeDragOver.value = null
}
async function onTreeGoodDrop(event: DragEvent, data: any) {
event.preventDefault()
event.stopPropagation()
const draggedId = treeDragGoodId.value
const target = treeDragOver.value
treeDragGoodId.value = null
treeDragOver.value = null
if (mode.value !== 'country' || !draggedId || !target) return
if (target.id !== data.goodId || draggedId === data.goodId) return
// 目标所在国家分组的商品(新顺序)
const group = leftTreeData.value.find((c: any) =>
(c.children ?? []).some((g: any) => g.goodId === target.id),
)
if (!group) return
const nodes = [...group.children]
const fromIdx = nodes.findIndex(n => n.goodId === draggedId)
if (fromIdx === -1) return // 跨国拖动不支持(商品归属固定国家)
const [moved] = nodes.splice(fromIdx, 1)
let insertIdx = nodes.findIndex(n => n.goodId === target.id)
if (target.pos === 'after') insertIdx++
nodes.splice(insertIdx, 0, moved)
// 保序重排:该国家现有优先级集合不变,按新顺序重新分配(尽量少扰动全局/品类排序)
const priorities = nodes.map(n => Number(n.priority || 0)).sort((a, b) => b - a)
moveTopLoading.value = 'drag'
try {
await goodsApi.batchUpdatePriority({
items: nodes.map((n, i) => ({ id: Number(n.goodId), priority: priorities[i] ?? 0 })),
})
ElMessage.success('排序已保存')
await refreshLeftTree()
} catch {
ElMessage.error('排序失败')
} finally {
moveTopLoading.value = null
}
}
function onTreeGoodDragEnd() {
treeDragGoodId.value = null
treeDragOver.value = null
}
// ─── Country CRUD ─── // ─── Country CRUD ───
const countryEditVisible = ref(false) const countryEditVisible = ref(false)
const countryEditMode = ref<'create' | 'edit'>('create') const countryEditMode = ref<'create' | 'edit'>('create')
@@ -978,8 +1050,20 @@ onMounted(() => loadAll())
<div <div
v-else-if="data.isGood" v-else-if="data.isGood"
class="good-node" class="good-node"
:class="{ 'good-node--flash': data.goodId === locateFlashGoodId }" :class="{
'good-node--flash': data.goodId === locateFlashGoodId,
'is-dragging': mode === 'country' && treeDragGoodId === data.goodId,
'is-drop-before': mode === 'country' && treeDragOver?.id === data.goodId && treeDragOver?.pos === 'before',
'is-drop-after': mode === 'country' && treeDragOver?.id === data.goodId && treeDragOver?.pos === 'after',
}"
:draggable="mode === 'country'"
title="拖拽调整在国家内的排序"
@click="openEdit(data.raw)" @click="openEdit(data.raw)"
@dragstart="onTreeGoodDragStart($event, data)"
@dragover="onTreeGoodDragOver($event, data)"
@dragleave="onTreeGoodDragLeave($event, data)"
@drop="onTreeGoodDrop($event, data)"
@dragend="onTreeGoodDragEnd"
> >
<el-image v-if="data.goodImage" :src="data.goodImage" fit="cover" class="good-thumb" /> <el-image v-if="data.goodImage" :src="data.goodImage" fit="cover" class="good-thumb" />
<div v-else class="good-thumb-placeholder" /> <div v-else class="good-thumb-placeholder" />
@@ -1403,6 +1487,9 @@ onMounted(() => loadAll())
transition: background 0.12s; transition: background 0.12s;
} }
.good-node:hover { background: #f0f2f5; } .good-node:hover { background: #f0f2f5; }
.good-node.is-dragging { opacity: 0.5; }
.good-node.is-drop-before { box-shadow: inset 0 2px 0 var(--el-color-primary); }
.good-node.is-drop-after { box-shadow: inset 0 -2px 0 var(--el-color-primary); }
.good-thumb { .good-thumb {
width: 36px; height: 36px; border-radius: 6px; flex-shrink: 0; width: 36px; height: 36px; border-radius: 6px; flex-shrink: 0;
object-fit: cover; object-fit: cover;