|
|
<template>
|
|
|
<view class="page-container">
|
|
|
<view class="fixed-header">
|
|
|
<NavBar :title="t('equipmentInspectionTasks.detailTitle')" />
|
|
|
</view>
|
|
|
|
|
|
<scroll-view scroll-y class="detail-scroll">
|
|
|
<view class="content-section">
|
|
|
<view class="info-card">
|
|
|
<view class="card-title">{{ t('equipmentInspectionTasks.basicInfo') }}</view>
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">{{ t('equipmentInspectionTasks.name') }}</text>
|
|
|
<text class="info-value">{{ textValue(detailData.name) }}</text>
|
|
|
</view>
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">{{ t('equipmentInspectionTasks.taskType') }}</text>
|
|
|
<text class="info-value">{{ taskTypeText }}</text>
|
|
|
</view>
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">{{ t('equipmentInspectionTasks.deviceList') }}</text>
|
|
|
<text class="info-value">{{ deviceListText }}</text>
|
|
|
</view>
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">{{ t('equipmentInspectionTasks.projectFormName') }}</text>
|
|
|
<text class="info-value">{{ textValue(detailData.projectFormName) }}</text>
|
|
|
</view>
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">{{ t('equipmentInspectionTasks.dateRange') }}</text>
|
|
|
<text class="info-value">{{ formatDate(detailData.startDate) }} ~ {{ formatDate(detailData.endDate) }}</text>
|
|
|
</view>
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">{{ t('equipmentInspectionTasks.cronExpression') }}</text>
|
|
|
<text class="info-value">{{ textValue(detailData.cronExpression) }}</text>
|
|
|
</view>
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">{{ t('equipmentInspectionTasks.operableUsers') }}</text>
|
|
|
<text class="info-value">{{ textValue(detailData.operableUsers) }}</text>
|
|
|
</view>
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">{{ t('equipmentInspectionTasks.enabled') }}</text>
|
|
|
<text :class="['info-value', isEnabled(detailData.enabled) ? 'text-success' : 'text-danger']">{{ enabledText(detailData.enabled) }}</text>
|
|
|
</view>
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">{{ t('equipmentInspectionTasks.creatorName') }}</text>
|
|
|
<text class="info-value">{{ textValue(detailData.creatorName) }}</text>
|
|
|
</view>
|
|
|
<view class="info-row">
|
|
|
<text class="info-label">{{ t('equipmentInspectionTasks.createTime') }}</text>
|
|
|
<text class="info-value">{{ formatDateTime(detailData.createTime) }}</text>
|
|
|
</view>
|
|
|
<view class="info-row no-border">
|
|
|
<text class="info-label">{{ t('equipmentInspectionTasks.updateTime') }}</text>
|
|
|
<text class="info-value">{{ formatDateTime(detailData.updateTime) }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
|
|
|
<view class="action-bar">
|
|
|
<view :class="['action-btn', ticketLoading ? 'action-btn-disabled' : '']" @click="handleCreateTicket">
|
|
|
{{ t('equipmentInspectionTasks.createTicket') }}
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import { computed, reactive, ref } from 'vue'
|
|
|
import { onLoad } from '@dcloudio/uni-app'
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
import NavBar from '@/components/common/NavBar.vue'
|
|
|
import { createTaskManagementTicket } from '@/api/mes/taskManagement'
|
|
|
import { getDeviceLedgerList } from '@/api/mes/deviceLedger'
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
const detailData = reactive({})
|
|
|
const ticketLoading = ref(false)
|
|
|
const deviceOptions = ref([])
|
|
|
const taskTypeText = computed(() => {
|
|
|
const normalized = Number(detailData.taskType)
|
|
|
if (normalized === 1) return t('equipmentInspectionTasks.taskTypeInspect')
|
|
|
if (normalized === 2) return t('equipmentInspectionTasks.taskTypeMaintain')
|
|
|
return textValue(detailData.taskType)
|
|
|
})
|
|
|
async function ensureDeviceOptionsLoaded() {
|
|
|
try {
|
|
|
const res = await getDeviceLedgerList()
|
|
|
const root = res && res.data !== undefined ? res.data : res
|
|
|
const data = Array.isArray(root) ? root : (Array.isArray(root?.data) ? root.data : root?.list || [])
|
|
|
deviceOptions.value = (Array.isArray(data) ? data : [])
|
|
|
.filter((item) => item && item.id !== undefined && item.id !== null)
|
|
|
.map((item) => ({
|
|
|
id: item.id,
|
|
|
label: `${item.deviceCode || ''} ${item.deviceName || ''}`.trim(),
|
|
|
deviceName: item.deviceName || '',
|
|
|
raw: item
|
|
|
}))
|
|
|
} catch (error) {
|
|
|
deviceOptions.value = []
|
|
|
}
|
|
|
}
|
|
|
const deviceListText = computed(() => {
|
|
|
const value = detailData.deviceListText || detailData.deviceList
|
|
|
/* if (Array.isArray(value)) return value.join(',') || '-'
|
|
|
return textValue(value)*/
|
|
|
const ids = parseIdsValue(value)
|
|
|
if (!ids.length) return '-'
|
|
|
const names = ids
|
|
|
.map((id) => {
|
|
|
const matched = deviceOptions.value.find((item) => String(item.id) === id)
|
|
|
return matched?.deviceName || matched?.label || id
|
|
|
})
|
|
|
.filter(Boolean)
|
|
|
return names.length ? names.join(',') : '-'
|
|
|
})
|
|
|
|
|
|
function parseIdsValue(value) {
|
|
|
if (!value) return []
|
|
|
if (Array.isArray(value)) return value.map((item) => String(item).trim()).filter(Boolean)
|
|
|
return String(value).split(',').map((item) => item.trim()).filter(Boolean)
|
|
|
}
|
|
|
|
|
|
onLoad(async () => {
|
|
|
try {
|
|
|
const cached = uni.getStorageSync('equipmentInspectionTasksDetail')
|
|
|
if (cached) {
|
|
|
Object.assign(detailData, JSON.parse(cached))
|
|
|
uni.removeStorageSync('equipmentInspectionTasksDetail')
|
|
|
}
|
|
|
await Promise.all([ensureDeviceOptionsLoaded()])
|
|
|
} catch (error) {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
async function handleCreateTicket() {
|
|
|
if (!detailData.id || ticketLoading.value) return
|
|
|
ticketLoading.value = true
|
|
|
try {
|
|
|
await createTaskManagementTicket(detailData.id)
|
|
|
uni.showToast({ title: t('equipmentInspectionTasks.createTicketSuccess'), icon: 'success' })
|
|
|
} catch (error) {
|
|
|
uni.showToast({ title: t('equipmentInspectionTasks.createTicketFail'), icon: 'none' })
|
|
|
} finally {
|
|
|
ticketLoading.value = false
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function isEnabled(value) {
|
|
|
return value === true || value === 1 || value === '1' || value === 'true'
|
|
|
}
|
|
|
|
|
|
function enabledText(value) {
|
|
|
return isEnabled(value) ? t('functionCommon.yes') : t('functionCommon.no')
|
|
|
}
|
|
|
|
|
|
function textValue(value) {
|
|
|
if (value === 0) return '0'
|
|
|
if (value === null || value === undefined) return '-'
|
|
|
const text = String(value).trim()
|
|
|
return text || '-'
|
|
|
}
|
|
|
|
|
|
function formatDate(value) {
|
|
|
if (!value) return '-'
|
|
|
if (Array.isArray(value) && value.length >= 3) {
|
|
|
const [year, month, day] = value
|
|
|
const pad = (num) => String(num).padStart(2, '0')
|
|
|
return `${year}-${pad(month)}-${pad(day)}`
|
|
|
}
|
|
|
return String(value).trim().substring(0, 10) || '-'
|
|
|
}
|
|
|
|
|
|
function formatDateTime(value) {
|
|
|
if (!value) return '-'
|
|
|
if (Array.isArray(value) && value.length >= 3) {
|
|
|
const [year, month, day, hour = 0, minute = 0, second = 0] = value
|
|
|
const pad = (num) => String(num).padStart(2, '0')
|
|
|
return `${year}-${pad(month)}-${pad(day)} ${pad(hour)}:${pad(minute)}:${pad(second)}`
|
|
|
}
|
|
|
const date = new Date(Number(value))
|
|
|
if (Number.isNaN(date.getTime())) return textValue(value)
|
|
|
const pad = (num) => String(num).padStart(2, '0')
|
|
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.page-container { min-height: 100vh; background-color: #f0f2f5; }
|
|
|
.fixed-header { position: sticky; top: 0; z-index: 20; }
|
|
|
.detail-scroll { height: calc(100vh - 200rpx); }
|
|
|
.content-section { padding: 0 24rpx 24rpx; }
|
|
|
.info-card { margin-top: 20rpx; background: #fff; border-radius: 20rpx; padding: 28rpx; box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); }
|
|
|
.card-title { font-size: 32rpx; color: #1a3a5c; font-weight: 700; margin-bottom: 18rpx; }
|
|
|
.info-row { display: flex; justify-content: space-between; align-items: flex-start; padding: 18rpx 0; border-bottom: 1rpx solid #edf0f3; }
|
|
|
.no-border { border-bottom: none; }
|
|
|
.info-label { width: 220rpx; font-size: 27rpx; color: #8a9099; }
|
|
|
.info-value { flex: 1; text-align: right; font-size: 28rpx; color: #303133; line-height: 1.45; }
|
|
|
.text-success { color: #18bc37; }
|
|
|
.text-danger { color: #e34d59; }
|
|
|
.action-bar {
|
|
|
position: fixed;
|
|
|
left: 0;
|
|
|
right: 0;
|
|
|
bottom: 0;
|
|
|
padding: 20rpx 24rpx calc(20rpx + env(safe-area-inset-bottom));
|
|
|
background: #f0f2f5;
|
|
|
}
|
|
|
.action-btn {
|
|
|
height: 88rpx;
|
|
|
border-radius: 16rpx;
|
|
|
background: #1f4b79;
|
|
|
color: #ffffff;
|
|
|
font-size: 30rpx;
|
|
|
font-weight: 600;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
.action-btn-disabled {
|
|
|
background: #94a3b8;
|
|
|
}
|
|
|
</style>
|