From ea689dd91eaa72425dc01759042c3b4eb2186512 Mon Sep 17 00:00:00 2001
From: doum <doum>
Date: 星期三, 17 六月 2026 10:14:44 +0800
Subject: [PATCH] 新增智能电表、空调管理

---
 h5/pages/customer/contract/detail.vue |  403 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 385 insertions(+), 18 deletions(-)

diff --git a/h5/pages/customer/contract/detail.vue b/h5/pages/customer/contract/detail.vue
index 5085398..caabd9c 100644
--- a/h5/pages/customer/contract/detail.vue
+++ b/h5/pages/customer/contract/detail.vue
@@ -32,7 +32,11 @@
 
     <view v-if="contract.billStatusTip" :class="['cu-bill-tip', 'cu-bill-tip--inset', billTipClass(contract.billStatusType)]">
 
-      <text class="cu-bill-tip__icon">{{ billTipIcon(contract.billStatusType) }}</text>
+      <view class="cu-bill-tip__icon-box">
+
+        <u-icon :name="billTipIconName(contract.billStatusType)" color="#ffffff" size="18" />
+
+      </view>
 
       <text class="cu-bill-tip__text">{{ contract.billStatusTip }}</text>
 
@@ -54,7 +58,17 @@
 
       <view class="cu-panel">
 
-        <view class="cu-panel__title">鎴挎簮淇℃伅</view>
+        <view class="cu-panel__head">
+
+          <view class="cu-panel__icon cu-panel__icon--room">
+
+            <u-icon name="home-fill" color="#ffffff" size="20" />
+
+          </view>
+
+          <text class="cu-panel__title-text">鎴挎簮淇℃伅</text>
+
+        </view>
 
         <view v-if="contract.roomList && contract.roomList.length" class="cu-room-list">
 
@@ -80,7 +94,17 @@
 
       <view class="cu-panel">
 
-        <view class="cu-panel__title">鍩烘湰淇℃伅</view>
+        <view class="cu-panel__head">
+
+          <view class="cu-panel__icon cu-panel__icon--info">
+
+            <u-icon name="file-text-fill" color="#ffffff" size="20" />
+
+          </view>
+
+          <text class="cu-panel__title-text">鍩烘湰淇℃伅</text>
+
+        </view>
 
         <view class="cu-kv__item">
 
@@ -142,9 +166,211 @@
 
 
 
+      <view v-if="showZlClause" class="cu-panel cu-clause-panel cu-clause-panel--zl">
+
+        <view class="cu-clause-panel__head">
+
+          <view class="cu-clause-panel__badge">
+
+            <u-icon name="red-packet-fill" color="#ffffff" size="22" />
+
+          </view>
+
+          <text class="cu-clause-panel__title">绉熻祦鏉℃</text>
+
+        </view>
+
+
+
+        <view class="cu-clause-summary">
+
+          <view class="cu-clause-chip">
+
+            <text class="cu-clause-chip__label">鎶奸噾閲戦</text>
+
+            <text class="cu-clause-chip__value">{{ formatDeposit(contract.zlDeposit) }}</text>
+
+          </view>
+
+          <view class="cu-clause-chip">
+
+            <text class="cu-clause-chip__label">浠樻鏂瑰紡</text>
+
+            <text class="cu-clause-chip__value">{{ formatPayType(contract.zlPayType) }}</text>
+
+          </view>
+
+          <view class="cu-clause-chip cu-clause-chip--wide">
+
+            <text class="cu-clause-chip__label">鍏嶇鏈�</text>
+
+            <text class="cu-clause-chip__value">{{ formatFreePeriod(contract.zlFreeStartDate, contract.zlFreeEndDate) }}</text>
+
+          </view>
+
+        </view>
+
+
+
+        <view v-if="contract.zlDetailList && contract.zlDetailList.length" class="cu-clause-detail-list">
+
+          <view v-for="(item, idx) in contract.zlDetailList" :key="item.id || idx" class="cu-clause-detail-card">
+
+            <view class="cu-clause-detail-grid">
+
+              <view class="cu-clause-detail-grid__item">
+
+                <text class="cu-clause-detail-grid__label">寮�濮嬫棩鏈�</text>
+
+                <text class="cu-clause-detail-grid__value">{{ formatDate(item.startDate) || '-' }}</text>
+
+              </view>
+
+              <view class="cu-clause-detail-grid__item">
+
+                <text class="cu-clause-detail-grid__label">缁撴潫鏃ユ湡</text>
+
+                <text class="cu-clause-detail-grid__value">{{ formatDate(item.endDate) || '-' }}</text>
+
+              </view>
+
+              <view class="cu-clause-detail-grid__item cu-clause-detail-grid__item--highlight">
+
+                <text class="cu-clause-detail-grid__label">鍚堝悓鍗曚环</text>
+
+                <text class="cu-clause-detail-grid__value">{{ formatPrice(item.price, item.circleType) }}</text>
+
+              </view>
+
+              <view class="cu-clause-detail-grid__item cu-clause-detail-grid__item--highlight">
+
+                <text class="cu-clause-detail-grid__label">浠樻鎻愬墠澶╂暟</text>
+
+                <text class="cu-clause-detail-grid__value">{{ item.advanceDays != null ? item.advanceDays + '澶�' : '-' }}</text>
+
+              </view>
+
+            </view>
+
+          </view>
+
+        </view>
+
+        <view v-else class="cu-clause-empty">鏆傛棤鏉℃鏄庣粏</view>
+
+      </view>
+
+
+
+      <view v-if="showWyClause" class="cu-panel cu-clause-panel cu-clause-panel--wy">
+
+        <view class="cu-clause-panel__head">
+
+          <view class="cu-clause-panel__badge">
+
+            <u-icon name="tags-fill" color="#ffffff" size="22" />
+
+          </view>
+
+          <text class="cu-clause-panel__title">鐗╀笟璐规潯娆�</text>
+
+        </view>
+
+
+
+        <view class="cu-clause-summary">
+
+          <view class="cu-clause-chip">
+
+            <text class="cu-clause-chip__label">鎶奸噾閲戦</text>
+
+            <text class="cu-clause-chip__value">{{ formatDeposit(contract.wyDeposit) }}</text>
+
+          </view>
+
+          <view class="cu-clause-chip">
+
+            <text class="cu-clause-chip__label">浠樻鏂瑰紡</text>
+
+            <text class="cu-clause-chip__value">{{ formatPayType(contract.wyPayType) }}</text>
+
+          </view>
+
+          <view class="cu-clause-chip cu-clause-chip--wide">
+
+            <text class="cu-clause-chip__label">鍏嶇鏈�</text>
+
+            <text class="cu-clause-chip__value">{{ formatFreePeriod(contract.wyFreeStartDate, contract.wyFreeEndDate) }}</text>
+
+          </view>
+
+        </view>
+
+
+
+        <view v-if="contract.wyDetailList && contract.wyDetailList.length" class="cu-clause-detail-list">
+
+          <view v-for="(item, idx) in contract.wyDetailList" :key="item.id || idx" class="cu-clause-detail-card">
+
+            <view class="cu-clause-detail-grid">
+
+              <view class="cu-clause-detail-grid__item">
+
+                <text class="cu-clause-detail-grid__label">寮�濮嬫棩鏈�</text>
+
+                <text class="cu-clause-detail-grid__value">{{ formatDate(item.startDate) || '-' }}</text>
+
+              </view>
+
+              <view class="cu-clause-detail-grid__item">
+
+                <text class="cu-clause-detail-grid__label">缁撴潫鏃ユ湡</text>
+
+                <text class="cu-clause-detail-grid__value">{{ formatDate(item.endDate) || '-' }}</text>
+
+              </view>
+
+              <view class="cu-clause-detail-grid__item cu-clause-detail-grid__item--highlight">
+
+                <text class="cu-clause-detail-grid__label">鍚堝悓鍗曚环</text>
+
+                <text class="cu-clause-detail-grid__value">{{ formatPrice(item.price, item.circleType) }}</text>
+
+              </view>
+
+              <view class="cu-clause-detail-grid__item cu-clause-detail-grid__item--highlight">
+
+                <text class="cu-clause-detail-grid__label">浠樻鎻愬墠澶╂暟</text>
+
+                <text class="cu-clause-detail-grid__value">{{ item.advanceDays != null ? item.advanceDays + '澶�' : '-' }}</text>
+
+              </view>
+
+            </view>
+
+          </view>
+
+        </view>
+
+        <view v-else class="cu-clause-empty">鏆傛棤鏉℃鏄庣粏</view>
+
+      </view>
+
+
+
       <view class="cu-panel">
 
-        <view class="cu-panel__title">鍚堝悓闄勪欢</view>
+        <view class="cu-panel__head">
+
+          <view class="cu-panel__icon cu-panel__icon--file">
+
+            <u-icon name="folder" color="#ffffff" size="20" />
+
+          </view>
+
+          <text class="cu-panel__title-text">鍚堝悓闄勪欢</text>
+
+        </view>
 
         <view v-if="contract.fileList && contract.fileList.length">
 
@@ -160,7 +386,11 @@
 
           >
 
-            <text class="cu-file-item__icon">馃搸</text>
+            <view class="cu-file-item__icon-box">
+
+              <u-icon name="attach" color="#ffffff" size="22" />
+
+            </view>
 
             <view class="cu-file-item__main">
 
@@ -385,6 +615,7 @@
 <script>
 
 import { customerContractDetail } from '@/api'
+import { baseUrl } from '@/utils/config.js'
 
 const COST_TYPE_MAP = {
   0: '绉熻祦璐�',
@@ -404,7 +635,17 @@
   computed: {
     receivableLabel () { return this.billType === 0 ? '搴斾粯閲戦' : '搴旀敹閲戦' },
     receivedLabel () { return this.billType === 0 ? '瀹炰粯閲戦' : '瀹炴敹閲戦' },
-    dueDateLabel () { return this.billType === 0 ? '搴斾粯鏃ユ湡' : '搴旀敹鏃ユ湡' }
+    dueDateLabel () { return this.billType === 0 ? '搴斾粯鏃ユ湡' : '搴旀敹鏃ユ湡' },
+    showZlClause () {
+      if (!this.contract) return false
+      const t = this.contract.type
+      return t === 0 || t === 2
+    },
+    showWyClause () {
+      if (!this.contract) return false
+      const t = this.contract.type
+      return t === 0 || t === 1
+    }
   },
 
   onLoad (q) { this.id = q.id; this.load() },
@@ -419,7 +660,7 @@
 
         if (reloadContract) this.contract = res.data.contract
 
-        this.bills = res.data.bills || []
+        this.bills = this.sortBillsByPlanPayDate(res.data.bills || [])
 
       }).finally(() => {
 
@@ -441,6 +682,22 @@
 
     },
 
+    sortBillsByPlanPayDate (list) {
+
+      return [...list].sort((a, b) => {
+
+        const pa = this.formatDate(a.planPayDate) || ''
+
+        const pb = this.formatDate(b.planPayDate) || ''
+
+        if (pb !== pa) return pb.localeCompare(pa)
+
+        return (b.id || 0) - (a.id || 0)
+
+      })
+
+    },
+
     costTypeText (type) { return COST_TYPE_MAP[type] || '璐﹀崟' },
 
     formatMoney (val) {
@@ -456,6 +713,74 @@
       if (!val) return ''
 
       return String(val).replace('T', ' ').substring(0, 10)
+
+    },
+
+    formatDeposit (val) {
+
+      if (val === null || val === undefined || val === '') return '-'
+
+      return `${val}鍏僠
+
+    },
+
+    formatPayType (payType) {
+
+      if (payType === 1) return '姣忎笁涓湀涓�浠�'
+
+      if (payType === 2) return '鍏釜鏈堜竴浠�'
+
+      if (payType === 3) return '涓�骞翠竴浠�'
+
+      if (payType === 0) return '涓�娆℃�т粯娆�'
+
+      return '-'
+
+    },
+
+    formatFreePeriod (start, end) {
+
+      const s = this.formatDate(start)
+
+      const e = this.formatDate(end)
+
+      if (!s && !e) return '-'
+
+      return `${s || '-'} ~ ${e || '-'}`
+
+    },
+
+    circleTypeUnit (type) {
+
+      const map = {
+
+        0: '鍏�/m虏路澶�',
+
+        1: '鍏�/m虏路鏈�',
+
+        2: '鍏�/m虏路骞�',
+
+        3: '鍏�/澶�',
+
+        4: '鍏�/鏈�',
+
+        5: '鍏�/骞�',
+
+        6: '鍏�/鍦�'
+
+      }
+
+      return map[type] || ''
+
+    },
+
+    formatPrice (price, circleType) {
+
+      if (price === null || price === undefined || price === '') return '-'
+
+      const unit = this.circleTypeUnit(circleType)
+
+      return unit ? `${price} ${unit}` : String(price)
 
     },
 
@@ -527,19 +852,47 @@
 
     },
 
-    billTipIcon (type) {
+    billTipIconName (type) {
 
-      if (type === 'danger') return '鈿狅笍'
+      if (type === 'danger') return 'close-circle-fill'
 
-      if (type === 'warn') return '鈴�'
+      if (type === 'warn') return 'clock-fill'
 
-      return '鉁�'
+      return 'checkmark-circle-fill'
+
+    },
+
+    resolveFileUrl (raw) {
+
+      if (!raw) return ''
+
+      const url = String(raw).trim()
+
+      if (/^https?:\/\//i.test(url)) return url
+
+      if (typeof window === 'undefined') return url
+
+      if (url.startsWith('//')) return `${window.location.protocol}${url}`
+
+      if (url.startsWith('/')) return `${window.location.origin}${url}`
+
+      try {
+
+        const origin = new URL(baseUrl, window.location.href).origin
+
+        return `${origin}/${url.replace(/^\/+/, '')}`
+
+      } catch (e) {
+
+        return `${window.location.origin}/${url.replace(/^\/+/, '')}`
+
+      }
 
     },
 
     openFile (file) {
 
-      const url = file.fileurlFull || file.fileurl
+      const url = this.resolveFileUrl(file.fileurlFull || file.fileurl)
 
       if (!url) {
 
@@ -549,13 +902,29 @@
 
       }
 
-      // #ifdef H5
+      if (typeof window !== 'undefined' && window.document) {
 
-      window.open(url, '_blank')
+        const link = document.createElement('a')
 
-      // #endif
+        link.style.display = 'none'
 
-      // #ifndef H5
+        link.href = url
+
+        link.target = '_blank'
+
+        link.rel = 'noopener noreferrer'
+
+        if (file.name) link.setAttribute('download', file.name)
+
+        document.body.appendChild(link)
+
+        link.click()
+
+        document.body.removeChild(link)
+
+        return
+
+      }
 
       uni.showLoading({ title: '涓嬭浇涓�' })
 
@@ -582,8 +951,6 @@
         complete: () => uni.hideLoading()
 
       })
-
-      // #endif
 
     }
 

--
Gitblit v1.9.3