admin_new/src/components/ListTabs/ListTabs.vue

118 lines
2.3 KiB
Vue

<script setup lang="ts">
/**
* F006 通用 ListTabs 组件 (W17 设计规范整改)
*
* 状态 tab + 数字 badge, 完全按设计规范:
* 全部 128 待报价 23 进行中 45 已完成 52 已取消 8
*
* 用法:
* <ListTabs
* v-model="activeTab"
* :items="[
* { key: 'all', label: '全部', count: 128 },
* { key: 'pending', label: '待报价', count: 23 },
* ...
* ]"
* />
*/
export interface ListTabItem {
key: string
label: string
count?: number
}
interface Props {
modelValue: string
items: ListTabItem[]
}
const props = defineProps<Props>()
const emit = defineEmits<{
(e: 'update:modelValue', v: string): void
(e: 'change', v: string): void
}>()
function onClick(key: string) {
emit('update:modelValue', key)
emit('change', key)
}
</script>
<template>
<div class="rr-list-tabs">
<div
v-for="item in items"
:key="item.key"
class="rr-list-tab"
:class="{ 'is-active': props.modelValue === item.key }"
@click="onClick(item.key)"
>
<span class="rr-list-tab__label">{{ item.label }}</span>
<span v-if="item.count !== undefined" class="rr-list-tab__count">{{ item.count }}</span>
</div>
</div>
</template>
<style scoped lang="scss">
.rr-list-tabs {
display: flex;
align-items: center;
gap: 32px;
padding: 0 0 12px 0;
border-bottom: 1px solid var(--rr-border-color, #E2E8F0);
margin-bottom: 16px;
}
.rr-list-tab {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 0;
font-size: 14px;
color: var(--rr-text-secondary, #475569);
cursor: pointer;
position: relative;
user-select: none;
transition: color 0.2s;
&::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: -13px;
height: 2px;
background: transparent;
border-radius: 1px;
transition: background 0.2s;
}
&:hover {
color: var(--rr-color-primary, #2563EB);
}
&.is-active {
color: var(--rr-color-primary, #2563EB);
font-weight: 600;
&::after {
background: var(--rr-color-primary, #2563EB);
}
}
&__label {
line-height: 1;
}
&__count {
font-size: 12px;
color: var(--rr-text-tertiary, #94A3B8);
font-weight: 400;
line-height: 1;
}
&.is-active &__count {
color: var(--rr-color-primary, #2563EB);
}
}
</style>