index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. <template>
  2. <view class="detail-page">
  3. <view class="tabs">
  4. <view class="tab" v-for="(t, i) in tabs" :key="t.label" :class="{ active: category == t.value }"
  5. :style="{ opacity: loading ? 0.5 : 1 }" @click="selectTab(t)">
  6. {{ t.label }}
  7. </view>
  8. </view>
  9. <view class="tip">数据更新时间:{{ formatDate(new Date().setDate(new Date().getDate() - 1), 'YYYY-MM-DD') || '--' }}</view>
  10. <view class="card" :style="{ height: tableBodyHeight + 'rpx' }">
  11. <scroll-view class="grid-scroll" scroll-y="true" scroll-x="true" :show-scrollbar="false"
  12. :style="{ maxHeight: tableBodyHeight + 'rpx' }" enhanced @scrolltolower="onTableScrollToLower">
  13. <view :style="{
  14. width: '1296px',
  15. }">
  16. <view class="grid-header">
  17. <text class="hcell hcell-sticky-left">查询客户</text>
  18. <text class="hcell">社会信用代码</text>
  19. <text class="hcell">加入时间</text>
  20. <text class="hcell">品种</text>
  21. <text class="hcell">上游客户</text>
  22. <text class="hcell">货源片区</text>
  23. <text class="hcell">数量</text>
  24. <text class="hcell">批号</text>
  25. <text class="hcell">监管码样本(抽样)</text>
  26. <text class="hcell">终端到达数量</text>
  27. </view>
  28. <view v-if="!loading && rows.length > 0" class="grid-body">
  29. <view v-for="(row, idx) in rows" :key="idx + '-' + (row.produceBatchNo || '')" class="grow">
  30. <view v-if="row.flags.entName" class="gcell gcol-1 gspan" :class="['sticky-left']"
  31. :style="{ gridRowEnd: 'span ' + row.spans.entName }">{{ row.entName }}</view>
  32. <view v-if="row.flags.entName" class="gcell gcol-2 gspan"
  33. :style="{ gridRowEnd: 'span ' + row.spans.entName }">{{ row.orgCode }}</view>
  34. <view v-if="row.flags.entName" class="gcell gcol-3 gspan"
  35. :style="{ gridRowEnd: 'span ' + row.spans.entName }">{{ formatDate(row.time,
  36. 'YYYY-MM-DD') || '--' }}</view>
  37. <view v-if="row.flags.physicName" class="gcell gcol-4 gspan"
  38. :style="{ gridRowEnd: 'span ' + row.spans.physicName }">{{ row.physicName }}</view>
  39. <view v-if="row.flags.currentENTName" class="gcell gcol-5 gspan"
  40. :style="{ gridRowEnd: 'span ' + row.spans.currentENTName }">{{ row.currentENTName }}
  41. </view>
  42. <view class="gcell gcol-6">{{ row.fromRegionName }}</view>
  43. <view class="gcell gcol-7">{{ row.fromQuantity }}</view>
  44. <view class="gcell gcol-8">{{ row.produceBatchNo }}</view>
  45. <view class="gcell gcol-9">{{ row.tracCode }}</view>
  46. <view class="gcell gcol-10">{{ row.toQuantity }}</view>
  47. </view>
  48. <view class="gcell gcell-full loading-row" v-if="hasMore">
  49. <view class="loading-wrapper">
  50. <image class="loading-icon" src="../../../static/images/loading.png" />
  51. </view>
  52. </view>
  53. </view>
  54. </view>
  55. </scroll-view>
  56. <view v-if="loading" class="loading-wrap">
  57. <view class="loading-row">
  58. <view class="loading-wrapper">
  59. <image class="loading-icon" src="../../../static/images/loading.png" />
  60. </view>
  61. </view>
  62. </view>
  63. <view v-if="showEmptyData" class="empty-data">
  64. <EmptyView text="无相关数据" />
  65. </view>
  66. </view>
  67. <view class="fab-btn" @click="downloadTable">
  68. <text class="fab-text">下载表格</text>
  69. </view>
  70. </view>
  71. </template>
  72. <script>
  73. import EmptyView from "../../../wigets/empty.vue";
  74. import request, { downloadFile } from '../../../request/index.js'
  75. import { formatDate } from '../../../utils/utils.js'
  76. export default {
  77. components: {
  78. EmptyView,
  79. },
  80. data() {
  81. return {
  82. isLoading: false,
  83. tableBodyHeight: 0,
  84. baseRowHeight: 76,
  85. tabs: [
  86. {
  87. label: "药品购进",
  88. value: "1",
  89. },
  90. {
  91. label: "药品销售",
  92. value: "2",
  93. },
  94. ],
  95. category: "1",
  96. loading: false,
  97. rows: [],
  98. totalCount: 0,
  99. fetchLoading: false,
  100. hasMore: false,
  101. pageNum: 1,
  102. pageSize: 15,
  103. };
  104. },
  105. created() {
  106. this.tableBodyHeight = 14 * 80;
  107. // this.fetchList();
  108. },
  109. computed: {
  110. showEmptyData() {
  111. if (this.loading) return false;
  112. if (this.rows.length === 0) {
  113. return true;
  114. }
  115. return false;
  116. },
  117. },
  118. methods: {
  119. formatDate,
  120. selectTab(t) {
  121. if (this.loading) return;
  122. this.category = t.value;
  123. this.resetFetch();
  124. },
  125. computeBlackLeafCount(node) {
  126. if (!node || !node.dataList || !node.dataList.length) return 1;
  127. let sum = 0;
  128. node.dataList.forEach((c) => {
  129. sum += this.computeBlackLeafCount(c);
  130. });
  131. node.leafCount = sum;
  132. return sum;
  133. },
  134. buildBlackRows(data) {
  135. const res = [];
  136. (data || []).forEach((companyNode) => {
  137. const cLeaves = this.computeBlackLeafCount(companyNode);
  138. let companyStarted = false;
  139. (companyNode.dataList || []).forEach((expNode) => {
  140. const eLeaves = this.computeBlackLeafCount(expNode);
  141. let exporterStarted = false;
  142. const groups = {};
  143. (expNode.dataList || []).forEach((item) => {
  144. const key = item.currentENTName || "";
  145. if (!groups[key]) groups[key] = [];
  146. groups[key].push(item);
  147. });
  148. Object.keys(groups).forEach((prodName) => {
  149. const group = groups[prodName];
  150. let productStarted = false;
  151. group.forEach((batch) => {
  152. res.push({
  153. entName: companyNode.entName,
  154. orgCode: companyNode.orgCode,
  155. time: batch.time,
  156. physicName: expNode.physicName,
  157. currentENTName: prodName,
  158. fromRegionName: batch.fromRegionName,
  159. fromQuantity: batch.fromQuantity,
  160. produceBatchNo: batch.produceBatchNo,
  161. tracCode: batch.tracCode,
  162. toQuantity: batch.toQuantity,
  163. spans: {
  164. entName: companyStarted ? 0 : cLeaves,
  165. physicName: exporterStarted ? 0 : eLeaves,
  166. currentENTName: productStarted ? 0 : group.length,
  167. },
  168. flags: {
  169. entName: !companyStarted,
  170. physicName: !exporterStarted,
  171. currentENTName: !productStarted,
  172. },
  173. });
  174. companyStarted = true;
  175. exporterStarted = true;
  176. productStarted = true;
  177. });
  178. });
  179. });
  180. });
  181. return res;
  182. },
  183. onTableScrollToLower(e) {
  184. if (e?.detail?.direction === 'right') return
  185. // if (this.isLoading) return;
  186. // if (this.rows.length >= this.totalCount) return;
  187. // this.isLoading = true;
  188. // const remain = this.totalCount - this.rows.length;
  189. // const toAdd = Math.min(15, remain);
  190. // setTimeout(() => {
  191. // const more = this.rows.slice(0, toAdd);
  192. // this.rows = this.rows.concat(more);
  193. // this.isLoading = false;
  194. // }, 800);
  195. this.fetchList();
  196. },
  197. resetFetch() {
  198. this.loading = true;
  199. this.rows = [];
  200. this.totalCount = 0;
  201. this.fetchLoading = false;
  202. this.hasMore = true;
  203. this.pageNum = 1;
  204. this.fetchList();
  205. },
  206. fetchList() {
  207. if (this.fetchLoading || !this.hasMore) return;
  208. this.fetchLoading = true;
  209. let url = `/report/getBlacklistReport`;
  210. try {
  211. request(url, {
  212. pageNum: this.pageNum,
  213. pageSize: this.pageSize,
  214. path: '/blacklist/index.vue',
  215. }).then(res => {
  216. if (res.code == 200) {
  217. const { total, list } = res.data || {};
  218. this.totalCount = total || 0;
  219. let _list = [];
  220. if (Array.isArray(list)) {
  221. _list = this.buildBlackRows(list);
  222. }
  223. this.rows = [...this.rows, ..._list];
  224. if (this.rows.length < this.totalCount) {
  225. this.hasMore = true;
  226. this.pageNum++;
  227. } else {
  228. this.hasMore = false;
  229. }
  230. }
  231. this.isLoading = false;
  232. this.loading = false;
  233. this.fetchLoading = false;
  234. }).catch(() => {
  235. this.isLoading = false;
  236. this.loading = false;
  237. this.fetchLoading = false;
  238. })
  239. } catch (res) {
  240. this.isLoading = false;
  241. this.loading = false;
  242. this.fetchLoading = false;
  243. }
  244. },
  245. downloadTable() {
  246. downloadFile('', {
  247. id: '',
  248. path: '/blacklist/index.vue',
  249. })
  250. },
  251. },
  252. };
  253. </script>
  254. <style scoped>
  255. .detail-page {
  256. box-sizing: border-box;
  257. padding: 24rpx;
  258. position: relative;
  259. min-height: 100vh;
  260. background: #f3f6f9;
  261. padding-bottom: calc(50rpx + env(safe-area-inset-bottom));
  262. }
  263. .tip {
  264. font-size: 24rpx;
  265. color: #999;
  266. margin-bottom: 30rpx;
  267. }
  268. .tabs {
  269. display: flex;
  270. align-items: center;
  271. background: #fff;
  272. border-radius: 90rpx;
  273. padding: 10rpx;
  274. margin-bottom: 30rpx;
  275. }
  276. .tab {
  277. flex: 1;
  278. height: 70rpx;
  279. display: flex;
  280. align-items: center;
  281. justify-content: center;
  282. padding: 0 24rpx;
  283. border-radius: 70rpx;
  284. font-size: 28rpx;
  285. color: #333;
  286. }
  287. .tab+.tab {
  288. margin-left: 12rpx;
  289. }
  290. .tab.active {
  291. background: #2c69ff;
  292. color: #fff;
  293. }
  294. .card {
  295. position: relative;
  296. margin-top: 12rpx;
  297. font-size: 30rpx;
  298. overflow: hidden;
  299. }
  300. .grid-header {
  301. position: sticky;
  302. top: 0;
  303. z-index: 10;
  304. display: grid;
  305. grid-template-columns: 340rpx 300rpx 220rpx 220rpx 220rpx 220rpx 220rpx 220rpx 340rpx 200rpx;
  306. }
  307. .grid-header .hcell {
  308. background: #eaf2ff;
  309. font-weight: bold;
  310. color: #2c69ff;
  311. }
  312. .hcell {
  313. height: 76rpx;
  314. display: flex;
  315. align-items: center;
  316. justify-content: center;
  317. }
  318. .hcell:last-child {
  319. border-right: none;
  320. }
  321. .hcell-sticky-left {
  322. position: sticky;
  323. left: 0;
  324. z-index: 11;
  325. }
  326. .grid-scroll {
  327. border-radius: 16rpx;
  328. margin-top: 8rpx;
  329. overflow: hidden;
  330. }
  331. .grid-body {
  332. background: #fff;
  333. display: grid;
  334. grid-template-columns: 340rpx 300rpx 220rpx 220rpx 220rpx 220rpx 220rpx 220rpx 340rpx 200rpx;
  335. grid-auto-rows: auto;
  336. }
  337. .grow {
  338. display: contents;
  339. }
  340. .gcell {
  341. display: flex;
  342. align-items: center;
  343. justify-content: center;
  344. border-bottom: 1rpx solid #eef0f4;
  345. border-right: 1rpx solid #eef0f4;
  346. background: #fff;
  347. color: #333;
  348. padding: 0 20rpx;
  349. text-align: center;
  350. line-height: 50rpx;
  351. }
  352. .gcell:last-child {
  353. border-right: none;
  354. }
  355. .gcell.gcell-full {
  356. grid-column: 1 / -1;
  357. background: transparent;
  358. border: none;
  359. }
  360. .gspan {
  361. background: #fff;
  362. }
  363. .gcol-1 {
  364. grid-column: 1;
  365. }
  366. .gcol-2 {
  367. grid-column: 2;
  368. }
  369. .gcol-3 {
  370. grid-column: 3;
  371. }
  372. .gcol-4 {
  373. grid-column: 4;
  374. }
  375. .gcol-5 {
  376. grid-column: 5;
  377. }
  378. .gcol-6 {
  379. grid-column: 6;
  380. }
  381. .gcol-7 {
  382. grid-column: 7;
  383. }
  384. .gcol-8 {
  385. grid-column: 8;
  386. }
  387. .gcol-9 {
  388. grid-column: 9;
  389. }
  390. .gcol-10 {
  391. grid-column: 10;
  392. }
  393. .sticky-left {
  394. position: sticky;
  395. left: 0;
  396. z-index: 9;
  397. background: #fff;
  398. }
  399. .loading-row {
  400. justify-content: flex-start;
  401. }
  402. .loading-wrapper {
  403. position: sticky;
  404. top: 50%;
  405. left: 0;
  406. width: calc(100vw - 48rpx);
  407. height: 76rpx;
  408. display: flex;
  409. align-items: center;
  410. justify-content: center;
  411. }
  412. .loading-icon {
  413. width: 40rpx;
  414. height: 40rpx;
  415. animation: spin 1s linear infinite;
  416. }
  417. @keyframes spin {
  418. from {
  419. transform: rotate(0deg);
  420. }
  421. to {
  422. transform: rotate(360deg);
  423. }
  424. }
  425. .empty-data,
  426. .loading-wrap {
  427. position: absolute;
  428. left: 0;
  429. top: 85rpx;
  430. z-index: 999;
  431. width: 100%;
  432. background-color: #fff;
  433. border-bottom-left-radius: 16rpx;
  434. border-bottom-right-radius: 16rpx;
  435. }
  436. .fab-btn {
  437. position: fixed;
  438. bottom: calc(50rpx + env(safe-area-inset-bottom));
  439. right: 30rpx;
  440. background-color: #007aff;
  441. color: #ffffff;
  442. padding: 20rpx 40rpx;
  443. border-radius: 50rpx;
  444. box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.2);
  445. z-index: 100;
  446. }
  447. .fab-text {
  448. font-size: 28rpx;
  449. font-weight: bold;
  450. }
  451. </style>