magiccube-vue3
Version:
vue3-js版组件库
173 lines (153 loc) • 4.89 kB
JavaScript
import { ref, computed, nextTick, h, resolveDynamicComponent, watch } from 'vue'
const Tab = {
name: 'McTab',
props: {
modelValue: [String, Number],
data: Object,
options: Array,
labelName: {
type: String,
default: 'name'
},
valueName: {
type: String,
default: 'value'
},
type: String
},
emits: ['update:modelValue', 'change'],
setup(props, { emit }) {
const styleData = ref({})
const tabEl = ref(null)
const handleClick = (e, item) => {
const _val = item[props.valueName]
if (_val === props.modelValue || item.disabled) return
setSubscript(e.target)
model.value = _val
}
const setSubscript = (target) => {
styleData.value = {
left: target.offsetLeft + 'px'
}
}
const model = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
emit('change', value)
}
})
nextTick(() => {
if (tabEl.value) {
setSubscript(tabEl.value)
}
})
watch(() => model.value, async (nv, ov) => {
if(ov !== nv && props.type === 'box'){
await nextTick()
tabEl.value.scrollIntoView()
}
})
const innerNode = (item, idx) => {
const isActive = model.value === item[props.valueName];
const isDisabled = item.disabled;
return (
<span class={{
'mc-tab__inner': true,
'mc-tab-active': isActive,
'mc-tab-disabled': isDisabled
}}
ref={isActive ? tabEl : ''}
key={idx}
onClick={(e) => handleItemClick(e, item)}>
{
escapeHtml(item[props.labelName])
}
{/* icon图标,需要传入vue template格式的组件 */
renderTabIcon(item.tabIcon)
}
{
renderBadge(item.badge)
}
{
renderData(props.data, item.key)
}
</span>
);
}
// 处理点击事件
const handleItemClick = (e, item) => {
try {
handleClick(e, item);
} catch (error) {
console.error('Error in handleClick:', error);
}
};
// 渲染图标
const renderTabIcon = (tabIcon) => {
if (tabIcon && isTrustedComponent(tabIcon)) {
return (
<i class="mc-tab__inner--icon">
{h(resolveDynamicComponent(tabIcon), {})}
</i>
);
}
return null;
};
// 渲染徽章
const renderBadge = (badge) => {
if (badge && badge !== '0') {
return (
<i class="mc-tab__inner--badge">{ badge }</i>
);
}
return null;
};
// 渲染数据
const renderData = (data, key) => {
if (data && data[key]) {
return (
<i class="mc-tab__inner--count">{ data[key] }</i>
);
}
return null;
};
// 转义HTML
const escapeHtml = (unsafe) => {
return unsafe
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
};
// 验证组件是否可信
const isTrustedComponent = (component) => {
// 这里可以根据实际情况实现具体的验证逻辑
return true; // 示例中假设所有组件都是可信的
};
return () => (
<div class={{
'mc-tab': true,
'mc-tab-card': props.type === 'card',
'mc-tab-box': props.type === 'box'
}}>
{
props.options.map(innerNode)
}
<span class={{
'mc-tab__subscript': true,
'mc-tab-card': props.type === 'card',
'mc-tab-box': props.type === 'box'
}} style={styleData.value}></span>
</div>
)
}
}
Tab.install = (app) => {
app.component(Tab.name, Tab)
}
const McTab = Tab
export { McTab, McTab as default }