fix(F006): rating page UI fixes (menu highlight strict match / no stats / inline search labels / real API + industry column)
This commit is contained in:
parent
fd0d43be7e
commit
2cdf5b2ee5
@ -57,7 +57,10 @@ function logout() {
|
|||||||
router.push('/login')
|
router.push('/login')
|
||||||
}
|
}
|
||||||
function isActive(path: string): boolean {
|
function isActive(path: string): boolean {
|
||||||
return path === '/' ? route.path === '/' : route.path.startsWith(path)
|
if (path === '/') return route.path === '/'
|
||||||
|
// 严格匹配: route.path 完全等于 path, 或 route.path 是 path 的子路径 (path 后必须是 '/')
|
||||||
|
// 避免 '/user/enterprise/rating' 同时匹配 '/user/enterprise' (前缀误激活)
|
||||||
|
return route.path === path || route.path.startsWith(path + '/')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,108 +1,122 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
/**
|
/**
|
||||||
* F006 供应商评级管理 - W16 + UI 审计补完 (2026-08-22)
|
* F006 供应商评级管理 - 2026-08-22 UI 整改
|
||||||
*
|
*
|
||||||
* 端点: 4 URL 探测 (enterpriserating/page, /list, /search, /queryPage); 404 → FALLBACK seed
|
* 后端端点: GET /user/enterprise/getRatingPage (RatingVO 返回)
|
||||||
|
* 参考老后台: /vue/statistics/index-town#/user/enterprise/enterprise-rating
|
||||||
|
*
|
||||||
|
* 老后台搜索字段: 企业id / 企业名称 / 手机号 / 行业类型 / 账号状态 / 供应商等级
|
||||||
|
* 老后台列: 供应商id / 供应商信息 / 等级 / 客户评价 / 履约评价 / 奖惩 / 最终 / 评级时间
|
||||||
*/
|
*/
|
||||||
import { ref, reactive, onMounted, computed } from 'vue'
|
import { ref, reactive, onMounted, computed } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { httpGet } from '@/utils/request'
|
import { httpGet } from '@/utils/request'
|
||||||
import PageHeader from '@/components/PageHeader/PageHeader.vue'
|
import PageHeader from '@/components/PageHeader/PageHeader.vue'
|
||||||
import SearchBar, { type SearchField } from '@/components/SearchBar/SearchBar.vue'
|
|
||||||
import DataTable from '@/components/DataTable/DataTable.vue'
|
import DataTable from '@/components/DataTable/DataTable.vue'
|
||||||
import TableActions from '@/components/TableActions/TableActions.vue'
|
import TableActions from '@/components/TableActions/TableActions.vue'
|
||||||
|
|
||||||
interface RatingRow { id:string; enterpriseId:string; enterpriseName:string; supplierLevel:string; clientScore:number; performanceScore:number; awardScore:number; finalScore:number; deadline:string }
|
interface RatingRow {
|
||||||
|
id: string
|
||||||
|
enterpriseName: string
|
||||||
|
industry?: string
|
||||||
|
supplierLevel: string
|
||||||
|
clientScore: number
|
||||||
|
performanceScore: number
|
||||||
|
awardScore: number
|
||||||
|
finalScore: number
|
||||||
|
deadline: string
|
||||||
|
}
|
||||||
|
|
||||||
const ENDPOINTS = [
|
const ENDPOINTS = [
|
||||||
'/user/performancescorerecord/page',
|
'/user/enterprise/getRatingPage',
|
||||||
'/user/performancescorerecord/list'
|
'/user/enterprise/ratingExport'
|
||||||
]
|
]
|
||||||
|
|
||||||
const FALLBACK: RatingRow[] = [
|
const FALLBACK: RatingRow[] = [
|
||||||
{ id:'R001', enterpriseId:'s20015', enterpriseName:'坚美建筑材料有限公司', supplierLevel:'A 战略级', clientScore:96, performanceScore:92, awardScore:5, finalScore:193, deadline:'2026-06-30 23:59:59' },
|
{ id:'R001', enterpriseName:'坚美建筑材料有限公司', industry:'建材', supplierLevel:'A 战略级', clientScore:96, performanceScore:92, awardScore:5, finalScore:193, deadline:'2026-06-30 23:59:59' },
|
||||||
{ id:'R002', enterpriseId:'s20016', enterpriseName:'华建门窗厂', supplierLevel:'B 核心级', clientScore:88, performanceScore:85, awardScore:3, finalScore:176, deadline:'2026-06-30 23:59:59' },
|
{ id:'R002', enterpriseName:'华建门窗厂', industry:'门窗', supplierLevel:'B 核心级', clientScore:88, performanceScore:85, awardScore:3, finalScore:176, deadline:'2026-06-30 23:59:59' },
|
||||||
{ id:'R003', enterpriseId:'s20017', enterpriseName:'融鑫管业', supplierLevel:'B 核心级', clientScore:84, performanceScore:88, awardScore:0, finalScore:172, deadline:'2026-06-30 23:59:59' },
|
{ id:'R003', enterpriseName:'融鑫管业', industry:'管业', supplierLevel:'B 核心级', clientScore:84, performanceScore:88, awardScore:0, finalScore:172, deadline:'2026-06-30 23:59:59' }
|
||||||
{ id:'R004', enterpriseId:'s20018', enterpriseName:'东方五金机械', supplierLevel:'A 战略级', clientScore:92, performanceScore:90, awardScore:8, finalScore:190, deadline:'2026-06-30 23:59:59' },
|
|
||||||
{ id:'R005', enterpriseId:'s20019', enterpriseName:'美洁涂料', supplierLevel:'C 成长级', clientScore:78, performanceScore:76, awardScore:-2, finalScore:152, deadline:'2026-06-30 23:59:59' },
|
|
||||||
{ id:'R006', enterpriseId:'s20018', enterpriseName:'东方五金机械', supplierLevel:'B 核心级', clientScore:86, performanceScore:82, awardScore:2, finalScore:170, deadline:'2026-06-30 23:59:59' },
|
|
||||||
{ id:'R007', enterpriseId:'s20021', enterpriseName:'顺达物流', supplierLevel:'D 入门级', clientScore:65, performanceScore:68, awardScore:0, finalScore:133, deadline:'2026-06-30 23:59:59' },
|
|
||||||
{ id:'R008', enterpriseId:'s20022', enterpriseName:'众成厨具', supplierLevel:'C 成长级', clientScore:75, performanceScore:72, awardScore:-3, finalScore:144, deadline:'2026-06-30 23:59:59' },
|
|
||||||
{ id:'R009', enterpriseId:'s20015', enterpriseName:'坚美建筑材料有限公司', supplierLevel:'A 战略级', clientScore:94, performanceScore:91, awardScore:6, finalScore:191, deadline:'2026-06-30 23:59:59' },
|
|
||||||
{ id:'R010', enterpriseId:'s20024', enterpriseName:'华建门窗厂', supplierLevel:'A 战略级', clientScore:90, performanceScore:89, awardScore:4, finalScore:183, deadline:'2026-06-30 23:59:59' }
|
|
||||||
]
|
]
|
||||||
|
|
||||||
type Col = { prop?: string; label: string; minWidth?: number; width?: number; slot?: string; fixed?: 'right'; align?: 'center' | 'left' | 'right' }
|
type Col = { prop?: string; label: string; minWidth?: number; width?: number; slot?: string; fixed?: 'right'; align?: 'center' | 'left' | 'right' }
|
||||||
const columns: Col[] = [
|
const columns: Col[] = [
|
||||||
{ prop: 'enterpriseName', label: '供应商', minWidth: 180 },
|
{ prop: 'enterpriseName', label: '供应商', minWidth: 180 },
|
||||||
{ prop: 'supplierLevel', label: '等级', minWidth: 110, slot: 'level', align: 'center' },
|
{ prop: 'supplierLevel', label: '供应商等级', minWidth: 110, slot: 'level', align: 'center' },
|
||||||
{ prop: 'clientScore', label: '客户评价', minWidth: 110, align: 'center' },
|
{ prop: 'industry', label: '所属行业', minWidth: 120, align: 'center' },
|
||||||
{ prop: 'performanceScore', label: '履约评价', minWidth: 110, align: 'center' },
|
{ prop: 'clientScore', label: '客户评价', minWidth: 100, align: 'center' },
|
||||||
{ prop: 'awardScore', label: '奖惩分', minWidth: 100, slot: 'award', align: 'center' },
|
{ prop: 'performanceScore', label: '履约评价', minWidth: 100, align: 'center' },
|
||||||
|
{ prop: 'awardScore', label: '奖惩加减分', minWidth: 110, slot: 'award', align: 'center' },
|
||||||
{ prop: 'finalScore', label: '最终得分', minWidth: 110, slot: 'final', align: 'center' },
|
{ prop: 'finalScore', label: '最终得分', minWidth: 110, slot: 'final', align: 'center' },
|
||||||
{ prop: 'deadline', label: '评级时间', minWidth: 170 },
|
{ prop: 'deadline', label: '评级时间', minWidth: 170 },
|
||||||
{ label: '操作', width: 180, slot: 'actions', fixed: 'right', align: 'center' }
|
{ label: '操作', width: 200, slot: 'actions', fixed: 'right', align: 'center' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const list = ref<RatingRow[]>([]); const total = ref(0); const loading = ref(false); const page = ref(1); const limit = ref(10); const selection = ref<RatingRow[]>([])
|
const list = ref<RatingRow[]>([]); const total = ref(0); const loading = ref(false); const page = ref(1); const limit = ref(10); const selection = ref<RatingRow[]>([])
|
||||||
const filterKw = ref('')
|
const filterKw = ref('')
|
||||||
const dataSource = ref<'api'|'mock'|'loading'>('loading'); const apiError = ref<string>('')
|
const dataSource = ref<'api' | 'mock' | 'loading'>('loading'); const apiError = ref<string>('')
|
||||||
|
|
||||||
|
// inline 表单搜索字段 (label 在输入框左侧)
|
||||||
|
const searchFields = reactive({
|
||||||
|
enterpriseName: '',
|
||||||
|
linkMobile: '',
|
||||||
|
supplierLevel: ''
|
||||||
|
})
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
loading.value = true; dataSource.value = 'loading'
|
loading.value = true; dataSource.value = 'loading'
|
||||||
for (const ep of ENDPOINTS) {
|
for (const ep of ENDPOINTS) {
|
||||||
try {
|
try {
|
||||||
const data: any = await httpGet<any>(ep, { page: page.value, limit: limit.value })
|
const data: any = await httpGet<any>(ep, { page: page.value, limit: limit.value, ...searchFields })
|
||||||
const rows = (data?.list || []).map((it: any) => ({ ...it, raw: it }))
|
const rows = (data?.list || data?.data?.list || []).map((it: any) => ({
|
||||||
list.value = rows; total.value = Number(data?.total || rows.length); dataSource.value = 'api'; apiError.value = ''; loading.value = false; return
|
id: String(it.id ?? ''),
|
||||||
|
enterpriseName: it.enterpriseName || '-',
|
||||||
|
industry: it.industry || '-',
|
||||||
|
supplierLevel: it.supplierLevel || '-',
|
||||||
|
clientScore: Number(it.clientScore) || 0,
|
||||||
|
performanceScore: Number(it.performanceScore) || 0,
|
||||||
|
awardScore: Number(it.awardScore) || 0,
|
||||||
|
finalScore: Number(it.finalScore) || 0,
|
||||||
|
deadline: it.deadline || ''
|
||||||
|
}))
|
||||||
|
list.value = rows
|
||||||
|
total.value = Number(data?.total || data?.data?.total || rows.length)
|
||||||
|
dataSource.value = 'api'; apiError.value = ''
|
||||||
|
loading.value = false; return
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
list.value = [...FALLBACK]; total.value = FALLBACK.length; dataSource.value = 'mock'; apiError.value = '后端 API 暂未实装, 已加载前端示例数据'; loading.value = false
|
// 所有端点失败, fallback
|
||||||
|
list.value = FALLBACK
|
||||||
|
total.value = FALLBACK.length
|
||||||
|
dataSource.value = 'mock'
|
||||||
|
apiError.value = '后端 API 暂未实装, 已加载前端示例数据'
|
||||||
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const filtered = computed(() => {
|
const filtered = computed(() => {
|
||||||
const kw = filterKw.value.trim().toLowerCase(); let rs = list.value
|
const kw = filterKw.value.trim().toLowerCase()
|
||||||
if (kw) rs = rs.filter(r => String(r.enterpriseName).toLowerCase().includes(kw) || String(r.id).toLowerCase().includes(kw))
|
let rs = list.value
|
||||||
|
if (kw) rs = rs.filter(r => String(r.enterpriseName).toLowerCase().includes(kw))
|
||||||
return rs
|
return rs
|
||||||
})
|
})
|
||||||
|
|
||||||
// 统计 + 平均分 (用可选链防御 API 字段缺失)
|
|
||||||
const stats = computed(() => {
|
|
||||||
const rs = filtered.value
|
|
||||||
const aCount = rs.filter(r => r.supplierLevel?.startsWith('A') ?? false).length
|
|
||||||
const avgFinal = rs.length ? Math.round(rs.reduce((s, r) => s + (r.finalScore || 0), 0) / rs.length) : 0
|
|
||||||
const avgClient = rs.length ? Math.round(rs.reduce((s, r) => s + (r.clientScore || 0), 0) / rs.length) : 0
|
|
||||||
return [
|
|
||||||
{ label: '评级总数', value: rs.length, icon: 'Medal', color: 'var(--brand)' },
|
|
||||||
{ label: 'A 战略级', value: aCount, icon: 'Top', color: 'var(--green)' },
|
|
||||||
{ label: '平均最终得分', value: avgFinal, icon: 'Trophy', color: 'var(--amber)' },
|
|
||||||
{ label: '平均客户评价', value: avgClient, icon: 'Star', color: 'var(--purple)' }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const searchFields: SearchField[] = [
|
|
||||||
{ key: 'enterpriseName', label: '供应商', type: 'text', placeholder: '搜索供应商名称' },
|
|
||||||
{ key: 'id', label: '评级 ID', type: 'text', placeholder: '如 R001' }
|
|
||||||
]
|
|
||||||
const filters = reactive<Record<string, any>>({ id: '', enterpriseName: '' })
|
|
||||||
|
|
||||||
function onSearch() {
|
|
||||||
filterKw.value = `${filters.id || ''}|${filters.enterpriseName || ''}`
|
|
||||||
page.value = 1
|
|
||||||
}
|
|
||||||
function onReset() { filters.id = ''; filters.enterpriseName = ''; filterKw.value = ''; page.value = 1 }
|
|
||||||
|
|
||||||
function onSelectionChange(rows: RatingRow[]) { selection.value = rows }
|
function onSelectionChange(rows: RatingRow[]) { selection.value = rows }
|
||||||
|
function onSearch() { page.value = 1; load() }
|
||||||
|
function onReset() { searchFields.enterpriseName = ''; searchFields.linkMobile = ''; searchFields.supplierLevel = ''; filterKw.value = ''; page.value = 1 }
|
||||||
|
|
||||||
function onView(row: RatingRow) { ElMessage.info(`详情 ${row.id}`) }
|
function onView(row: RatingRow) { ElMessage.info(`详情 ${row.enterpriseName}`) }
|
||||||
function onEdit(row: RatingRow) { ElMessage.success('已发起调整 (前端示例)') }
|
function onEdit(row: RatingRow) { ElMessage.success('已发起调整 (前端示例)') }
|
||||||
async function onBatchExport() {
|
async function onBatchExport() {
|
||||||
await ElMessageBox.confirm(`确认导出选中的 ${selection.value.length} 条评级?`, '批量导出', { type: 'info' }).catch(() => {})
|
await ElMessageBox.confirm(`确认导出选中的 ${selection.value.length} 条评级?`, '批量导出', { type: 'info' }).catch(() => {})
|
||||||
ElMessage.success(`已发起批量导出 (前端示例, 共 ${selection.value.length} 条)`)
|
ElMessage.success(`已发起批量导出 (前端示例, 共 ${selection.value.length} 条)`)
|
||||||
}
|
}
|
||||||
function onExport() { ElMessage.success(`已发起导出 (前端示例, 共 ${filtered.value.length} 条)`) }
|
function onExport() { ElMessage.success(`已发起导出 (前端示例, 共 ${list.value.length} 条)`) }
|
||||||
|
|
||||||
function levelPill(r: RatingRow) {
|
function levelPill(r: RatingRow) {
|
||||||
if (r.supplierLevel?.startsWith('A')) return { label: r.supplierLevel, color: 'var(--st-ok-fg)', bg: 'var(--st-ok-bg)' }
|
const lvl = r.supplierLevel || ''
|
||||||
if (r.supplierLevel?.startsWith('B')) return { label: r.supplierLevel, color: 'var(--brand)', bg: 'var(--brand-soft)' }
|
if (lvl.startsWith('A')) return { color: 'var(--st-ok-fg)', bg: 'var(--st-ok-bg)' }
|
||||||
if (r.supplierLevel?.startsWith('C')) return { label: r.supplierLevel, color: 'var(--st-warn-fg)', bg: 'var(--st-warn-bg)' }
|
if (lvl.startsWith('B')) return { color: 'var(--brand)', bg: 'var(--brand-soft)' }
|
||||||
return { label: r.supplierLevel ?? '-', color: 'var(--st-err-fg)', bg: 'var(--st-err-bg)' }
|
if (lvl.startsWith('C')) return { color: 'var(--st-warn-fg)', bg: 'var(--st-warn-bg)' }
|
||||||
|
return { color: 'var(--st-err-fg)', bg: 'var(--st-err-bg)' }
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(load)
|
onMounted(load)
|
||||||
@ -114,21 +128,25 @@ onMounted(load)
|
|||||||
<el-button @click="onExport">导出明细</el-button>
|
<el-button @click="onExport">导出明细</el-button>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div class="stats-grid">
|
<!-- 内联搜索栏 (label 在输入框左侧) -->
|
||||||
<div v-for="(s, i) in stats" :key="i" class="stat-card">
|
|
||||||
<div class="stat-icon" :style="{ color: s.color, background: 'var(--brand-soft)' }">
|
|
||||||
<el-icon><component :is="s.icon" /></el-icon>
|
|
||||||
</div>
|
|
||||||
<div class="stat-info">
|
|
||||||
<div class="stat-label">{{ s.label }}</div>
|
|
||||||
<div class="stat-value">{{ s.value }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SearchBar v-model:filters="filters" :fields="searchFields" @search="onSearch" @reset="onReset" />
|
|
||||||
|
|
||||||
<div class="rr-list-card">
|
<div class="rr-list-card">
|
||||||
|
<el-form :inline="true" class="rr-search-inline">
|
||||||
|
<el-form-item label="供应商名称">
|
||||||
|
<el-input v-model="searchFields.enterpriseName" placeholder="请输入供应商名称" clearable style="width:200px" @keyup.enter="onSearch" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="手机号">
|
||||||
|
<el-input v-model="searchFields.linkMobile" placeholder="请输入手机号" clearable style="width:160px" @keyup.enter="onSearch" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="供应商等级">
|
||||||
|
<el-input v-model="searchFields.supplierLevel" placeholder="如 A 战略级" clearable style="width:140px" @keyup.enter="onSearch" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="onSearch">查询</el-button>
|
||||||
|
<el-button @click="onReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 批量操作栏 -->
|
||||||
<div class="batch-bar" :class="{ 'has-selection': selection.length > 0 }">
|
<div class="batch-bar" :class="{ 'has-selection': selection.length > 0 }">
|
||||||
<span v-if="selection.length > 0" class="batch-count">已选 <strong>{{ selection.length }}</strong> 条评级</span>
|
<span v-if="selection.length > 0" class="batch-count">已选 <strong>{{ selection.length }}</strong> 条评级</span>
|
||||||
<span v-else class="batch-hint">勾选后可批量导出</span>
|
<span v-else class="batch-hint">勾选后可批量导出</span>
|
||||||
@ -137,14 +155,25 @@ onMounted(load)
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DataTable :columns="columns" :data="filtered"
|
<DataTable
|
||||||
:borderless="true" :loading="loading" :page="page" :limit="limit" :total="total" row-key="id"
|
:columns="columns"
|
||||||
|
:data="list"
|
||||||
|
:loading="loading"
|
||||||
|
row-key="id"
|
||||||
|
borderless
|
||||||
|
empty-text="暂无评级"
|
||||||
|
:show-pagination="true"
|
||||||
|
:page="page"
|
||||||
|
:limit="limit"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
@selection-change="onSelectionChange"
|
@selection-change="onSelectionChange"
|
||||||
@page-change="(p: number) => { page = p; load() }"
|
@page-change="(p: number) => { page = p; load() }"
|
||||||
@limit-change="(l: number) => { limit = l; page = 1; load() }">
|
@limit-change="(l: number) => { limit = l; page = 1; load() }"
|
||||||
|
>
|
||||||
<template #level="{ row }">
|
<template #level="{ row }">
|
||||||
<span class="st-pill" :style="{ color: levelPill(row).color, background: levelPill(row).bg }">
|
<span class="st-pill" :style="{ color: levelPill(row).color, background: levelPill(row).bg }">
|
||||||
<span class="st-dot"></span><span class="truncate-text">{{ levelPill(row).label }}</span>
|
<span class="st-dot"></span><span class="truncate-text">{{ row.supplierLevel }}</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #award="{ row }">
|
<template #award="{ row }">
|
||||||
@ -171,24 +200,34 @@ onMounted(load)
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.rr-page { padding: 16px; }
|
.rr-page { padding: 16px; }
|
||||||
.stats-grid {
|
|
||||||
display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;
|
/* inline 搜索栏: label 在输入框左侧, 与查询按钮同行 */
|
||||||
margin-bottom: 16px;
|
.rr-search-inline {
|
||||||
|
padding: 12px 16px 0;
|
||||||
|
background: var(--bg-soft);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
:deep(.el-form-item__label) {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--ink-2);
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.stat-card {
|
|
||||||
background: var(--surface); border: 1px solid var(--border);
|
/* 长文本截断 (level pill 文本过长) */
|
||||||
border-radius: var(--radius-lg); padding: 16px 18px;
|
.truncate-text {
|
||||||
display: flex; align-items: center; gap: 12px;
|
display: inline-block;
|
||||||
transition: all .15s;
|
max-width: 90px;
|
||||||
&:hover { border-color: var(--border-strong); box-shadow: var(--shadow); }
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.stat-icon {
|
|
||||||
width: 40px; height: 40px; border-radius: 10px;
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
.stat-info { flex: 1; min-width: 0; }
|
|
||||||
.stat-label { font-size: 12px; color: var(--ink-3); margin-bottom: 4px; }
|
|
||||||
.stat-value { font-size: 22px; font-weight: 700; color: var(--ink); letter-spacing: -0.02em; }
|
|
||||||
.truncate-text { display: inline-block; max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user