smart-switch-ui/src/views/system/device/detail.vue
2024-12-16 21:36:46 +08:00

575 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container" v-loading="loading" >
<div v-if="deviceData">
<el-row :gutter="16">
<el-col :lg="18" :xs="24">
<el-card class="box-card" >
<el-descriptions title="设备信息" :column="4">
<template slot="extra">
<el-button size="small" plain icon="el-icon-plus" @click="handleAddTime">增加时长</el-button>
<el-button size="small" plain icon="el-icon-plus" @click="handleAddEle" v-if="deviceData.modelTags.includes(ModelTag.ELE)">增加电量</el-button>
<el-button size="small" plain icon="el-icon-refresh" @click="handleReset">时长归零</el-button>
<el-button size="small" plain icon="el-icon-refresh" @click="handleResetEle" v-if="deviceData.modelTags.includes(ModelTag.ELE)">电量归零</el-button>
<el-button size="small" plain type="warning" icon="el-icon-switch-button" v-if="!isOpen" @click="handleSwitch(true)">强制开启</el-button>
<el-button size="small" plain type="warning" icon="el-icon-switch-button" v-if="isOpen" @click="handleSwitch(false)">强制关闭</el-button>
<el-button size="small" plain icon="el-icon-refresh" @click="refreshIot(deviceId, true)" style="margin-right: 1em">刷新设备信息</el-button>
<el-popover
placement="left"
width="180"
trigger="click">
<div class="qr-code-box">
<qr-code :text="qrCodeText(deviceData)" :width="150" :height="150" />
<p>扫描二维码进行设备绑定</p>
</div>
<el-button size="small" slot="reference" type="primary" icon="el-icon-picture">设备二维码</el-button>
</el-popover>
</template>
<el-descriptions-item label="MAC-1">
{{deviceData.mac | defaultValue}}
<dict-tag :options="dict.type.sm_device_online_status" :value="deviceData.onlineStatus1" size="mini"/>
</el-descriptions-item>
<el-descriptions-item label="MAC-2">
{{deviceData.mac2 | defaultValue}}
<template v-if="!isEmpty(deviceData.mac2)">
<dict-tag :options="dict.type.sm_device_online_status" :value="deviceData.onlineStatus2" size="mini"/>
</template>
</el-descriptions-item>
<el-descriptions-item label="SN">
{{deviceData.deviceNo | defaultValue}}
</el-descriptions-item>
<el-descriptions-item label="设备状态">
<dict-tag :options="dict.type.sm_device_status" :value="deviceData.status" size="mini"/>
</el-descriptions-item>
<el-descriptions-item label="型号">{{deviceData.model | defaultValue}}</el-descriptions-item>
<el-descriptions-item label="产品ID">{{deviceData.productId | defaultValue}}</el-descriptions-item>
<el-descriptions-item label="型号功能" :span="2">
<dict-tag :options="dict.type.sm_model_tag" :value="deviceData.modelTags" size="mini"/>
</el-descriptions-item>
<el-descriptions-item label="限制充值时间">
{{deviceData.limitRechargeTime | defaultValue}}
<boolean-tag v-if="deviceData.limitRechargeTime != null" :value="!isLimitRecharge" size="small" true-text="限制充值中" false-text="已解封"/>
</el-descriptions-item>
<el-descriptions-item label="限制充值原因">
{{deviceData.limitRechargeReason | defaultValue}}
</el-descriptions-item>
</el-descriptions>
</el-card>
<el-card class="box-card">
<el-descriptions title="归属信息" :column="4">
<template #extra>
<el-row type="flex">
<el-button size="small" plain icon="el-icon-link" type="danger" @click="handleUnbindAgent" v-if="deviceData.agentId != null">解绑代理商</el-button>
<bind-agent-button v-else :device-id="deviceData.deviceId" @success="getDevice" style="margin-right: 0.5em"/>
<el-button size="small" plain icon="el-icon-link" type="danger" @click="handleUnbind" v-if="deviceData.userId != null">解绑商户</el-button>
<bind-mch-button v-else :device-id="deviceData.deviceId" @success="getDevice" style="margin-left: 0.5em"/>
</el-row>
</template>
<el-descriptions-item label="设备名称">{{deviceData.deviceName | defaultValue}}</el-descriptions-item>
<el-descriptions-item label="服务模式">
<dict-tag :options="dict.type.device_service_mode" :value="deviceData.serviceMode" size="small"/>
</el-descriptions-item>
<el-descriptions-item label="所属代理" v-if="deviceData.serviceMode === DeviceServiceMode.AGENT">
<user-link :name="deviceData.agentName" :id="deviceData.agentId"/>
</el-descriptions-item>
<el-descriptions-item label="所属商户">
<user-link :name="deviceData.userName" :id="deviceData.userId"/>
</el-descriptions-item>
<el-descriptions-item label="店铺名称">
<store-link :name="deviceData.storeName" :id="deviceData.storeId"/>
</el-descriptions-item>
<el-descriptions-item label="月费">
{{deviceData.monthFee | money | defaultValue}} 元 / 月
(到期时间:{{deviceData.rentTime | defaultValue}}
</el-descriptions-item>
<el-descriptions-item label="平台服务费" v-if="deviceData.serviceMode === DeviceServiceMode.DIRECT">
{{deviceData.realServiceRate | money | defaultValue}} %
</el-descriptions-item>
<el-descriptions-item label="代理服务费" v-if="deviceData.serviceMode === DeviceServiceMode.AGENT">
{{deviceData.agentServiceRate | money | defaultValue}} %
</el-descriptions-item>
<el-descriptions-item label="备注">{{deviceData.remark | defaultValue}}</el-descriptions-item>
</el-descriptions>
</el-card>
<el-card class="box-card">
<el-descriptions :column="4" title="物联网信息">
<el-descriptions-item label="在线状态">
<dict-tag :options="dict.type.sm_device_online_status" :value="deviceData.onlineStatus" size="mini"/>
</el-descriptions-item>
<el-descriptions-item label="最后在线时间">
{{deviceData.lastOnlineTime | defaultValue}}
</el-descriptions-item>
<el-descriptions-item label="版本号">
{{deviceData.version | defaultValue}}
</el-descriptions-item>
<el-descriptions-item label="WIFI" v-if="deviceData.modelTags.includes(ModelTag.WIFI)">
{{deviceData.wifi | defaultValue}}
<el-link @click="handleSetWifi" type="primary" icon="el-icon-link" style="margin-left: 0.5em">远程配网</el-link>
</el-descriptions-item>
<el-descriptions-item label="开关状态">
<el-tag :type="isOpen ? 'success' : 'danger'" size="mini">{{isOpen ? '已开启' : '已关闭'}}</el-tag>
</el-descriptions-item>
<!-- <el-descriptions-item label="电压">{{deviceData.voltage | money | defaultValue }} V</el-descriptions-item>-->
<!-- <el-descriptions-item label="电流">{{deviceData.electricity | money | defaultValue }} A</el-descriptions-item>-->
<!-- <el-descriptions-item label="功率">{{deviceData.realTimePower | money | defaultValue }} W</el-descriptions-item>-->
<el-descriptions-item label="总用电量">
{{deviceData.totalElectriQuantity | money | defaultValue}} 度
</el-descriptions-item>
<!-- <el-descriptions-item label="电压系数">-->
<!-- {{deviceData.vxs | fix3 | defaultValue}}-->
<!-- </el-descriptions-item>-->
<el-descriptions-item label="电量读数">
{{deviceData.totalElectriQuantity - deviceData.initReading | money | defaultValue}} 度
<el-link @click="handleInitReading" type="primary" icon="el-icon-refresh" style="margin-left: 0.5em">重置</el-link>
</el-descriptions-item>
<el-descriptions-item label="最近重置读数时间">
{{deviceData.lastInitReading | defaultValue}}
</el-descriptions-item>
<!-- <el-descriptions-item label="剩余时长">-->
<!-- 数据库:{{surplusTimeDesc(surplusTime).text}} / 设备:{{surplusTimeDesc(deviceData.remainTime).text}}-->
<!-- </el-descriptions-item>-->
<!-- <el-descriptions-item label="剩余电量">-->
<!-- {{deviceData.surplusEle | defaultValue}} 度-->
<!-- </el-descriptions-item>-->
<!-- <el-descriptions-item label="最近更新时间">-->
<!-- {{deviceData.lastPullTime | defaultValue }}-->
<!-- </el-descriptions-item>-->
</el-descriptions>
</el-card>
</el-col>
<el-col :lg="6" :xs="24">
<el-card class="box-card" header="分成信息">
<line-field v-for="bonus of deviceData.bonusList" :label="bonus.arrivalName">
<template #label>
<el-row type="flex">
<dict-tag :options="dict.type.bonus_arrival_type" :value="bonus.arrivalType" size="mini"/>
{{bonus.arrivalName}}
</el-row>
</template>
<template>{{bonus.point | money | defaultValue }} %</template>
</line-field>
</el-card>
<el-card class="box-card">
<template #header>
<el-row type="flex" style="justify-content: space-between;align-items: center">
<div>实时信息</div>
<div>{{deviceData.lastPullTime | defaultValue}}</div>
</el-row>
</template>
<el-row>
<el-col :span="8">
<el-statistic style="margin-bottom: 8px" title="时长(数据库)">
<template #formatter>{{surplusTimeDesc(surplusTime).text}}</template>
</el-statistic>
</el-col>
<el-col :span="8">
<el-statistic style="margin-bottom: 8px" title="时长(设备)">
<template #formatter>{{surplusTimeDesc(deviceData.remainTime).text}}</template>
</el-statistic>
</el-col>
<el-col :span="8">
<el-statistic style="margin-bottom: 8px" title="电量" :value="deviceData.surplusEle" :precision="2" suffix="度"/>
</el-col>
<el-col :span="8">
<el-statistic style="margin-bottom: 8px" title="电压" :value="deviceData.voltage" :precision="2" suffix="V"/>
</el-col>
<el-col :span="8">
<el-statistic style="margin-bottom: 8px" title="电流" :value="deviceData.electricity" :precision="2" suffix="A"/>
</el-col>
<el-col :span="8">
<el-statistic style="margin-bottom: 8px" title="功率" :value="deviceData.realTimePower" :precision="2" suffix="W"/>
</el-col>
<el-col :span="8">
<el-statistic style="margin-bottom: 8px" title="电压系数">
<template #formatter>{{deviceData.vxs | fix3 | defaultValue}}</template>
</el-statistic>
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
<el-card class="box-card">
<el-tabs>
<el-tab-pane label="套餐列表" :lazy="true">
<suit
v-if="deviceData.deviceId != null"
:view="views.device"
:query="{
deviceId: deviceData.deviceId,
userId: deviceData.userId
}"
/>
</el-tab-pane>
<el-tab-pane label="订单列表" :lazy="true">
<recharge :query="{deviceId: deviceData.deviceId}" :view="views.device"/>
</el-tab-pane>
<el-tab-pane label="命令日志" :lazy="true">
<command-log :query="{macList: macList}" :views="views.device"/>
</el-tab-pane>
<el-tab-pane label="时长/电量变化" :lazy="true">
<record-time :query="{deviceId: deviceData.deviceId}" view="device"/>
</el-tab-pane>
<el-tab-pane label="抄表记录" :lazy="true">
<reading-record :device-id="deviceData.deviceId"/>
</el-tab-pane>
<el-tab-pane label="绑定/解绑记录" :lazy="true">
<bind-record :query="{deviceId: deviceData.deviceId}" :view="views.device"/>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
<el-empty v-else description="设备不存在或已被删除"/>
<!--添加时长-->
<el-dialog title="增加时长" :visible.sync="showAddElectricity" center width="400px">
<el-form :model="addElectricityForm" :rules="addRules">
<el-form-item label="时长" prop="amount">
<el-input v-model="addElectricityForm.amount" :min="0" type="number">
<template #append>
<el-select v-model="addElectricityForm.timeUnit" style="width: 6em">
<el-option v-for="item in dict.type.time_unit" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
</template>
</el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" plain @click="submitAddElectricity">确认</el-button>
<el-button plain @click="showAddElectricity = false">取消</el-button>
</template>
</el-dialog>
<device-set-wifi-dialog :show.sync="showSetWifi" :device-id="deviceData.deviceId" @success="onSetWifiSuccess"/>
</div>
</template>
<script>
import {
addEle,
addTime, deviceInitTotalEle,
getDevice,
refreshIot,
resetDevice,
resetEleDevice,
switchDevice,
unbind, unbindAgent
} from '@/api/system/device'
import LineChart from "@/views/dashboard/LineChart.vue";
import RechargeRecord from "@/views/system/device/components/rechargeRecord.vue";
import QrCode from "@/components/QrCode/index.vue";
import MeterRecordReport from "@/views/system/device/components/meterRecordReport.vue";
import ReadingRecord from "@/views/system/device/components/readingRecord.vue";
import {getWxIndexUrl} from "@/utils/wx";
import BindRecord from "@/views/system/bindRecord/index.vue";
import ResetRecord from "@/views/system/device/components/resetRecord.vue";
import TenantList from "@/views/system/device/components/tenantList.vue";
import SuitList from '@/views/system/device/components/suitList.vue'
import Suit from '@/views/ss/suit/index.vue'
import RecordTime from '@/views/ss/time/index.vue'
import { toDescriptionFromSecond } from '@/utils/date'
import StoreLink from '@/components/Business/Store/StoreLink.vue'
import UserLink from '@/components/Business/SmUser/UserLink.vue'
import { $serviceType, $view } from '@/utils/mixins'
import Recharge from '@/views/system/recharge/index.vue'
import BooleanTag from '@/components/BooleanTag/index.vue'
import { DeviceOnlineStatus, DeviceServiceMode, ModelTag } from '@/utils/constants'
import { isEmpty } from '@/utils'
import BindMchButton from '@/views/system/device/components/BindMchButton.vue'
import BindAgentButton from '@/views/system/device/components/BindAgentButton.vue'
import LineField from '@/components/LineField/index.vue'
import DeviceSetWifiDialog from '@/views/system/device/components/DeviceSetWifiDialog.vue'
import CommandLog from "@/views/ss/commandLog/index.vue";
export default {
name: 'Device/:deviceId',
mixins: [$serviceType, $view],
dicts: ['sm_device_status', 'sm_device_outage_way', 'sm_device_notice_way', 'sm_model_tag', 'sm_device_online_status', 'service_type', 'device_service_mode', 'time_unit', 'bonus_arrival_type'],
components: {
CommandLog,
DeviceSetWifiDialog,
LineField,
BindAgentButton,
BindMchButton,
BooleanTag,
Recharge,
UserLink,
StoreLink,
RecordTime,
Suit,
SuitList,
TenantList, ResetRecord, BindRecord, ReadingRecord, MeterRecordReport, QrCode, RechargeRecord, LineChart},
data() {
return {
showSetWifi: false,
loading: false,
deviceData: {
modelTags: [],
bonusList: []
},
timer: null,
surplusTime: 0, // 剩余时长
addElectricityForm: {
amount: 0,
timeUnit: '3'
},
showAddElectricity: false,
addRules: {
amount: [
{ required: true, message: '请输入电量', trigger: 'blur' }
]
},
// 设备ID
deviceId: null,
}
},
computed: {
macList() {
let list = [];
if (!isEmpty(this.deviceData.mac)) {
list.push(this.deviceData.mac)
}
if (!isEmpty(this.deviceData.mac2)) {
list.push(this.deviceData.mac2);
}
return list;
},
DeviceOnlineStatus() {
return DeviceOnlineStatus
},
ModelTag() {
return ModelTag
},
DeviceServiceMode() {
return DeviceServiceMode
},
qrCodeText() {
return (device) => {
return getWxIndexUrl({ s: device.deviceNo});
}
},
surplusTimeDesc() {
return (second) => {
return toDescriptionFromSecond(second);
}
},
isOpen() {
return this.deviceData != null && this.deviceData.powerStatus === '1';
},
// 是否限制充值
isLimitRecharge() {
if (this.deviceData == null || this.deviceData.limitRechargeTime == null) {
return false;
}
return new Date(this.deviceData.limitRechargeTime).getTime() < new Date();
}
},
created() {
this.deviceId = this.$route.params.deviceId;
this.refreshIot(this.deviceId);
},
beforeDestroy() {
clearInterval(this.timer);
},
methods: {
handleInitReading() {
this.$confirm(`是否初始化电量读数?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deviceInitTotalEle(this.deviceData.deviceId).then(res => {
if (res.code === 200) {
this.$message.success("操作成功");
this.getDevice();
}
})
})
},
onSetWifiSuccess(data) {
this.deviceData.wifi = data.wifiName;
},
handleSetWifi() {
this.showSetWifi = true;
},
isEmpty,
handleUnbind() {
this.$confirm('是否强制解绑该商户?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.loading = true;
unbind(this.deviceData.deviceId).then(res => {
if (res.code === 200) {
this.$message.success("操作成功");
}
}).finally(() => {
this.getDevice();
})
})
},
handleUnbindAgent() {
this.$confirm('是否强制解绑该代理商?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.loading = true;
unbindAgent(this.deviceData.deviceId).then(res => {
if (res.code === 200) {
this.$message.success("操作成功");
}
}).finally(() => {
this.getDevice();
})
})
},
handleSwitch(open) {
this.$confirm(`是否确认强制${open ? '开启' : '关闭'}设备?`, '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.loading = true;
switchDevice(this.deviceData.deviceId, open).then(res => {
if (res.code === 200) {
this.$message.success("操作成功");
this.deviceData.powerStatus = open ? '1' : '0';
}
}).finally(() => {
this.loading = false;
})
})
},
// 计算剩余时长
computeSurplusTime() {
if (this.deviceData.expireTime == null) {
return 0;
}
let expireTime = new Date(this.deviceData.expireTime).getTime();
let now = new Date().getTime();
if (expireTime < now) {
return 0;
}
return ((expireTime - now) / 1000).toFixed(2);
},
handleReset() {
this.$confirm('是否确认归零设备时长?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
resetDevice(this.deviceData.deviceId).then(res => {
if (res.code === 200) {
this.$message.success('操作成功');
this.getDevice();
}
})
})
},
handleResetEle() {
this.$confirm('是否确认归零设备电量?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
resetEleDevice(this.deviceData.deviceId).then(res => {
if (res.code === 200) {
this.$message.success('操作成功');
this.getDevice();
}
})
})
},
handleAddTime() {
this.resetAddElectricityForm();
this.showAddElectricity = true;
},
handleAddEle() {
this.$prompt('请输入增加的电量(度)', '增加电量', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputValidator: (val) => {
if (val < 0) {
return "输入的电量不允许小于0";
}
return true;
}
}).then(({ value }) => {
addEle(this.deviceData.deviceId, value).then(res => {
if (res.code === 200) {
this.$message.success('操作成功');
this.getDevice();
}
})
}).catch(() => {
this.$message({
type: 'info',
message: '取消输入'
});
});
},
submitAddElectricity() {
addTime(this.deviceData.deviceId, this.addElectricityForm.amount, this.addElectricityForm.timeUnit).then(res => {
if (res.code === 200) {
this.$message.success('操作成功');
this.showAddElectricity = false;
this.getDevice();
}
})
},
resetAddElectricityForm() {
this.addElectricityForm = {
amount: 0,
timeUnit: '3'
}
},
// 刷新设备信息
refreshIot(deviceId, notice = false) {
this.loading = true;
refreshIot(deviceId).then(res => {
if (res.code !== 200) {
return this.$message.error(res.msg);
}
if (notice) {
this.$message.success('操作成功');
}
}).finally(() => {
this.getDevice();
})
},
getDevice() {
this.loading = true;
getDevice(this.deviceId).then(response => {
this.deviceData = response.data;
this.surplusTime = this.computeSurplusTime();
if (this.timer == null) {
this.timer = setInterval(() => {
this.surplusTime = this.computeSurplusTime();
}, 1000)
}
}).finally(() => {
this.loading = false;
})
},
}
}
</script>
<style scoped>
.remark-text {
color: #ccc;
margin-left: 1em;
}
.statistic {
margin-bottom: 1em;
.el-tag {
line-height: 26px !important;
}
}
</style>