list.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <script setup>
  2. import { ref } from 'vue'
  3. import { onShow, onReachBottom } from '@dcloudio/uni-app'
  4. import { listContract } from '@/api/crm'
  5. import { labelOf, dash, formatMoney } from '@/utils/crmLabels'
  6. import { PAGE_PATHS } from '@/config/api'
  7. const tabs = [
  8. { key: 'all', label: '全部' },
  9. { key: 'draft', label: '草稿', status: 'draft' },
  10. { key: 'pending', label: '待审批', status: 'pending' },
  11. { key: 'approved', label: '已生效', status: 'approved' },
  12. { key: 'executing', label: '执行中', status: 'executing' }
  13. ]
  14. const activeTab = ref('all')
  15. const keyword = ref('')
  16. const list = ref([])
  17. const page = ref(1)
  18. const total = ref(0)
  19. const loading = ref(false)
  20. const finished = ref(false)
  21. const pageSize = 20
  22. const goBack = () => {
  23. uni.navigateBack({
  24. fail: () => uni.switchTab({ url: PAGE_PATHS.workbench })
  25. })
  26. }
  27. const goDetail = (row) => {
  28. uni.navigateTo({ url: `${PAGE_PATHS.crmContractDetail}?id=${row.id}` })
  29. }
  30. const statusLabel = (s) => labelOf('contractStatus', s)
  31. const loadList = async (reset = false) => {
  32. if (loading.value) return
  33. if (reset) {
  34. page.value = 1
  35. finished.value = false
  36. list.value = []
  37. }
  38. if (finished.value) return
  39. loading.value = true
  40. try {
  41. const tab = tabs.find((t) => t.key === activeTab.value)
  42. const query = {
  43. pageNum: page.value,
  44. pageSize,
  45. contractName: keyword.value.trim() || undefined,
  46. customerName: keyword.value.trim() || undefined
  47. }
  48. if (tab?.status) query.status = tab.status
  49. const res = await listContract(query)
  50. const rows = res.rows || []
  51. list.value = reset ? rows : list.value.concat(rows)
  52. total.value = res.total || 0
  53. if (list.value.length >= total.value || rows.length < pageSize) finished.value = true
  54. else page.value += 1
  55. } catch (e) {
  56. uni.showToast({ title: e.message || '加载失败', icon: 'none' })
  57. } finally {
  58. loading.value = false
  59. }
  60. }
  61. const switchTab = (key) => {
  62. if (activeTab.value === key) return
  63. activeTab.value = key
  64. loadList(true)
  65. }
  66. onShow(() => loadList(true))
  67. onReachBottom(() => loadList(false))
  68. </script>
  69. <template>
  70. <view class="mp-page mp-page--sub">
  71. <view class="mp-header">
  72. <text class="mp-subpage-back" @click="goBack">← 工作台</text>
  73. <text class="mp-header__title" style="margin-top: 16rpx">合同管理</text>
  74. <view class="mp-header__search">
  75. <view class="mp-header__search-dot" />
  76. <input
  77. v-model="keyword"
  78. class="mp-header__search-input"
  79. placeholder="搜索合同 / 客户"
  80. confirm-type="search"
  81. @confirm="loadList(true)"
  82. />
  83. </view>
  84. </view>
  85. <view class="mp-section" style="padding-bottom: 0">
  86. <scroll-view scroll-x class="mp-tabs">
  87. <view
  88. v-for="t in tabs"
  89. :key="t.key"
  90. class="mp-tab"
  91. :class="{ 'mp-tab--active': activeTab === t.key }"
  92. @click="switchTab(t.key)"
  93. >
  94. {{ t.label }}
  95. </view>
  96. </scroll-view>
  97. </view>
  98. <view class="mp-section">
  99. <mp-empty v-if="!list.length && !loading" title="暂无合同" />
  100. <view v-else class="mp-list">
  101. <view v-for="row in list" :key="row.id" class="mp-card" @click="goDetail(row)">
  102. <view class="mp-card__head">
  103. <view>
  104. <text class="mp-card__title">{{ dash(row.contractName) }}</text>
  105. <text class="mp-card__sub">{{ dash(row.contractNo) }} · {{ dash(row.customerName) }}</text>
  106. </view>
  107. <text class="mp-tag" :class="'mp-tag--' + statusLabel(row.status).tag">
  108. {{ statusLabel(row.status).text }}
  109. </text>
  110. </view>
  111. <view class="mp-card__row">
  112. <text>金额</text>
  113. <text class="mp-card__row-val">{{ formatMoney(row.totalAmount ?? row.amount) }}</text>
  114. </view>
  115. <view class="mp-card__row">
  116. <text>负责人</text>
  117. <text class="mp-card__row-val">{{ dash(row.ownerUserName) }}</text>
  118. </view>
  119. <view class="mp-card__row">
  120. <text>签订日期</text>
  121. <text class="mp-card__row-val">{{ dash(row.signDate) }}</text>
  122. </view>
  123. </view>
  124. </view>
  125. <view v-if="loading" class="mp-list-loading">加载中…</view>
  126. <view v-else-if="finished && list.length" class="mp-list-end">已加载全部</view>
  127. </view>
  128. </view>
  129. </template>