From 074bcb8394fab66ce531c219e1e7de7c142ff2d5 Mon Sep 17 00:00:00 2001
From: doum <doum>
Date: 星期五, 29 五月 2026 11:07:10 +0800
Subject: [PATCH] 新增智能电表、空调管理
---
admin/src/views/business/components/YwElectricalRemote.vue | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 232 insertions(+), 7 deletions(-)
diff --git a/admin/src/views/business/components/YwElectricalRemote.vue b/admin/src/views/business/components/YwElectricalRemote.vue
index 6261d0e..571c9e1 100644
--- a/admin/src/views/business/components/YwElectricalRemote.vue
+++ b/admin/src/views/business/components/YwElectricalRemote.vue
@@ -1,6 +1,6 @@
<template>
<GlobalWindow title="鐢佃〃杩滅▼鎺у埗" :visible.sync="visible" width="780px" :show-confirm="false">
- <el-tabs v-model="activeTab">
+ <el-tabs v-model="activeTab" @tab-click="onTabClick">
<el-tab-pane label="鍩烘湰鎿嶄綔" name="basic">
<div class="info-block">
<p>閲囬泦鍣ㄥ彿锛歿{ info.collectorId }} <span :class="info.online === 1 ? 'green' : 'red'">{{ info.online === 1 ? '鍦ㄧ嚎' : '绂荤嚎' }}</span></p>
@@ -9,11 +9,23 @@
<p>鐢佃〃绫诲瀷锛歿{ info.type }}</p>
<p>鍏宠仈鎴块棿锛歿{ info.roomNames }}</p>
</div>
+ <div class="meter-data-block" v-loading="infoLoading">
+ <div class="meter-data-block__title">鏈�鏂版妱琛ㄦ暟鎹�</div>
+ <p>褰撳墠鎬荤數閲忥細{{ totalPower }}</p>
+ <p>鐢甸噺鍚屾鏃堕棿锛歿{ latest && latest.addTime ? latest.addTime : '-' }}</p>
+ <p>褰撳墠鍓╀綑閲戦(鍏�)锛歿{ latest && latest.ye != null ? latest.ye : (info.balance != null ? info.balance : '0.0000') }}</p>
+ <p>浣欓鍚屾鏃堕棿锛歿{ balanceSyncTime }}</p>
+ </div>
<div class="btn-row">
<el-button type="primary" :loading="isOperating" @click="doOperate('resetPrepay', '纭娓呴浂骞跺垏鎹㈠埌棰勪粯璐规ā寮忓悧锛�')">娓呴浂骞跺垏鎹㈠埌棰勪粯璐规ā寮�</el-button>
<el-button type="primary" :loading="isOperating" @click="doOperate('resetPostpay', '纭娓呴浂骞跺垏鎹㈠埌鍚庝粯璐规ā寮忓悧锛�')">娓呴浂骞跺垏鎹㈠埌鍚庝粯璐规ā寮�</el-button>
- <el-button type="primary" :loading="isOperating" @click="doOperate('trip', '纭鎷夐椄鍚楋紵')">鎷夐椄</el-button>
- <el-button type="primary" :loading="isOperating" @click="doOperate('close', '纭鍚堥椄鍚楋紵')">鍚堥椄</el-button>
+ </div>
+ <div class="btn-row btn-row--relay">
+ <span class="btn-row__label">鎷夊悎闂告搷浣�</span>
+ <el-button type="danger" :loading="isOperating" @click="doRelayOperate('trip')">鎷夐椄</el-button>
+ <el-button type="primary" :loading="isOperating" @click="doRelayOperate('close')">鍚堥椄</el-button>
+ <el-button type="warning" :loading="isOperating" @click="doRelayOperate('powerProtect')">淇濈數</el-button>
+ <el-button type="danger" plain :loading="isOperating" @click="doRelayOperate('powerProtectRelease')">瑙i櫎淇濈數</el-button>
</div>
</el-tab-pane>
<el-tab-pane label="寮�鎴�" name="open">
@@ -39,18 +51,86 @@
@confirm="doOperate('recharge', '纭鍏呭�煎悧锛�')"
/>
</el-tab-pane>
+ <el-tab-pane label="鎺у埗璁板綍" name="records">
+ <el-table v-loading="recordsLoading" :data="recordsList" stripe size="small" class="records-table">
+ <el-table-column label="鎿嶄綔绫诲瀷" min-width="100" align="center">
+ <template slot-scope="{ row }">{{ formatActionType(row.actionType) }}</template>
+ </el-table-column>
+ <el-table-column prop="oprId" label="鎿嶄綔ID" min-width="150" align="center" show-overflow-tooltip />
+ <el-table-column label="鐘舵��" min-width="80" align="center">
+ <template slot-scope="{ row }">
+ <span :class="statusClass(row.status)">{{ formatStatus(row.status) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="resultMsg" label="鎵ц缁撴灉" min-width="140" align="center" show-overflow-tooltip>
+ <template slot-scope="{ row }">{{ row.resultMsg || '-' }}</template>
+ </el-table-column>
+ <el-table-column prop="createDate" label="鎿嶄綔鏃堕棿" min-width="150" align="center" />
+ <el-table-column label="鎶ユ枃" min-width="120" align="center">
+ <template slot-scope="{ row }">
+ <el-button type="text" :disabled="!row.requestBody" @click="openJson('璇锋眰鎶ユ枃', row.requestBody)">璇锋眰</el-button>
+ <el-button type="text" :disabled="!row.responseBody" @click="openJson('鍝嶅簲鎶ユ枃', row.responseBody)">鍝嶅簲</el-button>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" min-width="100" align="center" fixed="right">
+ <template slot-scope="{ row }">
+ <el-button
+ v-if="row.status === 0 && row.oprId"
+ type="text"
+ :loading="queryLoadingId === row.id"
+ v-permissions="['business:ywelectricalactions:queryResult']"
+ @click="handleQueryResult(row)"
+ >鏌ヨ缁撴灉</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination
+ small
+ @size-change="handleRecordsSizeChange"
+ @current-change="loadActionRecords"
+ :pagination="recordsPagination"
+ />
+ </el-tab-pane>
</el-tabs>
+ <OperaInterfaceLogWindow ref="jsonWindow" />
+ <template slot="footer">
+ <el-button type="primary" icon="el-icon-refresh" :loading="isRefreshing" @click="refreshMeterData">鍒锋柊鐢佃〃鏁版嵁</el-button>
+ <el-button @click="visible = false">杩斿洖</el-button>
+ </template>
</GlobalWindow>
</template>
<script>
import GlobalWindow from '@/components/common/GlobalWindow'
+import Pagination from '@/components/common/Pagination'
+import OperaInterfaceLogWindow from '@/components/business/OperaInterfaceLogWindow'
import { getRemoteInfo, operate } from '@/api/business/ywelectrical'
+import * as actionsApi from '@/api/business/ywelectricalactions'
import AccountRechargePanel from './AccountRechargePanel'
+
+const RELAY_ACTIONS = {
+ trip: { label: '鎷夐椄', message: '纭瀵硅鐢佃〃鎵ц鎷夐椄鎿嶄綔鍚楋紵' },
+ close: { label: '鍚堥椄', message: '纭瀵硅鐢佃〃鎵ц鍚堥椄鎿嶄綔鍚楋紵' },
+ powerProtect: { label: '淇濈數', message: '纭瀵硅鐢佃〃鎵ц淇濈數鎿嶄綔鍚楋紵' },
+ powerProtectRelease: { label: '瑙i櫎淇濈數', message: '纭瀵硅鐢佃〃鎵ц瑙i櫎淇濈數鎿嶄綔鍚楋紵' }
+}
+
+const ACTION_TYPE_MAP = {
+ 1: '棰勪粯璐规竻闆�',
+ 2: '鍚庝粯璐规竻闆�',
+ 3: '杩滅▼閿�鎴�',
+ 4: '鎷夐椄',
+ 5: '鍚堥椄',
+ 6: '寮�鎴�',
+ 7: '鍏呭��',
+ 8: '鎶勮〃',
+ 9: '淇濈數',
+ 10: '瑙i櫎淇濈數'
+}
export default {
name: 'YwElectricalRemote',
- components: { GlobalWindow, AccountRechargePanel },
+ components: { GlobalWindow, AccountRechargePanel, Pagination, OperaInterfaceLogWindow },
data () {
return {
visible: false,
@@ -60,7 +140,23 @@
latest: null,
purchaseCount: '0',
form: { money: 0, remark: '' },
- isOperating: false
+ isOperating: false,
+ isRefreshing: false,
+ infoLoading: false,
+ recordsLoading: false,
+ recordsList: [],
+ recordsPagination: { pageIndex: 1, pageSize: 10, total: 0 },
+ queryLoadingId: null
+ }
+ },
+ computed: {
+ totalPower () {
+ if (!this.latest) return '0.00kWh'
+ const v = this.latest.zhygzdl || '0'
+ return String(v).toLowerCase().indexOf('kwh') >= 0 ? v : v + 'kWh'
+ },
+ balanceSyncTime () {
+ return (this.latest && this.latest.addTime) ? this.latest.addTime : (this.info.balanceTime || '-')
}
},
methods: {
@@ -68,21 +164,86 @@
this.electricalId = row.id
this.activeTab = tab || 'basic'
this.form = { money: 0, remark: '' }
+ this.recordsPagination.pageIndex = 1
+ this.recordsList = []
this.visible = true
this.loadInfo()
+ if (this.activeTab === 'records') {
+ this.loadActionRecords(1)
+ }
+ },
+ onTabClick (tab) {
+ if (tab.name === 'records' && this.recordsList.length === 0) {
+ this.loadActionRecords(1)
+ }
},
loadInfo () {
+ if (!this.electricalId) return
+ this.infoLoading = true
getRemoteInfo(this.electricalId).then(res => {
this.info = res.electrical || {}
this.latest = res.latestData
this.purchaseCount = res.purchaseCount || '0'
- }).catch(e => this.$tip.apiFailed(e))
+ }).catch(e => this.$tip.apiFailed(e)).finally(() => { this.infoLoading = false })
+ },
+ refreshMeterData () {
+ if (!this.electricalId) return
+ this.isRefreshing = true
+ operate({
+ electricalId: this.electricalId,
+ action: 'readMeter'
+ })
+ .then(res => {
+ this.$tip.apiSuccess(res || '鎶勮〃璇锋眰宸叉彁浜わ紝姝e湪鍒锋柊鏁版嵁')
+ this.loadInfo()
+ this.$emit('success')
+ if (this.activeTab === 'records') {
+ this.loadActionRecords(this.recordsPagination.pageIndex)
+ }
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.isRefreshing = false })
+ },
+ loadActionRecords (page) {
+ if (!this.electricalId) return
+ if (page) {
+ this.recordsPagination.pageIndex = page
+ }
+ this.recordsLoading = true
+ actionsApi.fetchList({
+ page: this.recordsPagination.pageIndex,
+ capacity: this.recordsPagination.pageSize,
+ model: { electricalId: this.electricalId }
+ })
+ .then(data => {
+ this.recordsList = data.records || []
+ this.recordsPagination.total = data.total || 0
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.recordsLoading = false })
+ },
+ handleRecordsSizeChange (size) {
+ this.recordsPagination.pageSize = size
+ this.loadActionRecords(1)
},
readMeter () {
this.submitOperate('readMeter', true)
},
doOperate (action, msg) {
this.$dialog.actionConfirm(msg, '鎿嶄綔纭')
+ .then(() => this.submitOperate(action, false))
+ .catch(() => {})
+ },
+ relayMeterTip () {
+ const name = this.info.name || '-'
+ const address = this.info.address || '-'
+ return `琛ㄥ悕绉帮細${name}锛岃〃鍦板潃锛�${address}`
+ },
+ doRelayOperate (action) {
+ const cfg = RELAY_ACTIONS[action]
+ if (!cfg) return
+ const meterTip = this.relayMeterTip()
+ this.$dialog.actionConfirm(`${cfg.message}锛�${meterTip}锛塦, '鎿嶄綔纭')
.then(() => this.submitOperate(action, false))
.catch(() => {})
},
@@ -98,15 +259,79 @@
this.$tip.apiSuccess(res || (silent ? '鎶勮〃璇锋眰宸叉彁浜�' : '鎻愪氦鎴愬姛锛岃鍦ㄣ�愭棩甯哥敤鐢电鐞�-鍏呭�艰褰曘�戜腑鏌ョ湅鍏呭�肩粨鏋�'))
this.loadInfo()
this.$emit('success')
+ if (this.activeTab === 'records') {
+ this.loadActionRecords(this.recordsPagination.pageIndex)
+ }
})
.catch(e => this.$tip.apiFailed(e))
.finally(() => { this.isOperating = false })
+ },
+ formatActionType (val) {
+ return ACTION_TYPE_MAP[val] || val || '-'
+ },
+ formatStatus (val) {
+ if (val === 0 || val === '0') return '澶勭悊涓�'
+ if (val === 1 || val === '1') return '鎴愬姛'
+ if (val === 2 || val === '2') return '澶辫触'
+ return val == null || val === '' ? '-' : String(val)
+ },
+ statusClass (val) {
+ if (val === 1 || val === '1') return 'green'
+ if (val === 2 || val === '2') return 'red'
+ if (val === 0 || val === '0') return 'orange'
+ return ''
+ },
+ openJson (title, content) {
+ if (!content) return
+ this.$refs.jsonWindow.open(title, { content })
+ },
+ handleQueryResult (row) {
+ this.queryLoadingId = row.id
+ actionsApi.queryResult(row.id)
+ .then(msg => {
+ this.$tip.success(msg || '鏌ヨ瀹屾垚')
+ this.loadActionRecords(this.recordsPagination.pageIndex)
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.queryLoadingId = null })
}
}
}
</script>
<style scoped>
-.info-block { margin-bottom: 16px; line-height: 28px; color: #303133; }
+.info-block { margin-bottom: 12px; line-height: 28px; color: #303133; }
+.meter-data-block {
+ margin-bottom: 16px;
+ padding: 12px 14px;
+ background: #fafbfe;
+ border: 1px solid #eef2f8;
+ border-radius: 8px;
+ line-height: 26px;
+ color: #303133;
+}
+.meter-data-block__title {
+ font-weight: 600;
+ font-size: 14px;
+ margin-bottom: 6px;
+ color: #222;
+}
+.meter-data-block p { margin: 0; }
.btn-row .el-button { margin: 0 8px 8px 0; }
+.btn-row--relay {
+ margin-top: 4px;
+ padding-top: 12px;
+ border-top: 1px dashed #ebeef5;
+}
+.btn-row__label {
+ display: block;
+ width: 100%;
+ margin-bottom: 8px;
+ font-size: 13px;
+ color: #909399;
+}
+.records-table { width: 100%; margin-top: 4px; }
+.green { color: #67c23a; }
+.red { color: #f56c6c; }
+.orange { color: #e6a23c; }
</style>
--
Gitblit v1.9.3