566 lines
14 KiB
Vue
566 lines
14 KiB
Vue
<template>
|
||
<view class="padding page">
|
||
<!-- 查询条件 -->
|
||
<view class="bg-white radius padding margin-top search-card">
|
||
<view class="section-header">
|
||
<image class="section-icon" src="/static/yujin/yujin_sousuo.png" mode="aspectFit"></image>
|
||
<view class="text-black text-bold">查询条件</view>
|
||
</view>
|
||
<!-- 日期选择行 -->
|
||
<view class="date-row margin-top">
|
||
<view class="date-item">
|
||
<view class="date-label">开始日期</view>
|
||
<view class="date-picker" @click="showStartDatePicker = true">
|
||
<text :class="searchForm.startDate ? 'date-value' : 'date-placeholder'">
|
||
{{ searchForm.startDate || '请选择' }}
|
||
</text>
|
||
<text class="cuIcon-unfold"></text>
|
||
</view>
|
||
<up-datetime-picker
|
||
:show="showStartDatePicker"
|
||
v-model="startDateValue"
|
||
mode="date"
|
||
@confirm="onStartDateConfirm"
|
||
@cancel="showStartDatePicker = false"
|
||
@close="showStartDatePicker = false"
|
||
></up-datetime-picker>
|
||
</view>
|
||
<view class="date-item">
|
||
<view class="date-label">结束日期</view>
|
||
<view class="date-picker" @click="showEndDatePicker = true">
|
||
<text :class="searchForm.endDate ? 'date-value' : 'date-placeholder'">
|
||
{{ searchForm.endDate || '请选择' }}
|
||
</text>
|
||
<text class="cuIcon-unfold"></text>
|
||
</view>
|
||
<up-datetime-picker
|
||
:show="showEndDatePicker"
|
||
v-model="endDateValue"
|
||
mode="date"
|
||
@confirm="onEndDateConfirm"
|
||
@cancel="showEndDatePicker = false"
|
||
@close="showEndDatePicker = false"
|
||
></up-datetime-picker>
|
||
</view>
|
||
</view>
|
||
<view class="margin-top">
|
||
<view class="date-label">公司名称</view>
|
||
<up-input v-model="searchForm.deptName" placeholder="请输入公司名称" border="surround"></up-input>
|
||
</view>
|
||
<button class="search-btn" @click="handleSearch">查询</button>
|
||
</view>
|
||
|
||
<!-- 统计概览 -->
|
||
<view class="padding bg-white radius margin-top">
|
||
<view class="section-header">
|
||
<image class="section-icon" src="/static/yujin/yujin_tongji.png" mode="aspectFit"></image>
|
||
<view class="text-bold text-black">统计概览</view>
|
||
</view>
|
||
<view class="stat-grid margin-top">
|
||
<view class="stat-item stat-total">
|
||
<view class="stat-num">{{ statistics.total }}</view>
|
||
<view class="stat-label">总计</view>
|
||
</view>
|
||
<view class="stat-item stat-overdue">
|
||
<view class="stat-num">{{ statistics.overdue }}</view>
|
||
<view class="stat-label">逾期</view>
|
||
</view>
|
||
<view class="stat-item stat-completed">
|
||
<view class="stat-num">{{ statistics.onTimeCompleted }}</view>
|
||
<view class="stat-label">已完成</view>
|
||
</view>
|
||
<view class="stat-item stat-pending">
|
||
<view class="stat-num">{{ statistics.completed }}</view>
|
||
<view class="stat-label">待处理</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 数据列表标题 + 状态筛选 -->
|
||
<view class="bg-white radius padding-top padding-left padding-right margin-top margin-bottom">
|
||
<view class="flex align-center" style="margin-bottom: 20rpx;">
|
||
<view class="list-title-bar"></view>
|
||
<view class="text-bold text-black">日常安全检查预警数据列表</view>
|
||
</view>
|
||
<scroll-view scroll-x :show-scrollbar="false">
|
||
<view class="status-tabs-inner">
|
||
<view v-for="(tab, index) in statusTabs" :key="index" class="status-tab-item"
|
||
:class="{ 'status-tab-active': activeStatusTab === index }" @click="switchStatusTab(index)">
|
||
<text class="status-tab-text">{{ tab.label }}{{ tab.count != null ? tab.count : '' }}</text>
|
||
<view v-if="activeStatusTab === index" class="status-tab-bar"></view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 数据列表 -->
|
||
<view v-for="(item, index) in dataList" :key="item.id" class="list-card">
|
||
<!-- 卡片头部:企业名称 + 状态标签 -->
|
||
<view class="card-title-row">
|
||
<view class="card-company-name">{{ item.deptName || '-' }}</view>
|
||
<view class="card-status-tag" :class="getStatusClass(item.overdueDays)">
|
||
<text>{{ getStatusText(item.overdueDays, item.statusName) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="card-body">
|
||
<view class="card-row">
|
||
<view class="row-label">计划名称:</view>
|
||
<view class="row-value">{{ item.planName || '-' }}</view>
|
||
</view>
|
||
<view class="card-row">
|
||
<view class="row-label">计划周期:</view>
|
||
<view class="row-value">{{ item.cycleName || '-' }}</view>
|
||
</view>
|
||
<view class="card-row">
|
||
<view class="row-label">预约检查日期:</view>
|
||
<view class="row-value">{{ item.taskDate || '-' }}</view>
|
||
</view>
|
||
<view class="card-row">
|
||
<view class="row-label">实际完成时间:</view>
|
||
<view class="row-value">{{ item.finishTime || '未完成' }}</view>
|
||
</view>
|
||
<view class="card-row">
|
||
<view class="row-label">负责人:</view>
|
||
<view class="row-value">{{ item.executorName || '-' }}</view>
|
||
</view>
|
||
<view class="card-row">
|
||
<view class="row-label">逾期天数:</view>
|
||
<view class="row-value">{{ item.overdueDays || '-' }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载更多 -->
|
||
<u-loadmore :status="loadStatus" v-if="dataList.length > 0" style="margin-top: 20rpx; margin-bottom: 20rpx;" />
|
||
|
||
<!-- 空状态 -->
|
||
<view v-if="dataList.length === 0" class="empty-tip">
|
||
<text>暂无数据</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive, onMounted } from 'vue'
|
||
import { onShow, onReachBottom } from '@dcloudio/uni-app'
|
||
import { getInspectionWarningList } from '@/request/api.js'
|
||
|
||
// 搜索表单
|
||
const searchForm = reactive({
|
||
startDate: '',
|
||
endDate: '',
|
||
deptName: ''
|
||
})
|
||
|
||
// 日期选择器
|
||
const showStartDatePicker = ref(false)
|
||
const showEndDatePicker = ref(false)
|
||
const startDateValue = ref(Number(new Date()))
|
||
const endDateValue = ref(Number(new Date()))
|
||
|
||
// 统计数据
|
||
const statistics = reactive({ total: 0, overdue: 0, pending: 0, completed: 0, overdueCompleted: 0, onTimeCompleted: 0 }); const _old_stats = reactive({
|
||
total: 0,
|
||
overdue: 0,
|
||
completed: 0,
|
||
pending: 0
|
||
})
|
||
|
||
// 列表数据
|
||
const dataList = ref([])
|
||
const pageNum = ref(1)
|
||
const pageSize = ref(20)
|
||
const loadStatus = ref('loadmore')
|
||
|
||
// 状态筛选 Tab
|
||
const statusTabs = ref([
|
||
{ label: '全部状态', value: 0, count: null },
|
||
{ label: '逾期未检', value: 1, count: null },
|
||
{ label: '严重逾期', value: 2, count: null },
|
||
{ label: '期限内待检', value: 3, count: null },
|
||
{ label: '逾期已完成', value: 4, count: null },
|
||
{ label: '按期已完成', value: 5, count: null }
|
||
])
|
||
const activeStatusTab = ref(0)
|
||
|
||
const switchStatusTab = (index) => { activeStatusTab.value = index; pageNum.value = 1; dataList.value = []; fetchData(); }; const _unused_tab = (index) => {
|
||
activeStatusTab.value = index
|
||
pageNum.value = 1
|
||
fetchData()
|
||
}
|
||
|
||
// 日期格式化(仅日期,用于页面显示)
|
||
const formatDate = (timestamp) => {
|
||
const date = new Date(timestamp)
|
||
const year = date.getFullYear()
|
||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||
const day = String(date.getDate()).padStart(2, '0')
|
||
return `${year}-${month}-${day}`
|
||
}
|
||
|
||
// 日期选择确认(开始日期补 00:00:00)
|
||
const onStartDateConfirm = (e) => {
|
||
const dateStr = formatDate(e.value)
|
||
searchForm.startDate = dateStr
|
||
showStartDatePicker.value = false
|
||
}
|
||
|
||
// 日期选择确认(结束日期补 23:59:59)
|
||
const onEndDateConfirm = (e) => {
|
||
const dateStr = formatDate(e.value)
|
||
searchForm.endDate = dateStr
|
||
showEndDatePicker.value = false
|
||
}
|
||
|
||
// 获取状态样式类
|
||
const getStatusClass = (overdueDays) => {
|
||
if (!overdueDays || overdueDays === '按期') {
|
||
return 'status-normal' // 按期/期限内
|
||
}
|
||
const days = parseInt(overdueDays)
|
||
if (days >= 7) {
|
||
return 'status-serious' // 严重逾期(红色)
|
||
} else if (days >= 1) {
|
||
return 'status-overdue' // 逾期(橙色)
|
||
}
|
||
return 'status-normal'
|
||
}
|
||
|
||
// 获取状态文本
|
||
const getStatusText = (overdueDays, statusName) => {
|
||
if (!overdueDays || overdueDays === '按期') {
|
||
return statusName === '已完成' ? '按期已完成' : '期限内待检'
|
||
}
|
||
const days = parseInt(overdueDays)
|
||
if (days >= 7) {
|
||
return '严重逾期'
|
||
} else if (days >= 1) {
|
||
return statusName === '已完成' ? '逾期已完成' : '逾期未检'
|
||
}
|
||
return '期限内待检'
|
||
}
|
||
|
||
// 获取数据
|
||
const fetchData = async () => {
|
||
try {
|
||
const params = { pageNum: pageNum.value, pageSize: pageSize.value }
|
||
if (searchForm.startDate) params.startDate = searchForm.startDate
|
||
if (searchForm.endDate) params.endDate = searchForm.endDate
|
||
if (searchForm.deptName && searchForm.deptName.trim()) params.deptName = searchForm.deptName.trim()
|
||
const statusValue = statusTabs.value[activeStatusTab.value].value
|
||
if (statusValue !== 0) params.inspectionStatus = statusValue
|
||
const res = await getInspectionWarningList(params)
|
||
if (res.code === 0) {
|
||
if (res.data.statistics) {
|
||
statistics.total = res.data.statistics.total || 0
|
||
statistics.overdue = res.data.statistics.overdue || 0
|
||
statistics.pending = res.data.statistics.pending || 0
|
||
statistics.completed = res.data.statistics.completed || 0
|
||
statistics.overdueCompleted = res.data.statistics.overdueCompleted || 0
|
||
statistics.onTimeCompleted = res.data.statistics.onTimeCompleted || 0
|
||
statusTabs.value[0].count = res.data.statistics.total || 0
|
||
statusTabs.value[1].count = res.data.statistics.overdue || 0
|
||
statusTabs.value[2].count = res.data.statistics.pending || 0
|
||
statusTabs.value[3].count = res.data.statistics.completed || 0
|
||
statusTabs.value[4].count = res.data.statistics.overdueCompleted || 0
|
||
statusTabs.value[5].count = res.data.statistics.onTimeCompleted || 0
|
||
}
|
||
if (res.data.page && res.data.page.records) {
|
||
const records = res.data.page.records
|
||
if (pageNum.value === 1) {
|
||
dataList.value = records
|
||
} else {
|
||
dataList.value = [...dataList.value, ...records]
|
||
}
|
||
const totalRecords = res.data.page.total || 0
|
||
if (dataList.value.length >= totalRecords) {
|
||
loadStatus.value = 'nomore'
|
||
} else {
|
||
loadStatus.value = 'loadmore'
|
||
}
|
||
} else {
|
||
loadStatus.value = 'nomore'
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取预警列表失败:', error)
|
||
}
|
||
}
|
||
|
||
// 搜索
|
||
const handleSearch = () => {
|
||
pageNum.value = 1
|
||
dataList.value = []
|
||
fetchData()
|
||
}
|
||
|
||
// 监听触底上拉加载更多
|
||
onReachBottom(() => {
|
||
if (loadStatus.value === 'loadmore') {
|
||
pageNum.value++
|
||
fetchData()
|
||
}
|
||
})
|
||
|
||
// 页面加载
|
||
onShow(() => {
|
||
fetchData()
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.page {
|
||
min-height: 100vh;
|
||
background: #EBF2FC;
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
// 区块标题
|
||
.section-header {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.section-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
margin-right: 12rpx;
|
||
}
|
||
}
|
||
|
||
// 搜索卡片
|
||
.search-card {
|
||
.date-row {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
|
||
.date-item {
|
||
flex: 1;
|
||
}
|
||
}
|
||
|
||
.date-label {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.date-picker {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
height: 72rpx;
|
||
padding: 0 20rpx;
|
||
background: #f8f8f8;
|
||
border-radius: 8rpx;
|
||
border: 1rpx solid #eee;
|
||
|
||
.date-value {
|
||
color: #333;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.date-placeholder {
|
||
color: #999;
|
||
font-size: 28rpx;
|
||
}
|
||
}
|
||
|
||
.search-btn {
|
||
margin-top: 30rpx;
|
||
background: linear-gradient(135deg, #667eea 0%, #2667E9 100%);
|
||
color: #fff;
|
||
border-radius: 40rpx;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
font-size: 30rpx;
|
||
}
|
||
}
|
||
|
||
// 统计卡片
|
||
.stat-grid {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 16rpx;
|
||
|
||
.stat-item {
|
||
flex: 1;
|
||
height: 124rpx;
|
||
border-radius: 12rpx;
|
||
color: #fff;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.stat-num {
|
||
font-size: 40rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 24rpx;
|
||
margin-top: 8rpx;
|
||
}
|
||
}
|
||
|
||
.stat-total {
|
||
background: linear-gradient(135deg, #628EFB 0%, #4A7CF7 100%);
|
||
}
|
||
|
||
.stat-overdue {
|
||
background: linear-gradient(135deg, #32DCC7 0%, #20C5B0 100%);
|
||
}
|
||
|
||
.stat-completed {
|
||
background: linear-gradient(135deg, #32D1E9 0%, #20B8D0 100%);
|
||
}
|
||
|
||
.stat-pending {
|
||
background: linear-gradient(135deg, #A190F5 0%, #8B78E8 100%);
|
||
}
|
||
}
|
||
|
||
// 列表标题
|
||
.list-title-bar {
|
||
width: 8rpx;
|
||
height: 32rpx;
|
||
background: #2667E9;
|
||
border-radius: 4rpx;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
// 数据卡片
|
||
.list-card {
|
||
background: #FFFFFF;
|
||
box-shadow: 0rpx 2rpx 10rpx rgba(0, 0, 0, 0.08);
|
||
border-radius: 16rpx;
|
||
padding: 0;
|
||
margin-bottom: 20rpx;
|
||
overflow: hidden;
|
||
|
||
.card-title-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
background: linear-gradient(135deg, #4A7CF7 0%, #2667E9 100%);
|
||
padding: 24rpx 30rpx;
|
||
}
|
||
|
||
.card-company-name {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #fff;
|
||
flex: 1;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.card-status-tag {
|
||
padding: 8rpx 24rpx;
|
||
border-radius: 8rpx;
|
||
white-space: nowrap;
|
||
flex-shrink: 0;
|
||
font-size: 24rpx;
|
||
color: #fff;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.card-body {
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.card-row {
|
||
display: flex;
|
||
margin-top: 16rpx;
|
||
font-size: 28rpx;
|
||
line-height: 1.5;
|
||
|
||
&:first-of-type {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.row-label {
|
||
color: #999;
|
||
white-space: nowrap;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.row-value {
|
||
color: #333;
|
||
word-break: break-all;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 严重逾期 - 红色
|
||
.status-serious {
|
||
background: linear-gradient(135deg, #FF6B6B 0%, #EE5A5A 100%);
|
||
}
|
||
|
||
// 逾期 - 橙色
|
||
.status-overdue {
|
||
background: linear-gradient(135deg, #FFA726 0%, #FF9800 100%);
|
||
}
|
||
|
||
// 按期/正常 - 绿色
|
||
.status-normal {
|
||
background: linear-gradient(135deg, #66BB6A 0%, #4CAF50 100%);
|
||
}
|
||
|
||
// 逾期已完成 - 蓝色
|
||
.status-completed {
|
||
background: linear-gradient(135deg, #42A5F5 0%, #2196F3 100%);
|
||
}
|
||
|
||
// 空状态
|
||
.empty-tip {
|
||
text-align: center;
|
||
padding: 100rpx 0;
|
||
color: #999;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
// 状态筛选 Tab 样式
|
||
.status-tabs {
|
||
white-space: nowrap;
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 20rpx;
|
||
padding: 0 10rpx;
|
||
}
|
||
|
||
.status-tabs-inner {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
height: 88rpx;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.status-tab-item {
|
||
display: inline-flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0 24rpx;
|
||
height: 88rpx;
|
||
position: relative;
|
||
}
|
||
|
||
.status-tab-text {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.status-tab-active .status-tab-text {
|
||
color: #2667E9;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.status-tab-bar {
|
||
position: absolute;
|
||
bottom: 8rpx;
|
||
width: 40rpx;
|
||
height: 6rpx;
|
||
background: #2667E9;
|
||
border-radius: 3rpx;
|
||
}
|
||
</style> |