|
|
|
|
@ -0,0 +1,733 @@
|
|
|
|
|
<template>
|
|
|
|
|
<view class="page-container">
|
|
|
|
|
<NavBar :title="t('moldOperate.tabDown')" />
|
|
|
|
|
|
|
|
|
|
<!-- 操作按钮区 -->
|
|
|
|
|
<view class="action-row">
|
|
|
|
|
<view class="action-btn scan-btn" @click="handleScan">
|
|
|
|
|
<view class="btn-icon-wrap">
|
|
|
|
|
<text class="iconfont icon-scan btn-icon"></text>
|
|
|
|
|
</view>
|
|
|
|
|
<text class="btn-text">{{ t('moldOperate.scanDevice') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="action-btn select-btn" @click="openDevicePicker">
|
|
|
|
|
<view class="btn-icon-wrap">
|
|
|
|
|
<text class="iconfont icon-device btn-icon"></text>
|
|
|
|
|
</view>
|
|
|
|
|
<text class="btn-text">{{ t('moldOperate.selectDevice') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 设备信息卡片 -->
|
|
|
|
|
<view class="section-card">
|
|
|
|
|
<view class="section-title-bar">
|
|
|
|
|
<view class="section-bar-line"></view>
|
|
|
|
|
<text class="section-title">{{ t('moldOperate.deviceInfo') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="card-body-grid">
|
|
|
|
|
<view class="grid-row">
|
|
|
|
|
<view class="grid-cell">
|
|
|
|
|
<text class="grid-label">{{ t('moldOperate.deviceName') }}</text>
|
|
|
|
|
<text class="grid-value">{{ textValue(selectedDevice.deviceName) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="grid-cell">
|
|
|
|
|
<text class="grid-label">{{ t('moldOperate.deviceCode') }}</text>
|
|
|
|
|
<text class="grid-value">{{ textValue(selectedDevice.deviceCode) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="grid-row">
|
|
|
|
|
<view class="grid-cell">
|
|
|
|
|
<text class="grid-label">{{ t('moldOperate.productionLine') }}</text>
|
|
|
|
|
<text class="grid-value">{{ textValue(selectedDevice.workshopName) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="grid-cell">
|
|
|
|
|
<text class="grid-label">{{ t('moldOperate.deviceStatus') }}</text>
|
|
|
|
|
<view class="status-tag" :class="deviceStatusClass">{{ deviceStatusLabel }}</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 当前在机模具 -->
|
|
|
|
|
<view class="section-card">
|
|
|
|
|
<view class="section-title-bar">
|
|
|
|
|
<view class="section-bar-line"></view>
|
|
|
|
|
<text class="section-title">{{ t('moldOperate.currentMoldInfo') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<template v-if="selectedMold && selectedMold.id">
|
|
|
|
|
<view class="card-body-grid">
|
|
|
|
|
<view class="grid-row">
|
|
|
|
|
<view class="grid-cell">
|
|
|
|
|
<text class="grid-label">{{ t('moldOperate.moldName') }}</text>
|
|
|
|
|
<text class="grid-value highlight">{{ textValue(selectedMold.moldName) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="grid-cell">
|
|
|
|
|
<text class="grid-label">{{ t('moldOperate.moldCode') }}</text>
|
|
|
|
|
<text class="grid-value">{{ textValue(selectedMold.moldCode) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="grid-row">
|
|
|
|
|
<view class="grid-cell">
|
|
|
|
|
<text class="grid-label">{{ t('moldOperate.product') }}</text>
|
|
|
|
|
<text class="grid-value">{{ textValue(selectedMold.productName) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="grid-cell">
|
|
|
|
|
<text class="grid-label">{{ t('moldOperate.mountTime') }}</text>
|
|
|
|
|
<text class="grid-value">{{ textValue(selectedMold.mountTime) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="grid-row">
|
|
|
|
|
<view class="grid-cell full-width">
|
|
|
|
|
<text class="grid-label">{{ t('moldOperate.useCount') }}</text>
|
|
|
|
|
<text class="grid-value highlight">{{ formatUseCount(selectedMold.useCount) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 更换下模对象按钮 -->
|
|
|
|
|
<view class="change-target-btn" @click="openLowerMoldPicker">
|
|
|
|
|
<text class="change-icon">⇄</text>
|
|
|
|
|
<text class="change-text">{{ t('moldOperate.changeTarget') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- 已选设备但无在机模具 -->
|
|
|
|
|
<view v-else-if="selectedDevice.id && moldsLoaded" class="empty-mold-hint">
|
|
|
|
|
<text class="empty-mold-text empty-gray">{{ t('moldOperate.noMoldOnDevice') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 未选设备 -->
|
|
|
|
|
<view v-else class="empty-mold-hint" @click="handleSelectDeviceFirst">
|
|
|
|
|
<text class="empty-mold-text">+ {{ t('moldOperate.clickSelectDeviceFirst') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 底部操作栏 -->
|
|
|
|
|
<view class="bottom-actions">
|
|
|
|
|
<view class="bottom-btn confirm-btn" @click="handleConfirm">{{ t('moldOperate.confirmDismount') }}</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 在机模具选择弹框(更换下模对象) -->
|
|
|
|
|
<uni-popup ref="lowerMoldPickerRef" type="bottom">
|
|
|
|
|
<view class="picker-popup">
|
|
|
|
|
<view class="picker-header">
|
|
|
|
|
<text class="picker-close-left" @click="closeLowerMoldPicker">{{ t('functionCommon.cancel') }}</text>
|
|
|
|
|
<text class="picker-title">{{ t('moldOperate.currentMoldInfo') }}</text>
|
|
|
|
|
<text class="picker-close-right" @click="confirmLowerMoldSelection">{{ t('functionCommon.confirm') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<scroll-view scroll-y class="picker-scroll">
|
|
|
|
|
<view v-for="m in lowerMoldOptions" :key="m.id || m.moldId"
|
|
|
|
|
class="picker-item" :class="{ active: tempSelectedMoldId === String(m.id || m.moldId) }"
|
|
|
|
|
@click="tempSelectedMoldId = String(m.id || m.moldId)">
|
|
|
|
|
<view class="picker-item-content">
|
|
|
|
|
<text class="picker-item-name">{{ textValue(m.moldName || m.name) }}</text>
|
|
|
|
|
<text class="picker-item-code">{{ textValue(m.moldCode || m.code) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<text class="picker-check-icon" v-if="tempSelectedMoldId === String(m.id || m.moldId)">✓</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-if="lowerMoldLoading" class="picker-hint">{{ t('functionCommon.loading') }}</view>
|
|
|
|
|
<view v-else-if="!lowerMoldOptions.length" class="picker-empty">{{ t('moldOperate.noMoldOnDevice') }}</view>
|
|
|
|
|
</scroll-view>
|
|
|
|
|
</view>
|
|
|
|
|
</uni-popup>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { computed, reactive, ref } from 'vue'
|
|
|
|
|
import { onShow } from '@dcloudio/uni-app'
|
|
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
|
import NavBar from '@/components/common/NavBar.vue'
|
|
|
|
|
import { getLowerMoldList, getDeviceLedgerList, createMoldOperate } from '@/api/mes/moldoperate'
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
|
|
|
// ---- 设备相关 ----
|
|
|
|
|
const selectedDevice = ref({})
|
|
|
|
|
const deviceOptions = ref([])
|
|
|
|
|
|
|
|
|
|
// ---- 模具相关 ----
|
|
|
|
|
const selectedMold = ref({})
|
|
|
|
|
const lowerMoldPickerRef = ref(null)
|
|
|
|
|
const lowerMoldOptions = ref([])
|
|
|
|
|
const lowerMoldLoading = ref(false)
|
|
|
|
|
const moldsLoaded = ref(false)
|
|
|
|
|
const tempSelectedMoldId = ref(null)
|
|
|
|
|
|
|
|
|
|
// ---- 工具函数 ----
|
|
|
|
|
function textValue(v) {
|
|
|
|
|
if (v === 0) return '0'
|
|
|
|
|
if (v == null) return '-'
|
|
|
|
|
const s = String(v).trim()
|
|
|
|
|
return s || '-'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatUseCount(count) {
|
|
|
|
|
if (count == null) return '-'
|
|
|
|
|
const num = Number(count)
|
|
|
|
|
if (isNaN(num)) return String(count)
|
|
|
|
|
return num.toLocaleString() + ' ' + t('moldOperate.countUnit')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatTime(time) {
|
|
|
|
|
if (!time) return '-'
|
|
|
|
|
if (typeof time === 'string') {
|
|
|
|
|
if (time.includes('-') || time.includes('T')) {
|
|
|
|
|
return time.replace(/T/, ' ').substring(0, 19)
|
|
|
|
|
}
|
|
|
|
|
const ts = Number(time)
|
|
|
|
|
if (!isNaN(ts) && ts > 1e10) return new Date(ts).toLocaleString()
|
|
|
|
|
else if (!isNaN(ts) && ts > 1e7) return new Date(ts * 1000).toLocaleString()
|
|
|
|
|
return time
|
|
|
|
|
}
|
|
|
|
|
if (typeof time === 'number') {
|
|
|
|
|
if (time > 1e10) return new Date(time).toLocaleString()
|
|
|
|
|
if (time > 1e7) return new Date(time * 1000).toLocaleString()
|
|
|
|
|
return String(time)
|
|
|
|
|
}
|
|
|
|
|
return '-'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const deviceStatusClass = computed(() => {
|
|
|
|
|
const status = Number(selectedDevice.value?.deviceStatus)
|
|
|
|
|
if (status === 0) return 'running-tag'
|
|
|
|
|
if (status === 1) return 'stop-tag'
|
|
|
|
|
if (status >= 2) return 'fault-tag'
|
|
|
|
|
return ''
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const deviceStatusLabel = computed(() => {
|
|
|
|
|
const status = Number(selectedDevice.value?.deviceStatus)
|
|
|
|
|
const map = {
|
|
|
|
|
0: t('moldOperate.statusRunning'),
|
|
|
|
|
1: t('moldOperate.statusStop'),
|
|
|
|
|
2: t('moldOperate.statusFault'),
|
|
|
|
|
3: t('moldOperate.statusFault')
|
|
|
|
|
}
|
|
|
|
|
return map[status] || textValue(selectedDevice.value?.deviceStatus) || '-'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// ---- 数据加载 ----
|
|
|
|
|
async function loadDevices() {
|
|
|
|
|
try {
|
|
|
|
|
const res = await getDeviceLedgerList({ pageNo: 1, pageSize: 100 })
|
|
|
|
|
const root = res && res.data !== undefined ? res.data : res
|
|
|
|
|
const data = Array.isArray(root)
|
|
|
|
|
? root
|
|
|
|
|
: Array.isArray(root?.list) ? root.list
|
|
|
|
|
: Array.isArray(root?.rows) ? root.rows
|
|
|
|
|
: []
|
|
|
|
|
deviceOptions.value = data.map((d) => ({
|
|
|
|
|
value: d.id,
|
|
|
|
|
label: `${d.deviceCode || ''} ${d.deviceName || ''}`.trim(),
|
|
|
|
|
raw: d
|
|
|
|
|
}))
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('loadDevices error', e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 通用递归数组提取
|
|
|
|
|
function findArr(obj, d = 0) {
|
|
|
|
|
if (!obj || typeof obj !== 'object' || d > 3) return []
|
|
|
|
|
if (Array.isArray(obj)) return obj
|
|
|
|
|
for (const k of ['list', 'rows', 'records', 'items', 'data']) {
|
|
|
|
|
if (Array.isArray(obj[k])) return obj[k]
|
|
|
|
|
}
|
|
|
|
|
for (const v of Object.values(obj)) {
|
|
|
|
|
const found = findArr(v, d + 1)
|
|
|
|
|
if (found.length) return found
|
|
|
|
|
}
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadLowerMolds(deviceId) {
|
|
|
|
|
lowerMoldLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
// ====== 策略1:优先从持久化存储读取(按设备ID索引,不会互覆盖)======
|
|
|
|
|
let map = {}
|
|
|
|
|
try { map = uni.getStorageSync('_mountedMoldInfoMap') || {} } catch {}
|
|
|
|
|
const savedInfo = map[String(deviceId)] || null
|
|
|
|
|
|
|
|
|
|
console.log('[下模] 持久化在机模具信息 =', savedInfo ? JSON.stringify(savedInfo) : 'null', 'deviceId=', deviceId)
|
|
|
|
|
|
|
|
|
|
if (savedInfo && savedInfo.mold) {
|
|
|
|
|
console.log('[下模] 命中持久化数据, deviceId=', deviceId)
|
|
|
|
|
const m = savedInfo.mold
|
|
|
|
|
lowerMoldOptions.value = [{
|
|
|
|
|
id: m.id,
|
|
|
|
|
moldId: m.id,
|
|
|
|
|
moldName: m.name || m.moldName || '-',
|
|
|
|
|
moldCode: m.code || m.moldCode || '-',
|
|
|
|
|
productName: m.productName || '-',
|
|
|
|
|
mountTime: formatTime(savedInfo.mountTime),
|
|
|
|
|
useCount: '-'
|
|
|
|
|
}]
|
|
|
|
|
console.log('[下模] 使用持久化数据 =', JSON.stringify(lowerMoldOptions.value[0]))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
console.log('[下模] 未命中持久化数据,进入接口查询')
|
|
|
|
|
|
|
|
|
|
// ====== 策略2:fallback - 调用在机模具接口 ======
|
|
|
|
|
let list = []
|
|
|
|
|
try {
|
|
|
|
|
const res = await getLowerMoldList(deviceId)
|
|
|
|
|
const root = (res && res.data !== undefined) ? res.data : res
|
|
|
|
|
list = findArr(root)
|
|
|
|
|
} catch (apiErr) {
|
|
|
|
|
console.warn('[下模] getLowerMoldList 接口异常', apiErr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lowerMoldOptions.value = list
|
|
|
|
|
console.log('[下模] loadLowerMolds 返回 list.length =', list.length)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('loadLowerMolds error', e)
|
|
|
|
|
} finally {
|
|
|
|
|
lowerMoldLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- 扫描设备码 ----
|
|
|
|
|
function handleScan() {
|
|
|
|
|
uni.scanCode({
|
|
|
|
|
onlyFromCamera: false,
|
|
|
|
|
scanType: ['qrCode', 'barCode'],
|
|
|
|
|
success(res) {
|
|
|
|
|
const code = res.result?.trim()
|
|
|
|
|
if (!code) return
|
|
|
|
|
const matched = deviceOptions.value.find((d) =>
|
|
|
|
|
d.raw.deviceCode === code || String(d.raw.code) === code || d.label.includes(code)
|
|
|
|
|
)
|
|
|
|
|
if (matched) {
|
|
|
|
|
selectDevice(matched.raw)
|
|
|
|
|
} else {
|
|
|
|
|
uni.showToast({ title: t('moldOperate.deviceNotFound'), icon: 'none' })
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
fail(err) {
|
|
|
|
|
if (err.errMsg && !err.errMsg.includes('cancel')) {
|
|
|
|
|
console.warn('scan failed:', err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- 设备选择 ----
|
|
|
|
|
function selectDevice(device) {
|
|
|
|
|
selectedDevice.value = device || {}
|
|
|
|
|
moldsLoaded.value = false
|
|
|
|
|
selectedMold.value = {}
|
|
|
|
|
if (device?.id) {
|
|
|
|
|
loadLowerMolds(device.id).then(() => {
|
|
|
|
|
moldsLoaded.value = true
|
|
|
|
|
if (lowerMoldOptions.value.length > 0) {
|
|
|
|
|
const first = lowerMoldOptions.value[0]
|
|
|
|
|
selectedMold.value = {
|
|
|
|
|
id: first.id || first.moldId,
|
|
|
|
|
moldName: first.moldName || first.name,
|
|
|
|
|
moldCode: first.moldCode || first.code,
|
|
|
|
|
productName: first.productName || '-',
|
|
|
|
|
mountTime: first.mountTime || '-',
|
|
|
|
|
useCount: first.useCount ?? '-'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
function openDevicePicker() {
|
|
|
|
|
uni.navigateTo({ url: '/pages_function/pages/moldoperate/deviceSelect' })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ---- 在机模具选择(更换下模对象)----
|
|
|
|
|
function openLowerMoldPicker() {
|
|
|
|
|
if (!selectedDevice.value?.id) {
|
|
|
|
|
uni.showToast({ title: t('moldOperate.validatorDeviceRequired'), icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
tempSelectedMoldId.value = selectedMold.value ? String(selectedMold.value.id || selectedMold.value.moldId) : null
|
|
|
|
|
loadLowerMolds(selectedDevice.value.id).then(() => {
|
|
|
|
|
lowerMoldPickerRef.value?.open()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function confirmLowerMoldSelection() {
|
|
|
|
|
const mold = lowerMoldOptions.value.find(
|
|
|
|
|
(m) => String(m.id || m.moldId) === tempSelectedMoldId.value
|
|
|
|
|
)
|
|
|
|
|
if (mold) {
|
|
|
|
|
selectedMold.value = {
|
|
|
|
|
id: mold.id || mold.moldId,
|
|
|
|
|
moldName: mold.moldName || mold.name,
|
|
|
|
|
moldCode: mold.moldCode || mold.code,
|
|
|
|
|
productName: mold.productName || '-',
|
|
|
|
|
mountTime: mold.mountTime || '-',
|
|
|
|
|
useCount: mold.useCount ?? '-'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lowerMoldPickerRef.value?.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeLowerMoldPicker() { lowerMoldPickerRef.value?.close() }
|
|
|
|
|
|
|
|
|
|
function handleSelectDeviceFirst() {
|
|
|
|
|
openDevicePicker()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- 提交 ----
|
|
|
|
|
function validForm() {
|
|
|
|
|
if (!selectedDevice.value?.id) {
|
|
|
|
|
uni.showToast({ title: t('moldOperate.validatorDeviceRequired'), icon: 'none' })
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if (!selectedMold.value?.id) {
|
|
|
|
|
uni.showToast({ title: t('moldOperate.validatorLowerMoldRequired'), icon: 'none' })
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function handleConfirm() {
|
|
|
|
|
if (!validForm()) return
|
|
|
|
|
try {
|
|
|
|
|
const payload = {
|
|
|
|
|
operateType: '2',
|
|
|
|
|
deviceId: String(selectedDevice.value.id),
|
|
|
|
|
moldId: String(selectedMold.value.id)
|
|
|
|
|
}
|
|
|
|
|
console.log('[下模] 提交参数 =', JSON.stringify(payload))
|
|
|
|
|
await createMoldOperate(payload)
|
|
|
|
|
uni.showToast({ title: t('functionCommon.createSuccess'), icon: 'success' })
|
|
|
|
|
// 下模成功后清除该设备的持久化数据
|
|
|
|
|
if (selectedDevice.value?.id) {
|
|
|
|
|
try {
|
|
|
|
|
const map = uni.getStorageSync('_mountedMoldInfoMap') || {}
|
|
|
|
|
delete map[String(selectedDevice.value.id)]
|
|
|
|
|
uni.setStorageSync('_mountedMoldInfoMap', map)
|
|
|
|
|
} catch {}
|
|
|
|
|
}
|
|
|
|
|
// 重置表单
|
|
|
|
|
selectedDevice.value = {}
|
|
|
|
|
selectedMold.value = {}
|
|
|
|
|
lowerMoldOptions.value = []
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('[下模] 保存失败 =', e)
|
|
|
|
|
const errMsg = e?.msg || (typeof e === 'string' ? e : e?.message) || '系统异常'
|
|
|
|
|
uni.showToast({ title: t('functionCommon.saveFailed') + ': ' + errMsg, icon: 'none', duration: 3000 })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onShow(async () => {
|
|
|
|
|
await Promise.allSettled([loadDevices()])
|
|
|
|
|
// 从 globalData 读取设备选择页回传的设备
|
|
|
|
|
const device = getApp().globalData._deviceSelectResult
|
|
|
|
|
if (device) {
|
|
|
|
|
getApp().globalData._deviceSelectResult = null
|
|
|
|
|
selectDevice(device)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.page-container {
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
background: #f5f6f8;
|
|
|
|
|
padding-bottom: 140rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ====== 操作按钮区 ====== */
|
|
|
|
|
.action-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
padding: 20rpx 24rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.action-btn {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 12rpx;
|
|
|
|
|
height: 96rpx;
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
background: #2563eb;
|
|
|
|
|
color: #fff;
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
|
|
|
|
.btn-icon-wrap {
|
|
|
|
|
width: 44rpx;
|
|
|
|
|
height: 44rpx;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-icon {
|
|
|
|
|
font-size: 36rpx;
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-text {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ====== 卡片通用样式 ====== */
|
|
|
|
|
.section-card {
|
|
|
|
|
margin: 16rpx 24rpx;
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
border-radius: 14rpx;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-title-bar {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10rpx;
|
|
|
|
|
padding: 22rpx 24rpx 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-bar-line {
|
|
|
|
|
width: 6rpx;
|
|
|
|
|
height: 32rpx;
|
|
|
|
|
border-radius: 3rpx;
|
|
|
|
|
background: #2563eb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: #1a1a1a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-body-grid {
|
|
|
|
|
padding: 20rpx 24rpx 28rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ====== 网格布局 ====== */
|
|
|
|
|
.grid-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
margin-top: 18rpx;
|
|
|
|
|
|
|
|
|
|
&:first-child {
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.grid-cell {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
|
|
|
|
&.full-width {
|
|
|
|
|
flex-basis: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 12rpx;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.grid-label {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
color: #999;
|
|
|
|
|
margin-bottom: 6rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.grid-value {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 27rpx;
|
|
|
|
|
color: #333;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
|
|
|
|
&.highlight {
|
|
|
|
|
color: #2563eb;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ====== 状态标签 ====== */
|
|
|
|
|
.status-tag {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 6rpx 18rpx;
|
|
|
|
|
border-radius: 6rpx;
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
|
|
|
|
&.running-tag {
|
|
|
|
|
color: #059669;
|
|
|
|
|
background: #ecfdf5;
|
|
|
|
|
border: 1rpx solid #a7f3d0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.stop-tag {
|
|
|
|
|
color: #d97706;
|
|
|
|
|
background: #fffbeb;
|
|
|
|
|
border: 1rpx solid #fde68a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.fault-tag {
|
|
|
|
|
color: #dc2626;
|
|
|
|
|
background: #fef2f2;
|
|
|
|
|
border: 1rpx solid #fecaca;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.pending-tag {
|
|
|
|
|
color: #2563eb;
|
|
|
|
|
background: #eff6ff;
|
|
|
|
|
border: 1rpx solid #bfdbfe;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ====== 更换下模对象按钮 ====== */
|
|
|
|
|
.change-target-btn {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 8rpx;
|
|
|
|
|
margin: 0 24rpx 24rpx;
|
|
|
|
|
padding: 20rpx 0;
|
|
|
|
|
border: 1rpx dashed #ccc;
|
|
|
|
|
border-radius: 10rpx;
|
|
|
|
|
|
|
|
|
|
.change-icon {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #666;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.change-text {
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
color: #666;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
background: #fafafa;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ====== 空提示 ====== */
|
|
|
|
|
.empty-mold-hint {
|
|
|
|
|
padding: 40rpx 24rpx;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.empty-mold-text {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #2563eb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ====== 底部操作栏 ====== */
|
|
|
|
|
.bottom-actions {
|
|
|
|
|
position: fixed;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
|
|
|
|
|
background: #fff;
|
|
|
|
|
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
|
|
|
|
|
z-index: 99;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bottom-btn {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 88rpx;
|
|
|
|
|
line-height: 88rpx;
|
|
|
|
|
text-align: center;
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.confirm-btn {
|
|
|
|
|
background: #2563eb;
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ====== 弹窗通用 ====== */
|
|
|
|
|
.picker-popup {
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 24rpx 24rpx 0 0;
|
|
|
|
|
max-height: 75vh;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-header {
|
|
|
|
|
height: 96rpx;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 0 30rpx;
|
|
|
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-close-left,
|
|
|
|
|
.picker-close-right {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #2563eb;
|
|
|
|
|
padding: 8rpx 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-title {
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: #1a1a1a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-scroll {
|
|
|
|
|
max-height: calc(75vh - 96rpx);
|
|
|
|
|
padding: 12rpx 0 32rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
margin: 0 24rpx 12rpx;
|
|
|
|
|
padding: 22rpx 20rpx;
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
|
|
|
|
|
&.active {
|
|
|
|
|
background: #eff6ff;
|
|
|
|
|
border: 1rpx solid #bfdbfe;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-item-content {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-item-name {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #1a1a1a;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-item-code {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 23rpx;
|
|
|
|
|
color: #999;
|
|
|
|
|
margin-top: 4rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-check-icon {
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
color: #2563eb;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
margin-left: 16rpx;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-empty,
|
|
|
|
|
.picker-hint {
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: #999;
|
|
|
|
|
padding: 60rpx 0;
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
}
|
|
|
|
|
</style>
|