|
|
|
|
@ -1,12 +1,58 @@
|
|
|
|
|
<template>
|
|
|
|
|
<view class="page-container">
|
|
|
|
|
<view class="filter-bar">
|
|
|
|
|
<view class="filter-toggle" @click="showFilter = !showFilter">
|
|
|
|
|
<text class="filter-toggle-text">{{ t('taskList.filter') }}</text>
|
|
|
|
|
<text class="filter-toggle-arrow">{{ showFilter ? '▲' : '▼' }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-if="showFilter" class="filter-panel">
|
|
|
|
|
<view class="filter-row">
|
|
|
|
|
<text class="filter-label">{{ t('taskList.code') }}</text>
|
|
|
|
|
<input class="filter-input" v-model="filterParams.code" :placeholder="t('taskList.code')" />
|
|
|
|
|
</view>
|
|
|
|
|
<view class="filter-row">
|
|
|
|
|
<text class="filter-label">{{ t('taskList.status') }}</text>
|
|
|
|
|
<picker mode="selector" :range="statusOptions" range-key="label" :value="statusIndex" @change="onStatusChange">
|
|
|
|
|
<view class="filter-picker">
|
|
|
|
|
<text :class="{ 'filter-placeholder': !filterParams.status }">{{ statusLabel || t('taskList.status') }}</text>
|
|
|
|
|
<text class="picker-arrow">▼</text>
|
|
|
|
|
</view>
|
|
|
|
|
</picker>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="filter-row">
|
|
|
|
|
<text class="filter-label">{{ t('taskList.orderDate') }}</text>
|
|
|
|
|
<uni-datetime-picker v-model="filterParams.orderDate" type="daterange"
|
|
|
|
|
:placeholder="t('taskList.orderDate')" />
|
|
|
|
|
</view>
|
|
|
|
|
<view class="filter-row">
|
|
|
|
|
<text class="filter-label">{{ t('taskList.deliveryDate') }}</text>
|
|
|
|
|
<uni-datetime-picker v-model="filterParams.deliveryDate" type="daterange"
|
|
|
|
|
:placeholder="t('taskList.deliveryDate')" />
|
|
|
|
|
</view>
|
|
|
|
|
<view class="filter-row">
|
|
|
|
|
<text class="filter-label">{{ t('taskList.remark') }}</text>
|
|
|
|
|
<input class="filter-input" v-model="filterParams.remark" :placeholder="t('taskList.remark')" />
|
|
|
|
|
</view>
|
|
|
|
|
<view class="filter-row">
|
|
|
|
|
<text class="filter-label">{{ t('taskList.createTime') }}</text>
|
|
|
|
|
<uni-datetime-picker v-model="filterParams.createTime" type="daterange"
|
|
|
|
|
:placeholder="t('taskList.createTime')" />
|
|
|
|
|
</view>
|
|
|
|
|
<view class="filter-actions">
|
|
|
|
|
<view class="filter-btn reset-btn" @click="resetFilter">
|
|
|
|
|
<text>{{ t('functionCommon.cancel') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="filter-btn search-btn" @click="handleSearch">
|
|
|
|
|
<text>{{ t('functionCommon.search') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="task-list">
|
|
|
|
|
<view v-for="(task, index) in taskList" :key="index" class="task-card" @click="handleTaskClick(task)">
|
|
|
|
|
<view v-for="(task, index) in taskList" :key="index" class="task-card">
|
|
|
|
|
<view class="task-header">
|
|
|
|
|
<text class="task-code">{{ task.code }}</text>
|
|
|
|
|
<view class="task-status" :class="'status-' + task.statusType">
|
|
|
|
|
<text>{{ task.statusText }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<u-tag :text="task.statusText" :type="statusTagType(task.status)" size="mini" />
|
|
|
|
|
</view>
|
|
|
|
|
<view class="task-body">
|
|
|
|
|
<view class="task-row">
|
|
|
|
|
@ -56,13 +102,34 @@
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
|
|
|
import { onReachBottom, onShow } from '@dcloudio/uni-app'
|
|
|
|
|
import { ref, reactive, computed, onMounted } from 'vue'
|
|
|
|
|
import { onReachBottom, onShow, onLoad } from '@dcloudio/uni-app'
|
|
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
|
import request from '@/utils/request'
|
|
|
|
|
import { setNavigationTitle } from '@/locales'
|
|
|
|
|
import { DICT_TYPE, getDictLabel, useDict } from '@/utils/dict'
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
const { mes_task_status } = useDict(DICT_TYPE.MES_TASK_STATUS)
|
|
|
|
|
|
|
|
|
|
const statusOptions = computed(() => {
|
|
|
|
|
return [{ label: t('functionCommon.all'), value: '' }, ...(mes_task_status.value || [])]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const statusIndex = computed(() => {
|
|
|
|
|
return statusOptions.value.findIndex((item) => String(item.value) === String(filterParams.status))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const statusLabel = computed(() => {
|
|
|
|
|
const item = statusOptions.value.find((item) => String(item.value) === String(filterParams.status))
|
|
|
|
|
return item?.label || ''
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
function onStatusChange(e) {
|
|
|
|
|
const idx = e.detail.value
|
|
|
|
|
const item = statusOptions.value[idx]
|
|
|
|
|
filterParams.status = item ? item.value : ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const taskList = reactive([])
|
|
|
|
|
const pageNo = ref(1)
|
|
|
|
|
@ -70,6 +137,16 @@ const pageSize = ref(10)
|
|
|
|
|
const total = ref(0)
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
const noMore = ref(false)
|
|
|
|
|
const showFilter = ref(false)
|
|
|
|
|
|
|
|
|
|
const filterParams = reactive({
|
|
|
|
|
code: '',
|
|
|
|
|
status: '',
|
|
|
|
|
orderDate: [],
|
|
|
|
|
deliveryDate: [],
|
|
|
|
|
remark: '',
|
|
|
|
|
createTime: []
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
function formatDate(dateStr) {
|
|
|
|
|
if (!dateStr) return '-'
|
|
|
|
|
@ -79,16 +156,46 @@ function formatDate(dateStr) {
|
|
|
|
|
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 statusTagType(status) {
|
|
|
|
|
const dict = mes_task_status.value || []
|
|
|
|
|
const item = dict.find((d) => String(d.value) === String(status))
|
|
|
|
|
const tagType = item?.tagType || 'primary'
|
|
|
|
|
if (tagType === 'danger') return 'error'
|
|
|
|
|
if (tagType === 'default') return 'info'
|
|
|
|
|
return tagType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleTaskClick(task) {
|
|
|
|
|
uni.showToast({ title: t('taskList.viewTask', { code: task.code }), icon: 'none' })
|
|
|
|
|
function buildFilterParams() {
|
|
|
|
|
const params = {}
|
|
|
|
|
if (filterParams.code) params.code = filterParams.code
|
|
|
|
|
if (filterParams.status) params.status = filterParams.status
|
|
|
|
|
if (filterParams.remark) params.remark = filterParams.remark
|
|
|
|
|
appendDateRange(params, 'orderDate', filterParams.orderDate)
|
|
|
|
|
appendDateRange(params, 'deliveryDate', filterParams.deliveryDate)
|
|
|
|
|
appendDateRange(params, 'createTime', filterParams.createTime)
|
|
|
|
|
return params
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function appendDateRange(params, field, range) {
|
|
|
|
|
if (Array.isArray(range) && range.length === 2 && range[0] && range[1]) {
|
|
|
|
|
params[`${field}[0]`] = range[0] + ' 00:00:00'
|
|
|
|
|
params[`${field}[1]`] = range[1] + ' 23:59:59'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleSearch() {
|
|
|
|
|
showFilter.value = false
|
|
|
|
|
loadTaskList(true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resetFilter() {
|
|
|
|
|
filterParams.code = ''
|
|
|
|
|
filterParams.status = ''
|
|
|
|
|
filterParams.orderDate = []
|
|
|
|
|
filterParams.deliveryDate = []
|
|
|
|
|
filterParams.remark = ''
|
|
|
|
|
filterParams.createTime = []
|
|
|
|
|
loadTaskList(true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadTaskList(reset = false) {
|
|
|
|
|
@ -101,15 +208,15 @@ async function loadTaskList(reset = false) {
|
|
|
|
|
if (noMore.value && !reset) return
|
|
|
|
|
loading.value = true
|
|
|
|
|
try {
|
|
|
|
|
const params = { pageNo: pageNo.value, pageSize: pageSize.value, ...buildFilterParams() }
|
|
|
|
|
const res = await request({
|
|
|
|
|
url: '/admin-api/mes/task/pagePlanTask',
|
|
|
|
|
method: 'get',
|
|
|
|
|
params: { pageNo: pageNo.value, pageSize: pageSize.value }
|
|
|
|
|
params
|
|
|
|
|
})
|
|
|
|
|
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 ?? '-',
|
|
|
|
|
@ -119,8 +226,8 @@ async function loadTaskList(reset = false) {
|
|
|
|
|
isUrgent: item?.isUrgent,
|
|
|
|
|
isScheduled: item?.isScheduled,
|
|
|
|
|
status: item?.status,
|
|
|
|
|
statusText: statusInfo.statusText,
|
|
|
|
|
statusType: statusInfo.statusType,
|
|
|
|
|
statusText: getDictLabel(DICT_TYPE.MES_TASK_STATUS, item?.status),
|
|
|
|
|
statusType: statusTagType(item?.status),
|
|
|
|
|
totalNumber: item?.totalNumber ?? 0,
|
|
|
|
|
planNumber: item?.planNumber ?? 0,
|
|
|
|
|
unPlanNumber: item?.unPlanNumber ?? 0,
|
|
|
|
|
@ -139,10 +246,16 @@ async function loadTaskList(reset = false) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
onLoad((query) => {
|
|
|
|
|
if (query?.status) {
|
|
|
|
|
filterParams.status = query.status
|
|
|
|
|
}
|
|
|
|
|
loadTaskList(true)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onReachBottom(() => {
|
|
|
|
|
loadTaskList()
|
|
|
|
|
})
|
|
|
|
|
@ -159,6 +272,121 @@ onShow(() => {
|
|
|
|
|
padding: 24rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-bar {
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-toggle {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: 16rpx 24rpx;
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
background: #f0f2f5;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-toggle-text {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #1a3a5c;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
margin-right: 8rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-toggle-arrow {
|
|
|
|
|
font-size: 20rpx;
|
|
|
|
|
color: #999999;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-panel {
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
padding: 24rpx;
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
|
|
|
|
|
&:last-of-type {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-label {
|
|
|
|
|
width: 140rpx;
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
color: #666666;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
height: 64rpx;
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
padding: 0 16rpx;
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
border: 2rpx solid #e0e6ed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-picker {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
height: 64rpx;
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
padding: 0 16rpx;
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
border: 2rpx solid #e0e6ed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-placeholder {
|
|
|
|
|
color: #999999;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-arrow {
|
|
|
|
|
font-size: 20rpx;
|
|
|
|
|
color: #999999;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
margin-top: 24rpx;
|
|
|
|
|
gap: 16rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-btn {
|
|
|
|
|
padding: 14rpx 40rpx;
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.reset-btn {
|
|
|
|
|
background: #f0f2f5;
|
|
|
|
|
color: #666666;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-btn {
|
|
|
|
|
background: #1a3a5c;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.task-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
@ -189,27 +417,6 @@ onShow(() => {
|
|
|
|
|
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;
|
|
|
|
|
|