一、实现效果

二、核心功能实现
1. 数据模型设计
const treeData = ref([])
let specId = 0 // 用于生成唯一ID
数据结构示例:
[
{
id: 'spec-1',
label: '颜色',
type: 'spec',
children: [
{ id: 'item-1', label: '红色', type: 'item' },
{ id: 'item-2', label: '蓝色', type: 'item' }
]
}
]
2. 添加规格功能
const addSpec = () => {
const spec = {
id: `spec-${specId++}`,
label: '',
type: 'spec',
children: []
}
treeData.value.push(spec)
}
3. 添加子规格项
const addSpecItem = (spec) => {
const item = {
id: `item-${specId++}`,
label: '',
type: 'item'
}
spec.children.push(item)
}
4. 删除功能
const removeSpec = (item, specIndex, itemIndex) => {
treeData.value[specIndex].children.splice(itemIndex, 1)
}
三、响应式数据管理
1. 数据绑定
<el-input v-model="spec.label" placeholder="请输入规格名称"></el-input>
<el-input v-model="item.label" placeholder="请输入具体规格"></el-input>
2. 列表渲染
<div v-for="(spec, index) in treeData" :key="spec.id">
<!-- 一级规格 -->
<div v-for="(item, itemIndex) in spec.children" :key="item.id">
<!-- 二级规格项 -->
</div>
</div>
四、样式设计技巧
1. 主容器布局
.spec-main {
display: flex;
min-height: 40px;
}
2. 固定左侧宽度
.spec-name {
width: 250px;
min-width: 250px;
position: sticky;
left: 0;
}
3. 右侧可滚动区域
.spec-items-container {
flex: 1;
border-left: 1px solid #eee;
padding-left: 20px;
overflow: hidden;
}
4. 自定义按钮样式
.add-item-btn {
width: 24px;
height: 24px;
border: 1px solid #dcdfe6;
border-radius: 4px;
transition: all 0.3s;
}
.add-item-btn:hover {
color: #409eff;
border-color: #c6e2ff;
background-color: #ecf5ff;
}
五、数据监控与调试
1. 深度监听数据变化
watch(treeData, (newValue) => {
console.log("当前规格数据:", JSON.parse(JSON.stringify(newValue)))
}, { deep: true })
2. 输入事件监控
<el-input @input="logSpecData"></el-input>
const logSpecData = () => {
console.log("用户输入更新:", JSON.parse(JSON.stringify(treeData.value)))
}
六、完整代码
<template>
<div>
<el-button type="primary" @click="addSpec">添加规格</el-button>
<div class="spec-tree-container">
<div v-for="(spec, index) in treeData" :key="spec.id" class="spec-group">
<div class="spec-main">
<div class="spec-name">
<el-input
v-model="spec.label"
placeholder="请输入规格名称"
@input="logSpecData">
</el-input>
<button class="add-item-btn" @click="addSpecItem(spec)">+</button>
</div>
<div class="spec-items-container">
<div class="spec-items">
<div v-for="(item, itemIndex) in spec.children" :key="item.id" class="spec-item">
<el-input
v-model="item.label"
placeholder="请输入具体规格"
@input="logSpecData">
</el-input>
<button class="remove-btn" @click="removeSpec(item, index, itemIndex)">×</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { ref, watch } from 'vue'
export default {
setup () {
const treeData = ref([])
let specId = 0
// 实时监听treeData变化
watch(treeData, (newValue) => {
console.log('当前规格数据:', JSON.parse(JSON.stringify(newValue)))
}, { deep: true })
const addSpec = () => {
const spec = {
id: `spec-${specId++}`,
label: '',
type: 'spec',
children: []
}
treeData.value.push(spec)
}
const addSpecItem = (spec) => {
const item = {
id: `item-${specId++}`,
label: '',
type: 'item'
}
spec.children.push(item)
}
const removeSpec = (item, specIndex, itemIndex) => {
treeData.value[specIndex].children.splice(itemIndex, 1)
}
// 手动打印数据的方法
const logSpecData = () => {
console.log('用户输入更新:', JSON.parse(JSON.stringify(treeData.value)))
}
return {
treeData,
addSpec,
addSpecItem,
removeSpec,
logSpecData
}
}
}
</script>
<!-- 样式保持不变 -->
<style scoped>
.spec-tree-container {
margin-top: 20px;
}
.spec-group {
margin-bottom: 15px;
}
.spec-main {
display: flex;
min-height: 40px;
}
.spec-name {
display: flex;
align-items: flex-start;
width: 250px;
min-width: 250px;
position: sticky;
left: 0;
background: transparent;
z-index: 1;
padding-right: 15px;
}
.spec-name .el-input {
flex: 1;
}
.add-item-btn {
margin-left: 10px;
margin-top: 10px;
width: 24px;
height: 24px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background: #fff;
color: #606266;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
flex-shrink: 0;
}
.add-item-btn:hover {
color: #409eff;
border-color: #c6e2ff;
background-color: #ecf5ff;
}
.spec-items-container {
flex: 1;
min-width: 0;
border-left: 1px solid #eee;
padding-left: 20px;
overflow: hidden;
}
.spec-items {
display: flex;
flex-direction: column;
gap: 8px;
}
.spec-item {
display: flex;
align-items: center;
}
.spec-item .el-input {
flex: 1;
max-width: 300px;
}
.remove-btn {
margin-left: 10px;
width: 24px;
height: 24px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background: #fff;
color: #606266;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
}
.remove-btn:hover {
color: #f56c6c;
border-color: #fbc4c4;
background-color: #fef0f0;
}
</style>

4615

被折叠的 条评论
为什么被折叠?



