feat(F006): 企业列表页专业UI升级 - 品牌渐变头像/等级彩色徽章/状态圆点标签(待审核脉冲)/标签前2+溢出/批量栏选中高亮/补审核状态筛选/清理死代码详情弹窗

This commit is contained in:
zou 2026-08-18 14:07:07 +08:00
parent 8be7ca9dc2
commit 508c84e571

View File

@ -1,18 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
/** /**
* W18.2 企业列表 — 按项目 UI 规范 (PageHeader/SearchBar/DataTable) + 老 vue 全功能 * W18.2 + 2026-08-18 企业列表 — 专业 UI 升级版 (按项目 UI 规范 + 老 vue 全功能)
* *
* 后端端点: ApiEnterpriseController @ /user/enterprise * 后端端点: ApiEnterpriseController @ /user/enterprise
* GET /user/enterprise/page 分页 (EnterpriseDTO 全字段, 支持 enterpriseName/industry/supplierLevel/salesperson/supplierLabel/status/homeShow 过滤) * GET /user/enterprise/page 分页 (EnterpriseDTO 全字段, 支持 enterpriseName/industry/supplierLevel/salesperson/supplierLabel/status/homeShow 过滤)
* GET /user/enterprise/checkStatus?id=&status=&failReason= 审核(2通过/3驳回, 支持逗号批量)
* POST /user/enterprise/enabled?id= 禁用/启用切换 (后端 @RequestParam 从 query 读)
* POST /user/enterprise/showEnterprise?id= 显示/隐藏切换 (query)
* GET /user/enterprise/homeShow?id=&homeShow= 首页推荐切换 (query)
* *
* EnterpriseDTO 真实字段 (来源: devbox 编译反编译确认, 2026-07-15): * EnterpriseDTO 真实字段 (来源: devbox 编译反编译确认, 2026-07-15):
* id/enterpriseName/enterpriseLogo/administrator/administratorMobile/industry/supplierLevel * id/enterpriseName/enterpriseLogo/administrator/administratorMobile/industry/supplierLevel
* supplierLabel/salesperson/salespersonMobile/status/createDate/enabled/showFlag/homeShow * supplierLabel/salesperson/salespersonMobile/status/createDate/enabled/showFlag/homeShow
* linkMan/linkMobile/legalMan/email/enterpriseAddress/mainProducts/registerTime/orgNo/failReason * linkMan/linkMobile/legalMan/email/enterpriseAddress/mainProducts/registerTime/orgNo/failReason
*/ */
import { onMounted, ref, reactive } from 'vue' import { onMounted, ref, reactive, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { User, Clock, Upload, Download, MoreFilled } from '@element-plus/icons-vue'
import { httpGet, httpPost, httpDelete } from '@/utils/request' import { httpGet, httpPost, httpDelete } from '@/utils/request'
import DataTable from '@/components/DataTable/DataTable.vue' import DataTable from '@/components/DataTable/DataTable.vue'
import PageHeader from '@/components/PageHeader/PageHeader.vue' import PageHeader from '@/components/PageHeader/PageHeader.vue'
@ -58,8 +63,7 @@ const loading = ref(false)
const page = ref(1) const page = ref(1)
const limit = ref(10) const limit = ref(10)
const selectedIds = ref<string[]>([]) const selectedIds = ref<string[]>([])
const detailVisible = ref(false) const selectedRows = ref<Enterprise[]>([])
const detailRow = ref<Enterprise | null>(null)
// ========== 单行审核 ========== // ========== 单行审核 ==========
const auditVisible = ref(false) const auditVisible = ref(false)
@ -82,19 +86,32 @@ const filters = reactive({
}) })
// status 映射 (老 vue: 1=待审核,2=已认证,3=已驳回) // status 映射 (老 vue: 1=待审核,2=已认证,3=已驳回)
const STATUS_MAP: Record<string, string> = { const STATUS_META: Record<string, { label: string; color: string; bg: string }> = {
'1': '待审核', '2': '已认证', '3': '已驳回' '1': { label: '待审核', color: '#b45309', bg: '#fef3c7' },
} '2': { label: '已认证', color: '#15803d', bg: '#dcfce7' },
const STATUS_CLASS: Record<string, string> = { '3': { label: '已驳回', color: '#b91c1c', bg: '#fee2e2' }
'1': 'rr-tag-warn', '2': 'rr-tag-ok', '3': 'rr-tag-err'
} }
// supplierLevel 映射 (老 vue: 1=优质,2=钻石,3=金牌,4=银牌,5=铜牌) // supplierLevel 映射 (老 vue: 1=优质,2=钻石,3=金牌,4=银牌,5=铜牌)
const LEVEL_MAP: Record<number, string> = { const LEVEL_META: Record<number, { label: string; color: string; bg: string }> = {
1: '优质供应商', 2: '钻石供应商', 3: '金牌供应商', 1: { label: '优质供应商', color: '#92400e', bg: '#fef3c7' },
4: '银牌供应商', 5: '铜牌供应商' 2: { label: '钻石供应商', color: '#6d28d9', bg: '#ede9fe' },
3: { label: '金牌供应商', color: '#c2410c', bg: '#ffedd5' },
4: { label: '银牌供应商', color: '#475569', bg: '#f1f5f9' },
5: { label: '铜牌供应商', color: '#a16207', bg: '#fefce8' }
} }
// 头像品牌渐变色组 (企业无 logo 时交替使用)
const AVATAR_GRADIENTS = [
'linear-gradient(135deg, #0b5394, #6fb1e8)',
'linear-gradient(135deg, #2563eb, #93c5fd)',
'linear-gradient(135deg, #0e7490, #67e8f9)',
'linear-gradient(135deg, #7c3aed, #c4b5fd)',
'linear-gradient(135deg, #be123c, #fda4af)'
]
const selectedCount = computed(() => selectedIds.value.length)
function fmtDate(ts?: string | null) { function fmtDate(ts?: string | null) {
if (!ts) return '--' if (!ts) return '--'
const d = new Date(ts) const d = new Date(ts)
@ -110,6 +127,16 @@ function maskMobile(mobile?: string | null) {
/** 企业名首字 (Avatar 用) */ /** 企业名首字 (Avatar 用) */
function initial(name: string) { return name ? name.charAt(0) : '企' } function initial(name: string) { return name ? name.charAt(0) : '企' }
/** 头像背景 (有 logo 用图片, 否则渐变) */
function avatarStyle(row: Enterprise, index: number) {
return { background: AVATAR_GRADIENTS[index % AVATAR_GRADIENTS.length] }
}
/** 标签前 2 个 + 剩余计数 */
function shownTags(row: Enterprise): { tags: string[]; rest: number } {
const raw = row.supplierLabel || ''
const tags = String(raw).split(/[,,、;;\s]+/).map((s: string) => s.trim()).filter(Boolean)
return { tags: tags.slice(0, 2), rest: Math.max(0, tags.length - 2) }
}
async function load() { async function load() {
loading.value = true loading.value = true
@ -168,7 +195,7 @@ function onReset() {
} }
function onPageChange(p: number) { page.value = p; load() } function onPageChange(p: number) { page.value = p; load() }
function onLimitChange(l: number) { limit.value = l; page.value = 1; load() } function onLimitChange(l: number) { limit.value = l; page.value = 1; load() }
function onSelectionChange(rows: any[]) { selectedIds.value = rows.map((r: any) => r.id) } function onSelectionChange(rows: any[]) { selectedIds.value = rows.map((r: any) => r.id); selectedRows.value = rows }
// ========== 审核交互 (后端 GET /user/enterprise/checkStatus?id=&status=&failReason=, 支持逗号批量) ========== // ========== 审核交互 (后端 GET /user/enterprise/checkStatus?id=&status=&failReason=, 支持逗号批量) ==========
function onAudit(row: Enterprise) { function onAudit(row: Enterprise) {
@ -246,7 +273,6 @@ async function deleteHandle() {
function exportSupplierDetail() { ElMessage.info('导出待实现') } function exportSupplierDetail() { ElMessage.info('导出待实现') }
// ========== 单行操作 ========== // ========== 单行操作 ==========
function showDetail(row: Enterprise) { detailRow.value = row; detailVisible.value = true }
function goDetail(row: Enterprise) { router.push(`/user/enterprise/detail/${row.id}`) } function goDetail(row: Enterprise) { router.push(`/user/enterprise/detail/${row.id}`) }
function changeAdminHandle(_row: Enterprise) { ElMessage.info('更换管理员待实现') } function changeAdminHandle(_row: Enterprise) { ElMessage.info('更换管理员待实现') }
@ -291,7 +317,7 @@ onMounted(() => load())
:fields="[ :fields="[
{ key: 'enterpriseName', label: '企业名称', type: 'text', placeholder: '企业名称' }, { key: 'enterpriseName', label: '企业名称', type: 'text', placeholder: '企业名称' },
{ key: 'industry', label: '行业类型', type: 'text', placeholder: '行业类型' }, { key: 'industry', label: '行业类型', type: 'text', placeholder: '行业类型' },
{ key: 'supplierLevel', label: '供应商等级', type: 'select', placeholder: '供应商等级', { key: 'supplierLevel', label: '供应商等级', type: 'select', placeholder: '全部等级',
options: [ options: [
{ value: 1, label: '优质供应商' }, { value: 1, label: '优质供应商' },
{ value: 2, label: '钻石供应商' }, { value: 2, label: '钻石供应商' },
@ -299,6 +325,13 @@ onMounted(() => load())
{ value: 4, label: '银牌供应商' }, { value: 4, label: '银牌供应商' },
{ value: 5, label: '铜牌供应商' } { value: 5, label: '铜牌供应商' }
] ]
},
{ key: 'status', label: '审核状态', type: 'select', placeholder: '全部状态',
options: [
{ value: 1, label: '待审核' },
{ value: 2, label: '已认证' },
{ value: 3, label: '已驳回' }
]
} }
]" ]"
@search="onSearch" @search="onSearch"
@ -306,26 +339,32 @@ onMounted(() => load())
/> />
<div class="rr-list-card"> <div class="rr-list-card">
<!-- 批量操作栏 --> <!-- 批量操作栏 (选中时高亮) -->
<div class="rr-batch-bar"> <div class="rr-batch-bar" :class="{ 'has-selection': selectedCount > 0 }">
<el-button size="small" type="success" @click="importHandle">导入</el-button> <span v-if="selectedCount > 0" class="batch-count">
<el-button size="small" type="danger" @click="openBatchAudit">批量审核</el-button> 已选 <strong>{{ selectedCount }}</strong> 家企业
<el-button size="small" @click="batchLabelHandle">批量设置标签</el-button> </span>
<el-button size="small" type="warning" @click="batchAssignSalesperson">批量分配业务员</el-button> <span v-else class="batch-hint">勾选企业后可执行批量操作</span>
<el-button size="small" type="danger" @click="deleteHandle">删除</el-button> <span class="batch-actions">
<el-button size="small" type="primary" @click="exportSupplierDetail">导出供应商信息明细</el-button> <el-button size="small" type="primary" @click="openBatchAudit">批量审核</el-button>
<el-button size="small" @click="batchLabelHandle">批量设置标签</el-button>
<el-button size="small" type="warning" plain @click="batchAssignSalesperson">批量分配业务员</el-button>
<el-button size="small" @click="importHandle"><el-icon style="margin-right:4px"><Upload /></el-icon>导入</el-button>
<el-button size="small" @click="exportSupplierDetail"><el-icon style="margin-right:4px"><Download /></el-icon>导出明细</el-button>
<el-button size="small" type="danger" plain @click="deleteHandle">删除</el-button>
</span>
</div> </div>
<DataTable <DataTable
:columns="[ :columns="[
{ type: 'selection', width: 48, label: '' }, { type: 'selection', width: 48, label: '' },
{ label: '企业名称', minWidth: 220, slot: 'enterpriseName' }, { label: '企业名称', minWidth: 250, slot: 'enterpriseName' },
{ label: '企业管理员/手机号', minWidth: 200, slot: 'adminInfo' }, { label: '企业管理员', minWidth: 170, slot: 'adminInfo' },
{ label: '行业类型/供应商等级', minWidth: 200, slot: 'levelInfo' }, { label: '行业 / 供应商等级', minWidth: 190, slot: 'levelInfo' },
{ prop: 'supplierLabel', label: '标签', width: 120, align: 'center' }, { label: '标签', minWidth: 140, slot: 'supplierLabel' },
{ label: '业务员', minWidth: 160, slot: 'salespersonInfo' }, { label: '业务员', minWidth: 150, slot: 'salespersonInfo' },
{ label: '状态', width: 100, slot: 'status' }, { label: '状态', width: 100, slot: 'status', align: 'center' },
{ label: '入驻时间', width: 110, slot: 'createDate' }, { label: '入驻时间', width: 120, slot: 'createDate' },
{ label: '操作', width: 200, slot: 'action', fixed: 'right', align: 'center' } { label: '操作', width: 200, slot: 'action', fixed: 'right', align: 'center' }
]" ]"
:data="list" :data="list"
@ -341,71 +380,91 @@ onMounted(() => load())
@limit-change="onLimitChange" @limit-change="onLimitChange"
@selection-change="onSelectionChange" @selection-change="onSelectionChange"
> >
<!-- 企业名称(带头像) --> <!-- 企业名称 (头像 + 名称 + 法人) -->
<template #enterpriseName="{ row, index }"> <template #enterpriseName="{ row, index }">
<div style="display:flex;align-items:center;gap:8px"> <div class="ent-cell">
<span :style="{ display:'inline-flex',alignItems:'center',justifyContent:'center', <span class="ent-avatar" :style="avatarStyle(row, index)">
width:'32px',height:'32px',borderRadius:'50%',flexShrink:0,fontSize:'14px',fontWeight:500, <el-image v-if="row.enterpriseLogo" :src="row.enterpriseLogo" fit="cover" class="ent-avatar-img" />
background:index%2===0?'#fce4ec':'#e3f2fd',color:index%2===0?'#c62828':'#1565c0' <template v-else>{{ initial(row.enterpriseName) }}</template>
}">{{ initial(row.enterpriseName) }}</span> </span>
<span style="font-weight:500">{{ row.enterpriseName }}</span> <span class="ent-text">
<span class="ent-name">{{ row.enterpriseName }}</span>
<span class="ent-sub" v-if="row.legalMan && row.legalMan !== '--'">法人:{{ row.legalMan }}</span>
</span>
</div> </div>
</template> </template>
<!-- 企业管理员/手机号(两行) --> <!-- 企业管理员/手机号 (两行) -->
<template #adminInfo="{ row }"> <template #adminInfo="{ row }">
<div style="display:flex;flex-direction:column;gap:2px"> <div class="two-line">
<span>{{ row.administrator }}</span> <span class="line-main">{{ row.administrator }}</span>
<span style="font-size:12px;color:var(--ink-3)">{{ maskMobile(row.administratorMobile) }}</span> <span class="line-sub"><el-icon><User /></el-icon>{{ maskMobile(row.administratorMobile) }}</span>
</div> </div>
</template> </template>
<!-- 行业类型/供应商等级(两行) --> <!-- 行业/供应商等级 (两行 + 等级徽章) -->
<template #levelInfo="{ row }"> <template #levelInfo="{ row }">
<div style="display:flex;flex-direction:column;gap:2px"> <div class="two-line">
<span>{{ row.industry || '-' }}</span> <span class="line-main">{{ row.industry || '-' }}</span>
<span style="font-size:12px;color:var(--ink-3)">{{ LEVEL_MAP[row.supplierLevel as number] || '-' }}</span> <span v-if="LEVEL_META[row.supplierLevel as number]" class="level-chip"
:style="{ color: LEVEL_META[row.supplierLevel as number].color, background: LEVEL_META[row.supplierLevel as number].bg }">
{{ LEVEL_META[row.supplierLevel as number].label }}
</span>
<span v-else class="line-sub">未评级</span>
</div> </div>
</template> </template>
<!-- 标签 (前 2 个 + 溢出) -->
<template #supplierLabel="{ row }">
<div class="label-tags" v-if="row.supplierLabel">
<span v-for="(t, i) in shownTags(row).tags" :key="i" class="label-tag">{{ t }}</span>
<span v-if="shownTags(row).rest > 0" class="label-more">+{{ shownTags(row).rest }}</span>
</div>
<span v-else class="rr-muted">--</span>
</template>
<!-- 业务员 --> <!-- 业务员 -->
<template #salespersonInfo="{ row }"> <template #salespersonInfo="{ row }">
<div v-if="row.salesperson" style="font-size:12px;color:var(--ink-3)"> <div v-if="row.salesperson" class="two-line">
{{ row.salesperson }}({{ maskMobile(row.salespersonMobile) }}) <span class="line-main">{{ row.salesperson }}</span>
<span class="line-sub">{{ maskMobile(row.salespersonMobile) }}</span>
</div> </div>
<div v-else style="font-size:12px;color:var(--ink-3);font-style:italic">暂未分配</div> <span v-else class="unassigned">待分配</span>
</template> </template>
<!-- 状态标签 --> <!-- 状态标签 (圆点 + 脉冲待审核) -->
<template #status="{ row }"> <template #status="{ row }">
<span v-if="STATUS_MAP[String(row.status)]" :class="['rr-status-tag', STATUS_CLASS[String(row.status)] || '']"> <span v-if="STATUS_META[String(row.status)]" class="status-tag" :class="{ pulsing: row.status === 1 }"
{{ STATUS_MAP[String(row.status)] }} :style="{ color: STATUS_META[String(row.status)].color, background: STATUS_META[String(row.status)].bg }">
<span class="status-dot"></span>{{ STATUS_META[String(row.status)].label }}
</span> </span>
<span v-else class="rr-muted">--</span> <span v-else class="rr-muted">--</span>
</template> </template>
<!-- 入驻时间 --> <!-- 入驻时间 -->
<template #createDate="{ row }"> <template #createDate="{ row }">
<span style="font-family:'SF Mono',Menlo,monospace;font-size:12px">{{ fmtDate(row.createDate) }}</span> <span class="date-cell"><el-icon><Clock /></el-icon>{{ fmtDate(row.createDate) }}</span>
</template> </template>
<!-- 操作列 --> <!-- 操作列 -->
<template #action="{ row }"> <template #action="{ row }">
<div style="display:flex;align-items:center;gap:2px"> <div class="action-cell">
<el-button type="primary" size="small" text @click="goDetail(row)">详情</el-button> <el-button type="primary" size="small" text @click="goDetail(row)">详情</el-button>
<el-button type="primary" size="small" text @click="changeAdminHandle(row)">更换管理员</el-button> <el-button size="small" text @click="changeAdminHandle(row)">更换管理员</el-button>
<el-dropdown trigger="click" placement="bottom-end" @command="(cmd:string) => handleMore(cmd, row)"> <el-dropdown trigger="click" placement="bottom-end" @command="(cmd:string) => handleMore(cmd, row)">
<el-button type="primary" size="small" text><el-icon><MoreFilled /></el-icon></el-button> <el-button size="small" text><el-icon><MoreFilled /></el-icon></el-button>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item v-if="row.status===1" command="audit">审核</el-dropdown-item> <el-dropdown-item v-if="row.status===1" command="audit">
<span class="dd-audit">审核</span>
</el-dropdown-item>
<el-dropdown-item command="label">标签</el-dropdown-item> <el-dropdown-item command="label">标签</el-dropdown-item>
<el-dropdown-item command="rating">评级</el-dropdown-item> <el-dropdown-item command="rating">评级</el-dropdown-item>
<el-dropdown-item command="enabled">{{ row.enabled===1?'启用':'禁用' }}</el-dropdown-item> <el-dropdown-item divided command="enabled">{{ row.enabled===1?'启用':'禁用' }}</el-dropdown-item>
<el-dropdown-item command="showFlag">{{ row.showFlag===1?'显示企业':'隐藏企业' }}</el-dropdown-item> <el-dropdown-item command="showFlag">{{ row.showFlag===1?'显示企业':'隐藏企业' }}</el-dropdown-item>
<el-dropdown-item command="homeShow">{{ row.homeShow===1?'取消推荐':'推荐显示' }}</el-dropdown-item> <el-dropdown-item command="homeShow">{{ row.homeShow===1?'取消推荐':'推荐显示' }}</el-dropdown-item>
<el-dropdown-item command="assign">分配业务员</el-dropdown-item> <el-dropdown-item command="assign">分配业务员</el-dropdown-item>
<el-dropdown-item command="deleteRow">删除</el-dropdown-item> <el-dropdown-item divided command="deleteRow" class="dd-danger">删除</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
@ -414,34 +473,6 @@ onMounted(() => load())
</DataTable> </DataTable>
</div> </div>
<!-- 详情弹窗 (EnterpriseDTO 全字段) -->
<el-dialog v-model="detailVisible" title="企业详情" width="700px" :close-on-click-modal="false" destroy-on-close>
<template v-if="detailRow">
<el-descriptions :column="2" border size="small">
<el-descriptions-item label="企业名称">{{ detailRow.enterpriseName }}</el-descriptions-item>
<el-descriptions-item label="管理员">{{ detailRow.administrator }}</el-descriptions-item>
<el-descriptions-item label="管理员手机">{{ maskMobile(detailRow.administratorMobile) }}</el-descriptions-item>
<el-descriptions-item label="行业">{{ detailRow.industry || '-' }}</el-descriptions-item>
<el-descriptions-item label="供应商等级">{{ LEVEL_MAP[detailRow.supplierLevel as number] || '-' }}</el-descriptions-item>
<el-descriptions-item label="标签">{{ detailRow.supplierLabel || '-' }}</el-descriptions-item>
<el-descriptions-item label="业务员">{{ detailRow.salesperson || '暂未分配' }}</el-descriptions-item>
<el-descriptions-item label="状态">{{ STATUS_MAP[String(detailRow.status)] || '-' }}</el-descriptions-item>
<el-descriptions-item v-if="detailRow.status===3" label="驳回原因" :span="2">{{ detailRow.failReason || '-' }}</el-descriptions-item>
<el-descriptions-item label="入驻时间">{{ fmtDate(detailRow.createDate) }}</el-descriptions-item>
<el-descriptions-item label="法人代表">{{ detailRow.legalMan }}</el-descriptions-item>
<el-descriptions-item label="联系电话">{{ maskMobile(detailRow.linkMobile) }}</el-descriptions-item>
<el-descriptions-item label="邮箱">{{ detailRow.email || '-' }}</el-descriptions-item>
<el-descriptions-item label="地址" :span="2">{{ detailRow.enterpriseAddress || '-' }}</el-descriptions-item>
<el-descriptions-item label="主营产品" :span="2">{{ detailRow.mainProducts || '-' }}</el-descriptions-item>
<el-descriptions-item label="工商号">{{ detailRow.orgNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="注册时间">{{ fmtDate(detailRow.registerTime) }}</el-descriptions-item>
</el-descriptions>
</template>
<template #footer>
<el-button @click="detailVisible=false">关闭</el-button>
</template>
</el-dialog>
<!-- 单行审核弹窗 --> <!-- 单行审核弹窗 -->
<el-dialog v-model="auditVisible" title="企业认证审核" width="480px" :close-on-click-modal="false" destroy-on-close> <el-dialog v-model="auditVisible" title="企业认证审核" width="480px" :close-on-click-modal="false" destroy-on-close>
<div style="background:var(--bg,#f6f8fb);border:1px solid var(--border);border-radius:8px;padding:10px 14px;font-size:13px;color:var(--ink-2);margin-bottom:18px"> <div style="background:var(--bg,#f6f8fb);border:1px solid var(--border);border-radius:8px;padding:10px 14px;font-size:13px;color:var(--ink-2);margin-bottom:18px">
@ -497,15 +528,82 @@ onMounted(() => load())
.rr-list-card { .rr-list-card {
background: var(--surface); border-radius: 12px; overflow: hidden; background: var(--surface); border-radius: 12px; overflow: hidden;
} }
/* ===== 批量操作栏 ===== */
.rr-batch-bar { .rr-batch-bar {
display: flex; gap: 8px; flex-wrap: wrap; padding: 12px 16px; display: flex; align-items: center; gap: 12px; padding: 12px 16px;
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
transition: background .2s;
} }
.rr-status-tag { .rr-batch-bar.has-selection { background: var(--primary-soft, #e0ecff); }
display: inline-block; padding: 2px 10px; border-radius: 4px; font-size: 12px; font-weight: 500; .batch-count { font-size: 13px; color: var(--ink-2); }
.batch-count strong { color: var(--primary); font-size: 15px; }
.batch-hint { font-size: 12px; color: var(--ink-3); }
.batch-actions { margin-left: auto; display: flex; gap: 4px; flex-wrap: wrap; }
/* ===== 企业名称 ===== */
.ent-cell { display: flex; align-items: center; gap: 10px; }
.ent-avatar {
flex: 0 0 38px; width: 38px; height: 38px; border-radius: 10px;
display: flex; align-items: center; justify-content: center;
color: #fff; font-size: 16px; font-weight: 700; flex-shrink: 0;
} }
.rr-tag-warn { background: #fff3e0; color: #e65100; } .ent-avatar-img { width: 100%; height: 100%; border-radius: 10px; }
.rr-tag-ok { background: #e8f5e9; color: #2e7d32; } .ent-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rr-tag-err { background: #fce4ec; color: #c62828; } .ent-name { font-size: 14px; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ent-sub { font-size: 12px; color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* ===== 两行单元格 ===== */
.two-line { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.line-main { font-size: 13px; color: var(--ink); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.line-sub { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--ink-3); }
/* ===== 等级徽章 ===== */
.level-chip {
display: inline-flex; align-items: center; width: fit-content;
font-size: 11px; font-weight: 600; padding: 2px 10px; border-radius: 20px;
white-space: nowrap;
}
/* ===== 标签 ===== */
.label-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.label-tag {
font-size: 11px; color: var(--primary); background: var(--primary-soft, #e0ecff);
padding: 2px 10px; border-radius: 6px; white-space: nowrap;
}
.label-more { font-size: 11px; color: var(--ink-3); align-self: center; }
/* ===== 业务员 ===== */
.unassigned {
display: inline-flex; font-size: 11px; color: var(--ink-3);
background: var(--bg, #f6f8fb); border: 1px dashed var(--border);
padding: 2px 10px; border-radius: 20px;
}
/* ===== 状态标签 ===== */
.status-tag {
display: inline-flex; align-items: center; gap: 6px;
font-size: 12px; font-weight: 500; padding: 3px 12px; border-radius: 20px;
white-space: nowrap;
}
.status-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.status-tag.pulsing .status-dot { animation: pulse-dot 1.6s ease-in-out infinite; }
@keyframes pulse-dot {
0%, 100% { box-shadow: 0 0 0 0 rgba(180, 83, 9, .4); }
50% { box-shadow: 0 0 0 5px rgba(180, 83, 9, 0); }
}
/* ===== 入驻时间 ===== */
.date-cell { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--ink-2); font-family: 'SF Mono', Menlo, Consolas, monospace; }
/* ===== 操作列 ===== */
.action-cell { display: flex; align-items: center; gap: 2px; }
.dd-audit { color: #b45309; font-weight: 600; }
.dd-danger { color: #dc2626; }
.rr-muted { color: var(--ink-3); font-size: 12px; } .rr-muted { color: var(--ink-3); font-size: 12px; }
@media (max-width: 1200px) {
.batch-actions { margin-left: 0; }
}
</style> </style>