ScanRateTable.vue 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457
  1. <template>
  2. <view
  3. class="section"
  4. :style="{
  5. position: 'relative',
  6. zIndex: dropdownOpen || dropdownRegionOpen || dropdownManufacturerOpen ? 9 : 'auto',
  7. }">
  8. <view v-if="showHeader" class="section-header" :class="!shouldShowAreaSearch ? 'noAreaHeader' : ''">
  9. <view class="section-title-row">
  10. <text class="section-title">{{ title }}</text>
  11. </view>
  12. <view v-if="showSelector" class="selector-row">
  13. <view class="selector-wrap" v-if="shouldShowAreaSearch">
  14. <view class="selector" @click.stop="toggleDropdownRegion">
  15. <text class="selector-text">{{ region?.regionCode ? region.regionName : "选择片区" }}</text>
  16. <text class="selector-arrow" :class="{ open: dropdownRegionOpen }"></text>
  17. </view>
  18. <view class="dropdown" v-show="dropdownRegionOpen" :style="dropdownDirection === 'top' ? { top: 'auto', bottom: '48rpx' } : {}" @click.stop>
  19. <view class="dropdown-search-bar">
  20. <input class="dropdown-search-input" v-model="regionSearchText" @input="onRegionSearch" placeholder="搜索..." />
  21. </view>
  22. <scroll-view scroll-y="true" class="dropdown-scroll-view" lower-threshold="80" @scrolltolower="loadMoreRegions">
  23. <view
  24. class="dropdown-item"
  25. v-for="(p, i) in regionList"
  26. :key="p.regionCode || i"
  27. :style="
  28. p.regionCode === region?.regionCode || (!region?.regionCode && i == 0)
  29. ? {
  30. backgroundColor: '#2c69ff',
  31. color: '#fff',
  32. }
  33. : {}
  34. "
  35. @click.stop="selectProduct('region', p)"
  36. >{{ p.regionName || p }}</view
  37. >
  38. <view v-if="regionList.length === 0" class="dropdown-item" style="text-align: center; color: #999">暂无数据 </view>
  39. </scroll-view>
  40. </view>
  41. </view>
  42. <view class="selector-wrap">
  43. <view class="selector" @click.stop="toggleDropdownManufacturer">
  44. <text class="selector-text">{{ manufacturer?.manufacturerId ? manufacturer.produceEntName : "选择厂家" }}</text>
  45. <text class="selector-arrow" :class="{ open: dropdownManufacturerOpen }"></text>
  46. </view>
  47. <view class="dropdown" v-show="dropdownManufacturerOpen" :style="dropdownDirection === 'top' ? { top: 'auto', bottom: '48rpx' } : {}" @click.stop>
  48. <view class="dropdown-search-bar">
  49. <input class="dropdown-search-input" v-model="manufacturerSearchText" @input="onManufacturerSearch" placeholder="搜索..." />
  50. </view>
  51. <scroll-view scroll-y="true" class="dropdown-scroll-view" lower-threshold="80" @scrolltolower="loadMoreManufacturers">
  52. <view
  53. class="dropdown-item"
  54. v-for="(m, i) in manufacturerList"
  55. :key="m.manufacturerId || i"
  56. :style="
  57. m.manufacturerId === manufacturer?.manufacturerId || (!manufacturer?.manufacturerId && i == 0)
  58. ? {
  59. backgroundColor: '#2c69ff',
  60. color: '#fff',
  61. }
  62. : {}
  63. "
  64. @click.stop="selectProduct('manufacturer', m)"
  65. >{{ m.produceEntName }}</view
  66. >
  67. <view v-if="manufacturerList.length === 0" class="dropdown-item" style="text-align: center; color: #999">暂无数据</view>
  68. </scroll-view>
  69. </view>
  70. </view>
  71. <view class="selector-wrap">
  72. <view class="selector" @click.stop="toggleDropdown">
  73. <text class="selector-text">{{ cascaderDisplayText }}</text>
  74. <text class="selector-arrow" :class="{ open: dropdownOpen }"></text>
  75. </view>
  76. <view
  77. class="dropdown cascader-dropdown"
  78. v-show="dropdownOpen"
  79. :style="
  80. dropdownDirection === 'top'
  81. ? {
  82. top: 'auto',
  83. bottom: '48rpx',
  84. }
  85. : {}
  86. "
  87. @click.stop>
  88. <view class="cascader-search-row">
  89. <view class="dropdown-search-bar cascader-search">
  90. <input class="dropdown-search-input" v-model="productSearchText" @input="onProductSearch" placeholder="搜索品种" />
  91. </view>
  92. <view class="dropdown-search-bar cascader-search">
  93. <input class="dropdown-search-input" v-model="specSearchText" @input="onSpecSearch" placeholder="搜索规格" />
  94. </view>
  95. </view>
  96. <view class="cascader-body">
  97. <scroll-view scroll-y="true" class="dropdown-scroll-view cascader-col" lower-threshold="80" @scrolltolower="loadMoreProducts">
  98. <view
  99. class="dropdown-item"
  100. v-for="(p, i) in productList"
  101. :key="p.drugEntBaseInfoId || i"
  102. :style="
  103. p.drugEntBaseInfoId === activeProductForSpec?.drugEntBaseInfoId
  104. ? {
  105. backgroundColor: '#2c69ff',
  106. color: '#fff',
  107. }
  108. : {}
  109. "
  110. @click.stop="selectCascadeProduct(p)"
  111. >{{ p.physicName }}</view
  112. >
  113. <view v-if="productList.length === 0" class="dropdown-item" style="text-align: center; color: #999">暂无数据 </view>
  114. </scroll-view>
  115. <scroll-view scroll-y="true" class="dropdown-scroll-view cascader-col cascader-col-right" lower-threshold="80" @scrolltolower="loadMoreSpecs">
  116. <view
  117. class="dropdown-item"
  118. v-for="(s, i) in specList"
  119. :key="s.specId || i"
  120. :style="
  121. s.specId === selectedSpec?.specId || (!selectedSpec?.specId && i == 0)
  122. ? {
  123. backgroundColor: '#2c69ff',
  124. color: '#fff',
  125. }
  126. : {}
  127. "
  128. @click.stop="selectCascadeSpec(s)"
  129. >{{ s.pkgSpec }}</view
  130. >
  131. <view v-if="specList.length === 0" class="dropdown-item" style="text-align: center; color: #999">暂无数据 </view>
  132. </scroll-view>
  133. </view>
  134. </view>
  135. </view>
  136. </view>
  137. </view>
  138. <view
  139. class="table-scroll-x"
  140. :style="{
  141. height: tableHeightAuto || list.length < 7 ? 'auto' : _tableBodyHeight + 80 + 'rpx',
  142. maxHeight: tableHeightAuto,
  143. }">
  144. <scroll-view
  145. class="no-scrollbar"
  146. :style="{ height: '100%' }"
  147. scroll-x="true"
  148. scroll-y="true"
  149. enhanced
  150. :show-scrollbar="false"
  151. @scrolltolower="onScrollToLower">
  152. <view class="card" :style="{ width: tableWidth }">
  153. <view
  154. class="table-header"
  155. :style="{
  156. position: 'sticky',
  157. top: '0',
  158. zIndex: 10,
  159. fontWeight: headerBold ? 'bold' : 'normal',
  160. }">
  161. <view
  162. v-for="(col, ci) in columnList"
  163. :key="getUid()"
  164. class="cell"
  165. :style="{
  166. ...headerCellStyle(ci, col),
  167. }">
  168. <view
  169. v-if="isPlainTitle(col.title)"
  170. :style="{
  171. display: 'flex',
  172. alignItems: 'center',
  173. position: 'relative',
  174. }">
  175. <text>{{ col.title }}</text>
  176. <uni-icons v-if="col.tooltip" type="help" size="16" color="#2c69ff" @tap.stop.prevent="toggleHeaderTooltip(ci)"></uni-icons>
  177. <view v-if="headerTooltip[ci]" class="header-tooltip-wrap" @click.stop>
  178. <view class="header-tooltip">{{ col.tooltip }}</view>
  179. <view class="header-tooltip-arrow"></view>
  180. </view>
  181. </view>
  182. <rich-text v-else :nodes="titleNodes(col.title)" />
  183. </view>
  184. </view>
  185. <view v-if="!loading && list && list.length > 0" class="table-body">
  186. <template>
  187. <view class="row" v-for="(item, i) in list" :key="getUid()">
  188. <view
  189. v-for="(col, ci) in columnList"
  190. :key="getUid()"
  191. class="cell"
  192. :class="{
  193. underline: col.underline,
  194. striped: striped && (i + 1) % 2 === 0,
  195. }"
  196. :style="bodyCellStyle(ci, col)"
  197. @click="handleClick(item, col)"
  198. >{{ renderCell(item, col) }}</view
  199. >
  200. </view>
  201. <view class="row loading-row" v-if="hasmore">
  202. <view class="loading-wrapper">
  203. <image class="loading-icon" :src="loadingImg" />
  204. </view>
  205. </view>
  206. <view
  207. class="row"
  208. v-if="showSummary"
  209. :style="{
  210. position: 'sticky',
  211. bottom: '0',
  212. zIndex: 5,
  213. backgroundColor: '#fff',
  214. borderTop: '1rpx solid #eef0f4',
  215. color: summaryFontColor,
  216. }">
  217. <view
  218. v-for="(col, ci) in columnList"
  219. :key="getUid()"
  220. class="cell"
  221. :style="{
  222. ...bodyCellStyle(ci, col),
  223. fontWeight: summaryBold ? 'bold' : 'normal',
  224. }"
  225. >{{ getSummary(col, ci) }}</view
  226. >
  227. </view>
  228. </template>
  229. </view>
  230. </view>
  231. <view v-if="loading" class="no-data-placeholder" :style="{ height: 'auto' }">
  232. <view class="row loading-row">
  233. <view class="loading-wrapper">
  234. <image class="loading-icon" :src="loadingImg" />
  235. </view>
  236. </view>
  237. </view>
  238. <view v-if="!loading && (!list || list.length === 0)" class="no-data-placeholder">
  239. <EmptyView />
  240. </view>
  241. </scroll-view>
  242. <view v-if="loading" class="no-data" :style="{ height: 'auto', top: cellHeight }">
  243. <view class="row loading-row">
  244. <view class="loading-wrapper">
  245. <image class="loading-icon" :src="loadingImg" />
  246. </view>
  247. </view>
  248. </view>
  249. <view v-if="!loading && (!list || list.length === 0)" class="no-data" :style="{ top: cellHeight }">
  250. <EmptyView />
  251. </view>
  252. </view>
  253. </view>
  254. </template>
  255. <script>
  256. import EmptyView from "../../../../wigets/empty.vue";
  257. import loadingImg from "../../../../static/images/loading.png";
  258. import { getUid } from "../../../../utils/utils.js";
  259. import request from "../../../../request/index.js";
  260. import Select from "../../../../wigets/select.vue";
  261. export default {
  262. name: "ScanRateTable",
  263. components: { EmptyView, Select },
  264. props: {
  265. params: {
  266. type: Object,
  267. default: null,
  268. },
  269. scanType: { type: String, default: "1" },
  270. api: {
  271. type: String,
  272. default: "",
  273. },
  274. tableType: { type: String, default: "customer" },
  275. title: { type: String, default: "客户扫码率" },
  276. showSelector: { type: Boolean, default: true },
  277. showAreaSearch: { type: [Boolean, String], default: true },
  278. showHeader: { type: Boolean, default: true },
  279. dropdownDirection: { type: String, default: "bottom" },
  280. showSummary: { type: Boolean, default: true },
  281. striped: { type: Boolean, default: true },
  282. summaryStructure: {
  283. type: Array,
  284. default: () => [
  285. {
  286. key: "totalInRate",
  287. title: "入库扫码率",
  288. unit: "%",
  289. },
  290. {
  291. key: "totalOutRate",
  292. title: "出库扫码率",
  293. unit: "%",
  294. },
  295. // {
  296. // key: "totalSelfExaminationRate",
  297. // title: "自验证率",
  298. // unit: "%",
  299. // },
  300. ],
  301. },
  302. summaryBold: { type: Boolean, default: false },
  303. summaryFontColor: {
  304. type: String,
  305. default: "#2c69ff",
  306. },
  307. initial: { type: Array, default: () => [] },
  308. products: { type: Array, default: () => [] },
  309. regions: { type: Array, default: () => [] },
  310. entNames: { type: Array, default: () => [] },
  311. product: { type: String, default: "" },
  312. drugEntBaseInfoId: { type: [String, Number], default: "" },
  313. spec: { type: String, default: "" },
  314. entName: { type: String, default: "" },
  315. regionCode: { type: [String, Number], default: "" },
  316. regionName: { type: String, default: "" },
  317. cellHeight: {
  318. type: String,
  319. default: "76rpx",
  320. },
  321. headerFontSize: {
  322. type: String,
  323. default: "32rpx",
  324. },
  325. headerBold: {
  326. type: Boolean,
  327. default: true,
  328. },
  329. bodyFontSize: {
  330. type: String,
  331. default: "32rpx",
  332. },
  333. bodyFontColor: {
  334. type: String,
  335. default: "",
  336. },
  337. //若需要横向滚动且左侧固定,则tableWidth需等于columns所有width之和(可以略小于)
  338. tableWidth: {
  339. type: String,
  340. default: "100%",
  341. },
  342. tableHeightAuto: {
  343. type: Boolean,
  344. default: false,
  345. },
  346. pageSize: {
  347. type: Number,
  348. default: 10,
  349. },
  350. tableBodyHeight: {
  351. type: Number,
  352. default: 0,
  353. },
  354. columns: {
  355. type: Array,
  356. default: () => [
  357. {
  358. key: "regionName",
  359. title: "区域",
  360. underline: true,
  361. fixed: true,
  362. width: "",
  363. },
  364. {
  365. key: "inScanRate",
  366. title: "入库扫码率",
  367. fixed: false,
  368. width: "",
  369. tooltip: "客户入库扫码量(与上游出库单中相同的追溯码)/上游企业出库扫码量",
  370. unit: "%",
  371. },
  372. {
  373. key: "outScanRate",
  374. title: "出库扫码率",
  375. fixed: false,
  376. width: "",
  377. tooltip: "客户出库扫码量(与下游入库单中相同的追溯码)/下游企业入库扫码量",
  378. unit: "%",
  379. },
  380. // {
  381. // key: "selfExaminationRate",
  382. // title: "自验证率",
  383. // fixed: false,
  384. // width: "",
  385. // tooltip: "客户出库扫码量(与入库单中相同的追溯码)/入库扫码量",
  386. // unit: "%",
  387. // },
  388. ],
  389. },
  390. },
  391. data() {
  392. return {
  393. dropdownOpen: false,
  394. dropdownRegionOpen: false,
  395. dropdownManufacturerOpen: false,
  396. dropdownSpecOpen: false,
  397. list: [],
  398. totalCount: 0,
  399. hasmore: true,
  400. loading: true,
  401. fetchLoading: false,
  402. loadingImg,
  403. selectedProduct: { drugEntBaseInfoId: null, physicName: "全品种" },
  404. activeProductForSpec: { drugEntBaseInfoId: null, physicName: "全品种" },
  405. region: null,
  406. manufacturer: { manufacturerId: null, produceEntName: "全厂家" },
  407. selectedSpec: { specId: null, pkgSpec: "全规格" },
  408. tablePage: 1,
  409. tablePageSize: this.pageSize,
  410. summaryData: [],
  411. headerTooltip: {},
  412. regionSearchText: "",
  413. productSearchText: "",
  414. manufacturerSearchText: "",
  415. specSearchText: "",
  416. regionList: [],
  417. productList: [],
  418. manufacturerList: [],
  419. specList: [],
  420. allProductOption: { drugEntBaseInfoId: null, physicName: "全品种" },
  421. allManufacturerOption: {
  422. manufacturerId: null,
  423. produceEntName: "全厂家",
  424. },
  425. allSpecOption: { specId: null, pkgSpec: "全规格" },
  426. mockProductManufacturerSpecMap: {
  427. 感冒灵颗粒: [
  428. {
  429. manufacturerId: "m1",
  430. produceEntName: "华润三九医药股份有限公司",
  431. specs: [
  432. { specId: "s1", pkgSpec: "10g*9袋/盒" },
  433. { specId: "s2", pkgSpec: "10g*12袋/盒" },
  434. ],
  435. },
  436. {
  437. manufacturerId: "m2",
  438. produceEntName: "昆明华润圣火药业有限公司",
  439. specs: [{ specId: "s3", pkgSpec: "6g*10袋/盒" }],
  440. },
  441. ],
  442. 阿莫西林胶囊: [
  443. {
  444. manufacturerId: "m3",
  445. produceEntName: "华润双鹤药业股份有限公司",
  446. specs: [
  447. { specId: "s4", pkgSpec: "0.25g*24粒/盒" },
  448. { specId: "s5", pkgSpec: "0.5g*12粒/盒" },
  449. ],
  450. },
  451. ],
  452. },
  453. mockProducts: [
  454. { drugEntBaseInfoId: "p1", physicName: "感冒灵颗粒" },
  455. { drugEntBaseInfoId: "p2", physicName: "阿莫西林胶囊" },
  456. ],
  457. };
  458. },
  459. computed: {
  460. filteredRegions() {
  461. if (!this.regionSearchText) return this.regions;
  462. const lower = this.regionSearchText.toLowerCase();
  463. return this.regions.filter((r) => (r.regionName || r.name || r || "").toString().toLowerCase().indexOf(lower) > -1);
  464. },
  465. filteredProducts() {
  466. const rawProducts = this.products && this.products.length ? this.products : this.mockProducts || [];
  467. const hasAllOption = rawProducts.some((p) => !p.drugEntBaseInfoId);
  468. const source = hasAllOption ? rawProducts : [this.allProductOption, ...rawProducts];
  469. if (!this.productSearchText) return source;
  470. const lower = this.productSearchText.toLowerCase();
  471. return source.filter((p) => (p.physicName || "").toLowerCase().indexOf(lower) > -1);
  472. },
  473. filteredManufacturers() {
  474. const source = this.getManufacturerSource();
  475. if (!this.manufacturerSearchText) return source;
  476. const lower = this.manufacturerSearchText.toLowerCase();
  477. return source.filter((m) => (m.produceEntName || "").toLowerCase().includes(lower));
  478. },
  479. filteredSpecs() {
  480. const source = this.getSpecSource();
  481. if (!this.specSearchText) return source;
  482. const lower = this.specSearchText.toLowerCase();
  483. return source.filter((s) => (s.pkgSpec || "").toLowerCase().includes(lower));
  484. },
  485. cascaderDisplayText() {
  486. const productName = this.selectedProduct?.drugEntBaseInfoId ? this.selectedProduct.physicName : "";
  487. const pkgSpec = this.selectedSpec?.specId ? this.selectedSpec.pkgSpec : "";
  488. if (productName && pkgSpec) return `${productName}/${pkgSpec}`;
  489. if (productName) return productName;
  490. if (pkgSpec) return pkgSpec;
  491. return "选择药品";
  492. },
  493. shouldShowAreaSearch() {
  494. return this.showAreaSearch !== false && this.showAreaSearch !== "false";
  495. },
  496. _tableBodyHeight() {
  497. if (this.tableBodyHeight) return this.tableBodyHeight;
  498. return 6 * 80;
  499. },
  500. columnList() {
  501. if (this.params) {
  502. const type = this.params?.type;
  503. if (type === "1" || type === "2") return this.columns.filter((i) => i.key !== "selfExaminationRate");
  504. }
  505. return this.columns;
  506. },
  507. },
  508. watch: {
  509. dropdownRegionOpen(val) {
  510. if (val) {
  511. this.regionSearchText = "";
  512. this.initRegionList();
  513. }
  514. },
  515. dropdownOpen(val) {
  516. if (val) {
  517. this.productSearchText = "";
  518. this.initProductList();
  519. }
  520. },
  521. dropdownManufacturerOpen(val) {
  522. if (val) {
  523. this.manufacturerSearchText = "";
  524. this.initManufacturerList();
  525. }
  526. },
  527. dropdownSpecOpen(val) {
  528. if (val) {
  529. this.specSearchText = "";
  530. this.initSpecList();
  531. }
  532. },
  533. product() {
  534. this.applyInitialFilters();
  535. },
  536. drugEntBaseInfoId() {
  537. this.applyInitialFilters();
  538. },
  539. spec() {
  540. this.applyInitialFilters();
  541. },
  542. entName() {
  543. this.applyInitialFilters();
  544. },
  545. regionCode() {
  546. this.applyInitialFilters();
  547. },
  548. regionName() {
  549. this.applyInitialFilters();
  550. },
  551. selectedProduct(val) {
  552. this.resetFetch();
  553. },
  554. region: {
  555. handler(val) {
  556. this.resetFetch();
  557. },
  558. deep: true,
  559. },
  560. params: {
  561. handler(newVal, oldVal) {
  562. if (newVal !== oldVal && JSON.stringify(newVal) === JSON.stringify(oldVal)) return;
  563. this.resetFetch();
  564. },
  565. deep: true,
  566. },
  567. products: {
  568. handler(newVal, oldVal) {
  569. this.applyInitialFilters();
  570. if (this.dropdownOpen) {
  571. this.initProductList();
  572. }
  573. },
  574. deep: true,
  575. },
  576. regions: {
  577. handler(newVal, oldVal) {
  578. this.applyInitialFilters();
  579. if (this.dropdownRegionOpen) {
  580. this.initRegionList();
  581. }
  582. },
  583. deep: true,
  584. },
  585. entNames: {
  586. handler() {
  587. this.applyInitialFilters();
  588. if (this.dropdownManufacturerOpen) {
  589. this.initManufacturerList();
  590. }
  591. },
  592. deep: true,
  593. },
  594. manufacturer(val) {
  595. this.resetFetch();
  596. },
  597. selectedSpec(val) {
  598. this.resetFetch();
  599. },
  600. },
  601. created() {
  602. // this.fetchLoading = false;
  603. // this.isLoading = false;
  604. // this.loading = false;
  605. // this.list =
  606. // this.initial && this.initial.length ? this.initial : this.genRows(7);
  607. // if (this.list.length > this.totalCount) {
  608. // this.list = this.list.slice(0, this.totalCount);
  609. // }
  610. // if (!this.showHeader) {
  611. // this.fetchList();
  612. // } else {
  613. // Promise.all([this.getProviceList(), this.getDrugInfoName()]).then(
  614. // () => { }
  615. // );
  616. // }
  617. this._suppressFetch = true;
  618. this.applyInitialFilters();
  619. this._suppressFetch = false;
  620. this.initProductList();
  621. this.initManufacturerList();
  622. this.initSpecList();
  623. this.fetchList();
  624. },
  625. methods: {
  626. renderCell(item, col) {
  627. const raw = item[col.key];
  628. if (raw === undefined || raw === null || raw === "") return "--";
  629. if (raw === "NA") return "NA";
  630. const reg = /rate/i;
  631. if (reg.test(col.key)) {
  632. const num = Number(raw);
  633. if (!Number.isFinite(num)) return "--";
  634. return Math.floor(num) + (col.unit || "");
  635. }
  636. return raw;
  637. },
  638. initRegionList() {
  639. this.regionList = this.filteredRegions.slice(0, 10);
  640. },
  641. loadMoreRegions(e) {
  642. if (this.regionList.length >= this.filteredRegions.length) return;
  643. const currentLen = this.regionList.length;
  644. const more = this.filteredRegions.slice(currentLen, currentLen + 10);
  645. this.regionList = this.regionList.concat(more);
  646. },
  647. onRegionSearch() {
  648. this.initRegionList();
  649. },
  650. initProductList() {
  651. this.productList = this.filteredProducts.slice(0, 10);
  652. },
  653. loadMoreProducts() {
  654. if (this.productList.length >= this.filteredProducts.length) return;
  655. const currentLen = this.productList.length;
  656. const more = this.filteredProducts.slice(currentLen, currentLen + 10);
  657. this.productList = this.productList.concat(more);
  658. },
  659. onProductSearch() {
  660. this.initProductList();
  661. },
  662. applyInitialFilters() {
  663. const regionCode = this.regionCode == null ? "" : String(this.regionCode);
  664. if (regionCode || this.regionName) {
  665. const region = (this.regions || []).find((item) => String(item.regionCode || "") === regionCode || item.regionName === this.regionName);
  666. this.region = region || { regionCode: this.regionCode || null, regionName: this.regionName || "全片区" };
  667. }
  668. const productName = this.product || "";
  669. const drugEntBaseInfoId = this.drugEntBaseInfoId == null ? "" : String(this.drugEntBaseInfoId);
  670. if (productName || drugEntBaseInfoId) {
  671. const product = this.filteredProducts.find((item) => String(item.drugEntBaseInfoId || "") === drugEntBaseInfoId || item.physicName === productName);
  672. this.selectedProduct = product || {
  673. drugEntBaseInfoId: this.drugEntBaseInfoId || null,
  674. physicName: productName || "全品种",
  675. pkgSpec: [],
  676. };
  677. this.activeProductForSpec = this.selectedProduct;
  678. }
  679. if (this.spec) {
  680. const spec = this.getSpecSource().find((item) => item.pkgSpec === this.spec || String(item.specId || "") === String(this.spec));
  681. this.selectedSpec = spec || { specId: this.spec, pkgSpec: this.spec };
  682. }
  683. if (this.entName) {
  684. const manufacturer = this.getManufacturerSource().find(
  685. (item) => item.produceEntName === this.entName || String(item.manufacturerId || "") === this.entName,
  686. );
  687. this.manufacturer = manufacturer || { manufacturerId: this.entName, produceEntName: this.entName };
  688. }
  689. },
  690. getManufacturerSource() {
  691. if (this.entNames && this.entNames.length) {
  692. return this.entNames.map((item) => ({
  693. manufacturerId: item.manufacturerId || item.entNameId || item.produceEntName || item.entName || null,
  694. produceEntName: item.produceEntName || item.entName || String(item),
  695. }));
  696. }
  697. const key = this.selectedProduct.physicName || "";
  698. const manufacturers = this.mockProductManufacturerSpecMap[key] || [];
  699. return [this.allManufacturerOption, ...manufacturers];
  700. },
  701. getSpecSource() {
  702. const activeProduct = this.activeProductForSpec || this.selectedProduct;
  703. if (!activeProduct || !activeProduct.drugEntBaseInfoId) {
  704. return [this.allSpecOption];
  705. }
  706. const specs = (activeProduct.pkgSpec || activeProduct.specs || []).map((item, index) => {
  707. if (typeof item === "string" || typeof item === "number") {
  708. return {
  709. specId: item,
  710. pkgSpec: String(item),
  711. };
  712. }
  713. const specValue = item.specId || item.pkgSpec || item.pkgSpec || item.pkgpkgSpec || index;
  714. return {
  715. ...item,
  716. specId: specValue,
  717. pkgSpec: item.pkgSpec || item.pkgpkgSpec || item.pkgSpec || String(specValue),
  718. };
  719. });
  720. return [this.allSpecOption, ...specs];
  721. },
  722. initManufacturerList() {
  723. this.manufacturerList = this.filteredManufacturers.slice(0, 10);
  724. },
  725. loadMoreManufacturers() {
  726. if (this.manufacturerList.length >= this.filteredManufacturers.length) return;
  727. const currentLen = this.manufacturerList.length;
  728. const more = this.filteredManufacturers.slice(currentLen, currentLen + 10);
  729. this.manufacturerList = this.manufacturerList.concat(more);
  730. },
  731. onManufacturerSearch() {
  732. this.initManufacturerList();
  733. },
  734. initSpecList() {
  735. this.specList = this.filteredSpecs.slice(0, 10);
  736. },
  737. loadMoreSpecs() {
  738. if (this.specList.length >= this.filteredSpecs.length) return;
  739. const currentLen = this.specList.length;
  740. const more = this.filteredSpecs.slice(currentLen, currentLen + 10);
  741. this.specList = this.specList.concat(more);
  742. },
  743. onSpecSearch() {
  744. this.initSpecList();
  745. },
  746. getUid,
  747. getSummary(col, ci) {
  748. if (ci == 0) return "合计";
  749. const raw = this.summaryData[ci];
  750. if (raw === undefined || raw === null || raw === "") return "--";
  751. if (raw === "NA") return "NA";
  752. const num = Number(raw);
  753. if (!Number.isFinite(num)) return "--";
  754. return Math.floor(num) + (col.unit || "");
  755. },
  756. closeAllTooltip() {
  757. this.headerTooltip = {};
  758. },
  759. toggleHeaderTooltip(i) {
  760. const value = !this.headerTooltip[i];
  761. this.headerTooltip = {};
  762. this.$set(this.headerTooltip, i, value);
  763. },
  764. // getUid(len = 16) {
  765. // const ts = Date.now().toString(36);
  766. // const rand = Math.random().toString(36).slice(2);
  767. // return (ts + rand).slice(0, len);
  768. // },
  769. // getProviceList() {
  770. // request('/common/getProviceList', {
  771. // path: 'customerScanningRate/wigets/ScanRateTable.vue',
  772. // }).then(res => {
  773. // if (res.code == 200) {
  774. // const _data = res.data;
  775. // this.regions = _data;
  776. // this.region = this.regions[0];
  777. // }
  778. // })
  779. // },
  780. // getDrugInfoName() {
  781. // request('/bills/getDrugInfoName', {
  782. // path: 'customerScanningRate/wigets/ScanRateTable.vue',
  783. // }).then(res => {
  784. // if (res.code == 200) {
  785. // const _data = res.data;
  786. // this.products = _data;
  787. // this.product = this.products[0];
  788. // }
  789. // })
  790. // },
  791. toggleDropdown() {
  792. if (this.fetchLoading) return;
  793. const next = !this.dropdownOpen;
  794. if (!next) {
  795. this.dropdownOpen = false;
  796. return;
  797. }
  798. this.$emit("dropdown-open", this);
  799. this.$nextTick(() => {
  800. this.dropdownRegionOpen = false;
  801. this.dropdownManufacturerOpen = false;
  802. this.dropdownSpecOpen = false;
  803. this.dropdownOpen = true;
  804. });
  805. },
  806. toggleDropdownRegion() {
  807. if (this.fetchLoading) return;
  808. const next = !this.dropdownRegionOpen;
  809. if (!next) {
  810. this.dropdownRegionOpen = false;
  811. return;
  812. }
  813. this.$emit("dropdown-open", this);
  814. this.$nextTick(() => {
  815. this.dropdownOpen = false;
  816. this.dropdownManufacturerOpen = false;
  817. this.dropdownSpecOpen = false;
  818. this.dropdownRegionOpen = true;
  819. });
  820. },
  821. toggleDropdownManufacturer() {
  822. if (this.fetchLoading) return;
  823. const next = !this.dropdownManufacturerOpen;
  824. if (!next) {
  825. this.dropdownManufacturerOpen = false;
  826. return;
  827. }
  828. this.$emit("dropdown-open", this);
  829. this.$nextTick(() => {
  830. this.dropdownOpen = false;
  831. this.dropdownRegionOpen = false;
  832. this.dropdownSpecOpen = false;
  833. this.dropdownManufacturerOpen = true;
  834. });
  835. },
  836. toggleDropdownSpec() {
  837. if (this.fetchLoading) return;
  838. const next = !this.dropdownSpecOpen;
  839. if (!next) {
  840. this.dropdownSpecOpen = false;
  841. return;
  842. }
  843. this.$emit("dropdown-open", this);
  844. this.$nextTick(() => {
  845. this.dropdownOpen = false;
  846. this.dropdownRegionOpen = false;
  847. this.dropdownManufacturerOpen = false;
  848. this.dropdownSpecOpen = true;
  849. });
  850. },
  851. selectProduct(type, p) {
  852. // this.$emit("change-product", p);
  853. if (type === "product") {
  854. this.selectedProduct = p;
  855. this.activeProductForSpec = p;
  856. this.dropdownOpen = false;
  857. this.manufacturer = this.allManufacturerOption;
  858. this.selectedSpec = this.allSpecOption;
  859. this.manufacturerSearchText = "";
  860. this.specSearchText = "";
  861. this.initManufacturerList();
  862. this.initSpecList();
  863. } else if (type === "region") {
  864. this.region = p;
  865. this.dropdownRegionOpen = false;
  866. } else if (type === "manufacturer") {
  867. this.manufacturer = p;
  868. this.dropdownManufacturerOpen = false;
  869. this.selectedSpec = this.allSpecOption;
  870. this.specSearchText = "";
  871. this.initSpecList();
  872. } else if (type === "spec") {
  873. this.selectedSpec = p;
  874. this.dropdownSpecOpen = false;
  875. }
  876. this.emitFilterChange();
  877. },
  878. selectCascadeProduct(p) {
  879. this.activeProductForSpec = p || this.allProductOption;
  880. this.selectedProduct = this.activeProductForSpec;
  881. // this.manufacturer = this.allManufacturerOption;
  882. this.selectedSpec = this.allSpecOption;
  883. // this.manufacturerSearchText = "";
  884. this.specSearchText = "";
  885. this.initManufacturerList();
  886. this.initSpecList();
  887. this.emitFilterChange();
  888. },
  889. selectCascadeSpec(s) {
  890. this.selectedProduct = this.activeProductForSpec || this.allProductOption;
  891. this.selectedSpec = s || this.allSpecOption;
  892. this.dropdownOpen = false;
  893. this.emitFilterChange();
  894. },
  895. emitFilterChange() {
  896. this.$emit("filter-change", {
  897. regionCode: this.region?.regionCode || "",
  898. regionName: this.region?.regionName || "",
  899. drugEntBaseInfoId: this.selectedProduct?.drugEntBaseInfoId || "",
  900. physicName: this.selectedProduct?.physicName === "全品种" ? "" : this.selectedProduct?.physicName || "",
  901. spkSpec: this.selectedSpec?.pkgSpec === "全规格" ? "" : this.selectedSpec?.pkgSpec || "",
  902. produceEntName: this.manufacturer?.produceEntName === "全厂家" ? "" : this.manufacturer?.produceEntName || "",
  903. });
  904. },
  905. closeDropdown() {
  906. this.dropdownOpen = false;
  907. this.dropdownRegionOpen = false;
  908. this.dropdownManufacturerOpen = false;
  909. this.dropdownSpecOpen = false;
  910. },
  911. // genRows(n) {
  912. // const regions = ["广东", "广西", "湖南", "湖北", "四川", "浙江", "江苏"];
  913. // const res = [];
  914. // for (let i = 0; i < n; i++) {
  915. // const r = regions[i % regions.length];
  916. // res.push({
  917. // regionName: r,
  918. // outScanRate: `${(95 + (i % 5) * 0.5).toFixed(1)}%`,
  919. // inScanRate: `${(94 + (i % 5) * 0.6).toFixed(1)}%`,
  920. // });
  921. // }
  922. // return res;
  923. // },
  924. onScrollToLower(e) {
  925. if (e?.detail?.direction === "right") return;
  926. // if (!Array.isArray(this.list)) this.list = [];
  927. // if (this.list.length - 1 >= this.totalCount) return;
  928. // if (this.isLoading) return;
  929. // this.isLoading = true;
  930. // const remain = this.totalCount - this.list.length;
  931. // const toAdd = Math.min(7, remain);
  932. // setTimeout(() => {
  933. // if (this.list.length < this.totalCount) {
  934. // const more = this.genRows(toAdd);
  935. // this.list = this.list.concat(more);
  936. // }
  937. // this.isLoading = false;
  938. // }, 1000);
  939. this.fetchList();
  940. },
  941. resetFetch() {
  942. if (this._suppressFetch) return;
  943. this.tablePage = 1;
  944. this.list = [];
  945. this.loading = true;
  946. this.hasmore = true;
  947. this.fetchList();
  948. },
  949. fetchList() {
  950. if (this._fetchDebounceTimer) clearTimeout(this._fetchDebounceTimer);
  951. const delay = 500;
  952. this._fetchDebounceTimer = setTimeout(() => {
  953. if (!this.hasmore) {
  954. setTimeout(() => {
  955. this.fetchLoading = false;
  956. this.loading = false;
  957. }, 500);
  958. return;
  959. }
  960. if (this.fetchLoading) return;
  961. this.fetchLoading = true;
  962. const params = this.params || {};
  963. request(this.api, {
  964. ...params,
  965. physicName: this.selectedProduct ? (this.selectedProduct?.physicName == "全品种" ? "" : this.selectedProduct?.physicName) : "",
  966. produceEntName: this.manufacturer ? (this.manufacturer?.produceEntName === "全厂家" ? "" : this.manufacturer?.produceEntName) : "",
  967. pkgSpec: this.selectedSpec ? (this.selectedSpec?.pkgSpec === "全规格" ? "" : this.selectedSpec?.pkgSpec) : "",
  968. regionCode: this.region ? this.region?.regionCode || "" : "",
  969. pageNum: this.tablePage,
  970. pageSize: params.pageSize || this.tablePageSize,
  971. path: "customerScanningRate/wigets/ScanRateTable.vue",
  972. }).then((res) => {
  973. if (res.code == 200) {
  974. const _data = res.data || {};
  975. let pageInfo = _data || {};
  976. if (this.tableType !== "variety") {
  977. this.summaryData = [];
  978. this.summaryData[0] = undefined;
  979. this.summaryStructure.forEach((item) => {
  980. this.summaryData.push(_data[item.key] || "--");
  981. });
  982. pageInfo = _data?.pageInfo || {};
  983. }
  984. const { list, total } = pageInfo;
  985. this.totalCount = total || 0;
  986. if (this.list.length >= this.totalCount) {
  987. this.fetchLoading = false;
  988. this.hasmore = false;
  989. this.loading = false;
  990. return;
  991. }
  992. this.list = Array.isArray(list) ? [...this.list, ...list] : [...this.list];
  993. if (this.list.length < this.totalCount) {
  994. this.hasmore = true;
  995. this.tablePage++;
  996. } else {
  997. this.hasmore = false;
  998. }
  999. // this.list.push(this.list[0]);
  1000. // this.list.push(this.list[0]);
  1001. // this.list.push(this.list[0]);
  1002. // this.list.push(this.list[0]);
  1003. // this.list.push(this.list[0]);
  1004. // this.list.push(this.list[0]);
  1005. // this.list.push(this.list[0]);
  1006. // this.list.push(this.list[0]);
  1007. // this.list.push(this.list[0]);
  1008. // this.list.push(this.list[0]);
  1009. // this.list.push(this.list[0]);
  1010. }
  1011. this.fetchLoading = false;
  1012. this.loading = false;
  1013. });
  1014. }, delay);
  1015. },
  1016. headerCellStyle(ci, col) {
  1017. const base = this.cellBaseStyle(ci, col);
  1018. base.height = this.cellHeight;
  1019. base.fontSize = this.headerFontSize;
  1020. base.position = "sticky";
  1021. base.zIndex = 2;
  1022. base.top = "0rpx";
  1023. base.background = "#eaf2ff";
  1024. // base.display = 'flex';
  1025. // base.alignItems = 'center';
  1026. // base.justifyContent = 'center';
  1027. if (ci === 0) {
  1028. base.zIndex = 3;
  1029. }
  1030. if (col.fixed) {
  1031. base.left = this.stickyLeft(ci);
  1032. }
  1033. return base;
  1034. },
  1035. bodyCellStyle(ci, col) {
  1036. const base = this.cellBaseStyle(ci, col);
  1037. // base.lineHeight = this.cellHeight;
  1038. base.padding = "12rpx";
  1039. base.fontSize = this.bodyFontSize;
  1040. if (this.bodyFontColor) {
  1041. base.color = this.bodyFontColor;
  1042. }
  1043. if (col.fixed) {
  1044. base.position = "sticky";
  1045. base.left = this.stickyLeft(ci);
  1046. base.zIndex = 2;
  1047. }
  1048. return base;
  1049. },
  1050. cellBaseStyle(ci, col) {
  1051. const style = {};
  1052. // style.height = this.cellHeight;
  1053. if (col && col.width) {
  1054. style.width = col.width;
  1055. style.flex = `0 0 ${col.width}`;
  1056. } else {
  1057. style.flex = 1;
  1058. }
  1059. return style;
  1060. },
  1061. stickyLeft(ci) {
  1062. const parts = [];
  1063. for (let i = 0; i < ci; i++) {
  1064. const c = this.columns[i];
  1065. if (c && c.fixed && c.width) parts.push(c.width);
  1066. }
  1067. if (!parts.length) return "0rpx";
  1068. if (parts.length === 1) return parts[0];
  1069. return `calc(${parts.join(" + ")})`;
  1070. },
  1071. isPlainTitle(t) {
  1072. return typeof t === "string" ? t.indexOf("<") === -1 : true;
  1073. },
  1074. titleNodes(t) {
  1075. if (typeof t !== "string") return t;
  1076. let html = String(t);
  1077. html = html.replace(/<\s*view/g, "<div").replace(/<\/\s*view\s*>/g, "</div>");
  1078. html = html.replace(/:style=\"\{\s*textAlign:\s*'center'\s*\}\"/g, 'style="text-align:center;"');
  1079. return html;
  1080. },
  1081. handleClick(data, col) {
  1082. if (this.tableType === "variety" || this.tableType === "customerDetail" || col.key !== "regionName") return;
  1083. const drugEntBaseInfoId = this.selectedProduct?.drugEntBaseInfoId || data.drugEntBaseInfoId || data.druGentBaseInfoId || "";
  1084. const physicName = this.selectedProduct?.physicName || "";
  1085. const spkSpec = this.selectedSpec?.pkgSpec || "";
  1086. const produceEntName = this.manufacturer?.produceEntName || "";
  1087. uni.navigateTo({
  1088. url:
  1089. "/traceCodePackages/traceabilityReport/pages/customerScanningRate/detail/index?regionCode=" +
  1090. data.regionCode +
  1091. "&regionName=" +
  1092. encodeURIComponent(data.regionName) +
  1093. "&drugEntBaseInfoId=" +
  1094. encodeURIComponent(drugEntBaseInfoId) +
  1095. "&physicName=" +
  1096. encodeURIComponent(physicName) +
  1097. "&spkSpec=" +
  1098. encodeURIComponent(spkSpec) +
  1099. "&produceEntName=" +
  1100. encodeURIComponent(produceEntName) +
  1101. "&scanType=" +
  1102. this.scanType +
  1103. (this.params.type ? "&type=" + this.params.type : ""),
  1104. });
  1105. },
  1106. },
  1107. };
  1108. </script>
  1109. <style scoped>
  1110. .section {
  1111. margin-top: 24rpx;
  1112. transform: translate3d(0, 0, 0);
  1113. overflow: visible;
  1114. }
  1115. .section-header {
  1116. position: relative;
  1117. display: flex;
  1118. flex-direction: column;
  1119. align-items: flex-start;
  1120. padding: 8rpx 8rpx;
  1121. overflow: visible;
  1122. z-index: 95;
  1123. }
  1124. .noAreaHeader {
  1125. flex-direction: row;
  1126. align-items: center;
  1127. }
  1128. .section-title-row {
  1129. width: 100%;
  1130. display: flex;
  1131. align-items: center;
  1132. justify-content: space-between;
  1133. }
  1134. .section-title {
  1135. position: relative;
  1136. font-size: 30rpx;
  1137. font-weight: bold;
  1138. color: #2c69ff;
  1139. }
  1140. .section-title::after {
  1141. content: "";
  1142. position: absolute;
  1143. left: -20rpx;
  1144. bottom: 13rpx;
  1145. width: 8rpx;
  1146. height: 50%;
  1147. background: #2c69ff;
  1148. border-radius: 10px;
  1149. }
  1150. .selector-row {
  1151. width: 100%;
  1152. display: flex;
  1153. align-items: center;
  1154. gap: 40rpx;
  1155. flex-wrap: nowrap;
  1156. overflow: visible;
  1157. justify-content: flex-end;
  1158. }
  1159. .selector-wrap {
  1160. position: relative;
  1161. flex-shrink: 0;
  1162. z-index: 96;
  1163. }
  1164. .selector {
  1165. position: relative;
  1166. display: flex;
  1167. align-items: center;
  1168. color: #2c69ff;
  1169. padding-right: 30rpx;
  1170. }
  1171. .selector-text {
  1172. font-size: 30rpx;
  1173. max-width: 160rpx;
  1174. overflow: hidden;
  1175. white-space: nowrap;
  1176. text-overflow: ellipsis;
  1177. display: inline-block;
  1178. }
  1179. .selector-arrow {
  1180. position: absolute;
  1181. right: 0rpx;
  1182. bottom: 14rpx;
  1183. font-size: 30rpx;
  1184. display: inline-block;
  1185. width: 15rpx;
  1186. height: 15rpx;
  1187. border: 5rpx solid #2c69ff;
  1188. border-top: none;
  1189. border-left: none;
  1190. transform-origin: 50% 50%;
  1191. transform: rotate(45deg);
  1192. transition: transform 0.2s;
  1193. }
  1194. .selector-arrow.open {
  1195. bottom: 7rpx;
  1196. transform: rotate(225deg);
  1197. }
  1198. .dropdown {
  1199. position: absolute;
  1200. left: 0rpx;
  1201. top: 54rpx;
  1202. width: 310rpx;
  1203. max-height: 380rpx;
  1204. background: #fff;
  1205. border: 1rpx solid #eef0f4;
  1206. border-radius: 12rpx;
  1207. box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
  1208. z-index: 999;
  1209. overflow: hidden;
  1210. }
  1211. .cascader-dropdown {
  1212. left: -84%;
  1213. right: auto;
  1214. width: 560rpx;
  1215. max-width: calc(100vw - 48rpx);
  1216. max-height: 460rpx;
  1217. transform: translateX(-50%);
  1218. }
  1219. .cascader-search-row {
  1220. display: flex;
  1221. width: 100%;
  1222. }
  1223. .cascader-search {
  1224. flex: 1;
  1225. box-sizing: border-box;
  1226. }
  1227. .cascader-body {
  1228. display: flex;
  1229. width: 100%;
  1230. }
  1231. .cascader-col {
  1232. width: 50%;
  1233. max-height: 320rpx;
  1234. }
  1235. .cascader-col-right {
  1236. border-left: 1rpx solid #f0f2f5;
  1237. box-sizing: border-box;
  1238. }
  1239. .dropdown-search-bar {
  1240. padding: 10rpx;
  1241. border-bottom: 1rpx solid #f0f2f5;
  1242. background: #fff;
  1243. z-index: 94;
  1244. }
  1245. .dropdown-search-input {
  1246. width: 100%;
  1247. height: 60rpx;
  1248. background: #f5f7fa;
  1249. border-radius: 8rpx;
  1250. padding: 0 20rpx;
  1251. font-size: 28rpx;
  1252. box-sizing: border-box;
  1253. }
  1254. .dropdown-scroll-view {
  1255. flex: 1;
  1256. width: 100%;
  1257. max-height: 260rpx;
  1258. /* Adjusted height to fit search bar */
  1259. overflow-y: auto;
  1260. }
  1261. .cascader-body .cascader-col {
  1262. flex: 0 0 50%;
  1263. width: 50%;
  1264. max-height: 320rpx;
  1265. }
  1266. .dropdown-item {
  1267. padding: 16rpx 24rpx;
  1268. font-size: 28rpx;
  1269. color: #333;
  1270. text-wrap: wrap;
  1271. }
  1272. .dropdown-item + .dropdown-item {
  1273. border-top: 1rpx solid #f0f2f5;
  1274. }
  1275. .table-scroll-x {
  1276. position: relative;
  1277. margin-top: 12rpx;
  1278. width: 100%;
  1279. min-height: 120rpx;
  1280. border-radius: 16rpx;
  1281. overflow: visible;
  1282. }
  1283. .card {
  1284. background: #fff;
  1285. font-size: 32rpx;
  1286. }
  1287. .table-header {
  1288. display: flex;
  1289. background: #eaf2ff;
  1290. font-weight: bold;
  1291. color: #2c69ff;
  1292. border-top-left-radius: 16rpx;
  1293. border-top-right-radius: 16rpx;
  1294. }
  1295. /* .table-body {
  1296. margin-top: 8rpx;
  1297. } */
  1298. .row {
  1299. display: flex;
  1300. align-items: stretch;
  1301. border-bottom: 1rpx solid #eef0f4;
  1302. }
  1303. .row:last-child,
  1304. .row:nth-last-child(2) {
  1305. border-bottom: none;
  1306. }
  1307. .loading-row {
  1308. justify-content: flex-start;
  1309. }
  1310. .loading-wrapper {
  1311. width: calc(100vw - 60rpx);
  1312. height: 76rpx;
  1313. display: flex;
  1314. align-items: center;
  1315. justify-content: center;
  1316. position: sticky;
  1317. top: 50%;
  1318. left: 0;
  1319. }
  1320. .loading-icon {
  1321. width: 40rpx;
  1322. height: 40rpx;
  1323. animation: spin 1s linear infinite;
  1324. }
  1325. @keyframes spin {
  1326. from {
  1327. transform: rotate(0deg);
  1328. }
  1329. to {
  1330. transform: rotate(360deg);
  1331. }
  1332. }
  1333. .cell {
  1334. flex: 1;
  1335. box-sizing: border-box;
  1336. width: 0;
  1337. box-sizing: border-box;
  1338. display: flex;
  1339. align-items: center;
  1340. justify-content: center;
  1341. /* text-align: center; */
  1342. word-break: break-all;
  1343. }
  1344. .table-body .cell {
  1345. background-color: #fff;
  1346. text-align: center;
  1347. }
  1348. .table-body .striped {
  1349. background: #f7f7f7;
  1350. }
  1351. .underline {
  1352. text-decoration: underline;
  1353. color: #2c69ff;
  1354. }
  1355. /* hide scrollbars for H5/App while preserving scroll behavior */
  1356. .no-scrollbar {
  1357. position: relative;
  1358. -ms-overflow-style: none;
  1359. scrollbar-width: none;
  1360. z-index: 2;
  1361. }
  1362. ::v-deep .no-scrollbar::-webkit-scrollbar {
  1363. display: none !important;
  1364. width: 0 !important;
  1365. height: 0 !important;
  1366. -webkit-appearance: none;
  1367. background: transparent;
  1368. }
  1369. .header-tooltip-wrap {
  1370. position: absolute;
  1371. top: 60rpx;
  1372. right: 0rpx;
  1373. z-index: 93;
  1374. }
  1375. .header-tooltip {
  1376. background: #000;
  1377. color: #fff;
  1378. border-radius: 8rpx;
  1379. padding: 8rpx 12rpx;
  1380. font-size: 24rpx;
  1381. width: 270rpx;
  1382. }
  1383. .header-tooltip-arrow {
  1384. position: absolute;
  1385. top: -11rpx;
  1386. right: 20rpx;
  1387. width: 0;
  1388. height: 0;
  1389. border-left: 10rpx solid transparent;
  1390. border-right: 10rpx solid transparent;
  1391. border-bottom: 12rpx solid #000;
  1392. margin: 0 auto;
  1393. }
  1394. .no-data-placeholder {
  1395. opacity: 0;
  1396. }
  1397. .no-data {
  1398. position: absolute;
  1399. top: 76rpx;
  1400. left: 0;
  1401. width: 100%;
  1402. z-index: 1;
  1403. }
  1404. </style>