index.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <template>
  2. <Water></Water>
  3. <view class="scan-range-page">
  4. <view class="loading-page" v-if="getLoading()">
  5. <image class="loading-icon" src="../../../static/images/loading.png" />
  6. </view>
  7. <scroll-view v-else class="content" scroll-y="true" @scrolltolower="onScrollToLower">
  8. <view class="item" v-for="(item, i) in companyList" :key="i" @click="toggle(i)">
  9. <view class="item-header">
  10. <text class="item-title">{{ i + 1 }}.&nbsp;{{ item.entName || "" }}</text>
  11. <uni-icons type="down" size="22" color="#999" :style="{
  12. transform: expanded[i] ? 'rotate(180deg)' : 'rotate(0deg)',
  13. transition: 'transform 0.3s ease-in-out',
  14. }"></uni-icons>
  15. </view>
  16. <scroll-view class="item-collapse" scroll-y="true" :style="{
  17. height: getCollapseHeight(i),
  18. }" @scrolltolower="getDrugInfoNameList(i)">
  19. <view class="cv-item" v-for="(d, di) in products[i]" :key="'drug-' + di">
  20. <view class="cv-index-wrap">
  21. <text class="cv-index">{{ di + 1 }}</text>
  22. </view>
  23. <view class="cv-body">
  24. <view class="cv-row">
  25. <text class="cv-label">药品名称:</text>
  26. <text class="cv-value">{{ d.physicName || "" }}</text>
  27. </view>
  28. <view class="cv-row">
  29. <text class="cv-label">药品规格:</text>
  30. <text class="cv-value">{{ d.pkgSpec || "" }}</text>
  31. </view>
  32. </view>
  33. </view>
  34. </scroll-view>
  35. </view>
  36. <view class="loading-row" v-if="companyHasMore">
  37. <view class="loading-wrapper">
  38. <image class="loading-icon" src="../../../static/images/loading.png" />
  39. </view>
  40. </view>
  41. </scroll-view>
  42. </view>
  43. </template>
  44. <script>
  45. import request from "../../../request/index.js";
  46. import { getPaginatedList } from "../../../utils/utils.js";
  47. import Water from "@/components/water/water.vue";
  48. export default {
  49. components: {
  50. Water,
  51. },
  52. data() {
  53. return {
  54. companyList: [],
  55. companyPageNum: 1,
  56. companyPageSize: 20,
  57. companyTotalCount: 0,
  58. companyHasMore: true,
  59. companyLoading: false,
  60. fetchedCount: 0,
  61. allCompanies: [],
  62. loading: true,
  63. expanded: {},
  64. products: {},
  65. productPages: [],
  66. };
  67. },
  68. onLoad() {
  69. this.fetchInitial();
  70. },
  71. methods: {
  72. normalizeEntName(s) {
  73. return String(s || "")
  74. .trim()
  75. .replace(/\(/g, "(")
  76. .replace(/\)/g, ")");
  77. },
  78. getDrugInfoNameList(i) {
  79. const res = getPaginatedList({
  80. page: this.productPages[i] || 0,
  81. initData: this.companyList[i]?.drugInfoNameList || [],
  82. data: this.products[i] || [],
  83. });
  84. if (!res || !Array.isArray(res.data)) return;
  85. this.$set(this.productPages, i, res.page || this.productPages[i] || 0);
  86. this.$set(this.products, i, res.data);
  87. },
  88. getCollapseHeight(i) {
  89. if (!this.expanded[i]) return "0";
  90. const n = this.companyList[i]?.drugInfoNameList?.length || 0;
  91. const perItemRpx = 132;
  92. const maxRpx = 560;
  93. return Math.min(maxRpx, n * perItemRpx) + "rpx";
  94. },
  95. getLoading() {
  96. return this.loading;
  97. },
  98. fetchInitial() {
  99. this.fetchCustomers();
  100. },
  101. fetchCustomers() {
  102. if (!this.companyHasMore || this.companyLoading) return;
  103. this.companyLoading = true;
  104. request("/customer/info/getEnterpriseData", {
  105. pageNum: this.companyPageNum,
  106. pageSize: this.companyPageSize,
  107. path: "/traceabilityCodeQuery/pages/scanRange/index.vue",
  108. }).then((res) => {
  109. if (res.code == 200) {
  110. const { total, list } = res.data || {};
  111. this.companyTotalCount = total || 0;
  112. const rawList = Array.isArray(list) ? list : [];
  113. this.fetchedCount = (this.fetchedCount || 0) + rawList.length;
  114. this.companyList = [...this.companyList, ...rawList];
  115. if (
  116. rawList.length > 0 &&
  117. this.fetchedCount < this.companyTotalCount
  118. ) {
  119. this.companyPageNum++;
  120. this.companyHasMore = true;
  121. } else {
  122. this.companyHasMore = false;
  123. }
  124. this.companyList.forEach((item, i) => {
  125. this.getDrugInfoNameList(i);
  126. });
  127. if (
  128. this.companyHasMore &&
  129. (filtered.length === 0 || this.companyList.length < 10)
  130. ) {
  131. this.companyLoading = false;
  132. this.fetchCustomers();
  133. return;
  134. }
  135. }
  136. this.companyLoading = false;
  137. this.loading = false;
  138. });
  139. },
  140. onScrollToLower() {
  141. this.fetchCustomers();
  142. },
  143. toggle(i) {
  144. this.$set(this.expanded, i, !this.expanded[i]);
  145. },
  146. },
  147. };
  148. </script>
  149. <style>
  150. .scan-range-page {
  151. box-sizing: border-box;
  152. padding: 24rpx;
  153. height: 100vh;
  154. background: #f3f6f9;
  155. }
  156. .content {
  157. max-height: calc(100vh - env(safe-area-inset-bottom));
  158. background: #fff;
  159. border-radius: 16rpx;
  160. }
  161. .item {
  162. padding: 10rpx 20rpx;
  163. border-bottom: 1rpx solid #eef0f4;
  164. }
  165. .item-header {
  166. display: flex;
  167. align-items: center;
  168. justify-content: space-between;
  169. line-height: 60rpx;
  170. }
  171. .item-title {
  172. font-size: 30rpx;
  173. color: #333;
  174. }
  175. .item-collapse {
  176. margin-top: 12rpx;
  177. height: 0;
  178. transition: height 0.3s ease-in-out;
  179. }
  180. .cv-item {
  181. box-sizing: border-box;
  182. padding: 20rpx 12rpx 20rpx 8rpx;
  183. display: flex;
  184. align-items: flex-start;
  185. color: #666;
  186. border-bottom: 1rpx solid #f0f2f5;
  187. }
  188. .cv-item:last-child {
  189. border-bottom: none;
  190. }
  191. .cv-index-wrap {
  192. flex-shrink: 0;
  193. padding-top: 6rpx;
  194. margin-right: 16rpx;
  195. }
  196. .cv-index {
  197. display: flex;
  198. align-items: center;
  199. justify-content: center;
  200. min-width: 25rpx;
  201. height: 36rpx;
  202. padding: 0 6rpx;
  203. line-height: 1;
  204. font-size: 20rpx;
  205. text-align: center;
  206. border-radius: 50%;
  207. border: 1rpx solid #999;
  208. }
  209. .cv-body {
  210. flex: 1;
  211. min-width: 0;
  212. }
  213. .cv-row {
  214. display: flex;
  215. align-items: flex-start;
  216. font-size: 28rpx;
  217. line-height: 1.5;
  218. }
  219. .cv-row+.cv-row {
  220. margin-top: 10rpx;
  221. }
  222. .cv-label {
  223. flex-shrink: 0;
  224. width: 150rpx;
  225. color: #727a86;
  226. }
  227. .cv-value {
  228. flex: 1;
  229. min-width: 0;
  230. color: #333;
  231. word-break: break-all;
  232. white-space: normal;
  233. }
  234. .loading-page {
  235. width: 100%;
  236. height: 200rpx;
  237. display: flex;
  238. align-items: center;
  239. justify-content: center;
  240. }
  241. .loading-row {
  242. display: flex;
  243. align-items: center;
  244. justify-content: center;
  245. padding: 20rpx;
  246. }
  247. .loading-wrapper {
  248. width: 100%;
  249. display: flex;
  250. align-items: center;
  251. justify-content: center;
  252. }
  253. .loading-icon {
  254. width: 50rpx;
  255. height: 50rpx;
  256. animation: spin 1s linear infinite;
  257. }
  258. @keyframes spin {
  259. from {
  260. transform: rotate(0deg);
  261. }
  262. to {
  263. transform: rotate(360deg);
  264. }
  265. }
  266. </style>