style:设备点检记录-筛选框优化

master
黄伟杰 3 days ago
parent c8db4a7e73
commit 8971dffd0d

@ -582,6 +582,8 @@ export default {
selectAllDecisionError: 'Please choose a result for all pending items',
noResultData: 'No inspection items',
lineFilter: 'Line',
moreFilter: 'More Filters',
filterScope: 'Filter Scope',
},
moldWorkOrder: {
moduleName: 'Inspection Records',

@ -582,6 +582,8 @@ export default {
selectAllDecisionError: '请为所有待检项选择结果',
noResultData: '暂无点检项数据',
lineFilter: '产线',
moreFilter: '更多筛选',
filterScope: '筛选范围',
},
moldWorkOrder: {
moduleName: '点检记录',

@ -133,7 +133,7 @@
<view class="action-bar">
<view class="action-btn back-btn" @click="goBack">{{ t('dashboard.back') }}</view>
<view :class="['action-btn', 'save-btn', (saveLoading || isExecuted) ? 'action-btn-disabled' : '']" @click="handleSave">{{ t('functionCommon.save') }}</view>
<view v-if="!isExecuted" :class="['action-btn', 'save-btn', saveLoading ? 'action-btn-disabled' : '']" @click="handleSave">{{ t('functionCommon.save') }}</view>
</view>
</view>
</template>
@ -153,7 +153,7 @@ const resultList = ref([])
const resultLoading = ref(false)
const managementId = ref('')
const saveLoading = ref(false)
const isExecuted = computed(() => String(detailData.jobStatus || '') === '2')
const isExecuted = computed(() => String(detailData.jobStatus || '') !== '0')
const planTypeText = computed(() => {
const value = String(detailData.planType || '')

@ -3,36 +3,38 @@
<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
id="equipment-inspection-record-keyword-input"
v-model="searchKeyword"
class="keyword-input"
:placeholder="t('equipmentInspectionRecord.searchPlaceholder')"
confirm-type="search"
@input="handleKeywordInput"
@confirm="handleSearch"
/>
<view class="filter-row quick-row">
<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>
<picker mode="selector" :range="jobStatusLabels" :value="jobStatusIndex" @change="onJobStatusChange">
<view class="status-filter">
<text :class="['status-filter-text', selectedJobStatus === '' ? 'placeholder' : '']">{{ currentJobStatusLabel }}</text>
<uni-icons type="bottom" size="14" color="#a8adb7"></uni-icons>
</view>
</picker>
</view>
<picker mode="selector" :range="taskTypeLabels" :value="taskTypeIndex" @change="onTaskTypeChange">
<view class="status-box">
<text class="status-box-text">{{ currentTaskTypeLabel }}</text>
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
<view class="filter-row search-row">
<view class="keyword-wrap">
<input
id="equipment-inspection-record-keyword-input"
v-model="searchKeyword"
class="keyword-input"
:placeholder="t('equipmentInspectionRecord.searchPlaceholder')"
confirm-type="search"
@input="handleKeywordInput"
@confirm="handleSearch"
/>
</view>
</picker>
<picker mode="selector" :range="jobStatusLabels" :value="jobStatusIndex" @change="onJobStatusChange">
<view class="status-box">
<text class="status-box-text">{{ currentJobStatusLabel }}</text>
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
<view class="icon-filter-btn" @click="resetFilters">
<uni-icons type="refresh" size="24" color="#7b8491"></uni-icons>
</view>
</picker>
<view class="reset-filter-btn" @click="resetFilters">{{ t('functionCommon.reset') }}</view>
<view class="icon-filter-btn" @click="openFilterDrawer">
<uni-icons type="settings" size="24" color="#7b8491"></uni-icons>
</view>
</view>
</view>
<scroll-view scroll-y class="list-scroll" :scroll-top="scrollTop" @scroll="onScroll" @scrolltolower="loadMore" :lower-threshold="80">
<view class="list-wrap">
<view v-for="item in list" :key="item.id" class="task-card" @click="openDetail(item)">
@ -75,7 +77,49 @@
<view v-if="showGoTop" class="go-top-btn" @click="goTop">
<uni-icons type="arrow-up" size="20" color="#1a3a5c"></uni-icons>
</view>
<uni-popup
ref="filterPopupRef"
class="equipment-filter-popup"
type="right"
background-color="transparent"
:animation="false"
>
<view class="filter-drawer">
<view class="drawer-header">
<text class="drawer-title">{{ t('equipmentInspectionRecord.moreFilter') }}</text>
</view>
<scroll-view scroll-y class="drawer-body">
<view class="drawer-section">
<view class="drawer-section-head">
<text class="drawer-section-title">{{ t('equipmentInspectionRecord.filterScope') }}</text>
</view>
<view class="drawer-field drawer-field-wide">
<text class="drawer-label">{{ t('equipmentInspectionRecord.taskType') }}</text>
<picker mode="selector" :range="taskTypeLabels" :value="taskTypeIndex" @change="onTaskTypeChange">
<view class="drawer-picker">
<text :class="['drawer-picker-text', selectedTaskType === '' ? 'placeholder' : '']">{{ currentTaskTypeLabel }}</text>
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
</view>
</picker>
</view>
<view class="drawer-field drawer-field-wide drawer-field-gap">
<text class="drawer-label">{{ t('equipmentInspectionRecord.jobResult') }}</text>
<picker mode="selector" :range="jobResultLabels" :value="jobResultIndex" @change="onJobResultChange">
<view class="drawer-picker">
<text :class="['drawer-picker-text', selectedJobResult === '' ? 'placeholder' : '']">{{ currentJobResultLabel }}</text>
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
</view>
</picker>
</view>
</view>
</scroll-view>
<view class="drawer-actions">
<view class="drawer-action reset" @click="resetFilters">{{ t('functionCommon.reset') }}</view>
<view class="drawer-action confirm" @click="confirmFilterDrawer">{{ t('functionCommon.confirm') }}</view>
</view>
</view>
</uni-popup>
<up-cascader
:key="lineCascaderKey"
@ -103,8 +147,10 @@ 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'
import useDictStore from '@/store/modules/dict'
const { t } = useI18n()
const dictStore = useDictStore()
const selectedLineId = ref('')
const lineTree = ref([])
@ -118,7 +164,9 @@ const lineCascaderOptions = computed(() => [
const searchKeyword = ref('')
const selectedTaskType = ref('')
const selectedJobStatus = ref('')
const selectedJobStatus = ref('0')
const selectedJobResult = ref('')
const filterPopupRef = ref(null)
const list = ref([])
const loading = ref(false)
const loadingMore = ref(false)
@ -138,22 +186,24 @@ const selectedLineLabel = computed(() => {
return found?.name || t('equipmentInspectionRecord.lineFilter')
})
const taskTypeOptions = computed(() => [
{ label: t('functionCommon.all'), value: '' },
{ label: t('equipmentInspectionRecord.taskTypeInspect'), value: '1' },
{ label: t('equipmentInspectionRecord.taskTypeMaintain'), value: '2' }
])
const jobStatusOptions = computed(() => [
{ label: t('functionCommon.all'), value: '' },
{ label: t('equipmentInspectionRecord.jobStatusPending'), value: '0' },
{ label: t('equipmentInspectionRecord.jobStatusProcessing'), value: '1' },
{ label: t('equipmentInspectionRecord.jobStatusCompleted'), value: '2' },
{ label: t('equipmentInspectionRecord.jobStatusTimeout'), value: '3' },
{ label: t('equipmentInspectionRecord.jobStatusCancelled'), value: '4' }
const jobStatusOptions = computed(() => {
const dicts = dictStore.getDict(DICT_TYPE.JOB_STATUS) || []
return dicts
.filter((item) => item?.value !== null && item?.value !== undefined && String(item.value) !== '')
.map((item) => ({ label: item.label, value: String(item.value) }))
})
const jobResultOptions = computed(() => [
{ label: t('equipmentInspectionRecord.jobResultOk'), value: 'OK' },
{ label: t('equipmentInspectionRecord.jobResultNg'), value: 'NG' }
])
const taskTypeLabels = computed(() => taskTypeOptions.value.map((item) => item.label))
const jobStatusLabels = computed(() => jobStatusOptions.value.map((item) => item.label))
const jobResultLabels = computed(() => jobResultOptions.value.map((item) => item.label))
const taskTypeIndex = computed(() => {
const index = taskTypeOptions.value.findIndex((item) => item.value === selectedTaskType.value)
return index >= 0 ? index : 0
@ -162,13 +212,24 @@ const jobStatusIndex = computed(() => {
const index = jobStatusOptions.value.findIndex((item) => item.value === selectedJobStatus.value)
return index >= 0 ? index : 0
})
const jobResultIndex = computed(() => {
const index = jobResultOptions.value.findIndex((item) => item.value === selectedJobResult.value)
return index >= 0 ? index : 0
})
const currentTaskTypeLabel = computed(() => {
if (selectedTaskType.value === '') return t('equipmentInspectionRecord.taskType')
const current = taskTypeOptions.value.find((item) => item.value === selectedTaskType.value)
return current ? current.label : t('functionCommon.all')
return current ? current.label : t('equipmentInspectionRecord.taskType')
})
const currentJobStatusLabel = computed(() => {
if (selectedJobStatus.value === '') return t('equipmentInspectionRecord.jobStatus')
const current = jobStatusOptions.value.find((item) => item.value === selectedJobStatus.value)
return current ? current.label : t('functionCommon.all')
return current ? current.label : t('equipmentInspectionRecord.jobStatus')
})
const currentJobResultLabel = computed(() => {
if (selectedJobResult.value === '') return t('equipmentInspectionRecord.jobResult')
const current = jobResultOptions.value.find((item) => item.value === selectedJobResult.value)
return current ? current.label : t('equipmentInspectionRecord.jobResult')
})
onLoad(async () => {
@ -313,6 +374,7 @@ async function fetchList(reset) {
deviceId: deviceId || undefined,
planType: selectedTaskType.value || undefined,
jobStatus: selectedJobStatus.value || undefined,
jobResult: selectedJobResult.value || undefined,
deviceLineId: selectedLineId.value || undefined
}
const res = await getEquipmentInspectionRecordPage(params)
@ -361,16 +423,29 @@ async function resetFilters() {
searchKeyword.value = ''
selectedTaskType.value = ''
selectedJobStatus.value = ''
selectedJobResult.value = ''
resetLineFilter()
activateKeywordFocus()
await nextTick()
await fetchList(true)
}
function openFilterDrawer() {
filterPopupRef.value?.open()
}
function closeFilterDrawer() {
filterPopupRef.value?.close()
}
async function confirmFilterDrawer() {
closeFilterDrawer()
await fetchList(true)
}
function onTaskTypeChange(event) {
const index = Number(event?.detail?.value || 0)
selectedTaskType.value = taskTypeOptions.value[index]?.value ?? ''
fetchList(true)
}
function onJobStatusChange(event) {
@ -379,6 +454,11 @@ function onJobStatusChange(event) {
fetchList(true)
}
function onJobResultChange(event) {
const index = Number(event?.detail?.value || 0)
selectedJobResult.value = jobResultOptions.value[index]?.value ?? ''
}
async function loadMore() {
if (loading.value || loadingMore.value || finished.value) return
pageNo.value += 1
@ -481,70 +561,43 @@ function formatDateTime(value) {
<style lang="scss" scoped>
.page-container { min-height: 100vh; background: #f4f5f7; }
.filter-bar {
display: grid;
grid-template-columns: minmax(0, 1fr) 136rpx 136rpx 88rpx;
align-items: center;
gap: 10rpx;
padding: 18rpx 4rpx 0rpx;
}
.filter-bar { padding: 18rpx 14rpx 20rpx; background: #f4f5f7; }
.filter-row { display: flex; align-items: center; gap: 18rpx; }
.search-row { margin-top: 18rpx; }
.quick-row > picker { min-width: 0; flex: 1; }
.keyword-wrap,
.status-filter,
.line-filter,
.keyword-box,
.status-box,
.reset-filter-btn {
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 28rpx;
}
.keyword-input {
width: 100%;
font-size: 24rpx;
color: #374151;
}
.status-box {
justify-content: space-between;
padding: 0 28rpx;
}
.status-box-text {
font-size: 24rpx;
color: #374151;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.reset-filter-btn {
justify-content: center;
font-size: 24rpx;
color: #4b5563;
}
.icon-filter-btn { height: 66rpx; border: 1rpx solid #d9dde5; background: #ffffff; box-sizing: border-box; }
.keyword-wrap { min-width: 0; flex: 1; display: flex; align-items: center; }
.keyword-input { width: 100%; height: 64rpx; padding: 0 20rpx; font-size: 26rpx; color: #374151; }
.status-filter,
.line-filter { min-width: 0; flex: 1; display: flex; align-items: center; justify-content: space-between; padding: 0 18rpx 0 26rpx; }
.icon-filter-btn { width: 66rpx; flex: 0 0 66rpx; display: flex; align-items: center; justify-content: center; border-color: transparent; background: transparent; }
.status-filter-text,
.line-filter-text { min-width: 0rpx; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 26rpx; color: #374151; }
.status-filter-text.placeholder,
.line-filter-text.placeholder,
.placeholder { color: #a8adb7; }
.filter-drawer { width: 630rpx; height: calc(100vh - var(--status-bar-height)); margin-top: var(--status-bar-height); background: #f5f5f7; display: flex; flex-direction: column; overflow: hidden; border-radius: 28rpx 0 0 28rpx; }
:deep(.equipment-filter-popup.right .uni-popup__content-transition) { transform: none !important; }
.drawer-header { height: 104rpx; padding: 18rpx 34rpx 0; background: #ffffff; display: flex; align-items: center; box-sizing: border-box; }
.drawer-title { color: #1f2937; font-size: 34rpx; line-height: 1.3; font-weight: 700; }
.drawer-body { flex: 1; min-height: 0; padding-bottom: 40rpx; box-sizing: border-box; }
.drawer-section { margin-bottom: 18rpx; padding: 28rpx 28rpx 30rpx; border-radius: 24rpx; background: #ffffff; box-sizing: border-box; }
.drawer-section-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24rpx; }
.drawer-section-title { font-size: 32rpx; line-height: 1.3; color: #1f2937; font-weight: 700; }
.drawer-field { min-width: 0; }
.drawer-field-wide { grid-column: 1 / -1; }
.drawer-field-gap { margin-top: 22rpx; }
.drawer-label { display: block; margin-bottom: 12rpx; font-size: 24rpx; line-height: 1.3; color: #4b5563; font-weight: 500; }
.drawer-picker { width: 100%; min-height: 74rpx; border: 0; border-radius: 8rpx; background: #f7f8fb; box-sizing: border-box; display: flex; align-items: center; justify-content: center; padding: 0 18rpx; gap: 8rpx; }
.drawer-picker-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 26rpx; color: #111827; text-align: center; }
.drawer-picker-text.placeholder { color: #9ca3af; }
.drawer-actions { height: 126rpx; padding: 18rpx 28rpx 24rpx; box-sizing: border-box; display: flex; align-items: center; gap: 0; background: #ffffff; box-shadow: 0 -8rpx 24rpx rgba(17, 24, 39, 0.06); }
.drawer-action { flex: 1; height: 72rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 600; border: 2rpx solid #174b78; box-sizing: border-box; }
.drawer-action.reset { border-radius: 12rpx 0 0 12rpx; background: #ffffff; color: #174b78; }
.drawer-action.confirm { border-radius: 0 12rpx 12rpx 0; background: #174b78; color: #ffffff; }
.list-scroll { height: calc(100vh - 194rpx); }
.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); }

Loading…
Cancel
Save