设备列表数据修改完成

This commit is contained in:
likaikai 2025-06-05 20:47:27 +08:00
parent d8e92c9c31
commit 35782897d0
7 changed files with 350 additions and 184 deletions

View File

@ -46,6 +46,13 @@ export function getDeviceListApi(query) {
params: query
})
}
// 根据参数键名查询参数值
export function getDeviceConfigInfoApi() {
return request({
url: `/system/config/configKey/tg.consumables.lifespan`,
method: 'get',
})
}
//新增设备信息
export function addDeviceApi(data) {
return request({

View File

@ -157,7 +157,7 @@ export default {
editDevice: '编辑设备',
deviceStatus:'设备状态',
serviceStatus:'服务状态',
lifespan:'耗材寿命h',
lifespan:'耗材情况',
usedTime:'已使用时长',
viewAuth:'查看鉴权',
nickName:'用户昵称',

View File

@ -49,18 +49,18 @@
/>
</el-select>
</el-form-item>
<el-form-item :label="proxy.$t('equipment.localEMQ')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.localUserName" >
<el-input v-model="form.emqxAuth.localUserName" :placeholder="proxy.$t('common.select')" />
</el-form-item>
<el-form-item :label="proxy.$t('equipment.localPassword')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.localPass">
<el-input v-model="form.emqxAuth.localPass" :placeholder="proxy.$t('common.select')" />
</el-form-item>
<el-form-item :label="proxy.$t('equipment.localEMQPassword')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.cloudUserName">
<el-input v-model="form.emqxAuth.cloudUserName" :placeholder="proxy.$t('common.select')" />
</el-form-item>
<el-form-item :label="proxy.$t('equipment.localEMQUser')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.cloudPass">
<el-input v-model="form.emqxAuth.cloudPass" :placeholder="proxy.$t('common.select')" />
</el-form-item>
<!-- <el-form-item :label="proxy.$t('equipment.localEMQ')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.localUserName" >-->
<!-- <el-input v-model="form.emqxAuth.localUserName" :placeholder="proxy.$t('common.select')" />-->
<!-- </el-form-item>-->
<!-- <el-form-item :label="proxy.$t('equipment.localPassword')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.localPass">-->
<!-- <el-input v-model="form.emqxAuth.localPass" :placeholder="proxy.$t('common.select')" />-->
<!-- </el-form-item>-->
<!-- <el-form-item :label="proxy.$t('equipment.localEMQPassword')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.cloudUserName">-->
<!-- <el-input v-model="form.emqxAuth.cloudUserName" :placeholder="proxy.$t('common.select')" />-->
<!-- </el-form-item>-->
<!-- <el-form-item :label="proxy.$t('equipment.localEMQUser')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.cloudPass">-->
<!-- <el-input v-model="form.emqxAuth.cloudPass" :placeholder="proxy.$t('common.select')" />-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<div class="dialog-footer">
@ -109,12 +109,12 @@ const form = reactive({
deviceModel: '',
customerId: '',
userIdList: [],
emqxAuth: {
localEmqUser: '',
localPassword: '',
cloudEmqUser: '',
cloudPassword: ''
},
// emqxAuth: {
// localEmqUser: '',
// localPassword: '',
// cloudEmqUser: '',
// cloudPassword: ''
// },
id:''
})
@ -123,12 +123,12 @@ const rules = {
deviceCode: [{ required: true, message: '请输入设备编码', trigger: 'blur' }],
deviceModel: [{ required: true, message: '请选择设备型号', trigger: 'change' }],
customerId: [{ required: true, message: '请选择所属客户', trigger: 'change' }],
emqxAuth: {
localUserName: [{ required: true, message: '请输入本地EMQ用户名', trigger: 'blur' }],
localPass: [{ required: true, message: '请输入本地密码', trigger: 'blur' }],
cloudUserName: [{ required: true, message: '请输入云EMQ用户名', trigger: 'blur' }],
cloudPass: [{ required: true, message: '请输入云密码', trigger: 'blur' }]
}
// emqxAuth: {
// localUserName: [{ required: true, message: 'EMQ', trigger: 'blur' }],
// localPass: [{ required: true, message: '', trigger: 'blur' }],
// cloudUserName: [{ required: true, message: 'EMQ', trigger: 'blur' }],
// cloudPass: [{ required: true, message: '', trigger: 'blur' }]
// }
}
const query = {
pageNum: 1,
@ -182,8 +182,9 @@ const cancel = () => {
watch(
() => props.formData,
(newVal) => {
if (props.title === '编辑' && Object.keys(newVal).length > 0) {
(newVal, oldVal, onCleanup) => {
// 使 flush: 'sync'
if (props.title === proxy.$t('button.edit')) {
console.log(newVal,'newVal')
Object.keys(form).forEach(key => {
if (key === 'userIdList') {
@ -196,13 +197,16 @@ watch(
})
}
},
{ immediate: true, deep: true }
{
immediate: true, //
deep: true, //
flush: 'sync' //
}
)
defineExpose({
visible
})
onMounted(() => {
console.log('999')
getDeviceTypeList()
})
</script>

View File

@ -1,127 +1,138 @@
<template>
<div ref="chartRef" style="width: 100%; height: 400px;"></div>
<div ref="chartRef" style="width: 400px; height: 400px;"></div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
import * as echarts from 'echarts'
const chartRef = ref(null)
let myChart = null
const initChart = () => {
if (myChart) {
myChart.dispose()
}
nextTick(() => {
myChart = echarts.init(chartRef.value)
const option = {
grid: {
left: '5%',
left: '10%',
right: '5%',
bottom: '10%',
top: '10%',
containLabel: true
top: '10%'
},
xAxis: {
type: 'value',
min: 15,
max: 120,
min: 20,
max: 100,
interval: 20,
axisLine: { show: true },
splitLine: {
show: true,
lineStyle: {
type: 'dashed',
color: '#ddd'
}
lineStyle: { type: 'dashed' }
}
},
yAxis: {
type: 'value',
min: 0,
max: 48000,
interval: 8000
interval: 8000,
axisLine: { show: true },
splitLine: {
show: true,
lineStyle: { type: 'dashed' }
}
},
series: [
{
// 线
name: '红线',
type: 'line',
data: generateBowlData(true),
smooth: true,
symbol: 'none',
lineStyle: {
color: '#ff4444',
width: 2
}
lineStyle: { color: '#ff4444', width: 2 }
},
{
// 绿线
name: '绿线',
type: 'line',
data: generateBowlData(false),
smooth: true,
symbol: 'none',
lineStyle: {
color: '#44ff44',
width: 2
}
},
{
// 线 1
type: 'line',
markLine: {
symbol: ['none', 'none'],
data: [{ xAxis: 30 }],
lineStyle: {
type: 'dashed',
color: '#44ff44'
}
}
},
{
// 线 2
type: 'line',
markLine: {
symbol: ['none', 'none'],
data: [{ xAxis: 45 }],
lineStyle: {
type: 'dashed',
color: '#44ff44'
}
}
},
{
// 线 3
type: 'line',
markLine: {
symbol: ['none', 'none'],
data: [{ xAxis: 60 }],
lineStyle: {
type: 'dashed',
color: '#44ff44'
}
}
lineStyle: { color: '#44ff44', width: 2 }
}
]
}
myChart = echarts.init(chartRef.value)
// 线
const verticalLines = [40, 60, 80]
verticalLines.forEach(x => {
option.series.push({
type: 'line',
markLine: {
silent: true,
symbol: ['none', 'none'],
label: { show: false },
data: [
{
xAxis: x,
yAxis: 0
},
{
xAxis: x,
yAxis: 48000
}
],
lineStyle: {
color: '#44ff44',
type: 'dashed',
width: 1
}
}
})
})
myChart.setOption(option)
})
}
// 线
const generateBowlData = (isRed) => {
const data = []
const offset = isRed ? -2000 : 0
const points = []
for (let x = 15; x <= 120; x++) {
//
points.push([20, 45000 + offset]) //
points.push([40, 45000 + offset]) //
points.push([60, 6000 + offset]) //
points.push([80, 45000 + offset]) //
points.push([100, 45000 + offset]) //
// 线
for (let i = 0; i < points.length - 1; i++) {
const start = points[i]
const end = points[i + 1]
const steps = 20
for (let j = 0; j <= steps; j++) {
const t = j / steps
const x = start[0] + (end[0] - start[0]) * t
let y
if (x < 30) {
y = 45000 + offset
} else if (x < 45) {
y = 45000 + offset - Math.pow((x - 30) * 2500, 0.9)
} else if (x < 60) {
y = 6000 + offset
} else if (x < 75) {
y = 6000 + offset + Math.pow((x - 60) * 2500, 0.9)
if (i === 1) {
// 使线
y = start[1] + (end[1] - start[1]) * Math.pow(t, 2)
} else if (i === 2) {
// 使线
y = start[1] + (end[1] - start[1]) * Math.pow(t, 2)
} else {
y = 45000 + offset
// 使线
y = start[1] + (end[1] - start[1]) * t
}
data.push([x, y])
}
}
return data
}

View File

@ -288,7 +288,8 @@ const {
disconnect,
publish
} = useMqtt()
const clientId = ref('vue-client-' + Math.random().toString(16).substring(2, 8))
// const clientId = ref('vue-client-' + Math.random().toString(16).substring(2, 8))
const clientId = ref('lkk40a9bc')
const deviceInfo = ref({})
const deviceData = ref(null)
const deviceMode = ref(null)
@ -719,12 +720,13 @@ const sendDeviceTrend = async () => {
// console.log(':', message)
// })
}
//
const getDeviceDetail = async () => {
try {
const res = await getDeviceDetailApi(deviceId.value)
deviceInfo.value = res.data
initSubscribe()
await initSubscribe()
} catch (error) {
ElMessage.error(error.message)
}

View File

@ -80,8 +80,11 @@
<el-button type="primary" link @click="viewCustomer(scope.row)">{{proxy.$t('button.check')}}</el-button>
</template>
</el-table-column>
<el-table-column :label="proxy.$t('equipment.lifespan')" width="140" align="center" prop="lifespan" />
<el-table-column :label="proxy.$t('equipment.usedTime')" width="140" align="center" prop="usedTime" />
<el-table-column :label="proxy.$t('equipment.lifespan')" width="140" align="center" prop="lifespan">
<template #default="scope">
<el-button type="primary" link @click="viewSupplies(scope.row)">{{proxy.$t('button.check')}}</el-button>
</template>
</el-table-column>
<el-table-column :label="proxy.$t('equipment.deviceStatus')" align="center" prop="deviceStatus" width="100">
</el-table-column>
<el-table-column :label="proxy.$t('equipment.serviceStatus')" align="center" prop="serviceStatus" width="100">
@ -179,6 +182,42 @@
</div>
</template>
</el-dialog>
<!--查看耗材情况-->
<el-dialog
:title="proxy.$t('equipment.lifespan')"
v-model="suppliesVisible"
width="900px"
append-to-body
>
<el-table :data="suppliesList" border>
<!-- <el-table-column :label="proxy.$t('equipment.nickName')" prop="nickName" align="center" />-->
<!-- <el-table-column :label="proxy.$t('equipment.userName')" prop="userName" align="center" />-->
<!-- <el-table-column :label="proxy.$t('position.department')" prop="deptName" align="center" />-->
<!-- <el-table-column :label="proxy.$t('equipment.userStatus')" align="center" prop="status">-->
<!-- <template #default="scope">-->
<!-- {{Number(scope.row.status) === 0 ? proxy.$t('common.normal') :proxy.$t('common.deactivate')}}-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column label="模块名称" prop="name" align="center" />
<el-table-column label="扫描臂1耗材寿命(天)" prop="scanner1Time" align="center">
<template #default="scope">
<span :class="scope.row.scanner1Time > 80 ? 'red' : ''">{{scope.row.scanner1Time}}</span>
</template>
</el-table-column>
<el-table-column label="扫描臂1已使用时长(天)" prop="scanner1UsedTime" align="center" />
<el-table-column label="扫描臂2耗材寿命(天)" prop="scanner2Time" align="center" >
<template #default="scope">
<span :class="scope.row.scanner2Time > 80 ? 'red' : ''">{{scope.row.scanner2Time}}</span>
</template>
</el-table-column>
<el-table-column label="扫描臂2已使用时长(天)" prop="scanner2UsedTime" align="center" />
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button @click="suppliesVisible = false">{{ proxy.$t('button.cancel') }}</el-button>
</div>
</template>
</el-dialog>
<!--查看鉴权-->
<!-- <EmqDialog ref="emqDialogRef" :form-data="emqFormData" ></EmqDialog>-->
<!--分享-->
@ -195,7 +234,8 @@ import {
getDeviceSelectApi,
getUserSelectApi,
getDeviceDetailApi,
getAuthInfoApi
getAuthInfoApi,
getDeviceConfigInfoApi
} from '@/api/equipment'
import DeviceDialog from './common/DeviceDialog.vue'
import shareDialog from './common/shareDialog.vue'
@ -214,6 +254,7 @@ const {
disconnect,
publish
} = useMqtt()
const codeValue = ref(null)
const emqDialogRef = ref()
const emqFormData = ref({})
const router = useRouter()
@ -253,13 +294,16 @@ const dialog = reactive({
id: '',
}
})
const emqVisible = ref(false)
let deviceInfoTimer = ref(null)
const suppliesVisible = ref(false)
const suppliesList = ref([])
const queryParamsCustomer = reactive({
pageNum: 1,
pageSize: 100000000,
deptName: undefined,
parentId: 100,
})
const deviceInfoAll = ref([])
const customerList = ref([])
const statusFormat = (status) => {
if(status === undefined || status === null) {
@ -330,24 +374,6 @@ const exportDevice = async() => {
`equipment_${new Date().getTime()}.xlsx`
);
}
const subscribeDeviceStatus = () => {
if (deviceList.value.length > 0) {
deviceList.value.forEach(item => {
subscribe(`v1/cpycal/${item.deviceCode}/status`, (message) => {
console.log('设备状态消息:', message)
const index = deviceList.value.findIndex(device => device.deviceCode === item.deviceCode)
if (index !== -1) {
const updatedDevice = {
...deviceList.value[index],
serviceStatus: message.running ? proxy.$t('common.online') : proxy.$t('common.offline'),
deviceStatus: message.running ? proxy.$t('common.online') : proxy.$t('common.offline')
}
deviceList.value.splice(index, 1, updatedDevice)
}
})
})
}
}
//
const getCustomerList = async() => {
const res = await getCustomerListApi(queryParamsCustomer);
@ -360,11 +386,122 @@ const getList = async() => {
if(res.code === 200) {
deviceList.value = res.rows || []
total.value = res.total
subscribeDeviceStatus()
}
loading.value = false
await getDeviceInfo()
}
//
const getDeviceInfo = async() => {
if (deviceList.value.length > 0) {
deviceList.value.forEach(item => {
deviceInfoAll.value.push({key:item.deviceCode,serviceStatus:item.serviceStatus,date:new Date().getTime() })
console.log(deviceInfoAll.value,'deviceInfoAll.value')
//
subscribe(`v1/cpy/${item.deviceCode}/infos`, (message) => {
console.log('设备信息消息:', message)
if (message) {
const supplies = [
{
name: '采集板',
key: 'acquisition_board',
scanner1Time: message.scanner1.acquisition_board.useful_life,
scanner1UsedTime: message.scanner1.acquisition_board.usage_duration,
scanner2Time: message.scanner2.acquisition_board.useful_life,
scanner2UsedTime: message.scanner2.acquisition_board.usage_duration,
},
{
name: '电源',
key: 'power',
scanner1Time: message.scanner1.power.useful_life,
scanner1UsedTime: message.scanner1.power.usage_duration,
scanner2Time: message.scanner2.power.useful_life,
scanner2UsedTime: message.scanner2.power.usage_duration,
},
{
name: '射线',
key: 'xray',
scanner1Time: message.scanner1.xray.useful_life,
scanner1UsedTime: message.scanner1.xray.usage_duration,
scanner2Time: message.scanner2.xray.useful_life,
scanner2UsedTime: message.scanner2.xray.usage_duration,
},
{
name: '电机',
key: 'motor',
scanner1Time: message.scanner1.motor.useful_life,
scanner1UsedTime: message.scanner1.motor.usage_duration,
scanner2Time: message.scanner2.motor.useful_life,
scanner2UsedTime: message.scanner2.motor.usage_duration,
}
]
//
const deviceIndex = deviceList.value.findIndex(device => device.deviceCode === item.deviceCode)
if (deviceIndex !== -1) {
//
if (deviceList.value[deviceIndex].supplies) {
deviceList.value[deviceIndex].supplies = deviceList.value[deviceIndex].supplies.map(supply => {
const newSupply = supplies.find(s => s.key === supply.key)
return newSupply || supply
})
} else {
//
deviceList.value[deviceIndex].supplies = supplies
}
}
}
})
//
subscribe(`v1/cpycal/${item.deviceCode}/status`, (message) => {
console.log('设备状态消息:', message)
const index = deviceList.value.findIndex(device => device.deviceCode === item.deviceCode)
if (index !== -1) {
const updatedDevice = {
...deviceList.value[index],
serviceStatus: message.running ? proxy.$t('common.online') : proxy.$t('common.offline'),
deviceStatus: message.running ? proxy.$t('common.online') : proxy.$t('common.offline'),
date: new Date().getTime(),
}
deviceList.value.splice(index, 1, updatedDevice)
console.log(deviceList.value,'deviceList.value')
}
})
})
console.log(deviceList.value,'deviceList')
}
}
const compareArray = () => {
deviceList.value.forEach(device => {
deviceInfoAll.value.forEach(deviceItem => {
if(device.deviceCode === deviceItem.key) {
if(!device?.date || device?.date - deviceItem.date > 10) {
device.serviceStatus = proxy.$t('common.offline')
device.deviceStatus = proxy.$t('common.offline')
}
}
})
})
}
const startDeviceInfoPolling = () => {
//
if (deviceInfoTimer) {
clearInterval(deviceInfoTimer)
}
//
compareArray()
// 5
deviceInfoTimer = setInterval(() => {
compareArray()
}, 5000)
}
//
const stopDeviceInfoPolling = () => {
if (deviceInfoTimer) {
clearInterval(deviceInfoTimer)
deviceInfoTimer = null
}
}
const handleQuery = () => {
queryParams.pageNum = 1
getList()
@ -388,6 +525,16 @@ const viewCustomer = async(row) => {
userList.value = []
}
}
//
const viewSupplies = async(row) => {
suppliesVisible.value = true
if(row.supplies?.length > 0) {
suppliesList.value = row.supplies
} else {
suppliesList.value = []
}
}
//
const handleAdd = () => {
dialog.title = proxy.$t('button.add')
@ -413,7 +560,7 @@ const handleDelete = (row) => {
if (res.code === 200) {
ElMessage.success('删除成功')
}
getList()
await getList()
}).catch(() => {})
}
//
@ -435,24 +582,12 @@ const handleGo = (row) => {
//
router.push({ path: '/equipment/equipmentDetails', query: { id: row.id } });
}
//
const handleEmqx = async (row) => {
const res = await getAuthInfoApi(row.deviceCode)
emqFormData.value = res.data
emqDialogRef.value.visible = true
}
//
const handleUpdate = async(row) => {
const res = await getDeviceDetailApi(row.id)
if(res.code === 200) {
dialog.title = proxy.$t('button.edit')
dialog.visible = true
// dialog.form.deviceName = res.data.deviceName
// dialog.form.deviceCode = res.data.deviceCode
// dialog.form.deviceModel = res.data.deviceModel
// dialog.form.customerId = res.data.customerId
// dialog.form.userIdList = res.data.userIdList
// dialog.form.id = res.data.id
dialog.form = {...res.data}
console.log(dialog.form,'dialog.form')
} else {
@ -468,15 +603,7 @@ const getUserSelect = async() => {
ElMessage.error(res.msg)
}
}
//
// const getDeviceSelect = async() => {
// const res = await getDeviceSelectApi()
// if(res.code === 200) {
// deviceDropList.value = res.data || []
// } else {
// ElMessage.error(res.msg)
// }
// }
watch(
() => route.query,
(newQuery) => {
@ -505,6 +632,12 @@ const mqttConnect = () => {
path: '/mqtt'// WebSocket
})
}
//
const getConfigInfo = async() => {
const res = await getDeviceConfigInfoApi()
codeValue.value = res.msg
console.log(codeValue.value,'codeValue.value')
}
onMounted(async() => {
// 使
if (route.query.id) {
@ -513,12 +646,18 @@ onMounted(async() => {
if (route.query.id) {
queryParams.customerId = Number(route.query.id)
}
await getConfigInfo()
await mqttConnect()
await getList()
await getCustomerList()
await getUserSelect()
await getStatus()
startDeviceInfoPolling()
})
onUnmounted(() => {
stopDeviceInfoPolling() //
})
</script>
<style scoped lang="scss">
@ -557,4 +696,7 @@ onMounted(async() => {
padding: 5px 20px;
font-size: 14px;
}
.red {
color:red
}
</style>

View File

@ -30,11 +30,11 @@ export default defineConfig(({ mode, command }) => {
open: true,
proxy: {
[env.VITE_APP_BASE_API]: {
// target: 'http://124.236.46.74:8104',
target: 'http://192.168.0.109:8081/',
target: 'http://124.236.46.74:8104',
// target: 'http://192.168.0.109:8081/',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/dev-api/, '')
// rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '/api')
// rewrite: (p) => p.replace(/^\/dev-api/, '')
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '/api')
}
}
},