balanceChange.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. <template>
  2. <!--
  3. H5 余额变动明细页
  4. 用途:扫码付款场景查看与供货商的余额流水;与小程序共用 /ghs-balance-change/list
  5. 调用方:admin/ghs/pay 的「查看余额明细」
  6. -->
  7. <view class="bc-page">
  8. <view class="nav-bar">
  9. <view class="nav-back" @click="goBack">
  10. <text class="back-arrow">&lt;</text>
  11. </view>
  12. <view class="nav-title">余额变动</view>
  13. <view class="nav-placeholder"></view>
  14. </view>
  15. <view class="nav-bar-placeholder"></view>
  16. <view v-if="loading && page === 1" class="bc-status">加载中...</view>
  17. <view v-else-if="loadError" class="bc-status bc-status--error">{{ loadError }}</view>
  18. <template v-else>
  19. <view v-if="list.length > 0" class="bc-sheet">
  20. <view class="bc-head">
  21. <text class="bc-th bc-th--time">时间</text>
  22. <text class="bc-th bc-th--event">事项</text>
  23. <text class="bc-th bc-th--amt">变动</text>
  24. <text class="bc-th bc-th--bal">余额</text>
  25. </view>
  26. <view class="bc-body">
  27. <view
  28. v-for="(item, index) in list"
  29. :key="index"
  30. class="bc-item"
  31. :class="{ 'bc-item--alt': index % 2 === 1 }"
  32. >
  33. <view
  34. class="bc-row-main"
  35. :class="{ 'bc-row-main--with-extra': item.showExtraRow }"
  36. >
  37. <view class="bc-td bc-td--time">
  38. <text class="bc-date">{{ item.dateText }}</text>
  39. <text class="bc-clock">{{ item.timeLine }}</text>
  40. </view>
  41. <view class="bc-td bc-td--event">
  42. <text class="bc-event">{{ item.event }}</text>
  43. <text v-if="item.showStaff" class="bc-meta">{{ item.staffName }}</text>
  44. <text v-if="item.showRemark" class="bc-meta bc-meta--remark">{{ item.remark }}</text>
  45. </view>
  46. <view class="bc-td bc-td--amt">
  47. <text class="bc-amount" :class="item.amountClassName">{{ item.amount }}</text>
  48. </view>
  49. <view class="bc-td bc-td--bal">
  50. <text class="bc-balance">{{ item.balanceText }}</text>
  51. </view>
  52. </view>
  53. <view v-if="item.showExtraRow" class="bc-row-extra">
  54. <view class="bc-tags-row">
  55. <text
  56. v-if="item.showRefundTag"
  57. class="bc-tag bc-tag--refund"
  58. >{{ item.refundTagText }}</text>
  59. <text
  60. v-if="item.showClearLink"
  61. class="bc-tag bc-tag--link"
  62. >{{ item.clearLinkText }}</text>
  63. </view>
  64. </view>
  65. </view>
  66. </view>
  67. <view v-if="loadingMore" class="bc-status bc-status--more">加载更多...</view>
  68. <view v-else-if="finished" class="bc-status bc-status--more">没有更多了</view>
  69. </view>
  70. <view v-else class="bc-empty">
  71. <text class="bc-empty__text">暂无余额变动记录</text>
  72. </view>
  73. </template>
  74. </view>
  75. </template>
  76. <script>
  77. import { balanceChangeList } from '@/api/ghs'
  78. export default {
  79. name: 'ghsBalanceChangeH5',
  80. data() {
  81. return {
  82. ghsId: 0,
  83. salt: '',
  84. list: [],
  85. page: 1,
  86. pageSize: 20,
  87. loading: false,
  88. loadingMore: false,
  89. finished: false,
  90. loadError: ''
  91. }
  92. },
  93. onLoad(options) {
  94. this.ghsId = options.ghsId || options.id || 0
  95. this.salt = options.salt || ''
  96. if (!this.ghsId) {
  97. this.loadError = '参数错误,缺少供货商信息'
  98. return
  99. }
  100. this.fetchList(true)
  101. },
  102. onReachBottom() {
  103. if (!this.finished && !this.loading && !this.loadingMore) {
  104. this.fetchList(false)
  105. }
  106. },
  107. onPullDownRefresh() {
  108. this.fetchList(true).finally(() => {
  109. uni.stopPullDownRefresh()
  110. })
  111. },
  112. methods: {
  113. goBack() {
  114. uni.navigateBack({ delta: 1 })
  115. },
  116. formatMoney(val) {
  117. const n = parseFloat(val)
  118. return isNaN(n) ? '0' : String(n)
  119. },
  120. hasText(val) {
  121. return val !== undefined && val !== null && String(val).trim() !== ''
  122. },
  123. /** 结账类支出行不展示(与小程序一致) */
  124. shouldHideRow(item) {
  125. return item.io == 0 && item.event && String(item.event).indexOf('结账') === 0
  126. },
  127. /** 映射列表行展示字段 */
  128. mapListItem(item, amountDisplay) {
  129. const row = Object.assign({}, item)
  130. row.amount = amountDisplay
  131. const text = String(amountDisplay || '')
  132. row.amountClassName = (text.indexOf('+') === 0 || Number(row.io) === 1) ? 'bc-amount--in' : 'bc-amount--out'
  133. const addTime = row.addTime || ''
  134. if (addTime.length >= 16) {
  135. row.dateText = addTime.substr(5, 5)
  136. row.timeLine = addTime.substr(11, 5)
  137. } else if (addTime.length >= 11) {
  138. row.dateText = addTime.substr(0, 10)
  139. row.timeLine = addTime.substr(11)
  140. } else {
  141. row.dateText = addTime
  142. row.timeLine = ''
  143. }
  144. row.balanceText = this.formatMoney(row.balance)
  145. const clearSn = row.clearSn || ''
  146. const clearAmt = this.formatMoney(row.clearAmount)
  147. row.showClearLink = Number(row.clearId) > 0 || this.hasText(clearSn)
  148. row.clearLinkText = '结账单 ' + clearSn + ' · 本单销' + clearAmt + '元'
  149. row.showStaff = this.hasText(row.staffName)
  150. row.showRemark = this.hasText(row.remark)
  151. const eventText = String(row.event || '')
  152. row.showRefundTag = Number(row.capitalType) === 82 || eventText.indexOf('充值退款') >= 0
  153. row.refundTagText = row.showStaff
  154. ? '充值退款 · 操作人 ' + row.staffName
  155. : '充值退款'
  156. row.showExtraRow = row.showClearLink || row.showRefundTag
  157. return row
  158. },
  159. /**
  160. * 拉取余额变动列表
  161. * @param {boolean} reset 是否从第一页刷新
  162. */
  163. async fetchList(reset) {
  164. if (reset) {
  165. this.page = 1
  166. this.finished = false
  167. this.loadError = ''
  168. this.loading = true
  169. } else {
  170. this.loadingMore = true
  171. }
  172. try {
  173. const res = await balanceChangeList({
  174. page: this.page,
  175. pageSize: this.pageSize,
  176. ghsId: this.ghsId,
  177. salt: this.salt
  178. })
  179. if (!res || res.code !== 1) {
  180. if (reset) {
  181. this.loadError = (res && res.msg) || '加载失败'
  182. this.list = []
  183. }
  184. this.finished = true
  185. return
  186. }
  187. const rawList = (res.data && res.data.list) ? res.data.list : []
  188. const rows = []
  189. rawList.forEach((item) => {
  190. if (this.shouldHideRow(item)) {
  191. return
  192. }
  193. const amountDisplay = item.io == 0
  194. ? '-' + parseFloat(item.amount)
  195. : '+' + parseFloat(item.amount)
  196. rows.push(this.mapListItem(item, amountDisplay))
  197. })
  198. this.list = reset ? rows : this.list.concat(rows)
  199. const moreData = res.data && res.data.moreData
  200. if (!moreData) {
  201. this.finished = true
  202. } else {
  203. this.page += 1
  204. }
  205. } catch (e) {
  206. if (reset) {
  207. this.loadError = '网络异常,请稍后重试'
  208. this.list = []
  209. }
  210. this.finished = true
  211. } finally {
  212. this.loading = false
  213. this.loadingMore = false
  214. }
  215. }
  216. }
  217. }
  218. </script>
  219. <style lang="scss" scoped>
  220. .bc-page {
  221. min-height: 100vh;
  222. background: #fff;
  223. box-sizing: border-box;
  224. }
  225. .nav-bar {
  226. position: fixed;
  227. top: 0;
  228. left: 0;
  229. right: 0;
  230. z-index: 100;
  231. display: flex;
  232. align-items: center;
  233. justify-content: space-between;
  234. padding: 20upx 24upx;
  235. padding-top: calc(20upx + env(safe-area-inset-top));
  236. background: #fff;
  237. border-bottom: 1upx solid #f0f0f0;
  238. box-sizing: border-box;
  239. }
  240. .nav-back,
  241. .nav-placeholder {
  242. width: 72upx;
  243. height: 72upx;
  244. display: flex;
  245. align-items: center;
  246. justify-content: center;
  247. }
  248. .back-arrow {
  249. font-size: 40upx;
  250. color: #333;
  251. font-weight: bold;
  252. line-height: 1;
  253. }
  254. .nav-title {
  255. flex: 1;
  256. text-align: center;
  257. font-size: 34upx;
  258. font-weight: 600;
  259. color: #222;
  260. }
  261. .nav-bar-placeholder {
  262. height: calc(112upx + env(safe-area-inset-top));
  263. }
  264. .bc-status {
  265. padding: 80upx 30upx;
  266. text-align: center;
  267. font-size: 28upx;
  268. color: #999;
  269. }
  270. .bc-status--error {
  271. color: #e64340;
  272. }
  273. .bc-status--more {
  274. padding: 30upx;
  275. font-size: 24upx;
  276. }
  277. .bc-empty {
  278. padding: 160upx 30upx;
  279. text-align: center;
  280. }
  281. .bc-empty__text {
  282. font-size: 28upx;
  283. color: #999;
  284. }
  285. .bc-sheet {
  286. width: 100%;
  287. background: #fff;
  288. }
  289. .bc-head {
  290. display: flex;
  291. align-items: center;
  292. padding: 22upx 0;
  293. background: #f8f9fb;
  294. border-bottom: 1upx solid #eceef2;
  295. }
  296. .bc-th {
  297. font-size: 28upx;
  298. color: #8c8c8c;
  299. font-weight: 500;
  300. line-height: 1.2;
  301. }
  302. .bc-th--time {
  303. width: 100upx;
  304. flex-shrink: 0;
  305. padding-left: 8upx;
  306. box-sizing: border-box;
  307. }
  308. .bc-th--event {
  309. flex: 1;
  310. min-width: 0;
  311. padding-right: 12upx;
  312. }
  313. .bc-th--amt {
  314. width: 118upx;
  315. flex-shrink: 0;
  316. text-align: right;
  317. }
  318. .bc-th--bal {
  319. width: 140upx;
  320. flex-shrink: 0;
  321. padding-right: 8upx;
  322. text-align: right;
  323. box-sizing: border-box;
  324. }
  325. .bc-item {
  326. border-bottom: 1upx solid #f2f3f5;
  327. box-sizing: border-box;
  328. }
  329. .bc-item:last-child {
  330. border-bottom: none;
  331. }
  332. .bc-item--alt {
  333. background: #fafbfc;
  334. }
  335. .bc-row-main {
  336. display: flex;
  337. align-items: flex-start;
  338. padding: 24upx 0;
  339. box-sizing: border-box;
  340. }
  341. .bc-row-main--with-extra {
  342. padding-bottom: 0;
  343. }
  344. .bc-row-extra {
  345. width: 100%;
  346. padding: 12upx 0 20upx;
  347. box-sizing: border-box;
  348. }
  349. .bc-tags-row {
  350. display: flex;
  351. flex-direction: row;
  352. flex-wrap: wrap;
  353. align-items: flex-start;
  354. width: 100%;
  355. padding-left: 8upx;
  356. padding-right: 8upx;
  357. box-sizing: border-box;
  358. }
  359. .bc-td--time {
  360. width: 100upx;
  361. flex-shrink: 0;
  362. padding-left: 8upx;
  363. display: flex;
  364. flex-direction: column;
  365. align-items: flex-start;
  366. justify-content: center;
  367. box-sizing: border-box;
  368. }
  369. .bc-td--event {
  370. flex: 1;
  371. min-width: 0;
  372. padding-right: 12upx;
  373. }
  374. .bc-td--amt {
  375. width: 118upx;
  376. flex-shrink: 0;
  377. padding-right: 8upx;
  378. display: flex;
  379. justify-content: flex-end;
  380. box-sizing: border-box;
  381. }
  382. .bc-td--bal {
  383. width: 140upx;
  384. flex-shrink: 0;
  385. padding-right: 8upx;
  386. display: flex;
  387. justify-content: flex-end;
  388. box-sizing: border-box;
  389. }
  390. .bc-date,
  391. .bc-clock {
  392. font-size: 28upx;
  393. color: #2c2c2c;
  394. line-height: 1.35;
  395. }
  396. .bc-clock {
  397. margin-top: 4upx;
  398. }
  399. .bc-event {
  400. display: block;
  401. font-size: 28upx;
  402. color: #1a1a1a;
  403. font-weight: 500;
  404. line-height: 1.45;
  405. word-break: break-all;
  406. }
  407. .bc-meta {
  408. display: block;
  409. margin-top: 8upx;
  410. font-size: 22upx;
  411. color: #999;
  412. line-height: 1.4;
  413. word-break: break-all;
  414. }
  415. .bc-meta--remark {
  416. color: #b3b3b3;
  417. }
  418. .bc-tag {
  419. display: block;
  420. max-width: 100%;
  421. padding: 6upx 14upx;
  422. margin-top: 10upx;
  423. margin-right: 12upx;
  424. font-size: 28upx;
  425. line-height: 1.4;
  426. border-radius: 8upx;
  427. word-break: break-all;
  428. box-sizing: border-box;
  429. }
  430. .bc-tags-row .bc-tag:first-child {
  431. margin-top: 0;
  432. }
  433. .bc-tag--link {
  434. color: #969aa0;
  435. background: #eef4ff;
  436. }
  437. .bc-tag--refund {
  438. color: #cf1322;
  439. background: #fff1f0;
  440. }
  441. .bc-amount {
  442. font-size: 28upx;
  443. font-weight: 600;
  444. line-height: 1.35;
  445. }
  446. .bc-amount--in {
  447. color: #07a84c;
  448. }
  449. .bc-amount--out {
  450. color: #e62e45;
  451. }
  452. .bc-balance {
  453. font-size: 28upx;
  454. color: #131212;
  455. line-height: 1.35;
  456. }
  457. </style>