Merge branch 'devlopment' of http://pms.dtyx.net:3000/wuxueyu/Industrial-image-management-system---web into devlopment
This commit is contained in:
commit
e526e80de3
|
@ -9,14 +9,38 @@ export const equipmentApprovalApi = {
|
|||
* 分页查询待审批的设备采购申请
|
||||
*/
|
||||
getPendingApprovals(params: EquipmentApprovalListReq) {
|
||||
return http.get<EquipmentApprovalResp[]>('/equipment/approval/pending', params)
|
||||
// 确保分页参数格式正确,与设备采购模块保持一致
|
||||
const requestParams = {
|
||||
...params,
|
||||
// 直接使用 pageNum,因为后端分页器需要的是 pageNum 参数
|
||||
pageNum: params.pageNum || 1,
|
||||
pageSize: params.pageSize || 10,
|
||||
}
|
||||
|
||||
console.log('🔍 API - equipmentApprovalApi.getPendingApprovals 被调用')
|
||||
console.log('🔍 API - 接收到的参数:', params)
|
||||
console.log('🔍 API - 最终请求参数:', requestParams)
|
||||
|
||||
return http.get<EquipmentApprovalResp[]>('/equipment/approval/pending', requestParams)
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询已审批的设备采购申请
|
||||
*/
|
||||
getApprovedApprovals(params: EquipmentApprovalListReq) {
|
||||
return http.get<EquipmentApprovalResp[]>('/equipment/approval/approved', params)
|
||||
// 确保分页参数格式正确,与设备采购模块保持一致
|
||||
const requestParams = {
|
||||
...params,
|
||||
// 直接使用 pageNum,因为后端分页器需要的是 pageNum 参数
|
||||
pageNum: params.pageNum || 1,
|
||||
pageSize: params.pageSize || 10,
|
||||
}
|
||||
|
||||
console.log('🔍 API - equipmentApprovalApi.getApprovedApprovals 被调用')
|
||||
console.log('🔍 API - 接收到的参数:', params)
|
||||
console.log('🔍 API - 最终请求参数:', requestParams)
|
||||
|
||||
return http.get<EquipmentApprovalResp[]>('/equipment/approval/approved', requestParams)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,8 +20,8 @@ export const equipmentProcurementApi = {
|
|||
// 确保参数格式正确
|
||||
const requestParams = {
|
||||
...params,
|
||||
// 确保分页参数存在
|
||||
page: params.page || 1,
|
||||
// 直接使用 pageNum,因为后端分页器需要的是 pageNum 参数
|
||||
pageNum: params.pageNum || 1,
|
||||
pageSize: params.pageSize || 10,
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import http from '@/utils/http'
|
||||
|
||||
/* 分页查询 */
|
||||
export function getVideoPage(params: {
|
||||
pageNo: number
|
||||
pageSize: number
|
||||
projectId: string
|
||||
turbineId?: string
|
||||
}) {
|
||||
return http.get('/video-monitor/page', params )
|
||||
}
|
||||
|
||||
/* 单文件上传 */
|
||||
export function uploadSingleVideo(
|
||||
projectId: string,
|
||||
turbineId: string,
|
||||
type: string,
|
||||
file: File
|
||||
) {
|
||||
const fd = new FormData()
|
||||
fd.append('file', file)
|
||||
return http.post(
|
||||
`/video-monitor/${projectId}/upload?turbineId=${turbineId}&type=${type}`,
|
||||
fd,
|
||||
{ headers: { 'Content-Type': 'multipart/form-data' } }
|
||||
)
|
||||
}
|
||||
|
||||
/* 批量上传 */
|
||||
export function uploadBatchVideo(
|
||||
projectId: string,
|
||||
turbineId: string,
|
||||
type: string,
|
||||
files: File[]
|
||||
) {
|
||||
const fd = new FormData()
|
||||
files.forEach(f => fd.append('files', f))
|
||||
return http.post(
|
||||
`/video-monitor/${projectId}/upload-batch?turbineId=${turbineId}&type=${type}`,
|
||||
fd,
|
||||
{ headers: { 'Content-Type': 'multipart/form-data' } }
|
||||
)
|
||||
}
|
||||
|
||||
/* 删除 */
|
||||
export function deleteVideo(videoId: string) {
|
||||
return http.del(`/video-monitor/${videoId}`)
|
||||
}
|
||||
|
||||
/* 下载 */
|
||||
export function downloadVideo(videoId: string) {
|
||||
return http
|
||||
.get(`/video-monitor/download/${videoId}`, { responseType: 'blob' })
|
||||
.then(blob => URL.createObjectURL(blob))
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
<template>
|
||||
<div class="circular-progress-container">
|
||||
<div class="circular-progress" :style="{ width: size + 'px', height: size + 'px' }">
|
||||
<svg class="circular-progress-svg" :width="size" :height="size">
|
||||
<!-- 背景圆环 -->
|
||||
<circle
|
||||
class="circular-progress-bg"
|
||||
:cx="center"
|
||||
:cy="center"
|
||||
:r="radius"
|
||||
:stroke-width="strokeWidth"
|
||||
fill="none"
|
||||
/>
|
||||
<!-- 进度圆环 -->
|
||||
<circle
|
||||
class="circular-progress-fill"
|
||||
:cx="center"
|
||||
:cy="center"
|
||||
:r="radius"
|
||||
:stroke-width="strokeWidth"
|
||||
fill="none"
|
||||
:stroke-dasharray="circumference"
|
||||
:stroke-dashoffset="strokeDashoffset"
|
||||
:style="{ stroke: progressColor }"
|
||||
/>
|
||||
</svg>
|
||||
<!-- 中心文本 -->
|
||||
<div class="circular-progress-text" v-if="showText">
|
||||
<span class="progress-percent">{{ percent }}%</span>
|
||||
<span class="progress-label" v-if="label">{{ label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface Props {
|
||||
percent: number
|
||||
size?: number
|
||||
strokeWidth?: number
|
||||
showText?: boolean
|
||||
label?: string
|
||||
color?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
size: 60,
|
||||
strokeWidth: 4,
|
||||
showText: true,
|
||||
label: '',
|
||||
color: ''
|
||||
})
|
||||
|
||||
// 计算圆环参数
|
||||
const center = computed(() => props.size / 2)
|
||||
const radius = computed(() => (props.size - props.strokeWidth) / 2)
|
||||
const circumference = computed(() => 2 * Math.PI * radius.value)
|
||||
const strokeDashoffset = computed(() => circumference.value - (props.percent / 100) * circumference.value)
|
||||
|
||||
// 进度颜色
|
||||
const progressColor = computed(() => {
|
||||
if (props.color) return props.color
|
||||
|
||||
if (props.percent >= 80) return '#52c41a'
|
||||
if (props.percent >= 60) return '#1890ff'
|
||||
if (props.percent >= 40) return '#faad14'
|
||||
if (props.percent > 0) return '#ff4d4f'
|
||||
return '#d9d9d9'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.circular-progress-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.circular-progress {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.circular-progress-svg {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.circular-progress-bg {
|
||||
stroke: #f0f2f5;
|
||||
}
|
||||
|
||||
.circular-progress-fill {
|
||||
transition: stroke-dashoffset 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
.circular-progress-text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.progress-percent {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1d2129;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
font-size: 10px;
|
||||
color: #86909c;
|
||||
margin-top: 2px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
// 不同尺寸的样式调整
|
||||
.circular-progress[style*="width: 40px"],
|
||||
.circular-progress[style*="width: 40px;"] {
|
||||
.progress-percent {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
.circular-progress[style*="width: 80px"],
|
||||
.circular-progress[style*="width: 80px;"] {
|
||||
.progress-percent {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
.circular-progress[style*="width: 100px"],
|
||||
.circular-progress[style*="width: 100px;"] {
|
||||
.progress-percent {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -70,6 +70,6 @@ declare global {
|
|||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
|
|
|
@ -7,69 +7,7 @@ export {}
|
|||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ApprovalAssistant: typeof import('./../components/ApprovalAssistant/index.vue')['default']
|
||||
ApprovalMessageItem: typeof import('./../components/NotificationCenter/ApprovalMessageItem.vue')['default']
|
||||
Avatar: typeof import('./../components/Avatar/index.vue')['default']
|
||||
Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default']
|
||||
CellCopy: typeof import('./../components/CellCopy/index.vue')['default']
|
||||
Chart: typeof import('./../components/Chart/index.vue')['default']
|
||||
ColumnSetting: typeof import('./../components/GiTable/src/components/ColumnSetting.vue')['default']
|
||||
CronForm: typeof import('./../components/GenCron/CronForm/index.vue')['default']
|
||||
CronModal: typeof import('./../components/GenCron/CronModal/index.vue')['default']
|
||||
DateRangePicker: typeof import('./../components/DateRangePicker/index.vue')['default']
|
||||
DayForm: typeof import('./../components/GenCron/CronForm/component/day-form.vue')['default']
|
||||
FilePreview: typeof import('./../components/FilePreview/index.vue')['default']
|
||||
GiCellAvatar: typeof import('./../components/GiCell/GiCellAvatar.vue')['default']
|
||||
GiCellGender: typeof import('./../components/GiCell/GiCellGender.vue')['default']
|
||||
GiCellStatus: typeof import('./../components/GiCell/GiCellStatus.vue')['default']
|
||||
GiCellTag: typeof import('./../components/GiCell/GiCellTag.vue')['default']
|
||||
GiCellTags: typeof import('./../components/GiCell/GiCellTags.vue')['default']
|
||||
GiCodeView: typeof import('./../components/GiCodeView/index.vue')['default']
|
||||
GiDot: typeof import('./../components/GiDot/index.tsx')['default']
|
||||
GiEditTable: typeof import('./../components/GiEditTable/GiEditTable.vue')['default']
|
||||
GiFooter: typeof import('./../components/GiFooter/index.vue')['default']
|
||||
GiForm: typeof import('./../components/GiForm/src/GiForm.vue')['default']
|
||||
GiIconBox: typeof import('./../components/GiIconBox/index.vue')['default']
|
||||
GiIconSelector: typeof import('./../components/GiIconSelector/index.vue')['default']
|
||||
GiIframe: typeof import('./../components/GiIframe/index.vue')['default']
|
||||
GiOption: typeof import('./../components/GiOption/index.vue')['default']
|
||||
GiOptionItem: typeof import('./../components/GiOptionItem/index.vue')['default']
|
||||
GiPageLayout: typeof import('./../components/GiPageLayout/index.vue')['default']
|
||||
GiSpace: typeof import('./../components/GiSpace/index.vue')['default']
|
||||
GiSplitButton: typeof import('./../components/GiSplitButton/index.vue')['default']
|
||||
GiSplitPane: typeof import('./../components/GiSplitPane/index.vue')['default']
|
||||
GiSplitPaneFlexibleBox: typeof import('./../components/GiSplitPane/components/GiSplitPaneFlexibleBox.vue')['default']
|
||||
GiSvgIcon: typeof import('./../components/GiSvgIcon/index.vue')['default']
|
||||
GiTable: typeof import('./../components/GiTable/src/GiTable.vue')['default']
|
||||
GiTag: typeof import('./../components/GiTag/index.tsx')['default']
|
||||
GiThemeBtn: typeof import('./../components/GiThemeBtn/index.vue')['default']
|
||||
HourForm: typeof import('./../components/GenCron/CronForm/component/hour-form.vue')['default']
|
||||
Icon403: typeof import('./../components/icons/Icon403.vue')['default']
|
||||
Icon404: typeof import('./../components/icons/Icon404.vue')['default']
|
||||
Icon500: typeof import('./../components/icons/Icon500.vue')['default']
|
||||
IconBorders: typeof import('./../components/icons/IconBorders.vue')['default']
|
||||
IconTableSize: typeof import('./../components/icons/IconTableSize.vue')['default']
|
||||
IconTreeAdd: typeof import('./../components/icons/IconTreeAdd.vue')['default']
|
||||
IconTreeReduce: typeof import('./../components/icons/IconTreeReduce.vue')['default']
|
||||
ImageImport: typeof import('./../components/ImageImport/index.vue')['default']
|
||||
ImageImportWizard: typeof import('./../components/ImageImportWizard/index.vue')['default']
|
||||
IndustrialImageList: typeof import('./../components/IndustrialImageList/index.vue')['default']
|
||||
JsonPretty: typeof import('./../components/JsonPretty/index.vue')['default']
|
||||
MinuteForm: typeof import('./../components/GenCron/CronForm/component/minute-form.vue')['default']
|
||||
MonthForm: typeof import('./../components/GenCron/CronForm/component/month-form.vue')['default']
|
||||
NotificationCenter: typeof import('./../components/NotificationCenter/index.vue')['default']
|
||||
ParentView: typeof import('./../components/ParentView/index.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SecondForm: typeof import('./../components/GenCron/CronForm/component/second-form.vue')['default']
|
||||
SplitPanel: typeof import('./../components/SplitPanel/index.vue')['default']
|
||||
TextCopy: typeof import('./../components/TextCopy/index.vue')['default']
|
||||
TurbineGrid: typeof import('./../components/TurbineGrid/index.vue')['default']
|
||||
UserSelect: typeof import('./../components/UserSelect/index.vue')['default']
|
||||
Verify: typeof import('./../components/Verify/index.vue')['default']
|
||||
VerifyPoints: typeof import('./../components/Verify/Verify/VerifyPoints.vue')['default']
|
||||
VerifySlide: typeof import('./../components/Verify/Verify/VerifySlide.vue')['default']
|
||||
WeekForm: typeof import('./../components/GenCron/CronForm/component/week-form.vue')['default']
|
||||
YearForm: typeof import('./../components/GenCron/CronForm/component/year-form.vue')['default']
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ export interface EquipmentPageQuery {
|
|||
invoice?: string
|
||||
barcode?: string
|
||||
importer?: string
|
||||
page?: number
|
||||
pageSize?: number
|
||||
pageNum?: number // 当前页码 - 与后端分页器期望的参数名保持一致
|
||||
pageSize?: number // 每页大小
|
||||
orderBy?: string
|
||||
orderDirection?: string
|
||||
}
|
||||
|
|
|
@ -1,407 +1,311 @@
|
|||
<template>
|
||||
<GiPageLayout>
|
||||
<div class="raw-data-container">
|
||||
<!-- <div class="page-header">
|
||||
<div class="page-title">原始数据管理</div>
|
||||
<div class="page-subtitle">管理和分析原始视频数据</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 顶部按钮 -->
|
||||
<div class="action-bar">
|
||||
<div class="action-buttons">
|
||||
<a-button type="primary" @click="showUploadModal = true">
|
||||
<a-button type="primary" @click="openUploadModal">
|
||||
<template #icon>
|
||||
<IconUpload />
|
||||
</template>
|
||||
上传视频
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleBatchAnalysis">
|
||||
<template #icon>
|
||||
<IconPlayCircle />
|
||||
</template>
|
||||
批量分析
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleExportData">
|
||||
<template #icon>
|
||||
<IconDownload />
|
||||
</template>
|
||||
导出数据
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="filter-section">
|
||||
<a-form :model="filterForm" layout="inline">
|
||||
<a-form-item label="项目">
|
||||
<a-select v-model="filterForm.projectId" placeholder="请选择项目">
|
||||
<a-option value="project-1">风电场A区</a-option>
|
||||
<a-option value="project-2">风电场B区</a-option>
|
||||
<a-option value="project-3">风电场C区</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="机组号">
|
||||
<a-input v-model="filterForm.unitNumber" placeholder="请输入机组号" />
|
||||
</a-form-item>
|
||||
<a-form-item label="状态">
|
||||
<a-select v-model="filterForm.status" placeholder="请选择状态">
|
||||
<a-option value="completed">已完成</a-option>
|
||||
<a-option value="pending">待分析</a-option>
|
||||
<a-option value="analyzing">分析中</a-option>
|
||||
<a-option value="failed">失败</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" @click="handleFilterChange">查询</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="project-sections">
|
||||
<div v-for="project in filteredProjects" :key="project.id" class="project-section">
|
||||
<div class="project-header">
|
||||
<div class="project-title">{{ project.name }}</div>
|
||||
<div class="project-stats">
|
||||
<div class="stat-item">
|
||||
<IconVideoCamera />
|
||||
{{ project.totalVideos }} 个视频
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<IconCheckCircle />
|
||||
{{ project.completedCount }} 个已完成
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<IconClockCircle />
|
||||
{{ project.pendingCount }} 个待分析
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="units-grid">
|
||||
<div v-for="unit in project.units" :key="unit.id" class="unit-card">
|
||||
<div class="unit-header">
|
||||
<div class="unit-title">{{ unit.number }}</div>
|
||||
<div class="unit-actions">
|
||||
<a-button type="primary" size="small" @click="handleViewUnitVideos(unit)">查看全部</a-button>
|
||||
<a-button type="primary" size="small" @click="handleAnalyzeUnit(unit)">{{
|
||||
getAnalysisButtonText(unit.status)
|
||||
}}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="videos-list">
|
||||
<div v-for="video in unit.videos" :key="video.id" class="video-item">
|
||||
<div class="video-thumbnail">
|
||||
<img :src="video.thumbnail" alt="Video Thumbnail" />
|
||||
<div class="video-overlay" @click="handlePlayVideo(video)">
|
||||
<IconPlayArrowFill style="font-size: 24px; color: #fff;" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-info">
|
||||
<div class="video-name">{{ video.name }}</div>
|
||||
<div class="video-meta">
|
||||
<span>{{ video.duration }}</span>
|
||||
<span>{{ video.angle }}°</span>
|
||||
</div>
|
||||
<div class="video-status">
|
||||
<a-tag :color="getStatusColor(video.status)">{{ getStatusText(video.status) }}</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analysis-progress">
|
||||
<div class="progress-info">
|
||||
<span>分析进度</span>
|
||||
<span>{{ unit.progress }}%</span>
|
||||
</div>
|
||||
<a-progress :percent="unit.progress" :show-text="false" status="active" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 筛选 -->
|
||||
<div class="filter-section">
|
||||
<a-form :model="filterForm" layout="inline">
|
||||
<a-form-item label="项目" required>
|
||||
<a-select v-model="filterForm.projectId" placeholder="请选择项目" :options="projectOptions" allow-clear
|
||||
@change="" />
|
||||
</a-form-item>
|
||||
<a-form-item label="机组">
|
||||
<a-select v-model="filterForm.turbineId" placeholder="请选择机组" :options="turbineOptions" allow-clear
|
||||
:disabled="!filterForm.projectId" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" @click="handleQuery">查询</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
|
||||
<!-- 视频播放模态框 -->
|
||||
<a-modal v-model:visible="videoModalVisible" title="原始视频播放" width="900px" @ok="videoModalVisible = false"
|
||||
@cancel="videoModalVisible = false">
|
||||
<video v-if="selectedVideo" :src="selectedVideo.url" controls
|
||||
style="width: 100%; height: 480px; border-radius: 8px; background: #000;"></video>
|
||||
<div v-if="selectedVideo" class="video-meta-info">
|
||||
<p>项目:{{ selectedVideo.projectName }}</p>
|
||||
<p>机组号:{{ selectedVideo.unitNumber }}</p>
|
||||
<p>采集人:{{ selectedVideo.collector }}</p>
|
||||
<p>风速:{{ selectedVideo.windSpeed }} m/s</p>
|
||||
<p>转速:{{ selectedVideo.rpm }} rpm</p>
|
||||
<p>采集时间:{{ selectedVideo.time }}</p>
|
||||
<p>角度:{{ selectedVideo.angle }}°</p>
|
||||
</div>
|
||||
</a-modal>
|
||||
<!-- 列表 -->
|
||||
<a-table :columns="columns" :data="tableData" :pagination="pagination" :loading="loading"
|
||||
:scroll="{ y: 'calc(100vh - 380px)' }">
|
||||
<template #type="{ record }">
|
||||
<a-tag>{{ record.type === 'clearance' ? '净空' : '形变' }}</a-tag>
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<a-tag :color="record.preTreatment ? 'green' : 'red'">
|
||||
{{ record.preTreatment ? '已处理' : '未处理' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a-button size="mini" @click="handlePreview(record)">预览</a-button>
|
||||
<a-button size="mini" @click="handleDownload(record)">下载</a-button>
|
||||
<a-popconfirm content="确认删除?" @ok="handleDelete(record)">
|
||||
<a-button size="mini" status="danger">删除</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
|
||||
<!-- 上传视频模态框 -->
|
||||
<a-modal v-model:visible="showUploadModal" title="上传原始视频" width="600px" @ok="handleUpload"
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- 上传弹窗 -->
|
||||
<a-modal v-model:visible="showUploadModal" title="上传原始视频" width="600px" :ok-loading="uploading" @ok="handleUpload"
|
||||
@cancel="showUploadModal = false">
|
||||
<a-form :model="uploadForm" layout="vertical">
|
||||
<a-form-item label="项目" required>
|
||||
<a-select v-model="uploadForm.projectId" placeholder="请选择项目">
|
||||
<a-option value="project-1">风电场A区</a-option>
|
||||
<a-option value="project-2">风电场B区</a-option>
|
||||
<a-option value="project-3">风电场C区</a-option>
|
||||
</a-select>
|
||||
<a-select v-model="uploadForm.projectId" placeholder="请选择项目" :options="projectOptions" allow-clear
|
||||
@change="onProjectChangeUpload" />
|
||||
</a-form-item>
|
||||
<a-form-item label="机组号" required>
|
||||
<a-input v-model="uploadForm.unitNumber" placeholder="请输入机组号" />
|
||||
<a-form-item label="机组">
|
||||
<a-select v-model="uploadForm.turbineId" placeholder="请选择机组" :options="turbineOptionsUpload" allow-clear
|
||||
:disabled="!uploadForm.projectId" />
|
||||
</a-form-item>
|
||||
<a-form-item label="采集人" required>
|
||||
<a-input v-model="uploadForm.collector" placeholder="请输入采集人姓名" />
|
||||
<a-form-item label="类型" required>
|
||||
<a-select v-model="uploadForm.type" placeholder="请选择类型" :options="typeOptions" />
|
||||
</a-form-item>
|
||||
<a-form-item label="风速 (m/s)">
|
||||
<a-input-number v-model="uploadForm.windSpeed" :min="0" />
|
||||
<a-form-item label="视频文件" required>
|
||||
<a-upload v-model:file-list="uploadForm.fileList" :multiple="uploadMode === 'batch'"
|
||||
:limit="uploadMode === 'batch' ? 10 : 1" accept="video/*" :auto-upload="false" list-type="picture-card" />
|
||||
</a-form-item>
|
||||
<a-form-item label="转速 (rpm)">
|
||||
<a-input-number v-model="uploadForm.rpm" :min="0" />
|
||||
</a-form-item>
|
||||
<a-form-item label="采集时间" required>
|
||||
<a-date-picker v-model="uploadForm.time" show-time format="YYYY-MM-DD HH:mm" style="width: 100%;" />
|
||||
</a-form-item>
|
||||
<a-form-item label="视频文件(可多选,建议3个角度)" required>
|
||||
<a-upload v-model:file-list="uploadForm.fileList" :multiple="true" :limit="3" accept="video/*"
|
||||
:auto-upload="false" list-type="picture-card">
|
||||
<template #upload-button>
|
||||
<a-button>选择视频</a-button>
|
||||
</template>
|
||||
</a-upload>
|
||||
<a-form-item>
|
||||
<a-radio-group v-model="uploadMode" type="button">
|
||||
<a-radio value="single">单文件</a-radio>
|
||||
<a-radio value="batch">批量</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</GiPageLayout>
|
||||
<!-- 视频预览弹窗 -->
|
||||
<a-modal v-model:visible="previewVisible" title="视频预览" width="800px" :footer="false" @cancel="previewVisible = false">
|
||||
<a-tabs v-model:active-key="activePreviewTab" @change="activePreviewTab = $event as any">
|
||||
<!-- 原始视频 -->
|
||||
<a-tab-pane key="video" title="原始视频">
|
||||
<video v-if="previewUrl" :src="previewUrl" controls
|
||||
style="width: 100%; max-height: 60vh; border-radius: 4px"></video>
|
||||
</a-tab-pane>
|
||||
|
||||
<!-- 处理结果 -->
|
||||
<a-tab-pane key="result" title="处理结果">
|
||||
<a-spin :loading="loadingResult">
|
||||
<a-space direction="vertical" size="medium" style="width: 100%">
|
||||
<!-- 图片 -->
|
||||
<img v-if="resultImgUrl" :src="resultImgUrl" style="max-width: 100%; border-radius: 4px" alt="last frame" />
|
||||
|
||||
<!-- JSON 预览 -->
|
||||
<a-card title="results.json" size="small">
|
||||
<pre>{{ JSON.stringify(resultJson, null, 2) }}</pre>
|
||||
</a-card>
|
||||
</a-space>
|
||||
</a-spin>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { ref, reactive, onMounted, watch } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import type { TableColumnData } from '@arco-design/web-vue'
|
||||
import { IconUpload } from '@arco-design/web-vue/es/icon'
|
||||
import {
|
||||
IconUpload,
|
||||
IconPlayCircle,
|
||||
IconDownload,
|
||||
IconVideoCamera,
|
||||
IconCheckCircle,
|
||||
IconClockCircle,
|
||||
IconPlayArrowFill
|
||||
} from '@arco-design/web-vue/es/icon'
|
||||
getProjectList,
|
||||
getTurbineList
|
||||
} from '@/apis/industrial-image'
|
||||
import {
|
||||
getVideoPage,
|
||||
uploadBatchVideo,
|
||||
uploadSingleVideo,
|
||||
deleteVideo,
|
||||
downloadVideo
|
||||
} from '@/apis/video-monitor'
|
||||
|
||||
const showUploadModal = ref(false)
|
||||
const videoModalVisible = ref(false)
|
||||
const selectedVideo = ref<any>(null)
|
||||
/* ---------------- 下拉 & 表单 ---------------- */
|
||||
const projectOptions = ref<{ label: string; value: string }[]>([])
|
||||
const turbineOptions = ref<{ label: string; value: string }[]>([]) // 筛选用
|
||||
const turbineOptionsUpload = ref<{ label: string; value: string }[]>([]) // 上传弹窗用
|
||||
const typeOptions = [
|
||||
{ label: '净空', value: 'clearance' },
|
||||
{ label: '形变', value: 'deformation' }
|
||||
]
|
||||
|
||||
const filterForm = reactive({
|
||||
projectId: '',
|
||||
unitNumber: '',
|
||||
status: ''
|
||||
turbineId: ''
|
||||
})
|
||||
|
||||
const uploadForm = reactive({
|
||||
projectId: '',
|
||||
unitNumber: '',
|
||||
collector: '',
|
||||
windSpeed: null,
|
||||
rpm: null,
|
||||
time: '',
|
||||
fileList: []
|
||||
turbineId: '',
|
||||
type: '',
|
||||
fileList: [] as any[]
|
||||
})
|
||||
|
||||
// 示例数据结构
|
||||
const projects = ref([
|
||||
{
|
||||
id: 'project-1',
|
||||
name: '风电场A区',
|
||||
totalVideos: 6,
|
||||
completedCount: 4,
|
||||
pendingCount: 2,
|
||||
units: [
|
||||
{
|
||||
id: 'A-001',
|
||||
number: 'A-001',
|
||||
status: 'completed',
|
||||
progress: 100,
|
||||
videos: [
|
||||
{
|
||||
id: 'v1',
|
||||
name: 'A-001-正面',
|
||||
url: '/videos/A-001-front.mp4',
|
||||
thumbnail: '/images/A-001-front.jpg',
|
||||
angle: 0,
|
||||
duration: '00:30',
|
||||
status: 'completed',
|
||||
projectName: '风电场A区',
|
||||
unitNumber: 'A-001',
|
||||
collector: '张三',
|
||||
windSpeed: 8.2,
|
||||
rpm: 15,
|
||||
time: '2023-11-05 08:00'
|
||||
},
|
||||
{
|
||||
id: 'v2',
|
||||
name: 'A-001-侧面',
|
||||
url: '/videos/A-001-side.mp4',
|
||||
thumbnail: '/images/A-001-side.jpg',
|
||||
angle: 90,
|
||||
duration: '00:30',
|
||||
status: 'completed',
|
||||
projectName: '风电场A区',
|
||||
unitNumber: 'A-001',
|
||||
collector: '张三',
|
||||
windSpeed: 8.2,
|
||||
rpm: 15,
|
||||
time: '2023-11-05 08:00'
|
||||
},
|
||||
{
|
||||
id: 'v3',
|
||||
name: 'A-001-背面',
|
||||
url: '/videos/A-001-back.mp4',
|
||||
thumbnail: '/images/A-001-back.jpg',
|
||||
angle: 180,
|
||||
duration: '00:30',
|
||||
status: 'pending',
|
||||
projectName: '风电场A区',
|
||||
unitNumber: 'A-001',
|
||||
collector: '张三',
|
||||
windSpeed: 8.2,
|
||||
rpm: 15,
|
||||
time: '2023-11-05 08:00'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'A-002',
|
||||
number: 'A-002',
|
||||
status: 'analyzing',
|
||||
progress: 60,
|
||||
videos: [
|
||||
{
|
||||
id: 'v4',
|
||||
name: 'A-002-正面',
|
||||
url: '/videos/A-002-front.mp4',
|
||||
thumbnail: '/images/A-002-front.jpg',
|
||||
angle: 0,
|
||||
duration: '00:28',
|
||||
status: 'analyzing',
|
||||
projectName: '风电场A区',
|
||||
unitNumber: 'A-002',
|
||||
collector: '李四',
|
||||
windSpeed: 7.9,
|
||||
rpm: 14,
|
||||
time: '2023-11-05 12:00'
|
||||
},
|
||||
{
|
||||
id: 'v5',
|
||||
name: 'A-002-侧面',
|
||||
url: '/videos/A-002-side.mp4',
|
||||
thumbnail: '/images/A-002-side.jpg',
|
||||
angle: 90,
|
||||
duration: '00:28',
|
||||
status: 'pending',
|
||||
projectName: '风电场A区',
|
||||
unitNumber: 'A-002',
|
||||
collector: '李四',
|
||||
windSpeed: 7.9,
|
||||
rpm: 14,
|
||||
time: '2023-11-05 12:00'
|
||||
},
|
||||
{
|
||||
id: 'v6',
|
||||
name: 'A-002-背面',
|
||||
url: '/videos/A-002-back.mp4',
|
||||
thumbnail: '/images/A-002-back.jpg',
|
||||
angle: 180,
|
||||
duration: '00:28',
|
||||
status: 'pending',
|
||||
projectName: '风电场A区',
|
||||
unitNumber: 'A-002',
|
||||
collector: '李四',
|
||||
windSpeed: 7.9,
|
||||
rpm: 14,
|
||||
time: '2023-11-05 12:00'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
// ... 其他项目
|
||||
])
|
||||
const uploadMode = ref<'single' | 'batch'>('single')
|
||||
|
||||
const filteredProjects = computed(() => {
|
||||
// 按筛选条件过滤
|
||||
return projects.value
|
||||
.filter(p => !filterForm.projectId || p.id === filterForm.projectId)
|
||||
.map(project => ({
|
||||
...project,
|
||||
units: project.units
|
||||
.filter(u => !filterForm.unitNumber || u.number === filterForm.unitNumber)
|
||||
.map(unit => ({
|
||||
...unit,
|
||||
videos: unit.videos.filter(v => !filterForm.status || v.status === filterForm.status)
|
||||
}))
|
||||
.filter(u => u.videos.length > 0)
|
||||
}))
|
||||
.filter(p => p.units.length > 0)
|
||||
/* ---------------- 列表 ---------------- */
|
||||
const columns: TableColumnData[] = [
|
||||
{ title: '文件名', dataIndex: 'videoName', ellipsis: true, width: 220 },
|
||||
// { title: '项目', dataIndex: 'projectName' },
|
||||
// { title: '机组', dataIndex: 'turbineName' },
|
||||
{ title: '类型', slotName: 'type' },
|
||||
{ title: '上传时间', dataIndex: 'uploadTime' },
|
||||
{ title: '状态', slotName: 'status' },
|
||||
{ title: '操作', slotName: 'action', width: 120, fixed: 'right' }
|
||||
]
|
||||
|
||||
const tableData = ref<any[]>([])
|
||||
const loading = ref(false)
|
||||
const pagination = reactive({ current: 1, pageSize: 20, total: 0 })
|
||||
|
||||
/* ---------------- 控制弹窗 ---------------- */
|
||||
const showUploadModal = ref(false)
|
||||
const uploading = ref(false)
|
||||
|
||||
/* ---------------- 初始化 ---------------- */
|
||||
onMounted(async () => {
|
||||
const { data } = await getProjectList({ page: 1, pageSize: 1000 })
|
||||
projectOptions.value = data.map((p: any) => ({ label: p.projectName, value: p.projectId }))
|
||||
handleQuery()
|
||||
})
|
||||
const activePreviewTab = ref<'video' | 'result'>('video') // 当前标签页
|
||||
const resultImgUrl = ref('')
|
||||
const resultJson = ref<Record<string, any>>({})
|
||||
const loadingResult = ref(false)
|
||||
|
||||
function handleFilterChange() {
|
||||
// 这里可以加API请求
|
||||
async function loadResultFiles(row: any) {
|
||||
if (!row.preTreatment) return
|
||||
loadingResult.value = true
|
||||
try {
|
||||
const base = import.meta.env.VITE_API_BASE_URL.replace(/\/+$/, '')
|
||||
// 图片
|
||||
resultImgUrl.value = `${base}${row.preImagePath}/last_frame.jpg`
|
||||
|
||||
// JSON
|
||||
const jsonUrl = `${base}${row.preImagePath}/results.json`
|
||||
const res = await fetch(jsonUrl)
|
||||
resultJson.value = await res.json()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
resultJson.value = {}
|
||||
} finally {
|
||||
loadingResult.value = false
|
||||
}
|
||||
console.log('result', resultImgUrl.value)
|
||||
}
|
||||
/* 项目 -> 机组(筛选) */
|
||||
watch(
|
||||
() => filterForm.projectId,
|
||||
async (val) => {
|
||||
filterForm.turbineId = ''
|
||||
turbineOptions.value = []
|
||||
if (!val) return
|
||||
const { data } = await getTurbineList({ projectId: val })
|
||||
turbineOptions.value = data.map((t: any) => ({ label: t.turbineName, value: t.turbineId }))
|
||||
}
|
||||
)
|
||||
const previewVisible = ref(false)
|
||||
const previewUrl = ref('')
|
||||
|
||||
function handlePlayVideo(video: any) {
|
||||
selectedVideo.value = video
|
||||
videoModalVisible.value = true
|
||||
}
|
||||
|
||||
function handleViewUnitVideos(unit: any) {
|
||||
// 可扩展为弹窗或跳转页面,展示该机组所有视频
|
||||
Message.info(`查看机组 ${unit.number} 的所有视频`)
|
||||
}
|
||||
|
||||
function handleAnalyzeUnit(unit: any) {
|
||||
// 触发分析流程
|
||||
Message.success(`已提交机组 ${unit.number} 的分析任务`)
|
||||
// 实际应调用API并刷新状态
|
||||
}
|
||||
|
||||
function getStatusColor(status: string) {
|
||||
switch (status) {
|
||||
case 'completed': return 'green'
|
||||
case 'pending': return 'gray'
|
||||
case 'analyzing': return 'blue'
|
||||
case 'failed': return 'red'
|
||||
default: return 'gray'
|
||||
function handlePreview(row: any) {
|
||||
const base = import.meta.env.VITE_API_BASE_URL.replace(/\/+$/, '')
|
||||
previewUrl.value = new URL(row.videoPath.replace(/^\/+/, ''), base).href
|
||||
previewVisible.value = true
|
||||
activePreviewTab.value = 'video' // 默认先显示视频
|
||||
if (row.preTreatment) {
|
||||
loadResultFiles(row) // 预加载结果
|
||||
}
|
||||
}
|
||||
function getStatusText(status: string) {
|
||||
switch (status) {
|
||||
case 'completed': return '已完成'
|
||||
case 'pending': return '待分析'
|
||||
case 'analyzing': return '分析中'
|
||||
case 'failed': return '失败'
|
||||
default: return '未知'
|
||||
}
|
||||
/* 项目 -> 机组(上传弹窗) */
|
||||
async function onProjectChangeUpload(projectId: string) {
|
||||
uploadForm.turbineId = ''
|
||||
turbineOptionsUpload.value = []
|
||||
if (!projectId) return
|
||||
const { data } = await getTurbineList({ projectId })
|
||||
turbineOptionsUpload.value = data.map((t: any) => ({ label: t.turbineName, value: t.turbineId }))
|
||||
}
|
||||
function getAnalysisButtonText(status: string) {
|
||||
switch (status) {
|
||||
case 'completed': return '重新分析'
|
||||
case 'pending': return '分析'
|
||||
case 'analyzing': return '分析中...'
|
||||
case 'failed': return '重新分析'
|
||||
default: return '分析'
|
||||
|
||||
/* ---------------- 查询 ---------------- */
|
||||
function handleQuery() {
|
||||
pagination.current = 1
|
||||
loadTable()
|
||||
}
|
||||
async function loadTable() {
|
||||
loading.value = true
|
||||
try {
|
||||
const params = {
|
||||
pageNo: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
projectId: filterForm.projectId,
|
||||
turbineId: filterForm.turbineId || undefined
|
||||
}
|
||||
const { data } = await getVideoPage(params)
|
||||
console.log(data)
|
||||
tableData.value = data
|
||||
pagination.total = data.length
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleBatchAnalysis() {
|
||||
Message.success('批量分析任务已提交')
|
||||
/* ---------------- 上传 ---------------- */
|
||||
function openUploadModal() {
|
||||
uploadForm.projectId = ''
|
||||
uploadForm.turbineId = ''
|
||||
uploadForm.type = ''
|
||||
uploadForm.fileList = []
|
||||
showUploadModal.value = true
|
||||
}
|
||||
function handleExportData() {
|
||||
Message.success('数据导出成功')
|
||||
async function handleUpload() {
|
||||
if (!uploadForm.projectId || !uploadForm.type || !uploadForm.fileList.length) {
|
||||
Message.warning('请完整填写')
|
||||
return
|
||||
}
|
||||
uploading.value = true
|
||||
try {
|
||||
const files = uploadForm.fileList.map((f: any) => f.file)
|
||||
if (uploadMode.value === 'single') {
|
||||
await uploadSingleVideo(
|
||||
uploadForm.projectId,
|
||||
uploadForm.turbineId || '',
|
||||
uploadForm.type,
|
||||
files[0]
|
||||
)
|
||||
} else {
|
||||
await uploadBatchVideo(
|
||||
uploadForm.projectId,
|
||||
uploadForm.turbineId || '',
|
||||
uploadForm.type,
|
||||
files
|
||||
)
|
||||
}
|
||||
Message.success('上传成功')
|
||||
showUploadModal.value = false
|
||||
loadTable()
|
||||
} finally {
|
||||
uploading.value = false
|
||||
}
|
||||
}
|
||||
function handleUpload() {
|
||||
Message.success('上传成功')
|
||||
showUploadModal.value = false
|
||||
|
||||
/* ---------------- 下载 / 删除 ---------------- */
|
||||
async function handleDownload(row: any) {
|
||||
const url = await downloadVideo(row.videoId)
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
async function handleDelete(row: any) {
|
||||
await deleteVideo(row.videoId)
|
||||
Message.success('删除成功')
|
||||
loadTable()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped lang="scss">
|
||||
.raw-data-container {
|
||||
padding: 20px;
|
||||
|
|
|
@ -117,8 +117,9 @@
|
|||
:columns="columns"
|
||||
:data="tableData"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:pagination="false"
|
||||
row-key="approvalId"
|
||||
:scroll="{ x: 'max-content', y: 400 }"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<!-- 业务类型 -->
|
||||
|
@ -193,6 +194,23 @@
|
|||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- 分页器 - 固定在表格下方 -->
|
||||
<div class="pagination-container">
|
||||
<a-pagination
|
||||
v-model:current="pagination.current"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:show-total="true"
|
||||
:show-jumper="true"
|
||||
:show-page-size="true"
|
||||
:page-size-options="[10, 20, 50, 100]"
|
||||
:hide-on-single-page="false"
|
||||
size="default"
|
||||
@change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 审批详情弹窗 -->
|
||||
|
@ -249,6 +267,7 @@ const pagination = reactive<any>({
|
|||
showPageSize: true,
|
||||
showJumper: true,
|
||||
showTotal: (total: number) => `共 ${total} 条记录`,
|
||||
pageSizeOptions: [10, 20, 50, 100]
|
||||
})
|
||||
|
||||
// 弹窗控制
|
||||
|
@ -460,8 +479,8 @@ const loadData = async (searchParams?: EquipmentApprovalListReq) => {
|
|||
try {
|
||||
// 构建完整的请求参数 - 参考设备采购功能的实现
|
||||
const params: EquipmentApprovalListReq = {
|
||||
pageNum: pagination.current, // 修改为 pageNum,与后端期望的参数名一致
|
||||
pageSize: pagination.pageSize,
|
||||
page: pagination.current,
|
||||
...(searchParams || {}),
|
||||
}
|
||||
|
||||
|
@ -478,46 +497,52 @@ const loadData = async (searchParams?: EquipmentApprovalListReq) => {
|
|||
|
||||
console.log('API响应:', res)
|
||||
|
||||
// 处理不同的响应格式
|
||||
let dataList: any[] = []
|
||||
let total = 0
|
||||
if (res.code === 200 || res.success || res.status === 200) {
|
||||
let dataList: any[] = []
|
||||
let totalCount = 0
|
||||
|
||||
if (res && res.data) {
|
||||
if (Array.isArray(res.data)) {
|
||||
// 检查不同的数据字段 - 后端返回的是 PageResult 格式
|
||||
if ((res as any).rows && Array.isArray((res as any).rows)) {
|
||||
// 后端返回的是 PageResult 格式,数据在 rows 字段中
|
||||
dataList = (res as any).rows
|
||||
totalCount = (res as any).total || 0
|
||||
console.log('从 rows 字段获取数据,总数:', totalCount)
|
||||
} else if (Array.isArray(res.data)) {
|
||||
dataList = res.data
|
||||
total = res.data.length
|
||||
totalCount = (res as any).total || dataList.length || 0
|
||||
console.log('从 data 字段获取数据,总数:', totalCount)
|
||||
} else if (res.data && Array.isArray((res.data as any).records)) {
|
||||
dataList = (res.data as any).records
|
||||
total = (res.data as any).total || 0
|
||||
totalCount = (res.data as any).total || dataList.length || 0
|
||||
console.log('从 records 字段获取数据,总数:', totalCount)
|
||||
} else if (res.data && Array.isArray((res.data as any).list)) {
|
||||
dataList = (res.data as any).list
|
||||
total = (res.data as any).total || 0
|
||||
} else if (res.data && Array.isArray((res.data as any).rows)) {
|
||||
dataList = (res.data as any).rows
|
||||
total = (res.data as any).total || 0
|
||||
} else if (res.data && Array.isArray((res.data as any).data)) {
|
||||
dataList = (res.data as any).data
|
||||
total = (res.data as any).total || 0
|
||||
totalCount = (res.data as any).total || dataList.length || 0
|
||||
console.log('从 list 字段获取数据,总数:', totalCount)
|
||||
} else {
|
||||
console.warn('未找到有效的数据字段,响应结构:', res)
|
||||
dataList = []
|
||||
totalCount = 0
|
||||
}
|
||||
} else if (Array.isArray(res)) {
|
||||
dataList = res
|
||||
total = res.length
|
||||
}
|
||||
|
||||
console.log('处理后的数据列表:', dataList)
|
||||
console.log('总数:', total)
|
||||
if (dataList.length > 0) {
|
||||
const transformedData = transformBackendData(dataList)
|
||||
tableData.value = transformedData
|
||||
console.log('数据转换完成,设置到表格:', transformedData.length, '条')
|
||||
} else {
|
||||
tableData.value = []
|
||||
console.log('没有数据,清空表格')
|
||||
}
|
||||
|
||||
if (dataList.length > 0) {
|
||||
const transformedData = transformBackendData(dataList)
|
||||
console.log('转换后的数据:', transformedData)
|
||||
tableData.value = transformedData
|
||||
// 设置总数 - 优先使用后端返回的总数
|
||||
pagination.total = totalCount
|
||||
console.log('设置分页总数:', totalCount)
|
||||
} else {
|
||||
console.log('没有数据,设置空数组')
|
||||
console.error('请求失败,响应:', res)
|
||||
message.error(res.msg || (res as any).message || '加载数据失败')
|
||||
tableData.value = []
|
||||
pagination.total = 0
|
||||
}
|
||||
|
||||
pagination.total = total
|
||||
console.log('设置分页总数:', pagination.total)
|
||||
} catch (error: any) {
|
||||
console.error('加载数据失败:', error)
|
||||
message.error(error?.message || '加载数据失败')
|
||||
|
@ -525,7 +550,6 @@ const loadData = async (searchParams?: EquipmentApprovalListReq) => {
|
|||
pagination.total = 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
console.log('📊 loadData - 加载完成')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,6 +576,21 @@ const handleTableChange = (pag: any) => {
|
|||
loadData(currentSearchParams.value || {})
|
||||
}
|
||||
|
||||
// 分页变化处理
|
||||
const handlePageChange = (page: number) => {
|
||||
console.log('页码变化:', page)
|
||||
pagination.current = page
|
||||
loadData(currentSearchParams.value || {})
|
||||
}
|
||||
|
||||
// 每页条数变化处理
|
||||
const handlePageSizeChange = (pageSize: number) => {
|
||||
console.log('每页条数变化:', pageSize)
|
||||
pagination.pageSize = pageSize
|
||||
pagination.current = 1 // 重置到第一页
|
||||
loadData(currentSearchParams.value || {})
|
||||
}
|
||||
|
||||
// 标签页变化
|
||||
const handleTabChange = (key: string) => {
|
||||
activeTab.value = key
|
||||
|
@ -781,6 +820,42 @@ onMounted(() => {
|
|||
color: var(--color-text-4);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
// 分页器容器样式 - 固定在表格下方
|
||||
.pagination-container {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
background: white;
|
||||
padding: 16px 24px;
|
||||
border-top: 1px solid var(--color-border);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
z-index: 10;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
|
||||
|
||||
.arco-pagination {
|
||||
margin: 0;
|
||||
|
||||
.arco-pagination-item {
|
||||
border-radius: 6px;
|
||||
margin: 0 4px;
|
||||
|
||||
&.arco-pagination-item-active {
|
||||
background: var(--color-primary);
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.arco-pagination-size-changer {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.arco-pagination-jumper {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 确保搜索区域有良好的对比度
|
||||
.approval-search-container {
|
||||
|
|
|
@ -117,9 +117,13 @@
|
|||
:columns="columns"
|
||||
:data="tableData"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:pagination="false"
|
||||
row-key="equipmentId"
|
||||
@change="handleTableChange"
|
||||
:scroll="{ x: 'max-content', y: 400 }"
|
||||
:bordered="false"
|
||||
:stripe="true"
|
||||
size="medium"
|
||||
table-layout="auto"
|
||||
>
|
||||
<template #equipmentType="{ record }">
|
||||
<a-tag :color="getEquipmentTypeColor(record.equipmentType)">
|
||||
|
@ -205,6 +209,23 @@
|
|||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- 分页器 - 固定在表格下方 -->
|
||||
<div class="pagination-container">
|
||||
<a-pagination
|
||||
v-model:current="pagination.current"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:show-total="true"
|
||||
:show-jumper="true"
|
||||
:show-page-size="true"
|
||||
:page-size-options="[10, 20, 50, 100]"
|
||||
:hide-on-single-page="false"
|
||||
size="default"
|
||||
@change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
|
@ -247,13 +268,18 @@ const tableData = ref<EquipmentResp[]>([])
|
|||
const loading = ref(false)
|
||||
|
||||
// 分页配置
|
||||
const pagination = reactive<any>({
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showPageSize: true,
|
||||
showTotal: true,
|
||||
showJumper: true,
|
||||
showTotal: (total: number) => `共 ${total} 条记录`,
|
||||
showPageSize: true,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
// 确保分页器完全可见
|
||||
hideOnSinglePage: false,
|
||||
// 分页器大小
|
||||
size: 'default'
|
||||
})
|
||||
|
||||
// 弹窗控制
|
||||
|
@ -568,46 +594,101 @@ const transformBackendData = (data: any[]) => {
|
|||
const loadData = async (searchParams?: EquipmentPageQuery) => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 构建请求参数,确保分页参数正确
|
||||
const params = {
|
||||
pageSize: pagination.pageSize,
|
||||
page: pagination.current,
|
||||
pageNum: pagination.current, // 当前页码 - 使用pageNum匹配后端期望
|
||||
pageSize: pagination.pageSize, // 每页条数
|
||||
...(searchParams || {}), // 添加搜索条件
|
||||
}
|
||||
|
||||
console.log('🚀 发送分页请求参数:', params)
|
||||
console.log('📊 当前分页状态:', {
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
total: pagination.total
|
||||
})
|
||||
|
||||
const res = await EquipmentAPI.pageEquipment(params)
|
||||
|
||||
console.log('📡 后端响应数据:', res)
|
||||
|
||||
// 兼容不同的响应格式
|
||||
if (res.success || res.status === 200 || res.code === 200) {
|
||||
if (res.code === 200 || res.success || res.status === 200) {
|
||||
// 处理数据,确保数据格式正确
|
||||
let dataList: any[] = []
|
||||
let totalCount = 0
|
||||
|
||||
// 检查不同的数据字段
|
||||
if (Array.isArray(res.data)) {
|
||||
dataList = res.data
|
||||
} else if (res.data && Array.isArray((res.data as any).records)) {
|
||||
dataList = (res.data as any).records
|
||||
} else if (res.data && Array.isArray((res.data as any).list)) {
|
||||
dataList = (res.data as any).list
|
||||
} else if (res.data && Array.isArray((res.data as any).rows)) {
|
||||
dataList = (res.data as any).rows
|
||||
console.log('🔍 开始解析响应数据,响应结构:', res)
|
||||
|
||||
// 检查不同的数据字段 - 后端返回的是 PageResult 格式
|
||||
if ((res as any).rows && Array.isArray((res as any).rows)) {
|
||||
// 后端返回的是 PageResult 格式,数据在 rows 字段中
|
||||
dataList = (res as any).rows
|
||||
totalCount = (res as any).total || 0
|
||||
console.log('✅ 从 rows 字段获取数据,总数:', totalCount, '当前页数据:', dataList.length)
|
||||
} else if ((res as any).data && Array.isArray((res as any).data)) {
|
||||
dataList = (res as any).data
|
||||
totalCount = (res as any).total || dataList.length || 0
|
||||
console.log('✅ 从 data 字段获取数据,总数:', totalCount, '当前页数据:', dataList.length)
|
||||
} else if ((res as any).data && (res as any).data.rows && Array.isArray((res as any).data.rows)) {
|
||||
// 嵌套结构:res.data.rows 和 res.data.total
|
||||
dataList = (res as any).data.rows
|
||||
totalCount = (res as any).data.total || 0
|
||||
console.log('✅ 从嵌套 data.rows 字段获取数据,总数:', totalCount, '当前页数据:', dataList.length)
|
||||
} else if ((res as any).data && Array.isArray(((res as any).data as any).records)) {
|
||||
dataList = ((res as any).data as any).records
|
||||
totalCount = (((res as any).data as any).total) || dataList.length || 0
|
||||
console.log('✅ 从 records 字段获取数据,总数:', totalCount, '当前页数据:', dataList.length)
|
||||
} else if ((res as any).data && Array.isArray(((res as any).data as any).list)) {
|
||||
dataList = ((res as any).data as any).list
|
||||
totalCount = (((res as any).data as any).total) || dataList.length || 0
|
||||
console.log('✅ 从 list 字段获取数据,总数:', totalCount, '当前页数据:', dataList.length)
|
||||
} else {
|
||||
console.warn('⚠️ 未找到有效的数据字段,响应结构:', res)
|
||||
dataList = []
|
||||
totalCount = 0
|
||||
}
|
||||
|
||||
// 临时解决方案:如果后端没有返回total但有数据,使用当前页数据长度作为总数
|
||||
if (dataList.length > 0 && totalCount === 0) {
|
||||
totalCount = dataList.length
|
||||
console.log('⚠️ 后端未返回total,使用当前页数据长度作为总数:', totalCount)
|
||||
}
|
||||
|
||||
// 如果后端返回了数据但total为0,说明后端分页逻辑有问题
|
||||
if (dataList.length > 0 && totalCount === 0) {
|
||||
console.warn('🚨 后端分页逻辑问题:返回了数据但total为0,使用当前页数据长度作为总数')
|
||||
totalCount = dataList.length
|
||||
}
|
||||
|
||||
if (dataList.length > 0) {
|
||||
const transformedData = transformBackendData(dataList)
|
||||
|
||||
tableData.value = transformedData
|
||||
console.log('✅ 数据转换完成,设置到表格:', transformedData.length, '条')
|
||||
} else {
|
||||
tableData.value = []
|
||||
console.log('ℹ️ 没有数据,清空表格')
|
||||
}
|
||||
|
||||
// 设置总数
|
||||
pagination.total = (res.data as any)?.total || (res as any).total || dataList.length || 0
|
||||
// 设置总数 - 优先使用后端返回的总数
|
||||
pagination.total = totalCount
|
||||
console.log('📊 设置分页总数:', totalCount)
|
||||
console.log('📊 分页组件状态:', {
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
total: pagination.total
|
||||
})
|
||||
} else {
|
||||
message.error(res.msg || '加载数据失败')
|
||||
console.error('❌ 请求失败,响应:', res)
|
||||
message.error((res as any).msg || (res as any).message || '加载数据失败')
|
||||
tableData.value = []
|
||||
pagination.total = 0
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('加载数据失败:', error)
|
||||
console.error('❌ 加载数据失败:', error)
|
||||
message.error(error?.message || '加载数据失败')
|
||||
tableData.value = []
|
||||
pagination.total = 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
@ -631,11 +712,42 @@ const handleReset = () => {
|
|||
|
||||
// 表格变化
|
||||
const handleTableChange = (pag: any) => {
|
||||
pagination.current = pag.current || 1
|
||||
pagination.pageSize = pag.pageSize || 10
|
||||
console.log('表格变化,分页参数:', pag) // 添加调试日志
|
||||
|
||||
// 更新分页配置
|
||||
if (pag.current !== undefined) {
|
||||
pagination.current = pag.current
|
||||
}
|
||||
if (pag.pageSize !== undefined) {
|
||||
pagination.pageSize = pag.pageSize
|
||||
}
|
||||
|
||||
console.log('更新分页配置:', {
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize
|
||||
})
|
||||
|
||||
// 重新加载数据
|
||||
loadData(currentSearchParams.value)
|
||||
}
|
||||
|
||||
// 分页变化处理
|
||||
const handlePageChange = (page: number) => {
|
||||
console.log('页码变化:', page)
|
||||
pagination.current = page
|
||||
loadData(currentSearchParams.value)
|
||||
}
|
||||
|
||||
// 每页条数变化处理
|
||||
const handlePageSizeChange = (pageSize: number) => {
|
||||
console.log('每页条数变化:', pageSize)
|
||||
pagination.pageSize = pageSize
|
||||
pagination.current = 1 // 重置到第一页
|
||||
loadData(currentSearchParams.value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
modalMode.value = 'add'
|
||||
|
@ -920,7 +1032,8 @@ onMounted(() => {
|
|||
.table-card {
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
overflow: visible; // 改为 visible,允许内容溢出
|
||||
min-height: 600px; // 设置最小高度,确保有足够的滚动空间
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
|
@ -943,6 +1056,11 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.arco-table {
|
||||
// 确保表格可以正常滚动
|
||||
.arco-table-container {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.arco-table-th {
|
||||
background: var(--color-fill-2);
|
||||
font-weight: 600;
|
||||
|
@ -956,6 +1074,12 @@ onMounted(() => {
|
|||
.arco-table-tr:hover {
|
||||
background: var(--color-fill-1);
|
||||
}
|
||||
|
||||
// 确保表格可以正常滚动
|
||||
.arco-table-body {
|
||||
overflow-y: auto;
|
||||
max-height: 500px; // 设置最大高度,确保可以滚动
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -999,18 +1123,38 @@ onMounted(() => {
|
|||
}
|
||||
}
|
||||
|
||||
// 分页样式优化
|
||||
.arco-pagination {
|
||||
margin-top: 24px;
|
||||
justify-content: center;
|
||||
// 分页器容器样式 - 固定在表格下方
|
||||
.pagination-container {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
background: white;
|
||||
padding: 16px 24px;
|
||||
border-top: 1px solid var(--color-border);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
z-index: 10;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
|
||||
|
||||
.arco-pagination-item {
|
||||
border-radius: 6px;
|
||||
margin: 0 4px;
|
||||
.arco-pagination {
|
||||
margin: 0;
|
||||
|
||||
&.arco-pagination-item-active {
|
||||
background: var(--color-primary);
|
||||
border-color: var(--color-primary);
|
||||
.arco-pagination-item {
|
||||
border-radius: 6px;
|
||||
margin: 0 4px;
|
||||
|
||||
&.arco-pagination-item-active {
|
||||
background: var(--color-primary);
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.arco-pagination-size-changer {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.arco-pagination-jumper {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,9 +116,10 @@
|
|||
:columns="columns"
|
||||
:data="tableData"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:pagination="false"
|
||||
:row-selection="rowSelection"
|
||||
row-key="equipmentId"
|
||||
:scroll="{ x: 'max-content', y: 400 }"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<!-- 设备状态 -->
|
||||
|
@ -218,6 +219,23 @@
|
|||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- 分页器 - 固定在表格下方 -->
|
||||
<div class="pagination-container">
|
||||
<a-pagination
|
||||
v-model:current="pagination.current"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:show-total="true"
|
||||
:show-jumper="true"
|
||||
:show-page-size="true"
|
||||
:page-size-options="[10, 20, 50, 100]"
|
||||
:hide-on-single-page="false"
|
||||
size="default"
|
||||
@change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
|
@ -277,6 +295,7 @@ const pagination = reactive<any>({
|
|||
showPageSize: true,
|
||||
showJumper: true,
|
||||
showTotal: (total: number) => `共 ${total} 条记录`,
|
||||
pageSizeOptions: [10, 20, 50, 100]
|
||||
})
|
||||
|
||||
// 弹窗控制
|
||||
|
@ -592,31 +611,52 @@ const loadData = async (searchParams?: EquipmentListReq) => {
|
|||
|
||||
loading.value = true
|
||||
try {
|
||||
// 构建请求参数,确保分页参数正确
|
||||
const params: EquipmentListReq = {
|
||||
pageSize: pagination.pageSize,
|
||||
page: pagination.current,
|
||||
pageNum: pagination.current, // 当前页码
|
||||
pageSize: pagination.pageSize, // 每页条数
|
||||
minPrice: undefined,
|
||||
maxPrice: undefined,
|
||||
...(searchParams || {}),
|
||||
}
|
||||
|
||||
console.log('📊 loadData - 构建的完整请求参数:', params)
|
||||
console.log('📊 当前分页状态:', {
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
total: pagination.total
|
||||
})
|
||||
|
||||
const res = await equipmentProcurementApi.page(params)
|
||||
|
||||
console.log('API响应:', res)
|
||||
|
||||
if (res.success || res.status === 200 || res.code === 200) {
|
||||
if (res.code === 200 || res.success || res.status === 200) {
|
||||
let dataList: any[] = []
|
||||
let totalCount = 0
|
||||
|
||||
if (Array.isArray(res.data)) {
|
||||
// 检查不同的数据字段 - 后端返回的是 PageResult 格式
|
||||
if ((res as any).rows && Array.isArray((res as any).rows)) {
|
||||
// 后端返回的是 PageResult 格式,数据在 rows 字段中
|
||||
dataList = (res as any).rows
|
||||
totalCount = (res as any).total || 0
|
||||
console.log('✅ 从 rows 字段获取数据,总数:', totalCount, '当前页数据:', dataList.length)
|
||||
} else if (Array.isArray(res.data)) {
|
||||
dataList = res.data
|
||||
totalCount = (res as any).total || dataList.length || 0
|
||||
console.log('✅ 从 data 字段获取数据,总数:', totalCount, '当前页数据:', dataList.length)
|
||||
} else if (res.data && Array.isArray((res.data as any).records)) {
|
||||
dataList = (res.data as any).records
|
||||
totalCount = (res.data as any).total || dataList.length || 0
|
||||
console.log('✅ 从 records 字段获取数据,总数:', totalCount, '当前页数据:', dataList.length)
|
||||
} else if (res.data && Array.isArray((res.data as any).list)) {
|
||||
dataList = (res.data as any).list
|
||||
} else if (res.data && Array.isArray((res.data as any).rows)) {
|
||||
dataList = (res.data as any).rows
|
||||
totalCount = (res.data as any).total || dataList.length || 0
|
||||
console.log('✅ 从 list 字段获取数据,总数:', totalCount, '当前页数据:', dataList.length)
|
||||
} else {
|
||||
console.warn('⚠️ 未找到有效的数据字段,响应结构:', res)
|
||||
dataList = []
|
||||
totalCount = 0
|
||||
}
|
||||
|
||||
console.log('处理后的数据列表:', dataList)
|
||||
|
@ -633,14 +673,25 @@ const loadData = async (searchParams?: EquipmentListReq) => {
|
|||
tableData.value = []
|
||||
}
|
||||
|
||||
pagination.total = (res.data as any)?.total || (res as any).total || dataList.length || 0
|
||||
console.log('总数:', pagination.total)
|
||||
// 设置总数 - 优先使用后端返回的总数
|
||||
pagination.total = totalCount
|
||||
console.log('📊 总数:', totalCount)
|
||||
console.log('📊 分页组件状态:', {
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
total: pagination.total
|
||||
})
|
||||
} else {
|
||||
message.error(res.msg || '加载数据失败')
|
||||
console.error('❌ 请求失败,响应:', res)
|
||||
message.error(res.msg || (res as any).message || '加载数据失败')
|
||||
tableData.value = []
|
||||
pagination.total = 0
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('加载数据失败:', error)
|
||||
console.error('❌ 加载数据失败:', error)
|
||||
message.error(error?.message || '加载数据失败')
|
||||
tableData.value = []
|
||||
pagination.total = 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
@ -672,6 +723,21 @@ const handleTableChange = (pag: any) => {
|
|||
loadData(currentSearchParams.value)
|
||||
}
|
||||
|
||||
// 分页变化处理
|
||||
const handlePageChange = (page: number) => {
|
||||
console.log('页码变化:', page)
|
||||
pagination.current = page
|
||||
loadData(currentSearchParams.value)
|
||||
}
|
||||
|
||||
// 每页条数变化处理
|
||||
const handlePageSizeChange = (pageSize: number) => {
|
||||
console.log('每页条数变化:', pageSize)
|
||||
pagination.pageSize = pageSize
|
||||
pagination.current = 1 // 重置到第一页
|
||||
loadData(currentSearchParams.value)
|
||||
}
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
modalMode.value = 'add'
|
||||
|
@ -958,6 +1024,42 @@ onMounted(() => {
|
|||
color: var(--color-text-4);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
// 分页器容器样式 - 固定在表格下方
|
||||
.pagination-container {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
background: white;
|
||||
padding: 16px 24px;
|
||||
border-top: 1px solid var(--color-border);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
z-index: 10;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
|
||||
|
||||
.arco-pagination {
|
||||
margin: 0;
|
||||
|
||||
.arco-pagination-item {
|
||||
border-radius: 6px;
|
||||
margin: 0 4px;
|
||||
|
||||
&.arco-pagination-item-active {
|
||||
background: var(--color-primary);
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.arco-pagination-size-changer {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.arco-pagination-jumper {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
|
|
Loading…
Reference in New Issue