feat: 属性管理

This commit is contained in:
zc 2025-10-26 10:28:55 +08:00
parent c0c64aee57
commit 4fb256f328
4 changed files with 267 additions and 0 deletions

View File

@ -69,6 +69,24 @@ export default [
modifyTime: '2025-06-20 09:53:53',
tag: null,
childList: []
},
{
id: 127,
resourceName: 'app类目管理',
resourceType: 1,
resourceCode: null,
path: '/goods/appCategory/index',
pid: 37,
resourceDesc: null,
tenantId: 2,
icon: '',
isCache: 1,
visible: 0,
sort: '3',
createTime: '2025-06-20 09:52:12',
modifyTime: '2025-06-20 09:53:53',
tag: null,
childList: []
}
/* {
id: 127,

View File

@ -0,0 +1,169 @@
<template>
<div id="category-tree" class="p-2 bg-white">
<p>目展示</p>
<el-tree
ref="treeRef"
style="max-width: 600px"
:props="defaultProps"
node-key="id"
draggable
:render-after-expand="false"
check-strictly
:load="handleLoadNode"
lazy
highlight-current
:expand-on-click-node="false"
@node-click="handleNodeClick"
@node-drag-start="handleDragStart"
@node-drop="handleDropFinish"
>
<template #default="{ node, data }">
<div class="category-tree-node">
<span v-if="curId !== data.id">{{ data.name }}</span>
<el-input
ref="inputRef"
v-else
v-model="data.label2"
size="small"
class="w-40"
@keyup.enter="handleInputConfirm(data)"
@blur="handleInputCancel(node, data)"
/>
<div>
<el-button type="primary" link @click="onEdit(data)" size="small">编辑 </el-button>
<el-button type="primary" link @click="append(data)" size="small"> 添加 </el-button>
<el-button type="danger" size="small" link @click="remove(node, data)">
删除
</el-button>
</div>
</div>
</template>
</el-tree>
</div>
</template>
<script lang="ts" setup>
import { ElButton, ElMessage } from 'element-plus'
import type { RenderContentContext } from 'element-plus'
import { handleDragStart, handleDropFinish } from './use-drag'
interface Tree {
id: number
name: string
children?: Tree[]
}
type Node = RenderContentContext['node']
type Data = RenderContentContext['data']
const curId = ref(NaN)
const curParentId = ref(NaN)
const inputRef = ref()
const treeRef = ref()
//
const onEdit = (data: Data) => {
data.label2 = data.name
curId.value = data.id
nextTick(() => {
inputRef.value.focus()
})
}
//
const handleInputConfirm = async (data: any) => {
if (curId.value === Infinity) {
await api.commodity.updateAppCategory.post!({
...data,
name: data.label2,
parentId: curParentId.value
})
} else {
await api.commodity.updateAppCategory.post!({ ...data, name: data.label2 })
}
ElMessage.success('更新成功')
data.name = data.label2
data.label2 = ''
curId.value = NaN
}
//
const handleInputCancel = (node: Node, data: Data) => {
if (data.id === Infinity) {
remove(node, data)
}
curId.value = NaN
}
//
const append = async (data: Data) => {
const node = treeRef.value.getNode(data.id)
curId.value = Infinity
curParentId.value = data.id
const newChild = { id: curId.value, name: '', label2: 'test', hasChild: false }
if (!node.expanded) {
await api.commodity.getAppCategoryList.post!<any>({ parentId: data.id || 0 }).then((res) => {
res.data.forEach((item: any) => {
treeRef.value.append(
{
...item,
children: item.hasChild ? [] : undefined
},
data
)
})
})
}
treeRef.value.append(newChild, data)
node.expanded = true
nextTick(() => {
inputRef.value.focus()
})
}
//
const remove = (node: Node, data: Data) => {
const parent = node.parent
const children: Tree[] = parent?.data.children || parent?.data
const index = children.findIndex((d) => d.id === data.id)
children.splice(index, 1)
}
const defaultProps = {
label: 'name',
children: 'children',
disabled: 'disabled',
isLeaf: (data: any) => {
return !data.hasChild
}
}
//
const handleLoadNode = (node, resolve) => {
api.commodity.getAppCategoryList.post!<any>({ parentId: node.data.id || 0 }).then((res) => {
resolve(
res.data.map((item: any) => ({
...item,
children: item.hasChild ? [] : undefined
}))
)
})
}
//
const handleNodeClick = (node: Node, data: Data) => {
console.log('click', node, data)
}
</script>
<style lang="scss" scoped>
#category-tree {
.category-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
}
</style>

View File

@ -0,0 +1,56 @@
interface Tree {
id: number
label: string
children?: Tree[]
}
export const dataSource = ref<Tree[]>([
{
id: 1,
label: 'Level one 1',
children: [
{
id: 4,
label: 'Level two 1-1',
children: [
{
id: 9,
label: 'Level three 1-1-1'
},
{
id: 10,
label: 'Level three 1-1-2'
}
]
}
]
},
{
id: 2,
label: 'Level one 2',
children: [
{
id: 5,
label: 'Level two 2-1'
},
{
id: 6,
label: 'Level two 2-2'
}
]
},
{
id: 3,
label: 'Level one 3',
children: [
{
id: 7,
label: 'Level two 3-1'
},
{
id: 8,
label: 'Level two 3-2'
}
]
}
])

View File

@ -0,0 +1,24 @@
import type { NodeDropType, RenderContentContext } from 'element-plus'
type Node = RenderContentContext['node']
// 开始拖拽
export const handleDragStart = (node: Node) => {
console.log('drag start', node)
}
// 拖拽成功完成时触发的事件
export const handleDropFinish = (draggingNode: Node, dropNode: Node, dropType: NodeDropType) => {
// 获取拖拽之后最新的ids
const getSortIds = () => {
const parentNode = dropType === 'inner' ? dropNode : dropNode.parent // 获取父节点
if (!parentNode || !parentNode.childNodes) return []
return parentNode.childNodes.map((node) => node.data.id)
}
// 获取拖拽到的位置索引
const sortIds = getSortIds()
if (!sortIds.length) return
api.commodity.sortCategory.post!({ categoryIds: sortIds }).then(() => {
console.log('tree drop:', draggingNode, dropNode.label, dropType)
})
}