Files
cz-web/src/views/project/cpns/ProjectTable.vue
wangzzpeng 7bc84bbe5e first
2026-09-24 10:10:43 +08:00

184 lines
3.9 KiB
Vue

<script setup lang="ts">
import { StatusTag } from '@/components'
import { formatBusinessType } from '@/utils/format'
import type { Project, ProjectStatus } from '@/types/business/project'
interface Props {
projects: Project[]
}
defineProps<Props>()
const emit = defineEmits<{
view: [id: string]
afterSales: [id: string]
}>()
const STATUS_VARIANTS: Record<ProjectStatus, 'primary' | 'success' | 'warning' | 'danger' | 'info'> = {
'待立项': 'info',
'进行中': 'primary',
'待验收': 'warning',
'已完成': 'success',
'售后中': 'warning',
'已取消': 'info',
}
</script>
<template>
<div class="project-table">
<table class="project-table__grid">
<thead>
<tr>
<th>序号</th>
<th>项目编号</th>
<th>项目名称</th>
<th>业务类型</th>
<th>进度</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in projects" :key="item.id">
<td>{{ index + 1 }}</td>
<td>{{ item.projectNo }}</td>
<td class="project-table__name" :title="item.projectName">
{{ item.projectName }}
</td>
<td>{{ formatBusinessType(item.businessType) }}</td>
<td>
<div class="project-table__progress">
<div class="project-table__track">
<div
class="project-table__bar"
:style="{ width: `${item.progress}%` }"
/>
</div>
<span class="project-table__percent">{{ item.progress }}%</span>
</div>
</td>
<td>
<StatusTag :label="item.status" :variant="STATUS_VARIANTS[item.status]" />
</td>
<td class="project-table__actions">
<button class="project-table__btn" @click="emit('view', item.id)">
详情
</button>
<button
v-if="item.status === '已完成'"
class="project-table__btn project-table__btn--accent"
@click="emit('afterSales', item.id)"
>
申请售后
</button>
</td>
</tr>
<tr v-if="projects.length === 0">
<td colspan="7" class="project-table__empty text-secondary">
暂无符合条件的项目
</td>
</tr>
</tbody>
</table>
</div>
</template>
<style lang="less" scoped>
.project-table {
overflow-x: auto;
border-radius: @radius-lg;
background: @white;
box-shadow: @shadow-base;
.scrollbar();
&__grid {
width: 100%;
border-collapse: collapse;
font-size: @font-size-base;
th,
td {
padding: @spacing-base @spacing-base;
text-align: left;
border-bottom: 1px solid @border-color;
white-space: nowrap;
}
th {
background: @bg-color-light;
color: @heading-color;
font-weight: 600;
}
td {
color: @text-color;
}
tbody tr:last-child td {
border-bottom: none;
}
tbody tr:hover td {
background: @bg-color-light;
}
}
&__name {
max-width: 220px;
color: @heading-color;
.ellipsis();
}
&__progress {
.flex(row, center);
gap: @spacing-sm;
min-width: 140px;
}
&__track {
flex: 1;
height: 6px;
border-radius: @radius-round;
background: @bg-color;
overflow: hidden;
}
&__bar {
height: 100%;
border-radius: @radius-round;
background: @primary-color;
transition: width 0.3s;
}
&__percent {
flex-shrink: 0;
font-size: @font-size-sm;
color: @text-color-secondary;
}
&__actions {
.flex(row, center);
gap: @spacing-sm;
}
&__btn {
font-size: @font-size-base;
color: @primary-color;
transition: opacity 0.2s;
&:hover {
opacity: 0.75;
}
&--accent {
color: @warning-color;
}
}
&__empty {
padding: @spacing-xl 0;
text-align: center;
}
}
</style>