728 lines
20 KiB
Vue
728 lines
20 KiB
Vue
<template>
|
|
<div class="progress-detail-page">
|
|
<a-card class="mb-4">
|
|
<template #title>
|
|
<a-breadcrumb>
|
|
<a-breadcrumb-item>
|
|
<router-link :to="`/${tenantCode}/contests/reviews/progress`">评审进度</router-link>
|
|
</a-breadcrumb-item>
|
|
<a-breadcrumb-item>{{ contestName || '作品评审进度' }}</a-breadcrumb-item>
|
|
</a-breadcrumb>
|
|
</template>
|
|
<template #extra>
|
|
<a-space>
|
|
<a-button type="primary" @click="handleStartReview">
|
|
开始评审
|
|
</a-button>
|
|
<a-button @click="handleEndReview">
|
|
结束评审
|
|
</a-button>
|
|
<a-button @click="handleNotSubmitted">
|
|
{{ contestType === 'team' ? '未提交作品队伍' : '未提交作品选手' }}
|
|
</a-button>
|
|
<a-button>
|
|
<template #icon><DownloadOutlined /></template>
|
|
导出
|
|
</a-button>
|
|
</a-space>
|
|
</template>
|
|
</a-card>
|
|
|
|
<!-- 搜索表单 -->
|
|
<a-form :model="searchParams" layout="inline" class="search-form" @finish="handleSearch">
|
|
<a-form-item label="作品编号">
|
|
<a-input
|
|
v-model:value="searchParams.workNo"
|
|
placeholder="请输入作品编号"
|
|
allow-clear
|
|
style="width: 150px"
|
|
/>
|
|
</a-form-item>
|
|
<a-form-item label="报名账号">
|
|
<a-input
|
|
v-model:value="searchParams.username"
|
|
placeholder="请输入报名账号"
|
|
allow-clear
|
|
style="width: 150px"
|
|
/>
|
|
</a-form-item>
|
|
<a-form-item label="评审进度">
|
|
<a-select
|
|
v-model:value="searchParams.reviewProgress"
|
|
placeholder="请选择评审进度"
|
|
allow-clear
|
|
style="width: 150px"
|
|
>
|
|
<a-select-option value="not_reviewed">未评审</a-select-option>
|
|
<a-select-option value="in_progress">评审中</a-select-option>
|
|
<a-select-option value="completed">已完成</a-select-option>
|
|
</a-select>
|
|
</a-form-item>
|
|
<a-form-item>
|
|
<a-button type="primary" html-type="submit">
|
|
<template #icon><SearchOutlined /></template>
|
|
搜索
|
|
</a-button>
|
|
<a-button style="margin-left: 8px" @click="handleReset">
|
|
<template #icon><ReloadOutlined /></template>
|
|
重置
|
|
</a-button>
|
|
</a-form-item>
|
|
</a-form>
|
|
|
|
<!-- 数据表格 -->
|
|
<a-table
|
|
:columns="columns"
|
|
:data-source="dataSource"
|
|
:loading="loading"
|
|
:pagination="pagination"
|
|
row-key="id"
|
|
@change="handleTableChange"
|
|
>
|
|
<template #bodyCell="{ column, record, index }">
|
|
<template v-if="column.key === 'index'">
|
|
{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}
|
|
</template>
|
|
<template v-else-if="column.key === 'workNo'">
|
|
<a @click="handleViewWorkDetail(record)">{{ record.workNo || "-" }}</a>
|
|
</template>
|
|
<template v-else-if="column.key === 'username'">
|
|
{{ record.submitterAccountNo || record.registration?.user?.username || "-" }}
|
|
</template>
|
|
<template v-else-if="column.key === 'judgeScore'">
|
|
<span v-if="record.averageScore !== undefined && record.averageScore !== null">
|
|
{{ record.averageScore.toFixed(2) }}
|
|
</span>
|
|
<span v-else>-</span>
|
|
</template>
|
|
<template v-else-if="column.key === 'reviewProgress'">
|
|
<a-tag :color="getProgressColor(record)">
|
|
{{ getProgressText(record) }}
|
|
</a-tag>
|
|
</template>
|
|
<template v-else-if="column.key === 'action'">
|
|
<a-button type="link" size="small" @click="handleViewScores(record)">
|
|
查看
|
|
</a-button>
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
|
|
<!-- 评审详情抽屉 -->
|
|
<a-drawer
|
|
v-model:open="scoreDrawerVisible"
|
|
title="评审详情"
|
|
placement="right"
|
|
width="700"
|
|
>
|
|
<a-table
|
|
:columns="scoreColumns"
|
|
:data-source="scoreList"
|
|
:loading="scoreLoading"
|
|
:pagination="false"
|
|
row-key="id"
|
|
size="small"
|
|
>
|
|
<template #bodyCell="{ column, record }">
|
|
<template v-if="column.key === 'judgeName'">
|
|
{{ record.judge?.nickname || record.judge?.username || "-" }}
|
|
</template>
|
|
<template v-else-if="column.key === 'phone'">
|
|
{{ record.judge?.phone || "-" }}
|
|
</template>
|
|
<template v-else-if="column.key === 'tenant'">
|
|
{{ record.judge?.tenant?.name || "-" }}
|
|
</template>
|
|
<template v-else-if="column.key === 'score'">
|
|
<span v-if="record.score !== undefined && record.score !== null">
|
|
{{ record.score }}
|
|
</span>
|
|
<span v-else class="text-gray">未评分</span>
|
|
</template>
|
|
<template v-else-if="column.key === 'scoreTime'">
|
|
{{ formatDate(record.scoreTime) }}
|
|
</template>
|
|
<template v-else-if="column.key === 'action'">
|
|
<a-button type="link" size="small" @click="handleReplaceJudge(record)">
|
|
替换评委
|
|
</a-button>
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
</a-drawer>
|
|
|
|
<!-- 评委替换抽屉 -->
|
|
<a-drawer
|
|
v-model:open="replaceJudgeDrawerVisible"
|
|
title="评委替换"
|
|
placement="right"
|
|
width="700"
|
|
:footer-style="{ textAlign: 'right' }"
|
|
>
|
|
<!-- 搜索 -->
|
|
<a-form layout="inline" class="mb-3">
|
|
<a-form-item label="姓名">
|
|
<a-input
|
|
v-model:value="judgeSearchParams.nickname"
|
|
placeholder="请输入姓名"
|
|
allow-clear
|
|
style="width: 150px"
|
|
/>
|
|
</a-form-item>
|
|
<a-form-item label="机构信息">
|
|
<a-input
|
|
v-model:value="judgeSearchParams.tenantName"
|
|
placeholder="请输入机构信息"
|
|
allow-clear
|
|
style="width: 150px"
|
|
/>
|
|
</a-form-item>
|
|
<a-form-item>
|
|
<a-button type="primary" @click="handleSearchJudges">搜索</a-button>
|
|
<a-button class="ml-2" @click="handleResetJudgeSearch">重置</a-button>
|
|
</a-form-item>
|
|
</a-form>
|
|
|
|
<!-- 评委列表 -->
|
|
<a-table
|
|
:columns="judgeSelectColumns"
|
|
:data-source="judgeList"
|
|
:loading="judgeListLoading"
|
|
:pagination="judgePagination"
|
|
:row-selection="judgeRowSelection"
|
|
row-key="id"
|
|
size="small"
|
|
@change="handleJudgeTableChange"
|
|
>
|
|
<template #bodyCell="{ column, record }">
|
|
<template v-if="column.key === 'judgeName'">
|
|
{{ record.nickname || record.username || "-" }}
|
|
</template>
|
|
<template v-else-if="column.key === 'assignedCount'">
|
|
{{ record.contestJudges?.length || 0 }}
|
|
</template>
|
|
<template v-else-if="column.key === 'tenant'">
|
|
{{ record.tenant?.name || "-" }}
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
|
|
<template #footer>
|
|
<a-space>
|
|
<a-button @click="replaceJudgeDrawerVisible = false">取消</a-button>
|
|
<a-button type="primary" :loading="replaceLoading" @click="handleConfirmReplace">
|
|
确定
|
|
</a-button>
|
|
</a-space>
|
|
</template>
|
|
</a-drawer>
|
|
|
|
<!-- 作品详情弹框 -->
|
|
<WorkDetailModal
|
|
v-model:open="workDetailModalVisible"
|
|
:work-id="currentWorkId"
|
|
/>
|
|
|
|
<!-- 未提交作品弹框 -->
|
|
<a-modal
|
|
v-model:open="notSubmittedModalVisible"
|
|
:title="contestType === 'team' ? '未提交作品队伍' : '未提交作品选手'"
|
|
width="700px"
|
|
:footer="null"
|
|
>
|
|
<a-table
|
|
:columns="notSubmittedColumns"
|
|
:data-source="notSubmittedList"
|
|
:loading="notSubmittedLoading"
|
|
:pagination="false"
|
|
row-key="id"
|
|
size="small"
|
|
>
|
|
<template #bodyCell="{ column, record, index }">
|
|
<template v-if="column.key === 'index'">
|
|
{{ index + 1 }}
|
|
</template>
|
|
<template v-else-if="column.key === 'name'">
|
|
{{ contestType === 'team' ? record.teamName : (record.user?.nickname || '-') }}
|
|
</template>
|
|
<template v-else-if="column.key === 'username'">
|
|
{{ record.user?.username || "-" }}
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
</a-modal>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, computed, onMounted } from "vue"
|
|
import { useRoute, useRouter } from "vue-router"
|
|
import { message, Modal } from "ant-design-vue"
|
|
import type { TableProps } from "ant-design-vue"
|
|
import {
|
|
ArrowLeftOutlined,
|
|
SearchOutlined,
|
|
ReloadOutlined,
|
|
DownloadOutlined,
|
|
} from "@ant-design/icons-vue"
|
|
import dayjs from "dayjs"
|
|
import { contestsApi, worksApi, reviewsApi, type ContestWork } from "@/api/contests"
|
|
import { judgesManagementApi, type Judge } from "@/api/judges-management"
|
|
import WorkDetailModal from "../components/WorkDetailModal.vue"
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const tenantCode = route.params.tenantCode as string
|
|
const contestId = Number(route.params.id)
|
|
const contestType = (route.query.type as string) || "individual"
|
|
|
|
// 赛事名称
|
|
const contestName = ref("")
|
|
|
|
// 作品详情弹框
|
|
const workDetailModalVisible = ref(false)
|
|
const currentWorkId = ref<number | null>(null)
|
|
|
|
// 列表状态
|
|
const loading = ref(false)
|
|
const dataSource = ref<ContestWork[]>([])
|
|
const pagination = reactive({
|
|
current: 1,
|
|
pageSize: 10,
|
|
total: 0,
|
|
})
|
|
|
|
// 搜索参数
|
|
const searchParams = reactive({
|
|
workNo: "",
|
|
username: "",
|
|
reviewProgress: undefined as string | undefined,
|
|
})
|
|
|
|
// 表格列定义
|
|
const columns = [
|
|
{ title: "序号", key: "index", width: 70 },
|
|
{ title: "作品编号", key: "workNo", width: 120 },
|
|
{ title: "报名账号", key: "username", width: 150 },
|
|
{ title: "评委评分", key: "judgeScore", width: 100 },
|
|
{ title: "评审进度", key: "reviewProgress", width: 100 },
|
|
{ title: "操作", key: "action", width: 80, fixed: "right" as const },
|
|
]
|
|
|
|
// 评分详情抽屉
|
|
const scoreDrawerVisible = ref(false)
|
|
const scoreLoading = ref(false)
|
|
const scoreList = ref<any[]>([])
|
|
const currentWork = ref<ContestWork | null>(null)
|
|
|
|
// 评分详情列
|
|
const scoreColumns = [
|
|
{ title: "评委姓名", key: "judgeName", width: 100 },
|
|
{ title: "联系方式", key: "phone", width: 120 },
|
|
{ title: "机构信息", key: "tenant", width: 150 },
|
|
{ title: "评分", key: "score", width: 80 },
|
|
{ title: "评分时间", key: "scoreTime", width: 150 },
|
|
{ title: "操作", key: "action", width: 100 },
|
|
]
|
|
|
|
// 替换评委抽屉
|
|
const replaceJudgeDrawerVisible = ref(false)
|
|
const replaceLoading = ref(false)
|
|
const currentReplaceScore = ref<any>(null)
|
|
const judgeList = ref<Judge[]>([])
|
|
const judgeListLoading = ref(false)
|
|
const judgePagination = reactive({
|
|
current: 1,
|
|
pageSize: 10,
|
|
total: 0,
|
|
})
|
|
const judgeSearchParams = reactive({
|
|
nickname: "",
|
|
tenantName: "",
|
|
})
|
|
const selectedJudgeKeys = ref<number[]>([])
|
|
const selectedJudgeRow = ref<Judge | null>(null)
|
|
|
|
// 评委选择列
|
|
const judgeSelectColumns = [
|
|
{ title: "评委姓名", key: "judgeName", width: 120 },
|
|
{ title: "已分配赛事数", key: "assignedCount", width: 120 },
|
|
{ title: "机构信息", key: "tenant", width: 150 },
|
|
]
|
|
|
|
// 评委选择行配置(单选)
|
|
const judgeRowSelection = computed<TableProps["rowSelection"]>(() => ({
|
|
type: "radio",
|
|
selectedRowKeys: selectedJudgeKeys.value,
|
|
onChange: (keys: any, rows: Judge[]) => {
|
|
selectedJudgeKeys.value = keys
|
|
selectedJudgeRow.value = rows[0] || null
|
|
},
|
|
}))
|
|
|
|
// 未提交作品弹框
|
|
const notSubmittedModalVisible = ref(false)
|
|
const notSubmittedLoading = ref(false)
|
|
const notSubmittedList = ref<any[]>([])
|
|
|
|
// 未提交作品列
|
|
const notSubmittedColumns = computed(() => {
|
|
if (contestType === "team") {
|
|
return [
|
|
{ title: "序号", key: "index", width: 70 },
|
|
{ title: "队伍名称", key: "name", width: 150 },
|
|
{ title: "报名账号", key: "username", width: 150 },
|
|
]
|
|
}
|
|
return [
|
|
{ title: "序号", key: "index", width: 70 },
|
|
{ title: "姓名", key: "name", width: 120 },
|
|
{ title: "报名账号", key: "username", width: 150 },
|
|
]
|
|
})
|
|
|
|
// 获取评审进度颜色
|
|
const getProgressColor = (record: ContestWork) => {
|
|
if (!record.reviewedCount || record.reviewedCount === 0) return "default"
|
|
if (record.reviewedCount >= (record.totalJudgesCount || 1)) return "success"
|
|
return "processing"
|
|
}
|
|
|
|
// 获取评审进度文本
|
|
const getProgressText = (record: ContestWork) => {
|
|
if (!record.reviewedCount || record.reviewedCount === 0) return "未评审"
|
|
if (record.reviewedCount >= (record.totalJudgesCount || 1)) return "已完成"
|
|
return `${record.reviewedCount}/${record.totalJudgesCount || 0}`
|
|
}
|
|
|
|
// 格式化日期
|
|
const formatDate = (dateStr?: string) => {
|
|
if (!dateStr) return "-"
|
|
return dayjs(dateStr).format("YYYY-MM-DD HH:mm")
|
|
}
|
|
|
|
// 获取赛事信息
|
|
const fetchContestInfo = async () => {
|
|
try {
|
|
const contest = await contestsApi.getDetail(contestId)
|
|
contestName.value = contest.contestName
|
|
} catch (error) {
|
|
console.error("获取赛事信息失败", error)
|
|
}
|
|
}
|
|
|
|
// 获取作品列表
|
|
const fetchList = async () => {
|
|
loading.value = true
|
|
try {
|
|
const response = await worksApi.getList({
|
|
page: pagination.current,
|
|
pageSize: pagination.pageSize,
|
|
contestId,
|
|
workNo: searchParams.workNo || undefined,
|
|
username: searchParams.username || undefined,
|
|
})
|
|
dataSource.value = response.list
|
|
pagination.total = response.total
|
|
} catch (error: any) {
|
|
message.error(error?.response?.data?.message || "获取作品列表失败")
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// 获取评委列表(全部评委)
|
|
const fetchJudgeList = async () => {
|
|
judgeListLoading.value = true
|
|
try {
|
|
const response = await judgesManagementApi.getList({
|
|
page: judgePagination.current,
|
|
pageSize: judgePagination.pageSize,
|
|
nickname: judgeSearchParams.nickname || undefined,
|
|
})
|
|
judgeList.value = response.list
|
|
judgePagination.total = response.total
|
|
} catch (error) {
|
|
message.error("获取评委列表失败")
|
|
} finally {
|
|
judgeListLoading.value = false
|
|
}
|
|
}
|
|
|
|
// 搜索
|
|
const handleSearch = () => {
|
|
pagination.current = 1
|
|
fetchList()
|
|
}
|
|
|
|
// 重置
|
|
const handleReset = () => {
|
|
searchParams.workNo = ""
|
|
searchParams.username = ""
|
|
searchParams.reviewProgress = undefined
|
|
pagination.current = 1
|
|
fetchList()
|
|
}
|
|
|
|
// 表格分页变化
|
|
const handleTableChange = (pag: any) => {
|
|
pagination.current = pag.current
|
|
pagination.pageSize = pag.pageSize
|
|
fetchList()
|
|
}
|
|
|
|
// 返回
|
|
const handleBack = () => {
|
|
router.back()
|
|
}
|
|
|
|
// 查看作品详情
|
|
const handleViewWorkDetail = (record: ContestWork) => {
|
|
currentWorkId.value = record.id
|
|
workDetailModalVisible.value = true
|
|
}
|
|
|
|
// 开始评审
|
|
const handleStartReview = () => {
|
|
Modal.confirm({
|
|
title: "确认开始评审",
|
|
content: "确定要开始评审吗?开始后评委可以进行评分操作。",
|
|
okText: "确定",
|
|
cancelText: "取消",
|
|
async onOk() {
|
|
try {
|
|
await contestsApi.update(contestId, {
|
|
reviewStartTime: new Date().toISOString(),
|
|
})
|
|
message.success("评审已开始")
|
|
} catch (error: any) {
|
|
message.error(error?.response?.data?.message || "操作失败")
|
|
}
|
|
},
|
|
})
|
|
}
|
|
|
|
// 结束评审
|
|
const handleEndReview = () => {
|
|
Modal.confirm({
|
|
title: "确认结束评审",
|
|
content: "确定要结束评审吗?结束后评委将无法继续评分。",
|
|
okText: "确定",
|
|
cancelText: "取消",
|
|
async onOk() {
|
|
try {
|
|
await contestsApi.update(contestId, {
|
|
reviewEndTime: new Date().toISOString(),
|
|
})
|
|
message.success("评审已结束")
|
|
} catch (error: any) {
|
|
message.error(error?.response?.data?.message || "操作失败")
|
|
}
|
|
},
|
|
})
|
|
}
|
|
|
|
// 查看未提交作品
|
|
const handleNotSubmitted = async () => {
|
|
notSubmittedModalVisible.value = true
|
|
notSubmittedLoading.value = true
|
|
try {
|
|
// TODO: 调用获取未提交作品的API
|
|
notSubmittedList.value = []
|
|
} catch (error) {
|
|
message.error("获取未提交作品列表失败")
|
|
} finally {
|
|
notSubmittedLoading.value = false
|
|
}
|
|
}
|
|
|
|
// 查看评分详情
|
|
const handleViewScores = async (record: ContestWork) => {
|
|
currentWork.value = record
|
|
scoreDrawerVisible.value = true
|
|
scoreLoading.value = true
|
|
try {
|
|
// TODO: 调用获取作品评分列表的API
|
|
const scores = await reviewsApi.getWorkScores(record.id)
|
|
scoreList.value = scores
|
|
} catch (error) {
|
|
message.error("获取评分详情失败")
|
|
scoreList.value = []
|
|
} finally {
|
|
scoreLoading.value = false
|
|
}
|
|
}
|
|
|
|
// 替换评委
|
|
const handleReplaceJudge = (record: any) => {
|
|
currentReplaceScore.value = record
|
|
selectedJudgeKeys.value = []
|
|
selectedJudgeRow.value = null
|
|
replaceJudgeDrawerVisible.value = true
|
|
fetchJudgeList()
|
|
}
|
|
|
|
// 搜索评委
|
|
const handleSearchJudges = () => {
|
|
judgePagination.current = 1
|
|
fetchJudgeList()
|
|
}
|
|
|
|
// 重置评委搜索
|
|
const handleResetJudgeSearch = () => {
|
|
judgeSearchParams.nickname = ""
|
|
judgeSearchParams.tenantName = ""
|
|
judgePagination.current = 1
|
|
fetchJudgeList()
|
|
}
|
|
|
|
// 评委表格分页变化
|
|
const handleJudgeTableChange = (pag: any) => {
|
|
judgePagination.current = pag.current
|
|
judgePagination.pageSize = pag.pageSize
|
|
fetchJudgeList()
|
|
}
|
|
|
|
// 确认替换评委
|
|
const handleConfirmReplace = async () => {
|
|
if (!selectedJudgeRow.value) {
|
|
message.warning("请选择评委")
|
|
return
|
|
}
|
|
|
|
replaceLoading.value = true
|
|
try {
|
|
// TODO: 调用替换评委的API
|
|
await reviewsApi.replaceJudge(currentReplaceScore.value.id, selectedJudgeRow.value.id)
|
|
message.success("替换成功")
|
|
replaceJudgeDrawerVisible.value = false
|
|
// 刷新评分列表
|
|
if (currentWork.value) {
|
|
handleViewScores(currentWork.value)
|
|
}
|
|
} catch (error: any) {
|
|
message.error(error?.response?.data?.message || "替换失败")
|
|
} finally {
|
|
replaceLoading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
fetchContestInfo()
|
|
fetchList()
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
$primary: #1890ff;
|
|
$primary-dark: #0958d9;
|
|
$gradient-primary: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
|
|
|
|
.progress-detail-page {
|
|
:deep(.ant-card) {
|
|
border: none;
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
margin-bottom: 16px;
|
|
|
|
.ant-card-head {
|
|
border-bottom: none;
|
|
padding: 16px 24px;
|
|
|
|
.ant-card-head-title {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: rgba(0, 0, 0, 0.85);
|
|
}
|
|
}
|
|
|
|
.ant-card-body {
|
|
padding: 0;
|
|
}
|
|
}
|
|
|
|
:deep(.ant-btn-primary) {
|
|
background: $gradient-primary;
|
|
border: none;
|
|
box-shadow: 0 4px 12px rgba($primary, 0.35);
|
|
transition: all 0.3s ease;
|
|
|
|
&:hover {
|
|
background: linear-gradient(135deg, $primary-dark 0%, darken($primary-dark, 8%) 100%);
|
|
box-shadow: 0 6px 16px rgba($primary, 0.45);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
&:active {
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
:deep(.ant-table-wrapper) {
|
|
background: #fff;
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
overflow: hidden;
|
|
|
|
.ant-table {
|
|
.ant-table-thead > tr > th {
|
|
background: #fafafa;
|
|
font-weight: 600;
|
|
color: rgba(0, 0, 0, 0.85);
|
|
border-bottom: 1px solid #f0f0f0;
|
|
}
|
|
|
|
.ant-table-tbody > tr {
|
|
transition: all 0.2s ease;
|
|
|
|
&:hover > td {
|
|
background: rgba($primary, 0.04);
|
|
}
|
|
|
|
> td {
|
|
border-bottom: 1px solid #f5f5f5;
|
|
}
|
|
}
|
|
}
|
|
|
|
.ant-table-pagination {
|
|
padding: 16px;
|
|
margin: 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
.search-form {
|
|
margin-bottom: 16px;
|
|
padding: 20px 24px;
|
|
background: #fff;
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: flex-start;
|
|
gap: 16px 24px;
|
|
|
|
:deep(.ant-form-item) {
|
|
margin-bottom: 0;
|
|
margin-right: 0;
|
|
}
|
|
}
|
|
|
|
.mb-3 {
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.mb-4 {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.ml-2 {
|
|
margin-left: 8px;
|
|
}
|
|
|
|
.text-gray {
|
|
color: #999;
|
|
}
|
|
</style>
|