366 lines
12 KiB
Vue
366 lines
12 KiB
Vue
<template>
|
||
<div id="category-tree" class="p-2 bg-white">
|
||
<p>
|
||
类目展示:
|
||
<el-input
|
||
v-if="addRootCategory.showInput"
|
||
class="w-20 !h-[22px]"
|
||
v-model="addRootCategory.inputValue"
|
||
ref="addRootCategoryInputRef"
|
||
@keyup.enter="onAddRootCategory"
|
||
@blur="addRootCategory.showInput = false"
|
||
></el-input>
|
||
<el-button
|
||
v-else
|
||
class="w-20 !h-[22px]"
|
||
type="primary"
|
||
size="small"
|
||
@click="onClickAddRootCategory"
|
||
>添加根类目</el-button
|
||
>
|
||
</p>
|
||
<el-tree
|
||
ref="treeRef"
|
||
: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">
|
||
<div class="flex-1">
|
||
<span v-if="editChildCategoryId !== data.id">{{ data.name }}</span>
|
||
<el-input
|
||
ref="editChildCategoryInputRef"
|
||
v-else
|
||
v-model="data.label2"
|
||
size="small"
|
||
class="w-40"
|
||
@keyup.enter="handleEditChildCategoryConfirm(node, data)"
|
||
@blur="editChildCategoryId = NaN"
|
||
/>
|
||
<template v-if="data.isProperty">
|
||
<dl class="flex items-center">
|
||
<dt class="text-sm text-[#666] mb-2">
|
||
【属性】
|
||
<span v-if="editPropertyTypeId !== data.id">{{ data.categoryPropertyName }}</span>
|
||
<el-input
|
||
ref="editPropertyTypeInputRef"
|
||
v-else
|
||
v-model="data.label2"
|
||
size="small"
|
||
class="w-40"
|
||
@keyup.enter="handleEditPropertyTypeConfirm(node, data)"
|
||
@blur="editPropertyTypeId = NaN"
|
||
/>:
|
||
</dt>
|
||
<dd class="flex mb-2 flex-wrap">
|
||
<div v-for="child in data.vvPropertyValueList" :key="data.id + '-' + child.id">
|
||
<el-input
|
||
v-if="curPropertyValueId === child.id"
|
||
ref="editPropertyValueInputRef"
|
||
class="w-20"
|
||
size="small"
|
||
v-model="inputEditPropertyValue"
|
||
@keyup.enter="onEditPropertyValue(child)"
|
||
@blur="curPropertyValueId = NaN"
|
||
></el-input>
|
||
<el-tag
|
||
v-else
|
||
closable
|
||
effect="plain"
|
||
type="primary"
|
||
class="mr-1"
|
||
@click="onClickPropertyValue(child)"
|
||
@close="onClosePropertyValue(child.id, data)"
|
||
>{{ child.categoryPropertyValue }}</el-tag
|
||
>
|
||
</div>
|
||
<el-input
|
||
ref="propertyValueInputRef"
|
||
class="w-20"
|
||
size="small"
|
||
v-if="visiblePropertyValue[data.id]"
|
||
v-model="inputPropertyValue"
|
||
@keyup.enter="onAddPropertyValue(data)"
|
||
@blur="visiblePropertyValue[data.id] = false"
|
||
></el-input>
|
||
<el-button
|
||
v-else
|
||
type="primary"
|
||
size="small"
|
||
class="ml-2 !text-[10px] !px-2 !py-1 !pb-0.5"
|
||
@click="onClickPropertyBtn(data)"
|
||
>新增属性值</el-button
|
||
>
|
||
<el-button
|
||
type="warning"
|
||
size="small"
|
||
class="!text-[10px] !px-2 !py-1 !pb-0.5"
|
||
@click="onEditPropertyType(node, data.id)"
|
||
>修改属性</el-button
|
||
>
|
||
<el-button
|
||
type="danger"
|
||
size="small"
|
||
class="!text-[10px] !px-2 !py-1 !pb-0.5"
|
||
@click="onDeleteProperty(node, data.id)"
|
||
>删除属性</el-button
|
||
>
|
||
</dd>
|
||
</dl>
|
||
</template>
|
||
</div>
|
||
<div v-if="!data.isProperty">
|
||
<template v-if="!data.hasOriginChild">
|
||
<el-input
|
||
ref="propertyTypeInputRef"
|
||
class="w-20"
|
||
size="small"
|
||
v-if="visiblePropertyType[data.id]"
|
||
v-model="inputPropertyTypeValue"
|
||
@keyup.enter="onAddPropertyType(node, data)"
|
||
@blur="visiblePropertyType[data.id] = false"
|
||
></el-input>
|
||
<el-button
|
||
v-else
|
||
type="success"
|
||
link
|
||
size="small"
|
||
@click="onClickPropertyTypeBtn(data)"
|
||
>新增属性</el-button
|
||
>
|
||
</template>
|
||
<el-button type="primary" link @click="onEdit(data)" size="small">编辑 </el-button>
|
||
<el-input
|
||
ref="addChildCategoryInputRef"
|
||
class="w-20"
|
||
size="small"
|
||
v-if="data.id === addChildCategoryId"
|
||
v-model="childCategoryInputValue"
|
||
@keyup.enter="handleAddChildCategoryConfirm(node, data)"
|
||
@blur="addChildCategoryId = NaN"
|
||
></el-input>
|
||
<el-button v-else type="warning" link @click="onAppend(node, data)" size="small">
|
||
添加子类目
|
||
</el-button>
|
||
<el-button type="danger" size="small" link @click="onDelete(node)"> 删除 </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'
|
||
import { handleMessageBox } from '@/utils/page'
|
||
import {
|
||
treeRef,
|
||
onEdit,
|
||
onAppend,
|
||
handleAddChildCategoryConfirm,
|
||
handleEditChildCategoryConfirm,
|
||
onDelete,
|
||
editChildCategoryId,
|
||
addChildCategoryId,
|
||
childCategoryInputValue,
|
||
editChildCategoryInputRef,
|
||
addChildCategoryInputRef,
|
||
addRootCategory,
|
||
addRootCategoryInputRef,
|
||
onClickAddRootCategory,
|
||
onAddRootCategory
|
||
} from './init-method'
|
||
|
||
type Node = RenderContentContext['node']
|
||
type Data = RenderContentContext['data']
|
||
|
||
const defaultProps = {
|
||
label: 'name',
|
||
children: 'children',
|
||
disabled: 'disabled',
|
||
isLeaf: (data: any) => {
|
||
return !data.hasChild
|
||
}
|
||
}
|
||
|
||
// 获取类目列表
|
||
const handleLoadNode = (node, resolve) => {
|
||
console.warn('----- my data is 222: ', 222)
|
||
if (node.data.vvCategoryPropertyDTOList?.length) {
|
||
resolve(
|
||
node.data.vvCategoryPropertyDTOList.map((item: any) => ({
|
||
...item,
|
||
hasChild: 0,
|
||
isProperty: 1
|
||
}))
|
||
)
|
||
} else {
|
||
api.commodity.getCategoryList.post!<any>({ parentId: node.data.id || 0 }).then((res) => {
|
||
resolve(
|
||
res.data.map((item: any) => ({
|
||
...item,
|
||
vvCategoryPropertyDTOList: item.vvCategoryPropertyDTOList || [],
|
||
hasOriginChild: item.hasChild,
|
||
hasChild: item.hasChild || !!item.vvCategoryPropertyDTOList?.length,
|
||
children: item.hasChild ? [] : undefined
|
||
}))
|
||
)
|
||
})
|
||
}
|
||
}
|
||
|
||
const visiblePropertyValue = ref<Record<number, boolean>>({})
|
||
const inputPropertyValue = ref('')
|
||
const visiblePropertyType = ref<Record<number, boolean>>({})
|
||
const inputPropertyTypeValue = ref('')
|
||
|
||
// 删除类目的属性值
|
||
const onClosePropertyValue = async (id: number, data: any) => {
|
||
const i = data.vvPropertyValueList.findIndex((item: any) => item.id === id)
|
||
await api.commodity.deleteCategoryPropertyValue.post!({ id })
|
||
data.vvPropertyValueList.splice(i, 1)
|
||
ElMessage.success('删除成功')
|
||
}
|
||
|
||
// 新增属性值回车确认
|
||
const onAddPropertyValue = async (data: any = {}) => {
|
||
if (inputPropertyValue.value) {
|
||
const res = await api.commodity.updateCategoryPropertyValue.post!({
|
||
categoryPropertyId: data.id,
|
||
categoryPropertyValue: inputPropertyValue.value
|
||
})
|
||
data.vvPropertyValueList.push(res.data)
|
||
}
|
||
visiblePropertyValue.value[data.id] = false
|
||
inputPropertyValue.value = ''
|
||
ElMessage.success('新增成功')
|
||
}
|
||
|
||
// 新增属性类型回车确定
|
||
const onAddPropertyType = async (node: Node, data: Data) => {
|
||
if (inputPropertyTypeValue.value) {
|
||
const res = await api.commodity.updateCategoryProperty.post!<{
|
||
id: number
|
||
categoryPropertyName: string
|
||
}>({
|
||
categoryId: data.id,
|
||
categoryName: data.name,
|
||
categoryPropertyName: inputPropertyTypeValue.value
|
||
})
|
||
const newData = {
|
||
...res.data,
|
||
hasChild: 0,
|
||
isProperty: 1,
|
||
vvPropertyValueList: []
|
||
}
|
||
data.vvCategoryPropertyDTOList.push(newData)
|
||
node.loaded = false
|
||
node.expand()
|
||
visiblePropertyType.value[data.id] = false
|
||
inputPropertyTypeValue.value = ''
|
||
ElMessage.success('新增成功')
|
||
}
|
||
}
|
||
|
||
// 删除属性
|
||
const onDeleteProperty = async (node: Node, id: number) => {
|
||
handleMessageBox({
|
||
msg: `是否删除属性?`,
|
||
success: api.commodity.deleteCategoryProperty.post!,
|
||
data: { id }
|
||
}).then(() => {
|
||
treeRef.value.remove(node)
|
||
})
|
||
}
|
||
|
||
const propertyTypeInputRef = ref()
|
||
// 新增属性类型按钮
|
||
const onClickPropertyTypeBtn = (data: any) => {
|
||
visiblePropertyType.value[data.id] = true
|
||
nextTick(() => {
|
||
propertyTypeInputRef.value.input!.focus()
|
||
})
|
||
}
|
||
const propertyValueInputRef = ref()
|
||
// 新增属性值按钮
|
||
const onClickPropertyBtn = (data: any) => {
|
||
visiblePropertyValue.value[data.id] = true
|
||
nextTick(() => {
|
||
propertyValueInputRef.value.input!.focus()
|
||
})
|
||
}
|
||
|
||
const curPropertyValueId = ref(NaN)
|
||
const inputEditPropertyValue = ref('')
|
||
const editPropertyValueInputRef = ref()
|
||
// 点击属性值进行编辑
|
||
const onClickPropertyValue = (data: any) => {
|
||
curPropertyValueId.value = data.id
|
||
inputEditPropertyValue.value = data.categoryPropertyValue
|
||
nextTick(() => {
|
||
const timer = setTimeout(() => {
|
||
clearTimeout(timer)
|
||
editPropertyValueInputRef.value[0].input!.focus()
|
||
}, 500)
|
||
})
|
||
}
|
||
// 编辑属性值回车确定
|
||
const onEditPropertyValue = async (data: any) => {
|
||
await api.commodity.updateCategoryPropertyValue.post!({
|
||
id: data.id,
|
||
categoryPropertyValue: inputEditPropertyValue.value
|
||
})
|
||
ElMessage.success('编辑成功')
|
||
data.categoryPropertyValue = inputEditPropertyValue.value
|
||
curPropertyValueId.value = NaN
|
||
inputEditPropertyValue.value = ''
|
||
}
|
||
|
||
// 编辑属性类型相关
|
||
const editPropertyTypeId = ref(NaN)
|
||
const editPropertyTypeInputRef = ref()
|
||
const onEditPropertyType = (node: Node, id: number) => {
|
||
editPropertyTypeId.value = id
|
||
nextTick(() => {
|
||
editPropertyTypeInputRef.value.input!.focus()
|
||
})
|
||
}
|
||
// 编辑属性类型回车确认
|
||
const handleEditPropertyTypeConfirm = async (node: Node, data: any) => {
|
||
await api.commodity.updateCategoryProperty.post!({ ...data, categoryPropertyName: data.label2 })
|
||
ElMessage.success('编辑成功')
|
||
data.categoryPropertyName = data.label2
|
||
editPropertyTypeId.value = NaN
|
||
}
|
||
</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;
|
||
}
|
||
:deep(.el-tree) {
|
||
.el-tree-node__content {
|
||
height: auto;
|
||
}
|
||
}
|
||
}
|
||
</style>
|