|
|
|
|
@ -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; }
|
|
|
|
|
|