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

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

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

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

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

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