feat:任务/产品查看更多页面

master
黄伟杰 1 month ago
parent d3dfc45074
commit b1e6d3af72

@ -31,7 +31,7 @@ export default {
subtitle: 'Besure Digital Intelligent Control Platform', subtitle: 'Besure Digital Intelligent Control Platform',
functionNav: 'Function Navigation', functionNav: 'Function Navigation',
productionOverview: 'Production Overview', productionOverview: 'Production Overview',
productionPlan: 'Production Plans', productionPlan: 'Production Summary',
collapseList: 'Collapse', collapseList: 'Collapse',
viewMore: 'View More ', viewMore: 'View More ',
productName: 'Product', productName: 'Product',
@ -90,6 +90,31 @@ export default {
saturday: 'Sat', saturday: 'Sat',
sunday: 'Sun' sunday: 'Sun'
}, },
taskList: {
taskType: 'Task Type',
orderDate: 'Order Date',
deliveryDate: 'Delivery Date',
totalNumber: 'Total',
planNumber: 'Planned',
unPlanNumber: 'Unplanned',
storedPlanNumber: 'Stored',
urgent: 'Urgent',
noData: 'No task data',
viewTask: 'View Task: {code}'
},
planList: {
taskCode: 'Task Code',
planCode: 'Plan Code',
employeeName: 'Reporter',
baogongNum: 'Reported',
passNum: 'Passed',
noPassNum: 'Failed',
passRate: 'Pass Rate',
baogongTime: 'Report Time',
reason: 'Reason',
noData: 'No report data',
viewDetail: 'View Detail: {code}'
},
functionCommon: { functionCommon: {
search: 'Search', search: 'Search',
cancel: 'Cancel', cancel: 'Cancel',

@ -31,7 +31,7 @@ export default {
subtitle: '必硕数字化智能中控平台', subtitle: '必硕数字化智能中控平台',
functionNav: '功能导航', functionNav: '功能导航',
productionOverview: '生产整体概况', productionOverview: '生产整体概况',
productionPlan: '生产计划', productionPlan: '生产概括',
collapseList: '收起列表', collapseList: '收起列表',
viewMore: '查看更多 ', viewMore: '查看更多 ',
productName: '产品名称', productName: '产品名称',
@ -90,6 +90,31 @@ export default {
saturday: '周六', saturday: '周六',
sunday: '周日' sunday: '周日'
}, },
taskList: {
taskType: '任务类型',
orderDate: '下单日期',
deliveryDate: '交付日期',
totalNumber: '总数量',
planNumber: '已排产',
unPlanNumber: '未排产',
storedPlanNumber: '已入库',
urgent: '紧急',
noData: '暂无任务数据',
viewTask: '查看任务: {code}'
},
planList: {
taskCode: '任务编号',
planCode: '计划编号',
employeeName: '报工人',
baogongNum: '报工数',
passNum: '合格数',
noPassNum: '不合格数',
passRate: '合格率',
baogongTime: '报工时间',
reason: '原因',
noData: '暂无报工数据',
viewDetail: '查看详情: {code}'
},
functionCommon: { functionCommon: {
search: '查询', search: '查询',
cancel: '取消', cancel: '取消',

@ -510,6 +510,12 @@
"style": { "style": {
"navigationBarTitleText": "生产计划" "navigationBarTitleText": "生产计划"
} }
},
{
"path": "taskList/index",
"style": {
"navigationBarTitleText": "任务计划"
}
} }
] ]
} }

@ -69,14 +69,14 @@
<view class="date-picker-item"> <view class="date-picker-item">
<up-icon name="calendar" size="18" color="#999" /> <up-icon name="calendar" size="18" color="#999" />
<up-datetime-picker ref="startPickerRef" hasInput v-model="dateRange.start" mode="datetime" <up-datetime-picker ref="startPickerRef" hasInput v-model="dateRange.start" mode="datetime"
:placeholder="t('dashboard.startDate')" :formatter="datetimeFormatter" :placeholder="t('dashboard.startDate')" :formatter="datetimeFormatter" closeOnClickOverlay
closeOnClickOverlay @confirm="onStartDateConfirm" @close="onPickerClose" @cancel="onPickerClose" /> @confirm="onStartDateConfirm" @close="onPickerClose" @cancel="onPickerClose" />
</view> </view>
<view class="date-picker-item"> <view class="date-picker-item">
<up-icon name="calendar" size="18" color="#999" /> <up-icon name="calendar" size="18" color="#999" />
<up-datetime-picker ref="endPickerRef" hasInput v-model="dateRange.end" mode="datetime" <up-datetime-picker ref="endPickerRef" hasInput v-model="dateRange.end" mode="datetime"
:placeholder="t('dashboard.endDate')" :formatter="datetimeFormatter" :placeholder="t('dashboard.endDate')" :formatter="datetimeFormatter" closeOnClickOverlay
closeOnClickOverlay @confirm="onEndDateConfirm" @close="onPickerClose" @cancel="onPickerClose" /> @confirm="onEndDateConfirm" @close="onPickerClose" @cancel="onPickerClose" />
</view> </view>
</view> </view>
<view v-if="currentFilter === 'product'" class="trend-content"> <view v-if="currentFilter === 'product'" class="trend-content">
@ -100,21 +100,17 @@
</view> </view>
<view v-if="currentRange !== 'custom'" class="trend-chart"> <view v-if="currentRange !== 'custom'" class="trend-chart">
<text class="chart-title">{{ t('dashboard.baogongNum') }}</text> <text class="chart-title">{{ t('dashboard.baogongNum') }}</text>
<scroll-view scroll-x class="chart-scroll"> <view class="chart-box">
<view class="chart-box" :style="{ width: chartWidth }"> <qiun-data-charts type="line" :chartData="baogongChartData" :canvas2d="false"
<qiun-data-charts type="line" :chartData="baogongChartData" :canvas2d="false" :opts="{ color: ['#1a3a5c'], dataLabel: false, legend: { show: false }, xAxis: { disableGrid: true, labelCount: 4 }, yAxis: { gridType: 'dash', dashLength: 2 }, extra: { line: { type: 'straight', width: 2, activeType: 'hollow' } } }" />
:opts="{ legend: { show: false }, xAxis: { disableGrid: true }, yAxis: { gridType: 'dash', dashLength: 2 }, extra: { line: { type: 'straight', width: 2, activeType: 'hollow' } } }" /> </view>
</view>
</scroll-view>
</view> </view>
<view v-if="currentRange !== 'custom'" class="trend-chart"> <view v-if="currentRange !== 'custom'" class="trend-chart">
<text class="chart-title">{{ t('dashboard.passRate') }}</text> <text class="chart-title">{{ t('dashboard.passRate') }}</text>
<scroll-view scroll-x class="chart-scroll"> <view class="chart-box">
<view class="chart-box" :style="{ width: chartWidth }"> <qiun-data-charts type="line" :chartData="passRateChartData" :canvas2d="false"
<qiun-data-charts type="line" :chartData="passRateChartData" :canvas2d="false" :opts="{ color: ['#1a3a5c'], dataLabel: false, legend: { show: false }, xAxis: { disableGrid: true, labelCount: 4 }, yAxis: { gridType: 'dash', dashLength: 2, data: [{ min: 0, max: 100 }] }, extra: { line: { type: 'straight', width: 2, activeType: 'hollow' } } }" />
:opts="{ legend: { show: false }, xAxis: { disableGrid: true }, yAxis: { gridType: 'dash', dashLength: 2, data: [{ min: 0, max: 100 }] }, extra: { line: { type: 'straight', width: 2, activeType: 'hollow' } } }" /> </view>
</view>
</scroll-view>
</view> </view>
</view> </view>
<view v-else class="trend-content"> <view v-else class="trend-content">
@ -138,12 +134,10 @@
</view> </view>
<view v-if="currentRange !== 'custom'" class="trend-chart"> <view v-if="currentRange !== 'custom'" class="trend-chart">
<text class="chart-title">{{ t('dashboard.taskTrend') }}</text> <text class="chart-title">{{ t('dashboard.taskTrend') }}</text>
<scroll-view scroll-x class="chart-scroll"> <view class="chart-box">
<view class="chart-box" :style="{ width: chartWidth }"> <qiun-data-charts type="line" :chartData="taskChartData" :canvas2d="false"
<qiun-data-charts type="line" :chartData="taskChartData" :canvas2d="false" :opts="{ color: ['#1a3a5c'], dataLabel: false, legend: { show: false }, xAxis: { disableGrid: true, labelCount: 4 }, yAxis: { gridType: 'dash', dashLength: 2 }, extra: { line: { type: 'straight', width: 2, activeType: 'hollow' } } }" />
:opts="{ legend: { show: false }, xAxis: { disableGrid: true }, yAxis: { gridType: 'dash', dashLength: 2 }, extra: { line: { type: 'straight', width: 2, activeType: 'hollow' } } }" /> </view>
</view>
</scroll-view>
</view> </view>
</view> </view>
</view> </view>
@ -236,7 +230,7 @@ const showRangePicker = ref(false)
const startPickerRef = ref(null) const startPickerRef = ref(null)
const endPickerRef = ref(null) const endPickerRef = ref(null)
const currentFilter = ref('task') const currentFilter = ref('task')
const currentRange = ref('month') const currentRange = ref('year')
const filterColumns = computed(() => [ const filterColumns = computed(() => [
[ [
@ -310,12 +304,6 @@ const taskChartData = reactive({
] ]
}) })
const chartWidth = computed(() => {
const count = baogongChartData.categories.length
if (count <= 7) return '100%'
return Math.max(count * 50, 300) + 'rpx'
})
function getDateRange(type) { function getDateRange(type) {
const now = new Date() const now = new Date()
const pad2 = (n) => String(n).padStart(2, '0') const pad2 = (n) => String(n).padStart(2, '0')
@ -602,7 +590,11 @@ function handleNavClick(item) {
} }
function viewMorePlans() { function viewMorePlans() {
uni.navigateTo({ url: '/pages_function/pages/planList/index' }) if (currentFilter.value === 'task') {
uni.navigateTo({ url: '/pages_function/pages/taskList/index' })
} else {
uni.navigateTo({ url: '/pages_function/pages/planList/index' })
}
} }
function onScroll(e) { function onScroll(e) {
@ -1050,6 +1042,9 @@ onUnmounted(() => {
padding: 20rpx 12rpx; padding: 20rpx 12rpx;
margin: 0 6rpx; margin: 0 6rpx;
text-align: center; text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
&:first-child { &:first-child {
margin-left: 0; margin-left: 0;
@ -1098,11 +1093,6 @@ onUnmounted(() => {
margin-bottom: 16rpx; margin-bottom: 16rpx;
} }
.chart-scroll {
width: 100%;
white-space: nowrap;
}
.chart-box { .chart-box {
width: 100%; width: 100%;
height: 450rpx; height: 450rpx;

@ -1,46 +1,66 @@
<template> <template>
<view class="page-container"> <view class="page-container">
<view class="plan-list"> <view class="plan-list">
<view v-for="(plan, index) in planList" :key="index" class="plan-card" @click="handlePlanClick(plan)"> <view v-for="(item, index) in planList" :key="index" class="plan-card" @click="handleItemClick(item)">
<view class="plan-header"> <view class="plan-header">
<text class="plan-code">{{ plan.code }}</text> <text class="plan-code">{{ item.productName }}</text>
<view class="plan-status" :class="'status-' + plan.statusType"> <text class="plan-product-code">{{ item.productCode }}</text>
<text>{{ plan.status }}</text>
</view>
</view> </view>
<view class="plan-body"> <view class="plan-body">
<view class="plan-row"> <view class="plan-row">
<text class="plan-label">{{ t('dashboard.productName') }}</text> <text class="plan-label">{{ t('planList.taskCode') }}</text>
<text class="plan-value">{{ plan.productName }}</text> <text class="plan-value">{{ item.taskCode }}</text>
</view>
<view class="plan-row">
<text class="plan-label">{{ t('planList.planCode') }}</text>
<text class="plan-value">{{ item.planCode }}</text>
</view>
<view class="plan-row">
<text class="plan-label">{{ t('planList.employeeName') }}</text>
<text class="plan-value">{{ item.employeeName }}</text>
</view> </view>
<view class="plan-row"> <view class="plan-row">
<text class="plan-label">{{ t('dashboard.pipeline') }}</text> <text class="plan-label">{{ t('planList.baogongNum') }}</text>
<text class="plan-value">{{ plan.feedingPipelineName }}</text> <text class="plan-value plan-num">{{ item.baogongNum }}</text>
</view> </view>
<view class="plan-row"> <view class="plan-row">
<text class="plan-label">{{ t('dashboard.planNumber') }}</text> <text class="plan-label">{{ t('planList.passNum') }}</text>
<text class="plan-value plan-num">{{ plan.planNumber }}</text> <text class="plan-value plan-num pass">{{ item.passNum }}</text>
</view> </view>
<view class="plan-row"> <view class="plan-row">
<text class="plan-label">{{ t('dashboard.planStart') }}</text> <text class="plan-label">{{ t('planList.noPassNum') }}</text>
<text class="plan-value">{{ plan.planStartTimeText }}</text> <text class="plan-value plan-num fail">{{ item.noPassNum }}</text>
</view> </view>
<view class="plan-row"> <view class="plan-row">
<text class="plan-label">{{ t('dashboard.planEnd') }}</text> <text class="plan-label">{{ t('planList.passRate') }}</text>
<text class="plan-value">{{ plan.planEndTimeText }}</text> <text class="plan-value plan-num" :class="item.passRate >= 90 ? 'pass' : item.passRate >= 70 ? 'warn' : 'fail'">{{ item.passRateText }}</text>
</view>
<view class="plan-row">
<text class="plan-label">{{ t('planList.baogongTime') }}</text>
<text class="plan-value">{{ item.baogongTimeText }}</text>
</view>
<view v-if="item.reason" class="plan-row">
<text class="plan-label">{{ t('planList.reason') }}</text>
<text class="plan-value reason-text">{{ item.reason }}</text>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<view v-if="planList.length === 0" class="empty-state"> <view v-if="loading" class="loading-state">
<text class="empty-text">{{ t('functionCommon.loadFailed') }}</text> <text class="loading-text">{{ t('functionCommon.loading') }}</text>
</view>
<view v-else-if="planList.length === 0" class="empty-state">
<text class="empty-text">{{ t('planList.noData') }}</text>
</view>
<view v-else-if="noMore" class="no-more-state">
<text class="no-more-text">{{ t('functionCommon.noMoreData') }}</text>
</view> </view>
</view> </view>
</template> </template>
<script setup> <script setup>
import { onMounted, reactive } from 'vue' import { ref, reactive, onMounted } from 'vue'
import { onShow } from '@dcloudio/uni-app' import { onReachBottom, onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import request from '@/utils/request' import request from '@/utils/request'
import { setNavigationTitle } from '@/locales' import { setNavigationTitle } from '@/locales'
@ -48,8 +68,13 @@ import { setNavigationTitle } from '@/locales'
const { t } = useI18n() const { t } = useI18n()
const planList = reactive([]) const planList = reactive([])
const pageNo = ref(1)
const pageSize = ref(10)
const total = ref(0)
const loading = ref(false)
const noMore = ref(false)
function formatDate(ms) { function formatDateTime(ms) {
if (!ms) return '-' if (!ms) return '-'
const date = new Date(ms) const date = new Date(ms)
if (Number.isNaN(date.getTime())) return '-' if (Number.isNaN(date.getTime())) return '-'
@ -57,62 +82,70 @@ function formatDate(ms) {
const y = date.getFullYear() const y = date.getFullYear()
const m = pad2(date.getMonth() + 1) const m = pad2(date.getMonth() + 1)
const d = pad2(date.getDate()) const d = pad2(date.getDate())
return `${y}-${m}-${d}` const h = pad2(date.getHours())
} const min = pad2(date.getMinutes())
const s = pad2(date.getSeconds())
const getPlanStatusLabel = (value) => { return `${y}-${m}-${d} ${h}:${min}:${s}`
const v = value === '' || value === null || value === undefined ? undefined : String(value) }
if (v == '1') return t('dashboard.statusScheduled')
if (v == '6') return t('dashboard.statusTrial') function handleItemClick(item) {
if (v == '2') return t('dashboard.statusMass') uni.showToast({ title: t('planList.viewDetail', { code: item.taskCode }), icon: 'none' })
if (v == '3') return t('dashboard.statusPause') }
if (v == '4') return t('dashboard.statusWaitStockIn')
return '-' async function loadPlanList(reset = false) {
} if (loading.value) return
if (reset) {
function mapPlanStatus(status) { pageNo.value = 1
const v = status === '' || status === null || status === undefined ? undefined : String(status) noMore.value = false
if (v == '1') return { status: getPlanStatusLabel(v), statusType: 'pending' } planList.splice(0, planList.length)
if (v == '6') return { status: getPlanStatusLabel(v), statusType: 'running' } }
if (v == '2') return { status: getPlanStatusLabel(v), statusType: 'running' } if (noMore.value && !reset) return
if (v == '3') return { status: getPlanStatusLabel(v), statusType: 'pending' } loading.value = true
if (v == '4') return { status: getPlanStatusLabel(v), statusType: 'finished' } try {
return { status: getPlanStatusLabel(v), statusType: 'pending' } const res = await request({
} url: '/admin-api/mes/baogong-record/stat-page',
method: 'get',
function handlePlanClick(plan) { params: { pageNo: pageNo.value, pageSize: pageSize.value }
uni.showToast({ })
title: t('dashboard.viewPlan', { code: plan.code }), const list = res?.data?.list || []
icon: 'none' total.value = res?.data?.total || 0
}) const mapped = list.map((item) => ({
} id: item?.id,
taskCode: item?.taskCode ?? '-',
async function loadPlanList() { planCode: item?.planCode ?? '-',
const res = await request({ url: '/admin-api/mes/dashboard/getPlan', method: 'get' }) employeeName: item?.employeeName ?? '-',
const raw = Array.isArray(res?.data) ? res.data : (res?.data ? [res.data] : []) productName: item?.productName ?? '-',
const mapped = raw.map((p) => { productCode: item?.productCode ?? '-',
const statusInfo = mapPlanStatus(p?.status) baogongNum: item?.baogongNum ?? 0,
return { passNum: item?.passNum ?? 0,
id: p?.id, noPassNum: item?.noPassNum ?? 0,
code: p?.code ?? '-', passRate: item?.passRate ?? 0,
status: statusInfo.status, passRateText: item?.passRate != null ? `${item.passRate.toFixed(2)}%` : '-',
statusType: statusInfo.statusType, reason: item?.reason ?? '',
productName: p?.productName ?? '-', baogongTime: item?.baogongTime,
feedingPipelineName: p?.feedingPipelineName ?? '-', baogongTimeText: formatDateTime(item?.baogongTime)
planNumber: p?.planNumber ?? 0, }))
planStartTimeText: formatDate(p?.planStartTime), planList.splice(planList.length, 0, ...mapped)
planEndTimeText: formatDate(p?.planEndTime), if (planList.length >= total.value) {
noMore.value = true
} else {
pageNo.value++
} }
}) } finally {
planList.splice(0, planList.length, ...mapped) loading.value = false
}
} }
onMounted(() => { onMounted(() => {
loadPlanList(true)
})
onReachBottom(() => {
loadPlanList() loadPlanList()
}) })
onShow(() => { onShow(() => {
setNavigationTitle('nav.home') setNavigationTitle('dashboard.productionPlan')
}) })
</script> </script>
@ -153,25 +186,12 @@ onShow(() => {
color: #1a3a5c; color: #1a3a5c;
} }
.plan-status { .plan-product-code {
padding: 8rpx 20rpx; font-size: 24rpx;
border-radius: 20rpx; color: #999999;
font-size: 22rpx; background: #f0f2f5;
} padding: 4rpx 16rpx;
border-radius: 8rpx;
.status-pending {
background: rgba(255, 140, 0, 0.15);
color: #ff8c00;
}
.status-running {
background: rgba(24, 188, 55, 0.15);
color: #18bc37;
}
.status-finished {
background: rgba(74, 144, 194, 0.15);
color: #4a90c2;
} }
.plan-body { .plan-body {
@ -203,17 +223,39 @@ onShow(() => {
.plan-num { .plan-num {
font-weight: 600; font-weight: 600;
color: #1a3a5c; color: #1a3a5c;
&.pass {
color: #18bc37;
}
&.warn {
color: #ff8c00;
}
&.fail {
color: #ff3b30;
}
}
.reason-text {
max-width: 400rpx;
text-align: right;
word-break: break-all;
} }
.empty-state { .loading-state,
.empty-state,
.no-more-state {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 400rpx; height: 200rpx;
}
.empty-text { .loading-text,
font-size: 28rpx; .empty-text,
color: #999999; .no-more-text {
} font-size: 28rpx;
color: #999999;
} }
</style> </style>

@ -0,0 +1,273 @@
<template>
<view class="page-container">
<view class="task-list">
<view v-for="(task, index) in taskList" :key="index" class="task-card" @click="handleTaskClick(task)">
<view class="task-header">
<text class="task-code">{{ task.code }}</text>
<view class="task-status" :class="'status-' + task.statusType">
<text>{{ task.statusText }}</text>
</view>
</view>
<view class="task-body">
<view class="task-row">
<text class="task-label">{{ t('taskList.taskType') }}</text>
<text class="task-value">{{ task.taskType || '-' }}</text>
</view>
<view class="task-row">
<text class="task-label">{{ t('taskList.orderDate') }}</text>
<text class="task-value">{{ task.orderDateText }}</text>
</view>
<view class="task-row">
<text class="task-label">{{ t('taskList.deliveryDate') }}</text>
<text class="task-value">{{ task.deliveryDateText }}</text>
</view>
<view class="task-row">
<text class="task-label">{{ t('taskList.totalNumber') }}</text>
<text class="task-value task-num">{{ task.totalNumber }}</text>
</view>
<view class="task-row">
<text class="task-label">{{ t('taskList.planNumber') }}</text>
<text class="task-value task-num">{{ task.planNumber }}</text>
</view>
<view class="task-row">
<text class="task-label">{{ t('taskList.unPlanNumber') }}</text>
<text class="task-value task-num">{{ task.unPlanNumber }}</text>
</view>
<view class="task-row">
<text class="task-label">{{ t('taskList.storedPlanNumber') }}</text>
<text class="task-value task-num">{{ task.storedPlanNumber }}</text>
</view>
<view v-if="task.isUrgent === 1" class="task-urgent">
<text class="urgent-text">{{ t('taskList.urgent') }}</text>
</view>
</view>
</view>
</view>
<view v-if="loading" class="loading-state">
<text class="loading-text">{{ t('functionCommon.loading') }}</text>
</view>
<view v-else-if="taskList.length === 0" class="empty-state">
<text class="empty-text">{{ t('taskList.noData') }}</text>
</view>
<view v-else-if="noMore" class="no-more-state">
<text class="no-more-text">{{ t('functionCommon.noMoreData') }}</text>
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { onReachBottom, onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import request from '@/utils/request'
import { setNavigationTitle } from '@/locales'
const { t } = useI18n()
const taskList = reactive([])
const pageNo = ref(1)
const pageSize = ref(10)
const total = ref(0)
const loading = ref(false)
const noMore = ref(false)
function formatDate(dateStr) {
if (!dateStr) return '-'
const date = new Date(dateStr)
if (Number.isNaN(date.getTime())) return '-'
const pad2 = (n) => String(n).padStart(2, '0')
return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`
}
function mapTaskStatus(status) {
const v = status === '' || status === null || status === undefined ? undefined : String(status)
if (v === '1') return { statusText: t('dashboard.pending'), statusType: 'pending' }
if (v === '2') return { statusText: t('dashboard.running'), statusType: 'running' }
if (v === '3') return { statusText: t('dashboard.finished'), statusType: 'finished' }
return { statusText: '-', statusType: 'pending' }
}
function handleTaskClick(task) {
uni.showToast({ title: t('taskList.viewTask', { code: task.code }), icon: 'none' })
}
async function loadTaskList(reset = false) {
if (loading.value) return
if (reset) {
pageNo.value = 1
noMore.value = false
taskList.splice(0, taskList.length)
}
if (noMore.value && !reset) return
loading.value = true
try {
const res = await request({
url: '/admin-api/mes/task/pagePlanTask',
method: 'get',
params: { pageNo: pageNo.value, pageSize: pageSize.value }
})
const list = res?.data?.list || []
total.value = res?.data?.total || 0
const mapped = list.map((item) => {
const statusInfo = mapTaskStatus(item?.status)
return {
id: item?.id,
code: item?.code ?? '-',
taskType: item?.taskType ?? '-',
orderDateText: formatDate(item?.orderDate),
deliveryDateText: formatDate(item?.deliveryDate),
isUrgent: item?.isUrgent,
isScheduled: item?.isScheduled,
status: item?.status,
statusText: statusInfo.statusText,
statusType: statusInfo.statusType,
totalNumber: item?.totalNumber ?? 0,
planNumber: item?.planNumber ?? 0,
unPlanNumber: item?.unPlanNumber ?? 0,
storedPlanNumber: item?.storedPlanNumber ?? 0,
remark: item?.remark ?? ''
}
})
taskList.splice(taskList.length, 0, ...mapped)
if (taskList.length >= total.value) {
noMore.value = true
} else {
pageNo.value++
}
} finally {
loading.value = false
}
}
onMounted(() => {
loadTaskList(true)
})
onReachBottom(() => {
loadTaskList()
})
onShow(() => {
setNavigationTitle('dashboard.productionPlan')
})
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background-color: #f0f2f5;
padding: 24rpx;
}
.task-list {
display: flex;
flex-direction: column;
}
.task-card {
background: #ffffff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
&:active {
background: #e8f4ff;
}
}
.task-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.task-code {
font-size: 28rpx;
font-weight: 600;
color: #1a3a5c;
}
.task-status {
padding: 8rpx 20rpx;
border-radius: 20rpx;
font-size: 22rpx;
}
.status-pending {
background: rgba(255, 140, 0, 0.15);
color: #ff8c00;
}
.status-running {
background: rgba(24, 188, 55, 0.15);
color: #18bc37;
}
.status-finished {
background: rgba(74, 144, 194, 0.15);
color: #4a90c2;
}
.task-body {
display: flex;
flex-direction: column;
}
.task-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
&:last-child {
margin-bottom: 0;
}
}
.task-label {
font-size: 26rpx;
color: #999999;
}
.task-value {
font-size: 26rpx;
color: #333333;
}
.task-num {
font-weight: 600;
color: #1a3a5c;
}
.task-urgent {
margin-top: 12rpx;
padding: 8rpx 16rpx;
background: rgba(255, 59, 48, 0.1);
border-radius: 8rpx;
align-self: flex-start;
}
.urgent-text {
font-size: 22rpx;
color: #ff3b30;
font-weight: 600;
}
.loading-state,
.empty-state,
.no-more-state {
display: flex;
align-items: center;
justify-content: center;
height: 200rpx;
}
.loading-text,
.empty-text,
.no-more-text {
font-size: 28rpx;
color: #999999;
}
</style>
Loading…
Cancel
Save