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/YwCustomerConditionerTab.vue |  245 ++++++++++++++++++++++++++++++++++--------------
 1 files changed, 174 insertions(+), 71 deletions(-)

diff --git a/admin/src/views/business/components/YwCustomerConditionerTab.vue b/admin/src/views/business/components/YwCustomerConditionerTab.vue
index e731bc2..7a1e404 100644
--- a/admin/src/views/business/components/YwCustomerConditionerTab.vue
+++ b/admin/src/views/business/components/YwCustomerConditionerTab.vue
@@ -1,24 +1,34 @@
 <template>
   <div v-loading="loading" class="conditioner-tab">
-    <section class="config-section">
-      <div class="section-title">璁¤垂閰嶇疆</div>
-      <el-form label-width="150px" size="small" class="config-form">
-        <el-row :gutter="24">
+    <el-form ref="configForm" :model="form" :rules="rules" label-width="150px" size="small" class="config-form">
+      <section class="config-section">
+        <div class="section-title">璁¤垂閰嶇疆</div>
+        <el-row :gutter="24" class="config-first-row">
           <el-col :span="12">
-            <el-form-item label="璁¤垂寮�鍏�">
-              <el-switch v-model="form.isPwr" :active-value="1" :inactive-value="0" active-text="寮�鍚�" inactive-text="鍏抽棴"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="18:00-09:00 涓嶅仠鏈�">
-              <el-switch v-model="form.isRestStop" :active-value="1" :inactive-value="0" active-text="鏄�" inactive-text="鍚�"/>
+            <el-form-item label="娆犺垂棰濆害" prop="stopMoney" class="stop-money-item">
+              <div class="el-input-group stop-money-group">
+                <el-input-number
+                  v-model="form.stopMoney"
+                  :min="0"
+                  :precision="2"
+                  :step="10"
+                  controls-position="right"
+                  class="stop-money-input"
+                />
+                <div class="el-input-group__append">鍏�</div>
+              </div>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="24">
           <el-col :span="12">
-            <el-form-item label="娆犺垂棰濆害锛堝厓锛�">
-              <el-input-number v-model="form.stopMoney" :min="0" :precision="2" :step="10" controls-position="right" style="width: 100%"/>
+            <el-form-item label="璁¤垂寮�鍏�" prop="isPwr">
+              <el-switch v-model="form.isPwr" :active-value="1" :inactive-value="0" active-text="寮�鍚�" inactive-text="鍏抽棴"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="18:00-09:00 涓嶅仠鏈�" prop="isRestStop">
+              <el-switch v-model="form.isRestStop" :active-value="1" :inactive-value="0" active-text="鏄�" inactive-text="鍚�"/>
             </el-form-item>
           </el-col>
         </el-row>
@@ -32,36 +42,38 @@
             placeholder="璇疯緭鍏ュ娉紙閫夊~锛�"
           />
         </el-form-item>
-      </el-form>
-    </section>
+      </section>
 
-    <section class="config-section">
-      <div class="section-header">
-        <span class="section-title">鍏宠仈鍐呮満</span>
-        <el-button type="primary" size="small" icon="el-icon-plus" @click="openSelector">娣诲姞鍐呮満</el-button>
-      </div>
-      <el-table :data="form.conditioners" stripe size="small" class="device-table" empty-text="鏆傛湭鍏宠仈鍐呮満锛岃鐐瑰嚮娣诲姞">
-        <el-table-column label="璁惧" min-width="200" align="left" show-overflow-tooltip>
-          <template slot-scope="{ row }">{{ deviceLabel(row) }}</template>
-        </el-table-column>
-        <el-table-column prop="platformDevId" label="骞冲彴璁惧ID" min-width="110" align="center"/>
-        <el-table-column label="鍦ㄧ嚎" min-width="80" align="center">
-          <template slot-scope="{ row }">
-            <span :class="row.online === '鍦ㄧ嚎' ? 'green' : 'red'">{{ row.online || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="鐢佃垂鍗犳瘮%" min-width="130" align="center">
-          <template slot-scope="{ row }">
-            <el-input-number v-model="row.devRatio" :min="1" :max="100" size="small" controls-position="right"/>
-          </template>
-        </el-table-column>
-        <el-table-column label="鎿嶄綔" width="80" align="center" fixed="right">
-          <template slot-scope="{ $index }">
-            <el-button type="text" class="red" @click="form.conditioners.splice($index, 1)">绉婚櫎</el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-    </section>
+      <section class="config-section">
+        <div class="section-header">
+          <span class="section-title required-title">鍏宠仈鍐呮満</span>
+          <el-button type="primary" size="small" icon="el-icon-plus" @click="openSelector">娣诲姞鍐呮満</el-button>
+        </div>
+        <el-form-item prop="conditioners" label-width="0" class="conditioners-form-item">
+          <el-table :data="form.conditioners" stripe size="small" class="device-table" empty-text="鏆傛湭鍏宠仈鍐呮満锛岃鐐瑰嚮娣诲姞">
+            <el-table-column label="璁惧" min-width="200" align="left" show-overflow-tooltip>
+              <template slot-scope="{ row }">{{ deviceLabel(row) }}</template>
+            </el-table-column>
+            <el-table-column prop="platformDevId" label="骞冲彴璁惧ID" min-width="110" align="center"/>
+            <el-table-column label="鍦ㄧ嚎" min-width="80" align="center">
+              <template slot-scope="{ row }">
+                <span :class="row.online === '鍦ㄧ嚎' ? 'green' : 'red'">{{ row.online || '-' }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="鐢佃垂鍗犳瘮%" min-width="130" align="center">
+              <template slot-scope="{ row }">
+                <el-input-number v-model="row.devRatio" :min="1" :max="100" size="small" controls-position="right"/>
+              </template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔" width="80" align="center" fixed="right">
+              <template slot-scope="{ $index }">
+                <el-button type="text" class="red" @click="removeConditioner($index)">绉婚櫎</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-form-item>
+      </section>
+    </el-form>
 
     <div class="footer-btns">
       <el-button type="primary" :loading="saving" v-permissions="['business:ywcustomerrecharge:bindDevice']" @click="save">淇濆瓨閰嶇疆</el-button>
@@ -105,11 +117,46 @@
       loading: false,
       saving: false,
       form: {
-        isPwr: 1,
-        isRestStop: 0,
-        stopMoney: 0,
+        isPwr: null,
+        isRestStop: null,
+        stopMoney: undefined,
         gsBz: '',
         conditioners: []
+      },
+      rules: {
+        stopMoney: [{
+          validator: (rule, value, callback) => {
+            if (value === null || value === undefined || value === '') {
+              callback(new Error('璇疯緭鍏ユ瑺璐归搴�'))
+            } else if (Number(value) < 0) {
+              callback(new Error('娆犺垂棰濆害涓嶈兘灏忎簬0'))
+            } else {
+              callback()
+            }
+          },
+          trigger: 'blur'
+        }],
+        isPwr: [{
+          validator: (rule, value, callback) => {
+            if (value === 0 || value === 1) callback()
+            else callback(new Error('璇烽�夋嫨璁¤垂寮�鍏�'))
+          },
+          trigger: 'change'
+        }],
+        isRestStop: [{
+          validator: (rule, value, callback) => {
+            if (value === 0 || value === 1) callback()
+            else callback(new Error('璇烽�夋嫨鏄惁鍋滄満'))
+          },
+          trigger: 'change'
+        }],
+        conditioners: [{
+          validator: (rule, value, callback) => {
+            if (value && value.length) callback()
+            else callback(new Error('璇疯嚦灏戝叧鑱斾竴鍙板唴鏈�'))
+          },
+          trigger: 'change'
+        }]
       },
       selectorVisible: false,
       selectorLoading: false,
@@ -143,14 +190,14 @@
         rechargeApi.conditionerPage(this.customerId, { page: 1, capacity: 500, model: {} })
       ]).then(([gs, page]) => {
         if (gs) {
-          this.form.isPwr = gs.isPwr != null ? gs.isPwr : 1
-          this.form.isRestStop = gs.isRestStop != null ? gs.isRestStop : 0
-          this.form.stopMoney = gs.stopMoney != null ? gs.stopMoney : 0
+          this.form.isPwr = gs.isPwr != null ? gs.isPwr : null
+          this.form.isRestStop = gs.isRestStop != null ? gs.isRestStop : null
+          this.form.stopMoney = gs.stopMoney != null ? gs.stopMoney : undefined
           this.form.gsBz = gs.gsBz || ''
         } else {
-          this.form.isPwr = 1
-          this.form.isRestStop = 0
-          this.form.stopMoney = 0
+          this.form.isPwr = null
+          this.form.isRestStop = null
+          this.form.stopMoney = undefined
           this.form.gsBz = ''
         }
         this.form.conditioners = (page.records || []).map(c => ({
@@ -162,29 +209,41 @@
           online: c.online,
           devRatio: c.devRatio != null ? c.devRatio : 100
         }))
+        this.$nextTick(() => {
+          if (this.$refs.configForm) {
+            this.$refs.configForm.clearValidate()
+          }
+        })
       }).catch(e => this.$tip.apiFailed(e)).finally(() => { this.loading = false })
     },
     save () {
-      if (!this.form.conditioners.length) {
-        this.$tip.warning('璇疯嚦灏戝叧鑱斾竴鍙板唴鏈�')
-        return
-      }
-      this.saving = true
-      rechargeApi.saveGsConfig({
-        customerId: this.customerId,
-        isPwr: this.form.isPwr,
-        isRestStop: this.form.isRestStop,
-        stopMoney: this.form.stopMoney,
-        gsBz: this.form.gsBz,
-        conditioners: this.form.conditioners.map(c => ({
-          conditionerId: c.conditionerId,
-          devRatio: c.devRatio
-        }))
-      }).then(() => {
-        this.$tip.success('淇濆瓨鎴愬姛')
-        this.loadConfig()
-        this.$emit('success')
-      }).catch(e => this.$tip.apiFailed(e)).finally(() => { this.saving = false })
+      this.$refs.configForm.validate(valid => {
+        if (!valid) return
+        this.saving = true
+        rechargeApi.saveGsConfig({
+          customerId: this.customerId,
+          isPwr: this.form.isPwr,
+          isRestStop: this.form.isRestStop,
+          stopMoney: this.form.stopMoney,
+          gsBz: this.form.gsBz,
+          conditioners: this.form.conditioners.map(c => ({
+            conditionerId: c.conditionerId,
+            devRatio: c.devRatio
+          }))
+        }).then(() => {
+          this.$tip.success('淇濆瓨鎴愬姛')
+          this.loadConfig()
+          this.$emit('success')
+        }).catch(e => this.$tip.apiFailed(e)).finally(() => { this.saving = false })
+      })
+    },
+    removeConditioner (index) {
+      this.form.conditioners.splice(index, 1)
+      this.$nextTick(() => {
+        if (this.$refs.configForm) {
+          this.$refs.configForm.validateField('conditioners')
+        }
+      })
     },
     openSelector () {
       this.selectorVisible = true
@@ -226,6 +285,11 @@
         })
       })
       this.selectorVisible = false
+      this.$nextTick(() => {
+        if (this.$refs.configForm) {
+          this.$refs.configForm.validateField('conditioners')
+        }
+      })
     }
   }
 }
@@ -265,6 +329,31 @@
 .config-form {
   margin-bottom: 0;
 }
+.config-first-row {
+  margin-bottom: 4px;
+  padding-bottom: 4px;
+  border-bottom: 1px dashed #ebeef5;
+}
+.config-form ::v-deep .stop-money-item {
+  margin-bottom: 8px;
+}
+.stop-money-group {
+  display: inline-flex;
+  width: 100%;
+  max-width: 280px;
+  vertical-align: middle;
+}
+.stop-money-group ::v-deep .stop-money-input {
+  flex: 1;
+  width: auto;
+}
+.stop-money-group ::v-deep .el-input-number {
+  width: 100%;
+}
+.stop-money-group ::v-deep .el-input__inner {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
 .config-form ::v-deep .el-form-item {
   margin-bottom: 12px;
 }
@@ -277,6 +366,20 @@
 .config-form ::v-deep .el-switch {
   width: auto;
 }
+.required-title::before {
+  content: '*';
+  color: #f56c6c;
+  margin-right: 4px;
+}
+.conditioners-form-item {
+  margin-bottom: 0;
+}
+.conditioners-form-item ::v-deep .el-form-item__content {
+  margin-left: 0 !important;
+}
+.conditioners-form-item ::v-deep .el-form-item__error {
+  padding-top: 4px;
+}
 .device-table {
   width: 100%;
 }

--
Gitblit v1.9.3