feat:设备点检任务-添加模板项目展示

master
黄伟杰 2 days ago
parent 5f25b3594e
commit c8db4a7e73

@ -15,3 +15,20 @@ export function createTaskManagementTicket(id) {
params: { id }
})
}
export function getPlanMaintenancePage(params = {}) {
return request({
url: '/admin-api/mes/plan-maintenance/page',
method: 'get',
params
})
}
export function getPlanMaintenanceSubjectList(id) {
return request({
url: '/admin-api/mes/plan-maintenance/getSubjectList',
method: 'get',
params: { id }
})
}

@ -526,7 +526,21 @@ export default {
empty: 'No equipment inspection tasks',
createTicketSuccess: 'Work order created successfully',
createTicketFail: 'Work order creation failed',
lineFilter: 'Line', },
lineFilter: 'Line', moreFilter: 'More Filters',
filterScope: 'Filter Scope',
placeholderProjectForm: 'Select inspection template',
placeholderDeviceList: 'Select equipment',
selectedCount: '{count} selected',
noPlanData: 'No inspection templates',
noDeviceData: 'No equipment data',
confirmCreateTicketContent: 'Create a work order from \"{name}\"?',
subjectListTitle: 'Template Items',
noSubjectData: 'No template items',
subjectCode: 'Item Code',
subjectName: 'Item Name',
inspectionMethod: 'Inspection Method',
judgmentCriteria: 'Criteria',
},
equipmentInspectionRecord: {
moduleName: 'Equipment Inspection Records',
subTitle: 'Equipment inspection record list',

@ -526,7 +526,21 @@ export default {
empty: '暂无设备点检任务数据',
createTicketSuccess: '工单创建成功',
createTicketFail: '工单创建失败',
lineFilter: '产线', },
lineFilter: '产线', moreFilter: '更多筛选',
filterScope: '筛选范围',
placeholderProjectForm: '请选择点检模板',
placeholderDeviceList: '请选择设备',
selectedCount: '已选择 {count} 项',
noPlanData: '暂无点检模板',
noDeviceData: '暂无设备数据',
confirmCreateTicketContent: '确认根据\"{name}\"新增工单吗?',
subjectListTitle: '模板项目',
noSubjectData: '暂无模板项目',
subjectCode: '项目编码',
subjectName: '项目名称',
inspectionMethod: '点检方式',
judgmentCriteria: '判定标准',
},
equipmentInspectionRecord: {
moduleName: '设备点检记录',
subTitle: '设备点检记录查询',

@ -53,6 +53,35 @@
<text class="info-value">{{ formatDateTime(detailData.updateTime) }}</text>
</view>
</view>
<view class="info-card">
<view class="card-title">{{ t('equipmentInspectionTasks.subjectListTitle') }}</view>
<view v-if="subjectLoading" class="hint">{{ t('functionCommon.loading') }}</view>
<view v-else-if="!subjectList.length" class="hint">{{ t('equipmentInspectionTasks.noSubjectData') }}</view>
<view v-else class="subject-list">
<view v-for="(item, index) in subjectList" :key="item.id || index" class="subject-card">
<view class="subject-index">{{ index + 1 }}</view>
<view class="subject-body">
<view class="subject-row">
<text class="subject-label">{{ t('equipmentInspectionTasks.subjectCode') }}</text>
<text class="subject-value">{{ textValue(item.subjectCode) }}</text>
</view>
<view class="subject-row">
<text class="subject-label">{{ t('equipmentInspectionTasks.subjectName') }}</text>
<text class="subject-value">{{ textValue(item.subjectName) }}</text>
</view>
<view class="subject-row">
<text class="subject-label">{{ t('equipmentInspectionTasks.inspectionMethod') }}</text>
<text class="subject-value">{{ inspectionMethodText(item.inspectionMethod) }}</text>
</view>
<view class="subject-row">
<text class="subject-label">{{ t('equipmentInspectionTasks.judgmentCriteria') }}</text>
<text class="subject-value">{{ textValue(item.judgmentCriteria) }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
@ -69,13 +98,16 @@ import { computed, reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import { createTaskManagementTicket } from '@/api/mes/taskManagement'
import { createTaskManagementTicket, getPlanMaintenanceSubjectList } from '@/api/mes/taskManagement'
import { getDeviceLedgerList } from '@/api/mes/deviceLedger'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'
const { t } = useI18n()
const detailData = reactive({})
const ticketLoading = ref(false)
const deviceOptions = ref([])
const subjectList = ref([])
const subjectLoading = ref(false)
const taskTypeText = computed(() => {
const normalized = Number(detailData.taskType)
if (normalized === 1) return t('equipmentInspectionTasks.taskTypeInspect')
@ -120,6 +152,38 @@ function parseIdsValue(value) {
return String(value).split(',').map((item) => item.trim()).filter(Boolean)
}
function firstIdValue(value) {
return parseIdsValue(value)[0] || ''
}
function inspectionMethodText(value) {
return getDictLabel(DICT_TYPE.INSPECTION_METHOD, value, textValue(value))
}
function normalizeListData(res) {
const root = res && res.data !== undefined ? res.data : res
const data = Array.isArray(root) ? root : (root?.list || root?.data?.list || root?.records || root?.data || [])
return Array.isArray(data) ? data : []
}
async function loadSubjectList() {
const planId = firstIdValue(detailData.projectForm)
if (!planId) {
subjectList.value = []
return
}
subjectLoading.value = true
try {
const res = await getPlanMaintenanceSubjectList(planId)
subjectList.value = normalizeListData(res)
} catch (error) {
subjectList.value = []
uni.showToast({ title: t('functionCommon.loadFailed'), icon: 'none' })
} finally {
subjectLoading.value = false
}
}
onLoad(async () => {
try {
const cached = uni.getStorageSync('equipmentInspectionTasksDetail')
@ -127,22 +191,30 @@ onLoad(async () => {
Object.assign(detailData, JSON.parse(cached))
uni.removeStorageSync('equipmentInspectionTasksDetail')
}
await Promise.all([ensureDeviceOptionsLoaded()])
await initAllDict()
await Promise.all([ensureDeviceOptionsLoaded(), loadSubjectList()])
} catch (error) {
}
})
async function handleCreateTicket() {
if (!detailData.id || ticketLoading.value) return
ticketLoading.value = true
try {
await createTaskManagementTicket(detailData.id)
uni.showToast({ title: t('equipmentInspectionTasks.createTicketSuccess'), icon: 'success' })
} catch (error) {
uni.showToast({ title: t('equipmentInspectionTasks.createTicketFail'), icon: 'none' })
} finally {
ticketLoading.value = false
}
uni.showModal({
title: t('functionCommon.confirmTitle'),
content: t('equipmentInspectionTasks.confirmCreateTicketContent', { name: textValue(detailData.name) }),
success: async (res) => {
if (!res.confirm) return
ticketLoading.value = true
try {
await createTaskManagementTicket(detailData.id)
uni.showToast({ title: t('equipmentInspectionTasks.createTicketSuccess'), icon: 'success' })
} catch (error) {
uni.showToast({ title: t('equipmentInspectionTasks.createTicketFail'), icon: 'none' })
} finally {
ticketLoading.value = false
}
}
})
}
function isEnabled(value) {
@ -219,4 +291,12 @@ function formatDateTime(value) {
.action-btn-disabled {
background: #94a3b8;
}
.hint { text-align: center; color: #909399; padding: 24rpx 0; font-size: 26rpx; }
.subject-list { display: flex; flex-direction: column; gap: 16rpx; }
.subject-card { display: flex; background: #f7f9fc; border-radius: 14rpx; padding: 20rpx; gap: 16rpx; }
.subject-index { min-width: 48rpx; height: 48rpx; border-radius: 10rpx; background: #1a3a5c; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 24rpx; font-weight: 700; }
.subject-body { flex: 1; display: flex; flex-direction: column; gap: 8rpx; }
.subject-row { display: flex; justify-content: space-between; align-items: center; }
.subject-label { font-size: 24rpx; color: #8a9099; }
.subject-value { font-size: 26rpx; color: #30363d; max-width: 60%; text-align: right; }
</style>

@ -3,28 +3,37 @@
<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
id="equipment-inspection-tasks-keyword-input"
v-model="searchKeyword"
class="keyword-input"
:placeholder="t('equipmentInspectionTasks.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="taskTypeLabels" :value="taskTypeIndex" @change="onTaskTypeChange">
<view class="status-filter">
<text :class="['status-filter-text', selectedTaskType === '' ? 'placeholder' : '']">{{ currentTaskTypeLabel }}</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-tasks-keyword-input"
v-model="searchKeyword"
class="keyword-input"
:placeholder="t('equipmentInspectionTasks.searchPlaceholder')"
confirm-type="search"
@input="handleKeywordInput"
@confirm="handleSearch"
/>
</view>
<view class="icon-filter-btn" @click="resetFilters">
<uni-icons type="refresh" size="24" color="#7b8491"></uni-icons>
</view>
<view class="icon-filter-btn" @click="openFilterDrawer">
<uni-icons type="settings" size="24" color="#7b8491"></uni-icons>
</view>
</picker>
<view class="reset-filter-btn" @click="resetFilters">{{ t('functionCommon.reset') }}</view>
</view>
</view>
<scroll-view scroll-y class="list-scroll" :scroll-top="scrollTop" @scroll="onScroll" @scrolltolower="loadMore" :lower-threshold="80">
@ -63,6 +72,60 @@
<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('equipmentInspectionTasks.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('equipmentInspectionTasks.filterScope') }}</text>
</view>
<view class="drawer-field drawer-field-wide">
<text class="drawer-label">{{ t('equipmentInspectionTasks.projectFormName') }}</text>
<view class="drawer-picker" @click="togglePlanPanel">
<text :class="['drawer-picker-text', selectedPlanIds.length ? '' : 'placeholder']">{{ selectedPlanLabel }}</text>
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
</view>
<scroll-view v-if="planPanelOpen" scroll-y class="drawer-option-panel">
<view v-for="option in drawerPlanOptions" :key="option.id" :class="['drawer-option-item', selectedPlanIds.includes(String(option.id)) ? 'active' : '']" @click="togglePlanOption(option)">
<text class="drawer-option-text">{{ option.name }}</text>
<uni-icons v-if="selectedPlanIds.includes(String(option.id))" type="checkmarkempty" size="18" color="#174b78"></uni-icons>
</view>
<view v-if="!drawerPlanOptions.length" class="drawer-option-empty">{{ t('equipmentInspectionTasks.noPlanData') }}</view>
</scroll-view>
</view>
<view class="drawer-field drawer-field-wide drawer-field-gap">
<text class="drawer-label">{{ t('equipmentInspectionTasks.deviceList') }}</text>
<view class="drawer-picker" @click="toggleDevicePanel">
<text :class="['drawer-picker-text', selectedDeviceIds.length ? '' : 'placeholder']">{{ selectedDeviceLabel }}</text>
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
</view>
<scroll-view v-if="devicePanelOpen" scroll-y class="drawer-option-panel">
<view v-for="option in drawerDeviceOptions" :key="option.id" :class="['drawer-option-item', selectedDeviceIds.includes(String(option.id)) ? 'active' : '']" @click="toggleDeviceOption(option)">
<text class="drawer-option-text">{{ option.label }}</text>
<uni-icons v-if="selectedDeviceIds.includes(String(option.id))" type="checkmarkempty" size="18" color="#174b78"></uni-icons>
</view>
<view v-if="!drawerDeviceOptions.length" class="drawer-option-empty">{{ t('equipmentInspectionTasks.noDeviceData') }}</view>
</scroll-view>
</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"
v-model:show="lineCascaderShow"
@ -86,7 +149,7 @@ import { computed, nextTick, ref } from 'vue'
import { onLoad, onReachBottom, onReady, 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 { getTaskManagementPage, createTaskManagementTicket, getPlanMaintenancePage } from '@/api/mes/taskManagement'
import { getDeviceLineTree } from '@/api/mes/deviceLine'
import { getDeviceLedgerList } from '@/api/mes/deviceLedger'
@ -94,6 +157,12 @@ const { t } = useI18n()
const searchKeyword = ref('')
const selectedTaskType = ref('')
const selectedPlanIds = ref([])
const selectedDeviceIds = ref([])
const planOptions = ref([])
const planPanelOpen = ref(false)
const devicePanelOpen = ref(false)
const filterPopupRef = ref(null)
const list = ref([])
const loading = ref(false)
const loadingMore = ref(false)
@ -130,7 +199,6 @@ const lineOptions = computed(() => {
let searchTimer = null
const taskTypeOptions = computed(() => [
{ label: t('functionCommon.all'), value: '' },
{ label: t('equipmentInspectionTasks.taskTypeInspect'), value: '1' },
{ label: t('equipmentInspectionTasks.taskTypeMaintain'), value: '2' }
])
@ -141,13 +209,28 @@ const taskTypeIndex = computed(() => {
return index >= 0 ? index : 0
})
const currentTaskTypeLabel = computed(() => {
if (selectedTaskType.value === '') return t('equipmentInspectionTasks.taskType')
const current = taskTypeOptions.value.find((item) => item.value === selectedTaskType.value)
return current ? current.label : t('functionCommon.all')
return current ? current.label : t('equipmentInspectionTasks.taskType')
})
const drawerPlanOptions = computed(() => planOptions.value.map((item) => ({
id: String(item.id),
name: item.planName || item.name || String(item.id || '')
})))
const drawerDeviceOptions = computed(() => deviceOptions.value.map((item) => ({
id: String(item.id),
label: item.label || item.deviceName || String(item.id || '')
})))
const selectedPlanLabel = computed(() => formatSelectedSummary(selectedPlanIds.value, drawerPlanOptions.value, 'name', t('equipmentInspectionTasks.placeholderProjectForm')))
const selectedDeviceLabel = computed(() => formatSelectedSummary(selectedDeviceIds.value, drawerDeviceOptions.value, 'label', t('equipmentInspectionTasks.placeholderDeviceList')))
onLoad(async () => {
activateKeywordFocus()
await Promise.all([loadLineTree(), ensureDeviceOptionsLoaded(), fetchList(true)])
await Promise.all([loadLineTree(), ensurePlanOptionsLoaded(), ensureDeviceOptionsLoaded()])
await fetchList(true)
})
onShow(() => {
@ -168,6 +251,17 @@ onReachBottom(() => {
loadMore()
})
async function ensurePlanOptionsLoaded() {
try {
const res = await getPlanMaintenancePage({ pageNo: 1, pageSize: 100 })
const root = res && res.data !== undefined ? res.data : res
const data = Array.isArray(root) ? root : (root?.list || root?.data?.list || root?.records || [])
planOptions.value = (Array.isArray(data) ? data : []).filter((item) => item && item.id !== undefined && item.id !== null)
} catch (error) {
planOptions.value = []
}
}
async function ensureDeviceOptionsLoaded() {
try {
const res = await getDeviceLedgerList()
@ -205,8 +299,9 @@ async function fetchList(reset) {
pageNo: pageNo.value,
pageSize: pageSize.value,
name: deviceId ? undefined : keyword || undefined,
deviceIds: deviceId ? [deviceId] : undefined,
deviceIds: selectedDeviceIds.value.length ? selectedDeviceIds.value.join(',') : (deviceId ? [deviceId] : undefined),
taskType: selectedTaskType.value || undefined,
projectForm: selectedPlanIds.value.length ? selectedPlanIds.value.join(',') : undefined,
deviceLineId: selectedLineId.value || undefined
}
const res = await getTaskManagementPage(params)
@ -307,6 +402,56 @@ function normalizePageData(res) {
}
}
function openFilterDrawer() {
filterPopupRef.value?.open()
}
function closeFilterDrawer() {
filterPopupRef.value?.close()
}
async function confirmFilterDrawer() {
planPanelOpen.value = false
devicePanelOpen.value = false
closeFilterDrawer()
await fetchList(true)
}
function togglePlanPanel() {
planPanelOpen.value = !planPanelOpen.value
devicePanelOpen.value = false
}
function toggleDevicePanel() {
devicePanelOpen.value = !devicePanelOpen.value
planPanelOpen.value = false
}
function togglePlanOption(option) {
toggleStringSelection(selectedPlanIds, option?.id)
}
function toggleDeviceOption(option) {
toggleStringSelection(selectedDeviceIds, option?.id)
}
function toggleStringSelection(targetRef, value) {
const id = String(value ?? '')
if (!id) return
const current = targetRef.value.map((item) => String(item))
targetRef.value = current.includes(id) ? current.filter((item) => item !== id) : [...current, id]
}
function formatSelectedSummary(ids, options, labelKey, placeholder) {
const selected = Array.isArray(ids) ? ids : []
if (!selected.length) return placeholder
if (selected.length === 1) {
const found = options.find((item) => String(item.id) === String(selected[0]))
return found?.[labelKey] || placeholder
}
return t('equipmentInspectionTasks.selectedCount', { count: selected.length })
}
function handleSearch() {
clearSearchTimer()
uni.hideKeyboard()
@ -324,6 +469,11 @@ async function resetFilters() {
clearSearchTimer()
searchKeyword.value = ''
selectedTaskType.value = ''
selectedPlanIds.value = []
selectedDeviceIds.value = []
planPanelOpen.value = false
devicePanelOpen.value = false
closeFilterDrawer()
resetLineFilter()
activateKeywordFocus()
await nextTick()
@ -355,15 +505,22 @@ function openDetail(item) {
async function handleCreateTicket(item) {
if (!item?.id || ticketLoadingId.value) return
ticketLoadingId.value = item.id
try {
await createTaskManagementTicket(item.id)
uni.showToast({ title: t('equipmentInspectionTasks.createTicketSuccess'), icon: 'success' })
} catch (error) {
uni.showToast({ title: t('equipmentInspectionTasks.createTicketFail'), icon: 'none' })
} finally {
ticketLoadingId.value = null
}
uni.showModal({
title: t('functionCommon.confirmTitle'),
content: t('equipmentInspectionTasks.confirmCreateTicketContent', { name: textValue(item.name) }),
success: async (res) => {
if (!res.confirm) return
ticketLoadingId.value = item.id
try {
await createTaskManagementTicket(item.id)
uni.showToast({ title: t('equipmentInspectionTasks.createTicketSuccess'), icon: 'success' })
} catch (error) {
uni.showToast({ title: t('equipmentInspectionTasks.createTicketFail'), icon: 'none' })
} finally {
ticketLoadingId.value = null
}
}
})
}
function onScroll(event) {
@ -446,65 +603,50 @@ function formatDate(value) {
<style lang="scss" scoped>
.page-container { min-height: 100vh; background: #f4f5f7; }
.filter-bar {
display: grid;
grid-template-columns: minmax(0, 1fr) 150rpx 96rpx;
align-items: center;
gap: 14rpx;
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 20rpx;
}
.keyword-input {
width: 100%;
font-size: 26rpx;
color: #374151;
}
.status-box {
justify-content: space-between;
padding: 0 18rpx;
}
.status-box-text {
font-size: 26rpx;
color: #374151;
}
.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-option-panel { max-height: 420rpx; margin-top: 12rpx; border-radius: 12rpx; background: #f7f8fb; overflow: hidden; }
.drawer-option-item { min-height: 72rpx; padding: 0 24rpx; display: flex; align-items: center; gap: 12rpx; border-bottom: 1rpx solid #eceff3; box-sizing: border-box; }
.drawer-option-item:last-child { border-bottom: 0; }
.drawer-option-item.active { background: rgba(23, 75, 120, 0.1); }
.drawer-option-item.active .drawer-option-text { color: #174b78; font-weight: 600; }
.drawer-option-text { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 26rpx; color: #1f2937; }
.drawer-option-empty { padding: 28rpx 0; text-align: center; font-size: 26rpx; 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); }

@ -225,9 +225,8 @@ import {
import { getDeviceLineTree } from '@/api/mes/deviceLine'
const { t } = useI18n()
const DEFAULT_REPAIR_STATUS = '0'
const searchKeyword = ref('')
const selectedStatus = ref(DEFAULT_REPAIR_STATUS)
const selectedStatus = ref('')
const selectedLineId = ref('')
const repairNameFilter = ref('')
const acceptedByFilter = ref('')
@ -254,7 +253,6 @@ const focusNoKeyboardRef = ref(null)
const keywordInputSelector = '#equipment-maintenance-keyword-input input, input#equipment-maintenance-keyword-input'
const statusOptions = computed(() => [
{ label: t('functionCommon.all'), value: '' },
{ label: t('equipmentMaintenance.statusPending'), value: '0' },
{ label: t('equipmentMaintenance.statusPassed'), value: '1' },
{ label: t('equipmentMaintenance.statusRejected'), value: '2' }
@ -266,8 +264,9 @@ const statusPickerIndex = computed(() => {
return index >= 0 ? index : 0
})
const selectedStatusLabel = computed(() => {
if (selectedStatus.value === '') return t('equipmentMaintenance.status')
const current = statusOptions.value.find((item) => item.value === selectedStatus.value)
return current ? current.label : t('functionCommon.all')
return current ? current.label : t('equipmentMaintenance.status')
})
const documentStatusOptions = computed(() => [
@ -504,7 +503,7 @@ function onLineCascaderConfirm(values) {
async function resetFilters() {
searchKeyword.value = ''
selectedStatus.value = DEFAULT_REPAIR_STATUS
selectedStatus.value = ''
selectedLineId.value = ''
repairNameFilter.value = ''
acceptedByFilter.value = ''

Loading…
Cancel
Save