feat: 成果发布列表评审未完成时禁用发布成果入口
Made-with: Cursor
This commit is contained in:
parent
6c3b2f4a1d
commit
ea40131084
@ -8,12 +8,8 @@
|
|||||||
|
|
||||||
<!-- 统计卡片 -->
|
<!-- 统计卡片 -->
|
||||||
<div class="stats-row">
|
<div class="stats-row">
|
||||||
<div
|
<div v-for="item in statsItems" :key="item.key" :class="['stat-card', { active: activeFilter === item.key }]"
|
||||||
v-for="item in statsItems"
|
@click="handleStatClick(item.key)">
|
||||||
:key="item.key"
|
|
||||||
:class="['stat-card', { active: activeFilter === item.key }]"
|
|
||||||
@click="handleStatClick(item.key)"
|
|
||||||
>
|
|
||||||
<div class="stat-icon" :style="{ background: item.bgColor }">
|
<div class="stat-icon" :style="{ background: item.bgColor }">
|
||||||
<component :is="item.icon" :style="{ color: item.color }" />
|
<component :is="item.icon" :style="{ color: item.color }" />
|
||||||
</div>
|
</div>
|
||||||
@ -28,46 +24,31 @@
|
|||||||
<div class="filter-bar">
|
<div class="filter-bar">
|
||||||
<a-form layout="inline" :model="superSearch" @finish="handleSuperSearch">
|
<a-form layout="inline" :model="superSearch" @finish="handleSuperSearch">
|
||||||
<a-form-item label="所属活动">
|
<a-form-item label="所属活动">
|
||||||
<a-select
|
<a-select v-model:value="superSearch.contestId" placeholder="全部活动" allow-clear show-search
|
||||||
v-model:value="superSearch.contestId"
|
:filter-option="filterContestOption" style="width: 200px" :options="contestOptions" />
|
||||||
placeholder="全部活动"
|
|
||||||
allow-clear
|
|
||||||
show-search
|
|
||||||
:filter-option="filterContestOption"
|
|
||||||
style="width: 200px"
|
|
||||||
:options="contestOptions"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="发布状态">
|
<a-form-item label="发布状态">
|
||||||
<a-select
|
<a-select v-model:value="superSearch.resultState" placeholder="全部" allow-clear style="width: 110px">
|
||||||
v-model:value="superSearch.resultState"
|
|
||||||
placeholder="全部"
|
|
||||||
allow-clear
|
|
||||||
style="width: 110px"
|
|
||||||
>
|
|
||||||
<a-select-option value="published">已发布</a-select-option>
|
<a-select-option value="published">已发布</a-select-option>
|
||||||
<a-select-option value="unpublished">未发布</a-select-option>
|
<a-select-option value="unpublished">未发布</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="主办机构">
|
<a-form-item label="主办机构">
|
||||||
<a-select
|
<a-select v-model:value="superSearch.creatorTenantId" placeholder="全部机构" allow-clear show-search
|
||||||
v-model:value="superSearch.creatorTenantId"
|
:filter-option="filterTenantOption" style="width: 160px" :options="tenantOptions" />
|
||||||
placeholder="全部机构"
|
|
||||||
allow-clear
|
|
||||||
show-search
|
|
||||||
:filter-option="filterTenantOption"
|
|
||||||
style="width: 160px"
|
|
||||||
:options="tenantOptions"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button type="primary" html-type="submit">
|
<a-button type="primary" html-type="submit">
|
||||||
<template #icon><SearchOutlined /></template>
|
<template #icon>
|
||||||
|
<SearchOutlined />
|
||||||
|
</template>
|
||||||
搜索
|
搜索
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button @click="handleSuperReset">
|
<a-button @click="handleSuperReset">
|
||||||
<template #icon><ReloadOutlined /></template>
|
<template #icon>
|
||||||
|
<ReloadOutlined />
|
||||||
|
</template>
|
||||||
重置
|
重置
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
@ -76,26 +57,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<a-table
|
<a-table :columns="superColumns" :data-source="superDataSource" :loading="superLoading"
|
||||||
:columns="superColumns"
|
:pagination="superPagination" row-key="id" @change="handleSuperTableChange" class="data-table">
|
||||||
:data-source="superDataSource"
|
|
||||||
:loading="superLoading"
|
|
||||||
:pagination="superPagination"
|
|
||||||
row-key="id"
|
|
||||||
@change="handleSuperTableChange"
|
|
||||||
class="data-table"
|
|
||||||
>
|
|
||||||
<template #bodyCell="{ column, record, index }">
|
<template #bodyCell="{ column, record, index }">
|
||||||
<template v-if="column.key === 'index'">
|
<template v-if="column.key === 'index'">
|
||||||
{{ (superPagination.current - 1) * superPagination.pageSize + index + 1 }}
|
{{ (superPagination.current - 1) * superPagination.pageSize + index + 1 }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'contestName'">
|
<template v-else-if="column.key === 'contestName'">
|
||||||
<a-button
|
<a-button type="link" size="small" class="contest-link"
|
||||||
type="link"
|
@click="router.push(`/${tenantCode}/contests/${record.id}/overview`)">
|
||||||
size="small"
|
|
||||||
class="contest-link"
|
|
||||||
@click="router.push(`/${tenantCode}/contests/${record.id}/overview`)"
|
|
||||||
>
|
|
||||||
{{ record.contestName }}
|
{{ record.contestName }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
@ -137,7 +107,8 @@
|
|||||||
|
|
||||||
<!-- 统计概览 -->
|
<!-- 统计概览 -->
|
||||||
<div class="stats-row">
|
<div class="stats-row">
|
||||||
<div v-for="item in orgStatsItems" :key="item.key" :class="['stat-card', { active: orgActiveFilter === item.key }]" @click="handleOrgStatClick(item.key)">
|
<div v-for="item in orgStatsItems" :key="item.key"
|
||||||
|
:class="['stat-card', { active: orgActiveFilter === item.key }]" @click="handleOrgStatClick(item.key)">
|
||||||
<div class="stat-icon" :style="{ background: item.bgColor }">
|
<div class="stat-icon" :style="{ background: item.bgColor }">
|
||||||
<component :is="item.icon" :style="{ color: item.color }" />
|
<component :is="item.icon" :style="{ color: item.color }" />
|
||||||
</div>
|
</div>
|
||||||
@ -155,25 +126,33 @@
|
|||||||
<a-input v-model:value="searchParams.contestName" placeholder="请输入活动名称" allow-clear style="width: 200px" />
|
<a-input v-model:value="searchParams.contestName" placeholder="请输入活动名称" allow-clear style="width: 200px" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="活动类型">
|
<a-form-item label="活动类型">
|
||||||
<a-select v-model:value="searchParams.contestType" placeholder="全部" allow-clear style="width: 120px" @change="handleSearch">
|
<a-select v-model:value="searchParams.contestType" placeholder="全部" allow-clear style="width: 120px"
|
||||||
|
@change="handleSearch">
|
||||||
<a-select-option value="individual">个人参与</a-select-option>
|
<a-select-option value="individual">个人参与</a-select-option>
|
||||||
<a-select-option value="team">团队参与</a-select-option>
|
<a-select-option value="team">团队参与</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="发布状态">
|
<a-form-item label="发布状态">
|
||||||
<a-select v-model:value="orgResultState" placeholder="全部" allow-clear style="width: 110px" @change="handleSearch">
|
<a-select v-model:value="orgResultState" placeholder="全部" allow-clear style="width: 110px"
|
||||||
|
@change="handleSearch">
|
||||||
<a-select-option value="published">已发布</a-select-option>
|
<a-select-option value="published">已发布</a-select-option>
|
||||||
<a-select-option value="unpublished">未发布</a-select-option>
|
<a-select-option value="unpublished">未发布</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-button type="primary" html-type="submit"><template #icon><SearchOutlined /></template> 搜索</a-button>
|
<a-button type="primary" html-type="submit"><template #icon>
|
||||||
<a-button style="margin-left: 8px" @click="handleReset"><template #icon><ReloadOutlined /></template> 重置</a-button>
|
<SearchOutlined />
|
||||||
|
</template> 搜索</a-button>
|
||||||
|
<a-button style="margin-left: 8px" @click="handleReset"><template #icon>
|
||||||
|
<ReloadOutlined />
|
||||||
|
</template>
|
||||||
|
重置</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-table :columns="orgColumns" :data-source="dataSource" :loading="loading" :pagination="pagination" row-key="id" @change="handleTableChange" class="data-table">
|
<a-table :columns="orgColumns" :data-source="dataSource" :loading="loading" :pagination="pagination" row-key="id"
|
||||||
|
@change="handleTableChange" class="data-table">
|
||||||
<template #bodyCell="{ column, record, index }">
|
<template #bodyCell="{ column, record, index }">
|
||||||
<template v-if="column.key === 'index'">
|
<template v-if="column.key === 'index'">
|
||||||
{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}
|
{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}
|
||||||
@ -182,7 +161,8 @@
|
|||||||
<a @click="handleViewDetail(record)">{{ record.contestName }}</a>
|
<a @click="handleViewDetail(record)">{{ record.contestName }}</a>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'contestType'">
|
<template v-else-if="column.key === 'contestType'">
|
||||||
<a-tag :color="record.contestType === 'individual' ? 'blue' : 'green'">{{ record.contestType === 'individual' ? '个人' : '团队' }}</a-tag>
|
<a-tag :color="record.contestType === 'individual' ? 'blue' : 'green'">{{ record.contestType ===
|
||||||
|
'individual' ? '个人' : '团队' }}</a-tag>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'registrationCount'">
|
<template v-else-if="column.key === 'registrationCount'">
|
||||||
{{ record._count?.registrations || 0 }}
|
{{ record._count?.registrations || 0 }}
|
||||||
@ -195,8 +175,17 @@
|
|||||||
<a-tag v-else color="default">未发布</a-tag>
|
<a-tag v-else color="default">未发布</a-tag>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'action'">
|
<template v-else-if="column.key === 'action'">
|
||||||
<a-button v-if="record.resultState === 'published'" type="link" size="small" @click="handleViewDetail(record)">查看成果</a-button>
|
<a-button v-if="record.resultState === 'published'" type="link" size="small"
|
||||||
<a-button v-else type="link" size="small" style="color: #10b981" @click="handleViewDetail(record)">发布成果</a-button>
|
@click="handleViewDetail(record)">查看成果</a-button>
|
||||||
|
<template v-else-if="canPublishResults(record)">
|
||||||
|
<a-button type="link" size="small" style="color: #10b981"
|
||||||
|
@click="handleViewDetail(record)">发布成果</a-button>
|
||||||
|
</template>
|
||||||
|
<a-tooltip v-else placement="top" title="评审未完成,请待全部作品完成评审后再发布成果">
|
||||||
|
<span class="publish-results-btn-wrap">
|
||||||
|
<a-button type="link" size="small" style="color: #777" disabled>发布成果</a-button>
|
||||||
|
</span>
|
||||||
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
@ -232,6 +221,16 @@ const isSuperAdmin = computed(() => authStore.hasAnyRole(['super_admin']))
|
|||||||
|
|
||||||
const formatDate = (d?: string) => d ? dayjs(d).format("YYYY-MM-DD HH:mm") : "-"
|
const formatDate = (d?: string) => d ? dayjs(d).format("YYYY-MM-DD HH:mm") : "-"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许进入成果发布详情(发布成果入口)。
|
||||||
|
* 与活动列表接口的 reviewedCount / totalWorksCount 一致:有作品且已完成评审的作品数 ≥ 作品总数。
|
||||||
|
*/
|
||||||
|
function canPublishResults(record: Contest): boolean {
|
||||||
|
const totalWorks = Number(record.totalWorksCount ?? record._count?.works ?? 0)
|
||||||
|
const reviewed = Number(record.reviewedCount ?? 0)
|
||||||
|
return totalWorks > 0 && reviewed >= totalWorks
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================
|
// =============================================
|
||||||
// 超管端逻辑
|
// 超管端逻辑
|
||||||
// =============================================
|
// =============================================
|
||||||
@ -455,11 +454,26 @@ $primary: #6366f1;
|
|||||||
border: none;
|
border: none;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||||||
:deep(.ant-card-head) { border-bottom: none; .ant-card-head-title { font-size: 18px; font-weight: 600; } }
|
|
||||||
:deep(.ant-card-body) { padding: 0; }
|
:deep(.ant-card-head) {
|
||||||
|
border-bottom: none;
|
||||||
|
|
||||||
|
.ant-card-head-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-card-body) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-row { display: flex; gap: 12px; margin-bottom: 16px; }
|
.stats-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.stat-card {
|
.stat-card {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@ -473,10 +487,43 @@ $primary: #6366f1;
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
&:hover { box-shadow: 0 4px 16px rgba($primary, 0.12); }
|
|
||||||
&.active { border-color: $primary; background: rgba($primary, 0.02); }
|
&:hover {
|
||||||
.stat-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 16px; flex-shrink: 0; }
|
box-shadow: 0 4px 16px rgba($primary, 0.12);
|
||||||
.stat-info { display: flex; flex-direction: column; .stat-count { font-size: 18px; font-weight: 700; color: #1e1b4b; line-height: 1.2; } .stat-label { font-size: 12px; color: #9ca3af; } }
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: $primary;
|
||||||
|
background: rgba($primary, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.stat-count {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1e1b4b;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-bar {
|
.filter-bar {
|
||||||
@ -493,13 +540,35 @@ $primary: #6366f1;
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.ant-table-thead > tr > th { background: #fafafa; font-weight: 600; }
|
|
||||||
.ant-table-tbody > tr:hover > td { background: rgba($primary, 0.03); }
|
.ant-table-thead>tr>th {
|
||||||
.ant-table-pagination { padding: 16px; margin: 0; }
|
background: #fafafa;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-tbody>tr:hover>td {
|
||||||
|
background: rgba($primary, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-pagination {
|
||||||
|
padding: 16px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.contest-link { padding: 0; text-align: left; }
|
.contest-link {
|
||||||
.text-muted { color: #d1d5db; }
|
padding: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-muted {
|
||||||
|
color: #d1d5db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 包裹禁用链接按钮,便于 Tooltip 触发且布局不塌缩 */
|
||||||
|
.publish-results-btn-wrap {
|
||||||
|
display: inline-block;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user