This commit is contained in:
wty 2023-12-01 10:44:31 +08:00
commit 42c415f37d
34 changed files with 2626 additions and 63 deletions

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@
"html-loader": "^4.2.0",
"js-cookie": "2.2.0",
"markdown-loader": "^8.0.0",
"mqtt": "^2.18.9",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",

View File

@ -5,7 +5,226 @@
</template>
<script>
import { mapGetters } from 'vuex'
import {
getGatewayList,
updateGateway,
updateDevice,
deviceOffline,
gatewayOffline
} from '@/api/gateway/manage'
import mqtt from 'mqtt'
export default {
name: 'App'
name: 'App',
data() {
return {
connection: {
protocol: 'ws',
host: '8.140.53.225',
// ws: 8083; wss: 8084
port: 8083,
endpoint: '/mqtt',
// for more options, please refer to https://github.com/mqttjs/MQTT.js#mqttclientstreambuilder-options
clean: true,
connectTimeout: 30 * 1000, // ms
reconnectPeriod: 4000, // ms
clientId: 'emqx_vue',
// auth
username: 'xiuos',
password: 'xiuos'
},
subscription: [
{
topic: '$SYS/brokers/+/clients/+/#',
qos: 0
},
{
topic: 'gateway',
qos: 0
},
{
topic: 'deviceStatus',
qos: 0
}
],
publish: {
topic: 'topic/browser',
qos: 0,
payload: '{ "msg": "Hello, I am browser." }'
},
qosList: [0, 1, 2],
subscribeSuccess: false,
connecting: false,
retryTimes: 0
}
},
computed: {
...mapGetters(['client', 'gatewayList', 'token'])
},
mounted() {
this.createConnection()
this.doSubscribe()
this.doPublish()
},
methods: {
formatDate(value) {
var date = new Date(value)
var y = date.getFullYear()
var m = date.getMonth() + 1
var d = date.getDate()
var h = date.getHours()
var i = date.getMinutes()
var s = date.getSeconds()
if (m < 10) { m = '0' + m }
if (d < 10) { d = '0' + d }
if (h < 10) { h = '0' + h }
if (i < 10) { i = '0' + i }
if (s < 10) { s = '0' + s }
var t = y + '-' + m + '-' + d + ' ' + h + ':' + i + ':' + s
return t
},
initData() {
this.client = {
connected: false
}
this.retryTimes = 0
this.connecting = false
this.subscribeSuccess = false
},
handleOnReConnect() {
this.retryTimes += 1
if (this.retryTimes > 5) {
try {
this.client.end()
this.initData()
this.$message.error('Connection maxReconnectTimes limit, stop retry')
} catch (error) {
this.$message.error(error.toString())
}
}
},
createConnection() {
try {
this.connecting = true
const { protocol, host, port, endpoint, ...options } = this.connection
const connectUrl = `${protocol}://${host}:${port}${endpoint}`
// this.client = mqtt.connect(connectUrl, options)
const temp = mqtt.connect(connectUrl, options)
this.$store.dispatch('app/setClient', temp)
// this.$myContent.setClient(temp)
if (this.client.on) {
this.client.on('connect', () => {
this.connecting = false
console.log('Connection succeeded!')
})
this.client.on('reconnect', this.handleOnReConnect)
this.client.on('message', (topic, message) => {
console.log(`Received message ${message} from topic ${topic}`)
const res = JSON.parse(String().concat(message))
const regxConnected = /\$SYS\/brokers\/.+\/connected/
const regxDisconnected = /\$SYS\/brokers\/.+\/disconnected/
if (this.token === 'success') {
console.log('success')
if (regxDisconnected.test(topic)) {
console.log('topic disconnected')
if (
this.gatewayList.some((gateway) => gateway.gatewayid === res.clientid)
) {
updateGateway({ status: 2, gatewayid: res.clientid }).then(
(res) => {
this.getGatewayList()
}
)
}
}
if (regxConnected.test(topic)) {
console.log('topic connected')
if (
this.gatewayList.some((gateway) => gateway.gatewayid === res.clientid)
) {
const lastConnect = this.formatDate(res.connected_at)
updateGateway({
status: 1,
gatewayid: res.clientid,
lastConnect
}).then((res) => {
this.getGatewayList()
})
}
}
if (topic === 'gateway') {
console.log('gateway')
if (
this.gatewayList.some((gateway) => gateway.gatewayid === res.gatewayId)
) {
res.deviceList.forEach((device) => {
const { devEui, dataType, value } = device
const content = JSON.stringify({ dataType, value })
localStorage.setItem(devEui, content)
})
}
}
if (topic === 'deviceStatus') {
console.log('deviceStatus')
res.deviceList.forEach((device) => {
updateDevice(device).then()
})
}
}
})
this.client.on('error', (error) => {
console.log('Connection failed', error)
})
}
} catch (error) {
this.connecting = false
console.log('mqtt.connect error', error)
}
},
doSubscribe() {
this.subscription.forEach((subscription) => {
const { topic, qos } = subscription
this.client.subscribe(topic, { qos }, (error, res) => {
if (error) {
console.log(`Subscribe to topic ${topic} error`, error)
return
}
this.subscribeSuccess = true
console.log(`Subscribe to topic ${topic} res`, res)
})
})
},
doUnSubscribe() {
this.subscription.forEach((subscription) => {
const { topic } = subscription
this.client.unsubscribe(topic, (error) => {
if (error) {
console.log('Unsubscribe error', error)
}
})
})
},
doPublish() {
const { topic, qos, payload } = this.publish
this.client.publish(topic, payload, { qos }, (error) => {
if (error) {
console.log('Publish error', error)
}
})
},
destroyConnection() {
if (this.client.connected) {
try {
this.client.end(false, () => {
this.initData()
console.log('Successfully disconnected!')
})
} catch (error) {
console.log('Disconnect failed', error.toString())
}
}
}
}
}
</script>

View File

@ -0,0 +1,17 @@
import request from '@/utils/request'
export function getDeviceStatus(data) {
return request({
url: '/loraDevice/getStatus',
method: 'get',
params: data || {}
})
}
export function getGatewayStatus(data) {
return request({
url: '/loraGateway/getStatus',
method: 'get',
params: data || {}
})
}

View File

@ -0,0 +1,25 @@
import request from '@/utils/request'
export function getList(data) {
return request({
url: '/loraDevice/select',
method: 'get',
params: data || {}
})
}
export function add(data) {
return request({
url: '/loraDevice/add',
method: 'post',
data
})
}
export function remove(data) {
return request({
url: '/loraDevice/delete',
method: 'post',
data
})
}

View File

@ -0,0 +1,65 @@
import request from '@/utils/request'
export function getGatewayList(data) {
return request({
url: '/loraGateway/select',
method: 'get',
params: data || {}
})
}
export function getDeviceList(data) {
return request({
url: '/loraDevice/select',
method: 'get',
params: data || {}
})
}
export function add(data) {
return request({
url: '/loraGateway/add',
method: 'post',
data
})
}
export function updateGateway(data) {
return request({
url: '/loraGateway/update',
method: 'post',
data
})
}
export function updateDevice(data) {
return request({
url: '/loraDevice/update',
method: 'post',
data
})
}
export function remove(data) {
return request({
url: '/loraGateway/delete',
method: 'post',
data
})
}
export function deviceOffline(data) {
return request({
url: '/loraDevice/offline',
method: 'post',
data
})
}
export function gatewayOffline(data) {
return request({
url: '/loraGateway/offline',
method: 'post',
data
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

View File

@ -48,7 +48,22 @@ export default {
}
},
showBreadcrumb() {
const whiteList = ['overview', 'terminal/add', 'terminal/plc', 'terminal/stock', 'terminal/ota', 'data/value', 'configuration/development', 'dashboard', 'log/accessLog', 'log/systemLog', 'rule/manage', 'rule/dataTransfer']
const whiteList = [
'overview',
'terminal/add',
'terminal/plc',
'terminal/stock',
'terminal/ota',
'data/value',
'configuration/development',
'dashboard',
'log/accessLog',
'log/systemLog',
'rule/manage',
'rule/dataTransfer',
'gateway/device',
'gateway/manage'
]
return whiteList.every((item) => this.$route.path.indexOf(item) === -1)
}
},

View File

@ -157,7 +157,7 @@ export const asyncRouter = [
path: '',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: '设备看板', icon: 'dashboard', permission: ['dashengda', 'hangxiao', 'qianjiangdianqi'] }
meta: { title: '看板', icon: 'dashboard', permission: ['dashengda', 'hangxiao', 'qianjiangdianqi'] }
}]
},
{
@ -229,6 +229,32 @@ export const asyncRouter = [
}
]
},
{
path: '/gateway',
component: Layout,
name: 'Gateway',
meta: { title: '网关管理', icon: 'rule', permission: ['dashengda', 'hangxiao', 'qianjiangdianqi'] },
children: [
{
path: 'dashboard',
name: 'GatewayDashboard',
component: () => import('@/views/gateway/dashboard/index'),
meta: { title: '看板' }
},
{
path: 'device',
name: 'GatewayDevice',
component: () => import('@/views/gateway/device/index'),
meta: { title: '设备节点' }
},
{
path: 'manage',
name: 'GatewayManage',
component: () => import('@/views/gateway/manage/index'),
meta: { title: '网关' }
}
]
},
{
path: '/rule',
component: Layout,

View File

@ -1,6 +1,8 @@
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
client: state => state.app.client,
gatewayList: state => state.app.gatewayList,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,

View File

@ -5,7 +5,11 @@ const state = {
opened: true,
withoutAnimation: false
},
device: 'desktop'
device: 'desktop',
client: {
connected: false
},
gatewayList: []
}
const mutations = {
@ -25,6 +29,12 @@ const mutations = {
},
TOGGLE_DEVICE: (state, device) => {
state.device = device
},
SET_CLIENT: (state, client) => {
state.client = client
},
SET_GATEWAY_LIST: (state, gatewayList) => {
state.gatewayList = gatewayList
}
}
@ -37,6 +47,12 @@ const actions = {
},
toggleDevice({ commit }, device) {
commit('TOGGLE_DEVICE', device)
},
setClient({ commit }, client) {
commit('SET_CLIENT', client)
},
setGatewayList({ commit }, gatewayList) {
commit('SET_GATEWAY_LIST', gatewayList)
}
}

View File

@ -2,7 +2,7 @@
* @Author: 龚祖望 573413756@qq.com
* @Date: 2022-05-16 09:16:41
* @LastEditors: 龚祖望 573413756@qq.com
* @LastEditTime: 2022-11-17 11:15:19
* @LastEditTime: 2023-11-23 15:07:17
* @FilePath: \dashengda\src\store\modules\user.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
@ -56,12 +56,12 @@ const mutations = {
RESET_STATE: (state) => {
Object.assign(state, getDefaultState())
},
SET_TOKEN: (state, name) => {
state.name = name
},
SET_NAME: (state, token) => {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},

View File

@ -0,0 +1,255 @@
<template>
<div class="app-container" style="min-height: calc(100vh - 50px)">
<el-row
type="flex"
justify="space-between"
style="align-items: baseline; margin-bottom: 30px"
>
<div class="header">
<h1 class="title">看板</h1>
<span class="date">{{ date }}</span>
</div>
</el-row>
<div class="box_container">
<div class="chart_container">
<div class="title bg_purple">设备节点</div>
<div class="chart" />
</div>
<div class="divider" />
<div class="chart_container">
<div class="title bg_blue">网关</div>
<div class="chart" />
</div>
</div>
</div>
</template>
<script>
import { getDeviceStatus, getGatewayStatus } from '@/api/gateway/dashboard'
export default {
data() {
return {
date: '',
myCharts: [],
options: [
{
grid: {
left: 0,
right: 0
},
legend: {
bottom: '10%',
left: 'center',
itemGap: 50
},
tooltip: {
trigger: 'item'
},
series: [
{
type: 'pie',
center: ['50%', '40%'],
radius: ['30%', '50%'],
clockwise: true,
avoidLabelOverlap: true,
color: ['#20BE0B', '#EF6F59', '#789AF3'],
label: {
show: true,
position: 'outside',
formatter: '{a|{b}{c}}\n',
rich: {
a: {
padding: [0, 0, -20, 0],
fontSize: 14
}
}
},
labelLine: {
normal: {
length: 10,
length2: 10,
lineStyle: {
width: 1
}
}
},
emphasis: {
scaleSize: 10
},
data: [
{
name: '在线',
value: 1
},
{
name: '离线',
value: 2
},
{
name: '从未连接',
value: 3
}
]
}
]
},
{
grid: {
left: 0,
right: 0
},
legend: {
bottom: '10%',
left: 'center',
itemGap: 50
},
tooltip: {
trigger: 'item'
},
series: [
{
type: 'pie',
center: ['50%', '40%'],
radius: ['30%', '50%'],
clockwise: true,
avoidLabelOverlap: true,
color: ['#20BE0B', '#EF6F59', '#5CDBE5'],
label: {
show: true,
position: 'outside',
formatter: '{a|{b}{c}}\n',
rich: {
a: {
padding: [0, 0, -20, 0],
fontSize: 14
}
}
},
labelLine: {
normal: {
length: 10,
length2: 10,
lineStyle: {
width: 1
}
}
},
emphasis: {
scaleSize: 10
},
data: [
{
name: '在线',
value: 1
},
{
name: '离线',
value: 2
},
{
name: '从未连接',
value: 3
}
]
}
]
}
]
}
},
mounted() {
this.date = new Date().toLocaleDateString()
const doms = document.getElementsByClassName('chart')
Array.prototype.forEach.call(doms, (dom) => {
this.myCharts.push(this.$echarts.init(dom))
})
this.getSum()
window.addEventListener('resize', () => {
this.myCharts.forEach(chart => { chart.resize() })
})
},
methods: {
async getSum() {
const device = await getDeviceStatus()
const gateway = await getGatewayStatus()
this.options[0].series[0].data[0].value = device.data.filter(item => item.status === 1)[0]?.count || 0
this.options[0].series[0].data[1].value = device.data.filter(item => item.status === 2)[0]?.count || 0
this.options[0].series[0].data[2].value = device.data.filter(item => item.status === 0)[0]?.count || 0
this.options[1].series[0].data[0].value = gateway.data.filter(item => item.status === 1)[0]?.count || 0
this.options[1].series[0].data[1].value = gateway.data.filter(item => item.status === 2)[0]?.count || 0
this.options[1].series[0].data[2].value = gateway.data.filter(item => item.status === 0)[0]?.count || 0
this.myCharts.forEach((chart, index) => {
chart.setOption(this.options[index])
})
}
}
}
</script>
<style lang="scss" scoped>
.header {
font-family: Lato;
.title {
color: #174a84;
}
.date {
color: #a5c9ff;
}
}
.box_container {
background: #fff;
width: 100%;
box-shadow: rgba(0, 0, 0, 0.1) 0px 20px 25px -5px,
rgba(0, 0, 0, 0.04) 0px 10px 10px -5px;
padding: 20px 40px 70px;
display: flex;
justify-content: center;
.chart_container {
width: 50%;
display: flex;
flex-direction: column;
align-items: center;
.title {
width: 90%;
height: 100px;
line-height:100px;
text-align:center;
color: #fff;
font-size: 36px;
font-family: Microsoft YaHei;
font-weight: bold;
}
.chart {
width: 100%;
aspect-ratio: 1 / 0.6;
}
.legend {
display: flex;
justify-content: space-evenly;
}
}
.bg_purple {
border: 2px solid transparent;
background: linear-gradient(#789af3, #789af3) padding-box,
repeating-linear-gradient(
45deg,
#789af3 0,
#789af3 12px,
#fff 12px,
#fff 24px
);
}
.bg_blue {
border: 2px solid transparent;
background: linear-gradient(#00ccf2, #00ccf2) padding-box,
repeating-linear-gradient(
45deg,
#00ccf2 0,
#00ccf2 12px,
#fff 12px,
#fff 24px
);
}
}
</style>

View File

@ -0,0 +1,632 @@
<template>
<div class="app-container">
<el-row
type="flex"
justify="space-between"
style="align-items: baseline; margin-bottom: 30px"
>
<div class="header">
<h1 class="title">设备节点</h1>
<span class="date">{{ date }}</span>
<span class="sum">{{ "共" + deviceSum + "个设备节点" }}</span>
</div>
</el-row>
<el-row>
<el-button
class="add_btn"
icon="el-icon-plus"
@click="visible = true"
>新增节点</el-button>
</el-row>
<div class="list_container">
<div class="title_box">
<p />
<p>节点名称</p>
<p>终端ID</p>
<p>所属网关ID</p>
<p>支持OTAA</p>
<p>支持Class-C</p>
<i />
</div>
<div v-for="(item, index) in list" :key="index" class="item_box">
<img src="@/assets/images/geteway_device.png">
<p>{{ item.name }}</p>
<p>
<a @click="showDataDialog(item.devEui, item.status)">{{
item.devEui
}}</a>
</p>
<p>{{ item.gatewayid }}</p>
<p>{{ item.supportOtaa ? "是" : "否" }}</p>
<p>{{ item.supportClassc ? "是" : "否" }}</p>
<i
type="danger"
class="remove_icon el-icon-delete"
@click="remove(item.name, item.devEui)"
/>
</div>
</div>
<!-- 设备采集数据弹窗 -->
<div v-show="dataVisible" class="dialog_wrapper">
<div class="dialog-container">
<div class="dialog_header">
<p class="ip">设备采集数据</p>
<div class="remove_icon" @click="close">X</div>
</div>
<div class="dialog_content">
<div class="title" style="margin-top: -40px">
<img src="@/assets/images/gateway_device_status.png">
<p class="label">设备在线状态</p>
<p class="data">{{ transformStatus(currentDevice.status) }}</p>
</div>
<div class="line" />
<div v-if="currentDevice.dataType === 1">
<div class="title">
<img src="@/assets/images/gateway_device_data_type.png">
<p class="label">采集数据类型</p>
<p class="data">{{ transformType(currentDevice.dataType) }}</p>
</div>
<div class="chart">
<img src="@/assets/images/gateway_device_pm1.png">
<p class="data_center">{{ currentDevice.value }}</p>
<p class="unit">单位ug/</p>
</div>
<p class="name blue">PM1.0</p>
</div>
<div v-else-if="currentDevice.dataType === 2">
<div class="title">
<img src="@/assets/images/gateway_device_data_type.png">
<p class="label">采集数据类型</p>
<p class="data">{{ transformType(currentDevice.dataType) }}</p>
</div>
<div class="chart">
<img src="@/assets/images/gateway_device_pm2_5.png">
<p class="data_center">{{ currentDevice.value }}</p>
<p class="unit">单位ug/</p>
</div>
<p class="name green">PM2.5</p>
</div>
<div v-else-if="currentDevice.dataType === 3">
<div class="title">
<img src="@/assets/images/gateway_device_data_type.png">
<p class="label">采集数据类型</p>
<p class="data">{{ transformType(currentDevice.dataType) }}</p>
</div>
<div class="chart">
<img src="@/assets/images/gateway_device_pm10.png">
<p class="data_center">{{ currentDevice.value }}</p>
<p class="unit">单位ug/</p>
</div>
<p class="name orange">PM10</p>
</div>
<div v-else-if="currentDevice.dataType === 4">
<div class="title">
<img src="@/assets/images/gateway_device_data_type.png">
<p class="label">采集数据类型</p>
<p class="data">{{ transformType(currentDevice.dataType) }}</p>
</div>
<div class="chart">
<img src="@/assets/images/gateway_device_temperature.png">
<p class="data">{{ currentDevice.value }}</p>
<p class="label">当前温度</p>
<p class="unit">单位</p>
</div>
</div>
<div v-else-if="currentDevice.dataType === 5">
<div class="title">
<img src="@/assets/images/gateway_device_data_type.png">
<p class="label">采集数据类型</p>
<p class="data">{{ transformType(currentDevice.dataType) }}</p>
</div>
<div class="chart">
<img src="@/assets/images/gateway_device_humidity.png">
<p class="data">{{ currentDevice.value }}</p>
<p class="label">当前湿度</p>
<p class="unit">单位%RH</p>
</div>
</div>
<div v-else>
<div style="height: 200px; line-height: 200px; text-align: center">
暂无数据
</div>
</div>
</div>
</div>
</div>
<!-- 设备新增弹窗 -->
<div v-show="visible" class="dialog_wrapper">
<div class="dialog-container">
<div class="dialog_header">
<p class="ip">设备节点新增</p>
<div class="remove_icon" @click="close">X</div>
</div>
<div class="dialog_content">
<div class="form">
<div class="form_content">
<div class="item_title">
<img src="@/assets/images/device_name.png">名称
</div>
<div class="item_content">
<el-input v-model="form.name" />
</div>
<div class="item_title">
<img src="@/assets/images/device_devEUI.png">终端<br>ID
</div>
<div class="item_content">
<el-input v-model="form.devEui" />
</div>
</div>
<div class="line" />
<div class="form_content">
<div class="item_title">
<img src="@/assets/images/device_joinEUI.png">所属网<br>关ID
</div>
<div class="item_content">
<el-input v-model="form.gatewayid" />
</div>
</div>
<div class="line" />
<div class="form_content">
<div class="item_title">
<img src="@/assets/images/device_otaa.png">支持<br>OTAA
</div>
<div class="item_content">
<el-switch v-model="form.supportOtaa" active-color="#00CCF2" />
</div>
<div class="item_title">
<img src="@/assets/images/device_classc.png">支持<br>Class-C
</div>
<div class="item_content">
<el-switch
v-model="form.supportClassc"
active-color="#00CCF2"
/>
</div>
</div>
<div class="line" />
</div>
<div class="footer">
<el-button
size="medium"
class="cancel_btn"
@click="close"
>取消</el-button>
<el-button
size="medium"
class="save_btn"
style="border-radius: 4px"
type="primary"
@click="save"
>保存</el-button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { getList, add, remove } from '@/api/gateway/device'
export default {
data() {
return {
date: '',
deviceSum: 0,
list: [
{
name: '11',
devEui: '12345689',
gatewayid: '123',
supportOtaa: true,
supportClassc: false
}
],
visible: false,
dataVisible: false,
form: {
name: '',
devEui: '',
gatewayid: '',
supportOtaa: true,
supportClassc: true
},
currentDevice: {},
interval: null
}
},
mounted() {
this.date = new Date().toLocaleDateString()
this.getList()
// localStorage.setItem('A00001', JSON.stringify({ dataType: 4, value: 25 }))
// localStorage.setItem('A00002', JSON.stringify({ dataType: 5, value: 63 }))
// localStorage.setItem('A00004', JSON.stringify({ dataType: 1, value: 25 }))
// localStorage.setItem('A00005', JSON.stringify({ dataType: 2, value: 63 }))
// localStorage.setItem('A00006', JSON.stringify({ dataType: 3, value: 63 }))
},
methods: {
getList() {
getList({ gatewayid: '' }).then((res) => {
this.list = res.data
this.deviceSum = this.list.length
})
},
close() {
this.visible = false
this.dataVisible = false
clearInterval(this.interval)
},
remove(name, devEui) {
this.$confirm(`确认删除设备节点${name}`, '提示', { type: 'warning' })
.then(() => {
remove({ devEui }).then((res) => {
this.$message.success('删除成功')
this.getList()
})
})
.catch()
},
save() {
console.log('form', this.form)
add({ ...this.form, status: 0 }).then((res) => {
this.$message.success('新增成功')
this.getList()
this.close()
})
},
showDataDialog(devEui, status) {
this.dataVisible = true
this.getDeviceData(devEui, status)
this.interval = setInterval(() => {
this.getDeviceData(devEui, status)
}, 1000)
},
getDeviceData(devEui, status) {
const data = localStorage.getItem(devEui)
? JSON.parse(localStorage.getItem(devEui))
: {}
this.currentDevice = {
status,
...data
}
},
transformStatus(status) {
switch (status) {
case 0:
return '从未连接'
case 1:
return '在线'
case 2:
return '离线'
}
},
transformType(type) {
switch (type) {
case 1:
return 'PM1.0'
case 2:
return 'PM2.5'
case 3:
return 'PM10'
case 4:
return '温度'
case 5:
return '湿度'
}
}
}
}
</script>
<style lang="scss" scoped>
.header {
font-family: Lato;
.title {
color: #174a84;
}
.date {
color: #a5c9ff;
}
.sum {
margin-left: 85px;
color: #a5c9ff;
}
}
.add_btn {
width: 100%;
color: #fff;
border: 2px solid transparent;
background: linear-gradient(#789af3, #789af3) padding-box,
repeating-linear-gradient(
45deg,
#789af3 0,
#789af3 12px,
#fff 12px,
#fff 24px
);
}
.list_container {
width: 100%;
margin-top: 30px;
.title_box {
width: 100%;
height: 40px;
margin: 10px auto;
display: inline-grid;
grid-template-columns:
80px minmax(105px, 1fr) 2fr 2fr 2fr minmax(130px, 2fr) minmax(130px, 2fr)
100px;
justify-items: center;
align-content: center;
align-items: center;
p {
line-height: 40px;
font-size: 26px;
font-family: Microsoft YaHei;
color: #2e4765;
}
}
.item_box {
border: 1px solid rgba(0, 204, 242, 0.2);
background: linear-gradient(
90deg,
rgba(0, 204, 242, 0.2) 0%,
rgba(150, 230, 161, 0.2) 100%
);
border-radius: 10px;
width: 100%;
height: 100px;
margin: 5px auto;
display: inline-grid;
grid-template-columns:
80px 1fr 2fr 2fr 2fr minmax(130px, 2fr) minmax(130px, 2fr)
100px;
justify-items: center;
align-content: center;
align-items: center;
img {
width: 45px;
aspect-ratio: 1 / 1;
}
p {
line-height: 100px;
font-size: 26px;
font-family: Microsoft YaHei;
color: #2e4765;
a:hover {
text-decoration: underline;
}
}
.remove_icon {
font-size: 26px;
color: #f56c6c;
cursor: pointer;
}
}
}
.dialog_wrapper {
z-index: 2;
position: fixed;
left: 230px;
right: 0;
top: 0;
bottom: 0;
margin: 0;
&::before {
content: "";
position: absolute;
left: -230px;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
z-index: 2;
}
.dialog-container {
z-index: 10;
width: 600px;
transform: none;
margin: 15vh auto;
position: relative;
border-radius: 8px;
border: 2px solid #00ccf2;
font-family: Microsoft YaHei;
box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.16);
.dialog_header {
background-color: #00ccf2;
text-align: center;
color: #fff;
padding: 5px;
position: relative;
p {
font-size: 24px;
font-weight: bold;
margin: 5px auto;
}
.remove_icon {
position: absolute;
font-size: 18px;
right: 15px;
top: 10px;
cursor: pointer;
}
}
.dialog_content {
background-color: #fff;
border-radius: 0 0 8px 8px;
padding: 40px 0 20px;
.form {
width: 80%;
margin: auto;
.form_content {
display: flex;
width: 100%;
div {
justify-content: center;
display: flex;
align-items: center;
img {
vertical-align: middle;
margin-right: 10px;
}
}
.item_title {
text-align: center;
background: #c7f4fc;
width: 110px;
min-height: 90px;
font-size: 20px;
color: #00ccf2;
}
.item_content {
flex: 1;
word-break: break-all;
padding: 9px;
}
}
}
.title {
display: flex;
align-items: center;
justify-content: center;
img {
width: 18px;
height: 22px;
margin-right: 5px;
}
p {
font-size: 23px;
font-family: Microsoft YaHei;
}
.label {
color: #00ccf2;
}
.data {
color: #5f6874;
margin-left: 10px;
}
}
.line {
width: 100%;
height: 2px;
background-image: linear-gradient(
to right,
#00ccf2 0%,
#00ccf2 50%,
transparent 50%
);
background-size: 18px 100%;
background-repeat: repeat-x;
}
.chart {
margin: auto;
width: 180px;
aspect-ratio: 1 / 1;
position: relative;
img {
width: 100%;
height: 100%;
}
p {
margin: 0;
}
.data {
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
font-size: 45px;
font-family: DINCond-Medium;
color: #358caa;
line-height: 63px;
}
.label {
position: absolute;
left: 50%;
bottom: 45px;
transform: translateX(-50%);
font-size: 25px;
font-family: Microsoft YaHei;
font-weight: bold;
color: #358caa;
white-space: nowrap;
}
.unit {
position: absolute;
right: 0;
bottom: 0;
transform: translateX(100%);
font-size: 15px;
font-family: Segoe UI;
color: #358caa;
}
.data_center {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: 69px;
font-family: DINCond-Medium;
color: #000000;
}
}
.name {
font-size: 35px;
font-family: MicrosoftYaHei;
line-height: 44px;
margin: 0;
text-align: center;
}
.orange {
color: #ff4e00;
}
.green {
color: #20be0b;
}
.blue {
color: #00ccf2;
}
}
// .radio {
// ::v-deep .el-radio-button__inner {
// color: #8f59eb;
// border-color: #8f59eb;
// }
// ::v-deep .el-radio-button__orig-radio:checked + .el-radio-button__inner {
// background-color: #8f59eb;
// color: #fff;
// }
// ::v-deep .el-radio-button:first-child .el-radio-button__inner {
// border-radius: 9px 0 0 9px;
// }
// ::v-deep .el-radio-button:last-child .el-radio-button__inner {
// border-radius: 0 9px 9px 0;
// }
// }
}
.footer {
text-align: center;
margin: 40px auto 20px;
.cancel_btn {
border-color: #00ccf2;
color: #00ccf2;
&:hover {
background-color: rgba(0, 204, 242, 0.1);
}
}
.save_btn {
border-color: #00ccf2;
background-color: #00ccf2;
border-radius: 2px;
&:hover {
background-color: #00ccf2;
}
}
}
}
::v-deep .el-input > .el-input__inner {
border: none;
}
</style>

View File

@ -0,0 +1,553 @@
<template>
<div class="app-container">
<el-row
type="flex"
justify="space-between"
style="align-items: baseline; margin-bottom: 30px"
>
<div class="header">
<h1 class="title">网关</h1>
<span class="date">{{ date }}</span>
<span class="sum">{{ "共" + deviceSum + "个网关" }}</span>
</div>
</el-row>
<el-row>
<el-button
class="add_btn"
icon="el-icon-plus"
@click="visible = true"
>新增网关</el-button>
</el-row>
<div class="list_container">
<div class="title_box">
<p />
<p />
<p>最后连接时间</p>
<p>网关名称</p>
<p>网关ID</p>
<i />
</div>
<div
v-for="(item, index) in gatewayList"
:key="index"
class="item_box"
:class="
item.status === 1
? 'online'
: item.status === 2
? 'offline'
: 'never_seen'
"
>
<img :src="getStatusImg(item.status)">
<p>
{{
item.status === 1 ? "在线" : item.status === 2 ? "离线" : "从未连接"
}}
</p>
<p>{{ item.lastConnect ? item.lastConnect : "" }}</p>
<p>{{ item.name }}</p>
<p>
<a @click="showDataDialog(item.gatewayid)">{{ item.gatewayid }}</a>
</p>
<i
type="danger"
class="remove_icon el-icon-delete"
@click="remove(item.name, item.gatewayid)"
/>
</div>
</div>
<!-- 设备列表弹窗 -->
<div v-show="dataVisible" class="dialog_wrapper">
<div class="dialog-container">
<div class="dialog_header">
<p class="ip">包含设备列表</p>
<div class="remove_icon" @click="close">X</div>
</div>
<div class="dialog_content">
<div v-if="deviceList.length > 0" class="list_container">
<div class="title_box">
<p />
<p>状态</p>
<p>节点名称</p>
<p>终端ID</p>
</div>
<div class="content_box">
<div
v-for="(item, index) in deviceList"
:key="index"
class="item_box"
>
<img src="@/assets/images/geteway_device.png">
<p>
{{
item.status === 1 ? "在线" : item.status === 2 ? "离线" : "从未连接"
}}
</p>
<p>{{ item.name }}</p>
<p>{{ item.devEui }}</p>
</div>
</div>
</div>
<div v-else style="min-height:200px;text-align:center">
暂无数据
</div>
</div>
</div>
</div>
<!-- 设备新增弹窗 -->
<div v-show="visible" class="dialog_wrapper">
<div class="dialog-container">
<div class="dialog_header">
<p class="ip">网关新增</p>
<div class="remove_icon" @click="close">X</div>
</div>
<div class="dialog_content">
<div class="form">
<div class="form_content">
<div class="item_title">
<img src="@/assets/images/stock_status.png">名称
</div>
<div class="item_content">
<el-input v-model="form.name" />
</div>
</div>
<div class="line" />
<div class="form_content">
<div class="item_title">
<img src="@/assets/images/stock_no.png">网关<br>ID
</div>
<div class="item_content">
<el-input v-model="form.gatewayid" />
</div>
</div>
<div class="line" />
</div>
<div class="footer">
<el-button
size="medium"
class="cancel_btn"
@click="close"
>取消</el-button>
<el-button
size="medium"
class="save_btn"
style="border-radius: 4px"
type="primary"
@click="save"
>保存</el-button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import {
getGatewayList,
getDeviceList,
add,
updateGateway,
updateDevice,
remove
} from '@/api/gateway/manage'
export default {
data() {
return {
date: '',
deviceSum: 10,
// list: [
// {
// name: '11',
// gatewayid: '1234568932131331',
// lastConnect: '2023-10-11 12:00:00',
// status: 1,
// imgSrc: require('@/assets/images/gateway_online.png')
// },
// {
// name: '11',
// gatewayid: '1234568932131331',
// lastConnect: '2023-10-11 12:00:00',
// status: 2,
// imgSrc: require('@/assets/images/gateway_offline.png')
// },
// {
// name: '11',
// gatewayid: '1234568932131331',
// lastConnect: '2023-10-11 12:00:00',
// status: 0,
// imgSrc: require('@/assets/images/gateway_never_seen.png')
// }
// ],
deviceList: [],
visible: false,
dataVisible: false,
form: {
name: '',
gatewayid: ''
}
}
},
computed: {
...mapGetters(['gatewayList'])
},
mounted() {
this.date = new Date().toLocaleDateString()
this.getGatewayList()
},
methods: {
getGatewayList() {
getGatewayList().then((res) => {
this.$store.dispatch('app/setGatewayList', res.data)
this.deviceSum = this.gatewayList.length
})
},
getStatusImg(status) {
let imgSrc
switch (status) {
case 0:
imgSrc = require('@/assets/images/gateway_never_seen.png')
break
case 1:
imgSrc = require('@/assets/images/gateway_online.png')
break
case 2:
imgSrc = require('@/assets/images/gateway_offline.png')
break
}
return imgSrc
},
close() {
this.visible = false
this.dataVisible = false
},
remove(name, gatewayid) {
this.$confirm(`确认删除网关${name}`, '提示', { type: 'warning' })
.then(() => {
remove({ gatewayid }).then((res) => {
this.$message.success('删除成功')
this.getGatewayList()
})
})
.catch()
},
save() {
add({ ...this.form, status: 0 }).then((res) => {
this.$message.success('新增成功')
this.getGatewayList()
this.close()
})
},
showDataDialog(gatewayid) {
this.getDeviceList(gatewayid)
this.dataVisible = true
},
getDeviceList(gatewayid) {
getDeviceList({ gatewayid }).then((res) => {
if (res.data) {
this.deviceList = res.data
}
})
}
}
}
</script>
<style lang="scss" scoped>
.header {
font-family: Lato;
.title {
color: #174a84;
}
.date {
color: #a5c9ff;
}
.sum {
margin-left: 85px;
color: #a5c9ff;
}
}
.add_btn {
width: 100%;
color: #fff;
border: 2px solid transparent;
background: linear-gradient(#789af3, #789af3) padding-box,
repeating-linear-gradient(
45deg,
#789af3 0,
#789af3 12px,
#fff 12px,
#fff 24px
);
}
.list_container {
width: 100%;
margin-top: 30px;
.title_box {
width: 100%;
height: 40px;
margin: 10px auto;
display: inline-grid;
grid-template-columns: 100px 1fr minmax(260px, 2fr) 1fr 2fr 100px;
justify-items: center;
align-content: center;
align-items: center;
p {
line-height: 40px;
font-size: 26px;
font-family: Microsoft YaHei;
color: #2e4765;
}
}
.item_box {
border-radius: 10px;
width: 100%;
height: 100px;
margin: 5px auto;
display: inline-grid;
grid-template-columns: 100px 1fr minmax(260px, 2fr) 1fr 2fr 100px;
justify-items: center;
align-content: center;
align-items: center;
img {
width: 45px;
aspect-ratio: 1 / 1;
}
p {
line-height: 100px;
font-size: 26px;
font-family: Microsoft YaHei;
color: #2e4765;
a:hover {
text-decoration: underline;
}
}
.remove_icon {
font-size: 26px;
color: #f56c6c;
cursor: pointer;
}
}
.online {
background: linear-gradient(
90deg,
rgba(212, 252, 121, 0.2) 0%,
rgba(150, 230, 161, 0.2) 100%
);
}
.offline {
background: linear-gradient(
225deg,
rgba(254, 243, 229, 0.2) 0%,
rgba(254, 199, 168, 0.2) 0%,
rgba(254, 180, 141, 0.2) 38%,
rgba(255, 78, 0, 0.2) 100%
);
}
.never_seen {
background: linear-gradient(
90deg,
rgba(0, 204, 242, 0.2) 0%,
rgba(120, 154, 243, 0.2) 62%,
rgba(173, 223, 232, 0.2) 100%
);
}
}
.dialog_wrapper {
z-index: 2;
position: fixed;
left: 230px;
right: 0;
top: 0;
bottom: 0;
margin: 0;
&::before {
content: "";
position: absolute;
left: -230px;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
z-index: 2;
}
.dialog-container {
z-index: 10;
width: 600px;
transform: none;
margin: 15vh auto;
position: relative;
border-radius: 8px;
border: 2px solid #20be0b;
font-family: Microsoft YaHei;
box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.16);
.dialog_header {
background-color: #20be0b;
text-align: center;
color: #fff;
padding: 5px;
position: relative;
p {
font-size: 24px;
font-weight: bold;
margin: 5px auto;
}
.remove_icon {
position: absolute;
font-size: 18px;
right: 15px;
top: 10px;
cursor: pointer;
}
}
.dialog_content {
background-color: #fff;
border-radius: 0 0 8px 8px;
padding: 40px 10px 20px;
.form {
width: 80%;
margin: auto;
.form_content {
display: flex;
width: 100%;
div {
justify-content: center;
display: flex;
align-items: center;
img {
vertical-align: middle;
margin-right: 10px;
}
}
.item_title {
text-align: center;
background: #d5f3d1;
width: 110px;
min-height: 90px;
font-size: 20px;
color: #20be0b;
}
.item_content {
flex: 1;
word-break: break-all;
padding: 9px;
}
}
.line {
width: 100%;
height: 2px;
background-image: linear-gradient(
to right,
#20be0b 0%,
#20be0b 50%,
transparent 50%
);
background-size: 18px 100%;
background-repeat: repeat-x;
}
}
.list_container {
width: 100%;
margin-top: -20px;
.title_box {
width: 100%;
height: 40px;
margin: 10px auto;
display: inline-grid;
grid-template-columns: 80px 1fr 1fr 1fr;
justify-items: center;
align-content: center;
align-items: center;
p {
line-height: 40px;
font-size: 22px;
font-family: Microsoft YaHei;
color: #2e4765;
}
}
.content_box {
min-height: 200px;
.item_box {
border: 1px solid rgba(0, 204, 242, 0.2);
background: linear-gradient(
90deg,
rgba(0, 204, 242, 0.2) 0%,
rgba(150, 230, 161, 0.2) 100%
);
border-radius: 10px;
width: 100%;
height: 70px;
margin: 5px auto;
display: inline-grid;
grid-template-columns: 80px 1fr 1fr 1fr;
justify-items: center;
align-content: center;
align-items: center;
img {
width: 45px;
aspect-ratio: 1 / 1;
}
p {
line-height: 100px;
font-size: 22px;
font-family: Microsoft YaHei;
color: #2e4765;
a:hover {
text-decoration: underline;
}
}
.remove_icon {
font-size: 26px;
color: #f56c6c;
cursor: pointer;
}
}
}
}
}
// .radio {
// ::v-deep .el-radio-button__inner {
// color: #8f59eb;
// border-color: #8f59eb;
// }
// ::v-deep .el-radio-button__orig-radio:checked + .el-radio-button__inner {
// background-color: #8f59eb;
// color: #fff;
// }
// ::v-deep .el-radio-button:first-child .el-radio-button__inner {
// border-radius: 9px 0 0 9px;
// }
// ::v-deep .el-radio-button:last-child .el-radio-button__inner {
// border-radius: 0 9px 9px 0;
// }
// }
}
.footer {
text-align: center;
margin: 40px auto 20px;
.cancel_btn {
border-color: #20be0b;
color: #20be0b;
&:hover {
background-color: rgba(32, 190, 11, 0.1);
}
}
.save_btn {
border-color: #20be0b;
background-color: #20be0b;
border-radius: 2px;
&:hover {
background-color: #20be0b;
}
}
}
}
::v-deep .el-input > .el-input__inner {
border: none;
}
</style>

View File

@ -91,7 +91,12 @@
</template>
<script>
import { getGifCode } from '@/api/user'
import { mapGetters } from 'vuex'
import {
getGatewayList,
deviceOffline,
gatewayOffline
} from '@/api/gateway/manage'
import SIdentify from './components/identify'
export default {
@ -139,6 +144,9 @@ export default {
height: 0
}
},
computed: {
...mapGetters(['gatewayList'])
},
watch: {
$route: {
handler: function(route) {
@ -179,6 +187,9 @@ export default {
// this.loading = false
// })
.then(() => {
this.getGatewayList()
this.offlineDevice()
this.offlineGateway()
this.$router.push({ path: this.redirect || '/overview' })
this.loading = false
})
@ -268,6 +279,18 @@ export default {
}
bubble.y += bubble.vy
this.draw(bubble)
},
offlineDevice() {
deviceOffline().then()
},
offlineGateway() {
gatewayOffline().then()
},
getGatewayList() {
getGatewayList().then((res) => {
this.$store.dispatch('app/setGatewayList', res.data)
console.log('gatewayList on login page', this.gatewayList)
})
}
}
}

View File

@ -574,6 +574,7 @@ export default {
const temp = res.data.filter(
(resTask) => task.id === resTask.id
)[0]
task.status = temp.status
task.statusDesc = statusList[temp.status]
task.ratio = Math.round(
(temp.currentProcess / this.currentFileSize) * 100

View File

@ -6,7 +6,7 @@
style="align-items: baseline; margin-bottom: 30px"
>
<div class="header">
<div class="type_btn">
<!-- <div class="type_btn">
<el-button-group>
<el-button
@click="handleClick(1, '矽灵通RISCV')"
@ -35,7 +35,7 @@
@click="handleClick(10, '教学型ARM')"
>教学型ARM</el-button>
</el-button-group>
</div>
</div> -->
<h1 class="title">库存管理</h1>
<span class="date">{{ date }}</span>
<el-input
@ -44,6 +44,14 @@
suffix-icon="el-icon-search"
@change="handleSearch"
/>
<el-select v-model="fzDeviceType" style="margin-left:20px" clearable placeholder="请选择" @change="handleSearch">
<el-option
v-for="(item, index) in deviceOptions"
:key="index"
:value="item"
:label="item"
/>
</el-select>
</div>
</el-row>
<el-row>
@ -55,7 +63,7 @@
</el-row>
<div class="table_container">
<div class="title">
<div class="name">{{ terminalTypeDesc }}</div>
<div class="name">{{ fzDeviceType ? fzDeviceType : '全部' }}</div>
<div class="num">
<span>{{ "总数:" + totalnum }}</span>
</div>
@ -125,6 +133,8 @@
command="教学型RISCV"
>教学型RISCV</el-dropdown-item>
<el-dropdown-item command="教学型ARM">教学型ARM</el-dropdown-item>
<el-dropdown-item command="矽数通工业型RISCV">矽数通工业型RISCV</el-dropdown-item>
<el-dropdown-item command="矽数通工业型ARM">矽数通工业型ARM</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div class="remove_icon" @click="close">X</div>
@ -215,9 +225,22 @@ export default {
operationType: 1, // 1-2-
form: {},
totalnum: 0,
terminalType: 0,
terminalTypeDesc: '全部',
searchNo: ''
searchNo: '',
fzDeviceType: '',
deviceOptions: [
'矽灵通RISCV',
'矽灵通ARM',
'矽望通',
'矽慧通',
'矽达通RISCV',
'矽达通ARM',
'AIIT-RISCV',
'AIIT-ARM',
'教学型RISCV',
'教学型ARM',
'矽数通工业型RISCV',
'矽数通工业型ARM'
]
}
},
mounted() {
@ -231,12 +254,6 @@ export default {
this.totalnum = res.data.length
})
},
getListByType(data) {
getListByType(data).then((res) => {
this.list = res.data
this.totalnum = res.data.length
})
},
handleClick(type, desc) {
this.terminalType = type
this.terminalTypeDesc = desc
@ -245,13 +262,10 @@ export default {
}
this.getListByType(data)
},
handleSearch(no) {
if (!no) {
this.terminalTypeDesc = '全部'
this.getList()
} else {
handleSearch() {
if (this.searchNo) {
const data = {
device_no: no
device_no: this.searchNo
}
getListByNo(data).then((res) => {
if (res.data) {
@ -263,6 +277,14 @@ export default {
this.totalnum = 0
}
})
} else {
const data = {
type: this.fzDeviceType
}
getListByType(data).then((res) => {
this.list = res.data
this.totalnum = res.data.length
})
}
},
remove(fzDeviceNo) {
@ -288,7 +310,7 @@ export default {
this.visible = true
},
add() {
this.form = { fzDeviceType: '矽灵通RISCV' }
this.form = { fzDeviceType: this.fzDeviceType ? this.fzDeviceType : '矽灵通RISCV' }
this.operationType = 1 //
this.visible = true
},
@ -296,7 +318,46 @@ export default {
this.visible = false
},
async save() {
const data = { ...this.form }
let prepend = ''
switch (this.form.fzDeviceType) {
case '矽灵通RISCV':
prepend = '3559A 5G-'
break
case '矽灵通ARM':
prepend = '3559A 4G-'
break
case '矽望通':
prepend = 'M7 A7 5G-'
break
case '矽慧通':
prepend = 'MLU220 5G-'
break
case '矽达通RISCV':
prepend = 'RISCV 4G-1'
break
case '矽达通ARM':
prepend = 'ARM 4G-'
break
case 'AIIT-RISCV':
prepend = 'RISCV-'
break
case 'AIIT-ARM':
prepend = 'ARM-'
break
case '教学型RISCV':
prepend = 'EDU-RISCV-'
break
case '教学型ARM':
prepend = 'EDU-ARM-'
break
case '矽数通工业型RISCV':
prepend = 'RISCV LoRa-'
break
case '矽数通工业型ARM':
prepend = 'M4 LoRa-'
break
}
const data = { ...this.form, fzDeviceNo: prepend + this.form.fzDeviceNo }
const res =
this.operationType === 1
? await add(data)
@ -329,6 +390,9 @@ export default {
display: inline-block;
margin-left: 20%;
}
.el-select{
width: 280px;
}
.type_btn {
// margin-right: 10%;
float: right;