77094dd01f0c6ff59b4fb4fa1105addf34b2398c..ea689dd91eaa72425dc01759042c3b4eb2186512
2026-06-17 doum
新增智能电表、空调管理
ea689d 对比 | 目录
2026-06-17 doum
Merge remote-tracking branch 'origin/2.0.1' into 2.0.1
7ff92e 对比 | 目录
2026-06-04 renkang
短信问题
4a9924 对比 | 目录
已修改8个文件
1418 ■■■■ 文件已修改
h5/pages/customer/bill/list.vue 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
h5/pages/customer/contract/detail.vue 403 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
h5/pages/customer/contract/list.vue 206 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
h5/pages/customer/index.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
h5/styles/customer.scss 503 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/h5/CustomerBillQueryDTO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5BizServiceImpl.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/third/EmayService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
h5/pages/customer/bill/list.vue
@@ -4,27 +4,77 @@
    <view class="cu-bill-page__header">
      <view class="cu-tabs cu-bill-tabs">
      <view class="cu-bill-page__toolbar">
        <view
        <view class="cu-tabs cu-bill-tabs cu-bill-tabs--toolbar">
          v-for="(t, i) in tabs"
          <view
          :key="i"
            v-for="(t, i) in tabs"
          :class="['cu-tab', tabIdx === i ? 'cu-tab--active' : '']"
            :key="i"
          @click="tabIdx = i; load()"
            :class="['cu-tab', tabIdx === i ? 'cu-tab--active' : '']"
        >{{ t }}</view>
            @click="switchPayTab(i)"
          >{{ t }}</view>
        </view>
        <picker :range="costTypeFilters" range-key="label" @change="onCostTypeChange">
          <view :class="['cu-bill-cost-picker', costTypeIdx > 0 ? 'cu-bill-cost-picker--active' : '', costTypePickerClass]">
            <text class="cu-bill-cost-picker__label">{{ costTypeLabel }}</text>
            <text class="cu-bill-cost-picker__arrow">▾</text>
          </view>
        </picker>
      </view>
      <view class="cu-bill-summary">
        <text class="cu-bill-summary__count">{{ list.length }}</text>
        <view class="cu-bill-summary__main">
        <text class="cu-bill-summary__label">笔账单</text>
          <text class="cu-bill-summary__count">{{ list.length }}</text>
          <text class="cu-bill-summary__label">笔账单</text>
        </view>
        <view v-if="overdueCount > 0" class="cu-bill-summary__overdue-group">
          <text class="cu-bill-summary__chip cu-bill-summary__chip--stat">{{ overdueCount }}笔逾期</text>
          <view
            :class="['cu-bill-summary__chip', 'cu-bill-summary__chip--filter', onlyOverdue ? 'cu-bill-summary__chip--filter-on' : '']"
            @click="toggleOnlyOverdue"
          >
            <view :class="['cu-bill-summary__check', onlyOverdue ? 'cu-bill-summary__check--on' : '']">
              <text v-if="onlyOverdue" class="cu-bill-summary__check-icon">✓</text>
            </view>
            <text class="cu-bill-summary__chip-text">仅看逾期</text>
          </view>
        </view>
      </view>
@@ -36,7 +86,7 @@
      <view
        v-for="item in list"
        v-for="item in displayList"
        :key="item.id"
@@ -52,19 +102,21 @@
        <view class="cu-bill-card__body">
          <view class="cu-bill-card__head">
          <view class="cu-bill-card__type-row">
            <view class="cu-bill-card__head-main">
            <view :class="['cu-bill-type-tag', costTypeBadgeClass(item.costType)]">
              <text class="cu-bill-card__type">{{ costTypeText(item.costType) }}</text>
              <text class="cu-bill-card__code">{{ item.code }}</text>
              <text class="cu-bill-type-tag__text">{{ costTypeText(item.costType) }}</text>
            </view>
            <text :class="['cu-status', payStatusClass(item.payStatus)]">{{ payText(item.payStatus) }}</text>
          </view>
          <view class="cu-bill-card__code">{{ item.code }}</view>
@@ -117,10 +169,15 @@
          <view class="cu-bill-card__meta">
            <view class="cu-bill-card__meta-item cu-bill-card__meta-item--full">
              <text class="cu-bill-card__meta-label">计费周期</text>
              <text class="cu-bill-card__meta-value">{{ item.startDate }} ~ {{ item.endDate }}</text>
            </view>
          </view>
@@ -137,7 +194,7 @@
      </view>
      <u-empty v-if="!list.length" text="暂无账单" margin-top="80" />
      <u-empty v-if="!displayList.length" :text="onlyOverdue ? '暂无逾期账单' : '暂无账单'" margin-top="80" />
    </view>
@@ -175,9 +232,105 @@
const COST_TYPE_FILTERS = [
  { label: '费用类型', value: null },
  { label: '租赁费', value: 0 },
  { label: '物业费', value: 1 },
  { label: '租赁押金', value: 2 },
  { label: '物业押金', value: 3 },
  { label: '水电费', value: 4 },
  { label: '杂项费', value: 5 },
  { label: '其他', value: 6 },
  { label: '保证金', value: 7 }
]
export default {
  data () { return { list: [], tabIdx: 0, tabs: ['全部', '待支付', '已支付'] } },
  data () {
    return {
      list: [],
      tabIdx: 0,
      costTypeIdx: 0,
      tabs: ['全部', '待支付', '已支付'],
      costTypeFilters: COST_TYPE_FILTERS,
      onlyOverdue: false
    }
  },
  computed: {
    costTypeLabel () {
      return this.costTypeFilters[this.costTypeIdx].label
    },
    costTypePickerClass () {
      const value = this.costTypeFilters[this.costTypeIdx].value
      if (value == null) return ''
      const map = {
        0: 'cu-bill-cost-picker--rent',
        1: 'cu-bill-cost-picker--property',
        2: 'cu-bill-cost-picker--deposit',
        3: 'cu-bill-cost-picker--deposit',
        4: 'cu-bill-cost-picker--utility',
        5: 'cu-bill-cost-picker--misc',
        6: 'cu-bill-cost-picker--misc',
        7: 'cu-bill-cost-picker--deposit'
      }
      return map[value] || ''
    },
    overdueCount () {
      return this.list.filter(item => this.isOverdue(item)).length
    },
    displayList () {
      if (!this.onlyOverdue) return this.list
      return this.list.filter(item => this.isOverdue(item))
    }
  },
  onShow () { this.load() },
@@ -187,13 +340,79 @@
      const payTab = this.tabIdx === 0 ? null : (this.tabIdx === 1 ? 0 : 1)
      customerBillPage({ page: 1, capacity: 50, model: { payTab } })
      const costType = this.costTypeFilters[this.costTypeIdx].value
        .then(res => { this.list = (res.data && res.data.records) || [] })
      const model = { payTab }
      if (costType != null) model.costType = costType
      customerBillPage({ page: 1, capacity: 50, model })
        .then(res => {
          this.list = (res.data && res.data.records) || []
          if (!this.list.some(item => this.isOverdue(item))) this.onlyOverdue = false
        })
    },
    toggleOnlyOverdue () {
      this.onlyOverdue = !this.onlyOverdue
    },
    switchPayTab (i) {
      if (this.tabIdx === i) return
      this.tabIdx = i
      this.load()
    },
    onCostTypeChange (e) {
      const idx = Number(e.detail.value)
      if (this.costTypeIdx === idx) return
      this.costTypeIdx = idx
      this.load()
    },
    costTypeText (type) { return COST_TYPE_MAP[type] || '账单' },
    costTypeBadgeClass (type) {
      const map = {
        0: 'cu-bill-type-tag--rent',
        1: 'cu-bill-type-tag--property',
        2: 'cu-bill-type-tag--deposit',
        3: 'cu-bill-type-tag--deposit',
        4: 'cu-bill-type-tag--utility',
        5: 'cu-bill-type-tag--misc',
        6: 'cu-bill-type-tag--misc',
        7: 'cu-bill-type-tag--deposit'
      }
      return map[type] || 'cu-bill-type-tag--default'
    },
    formatMoney (val) {
@@ -242,16 +461,27 @@
    },
    contractPeriod (item) {
      if (!item) return '-'
      const start = this.formatDate(item.contractStartDate)
      const end = this.formatDate(item.contractEndDate)
      if (!start && !end) return '-'
      return `${start || '-'} ~ ${end || '-'}`
    },
    formatDate (val) {
      if (!val) return ''
      return String(val).replace('T', ' ').substring(0, 10)
    },
    goDetail (id) { uni.navigateTo({ url: `/pages/customer/bill/detail?id=${id}` }) }
  }
@@ -259,5 +489,3 @@
}
</script>
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
    }
h5/pages/customer/contract/list.vue
@@ -1,103 +1,103 @@
<template>
  <view class="cu-page">
    <scroll-view scroll-x class="cu-tabs cu-tabs--scroll">
      <view
        v-for="(t, i) in tabs"
        :key="t.value"
        :class="['cu-tab', tabIdx === i ? 'cu-tab--active' : '']"
        @click="tabIdx = i; load()"
      >{{ t.label }}</view>
    </scroll-view>
    <view class="cu-list-header">
      <text class="cu-list-header__count">共 {{ list.length }} 份合同</text>
    </view>
    <view class="cu-list-wrap">
      <view v-for="item in list" :key="item.id" class="cu-list-card cu-list-card--clickable" @click="goDetail(item.id)">
        <view class="cu-list-card__head">
          <view class="cu-list-card__icon cu-list-card__icon--contract">📄</view>
          <view class="cu-list-card__main">
            <view class="cu-list-card__title-row">
              <text class="cu-list-card__title">{{ item.code }}</text>
              <text :class="['cu-status', contractStatusClass(item.status)]">{{ statusText(item.status) }}</text>
            </view>
            <text class="cu-list-card__sub">{{ item.roomInfo || '暂无房间信息' }}</text>
            <view class="cu-period-chip">{{ item.startDate }} ~ {{ item.endDate }}</view>
          </view>
        </view>
        <view class="cu-info-grid">
          <view class="cu-info-cell">
            <text class="cu-info-cell__label">租赁面积</text>
            <text class="cu-info-cell__value cu-info-cell__value--primary">{{ formatArea(item.totalArea) }}</text>
          </view>
          <view class="cu-info-cell">
            <text class="cu-info-cell__label">付款方式</text>
            <text class="cu-info-cell__value">{{ item.payTypeText || '-' }}</text>
          </view>
        </view>
        <view v-if="item.billStatusTip" :class="['cu-bill-tip', billTipClass(item.billStatusType)]">
          <text class="cu-bill-tip__icon">{{ billTipIcon(item.billStatusType) }}</text>
          <text class="cu-bill-tip__text">{{ item.billStatusTip }}</text>
        </view>
        <view class="cu-list-card__foot">
          <text class="cu-time">点击查看完整合同信息</text>
          <text class="cu-list-card__arrow">详情 →</text>
        </view>
      </view>
      <u-empty v-if="!list.length" text="暂无合同" margin-top="80" />
    </view>
  </view>
</template>
<script>
import { customerContractPage } from '@/api'
export default {
  data () {
    return {
      list: [],
      tabIdx: 0,
      tabs: [
        { label: '全部', value: null }, { label: '待执行', value: 0 }, { label: '执行中', value: 1 },
        { label: '已到期', value: 2 }, { label: '退租中', value: 3 }, { label: '已退租', value: 4 }
      ]
    }
  },
  onShow () { this.load() },
  methods: {
    load () {
      customerContractPage({ page: 1, capacity: 50, model: { status: this.tabs[this.tabIdx].value } })
        .then(res => { this.list = (res.data && res.data.records) || [] })
    },
    formatArea (area) {
      if (area === null || area === undefined || area === '') return '-'
      return `${area}㎡`
    },
    statusText (s) {
      const map = { 0: '待执行', 1: '执行中', 2: '已到期', 3: '退租中', 4: '已退租' }
      return map[s] || '-'
    },
    contractStatusClass (s) {
      if (s === 1) return 'cu-status--ok'
      if (s === 2 || s === 4) return 'cu-status--muted'
      if (s === 3) return 'cu-status--warn'
      return 'cu-status--muted'
    },
    billTipClass (type) {
      if (type === 'danger') return 'cu-bill-tip--danger'
      if (type === 'warn') return 'cu-bill-tip--warn'
      return 'cu-bill-tip--ok'
    },
    billTipIcon (type) {
      if (type === 'danger') return '⚠️'
      if (type === 'warn') return '⏰'
      return '✅'
    },
    goDetail (id) { uni.navigateTo({ url: `/pages/customer/contract/detail?id=${id}` }) }
  }
}
</script>
<template>
  <view class="cu-page">
    <scroll-view scroll-x class="cu-tabs cu-tabs--scroll">
      <view
        v-for="(t, i) in tabs"
        :key="t.value"
        :class="['cu-tab', tabIdx === i ? 'cu-tab--active' : '']"
        @click="tabIdx = i; load()"
      >{{ t.label }}</view>
    </scroll-view>
    <view class="cu-list-header">
      <text class="cu-list-header__count">共 {{ list.length }} 份合同</text>
    </view>
    <view class="cu-list-wrap">
      <view v-for="item in list" :key="item.id" class="cu-list-card cu-list-card--clickable" @click="goDetail(item.id)">
        <view class="cu-list-card__head">
          <view class="cu-list-card__icon cu-list-card__icon--contract">
            <u-icon name="file-text-fill" color="#40a9ff" size="22" />
          </view>
          <view class="cu-list-card__main">
            <view class="cu-list-card__title-row">
              <text class="cu-list-card__title">{{ item.code }}</text>
              <text :class="['cu-status', contractStatusClass(item.status)]">{{ statusText(item.status) }}</text>
            </view>
            <text class="cu-list-card__sub">{{ item.roomInfo || '暂无房间信息' }}</text>
            <view class="cu-period-chip">{{ item.startDate }} ~ {{ item.endDate }}</view>
          </view>
        </view>
        <view class="cu-info-grid">
          <view class="cu-info-cell">
            <text class="cu-info-cell__label">租赁面积</text>
            <text class="cu-info-cell__value cu-info-cell__value--primary">{{ formatArea(item.totalArea) }}</text>
          </view>
          <view class="cu-info-cell">
            <text class="cu-info-cell__label">付款方式</text>
            <text class="cu-info-cell__value">{{ item.payTypeText || '-' }}</text>
          </view>
        </view>
        <view v-if="item.billStatusTip" :class="['cu-bill-tip', billTipClass(item.billStatusType)]">
          <text class="cu-bill-tip__text">{{ item.billStatusTip }}</text>
        </view>
        <view class="cu-list-card__foot">
          <text class="cu-time">点击查看完整合同信息</text>
          <text class="cu-list-card__arrow">详情 →</text>
        </view>
      </view>
      <u-empty v-if="!list.length" text="暂无合同" margin-top="80" />
    </view>
  </view>
</template>
<script>
import { customerContractPage } from '@/api'
export default {
  data () {
    return {
      list: [],
      tabIdx: 0,
      tabs: [
        { label: '全部', value: null }, { label: '待执行', value: 0 }, { label: '执行中', value: 1 },
        { label: '已到期', value: 2 }, { label: '退租中', value: 3 }, { label: '已退租', value: 4 }
      ]
    }
  },
  onShow () { this.load() },
  methods: {
    load () {
      customerContractPage({ page: 1, capacity: 50, model: { status: this.tabs[this.tabIdx].value } })
        .then(res => { this.list = (res.data && res.data.records) || [] })
    },
    formatArea (area) {
      if (area === null || area === undefined || area === '') return '-'
      return `${area}㎡`
    },
    statusText (s) {
      const map = { 0: '待执行', 1: '执行中', 2: '已到期', 3: '退租中', 4: '已退租' }
      return map[s] || '-'
    },
    contractStatusClass (s) {
      if (s === 1) return 'cu-status--ok'
      if (s === 2 || s === 4) return 'cu-status--muted'
      if (s === 3) return 'cu-status--warn'
      return 'cu-status--muted'
    },
    billTipClass (type) {
      if (type === 'danger') return 'cu-bill-tip--danger'
      if (type === 'warn') return 'cu-bill-tip--warn'
      return 'cu-bill-tip--ok'
    },
    goDetail (id) { uni.navigateTo({ url: `/pages/customer/contract/detail?id=${id}` }) }
  }
}
</script>
h5/pages/customer/index.vue
@@ -23,7 +23,9 @@
          <text class="cu-service-item__desc">电表 / 空调充值</text>
        </view>
        <view class="cu-service-item cu-service-item--contract" @click="go('/pages/customer/contract/list')">
          <view class="cu-service-item__icon">📄</view>
          <view class="cu-service-item__icon">
            <u-icon name="file-text-fill" color="#40a9ff" size="24" />
          </view>
          <text class="cu-service-item__label">查合同</text>
          <text class="cu-service-item__desc">租赁合同查询</text>
        </view>
h5/styles/customer.scss
@@ -130,7 +130,10 @@
}
.cu-service-item--electric .cu-service-item__icon { background: #fff7e6; }
.cu-service-item--contract .cu-service-item__icon { background: #e8f7ef; }
.cu-service-item--contract .cu-service-item__icon {
  background: linear-gradient(135deg, #e6f4ff 0%, #bae7ff 100%);
  box-shadow: 0 6rpx 16rpx rgba(105, 192, 255, 0.28);
}
.cu-service-item--bill .cu-service-item__icon { background: #eef2ff; }
.cu-service-item--record .cu-service-item__icon { background: #fce8f3; }
@@ -549,7 +552,10 @@
.cu-list-card__icon--electric { background: linear-gradient(135deg, #fff7e6, #ffe8b3); }
.cu-list-card__icon--conditioner { background: linear-gradient(135deg, #e8f7ef, #c8f0dc); }
.cu-list-card__icon--contract { background: linear-gradient(135deg, #eef2ff, #d6e4ff); }
.cu-list-card__icon--contract {
  background: linear-gradient(135deg, #e6f4ff 0%, #bae7ff 100%);
  box-shadow: 0 6rpx 14rpx rgba(105, 192, 255, 0.28);
}
.cu-list-card__icon--bill { background: linear-gradient(135deg, #fce8f3, #f5d0e8); }
.cu-list-card__icon--record { background: linear-gradient(135deg, #e8f4ff, #cce5ff); }
@@ -782,6 +788,44 @@
  color: $cu-text;
}
.cu-panel__head {
  display: flex;
  align-items: center;
  gap: 16rpx;
  padding: 24rpx 28rpx 16rpx;
}
.cu-panel__title-text {
  font-size: 28rpx;
  font-weight: 600;
  color: $cu-text;
}
.cu-panel__icon {
  width: 56rpx;
  height: 56rpx;
  border-radius: 16rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.cu-panel__icon--room {
  background: linear-gradient(135deg, #ff9c6e 0%, #ff4d4f 100%);
  box-shadow: 0 8rpx 20rpx rgba(255, 77, 79, 0.38);
}
.cu-panel__icon--info {
  background: linear-gradient(135deg, #69c0ff 0%, #2080f7 100%);
  box-shadow: 0 8rpx 20rpx rgba(32, 128, 247, 0.38);
}
.cu-panel__icon--file {
  background: linear-gradient(135deg, #b37feb 0%, #722ed1 100%);
  box-shadow: 0 8rpx 20rpx rgba(114, 46, 209, 0.38);
}
.cu-kv__item {
  display: flex;
  align-items: flex-start;
@@ -852,6 +896,31 @@
  color: #cf1322;
}
.cu-bill-tip__icon-box {
  width: 44rpx;
  height: 44rpx;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.cu-bill-tip--ok .cu-bill-tip__icon-box {
  background: linear-gradient(135deg, #95de64 0%, #52c41a 100%);
  box-shadow: 0 4rpx 12rpx rgba(82, 196, 26, 0.35);
}
.cu-bill-tip--warn .cu-bill-tip__icon-box {
  background: linear-gradient(135deg, #ffd666 0%, #fa8c16 100%);
  box-shadow: 0 4rpx 12rpx rgba(250, 140, 16, 0.35);
}
.cu-bill-tip--danger .cu-bill-tip__icon-box {
  background: linear-gradient(135deg, #ff7875 0%, #f5222d 100%);
  box-shadow: 0 4rpx 12rpx rgba(245, 34, 45, 0.35);
}
.cu-bill-tip__icon {
  flex-shrink: 0;
  font-size: 28rpx;
@@ -895,6 +964,19 @@
  gap: 16rpx;
  padding: 22rpx 28rpx;
  border-top: 1rpx solid #f5f6f8;
  cursor: pointer;
}
.cu-file-item__icon-box {
  width: 64rpx;
  height: 64rpx;
  border-radius: 18rpx;
  background: linear-gradient(135deg, #5cdbd3 0%, #13c2c2 100%);
  box-shadow: 0 6rpx 16rpx rgba(19, 194, 194, 0.38);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.cu-file-item__icon {
@@ -1103,15 +1185,114 @@
  padding: 16rpx 24rpx 8rpx;
}
.cu-bill-page__toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16rpx;
  margin-bottom: 16rpx;
}
.cu-bill-tabs--toolbar {
  flex: 1;
  min-width: 0;
  padding: 0;
  margin-bottom: 0;
  gap: 12rpx;
  background: transparent;
}
.cu-bill-tabs--toolbar .cu-tab {
  padding: 12rpx 20rpx;
  font-size: 24rpx;
}
.cu-bill-cost-picker {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 6rpx;
  padding: 12rpx 18rpx;
  background: #fff;
  border-radius: 999rpx;
  box-shadow: $cu-shadow;
  border: 2rpx solid transparent;
  max-width: 220rpx;
}
.cu-bill-cost-picker--active {
  border-color: $cu-primary;
  background: linear-gradient(135deg, #e8f2ff 0%, #fff 100%);
}
.cu-bill-cost-picker--rent {
  border-color: #40a9ff;
  background: linear-gradient(135deg, #e6f4ff 0%, #fff 100%);
}
.cu-bill-cost-picker--property {
  border-color: #52c41a;
  background: linear-gradient(135deg, #f0faf4 0%, #fff 100%);
}
.cu-bill-cost-picker--deposit {
  border-color: #fa8c16;
  background: linear-gradient(135deg, #fff7e6 0%, #fff 100%);
}
.cu-bill-cost-picker--utility {
  border-color: #13c2c2;
  background: linear-gradient(135deg, #e6fffb 0%, #fff 100%);
}
.cu-bill-cost-picker--misc {
  border-color: #9254de;
  background: linear-gradient(135deg, #f9f0ff 0%, #fff 100%);
}
.cu-bill-cost-picker__label {
  font-size: 24rpx;
  color: $cu-text-secondary;
  max-width: 150rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1.2;
}
.cu-bill-cost-picker--active .cu-bill-cost-picker__label,
.cu-bill-cost-picker--rent .cu-bill-cost-picker__label,
.cu-bill-cost-picker--property .cu-bill-cost-picker__label,
.cu-bill-cost-picker--deposit .cu-bill-cost-picker__label,
.cu-bill-cost-picker--utility .cu-bill-cost-picker__label,
.cu-bill-cost-picker--misc .cu-bill-cost-picker__label {
  color: $cu-text;
  font-weight: 600;
}
.cu-bill-cost-picker__arrow {
  font-size: 22rpx;
  color: $cu-text-muted;
  line-height: 1;
  transform: translateY(2rpx);
}
.cu-bill-tabs {
  margin-bottom: 20rpx;
  margin-bottom: 16rpx;
}
.cu-bill-summary {
  display: flex;
  align-items: center;
  gap: 12rpx;
  padding: 0 8rpx 12rpx;
  flex-wrap: wrap;
}
.cu-bill-summary__main {
  display: flex;
  align-items: baseline;
  gap: 8rpx;
  padding: 0 8rpx 12rpx;
}
.cu-bill-summary__count {
@@ -1124,6 +1305,76 @@
.cu-bill-summary__label {
  font-size: 26rpx;
  color: $cu-text-muted;
  line-height: 1.2;
}
.cu-bill-summary__overdue-group {
  display: flex;
  align-items: center;
  gap: 10rpx;
}
.cu-bill-summary__chip {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8rpx;
  height: 48rpx;
  padding: 0 16rpx;
  border-radius: 999rpx;
  box-sizing: border-box;
  font-size: 24rpx;
  line-height: 1;
}
.cu-bill-summary__chip--stat {
  color: $cu-danger;
  font-weight: 600;
  background: #fff1f0;
}
.cu-bill-summary__chip--filter {
  background: transparent;
  border: none;
  color: $cu-danger;
  font-weight: 500;
  padding: 0 4rpx;
}
.cu-bill-summary__chip--filter-on {
  font-weight: 600;
}
.cu-bill-summary__chip-text {
  font-size: 24rpx;
  line-height: 1;
  color: inherit;
}
.cu-bill-summary__check {
  width: 28rpx;
  height: 28rpx;
  border: 2rpx solid rgba(255, 77, 79, 0.45);
  border-radius: 6rpx;
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  flex-shrink: 0;
  transition: all 0.2s ease;
}
.cu-bill-summary__check--on {
  border-color: $cu-danger;
  background: $cu-danger;
}
.cu-bill-summary__check-icon {
  font-size: 20rpx;
  color: #fff;
  line-height: 1;
  font-weight: 700;
}
.cu-bill-card {
@@ -1168,27 +1419,80 @@
  margin-bottom: 24rpx;
}
.cu-bill-card__head-main {
  flex: 1;
  min-width: 0;
.cu-bill-card__type-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16rpx;
  margin-bottom: 12rpx;
}
.cu-bill-card__type {
  display: inline-flex;
  padding: 6rpx 14rpx;
  background: #f3f6fc;
  border-radius: 8rpx;
  font-size: 22rpx;
  color: $cu-primary;
  font-weight: 500;
  margin-bottom: 10rpx;
.cu-bill-type-tag {
  padding: 10rpx 22rpx;
  border-radius: 12rpx;
  box-shadow: 0 4rpx 12rpx rgba(15, 35, 75, 0.08);
}
.cu-bill-type-tag__text {
  font-size: 28rpx;
  font-weight: 700;
  line-height: 1.2;
}
.cu-bill-type-tag--rent {
  background: linear-gradient(135deg, #69c0ff 0%, #40a9ff 100%);
}
.cu-bill-type-tag--rent .cu-bill-type-tag__text {
  color: #fff;
}
.cu-bill-type-tag--property {
  background: linear-gradient(135deg, #95de64 0%, #52c41a 100%);
}
.cu-bill-type-tag--property .cu-bill-type-tag__text {
  color: #fff;
}
.cu-bill-type-tag--deposit {
  background: linear-gradient(135deg, #ffc069 0%, #fa8c16 100%);
}
.cu-bill-type-tag--deposit .cu-bill-type-tag__text {
  color: #fff;
}
.cu-bill-type-tag--utility {
  background: linear-gradient(135deg, #5cdbd3 0%, #13c2c2 100%);
}
.cu-bill-type-tag--utility .cu-bill-type-tag__text {
  color: #fff;
}
.cu-bill-type-tag--misc {
  background: linear-gradient(135deg, #b37feb 0%, #9254de 100%);
}
.cu-bill-type-tag--misc .cu-bill-type-tag__text {
  color: #fff;
}
.cu-bill-type-tag--default {
  background: linear-gradient(135deg, #d9d9d9 0%, #8c8c8c 100%);
}
.cu-bill-type-tag--default .cu-bill-type-tag__text {
  color: #fff;
}
.cu-bill-card__code {
  display: block;
  font-size: 30rpx;
  font-weight: 600;
  color: $cu-text;
  font-size: 26rpx;
  font-weight: 500;
  color: $cu-text-secondary;
  margin-bottom: 20rpx;
  word-break: break-all;
  line-height: 1.4;
}
@@ -1329,7 +1633,7 @@
}
.cu-contract-bill-panel {
  padding: 0 24rpx 32rpx;
  padding: 24rpx 24rpx 32rpx;
}
.cu-contract-bill-switch {
@@ -1693,6 +1997,165 @@
  font-weight: 600;
}
.cu-clause-panel {
  padding-bottom: 8rpx;
}
.cu-clause-panel__head {
  display: flex;
  align-items: center;
  gap: 16rpx;
  padding: 24rpx 28rpx 12rpx;
}
.cu-clause-panel__badge {
  width: 56rpx;
  height: 56rpx;
  border-radius: 16rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.cu-clause-panel--zl .cu-clause-panel__badge {
  background: linear-gradient(135deg, #69c0ff 0%, #1890ff 100%);
  box-shadow: 0 8rpx 22rpx rgba(24, 144, 255, 0.42);
}
.cu-clause-panel--wy .cu-clause-panel__badge {
  background: linear-gradient(135deg, #95de64 0%, #389e0d 100%);
  box-shadow: 0 8rpx 22rpx rgba(56, 158, 13, 0.42);
}
.cu-clause-panel__title {
  font-size: 30rpx;
  font-weight: 600;
  color: $cu-text;
}
.cu-clause-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 12rpx;
  padding: 8rpx 24rpx 20rpx;
}
.cu-clause-chip {
  flex: 1;
  min-width: calc(50% - 6rpx);
  padding: 18rpx 20rpx;
  border-radius: 16rpx;
  background: #f8fafc;
}
.cu-clause-panel--zl .cu-clause-chip {
  background: linear-gradient(180deg, #f3f8ff 0%, #f8fafc 100%);
}
.cu-clause-panel--wy .cu-clause-chip {
  background: linear-gradient(180deg, #f0faf4 0%, #f8fafc 100%);
}
.cu-clause-chip--wide {
  flex: 1 1 100%;
  min-width: 100%;
}
.cu-clause-chip__label {
  display: block;
  font-size: 22rpx;
  color: $cu-text-muted;
  margin-bottom: 8rpx;
}
.cu-clause-chip__value {
  display: block;
  font-size: 28rpx;
  font-weight: 500;
  color: $cu-text;
  line-height: 1.4;
  word-break: break-all;
}
.cu-clause-detail-list {
  padding: 0 24rpx 16rpx;
  display: flex;
  flex-direction: column;
  gap: 16rpx;
}
.cu-clause-detail-card {
  padding: 4rpx;
  border-radius: 18rpx;
  background: linear-gradient(135deg, rgba(32, 128, 247, 0.12), rgba(32, 128, 247, 0.04));
}
.cu-clause-panel--wy .cu-clause-detail-card {
  background: linear-gradient(135deg, rgba(47, 154, 79, 0.12), rgba(47, 154, 79, 0.04));
}
.cu-clause-detail-grid {
  display: flex;
  flex-wrap: wrap;
  padding: 20rpx;
  border-radius: 16rpx;
  background: #fff;
  gap: 16rpx 12rpx;
}
.cu-clause-detail-grid__item {
  width: calc(50% - 6rpx);
  padding: 16rpx 18rpx;
  border-radius: 12rpx;
  background: #f8fafc;
  box-sizing: border-box;
}
.cu-clause-panel--zl .cu-clause-detail-grid__item--highlight {
  background: linear-gradient(135deg, #eef5ff 0%, #f8fbff 100%);
}
.cu-clause-panel--wy .cu-clause-detail-grid__item--highlight {
  background: linear-gradient(135deg, #edf9f0 0%, #f6fcf8 100%);
}
.cu-clause-detail-grid__label {
  display: block;
  font-size: 22rpx;
  color: $cu-text-muted;
  margin-bottom: 8rpx;
}
.cu-clause-detail-grid__value {
  display: block;
  font-size: 26rpx;
  font-weight: 500;
  color: $cu-text;
  line-height: 1.45;
  word-break: break-all;
}
.cu-clause-panel--zl .cu-clause-detail-grid__item--highlight .cu-clause-detail-grid__value {
  color: $cu-primary;
  font-weight: 600;
}
.cu-clause-panel--wy .cu-clause-detail-grid__item--highlight .cu-clause-detail-grid__value {
  color: #2f9a4f;
  font-weight: 600;
}
.cu-clause-empty {
  padding: 32rpx 24rpx;
  margin: 0 24rpx 8rpx;
  text-align: center;
  font-size: 24rpx;
  color: $cu-text-muted;
  background: #f8fafc;
  border-radius: 16rpx;
}
.cu-bill-detail-hero {
  position: relative;
  margin: 24rpx 24rpx 0;
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/h5/CustomerBillQueryDTO.java
@@ -5,4 +5,6 @@
@Data
public class CustomerBillQueryDTO {
    private Integer payTab;
    /** 费用类型:0租赁费 1物业费 2租赁押金 3物业押金 4水电费 5杂项费 6其他 7保证金 */
    private Integer costType;
}
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5BizServiceImpl.java
@@ -13,6 +13,7 @@
import com.doumee.core.utils.Constants;
import com.doumee.core.utils.Utils;
import com.doumee.dao.business.YwContractBillMapper;
import com.doumee.dao.business.YwContractDetailMapper;
import com.doumee.dao.business.YwContractMapper;
import com.doumee.dao.business.YwContractRevenueMapper;
import com.doumee.dao.business.YwContractRoomMapper;
@@ -63,6 +64,8 @@
    private YwElectricalBizService ywElectricalBizService;
    @Autowired
    private YwContractMapper ywContractMapper;
    @Autowired
    private YwContractDetailMapper ywContractDetailMapper;
    @Autowired
    private YwContractBillMapper ywContractBillMapper;
    @Autowired
@@ -211,6 +214,9 @@
            } else if (q.getPayTab() == 1) {
                wrapper.eq(YwContractBill::getPayStatus, Constants.ONE);
            }
        }
        if (q != null && q.getCostType() != null) {
            wrapper.eq(YwContractBill::getCostType, q.getCostType());
        }
        wrapper.orderByDesc(YwContractBill::getPlanPayDate);
        IPage<YwContractBill> result = ywContractBillMapper.selectJoinPage(page, YwContractBill.class, wrapper);
@@ -385,6 +391,7 @@
        applyRoomSummary(contract, roomMap.getOrDefault(contract.getId(), Collections.emptyList()));
        contract.setPayTypeText(resolvePayTypeText(contract));
        contract.setFreeRentPeriod(resolveFreeRentPeriod(contract));
        initContractDetails(contract);
        fillBillStatusTip(contract, loadContractBillMap(Collections.singletonList(contract.getId()))
                .getOrDefault(contract.getId(), Collections.emptyList()));
        if (withFiles) {
@@ -588,6 +595,26 @@
                || Constants.equalsInteger(payStatus, Constants.FOUR);
    }
    private void initContractDetails(YwContract contract) {
        if (contract == null || contract.getId() == null) {
            return;
        }
        contract.setZlDetailList(ywContractDetailMapper.selectJoinList(YwContractDetail.class,
                new MPJLambdaWrapper<YwContractDetail>()
                        .selectAll(YwContractDetail.class)
                        .eq(YwContractDetail::getIsdeleted, Constants.ZERO)
                        .eq(YwContractDetail::getContractId, contract.getId())
                        .in(YwContractDetail::getType, Constants.ZERO, Constants.TWO)
                        .orderByAsc(YwContractDetail::getSortnum)));
        contract.setWyDetailList(ywContractDetailMapper.selectJoinList(YwContractDetail.class,
                new MPJLambdaWrapper<YwContractDetail>()
                        .selectAll(YwContractDetail.class)
                        .eq(YwContractDetail::getIsdeleted, Constants.ZERO)
                        .eq(YwContractDetail::getContractId, contract.getId())
                        .in(YwContractDetail::getType, Constants.ONE, Constants.THREE)
                        .orderByAsc(YwContractDetail::getSortnum)));
    }
    private void initContractFiles(YwContract contract) {
        List<Multifile> multifiles = multifileMapper.selectJoinList(Multifile.class, new MPJLambdaWrapper<Multifile>()
                .selectAll(Multifile.class)
@@ -674,6 +701,7 @@
                        .eq(YwContractBill::getStatus, Constants.ZERO)
                        .eq(YwContractBill::getContractId, contractId)
                        .eq(YwContractBill::getBillType, billType != null ? billType : Constants.ZERO)
                        .orderByDesc(YwContractBill::getPlanPayDate)
                        .orderByDesc(YwContractBill::getId));
        enrichContractBillsForH5(bills);
        return bills;
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/third/EmayService.java
@@ -76,7 +76,7 @@
                return false;
            }
            String customSmsId = "1";
            String extendedCode = "01";
            String extendedCode = "1";
            SmsSingleRequest request = new SmsSingleRequest(mobile, content, customSmsId, extendedCode, "");
            ResultModel<SmsResponse> result = client.sendSingleSms(request);