feat:新增模具管理-点检记录
parent
929dea162b
commit
493fd0125f
@ -0,0 +1,33 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getTicketManagementPage(params = {}) {
|
||||
return request({
|
||||
url: '/admin-api/mes/mold-ticket-management/page',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function batchUpdateTicketStatus(data) {
|
||||
return request({
|
||||
url: '/admin-api/mes/mold-ticket-management/batchUpdateStatus',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getTicketResultsPage(params = {}) {
|
||||
return request({
|
||||
url: '/admin-api/mes/mold-ticket-results/page',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function batchUpdateTicketResults(data) {
|
||||
return request({
|
||||
url: '/admin-api/mes/mold-ticket-results/batchUpdate',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
@ -0,0 +1,306 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<view class="fixed-header">
|
||||
<AppTitleHeader :title="t('moldWorkOrder.detailTitle')" />
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-y class="detail-scroll">
|
||||
<view class="content-section">
|
||||
<!-- 基础信息 -->
|
||||
<view class="info-card">
|
||||
<view class="card-title">{{ t('moldWorkOrder.basicInfo') }}</view>
|
||||
<view class="info-list">
|
||||
<view class="info-row">
|
||||
<text class="info-label">{{ t('moldWorkOrder.planNo') }}</text>
|
||||
<text class="info-value">{{ textValue(detailData.planNo) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">{{ t('moldWorkOrder.moldName') }}</text>
|
||||
<text class="info-value">{{ textValue(detailData.moldName) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">{{ t('moldWorkOrder.planType') }}</text>
|
||||
<text class="info-value">{{ planTypeText }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">{{ t('moldWorkOrder.configName') }}</text>
|
||||
<text class="info-value">{{ textValue(detailData.configName) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">{{ t('moldWorkOrder.jobStatus') }}</text>
|
||||
<text :class="['info-value', jobStatusClass]">{{ jobStatusText }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">{{ t('moldWorkOrder.jobResult') }}</text>
|
||||
<text :class="['info-value', jobResultClass]">{{ jobResultText }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">{{ t('moldWorkOrder.operatorName') }}</text>
|
||||
<text class="info-value">{{ textValue(detailData.operatorName) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">{{ t('moldWorkOrder.taskTime') }}</text>
|
||||
<text class="info-value">{{ formatDateTime(detailData.taskTime) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">{{ t('moldWorkOrder.taskEndTime') }}</text>
|
||||
<text class="info-value">{{ formatDateTime(detailData.taskEndTime) }}</text>
|
||||
</view>
|
||||
<view v-if="String(detailData.jobStatus) === '4'" class="info-row">
|
||||
<text class="info-label">{{ t('moldWorkOrder.cancelReason') }}</text>
|
||||
<text class="info-value">{{ textValue(detailData.cancelReason) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">{{ t('moldWorkOrder.createTime') }}</text>
|
||||
<text class="info-value">{{ formatDateTime(detailData.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 检验结果列表 -->
|
||||
<view class="info-card">
|
||||
<view class="card-title">{{ t('moldWorkOrder.resultListTitle') }}</view>
|
||||
<view v-if="resultLoading" class="hint">{{ t('functionCommon.loading') }}</view>
|
||||
<view v-else-if="!resultList.length" class="hint">{{ t('moldWorkOrder.noResultData') }}</view>
|
||||
<view v-else class="result-list">
|
||||
<view v-for="(item, index) in resultList" :key="item.id || index" class="result-card">
|
||||
<view class="result-index">{{ index + 1 }}</view>
|
||||
<view class="result-body">
|
||||
<view class="result-row">
|
||||
<text class="result-label">{{ t('moldWorkOrder.inspectionItemName') }}</text>
|
||||
<text class="result-value">{{ textValue(item.inspectionItemName) }}</text>
|
||||
</view>
|
||||
<view class="result-row">
|
||||
<text class="result-label">{{ t('moldWorkOrder.inspectionMethod') }}</text>
|
||||
<text class="result-value">{{ getInspectionMethodLabel(item.inspectionMethod) }}</text>
|
||||
</view>
|
||||
<view class="result-row">
|
||||
<text class="result-label">{{ t('moldWorkOrder.judgmentCriteria') }}</text>
|
||||
<text class="result-value">{{ textValue(item.judgmentCriteria) }}</text>
|
||||
</view>
|
||||
<view class="result-row">
|
||||
<text class="result-label">{{ t('moldWorkOrder.valueType') }}</text>
|
||||
<text class="result-value">{{ getValueTypeLabel(item.valueType) }}</text>
|
||||
</view>
|
||||
<view class="result-row">
|
||||
<text class="result-label">{{ t('moldWorkOrder.inspectionResult') }}</text>
|
||||
<text :class="['result-value', inspectionResultClass(item.inspectionResult)]">{{ inspectionResultText(item.inspectionResult) }}</text>
|
||||
</view>
|
||||
<view v-if="item.textInput" class="result-row">
|
||||
<text class="result-label">{{ t('moldWorkOrder.textInput') }}</text>
|
||||
<text class="result-value">{{ textValue(item.textInput) }}</text>
|
||||
</view>
|
||||
<view v-if="item.remark" class="result-row">
|
||||
<text class="result-label">{{ t('moldWorkOrder.remark') }}</text>
|
||||
<text class="result-value">{{ textValue(item.remark) }}</text>
|
||||
</view>
|
||||
<view v-if="item.images" class="result-row">
|
||||
<text class="result-label">{{ t('moldWorkOrder.images') }}</text>
|
||||
<view class="result-images">
|
||||
<image v-for="(img, imgIdx) in parseImages(item.images)" :key="imgIdx" :src="img" class="result-img" mode="aspectFill" @click="previewImage(img, parseImages(item.images))" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="resultList.length < resultTotal" class="load-more-btn" @click="loadMoreResults">
|
||||
<text class="load-more-text">{{ resultLoadingMore ? t('functionCommon.loadingMore') : t('moldWorkOrder.loadMore') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import AppTitleHeader from '@/components/common/AppTitleHeader.vue'
|
||||
import { getTicketResultsPage } from '@/api/mes/moldWorkOrderInquiry'
|
||||
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const detailData = reactive({})
|
||||
const resultList = ref([])
|
||||
const resultTotal = ref(0)
|
||||
const resultLoading = ref(false)
|
||||
const resultLoadingMore = ref(false)
|
||||
const resultPageNo = ref(1)
|
||||
const resultPageSize = ref(10)
|
||||
const managementId = ref(null)
|
||||
|
||||
const planTypeText = computed(() => {
|
||||
const v = detailData.planType
|
||||
if (String(v) === '1') return t('moldWorkOrder.planTypeInspect')
|
||||
if (String(v) === '2') return t('moldWorkOrder.planTypeMaintain')
|
||||
return textValue(v)
|
||||
})
|
||||
|
||||
const jobStatusText = computed(() => {
|
||||
return getDictLabel(DICT_TYPE.JOB_STATUS, detailData.jobStatus, textValue(detailData.jobStatus))
|
||||
})
|
||||
|
||||
const jobStatusClass = computed(() => {
|
||||
const s = String(detailData.jobStatus)
|
||||
if (s === '0') return 'text-warning'
|
||||
if (s === '1') return 'text-primary'
|
||||
if (s === '2') return 'text-success'
|
||||
if (s === '3') return 'text-danger'
|
||||
if (s === '4') return 'text-muted'
|
||||
return ''
|
||||
})
|
||||
|
||||
const jobResultText = computed(() => {
|
||||
const v = detailData.jobResult
|
||||
if (String(v) === '1') return t('moldWorkOrder.jobResultOk')
|
||||
if (String(v) === '2') return t('moldWorkOrder.jobResultNg')
|
||||
return textValue(v)
|
||||
})
|
||||
|
||||
const jobResultClass = computed(() => {
|
||||
if (String(detailData.jobResult) === '1') return 'text-success'
|
||||
if (String(detailData.jobResult) === '2') return 'text-danger'
|
||||
return ''
|
||||
})
|
||||
|
||||
function getInspectionMethodLabel(value) {
|
||||
return getDictLabel(DICT_TYPE.INSPECTION_METHOD, value, textValue(value))
|
||||
}
|
||||
|
||||
function getValueTypeLabel(value) {
|
||||
return getDictLabel(DICT_TYPE.VALUE_TYPES, value, textValue(value))
|
||||
}
|
||||
|
||||
function inspectionResultText(v) {
|
||||
if (String(v) === '0') return t('moldWorkOrder.inspectionResultPending')
|
||||
if (String(v) === '1') return t('moldWorkOrder.inspectionResultPass')
|
||||
if (String(v) === '2') return t('moldWorkOrder.inspectionResultFail')
|
||||
return textValue(v)
|
||||
}
|
||||
|
||||
function inspectionResultClass(v) {
|
||||
if (String(v) === '0') return 'text-warning'
|
||||
if (String(v) === '1') return 'text-success'
|
||||
if (String(v) === '2') return 'text-danger'
|
||||
return ''
|
||||
}
|
||||
|
||||
function textValue(v) {
|
||||
if (v === 0) return '0'
|
||||
if (v === null || v === undefined) return '-'
|
||||
const s = String(v).trim()
|
||||
return s || '-'
|
||||
}
|
||||
|
||||
function formatDateTime(v) {
|
||||
if (!v) return '-'
|
||||
if (Array.isArray(v) && v.length >= 3) {
|
||||
const [y, m, d, hh = 0, mm = 0, ss = 0] = v
|
||||
const p = (n) => String(n).padStart(2, '0')
|
||||
return `${y}-${p(m)}-${p(d)} ${p(hh)}:${p(mm)}:${p(ss)}`
|
||||
}
|
||||
const d = new Date(Number(v))
|
||||
if (Number.isNaN(d.getTime())) return textValue(v)
|
||||
const p = (n) => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
|
||||
}
|
||||
|
||||
function parseImages(value) {
|
||||
if (!value) return []
|
||||
if (Array.isArray(value)) return value.map(String).filter(Boolean)
|
||||
return String(value).split(',').map(v => v.trim()).filter(Boolean)
|
||||
}
|
||||
|
||||
function previewImage(current, urls) {
|
||||
uni.previewImage({ current, urls })
|
||||
}
|
||||
|
||||
async function fetchResults(reset) {
|
||||
if (!managementId.value) return
|
||||
if (reset) {
|
||||
resultPageNo.value = 1
|
||||
}
|
||||
if (resultPageNo.value === 1) {
|
||||
resultLoading.value = true
|
||||
} else {
|
||||
resultLoadingMore.value = true
|
||||
}
|
||||
try {
|
||||
const res = await getTicketResultsPage({
|
||||
pageNo: resultPageNo.value,
|
||||
pageSize: resultPageSize.value,
|
||||
managementId: managementId.value
|
||||
})
|
||||
const root = res && res.data !== undefined ? res.data : res
|
||||
const items = root?.list || root?.rows || root?.records || []
|
||||
const total = root?.total ?? 0
|
||||
if (reset) {
|
||||
resultList.value = Array.isArray(items) ? items : []
|
||||
} else {
|
||||
resultList.value = [...resultList.value, ...(Array.isArray(items) ? items : [])]
|
||||
}
|
||||
resultTotal.value = Number(total)
|
||||
} catch (e) {
|
||||
uni.showToast({ title: t('functionCommon.loadFailed'), icon: 'none' })
|
||||
} finally {
|
||||
resultLoading.value = false
|
||||
resultLoadingMore.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function loadMoreResults() {
|
||||
if (resultLoadingMore.value) return
|
||||
resultPageNo.value += 1
|
||||
fetchResults(false)
|
||||
}
|
||||
|
||||
onLoad(async (query) => {
|
||||
const id = query?.id
|
||||
if (!id) {
|
||||
uni.showToast({ title: t('functionCommon.noIdView'), icon: 'none' })
|
||||
return
|
||||
}
|
||||
await initAllDict()
|
||||
managementId.value = id
|
||||
try {
|
||||
const cached = uni.getStorageSync('moldWorkOrderDetail')
|
||||
if (cached) {
|
||||
const data = JSON.parse(cached)
|
||||
Object.assign(detailData, data)
|
||||
uni.removeStorageSync('moldWorkOrderDetail')
|
||||
}
|
||||
} catch (e) {}
|
||||
await fetchResults(true)
|
||||
})
|
||||
</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 - 88rpx); }
|
||||
.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; }
|
||||
.info-label { font-size: 27rpx; color: #8a9099; width: 220rpx; }
|
||||
.info-value { flex: 1; text-align: right; font-size: 28rpx; color: #303133; line-height: 1.45; }
|
||||
.text-success { color: #18bc37; }
|
||||
.text-danger { color: #e34d59; }
|
||||
.text-warning { color: #f0883e; }
|
||||
.text-primary { color: #1a3a5c; }
|
||||
.text-muted { color: #909399; }
|
||||
.hint { text-align: center; color: #909399; padding: 24rpx 0; font-size: 26rpx; }
|
||||
.result-list { display: flex; flex-direction: column; gap: 16rpx; }
|
||||
.result-card { display: flex; background: #f7f9fc; border-radius: 14rpx; padding: 20rpx; gap: 16rpx; }
|
||||
.result-index { min-width: 48rpx; height: 48rpx; border-radius: 10rpx; background: #1a3a5c; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 24rpx; font-weight: 700; }
|
||||
.result-body { flex: 1; display: flex; flex-direction: column; gap: 8rpx; }
|
||||
.result-row { display: flex; justify-content: space-between; align-items: flex-start; }
|
||||
.result-label { font-size: 24rpx; color: #8a9099; min-width: 160rpx; }
|
||||
.result-value { font-size: 26rpx; color: #30363d; flex: 1; text-align: right; }
|
||||
.result-images { display: flex; flex-wrap: wrap; gap: 10rpx; flex: 1; justify-content: flex-end; }
|
||||
.result-img { width: 100rpx; height: 100rpx; border-radius: 8rpx; }
|
||||
.load-more-btn { text-align: center; padding: 20rpx 0 0; }
|
||||
.load-more-text { font-size: 26rpx; color: #1a3a5c; }
|
||||
</style>
|
||||
Loading…
Reference in New Issue