413 lines
10 KiB
Vue
413 lines
10 KiB
Vue
<template>
|
||
<view class=" page padding">
|
||
<!-- 顶部状态筛选 Tab -->
|
||
<scroll-view class="status-tabs" 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': activeTab === index }" @click="switchStatusTab(index)">
|
||
<text class="status-tab-text">{{ tab.label }}</text>
|
||
<view v-if="activeTab === index" class="status-tab-bar"></view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<view v-if="hiddenDangerList.length === 0 && !listLoading" class="empty-tip text-gray text-center padding">暂无数据</view>
|
||
|
||
<view class="padding radius bg-white list-list margin-bottom" v-for="item in hiddenDangerList"
|
||
:key="item.hazardId">
|
||
<view class="flex justify-between margin-bottom">
|
||
<view class="text-bold text-black" style="word-break: break-all; flex: 1;">{{item.title}}</view>
|
||
<view class="text-blue" style="white-space: nowrap; flex-shrink: 0; margin-left: 16rpx;">{{item.statusName}}</view>
|
||
</view>
|
||
<view class="flex margin-bottom">
|
||
<view class="text-gray">隐患等级:</view>
|
||
<view class="level-tag" :class="{
|
||
'level-minor': item.levelName === '轻微隐患',
|
||
'level-normal': item.levelName === '一般隐患',
|
||
'level-major': item.levelName === '重大隐患'
|
||
}">{{item.levelName}}</view>
|
||
</view>
|
||
<view class="flex margin-bottom">
|
||
<view class="text-gray" style="white-space: nowrap;">隐患位置:</view>
|
||
<view class="text-black">{{item.address}}</view>
|
||
</view>
|
||
<view class="flex margin-bottom">
|
||
<view class="text-gray">创建时间:</view>
|
||
<view class="text-black">{{item.createdAt}}</view>
|
||
</view>
|
||
<view class="flex justify-end card-actions" style="gap: 10rpx;">
|
||
<button class="round cu-btn light bg-blue" @click="details(item)">查看详情</button>
|
||
<button v-if="item.statusName === '待整改' && item.canEdit"
|
||
class="round cu-btn light bg-blue" @click="assignHazard(item)">隐患交办</button>
|
||
<button v-if="item.statusName === '待整改' && item.canEdit"
|
||
class="round cu-btn bg-blue" @click="Rectification(item)">立即整改</button>
|
||
<button v-if="item.statusName === '待验收' && item.canEdit"
|
||
class="round cu-btn light bg-blue" @click="editRectification(item)">编辑整改信息</button>
|
||
<button v-if="canShowAcceptanceButton(item, userRole)"
|
||
class="round cu-btn bg-blue" @click="acceptance(item)">立即验收</button>
|
||
<button v-if="item.statusName === '待交办'"
|
||
class="round cu-btn bg-blue" @click="assignHazard(item)">隐患交办</button>
|
||
<button v-if="canShowWriteoffApplyButton(item)"
|
||
class="round cu-btn bg-blue" @click="goWriteoffApply(item)">销号申请</button>
|
||
<button v-if="canShowWriteoffApprovalButton(item, userRole)"
|
||
class="round cu-btn bg-blue" @click="goWriteoffApproval(item)">销号审批</button>
|
||
</view>
|
||
</view>
|
||
|
||
<u-loadmore
|
||
v-if="hiddenDangerList.length > 0"
|
||
:status="loadStatus"
|
||
style="margin-top: 20rpx; margin-bottom: 140rpx;"
|
||
/>
|
||
|
||
<view class="fixed-add-btn" @click="goToAdd">
|
||
<text class="cuIcon-add"></text>
|
||
<text>新增</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue'
|
||
import { onLoad, onShow, onReachBottom } from '@dcloudio/uni-app'
|
||
import {
|
||
enterCheckPlan,
|
||
getCheckTaskDetail,
|
||
getMyHiddenDangerList
|
||
} from '@/request/api.js'
|
||
import {
|
||
buildRectificationUrl,
|
||
buildEditRectificationUrl,
|
||
buildAssignmentUrl,
|
||
buildAcceptanceUrl,
|
||
buildWriteoffApplyUrl,
|
||
buildWriteoffApprovalUrl,
|
||
canShowAcceptanceButton,
|
||
canShowWriteoffApplyButton,
|
||
canShowWriteoffApprovalButton
|
||
} from '@/utils/hazardNav.js'
|
||
import { resolveUserRoleKey } from '@/utils/userInfo.js'
|
||
|
||
const taskId = ref('')
|
||
const checkPointId = ref('')
|
||
const oneTableId = ref('')
|
||
|
||
const userRole = ref('')
|
||
|
||
const getUserRole = () => {
|
||
try {
|
||
const userInfoStr = uni.getStorageSync('userInfo')
|
||
if (userInfoStr) {
|
||
userRole.value = resolveUserRoleKey(JSON.parse(userInfoStr))
|
||
}
|
||
} catch (error) {
|
||
console.error('获取用户信息失败:', error)
|
||
}
|
||
}
|
||
|
||
const fetchTaskInfo = async (oneTableId) => {
|
||
try {
|
||
const startRes = await enterCheckPlan(oneTableId)
|
||
if (startRes.code === 0 && startRes.data) {
|
||
const tid = startRes.data.taskId
|
||
const detailRes = await getCheckTaskDetail(tid)
|
||
if (detailRes.code === 0 && detailRes.data) {
|
||
taskId.value = detailRes.data.taskId
|
||
checkPointId.value = detailRes.data.checkPointId
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取任务信息失败:', error)
|
||
}
|
||
}
|
||
|
||
onLoad((options) => {
|
||
getUserRole()
|
||
if (options.id) {
|
||
oneTableId.value = options.id
|
||
fetchTaskInfo(options.id)
|
||
}
|
||
})
|
||
|
||
const HAZARD_PAGE_SIZE = 10
|
||
const hiddenDangerList = ref([])
|
||
const pageNum = ref(1)
|
||
const listLoading = ref(false)
|
||
const loadStatus = ref('loadmore')
|
||
|
||
const statusTabs = ref([
|
||
{ label: '全部', value: null },
|
||
{ label: '待交办', value: 1 },
|
||
{ label: '待整改', value: 2 },
|
||
{ label: '待验收', value: 3 },
|
||
{ label: '待销号', value: 4 },
|
||
{ label: '已完成', value: 5 }
|
||
])
|
||
const activeTab = ref(0)
|
||
|
||
const buildListParams = () => {
|
||
const params = {
|
||
pageNum: pageNum.value,
|
||
pageSize: HAZARD_PAGE_SIZE
|
||
}
|
||
const currentTab = statusTabs.value[activeTab.value]
|
||
if (currentTab?.value != null) {
|
||
params.status = currentTab.value
|
||
}
|
||
return params
|
||
}
|
||
|
||
const resetHiddenDangerList = () => {
|
||
pageNum.value = 1
|
||
hiddenDangerList.value = []
|
||
loadStatus.value = 'loadmore'
|
||
}
|
||
|
||
const switchStatusTab = (index) => {
|
||
if (activeTab.value === index) return
|
||
activeTab.value = index
|
||
resetHiddenDangerList()
|
||
fetchHiddenDangerList()
|
||
}
|
||
|
||
const fetchHiddenDangerList = async () => {
|
||
if (listLoading.value) return
|
||
if (pageNum.value > 1 && loadStatus.value === 'nomore') return
|
||
|
||
listLoading.value = true
|
||
if (pageNum.value > 1) {
|
||
loadStatus.value = 'loading'
|
||
}
|
||
try {
|
||
const res = await getMyHiddenDangerList(buildListParams())
|
||
if (res.code === 0) {
|
||
const records = res.data?.records || []
|
||
const total = Number(res.data?.total ?? 0)
|
||
if (pageNum.value === 1) {
|
||
hiddenDangerList.value = records
|
||
} else {
|
||
hiddenDangerList.value = [...hiddenDangerList.value, ...records]
|
||
}
|
||
if (hiddenDangerList.value.length >= total || records.length < HAZARD_PAGE_SIZE) {
|
||
loadStatus.value = 'nomore'
|
||
} else {
|
||
loadStatus.value = 'loadmore'
|
||
}
|
||
} else {
|
||
if (pageNum.value === 1) {
|
||
hiddenDangerList.value = []
|
||
}
|
||
loadStatus.value = 'nomore'
|
||
uni.showToast({
|
||
title: res.msg || '获取隐患列表失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.error(error)
|
||
if (pageNum.value > 1) {
|
||
pageNum.value--
|
||
}
|
||
loadStatus.value = 'loadmore'
|
||
uni.showToast({
|
||
title: '请求失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
listLoading.value = false
|
||
}
|
||
}
|
||
|
||
const loadMoreHiddenDangerList = () => {
|
||
if (loadStatus.value !== 'loadmore' || listLoading.value) return
|
||
pageNum.value++
|
||
fetchHiddenDangerList()
|
||
}
|
||
|
||
onShow(() => {
|
||
getUserRole()
|
||
resetHiddenDangerList()
|
||
fetchHiddenDangerList()
|
||
})
|
||
|
||
onReachBottom(() => {
|
||
loadMoreHiddenDangerList()
|
||
})
|
||
|
||
const goToAdd = () => {
|
||
let url = '/pages/hiddendanger/add'
|
||
const params = []
|
||
if (oneTableId.value) {
|
||
params.push(`id=${oneTableId.value}`)
|
||
}
|
||
if (taskId.value) {
|
||
params.push(`taskId=${taskId.value}`)
|
||
}
|
||
if (checkPointId.value) {
|
||
params.push(`checkPointId=${checkPointId.value}`)
|
||
}
|
||
if (params.length) {
|
||
url += `?${params.join('&')}`
|
||
}
|
||
uni.navigateTo({ url })
|
||
}
|
||
|
||
const details = (item) => {
|
||
uni.navigateTo({
|
||
url: `/pages/hiddendanger/process-chain?hazardId=${item.hazardId}`
|
||
})
|
||
}
|
||
|
||
const Rectification = (item) => {
|
||
uni.navigateTo({ url: buildRectificationUrl(item) });
|
||
}
|
||
|
||
const editRectification = (item) => {
|
||
uni.navigateTo({ url: buildEditRectificationUrl(item) });
|
||
}
|
||
|
||
const acceptance = (item) => {
|
||
uni.navigateTo({ url: buildAcceptanceUrl(item) });
|
||
}
|
||
|
||
const assignHazard = (item) => {
|
||
uni.navigateTo({ url: buildAssignmentUrl(item) });
|
||
}
|
||
|
||
const goWriteoffApply = (item) => {
|
||
uni.navigateTo({ url: buildWriteoffApplyUrl(item) });
|
||
}
|
||
|
||
const goWriteoffApproval = (item) => {
|
||
uni.navigateTo({ url: buildWriteoffApprovalUrl(item) });
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.page {
|
||
min-height: 100vh;
|
||
background: #EBF2FC;
|
||
padding-top: 100rpx;
|
||
padding-bottom: 120rpx;
|
||
}
|
||
|
||
.fixed-add-btn {
|
||
position: fixed;
|
||
bottom: 40rpx;
|
||
left: 30rpx;
|
||
right: 30rpx;
|
||
height: 88rpx;
|
||
background: linear-gradient(135deg, #667eea 0%, #2668EA 100%);
|
||
border-radius: 44rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #fff;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
box-shadow: 0 8rpx 24rpx rgba(38, 104, 234, 0.4);
|
||
z-index: 100;
|
||
|
||
.cuIcon-add {
|
||
margin-right: 10rpx;
|
||
font-size: 36rpx;
|
||
}
|
||
}
|
||
|
||
.list-list {
|
||
background: #FFFFFF;
|
||
box-shadow: 0rpx 2rpx 6rpx 2rpx rgba(0, 0, 0, 0.08);
|
||
border-left: 5px solid #2667E9;
|
||
border-radius: 20rpx;
|
||
padding: 20rpx;
|
||
}
|
||
|
||
.level-tag {
|
||
padding: 4rpx 16rpx;
|
||
border-radius: 8rpx;
|
||
white-space: nowrap;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.level-minor {
|
||
background: #F6FFED;
|
||
border: 2rpx solid #B7EB8F;
|
||
color: #52C41A;
|
||
}
|
||
|
||
.level-normal {
|
||
background: #FFF7E6;
|
||
border: 2rpx solid #FFD591;
|
||
color: #FA8C16;
|
||
}
|
||
|
||
.level-major {
|
||
background: #FFF1F0;
|
||
border: 2rpx solid #FFA39E;
|
||
color: #F5222D;
|
||
}
|
||
|
||
.status-tabs {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 99;
|
||
white-space: nowrap;
|
||
background: #fff;
|
||
padding: 0 10rpx;
|
||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.status-tabs-inner {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
height: 88rpx;
|
||
}
|
||
|
||
.status-tab-item {
|
||
display: inline-flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0 28rpx;
|
||
height: 88rpx;
|
||
position: relative;
|
||
}
|
||
|
||
.status-tab-text {
|
||
font-size: 28rpx;
|
||
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>
|
||
|
||
<style lang="scss">
|
||
.card-actions {
|
||
margin-top: 20rpx;
|
||
|
||
.cu-btn {
|
||
padding: 0 24rpx !important;
|
||
font-size: 28rpx !important;
|
||
height: 64rpx !important;
|
||
line-height: 64rpx !important;
|
||
white-space: nowrap !important;
|
||
flex-shrink: 0 !important;
|
||
width: auto !important;
|
||
}
|
||
}
|
||
</style>
|