feat:设备台账-页面重构

master
黄伟杰 3 weeks ago
parent 5d49ea1432
commit 1b97a41b27

@ -0,0 +1,16 @@
import request from '@/utils/request'
export function getDeviceLineTree() {
return request({
url: '/admin-api/mes/device-line/tree',
method: 'get'
})
}
export function getDeviceLine(id) {
return request({
url: '/admin-api/mes/device-line/get',
method: 'get',
params: { id }
})
}

@ -26,11 +26,13 @@ import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import useUserStore from '@/store/modules/user'
import { storeToRefs } from 'pinia'
import { getTabBarMenus } from '@/utils/permissionMenu'
import { useI18n } from 'vue-i18n'
const TABBAR_VISIBILITY_EVENT = 'tabbar-visibility-change'
const userStore = useUserStore()
const { menus } = storeToRefs(userStore)
const { t } = useI18n()
const inactiveColor = '#666666'
const activeColor = '#409eff'
@ -42,6 +44,25 @@ function normalizeRoute(path = '') {
return String(path || '').trim().replace(/^\/+/, '')
}
function normalizeMenuName(value = '') {
return String(value || '').trim().toLowerCase()
}
function isMineMenu(menu) {
const names = [menu?.name, menu?.enName, menu?.text].map(normalizeMenuName)
const paths = [menu?.path, menu?.component].map((item) => normalizeRoute(item).toLowerCase())
return (
names.some((name) => ['我的', '个人中心', 'mine', 'profile'].includes(name)) ||
paths.some((path) => path === 'pages/mine' || path === 'pages/smq/mine')
)
}
function isSmqTabbar() {
const pages = getCurrentPages()
const route = pages && pages.length > 0 ? pages[pages.length - 1].route : ''
return normalizeRoute(route).startsWith('pages/smq/')
}
function isUniIcon(icon) {
return String(icon || '').startsWith('uni-icons:')
}
@ -81,6 +102,29 @@ function resolveTabIconMeta(menu) {
}
}
function createTabItem(menu) {
const iconMeta = resolveTabIconMeta(menu)
return {
text: String(menu.name || menu.enName || '').trim(),
icon: iconMeta.icon,
selectedIcon: iconMeta.selectedIcon,
iconType: iconMeta.iconType,
iconName: iconMeta.iconName,
path: menu.path
}
}
function createMineTabItem() {
return {
text: t('tab.mine'),
icon: '',
selectedIcon: '',
iconType: 'uni-icons',
iconName: 'person',
path: isSmqTabbar() ? '/pages/smq/mine' : '/pages/mine'
}
}
function getCurrentActiveIndex() {
const pages = getCurrentPages()
if (pages && pages.length > 0) {
@ -92,18 +136,11 @@ function getCurrentActiveIndex() {
}
const tabList = computed(() => {
return getTabBarMenus(menus.value)
.map((menu) => {
const iconMeta = resolveTabIconMeta(menu)
return {
text: String(menu.name || menu.enName || '').trim(),
icon: iconMeta.icon,
selectedIcon: iconMeta.selectedIcon,
iconType: iconMeta.iconType,
iconName: iconMeta.iconName,
path: menu.path
}
})
const apiTabs = getTabBarMenus(menus.value)
.filter((menu) => !isMineMenu(menu))
.map(createTabItem)
return [...apiTabs, createMineTabItem()]
})
onMounted(() => {

@ -741,6 +741,10 @@ export default {
deviceName: 'Device Name',
deviceType: 'Device Type',
deviceStatus: 'Device Status',
lineFilter: 'Line',
scanUnrecognized: 'QR code content not recognized',
scanTypeMismatch: 'QR code type does not match',
scanFailed: 'Scan failed',
deviceSpec: 'Device Spec',
isScheduled: 'Scheduled',
ratedCapacity: 'Rated Capacity',

@ -741,6 +741,10 @@ export default {
deviceName: '设备名称',
deviceType: '设备类型',
deviceStatus: '设备状态',
lineFilter: '产线',
scanUnrecognized: '未识别二维码内容',
scanTypeMismatch: '二维码类型不匹配',
scanFailed: '扫码失败',
deviceSpec: '设备规格',
isScheduled: '是否排产',
ratedCapacity: '额定产能',

@ -1,7 +1,7 @@
<template>
<view class="scroll-container">
<view class="login-container">
<!-- <NavBar title=" " /> -->
<!-- Logo区域 -->
<view class="logo-section">
<view class="logo-wrapper">

@ -20,7 +20,18 @@
</view>
<view class="info-row">
<text class="info-label">{{ t('equipmentLedger.deviceStatus') }}</text>
<text :class="['info-value', getStatusClass(detailData?.deviceStatus)]">{{ getStatusText(detailData?.deviceStatus) }}</text>
<picker
class="status-picker-wrap"
:range="statusChangeLabels"
:value="getStatusChangeIndex(detailData?.deviceStatus)"
:disabled="!detailData || statusUpdating"
@change="onStatusChange"
>
<view class="status-picker">
<text :class="['info-value', 'status-value', getStatusClass(detailData?.deviceStatus)]">{{ getStatusText(detailData?.deviceStatus) }}</text>
<uni-icons type="bottom" size="14" color="#8a9099"></uni-icons>
</view>
</picker>
</view>
<view class="info-row">
<text class="info-label">{{ t('equipmentLedger.deviceType') }}</text>
@ -149,12 +160,14 @@ import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import { getDeviceLedger } from '@/api/mes/deviceLedger'
import { getDeviceLedger, updateDeviceLedger } from '@/api/mes/deviceLedger'
import { getDeviceTypeTree } from '@/api/mes/deviceType'
import { DICT_TYPE, getDictLabel, initAllDict } from '@/utils/dict'
import useDictStore from '@/store/modules/dict'
import request from '@/utils/request'
const { t } = useI18n()
const dictStore = useDictStore()
const detailId = ref(undefined)
const detailData = ref(null)
const deviceTypeList = ref([])
@ -162,6 +175,7 @@ const activeTab = ref('check')
const inspectionList = ref([])
const maintainList = ref([])
const repairList = ref([])
const statusUpdating = ref(false)
const tabs = computed(() => [
{ key: 'check', label: t('equipmentLedger.checkHistory') },
@ -169,6 +183,15 @@ const tabs = computed(() => [
{ key: 'repair', label: t('equipmentLedger.repairHistory') }
])
const statusOptions = computed(() => {
const dicts = dictStore.getDict(DICT_TYPE.MES_TZ_STATUS) || []
return dicts
.filter(item => item?.value !== null && item?.value !== undefined && String(item.value) !== '')
.map(item => ({ label: item.label, value: item.value }))
})
const statusChangeLabels = computed(() => statusOptions.value.map(item => item.label))
const scheduledText = computed(() => {
const val = detailData.value?.isSchedueld ?? detailData.value?.isScheduled
return Number(val) === 1 ? t('equipmentLedger.yes') : t('equipmentLedger.no')
@ -265,10 +288,38 @@ function getStatusText(status) {
function getStatusClass(status) {
const s = String(status)
if (s === '0' || s === '1') return 'text-success'
if (s === '0') return 'text-success'
if (s === '1') return 'text-warning'
return 'text-danger'
}
function getStatusChangeIndex(status) {
const idx = statusOptions.value.findIndex(item => String(item.value) === String(status))
return idx >= 0 ? idx : 0
}
async function onStatusChange(e) {
if (!detailData.value?.id) {
uni.showToast({ title: t('equipmentLedger.noId'), icon: 'none' })
return
}
const nextOption = statusOptions.value[Number(e?.detail?.value || 0)]
if (!nextOption || String(nextOption.value) === String(detailData.value.deviceStatus)) return
const oldStatus = detailData.value.deviceStatus
detailData.value.deviceStatus = nextOption.value
statusUpdating.value = true
try {
await updateDeviceLedger({ id: detailData.value.id, deviceStatus: nextOption.value })
uni.showToast({ title: t('functionCommon.updateSuccess'), icon: 'success' })
} catch (err) {
detailData.value.deviceStatus = oldStatus
uni.showToast({ title: t('functionCommon.saveFailed'), icon: 'none' })
} finally {
statusUpdating.value = false
}
}
function getResultText(result) {
if (result === 'PASS' || result === 'pass' || result === 'OK' || result === 'ok' || result === 1 || result === '1') return t('equipmentLedger.resultPass')
if (result === 'FAIL' || result === 'fail' || result === 'NG' || result === 'ng' || result === 0 || result === '0') return t('equipmentLedger.resultFail')
@ -348,7 +399,7 @@ function formatDateTime(value) {
<style lang="scss" scoped>
.page-container { min-height: 100vh; background-color: #f0f2f5; }
.fixed-header { position: sticky; top: 0; z-index: 10; }
.detail-scroll { height: calc(100vh - 120rpx); }
// .detail-scroll { height: calc(100vh - 120rpx); }
.content-section { padding: 0 24rpx 24rpx; }
.info-card { margin-top: 20rpx; background: #ffffff; border-radius: 20rpx; padding: 28rpx; box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); }
.card-title { font-size: 32rpx; color: #1a3a5c; font-weight: 700; margin-bottom: 18rpx; }
@ -356,6 +407,9 @@ function formatDateTime(value) {
.info-row { display: flex; justify-content: space-between; align-items: flex-start; padding: 18rpx 0; border-bottom: 1rpx solid #edf0f3; }
.info-label { font-size: 27rpx; color: #8a9099; width: 220rpx; }
.info-value { flex: 1; text-align: right; font-size: 28rpx; color: #30363d; line-height: 1.45; }
.status-picker-wrap { flex: 1; }
.status-picker { flex: 1; display: flex; align-items: center; justify-content: flex-end; gap: 8rpx; }
.status-value { flex: 0 1 auto; }
.text-success { color: #52c41a; }
.text-danger { color: #ff4d4f; }
.text-warning { color: #faad14; }

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save