我是用 naiveUI 的 datatable 时自己写一个过滤菜单样式,我目前可以按照预期运行,传入 title 还有 value 还有图标的 src 链接. 格式
[{title:'标签名', value:'test', imgSrc='1.jpg'}]
现在我要允许传递一个 h 函数进去,像这样
[{title:'标签名', value:'test', imgSrc:'1.jpg', icon:h('div',null,'我是图标')}]
但是我不知道这个子组件接收到之后怎么把这个 icon 函数放进去 li 标签里面,直接在 h 函数里把 NList 标签用 h 函数来写,像下面的方法,但是他会报错,浏览器全都是下面的报错,我想问下这个要怎么处理才比较好一点
chunk-6SSRW7KQ.js?v=5c5b974b:1543 [Vue warn]: Non-function value encountered for default slot. Prefer function slots for better performance.
at <Text>
at <ListItem key="peersGettingFromUs" onClick=fn<onClick> class="padding" >
at <List hoverable=true clickable=true showDivider=false >
at <List>
at <ResizeObserver onResize=fn<handleContentResize> >
at <ResizeObserver onResize=fn<handleContainerResize> >
at <Scrollbar ref="scrollbarInstRef" xScrollable=false theme=undefined ... >
at <Scrollbar style= Object >
at <Menu options= Array(20) selected= Array(4) onUpdate:modelValue=fn<onUpdate:modelValue> >
at <DropdownRenderOption tmNode= Object key="header" >
at <DropdownMenu ref=fn class="n-popover-shared n-dropdown" clsPrefix="n" ... >
at <BaseTransition onEnter=fn onAfterLeave=fn<onAfterLeave> appear=true ... >
at <Transition name="popover-transition" appear=true onEnter=fn<onEnter> ... >
at <LazyTeleport show=true to="body" disabled=false >
at <Follower ref="followerRef" zIndex=undefined show=true ... >
at <PopoverBody theme= Object themeOverrides=undefined builtinThemeOverrides=undefined ... >
at <Binder ref="binderInstRef" syncTarget=true syncTargetWithParent=false >
at <Popover show=true defaultShow=false showArrow=false ... >
at <Dropdown trigger="hover" options= Array(1) >
at <Flex>
at <HelloWorld>
at <App>
const mylist = () => {
return h(NList, {
hoverable: true,
clickable: true,
showDivider: false,
},
props.options.map((item) => {
return h(NListItem,
{
key: item.title,
onClick: () => itemClick(item),
class: 'padding',
}, {
prefix: () => {
return h(NAvatar, {
size: 22,
src: item.imgSrc,
color: 'white',
bordered: true
})
},
default: () => {
return h(NText, null, item.title)
},
suffix: () => {
return h(NIcon,
{
// color: isSelected(item) ? '#1abc9c' : '#bdc3c7'
size: 20
}, {
default: () => {
return h(CheckmarkDoneCircle, null)
}
}
)
}
})
})
)
}
<template>
<n-scrollbar style="max-height: 600px">
<!-- <mylist /> -->
<n-list hoverable clickable :show-divider="false">
<n-list-item v-for="item in options" @click="itemClick(item)" :class="padding">
<template #prefix v-if="item.imgSrc">
<n-avatar :size="22" :src="item.imgSrc" color="white" :bordered="true" />
</template>
{{ item.title ? item.title : empty }}
<template #suffix>
<n-icon :color="isSelected(item) ? '#1abc9c' : '#bdc3c7'" size="20">
<CheckmarkDoneCircle />
</n-icon>
</template>
</n-list-item>
</n-list>
</n-scrollbar>
<n-button-group style="width:100%" size="large" :class="btn_width">
<n-button secondary @click="contrary" :disabled="isAll || isEmpty" v-if="contrary_btn">
<template #icon>
<n-icon :color="isEmpty ? 'black' : '#00b894'" class="rotate"
:style="{ transform: 'rotate(' + target.contrary + 'deg)' }">
<RepeatSharp />
</n-icon>
</template>
反选
</n-button>
<n-button secondary @click="_submit" :style="'width:' + 1 / 3">
<template #icon>
<n-icon>
<SaveOutline />
</n-icon>
</template>
保存
</n-button>
<n-button secondary @click="clear" :disabled="isEmpty" :style="'width:' + 1 / 3">
<template #icon>
<n-icon class="rotate" :color="isEmpty ? 'black' : '#e74c3c'">
<TrashBinOutline />
</n-icon>
</template>
清空
</n-button>
</n-button-group>
</template>
<script lang="ts" setup>
import { computed, h, reactive, ref } from 'vue'
import { CheckmarkDoneCircle, TrashBinOutline, RepeatSharp, SaveOutline } from '@vicons/ionicons5'
import { NAvatar, NIcon, NList, NListItem, NText } from 'naive-ui';
const props = defineProps({
selected: { type: Array<string | boolean>, required: true },
options: { type: Array<menu>, required: true },
padding: { type: String, required: false, default: 'small' },
contrary_btn: { type: Boolean, required: false, default: true },
// 当 title 为空字符串时显示的文本
empty: { type: String, required: false, default: '(empty)' },
renderIcon: { type: Function, required: false },
})
// 临时数组,用于保存用户已选中但是还未点击提交按钮的勾选值
const tmp_selected = ref<Array<string | boolean>>([...props.selected])
let btn_width = 'two'
if (props.contrary_btn) {
btn_width = 'there'
}
// 从传入的选项菜单里提取所有 value
const all = props.options.map(item => item.value)
const emit = defineEmits(['update:modelValue']);
interface menu {
title: string
value: string | boolean
desc?: string
imgSrc?: string
}
const itemClick = (item: menu) => {
if (isSelected.value(item)) {
tmp_selected.value = tmp_selected.value.filter(o => o !== item.value)
} else {
tmp_selected.value.push(item.value)
}
}
const _submit = () => {
emit('update:modelValue', [...tmp_selected.value]);
}
const target = reactive({
contrary: 0
})
// 反选函数
const contrary = () => {
target.contrary += 180;
tmp_selected.value = all.filter(item => !tmp_selected.value.includes(item))
console.log(tmp_selected.value)
}
// 清空函数
const clear = () => {
target.contrary = 0;
tmp_selected.value = []
emit('update:modelValue', [])
}
const isSelected = computed(() => {
return (item: menu) => {
return tmp_selected.value.some(o => o === item.value);
};
});
const isAll = computed(() => {
return tmp_selected.value.length === props.options.length
})
const isEmpty = computed(() => {
return tmp_selected.value.length === 0
})
</script>
<style scoped>
.two button {
width: 50%;
}
.there button {
width: 33.33%;
}
.n-list.n-list--hoverable .n-list-item.small {
padding: 7px 20px;
}
.n-list.n-list--hoverable .n-list-item.medium {
padding: 10px 20px;
}
.n-list.n-list--hoverable .n-list-item.large {
padding: 15px 20px;
}
.n-avatar {
vertical-align: middle;
}
.select {
background-color: aquamarine;
}
.rotate {
transition: all 0.5s ease;
}
.rotated {
transform: rotate(180deg);
}
</style>
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.