|
|
<template> <div class="device-management"> <div class="setting-item"> <span class="label">日期与时间</span> <el-date-picker class="date-input" v-model="time" :key="pickerKey" type="datetime" placeholder="修改日期与时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" @change="(val) => updateSetting('date', val)" @focus="pauseTimer" @blur="resumeTimer" /> </div>
<!-- <div class="setting-item"> <span class="label">时间</span> <span class="value">{{ time }}</span> </div> -->
<div class="setting-item"> <span class="label">语言</span> <div class="options"> <button v-for="lang in languages" :key="lang.value" :class="{ active: settings.language === lang.value }" @click="updateSetting('language', lang.value)" :disabled="lang.value == 'ko_KR'" > {{ lang.label }} </button> </div> </div> <div class="setting-item-box"> <div class="setting-item setting-item-no-border"> <span class="label">整体温度控制</span> <div class="options"> <input style="width: 150px" v-model="settings.allTemperature" type="number" @focus="showKeyboard('number', 1)" readonly /> <button @click="updateSetting('allTemperature', settings.allTemperature)" > 设置 </button> </div> </div> <div class="setting-item setting-item-no-border"> <span class="label">孵育盘温度 <el-tag type="primary" class="option-tag"> {{ deviceStore.sensorState?.incubateBoxTemperature }}℃</el-tag></span>
<div class="options">
<input style="width: 150px" v-model="settings.incubateBoxTemperature" type="number" @focus="showKeyboard('number', 2)" readonly /> <button @click=" updateSetting( 'incubateBoxTemperature', settings.incubateBoxTemperature, ) " > 设置 </button> </div> </div> <div class="setting-item setting-item-no-border"> <span class="label">板夹区温度 <el-tag type="primary" class="option-tag"> {{ deviceStore.sensorState?.pboxTemperature }}℃</el-tag></span> <div class="options">
<input style="width: 150px" v-model="settings.plateBoxTemperature" type="number" @focus="showKeyboard('number', 3)" readonly /> <button @click=" updateSetting('plateBoxTemperature', settings.plateBoxTemperature) " > 设置 </button> </div> </div> </div>
<div class="setting-item" style="border-radius: 0"> <span class="label">DHCP</span> <div class="options"> <button :class="{ active: settings.DHCP, }" @click="updateSetting('DHCP', true)" > 开启 </button> <button :class="{ active: !settings.DHCP, }" @click="updateSetting('DHCP', false)" > 关闭 </button> </div> </div> <div v-if="!settings.DHCP" class="setting-item"> <span class="label">Local IP</span> <div class="options"> <input style="min-width: 250px" v-model="settings.localIp" type="text" @focus="showKeyboard(undefined, 4)" readonly /> <button @click="updateSetting('localIP', settings.localIp)"> 设置 </button> </div> </div> <div class="setting-item"> <span class="label">打印</span> <div class="options"> <button v-for="(mode, index) in printModes" :key="index" :class="{ active: settings.autoPrint === mode.value }" @click="updateSetting('autoPrint', mode.value)" > {{ mode.label }} </button> </div> </div> <div class="setting-item"> <span class="label">自动登出</span> <div class="options"> <button :class="{ active: settings.autoLogout, }" @click="updateSetting('autoLogout', true)" > 开启 </button> <button :class="{ active: !settings.autoLogout, }" @click="updateSetting('autoLogout', false)" > 关闭 </button> </div> </div> <div v-if="settings.autoLogout" class="setting-item"> <span class="label">登出时间</span> <div class="options"> <button v-for="time in logoutTimes" :key="time.value" :class="{ active: settings.autoLogoutTimeout === time.value }" @click="updateSetting('autoLogoutTimeout', time.value)" > {{ time.label }} </button> </div> </div> </div> <!-- 键盘 --> <transition name="slide-up"> <div class="keyboard" v-if="keyboardVisible"> <SimpleKeyboard :input="currentInputValue" :layout @onChange="handleKeyboardInput" @onKeyPress="handleKeyPress" /> </div> </transition> </template>
<script setup lang="ts"> import { ref, computed, onMounted, onUnmounted } from 'vue' import { format } from 'date-fns' import { getSystemSettings, setLanguage, setAutoPrint, setAutoLogout, getTemperatureRange, setDHCP, setLocalIP, setAutoLogoutTime, setAllTemperature, setIncubateBoxTemperature, setPlateBoxTemperature, setDateAndTime, } from '@/services' import { eMessage, isValidIPv4 } from '../utils' import { useDeviceStore } from '@/store/index' const deviceStore = useDeviceStore()
const layout = ref()
const numericLayout = { default: [ '1 2 3', '4 5 6', '7 8 9', '{bksp} 0 {enter}', // 包含删除和确认键
], }
// 系统设置状态
interface Settings { language: string autoPrint: boolean autoLogout: boolean autoLogoutTimeout: number allTemperature: number | undefined incubateBoxTemperature: number plateBoxTemperature: number DHCP: boolean localIp: string }
const settings = ref<Settings>({ language: 'zh-CN', autoPrint: true, autoLogout: false, autoLogoutTimeout: 10, allTemperature: 20, incubateBoxTemperature: 20, plateBoxTemperature: 20, DHCP: true, localIp: '', })
// 日期和时间
const currentDate = ref(new Date()) const time = ref('00:00:00') let pickerKey = ref(0) // const dateFormats = ['MM.dd.yyyy', 'dd.MM.yyyy', 'yyyy.MM.dd']
// const selectedDateFormat = ref(dateFormats[2])
// 选项配置
const languages = [ { label: 'English', value: 'en_US' }, { label: '简体中文', value: 'zh_CN' }, ]
interface PrintMode { label: string value: boolean }
const printModes: PrintMode[] = [ { label: '自动', value: true as const }, { label: '手动', value: false as const }, ]
const logoutTimes = [ { label: '10分钟', value: 10 }, { label: '30分钟', value: 30 }, { label: '1小时', value: 60 }, { label: '2小时', value: 120 }, ]
// 格式化日期显示
const formattedDate = computed(() => format(currentDate.value, 'yyyy.MM.dd HH:mm:ss'), )
// 获取系统设置
const fetchSettings = async () => { try { const res = await getSystemSettings() if (res.success) { settings.value = res.data if (res.data.incubateBoxTemperature === res.data.plateBoxTemperature) { settings.value.allTemperature = res.data.incubateBoxTemperature } } } catch (error) { eMessage.error('获取系统设置失败') } }
function isIntegerInRange(str: string): boolean { // 匹配 20-40 的整数
const regex = /^(2[0-9]|3[0-9]|40)$/ return regex.test(str) }
// 更新设置
const updateSetting = async (key: string, value: any) => { try { let res switch (key) { case 'date': // 暂停定时器
clearInterval(intervalId.value) res = await setDateAndTime(value) //更新后利用KEY重新加载date-picker,不然focus不起作用
pickerKey.value = new Date().getTime() // 恢复定时器
startTimer() break case 'language': res = await setLanguage(value) break case 'autoPrint': res = await setAutoPrint(value) break case 'autoLogout': res = await setAutoLogout(value) break case 'autoLogoutTimeout': res = await setAutoLogoutTime(value) break case 'allTemperature': if (!isIntegerInRange(value)) { eMessage.error('输入有误, 温度范围为20℃-40℃') return } res = await setAllTemperature(value) break case 'incubateBoxTemperature': if (!isIntegerInRange(value)) { eMessage.error('输入有误, 温度范围为20℃-40℃') return } res = await setIncubateBoxTemperature(value) break case 'plateBoxTemperature': if (!isIntegerInRange(value)) { eMessage.error('输入有误, 温度范围为20℃-40℃') return } res = await setPlateBoxTemperature(value) break case 'DHCP': res = await setDHCP(value) break case 'localIP': const addr = value.trim() if (!isValidIPv4(addr)) { eMessage.error('请输入合法的IP地址') return } hideKeyboard() res = await setLocalIP(addr) break }
if (res?.success) { settings.value = { ...settings.value, [key]: value }
if (key === 'allTemperature') { settings.value.incubateBoxTemperature = settings.value.plateBoxTemperature = value } if ( settings.value.incubateBoxTemperature === settings.value.plateBoxTemperature ) { settings.value.allTemperature = settings.value.incubateBoxTemperature } else { settings.value.allTemperature = undefined } } } catch (error) { console.log(error) eMessage.error('设置更新失败') } }
// 定时器相关
const intervalId = ref<number | null>(null)
// 启动定时器
const startTimer = () => { intervalId.value = setInterval(() => { const now = new Date() currentDate.value = now time.value = format(now, 'yyyy.MM.dd HH:mm:ss') }, 1000) }
const temperatures = ref<number[]>([])
// 初始化
onMounted(async () => { await fetchSettings()
// 启动定时器
startTimer()
const res = await getTemperatureRange() if (res && res.success) { temperatures.value = res.data } else { res && res.data && res.data.info && eMessage.error(res.data.info) } })
// 在组件卸载时清理定时器
onUnmounted(() => { if (intervalId.value) { clearInterval(intervalId.value) } hideKeyboard() })
// 键盘相关状态
const keyboardVisible = ref(false) const currentInputValue = ref('')
// 显示键盘
const showKeyboard = (KeyboardType: string | undefined, type: number) => { inputNumberType.value = type if (KeyboardType) { layout.value = numericLayout } else { layout.value = undefined } keyboardVisible.value = false setTimeout(() => { keyboardVisible.value = true }, 200) // 清空当前输入值,避免累加
currentInputValue.value = settings.value[inputType[type]] }
const inputNumberType = ref(0)
const inputType = { 1: 'allTemperature', 2: 'incubateBoxTemperature', 3: 'plateBoxTemperature', 4: 'localIp', }
// 处理键盘输入
const handleKeyboardInput = (value: string) => { // 更新当前输入值
currentInputValue.value = value settings.value[inputType[inputNumberType.value]] = value }
// 处理键盘按键
const handleKeyPress = (button: string) => { if (button === '{enter}') { hideKeyboard() } else if (button === '{bksp}') { // 处理退格键
const value = currentInputValue.value if (value.length > 0) { const newValue = value.slice(0, -1) handleKeyboardInput(newValue) } } }
// 隐藏键盘
const hideKeyboard = () => { keyboardVisible.value = false currentInputValue.value = '' }
// 在组件卸载时清理状态
onUnmounted(() => { hideKeyboard() })
// 暂停定时器
const pauseTimer = () => { if (intervalId.value) { console.log(111) clearInterval(intervalId.value) intervalId.value = null } }
// 恢复定时器
const resumeTimer = () => { if (!intervalId.value) { startTimer() } } </script>
<style scoped lang="less"> .device-management { width: 100%; height: 91.5vh; padding: 20px; box-sizing: border-box; background-color: #f5f7fa; display: flex; flex-direction: column; gap: 20px; .setting-item-box > :nth-child(2) { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }
.setting-item-no-border { border-radius: 0 !important; box-shadow: none !important; } .setting-item { background-color: #fff; border-radius: 12px; padding: 24px 32px; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); transition: all 0.3s ease;
.label { font-size: 28px; font-weight: 500; color: #303133; display: flex; align-items: center; .option-tag { font-size: 25px; padding: 20px 10px; margin: 0 10px; } }
.value { font-size: 28px; color: #606266; } input { padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 30px; transition: box-shadow 0.2s ease; border-radius: 10px; }
.options { display: flex; gap: 12px; flex-wrap: wrap;
button { min-width: 120px; height: 56px; padding: 0 24px; border: 1px solid #dcdfe6; background-color: #fff; border-radius: 8px; font-size: 24px; color: #606266; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center;
&.active { color: #fff; background-color: #409eff; border-color: #409eff; box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3); } } } } } .keyboard { position: fixed; bottom: 0; left: 0; width: 100%; height: 300px; background-color: #f5f7fa; border-top-left-radius: 16px; border-top-right-radius: 16px; box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.1); z-index: 1000; } // 键盘动画
.slide-up-enter-active, .slide-up-leave-active { transition: transform 0.3s ease; } .slide-up-enter-from, .slide-up-leave-to { transform: translateY(100%); } :deep(.date-input) { width: 250px; margin-right: 30px; .el-input__wrapper { height: 40px; } .el-input__inner { font-size: 18px; } }
</style>
|