3. 创建一个菜单组件-DeepSeek辅助编程
在deepseek中输入:
创建一个vue组件 组件实现菜单的功能 需要让调用该组件是可以自定义一些属性:mode:horizontal横向显示/vertical纵向显示,background-color,text-color,active-text-color,model:通过该model绑定菜单/model为MenuItem的数组 MenuItem由这些参数构成:index:唯一标识/控制显示先后顺序,title:标题/显示的菜单标题,disabled:是否禁用,to:url跳转地址,subitem:子菜单model 支持多级菜单 支持菜单图标
deepseek返回的代码:
Menu的代码
<!-- TMenu.vue -->
<template>
<div
class="menu-container"
:class="[modeClass]"
:style="containerStyle"
>
<TMenuItem
v-for="item in processedMenu"
:key="item.index"
:item="item"
:current-active="activeIndex"
:depth="0"
@item-click="handleItemClick"
/>
</div>
</template>
<script setup>
import { ref, computed, provide, defineProps, defineEmits } from 'vue'
import TMenuItem from './TMenuItem.vue'
const props = defineProps({
model: {
type: Array,
required: true
},
mode: {
type: String,
default: 'horizontal',
validator: v => ['horizontal', 'vertical'].includes(v)
},
backgroundColor: {
type: String,
default: '#ffffff'
},
textColor: {
type: String,
default: '#303133'
},
activeTextColor: {
type: String,
default: '#409eff'
}
})
const emit = defineEmits(['update:modelValue'])
// 提供样式变量给子组件
provide('menuColors', {
backgroundColor: computed(() => props.backgroundColor),
textColor: computed(() => props.textColor),
activeTextColor: computed(() => props.activeTextColor)
})
const activeIndex = ref('')
const modeClass = computed(() => `menu-${props.mode}`)
const containerStyle = computed(() => ({
backgroundColor: props.backgroundColor
}))
// 处理菜单数据
const processedMenu = computed(() => {
return props.model
.map(item => ({ ...item }))
.sort((a, b) => (a.order || 0) - (b.order || 0))
})
const handleItemClick = (item) => {
activeIndex.value = item.index
if (item.to) {
// 这里可以集成路由跳转逻辑
console.log('Navigate to:', item.to)
}
emit('item-click', item)
}
</script>
<style scoped>
.menu-container {
display: flex;
transition: all 0.3s;
}
.menu-horizontal {
flex-direction: row;
}
.menu-vertical {
flex-direction: column;
}
</style>
MenuItem的代码
<!-- TMenuItem.vue -->
<template>
<div
class="menu-item"
:class="[
`depth-${depth}`,
{ 'is-active': isActive },
{ 'is-disabled': item.disabled }
]"
:style="itemStyle"
>
<div
class="menu-item-content"
@click="handleClick"
>
<span class="menu-item-title">{{ item.title }}</span>
<span
v-if="hasSubmenu"
class="menu-arrow"
:class="[isSubmenuOpen ? 'arrow-open' : '']"
>▾</span>
</div>
<div
v-if="hasSubmenu && isSubmenuOpen"
class="submenu"
:class="[submenuClass]"
>
<t-menu-item
v-for="subItem in sortedSubmenu"
:key="subItem.index"
:item="subItem"
:current-active="currentActive"
:depth="depth + 1"
@item-click="$emit('item-click', $event)"
/>
</div>
</div>
</template>
<script setup>
import { computed, inject, ref } from 'vue'
const props = defineProps({
item: {
type: Object,
required: true
},
currentActive: String,
depth: {
type: Number,
default: 0
}
})
const emit = defineEmits(['item-click'])
const menuColors = inject('menuColors')
const isSubmenuOpen = ref(false)
const hasSubmenu = computed(() =>
props.item.submenu?.length > 0
)
const sortedSubmenu = computed(() =>
props.item.submenu?.sort((a, b) => (a.order || 0) - (b.order || 0)) || []) || []
const isActive = computed(() =>
props.currentActive === props.item.index
)
const itemStyle = computed(() => ({
color: isActive.value ? menuColors.activeTextColor : menuColors.textColor
}))
const submenuClass = computed(() =>
props.depth === 0 ? 'submenu-root' : ''
)
const handleClick = () => {
if (props.item.disabled) return
if (hasSubmenu.value) {
isSubmenuOpen.value = !isSubmenuOpen.value
} else {
emit('item-click', props.item)
}
}
</script>
<style scoped>
.menu-item {
position: relative;
cursor: pointer;
transition: all 0.3s;
}
.menu-item.depth-0 {
padding: 12px 20px;
}
.menu-item.depth-1 {
padding: 8px 30px;
}
.menu-item.depth-2 {
padding: 8px 40px;
}
.menu-item-content {
display: flex;
align-items: center;
gap: 8px;
}
.menu-item-title {
white-space: nowrap;
}
.menu-arrow {
transition: transform 0.3s;
margin-left: auto;
}
.arrow-open {
transform: rotate(180deg);
}
.submenu {
position: absolute;
background-color: v-bind('menuColors.backgroundColor');
min-width: 160px;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
}
.menu-vertical .submenu-root {
left: 100%;
top: 0;
}
.menu-horizontal .submenu-root {
top: 100%;
left: 0;
}
.menu-item.is-disabled {
opacity: 0.6;
cursor: not-allowed;
}
.menu-item:hover:not(.is-disabled) {
background-color: rgba(0,0,0,0.04);
}
</style>
调用的代码
<template> <TMenu :model="menuData" mode="vertical" background-color="#f5f7fa" text-color="#606266" active-text-color="#409eff" @item-click="handleMenuClick" /> </template> <script setup> import { ref } from 'vue' import TMenu from './components/TMenu.vue' const menuData = ref([ { index: '1', title: '首页', order: 1, to: '/home' }, { index: '2', title: '产品中心', order: 2, submenu: [ { index: '2-1', title: '手机', submenu: [ { index: '2-1-1', title: '旗舰机型' }, { index: '2-1-2', title: '性价比机型' } ] }, { index: '2-2', title: '电脑', disabled: true } ] }, { index: '3', title: '联系我们', order: 3, submenu: [ { index: '3-1', title: '在线客服' }, { index: '3-2', title: '电话联系' } ] } ]) const handleMenuClick = (item) => { console.log('Clicked menu:', item) } </script>
然后运行,我们就可以看到deepseek给我们写的这个组件了


浙公网安备 33010602011771号