app端点检任务及点检记录增加查询条件

master
liutao 1 week ago
parent 818f7737e7
commit ce78df6f54

@ -474,7 +474,8 @@ export default {
searchPlaceholder: 'Enter task name',
empty: 'No equipment inspection tasks',
createTicketSuccess: 'Work order created successfully',
createTicketFail: 'Work order creation failed'
createTicketFail: 'Work order creation failed',
lineFilter: 'Line',
},
equipmentInspectionRecord: {
moduleName: 'Equipment Inspection Records',
@ -515,7 +516,8 @@ export default {
images: 'Images',
maxUploadCount: 'You can upload up to 9 images',
selectAllDecisionError: 'Please choose a result for all pending items',
noResultData: 'No inspection items'
noResultData: 'No inspection items',
lineFilter: 'Line',
},
moldWorkOrder: {
moduleName: 'Inspection Records',

@ -474,7 +474,8 @@ export default {
searchPlaceholder: '请输入任务名称',
empty: '暂无设备点检任务数据',
createTicketSuccess: '工单创建成功',
createTicketFail: '工单创建失败'
createTicketFail: '工单创建失败',
lineFilter: '产线',
},
equipmentInspectionRecord: {
moduleName: '设备点检记录',
@ -515,7 +516,8 @@ export default {
images: '图片',
maxUploadCount: '最多上传 9 张图片',
selectAllDecisionError: '请为所有待检项选择结果',
noResultData: '暂无点检项数据'
noResultData: '暂无点检项数据',
lineFilter: '产线',
},
moldWorkOrder: {
moduleName: '点检记录',

@ -3,6 +3,10 @@
<NavBar :title="t('equipmentInspectionRecord.moduleName')" />
<view class="filter-bar">
<view class="line-filter" @click="openLineCascader">
<text :class="['line-filter-text', selectedLineId === '' ? 'placeholder' : '']">{{ selectedLineLabel }}</text>
<uni-icons type="bottom" size="14" color="#a8adb7"></uni-icons>
</view>
<view class="keyword-box">
<input
v-model="searchKeyword"
@ -72,6 +76,21 @@
<view v-if="showGoTop" class="go-top-btn" @click="goTop">
<uni-icons type="arrow-up" size="20" color="#1a3a5c"></uni-icons>
</view>
<up-cascader
:key="lineCascaderKey"
v-model:show="lineCascaderShow"
v-model="lineCascaderValue"
:data="lineCascaderOptions"
value-key="value"
label-key="label"
children-key="children"
header-direction="row"
:options-cols="2"
:auto-close="false"
@confirm="onLineCascaderConfirm"
/>
</view>
</template>
@ -81,10 +100,21 @@ import { onLoad, onReachBottom, onShow, onUnload } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import { getEquipmentInspectionRecordPage } from '@/api/mes/equipmentInspectionRecord'
import { getDeviceLineTree } from '@/api/mes/deviceLine'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'
const { t } = useI18n()
const selectedLineId = ref('')
const lineTree = ref([])
const lineCascaderShow = ref(false)
const lineCascaderValue = ref([])
const lineCascaderKey = ref(0)
const lineCascaderOptions = computed(() => [
{ label: t('functionCommon.all'), value: '' },
...normalizeLineTreeForCascader(lineTree.value)
])
const searchKeyword = ref('')
const selectedTaskType = ref('')
const selectedJobStatus = ref('')
@ -100,6 +130,11 @@ const keywordFocus = ref(false)
let searchTimer = null
const selectedLineLabel = computed(() => {
if (selectedLineId.value === '') return t('equipmentInspectionRecord.lineFilter')
const found = lineOptions.value.find(item => String(item.id) === String(selectedLineId.value))
return found?.name || t('equipmentInspectionRecord.lineFilter')
})
const taskTypeOptions = computed(() => [
{ label: t('functionCommon.all'), value: '' },
{ label: t('equipmentInspectionRecord.taskTypeInspect'), value: '1' },
@ -135,19 +170,114 @@ const currentJobStatusLabel = computed(() => {
})
onLoad(async () => {
activateKeywordFocus()
await initAllDict()
await loadLineTree()
await fetchList(true)
})
onShow(() => {
activateKeywordFocus()
if (!lineTree.value.length) loadLineTree()
})
onUnload(() => {
clearSearchTimer()
})
async function loadLineTree() {
try {
const res = await getDeviceLineTree()
const root = res && res.data !== undefined ? res.data : res
const treeData = Array.isArray(root) ? root : (Array.isArray(root?.data) ? root.data : [])
lineTree.value = normalizeLineTree(treeData)
} catch (_) {
lineTree.value = []
}
}
function openLineCascader() {
lineCascaderShow.value = true
}
function onLineCascaderConfirm(values) {
const selectedValues = Array.isArray(values) ? values : []
const nextValue = selectedValues[selectedValues.length - 1] ?? ''
selectedLineId.value = nextValue === '' ? '' : String(nextValue)
lineCascaderValue.value = nextValue === '' ? [] : selectedValues.map(item => String(item))
fetchList(true)
}
async function resetLineFilter() {
selectedLineId.value = ''
lineCascaderValue.value = []
lineCascaderShow.value = false
lineCascaderKey.value += 1
}
const lineOptions = computed(() => {
return flattenLineTree(lineTree.value, 1)
})
function flattenLineTree(nodes, level) {
const result = []
;(Array.isArray(nodes) ? nodes : []).forEach((node) => {
result.push({
id: node.id,
name: node.name || node.label || String(node.id || ''),
level
})
if (Array.isArray(node.children) && node.children.length) {
result.push(...flattenLineTree(node.children, level + 1))
}
})
return result
}
function normalizeLineTreeForCascader(nodes) {
return (Array.isArray(nodes) ? nodes : []).map((node) => {
const children = normalizeLineTreeForCascader(node.children)
const item = {
label: node.name || node.label || String(node.id || ''),
value: String(node.id ?? '')
}
if (children.length) item.children = children
return item
})
}
function normalizeLineTree(nodes) {
return (Array.isArray(nodes) ? nodes : []).map((node) => {
const children = normalizeLineTree(node.children)
const item = {
...node,
id: String(node.id ?? ''),
children
}
if (!children.length) delete item.children
return item
})
}
function normalizeListData(res) {
const root = res && res.data !== undefined ? res.data : res
if (Array.isArray(root)) return root
if (Array.isArray(res)) return res
if (root?.list && Array.isArray(root.list)) return root.list
if (root?.rows && Array.isArray(root.rows)) return root.rows
if (root?.records && Array.isArray(root.records)) return root.records
if (root?.data?.list && Array.isArray(root.data.list)) return root.data.list
if (root?.data?.rows && Array.isArray(root.data.rows)) return root.data.rows
if (root?.data?.records && Array.isArray(root.data.records)) return root.data.records
if (Array.isArray(root?.children)) return root.children
if (Array.isArray(root?.data)) return root.data
const keys = root ? Object.keys(root) : []
for (let i = 0; i < keys.length; i++) {
if (Array.isArray(root[keys[i]])) return root[keys[i]]
}
return []
}
onReachBottom(() => {
loadMore()
})
@ -168,9 +298,10 @@ async function fetchList(reset) {
const params = {
pageNo: pageNo.value,
pageSize: pageSize.value,
planNo: searchKeyword.value.trim() || undefined,
planNo: searchKeyword.value.trim() || undefined,
planType: selectedTaskType.value || undefined,
jobStatus: selectedJobStatus.value || undefined
jobStatus: selectedJobStatus.value || undefined,
deviceLineId: selectedLineId.value || undefined
}
const res = await getEquipmentInspectionRecordPage(params)
const page = normalizePageData(res)
@ -213,6 +344,7 @@ async function resetFilters() {
searchKeyword.value = ''
selectedTaskType.value = ''
selectedJobStatus.value = ''
resetLineFilter()
activateKeywordFocus()
await nextTick()
await fetchList(true)
@ -329,23 +461,45 @@ function formatDateTime(value) {
.page-container { min-height: 100vh; background: #f4f5f7; }
.filter-bar {
display: grid;
grid-template-columns: minmax(0, 0.9fr) 136rpx 136rpx 88rpx;
grid-template-columns: minmax(0, 1fr) 136rpx 136rpx 88rpx;
align-items: center;
gap: 10rpx;
padding: 18rpx 20rpx 20rpx;
padding: 18rpx 4rpx 0rpx;
}
.line-filter,
.keyword-box,
.status-box,
.reset-filter-btn {
height: 64rpx;
height: 66rpx;
background: #ffffff;
border: 1rpx solid #d9dde5;
box-sizing: border-box;
display: flex;
align-items: center;
}
.line-filter {
grid-column: 1 / -1;
justify-content: space-between;
padding: 0 28rpx;
border-radius: 8rpx;
}
.line-filter-text {
font-size: 26rpx;
color: #374151;
max-width: 85%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line-filter-text.placeholder {
color: #a8adb7;
}
.keyword-box {
padding: 0 16rpx;
padding: 0 28rpx;
}
.keyword-input {
width: 100%;
@ -354,7 +508,7 @@ function formatDateTime(value) {
}
.status-box {
justify-content: space-between;
padding: 0 14rpx;
padding: 0 28rpx;
}
.status-box-text {
font-size: 24rpx;
@ -370,7 +524,7 @@ function formatDateTime(value) {
color: #4b5563;
}
.list-scroll { height: calc(100vh - 194rpx); }
.list-wrap { padding: 0 24rpx 32rpx; }
.list-wrap { padding: 0 4rpx 32rpx; }
.task-card { position: relative; margin-top: 20rpx; padding: 28rpx; background: #fff; border-radius: 22rpx; box-shadow: 0 8rpx 28rpx rgba(15, 23, 42, 0.06); }
.card-header { margin-bottom: 18rpx; }
.header-main { display: flex; align-items: center; justify-content: space-between; gap: 16rpx; }

@ -3,6 +3,10 @@
<NavBar :title="t('equipmentInspectionTasks.moduleName')" />
<view class="filter-bar">
<view class="line-filter" @click="openLineCascader">
<text :class="['line-filter-text', selectedLineId === '' ? 'placeholder' : '']">{{ selectedLineLabel }}</text>
<uni-icons type="bottom" size="14" color="#a8adb7"></uni-icons>
</view>
<view class="keyword-box">
<input
v-model="searchKeyword"
@ -60,6 +64,19 @@
<view v-if="showGoTop" class="go-top-btn" @click="goTop">
<uni-icons type="arrow-up" size="20" color="#1a3a5c"></uni-icons>
</view>
<up-cascader
:key="lineCascaderKey"
v-model:show="lineCascaderShow"
v-model="lineCascaderValue"
:data="lineCascaderOptions"
value-key="value"
label-key="label"
children-key="children"
header-direction="row"
:options-cols="2"
:auto-close="false"
@confirm="onLineCascaderConfirm"
/>
</view>
</template>
@ -69,6 +86,7 @@ import { onLoad, onReachBottom, onShow, onUnload } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import { getTaskManagementPage, createTaskManagementTicket } from '@/api/mes/taskManagement'
import { getDeviceLineTree } from '@/api/mes/deviceLine'
import { getDeviceLedgerList } from '@/api/mes/deviceLedger'
const { t } = useI18n()
@ -86,6 +104,26 @@ const showGoTop = ref(false)
const ticketLoadingId = ref(null)
const deviceOptions = ref([])
const keywordFocus = ref(false)
const selectedLineId = ref('')
const lineTree = ref([])
const lineCascaderShow = ref(false)
const lineCascaderValue = ref([])
const lineCascaderKey = ref(0)
const lineCascaderOptions = computed(() => [
{ label: t('functionCommon.all'), value: '' },
...normalizeLineTreeForCascader(lineTree.value)
])
const selectedLineLabel = computed(() => {
if (selectedLineId.value === '') return t('equipmentInspectionTasks.lineFilter')
const found = lineOptions.value.find(item => String(item.id) === String(selectedLineId.value))
return found?.name || t('equipmentInspectionTasks.lineFilter')
})
const lineOptions = computed(() => {
return flattenLineTree(lineTree.value, 1)
})
let searchTimer = null
@ -107,7 +145,7 @@ const currentTaskTypeLabel = computed(() => {
onLoad(async () => {
activateKeywordFocus()
await Promise.all([ensureDeviceOptionsLoaded(), fetchList(true)])
await Promise.all([loadLineTree(), ensureDeviceOptionsLoaded(), fetchList(true)])
})
onShow(() => {
@ -157,7 +195,8 @@ async function fetchList(reset) {
pageNo: pageNo.value,
pageSize: pageSize.value,
name: searchKeyword.value.trim() || undefined,
taskType: selectedTaskType.value || undefined
taskType: selectedTaskType.value || undefined,
deviceLineId: selectedLineId.value || undefined
}
const res = await getTaskManagementPage(params)
const page = normalizePageData(res)
@ -172,6 +211,76 @@ async function fetchList(reset) {
}
}
async function loadLineTree() {
try {
const res = await getDeviceLineTree()
const root = res && res.data !== undefined ? res.data : res
const treeData = Array.isArray(root) ? root : (Array.isArray(root?.data) ? root.data : [])
lineTree.value = normalizeLineTree(treeData)
} catch (_) {
lineTree.value = []
}
}
function openLineCascader() {
lineCascaderShow.value = true
}
function onLineCascaderConfirm(values) {
const selectedValues = Array.isArray(values) ? values : []
const nextValue = selectedValues[selectedValues.length - 1] ?? ''
selectedLineId.value = nextValue === '' ? '' : String(nextValue)
lineCascaderValue.value = nextValue === '' ? [] : selectedValues.map(item => String(item))
fetchList(true)
}
async function resetLineFilter() {
selectedLineId.value = ''
lineCascaderValue.value = []
lineCascaderShow.value = false
lineCascaderKey.value += 1
}
function flattenLineTree(nodes, level) {
const result = []
;(Array.isArray(nodes) ? nodes : []).forEach((node) => {
result.push({
id: node.id,
name: node.name || node.label || String(node.id || ''),
level
})
if (Array.isArray(node.children) && node.children.length) {
result.push(...flattenLineTree(node.children, level + 1))
}
})
return result
}
function normalizeLineTreeForCascader(nodes) {
return (Array.isArray(nodes) ? nodes : []).map((node) => {
const children = normalizeLineTreeForCascader(node.children)
const item = {
label: node.name || node.label || String(node.id || ''),
value: String(node.id ?? '')
}
if (children.length) item.children = children
return item
})
}
function normalizeLineTree(nodes) {
return (Array.isArray(nodes) ? nodes : []).map((node) => {
const children = normalizeLineTree(node.children)
const item = {
...node,
id: String(node.id ?? ''),
children
}
if (!children.length) delete item.children
return item
})
}
function normalizePageData(res) {
const root = res && res.data !== undefined ? res.data : res
const candidateList = root?.list || root?.rows || root?.records || root?.data?.list || root?.data?.rows || root?.data?.records || []
@ -199,6 +308,7 @@ async function resetFilters() {
clearSearchTimer()
searchKeyword.value = ''
selectedTaskType.value = ''
resetLineFilter()
activateKeywordFocus()
await nextTick()
await fetchList(true)
@ -320,8 +430,9 @@ function formatDate(value) {
grid-template-columns: minmax(0, 1fr) 150rpx 96rpx;
align-items: center;
gap: 14rpx;
padding: 18rpx 28rpx 20rpx;
padding: 18rpx 4rpx 0rpx;
}
.line-filter,
.keyword-box,
.status-box,
.reset-filter-btn {
@ -332,6 +443,26 @@ function formatDate(value) {
display: flex;
align-items: center;
}
.line-filter {
grid-column: 1 / -1;
justify-content: space-between;
padding: 0 28rpx;
border-radius: 8rpx;
}
.line-filter-text {
font-size: 26rpx;
color: #374151;
max-width: 85%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line-filter-text.placeholder {
color: #a8adb7;
}
.keyword-box {
padding: 0 20rpx;
}
@ -354,7 +485,7 @@ function formatDate(value) {
color: #4b5563;
}
.list-scroll { height: calc(100vh - 194rpx); }
.list-wrap { padding: 0 24rpx 32rpx; }
.list-wrap { padding: 0 4rpx 32rpx; }
.task-card { position: relative; margin-top: 20rpx; padding: 28rpx; background: #fff; border-radius: 22rpx; box-shadow: 0 8rpx 28rpx rgba(15, 23, 42, 0.06); }
.card-header { margin-bottom: 18rpx; }
.header-main { display: flex; align-items: center; justify-content: space-between; gap: 16rpx; }

Loading…
Cancel
Save