|
|
<template>
|
|
|
<view class="page-container">
|
|
|
<!-- 顶部导航栏 -->
|
|
|
<NavBar :title="t('moldOperate.tabUp')">
|
|
|
<template #right>
|
|
|
<view class="nav-right-btn" @click="goToHistory">
|
|
|
<uni-icons type="calendar" size="22" color="#ffffff"></uni-icons>
|
|
|
</view>
|
|
|
</template>
|
|
|
</NavBar>
|
|
|
|
|
|
<!-- ========== 上模 ========== -->
|
|
|
<!-- 操作按钮区 -->
|
|
|
<view class="action-row">
|
|
|
<view class="scan-input-row">
|
|
|
<input
|
|
|
id="mold-scan-input"
|
|
|
class="scan-input"
|
|
|
v-model="scanCodeInput"
|
|
|
placeholder="红外扫码或输入设备码"
|
|
|
confirm-type="done"
|
|
|
@confirm="onScanInputConfirm"
|
|
|
/>
|
|
|
</view>
|
|
|
<view class="action-btn select-btn" @click="openDevicePicker">
|
|
|
<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.selectedDevice') }}</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.currentMold') }}</text>
|
|
|
<text class="grid-value highlight">{{ currentMoldDisplay }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="grid-row">
|
|
|
<view class="grid-cell full-width">
|
|
|
<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.selectMountMold') }}</text>
|
|
|
</view>
|
|
|
|
|
|
<template v-if="selectedMountMolds.length > 0">
|
|
|
<view v-for="(mold, index) in selectedMountMolds" :key="mold.id || index" 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(mold.name || mold.moldName) }}</text>
|
|
|
</view>
|
|
|
<view class="grid-cell">
|
|
|
<text class="grid-label">{{ t('moldOperate.moldCode') }}</text>
|
|
|
<text class="grid-value">{{ textValue(mold.code || mold.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(mold.productName) }}</text>
|
|
|
</view>
|
|
|
<view class="grid-cell">
|
|
|
<text class="grid-label">{{ t('moldOperate.status') }}</text>
|
|
|
<view :class="['status-tag', getMoldStatusClass(mold.status)]">{{ getMoldStatusText(mold.status) }}</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 更换上模对象按钮 -->
|
|
|
<view class="change-target-btn" @click="openMountMoldPicker">
|
|
|
<text class="change-icon">⇄</text>
|
|
|
<text class="change-text">{{ t('moldOperate.changeMountTarget') }}</text>
|
|
|
</view>
|
|
|
</template>
|
|
|
<view v-else class="empty-mold-hint" @click="openMountMoldPicker">
|
|
|
<text class="empty-mold-text">+ {{ t('moldOperate.clickSelectMold') }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 操作人与备注 -->
|
|
|
<view class="section-card">
|
|
|
<view class="section-title-bar">
|
|
|
<view class="section-bar-line"></view>
|
|
|
<text class="section-title">{{ t('moldOperate.operator') + ' & ' + t('moldOperate.remark') }}</text>
|
|
|
</view>
|
|
|
<view class="card-body-grid">
|
|
|
<view class="form-row">
|
|
|
<view class="form-cell">
|
|
|
<text class="form-label"><text class="required">*</text>{{ t('moldOperate.operator') }}</text>
|
|
|
<view class="dropdown-input readonly-input">
|
|
|
<text class="dropdown-value">{{ currentUserName }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="form-row">
|
|
|
<view class="form-cell">
|
|
|
<text class="form-label">{{ t('moldOperate.remark') }}</text>
|
|
|
<input
|
|
|
class="form-input"
|
|
|
v-model="remarkText"
|
|
|
:placeholder="t('moldOperate.placeholderRemark')"
|
|
|
:maxlength="200"
|
|
|
/>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 底部操作栏 - 上模 -->
|
|
|
<view class="bottom-actions">
|
|
|
<view class="bottom-btn cancel-btn" @click="handleCancel">{{ t('functionCommon.cancel') }}</view>
|
|
|
<view class="bottom-btn confirm-btn" @click="handleConfirmMount">{{ t('moldOperate.confirmMount') }}</view>
|
|
|
</view>
|
|
|
|
|
|
<sv-focus-no-keyboard ref="focusNoKeyboardRef"></sv-focus-no-keyboard>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import { computed, ref, nextTick } from 'vue'
|
|
|
import { onReady, onShow } from '@dcloudio/uni-app'
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
import NavBar from '@/components/common/NavBar.vue'
|
|
|
import { getDeviceLedgerList, createMoldOperate } from '@/api/mes/moldoperate'
|
|
|
import { getMoldBrandPage } from '@/api/mes/mold'
|
|
|
import { getDeviceLineTree } from '@/api/mes/deviceLine'
|
|
|
import useUserStore from '@/store/modules/user'
|
|
|
|
|
|
const { t } = useI18n()
|
|
|
const userStore = useUserStore()
|
|
|
|
|
|
// 当前登录用户
|
|
|
const currentUserName = computed(() => userStore.name || '未知用户')
|
|
|
const currentUserId = computed(() => userStore.userId)
|
|
|
|
|
|
// ---- 工具函数 ----
|
|
|
function textValue(v) {
|
|
|
if (v === 0) return '0'
|
|
|
if (v == null) return '-'
|
|
|
const s = String(v).trim()
|
|
|
return s || '-'
|
|
|
}
|
|
|
|
|
|
// ==================== 共享: 设备列表 ====================
|
|
|
const deviceOptions = ref([])
|
|
|
|
|
|
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
|
|
|
: Array.isArray(root?.records) ? root.records
|
|
|
: []
|
|
|
deviceOptions.value = data.map((d) => ({
|
|
|
value: d.id,
|
|
|
label: `${d.deviceCode || ''} ${d.deviceName || ''}`.trim(),
|
|
|
raw: d
|
|
|
}))
|
|
|
} catch (e) {
|
|
|
console.error('loadDevices error', e)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 产线树 - 用于设备产线名称转换
|
|
|
const lineInfoMap = ref(new Map())
|
|
|
|
|
|
function flattenLineTree(nodes, parentId) {
|
|
|
if (!Array.isArray(nodes)) return
|
|
|
for (const node of nodes) {
|
|
|
if (node.id != null && node.name != null) {
|
|
|
lineInfoMap.value.set(String(node.id), {
|
|
|
id: node.id,
|
|
|
name: node.name,
|
|
|
parentId: node.parentId != null ? node.parentId : (parentId || null),
|
|
|
parentChain: node.parentChain || ''
|
|
|
})
|
|
|
}
|
|
|
if (Array.isArray(node.children)) {
|
|
|
flattenLineTree(node.children, node.id)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function getTopLineName(deviceLineId) {
|
|
|
if (deviceLineId == null) return '-'
|
|
|
const node = lineInfoMap.value.get(String(deviceLineId))
|
|
|
if (!node) return '-'
|
|
|
if (node.parentChain) {
|
|
|
const firstId = node.parentChain.split(',')[0]?.trim()
|
|
|
if (firstId) {
|
|
|
const topNode = lineInfoMap.value.get(firstId)
|
|
|
if (topNode) return topNode.name
|
|
|
}
|
|
|
}
|
|
|
let current = node
|
|
|
const visited = new Set()
|
|
|
while (current.parentId != null && current.parentId > 0 && !visited.has(current.id)) {
|
|
|
visited.add(current.id)
|
|
|
const parent = lineInfoMap.value.get(String(current.parentId))
|
|
|
if (!parent) break
|
|
|
current = parent
|
|
|
}
|
|
|
return current.name || '-'
|
|
|
}
|
|
|
|
|
|
async function loadLineTree() {
|
|
|
if (lineInfoMap.value.size > 0) return
|
|
|
try {
|
|
|
const res = await getDeviceLineTree()
|
|
|
const tree = (res && res.data !== undefined) ? res.data : res
|
|
|
const nodes = Array.isArray(tree) ? tree : (tree?.list || tree?.children || [])
|
|
|
flattenLineTree(nodes)
|
|
|
} catch (e) {
|
|
|
console.error('load line tree error', e)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 设置设备产线名称(从产线树转换)
|
|
|
function setDeviceLineName(device) {
|
|
|
if (device && device.deviceLine != null) {
|
|
|
const lineName = getTopLineName(device.deviceLine)
|
|
|
if (lineName && lineName !== '-') {
|
|
|
device.workshopName = lineName
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ==================== 上模模块 ====================
|
|
|
const selectedDevice = ref({})
|
|
|
|
|
|
const selectedMountMolds = ref([])
|
|
|
const tempSelectedDeviceId = ref(null)
|
|
|
const remarkText = ref('')
|
|
|
const selectedOperator = ref(null)
|
|
|
const scanCodeInput = ref('') // 扫码/输入设备码
|
|
|
|
|
|
// 聚焦阻止键盘弹出
|
|
|
const focusNoKeyboardRef = ref(null)
|
|
|
const keywordInputSelector = '#mold-scan-input input, input#mold-scan-input'
|
|
|
|
|
|
function focusKeywordNoKeyboard() {
|
|
|
nextTick(() => {
|
|
|
setTimeout(() => {
|
|
|
focusNoKeyboardRef.value?.focus(keywordInputSelector)
|
|
|
}, 80)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// 设备状态 - 上模
|
|
|
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) || '-'
|
|
|
})
|
|
|
|
|
|
const MOLD_STATUS_MAP = { 0: '在机', 1: '待用', 2: '维修', 3: '报废', 4: '在库' }
|
|
|
function getMoldStatusText(s) { return MOLD_STATUS_MAP[s] || textValue(s) }
|
|
|
function getMoldStatusClass(s) {
|
|
|
if (s === 0) return 'inuse-tag'
|
|
|
if (s === 2) return 'repair-tag'
|
|
|
if (s === 3) return 'scrap-tag'
|
|
|
return 'standby-tag'
|
|
|
}
|
|
|
|
|
|
// 当前在机模具 - 通过模具分页接口查询(machineId = 当前设备ID)
|
|
|
const currentMoldList = ref([])
|
|
|
|
|
|
async function fetchCurrentMolds(deviceName) {
|
|
|
if (!deviceName) {
|
|
|
currentMoldList.value = []
|
|
|
return
|
|
|
}
|
|
|
try {
|
|
|
// 查模具型号表,按当前设备名称筛选
|
|
|
const params = { deviceName, pageSize: 100 }
|
|
|
const res = await getMoldBrandPage(params)
|
|
|
const root = res && res.data !== undefined ? res.data : res
|
|
|
const pageData = root?.pageResult || root
|
|
|
const list = Array.isArray(pageData) ? pageData : (Array.isArray(pageData?.list) ? pageData.list : [])
|
|
|
currentMoldList.value = list
|
|
|
} catch (e) {
|
|
|
console.error('fetchCurrentMolds error', e)
|
|
|
currentMoldList.value = []
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const currentMoldDisplay = computed(() => {
|
|
|
if (!selectedDevice.value?.id) return '-'
|
|
|
if (currentMoldList.value.length > 0) {
|
|
|
return currentMoldList.value.map(m => m.name || '').filter(Boolean).join(',') || '-'
|
|
|
}
|
|
|
return t('moldOperate.noMoldOnDevice')
|
|
|
})
|
|
|
|
|
|
// ---- 操作人自动设置为当前登录用户 ----
|
|
|
function autoSetOperator() {
|
|
|
if (currentUserId.value && currentUserName.value) {
|
|
|
selectedOperator.value = {
|
|
|
value: currentUserId.value,
|
|
|
label: currentUserName.value
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function selectDevice(device) {
|
|
|
selectedDevice.value = device || {}
|
|
|
tempSelectedDeviceId.value = device ? device.id : null
|
|
|
selectedMountMolds.value = []
|
|
|
if (device?.deviceName) fetchCurrentMolds(device.deviceName)
|
|
|
}
|
|
|
|
|
|
function openDevicePicker() {
|
|
|
uni.navigateTo({ url: '/pages_function/pages/moldoperate/deviceSelect?fromType=up' })
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function openMountMoldPicker() {
|
|
|
if (!selectedDevice.value?.id) {
|
|
|
uni.showToast({ title: t('moldOperate.validatorDeviceRequired'), icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
// 把已选模具 ID 传过去做回显
|
|
|
getApp().globalData._moldSelectPreSelected = selectedMountMolds.value.map((m) => String(m.id))
|
|
|
uni.navigateTo({ url: '/pages_function/pages/moldoperate/moldSelect' })
|
|
|
}
|
|
|
|
|
|
function validFormMount() {
|
|
|
if (!selectedDevice.value?.id) {
|
|
|
uni.showToast({ title: t('moldOperate.validatorDeviceRequired'), icon: 'none' })
|
|
|
return false
|
|
|
}
|
|
|
if (!selectedMountMolds.value.length) {
|
|
|
uni.showToast({ title: t('moldOperate.validatorMoldRequired'), icon: 'none' })
|
|
|
return false
|
|
|
}
|
|
|
if (!selectedOperator.value) {
|
|
|
uni.showToast({ title: t('moldOperate.validatorOperatorRequired'), icon: 'none' })
|
|
|
return false
|
|
|
}
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
async function handleConfirmMount() {
|
|
|
if (!validFormMount()) return
|
|
|
try {
|
|
|
const moldIds = selectedMountMolds.value.map((m) => m.id)
|
|
|
const payload = {
|
|
|
operateType: '1',
|
|
|
deviceId: String(selectedDevice.value.id),
|
|
|
moldId: moldIds.length === 1 ? String(moldIds[0]) : moldIds.map(String).join(','),
|
|
|
lineName: selectedDevice.value.workshopName || '',
|
|
|
lineId: selectedDevice.value.deviceLine || ''
|
|
|
}
|
|
|
// 添加操作人和备注
|
|
|
if (selectedOperator.value) {
|
|
|
payload.operatorId = selectedOperator.value.value
|
|
|
}
|
|
|
if (remarkText.value.trim()) {
|
|
|
payload.remark = remarkText.value.trim()
|
|
|
}
|
|
|
console.log('=== 上模提交参数 ===', JSON.stringify(payload))
|
|
|
console.log('=== deviceId type:', typeof payload.deviceId, 'value:', payload.deviceId)
|
|
|
console.log('=== moldId type:', typeof payload.moldId, 'value:', payload.moldId)
|
|
|
const res = await createMoldOperate(payload)
|
|
|
console.log('=== 上模返回 ===', JSON.stringify(res))
|
|
|
uni.showToast({ title: t('functionCommon.createSuccess'), icon: 'success' })
|
|
|
|
|
|
selectedDevice.value = {}
|
|
|
selectedMountMolds.value = []
|
|
|
} catch (e) {
|
|
|
console.error('=== 上模失败 ===', e)
|
|
|
const errMsg = e?.msg || (typeof e === 'string' ? e : e?.message) || '系统异常'
|
|
|
console.error('=== 错误详情 ===', errMsg)
|
|
|
uni.showToast({ title: t('functionCommon.saveFailed') + ': ' + errMsg, icon: 'none', duration: 3000 })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ==================== 共享: 扫码 / 取消 / 返回 ====================
|
|
|
function matchDeviceByCode(code) {
|
|
|
if (!code) return null
|
|
|
let matched = null
|
|
|
if (code.toUpperCase().startsWith('EQUIPMENT-')) {
|
|
|
const idFromQr = code.replace(/EQUIPMENT-/i, '')
|
|
|
matched = deviceOptions.value.find((d) => String(d.raw.id) === idFromQr)
|
|
|
}
|
|
|
if (!matched) {
|
|
|
matched = deviceOptions.value.find((d) =>
|
|
|
Object.values(d.raw).some((v) =>
|
|
|
typeof v === 'string' && v.trim().toUpperCase() === code.toUpperCase()
|
|
|
) || d.label.toUpperCase().includes(code.toUpperCase())
|
|
|
)
|
|
|
}
|
|
|
if (!matched) {
|
|
|
const idMatch = code.match(/(\d+)$/)
|
|
|
if (idMatch) {
|
|
|
matched = deviceOptions.value.find((d) => String(d.raw.id) === idMatch[1])
|
|
|
}
|
|
|
}
|
|
|
return matched
|
|
|
}
|
|
|
|
|
|
function handleScan() {
|
|
|
uni.scanCode({
|
|
|
onlyFromCamera: false,
|
|
|
scanType: ['qrCode', 'barCode'],
|
|
|
success(res) {
|
|
|
const code = (res.result || '').trim()
|
|
|
if (!code) return
|
|
|
scanCodeInput.value = code // 输入框显示扫码结果
|
|
|
const matched = matchDeviceByCode(code)
|
|
|
if (matched) {
|
|
|
setDeviceLineName(matched.raw)
|
|
|
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 onScanInputConfirm() {
|
|
|
const code = scanCodeInput.value.trim()
|
|
|
if (!code) return
|
|
|
const matched = matchDeviceByCode(code)
|
|
|
if (matched) {
|
|
|
setDeviceLineName(matched.raw)
|
|
|
selectDevice(matched.raw)
|
|
|
} else {
|
|
|
uni.showToast({ title: t('moldOperate.deviceNotFound'), icon: 'none' })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function handleCancel() {
|
|
|
selectedDevice.value = {}
|
|
|
selectedMountMolds.value = []
|
|
|
remarkText.value = ''
|
|
|
// 返回上一页(管理页面)
|
|
|
uni.navigateBack({
|
|
|
fail: () => uni.switchTab({ url: '/pages/work' })
|
|
|
})
|
|
|
}
|
|
|
|
|
|
function goToHistory() {
|
|
|
uni.navigateTo({ url: '/pages_function/pages/moldoperate/history?type=up' })
|
|
|
}
|
|
|
|
|
|
onReady(() => {
|
|
|
focusKeywordNoKeyboard()
|
|
|
})
|
|
|
|
|
|
onShow(async () => {
|
|
|
autoSetOperator()
|
|
|
await Promise.allSettled([loadDevices(), loadLineTree()])
|
|
|
// 从 globalData 读取设备选择页回传的设备
|
|
|
const device = getApp().globalData._deviceSelectResult
|
|
|
if (device) {
|
|
|
getApp().globalData._deviceSelectResult = null
|
|
|
selectDevice(device)
|
|
|
}
|
|
|
// 从 globalData 读取模具选择页回传的模具列表
|
|
|
const molds = getApp().globalData._moldSelectResult
|
|
|
if (molds) {
|
|
|
getApp().globalData._moldSelectResult = null
|
|
|
selectedMountMolds.value = molds
|
|
|
}
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.page-container {
|
|
|
min-height: 100vh;
|
|
|
background: #f5f6f8;
|
|
|
padding-bottom: 140rpx;
|
|
|
}
|
|
|
|
|
|
/* ====== 导航栏右侧按钮 ====== */
|
|
|
.nav-right-btn {
|
|
|
width: 64rpx;
|
|
|
height: 64rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
/* ====== 操作按钮区 ====== */
|
|
|
.action-row {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 16rpx;
|
|
|
padding: 20rpx 24rpx;
|
|
|
}
|
|
|
|
|
|
.action-btn {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
gap: 10rpx;
|
|
|
height: 72rpx;
|
|
|
padding: 0 24rpx;
|
|
|
border-radius: 10rpx;
|
|
|
background: #1f4b79;
|
|
|
color: #fff;
|
|
|
font-size: 26rpx;
|
|
|
font-weight: 600;
|
|
|
white-space: nowrap;
|
|
|
|
|
|
.btn-icon-wrap {
|
|
|
width: 36rpx;
|
|
|
height: 36rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
.btn-icon {
|
|
|
font-size: 30rpx;
|
|
|
color: #fff;
|
|
|
}
|
|
|
|
|
|
.btn-text {
|
|
|
font-size: 26rpx;
|
|
|
line-height: 1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* 扫码输入框行 */
|
|
|
.scan-input-row {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
height: 72rpx;
|
|
|
border-radius: 10rpx;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
.scan-input {
|
|
|
flex: 1;
|
|
|
height: 72rpx;
|
|
|
padding: 0 20rpx;
|
|
|
font-size: 26rpx;
|
|
|
color: #333;
|
|
|
background: #fff;
|
|
|
border: 1rpx solid #d0d5dd;
|
|
|
border-radius: 10rpx;
|
|
|
}
|
|
|
|
|
|
/* ====== 卡片通用样式 ====== */
|
|
|
.section-card {
|
|
|
margin: 16rpx 24rpx;
|
|
|
background: #ffffff;
|
|
|
border-radius: 14rpx;
|
|
|
}
|
|
|
|
|
|
.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;
|
|
|
}
|
|
|
// 模具状态
|
|
|
&.inuse-tag {
|
|
|
color: #059669; background: #ecfdf5; border: 1rpx solid #a7f3d0;
|
|
|
}
|
|
|
&.standby-tag {
|
|
|
color: #2563eb; background: #eff6ff; border: 1rpx solid #bfdbfe;
|
|
|
}
|
|
|
&.repair-tag {
|
|
|
color: #d97706; background: #fffbeb; border: 1rpx solid #fde68a;
|
|
|
}
|
|
|
&.scrap-tag {
|
|
|
color: #dc2626; background: #fef2f2; border: 1rpx solid #fecaca;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* ====== 更换下模对象按钮 ====== */
|
|
|
.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;
|
|
|
|
|
|
&:active { background: #fafafa; }
|
|
|
|
|
|
.change-icon { font-size: 28rpx; color: #666; }
|
|
|
.change-text { font-size: 26rpx; color: #666; }
|
|
|
}
|
|
|
|
|
|
/* ====== 表单行(操作人/备注) ====== */
|
|
|
.form-row {
|
|
|
margin-top: 18rpx;
|
|
|
|
|
|
&:first-child {
|
|
|
margin-top: 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.form-cell {
|
|
|
width: 100%;
|
|
|
|
|
|
.form-label {
|
|
|
display: block;
|
|
|
font-size: 24rpx;
|
|
|
color: #999;
|
|
|
margin-bottom: 8rpx;
|
|
|
|
|
|
.required {
|
|
|
color: #dc2626;
|
|
|
margin-right: 4rpx;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ====== 下拉选择器(操作人) ====== */
|
|
|
.select-dropdown {
|
|
|
position: relative;
|
|
|
width: 100%;
|
|
|
}
|
|
|
|
|
|
.dropdown-input {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
height: 72rpx;
|
|
|
padding: 0 20rpx;
|
|
|
border: 1rpx solid #e0e0e0;
|
|
|
border-radius: 10rpx;
|
|
|
font-size: 27rpx;
|
|
|
color: #333;
|
|
|
background: #f9fafb;
|
|
|
|
|
|
&.readonly-input {
|
|
|
background: #f8fafc;
|
|
|
border-color: #f0f0f0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.dropdown-value {
|
|
|
flex: 1;
|
|
|
|
|
|
&.placeholder {
|
|
|
color: #bbb;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.dropdown-arrow {
|
|
|
position: absolute;
|
|
|
right: 16rpx;
|
|
|
top: 50%;
|
|
|
transform: translateY(-50%);
|
|
|
pointer-events: none;
|
|
|
}
|
|
|
|
|
|
/* ====== 下拉面板(在输入框下方展开) ====== */
|
|
|
.dropdown-panel {
|
|
|
position: absolute;
|
|
|
top: 76rpx;
|
|
|
left: 0;
|
|
|
right: 0;
|
|
|
z-index: 99;
|
|
|
background: #ffffff;
|
|
|
border: 1rpx solid #e0e0e0;
|
|
|
border-radius: 12rpx;
|
|
|
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.1);
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.dropdown-scroll {
|
|
|
max-height: 400rpx;
|
|
|
}
|
|
|
|
|
|
.dropdown-item {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
padding: 22rpx 28rpx;
|
|
|
font-size: 27rpx;
|
|
|
color: #333;
|
|
|
border-bottom: 1rpx solid #f5f5f5;
|
|
|
|
|
|
&:last-child {
|
|
|
border-bottom: none;
|
|
|
}
|
|
|
|
|
|
&:active {
|
|
|
background: #f5f7fa;
|
|
|
}
|
|
|
|
|
|
&.active {
|
|
|
color: #1f7cff;
|
|
|
background: #f0f7ff;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.dropdown-item-text {
|
|
|
flex: 1;
|
|
|
overflow: hidden;
|
|
|
text-overflow: ellipsis;
|
|
|
white-space: nowrap;
|
|
|
}
|
|
|
|
|
|
.dropdown-empty {
|
|
|
padding: 32rpx;
|
|
|
text-align: center;
|
|
|
font-size: 26rpx;
|
|
|
color: #999;
|
|
|
}
|
|
|
|
|
|
.form-input {
|
|
|
height: 72rpx;
|
|
|
padding: 0 20rpx;
|
|
|
border: 1rpx solid #e0e0e0;
|
|
|
border-radius: 10rpx;
|
|
|
font-size: 27rpx;
|
|
|
color: #333;
|
|
|
background: #f9fafb;
|
|
|
}
|
|
|
|
|
|
/* ====== 空提示 ====== */
|
|
|
.empty-mold-hint {
|
|
|
padding: 40rpx 24rpx;
|
|
|
text-align: center;
|
|
|
}
|
|
|
|
|
|
.empty-mold-text {
|
|
|
font-size: 28rpx;
|
|
|
color: #2563eb;
|
|
|
|
|
|
&.loading-text {
|
|
|
color: #999;
|
|
|
}
|
|
|
|
|
|
&.empty-gray {
|
|
|
color: #bbb;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* ====== 底部操作栏 ====== */
|
|
|
.bottom-actions {
|
|
|
position: fixed;
|
|
|
left: 0;
|
|
|
right: 0;
|
|
|
bottom: 0;
|
|
|
display: flex;
|
|
|
gap: 18rpx;
|
|
|
padding: 18rpx 24rpx calc(18rpx + env(safe-area-inset-bottom));
|
|
|
background: #ffffff;
|
|
|
box-shadow: 0 -8rpx 24rpx rgba(15, 23, 42, 0.06);
|
|
|
z-index: 99;
|
|
|
|
|
|
&.single-btn {
|
|
|
gap: 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.bottom-btn {
|
|
|
flex: 1;
|
|
|
height: 84rpx;
|
|
|
line-height: 84rpx;
|
|
|
text-align: center;
|
|
|
border-radius: 16rpx;
|
|
|
font-size: 30rpx;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.confirm-btn {
|
|
|
background: #1f4b79;
|
|
|
color: #ffffff;
|
|
|
}
|
|
|
|
|
|
.cancel-btn {
|
|
|
background: #eef2f7;
|
|
|
color: #475569;
|
|
|
}
|
|
|
|
|
|
|
|
|
</style>
|