| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457 |
- <template>
- <view
- class="section"
- :style="{
- position: 'relative',
- zIndex: dropdownOpen || dropdownRegionOpen || dropdownManufacturerOpen ? 9 : 'auto',
- }">
- <view v-if="showHeader" class="section-header" :class="!shouldShowAreaSearch ? 'noAreaHeader' : ''">
- <view class="section-title-row">
- <text class="section-title">{{ title }}</text>
- </view>
- <view v-if="showSelector" class="selector-row">
- <view class="selector-wrap" v-if="shouldShowAreaSearch">
- <view class="selector" @click.stop="toggleDropdownRegion">
- <text class="selector-text">{{ region?.regionCode ? region.regionName : "选择片区" }}</text>
- <text class="selector-arrow" :class="{ open: dropdownRegionOpen }"></text>
- </view>
- <view class="dropdown" v-show="dropdownRegionOpen" :style="dropdownDirection === 'top' ? { top: 'auto', bottom: '48rpx' } : {}" @click.stop>
- <view class="dropdown-search-bar">
- <input class="dropdown-search-input" v-model="regionSearchText" @input="onRegionSearch" placeholder="搜索..." />
- </view>
- <scroll-view scroll-y="true" class="dropdown-scroll-view" lower-threshold="80" @scrolltolower="loadMoreRegions">
- <view
- class="dropdown-item"
- v-for="(p, i) in regionList"
- :key="p.regionCode || i"
- :style="
- p.regionCode === region?.regionCode || (!region?.regionCode && i == 0)
- ? {
- backgroundColor: '#2c69ff',
- color: '#fff',
- }
- : {}
- "
- @click.stop="selectProduct('region', p)"
- >{{ p.regionName || p }}</view
- >
- <view v-if="regionList.length === 0" class="dropdown-item" style="text-align: center; color: #999">暂无数据 </view>
- </scroll-view>
- </view>
- </view>
- <view class="selector-wrap">
- <view class="selector" @click.stop="toggleDropdownManufacturer">
- <text class="selector-text">{{ manufacturer?.manufacturerId ? manufacturer.produceEntName : "选择厂家" }}</text>
- <text class="selector-arrow" :class="{ open: dropdownManufacturerOpen }"></text>
- </view>
- <view class="dropdown" v-show="dropdownManufacturerOpen" :style="dropdownDirection === 'top' ? { top: 'auto', bottom: '48rpx' } : {}" @click.stop>
- <view class="dropdown-search-bar">
- <input class="dropdown-search-input" v-model="manufacturerSearchText" @input="onManufacturerSearch" placeholder="搜索..." />
- </view>
- <scroll-view scroll-y="true" class="dropdown-scroll-view" lower-threshold="80" @scrolltolower="loadMoreManufacturers">
- <view
- class="dropdown-item"
- v-for="(m, i) in manufacturerList"
- :key="m.manufacturerId || i"
- :style="
- m.manufacturerId === manufacturer?.manufacturerId || (!manufacturer?.manufacturerId && i == 0)
- ? {
- backgroundColor: '#2c69ff',
- color: '#fff',
- }
- : {}
- "
- @click.stop="selectProduct('manufacturer', m)"
- >{{ m.produceEntName }}</view
- >
- <view v-if="manufacturerList.length === 0" class="dropdown-item" style="text-align: center; color: #999">暂无数据</view>
- </scroll-view>
- </view>
- </view>
- <view class="selector-wrap">
- <view class="selector" @click.stop="toggleDropdown">
- <text class="selector-text">{{ cascaderDisplayText }}</text>
- <text class="selector-arrow" :class="{ open: dropdownOpen }"></text>
- </view>
- <view
- class="dropdown cascader-dropdown"
- v-show="dropdownOpen"
- :style="
- dropdownDirection === 'top'
- ? {
- top: 'auto',
- bottom: '48rpx',
- }
- : {}
- "
- @click.stop>
- <view class="cascader-search-row">
- <view class="dropdown-search-bar cascader-search">
- <input class="dropdown-search-input" v-model="productSearchText" @input="onProductSearch" placeholder="搜索品种" />
- </view>
- <view class="dropdown-search-bar cascader-search">
- <input class="dropdown-search-input" v-model="specSearchText" @input="onSpecSearch" placeholder="搜索规格" />
- </view>
- </view>
- <view class="cascader-body">
- <scroll-view scroll-y="true" class="dropdown-scroll-view cascader-col" lower-threshold="80" @scrolltolower="loadMoreProducts">
- <view
- class="dropdown-item"
- v-for="(p, i) in productList"
- :key="p.drugEntBaseInfoId || i"
- :style="
- p.drugEntBaseInfoId === activeProductForSpec?.drugEntBaseInfoId
- ? {
- backgroundColor: '#2c69ff',
- color: '#fff',
- }
- : {}
- "
- @click.stop="selectCascadeProduct(p)"
- >{{ p.physicName }}</view
- >
- <view v-if="productList.length === 0" class="dropdown-item" style="text-align: center; color: #999">暂无数据 </view>
- </scroll-view>
- <scroll-view scroll-y="true" class="dropdown-scroll-view cascader-col cascader-col-right" lower-threshold="80" @scrolltolower="loadMoreSpecs">
- <view
- class="dropdown-item"
- v-for="(s, i) in specList"
- :key="s.specId || i"
- :style="
- s.specId === selectedSpec?.specId || (!selectedSpec?.specId && i == 0)
- ? {
- backgroundColor: '#2c69ff',
- color: '#fff',
- }
- : {}
- "
- @click.stop="selectCascadeSpec(s)"
- >{{ s.pkgSpec }}</view
- >
- <view v-if="specList.length === 0" class="dropdown-item" style="text-align: center; color: #999">暂无数据 </view>
- </scroll-view>
- </view>
- </view>
- </view>
- </view>
- </view>
- <view
- class="table-scroll-x"
- :style="{
- height: tableHeightAuto || list.length < 7 ? 'auto' : _tableBodyHeight + 80 + 'rpx',
- maxHeight: tableHeightAuto,
- }">
- <scroll-view
- class="no-scrollbar"
- :style="{ height: '100%' }"
- scroll-x="true"
- scroll-y="true"
- enhanced
- :show-scrollbar="false"
- @scrolltolower="onScrollToLower">
- <view class="card" :style="{ width: tableWidth }">
- <view
- class="table-header"
- :style="{
- position: 'sticky',
- top: '0',
- zIndex: 10,
- fontWeight: headerBold ? 'bold' : 'normal',
- }">
- <view
- v-for="(col, ci) in columnList"
- :key="getUid()"
- class="cell"
- :style="{
- ...headerCellStyle(ci, col),
- }">
- <view
- v-if="isPlainTitle(col.title)"
- :style="{
- display: 'flex',
- alignItems: 'center',
- position: 'relative',
- }">
- <text>{{ col.title }}</text>
- <uni-icons v-if="col.tooltip" type="help" size="16" color="#2c69ff" @tap.stop.prevent="toggleHeaderTooltip(ci)"></uni-icons>
- <view v-if="headerTooltip[ci]" class="header-tooltip-wrap" @click.stop>
- <view class="header-tooltip">{{ col.tooltip }}</view>
- <view class="header-tooltip-arrow"></view>
- </view>
- </view>
- <rich-text v-else :nodes="titleNodes(col.title)" />
- </view>
- </view>
- <view v-if="!loading && list && list.length > 0" class="table-body">
- <template>
- <view class="row" v-for="(item, i) in list" :key="getUid()">
- <view
- v-for="(col, ci) in columnList"
- :key="getUid()"
- class="cell"
- :class="{
- underline: col.underline,
- striped: striped && (i + 1) % 2 === 0,
- }"
- :style="bodyCellStyle(ci, col)"
- @click="handleClick(item, col)"
- >{{ renderCell(item, col) }}</view
- >
- </view>
- <view class="row loading-row" v-if="hasmore">
- <view class="loading-wrapper">
- <image class="loading-icon" :src="loadingImg" />
- </view>
- </view>
- <view
- class="row"
- v-if="showSummary"
- :style="{
- position: 'sticky',
- bottom: '0',
- zIndex: 5,
- backgroundColor: '#fff',
- borderTop: '1rpx solid #eef0f4',
- color: summaryFontColor,
- }">
- <view
- v-for="(col, ci) in columnList"
- :key="getUid()"
- class="cell"
- :style="{
- ...bodyCellStyle(ci, col),
- fontWeight: summaryBold ? 'bold' : 'normal',
- }"
- >{{ getSummary(col, ci) }}</view
- >
- </view>
- </template>
- </view>
- </view>
- <view v-if="loading" class="no-data-placeholder" :style="{ height: 'auto' }">
- <view class="row loading-row">
- <view class="loading-wrapper">
- <image class="loading-icon" :src="loadingImg" />
- </view>
- </view>
- </view>
- <view v-if="!loading && (!list || list.length === 0)" class="no-data-placeholder">
- <EmptyView />
- </view>
- </scroll-view>
- <view v-if="loading" class="no-data" :style="{ height: 'auto', top: cellHeight }">
- <view class="row loading-row">
- <view class="loading-wrapper">
- <image class="loading-icon" :src="loadingImg" />
- </view>
- </view>
- </view>
- <view v-if="!loading && (!list || list.length === 0)" class="no-data" :style="{ top: cellHeight }">
- <EmptyView />
- </view>
- </view>
- </view>
- </template>
- <script>
- import EmptyView from "../../../../wigets/empty.vue";
- import loadingImg from "../../../../static/images/loading.png";
- import { getUid } from "../../../../utils/utils.js";
- import request from "../../../../request/index.js";
- import Select from "../../../../wigets/select.vue";
- export default {
- name: "ScanRateTable",
- components: { EmptyView, Select },
- props: {
- params: {
- type: Object,
- default: null,
- },
- scanType: { type: String, default: "1" },
- api: {
- type: String,
- default: "",
- },
- tableType: { type: String, default: "customer" },
- title: { type: String, default: "客户扫码率" },
- showSelector: { type: Boolean, default: true },
- showAreaSearch: { type: [Boolean, String], default: true },
- showHeader: { type: Boolean, default: true },
- dropdownDirection: { type: String, default: "bottom" },
- showSummary: { type: Boolean, default: true },
- striped: { type: Boolean, default: true },
- summaryStructure: {
- type: Array,
- default: () => [
- {
- key: "totalInRate",
- title: "入库扫码率",
- unit: "%",
- },
- {
- key: "totalOutRate",
- title: "出库扫码率",
- unit: "%",
- },
- // {
- // key: "totalSelfExaminationRate",
- // title: "自验证率",
- // unit: "%",
- // },
- ],
- },
- summaryBold: { type: Boolean, default: false },
- summaryFontColor: {
- type: String,
- default: "#2c69ff",
- },
- initial: { type: Array, default: () => [] },
- products: { type: Array, default: () => [] },
- regions: { type: Array, default: () => [] },
- entNames: { type: Array, default: () => [] },
- product: { type: String, default: "" },
- drugEntBaseInfoId: { type: [String, Number], default: "" },
- spec: { type: String, default: "" },
- entName: { type: String, default: "" },
- regionCode: { type: [String, Number], default: "" },
- regionName: { type: String, default: "" },
- cellHeight: {
- type: String,
- default: "76rpx",
- },
- headerFontSize: {
- type: String,
- default: "32rpx",
- },
- headerBold: {
- type: Boolean,
- default: true,
- },
- bodyFontSize: {
- type: String,
- default: "32rpx",
- },
- bodyFontColor: {
- type: String,
- default: "",
- },
- //若需要横向滚动且左侧固定,则tableWidth需等于columns所有width之和(可以略小于)
- tableWidth: {
- type: String,
- default: "100%",
- },
- tableHeightAuto: {
- type: Boolean,
- default: false,
- },
- pageSize: {
- type: Number,
- default: 10,
- },
- tableBodyHeight: {
- type: Number,
- default: 0,
- },
- columns: {
- type: Array,
- default: () => [
- {
- key: "regionName",
- title: "区域",
- underline: true,
- fixed: true,
- width: "",
- },
- {
- key: "inScanRate",
- title: "入库扫码率",
- fixed: false,
- width: "",
- tooltip: "客户入库扫码量(与上游出库单中相同的追溯码)/上游企业出库扫码量",
- unit: "%",
- },
- {
- key: "outScanRate",
- title: "出库扫码率",
- fixed: false,
- width: "",
- tooltip: "客户出库扫码量(与下游入库单中相同的追溯码)/下游企业入库扫码量",
- unit: "%",
- },
- // {
- // key: "selfExaminationRate",
- // title: "自验证率",
- // fixed: false,
- // width: "",
- // tooltip: "客户出库扫码量(与入库单中相同的追溯码)/入库扫码量",
- // unit: "%",
- // },
- ],
- },
- },
- data() {
- return {
- dropdownOpen: false,
- dropdownRegionOpen: false,
- dropdownManufacturerOpen: false,
- dropdownSpecOpen: false,
- list: [],
- totalCount: 0,
- hasmore: true,
- loading: true,
- fetchLoading: false,
- loadingImg,
- selectedProduct: { drugEntBaseInfoId: null, physicName: "全品种" },
- activeProductForSpec: { drugEntBaseInfoId: null, physicName: "全品种" },
- region: null,
- manufacturer: { manufacturerId: null, produceEntName: "全厂家" },
- selectedSpec: { specId: null, pkgSpec: "全规格" },
- tablePage: 1,
- tablePageSize: this.pageSize,
- summaryData: [],
- headerTooltip: {},
- regionSearchText: "",
- productSearchText: "",
- manufacturerSearchText: "",
- specSearchText: "",
- regionList: [],
- productList: [],
- manufacturerList: [],
- specList: [],
- allProductOption: { drugEntBaseInfoId: null, physicName: "全品种" },
- allManufacturerOption: {
- manufacturerId: null,
- produceEntName: "全厂家",
- },
- allSpecOption: { specId: null, pkgSpec: "全规格" },
- mockProductManufacturerSpecMap: {
- 感冒灵颗粒: [
- {
- manufacturerId: "m1",
- produceEntName: "华润三九医药股份有限公司",
- specs: [
- { specId: "s1", pkgSpec: "10g*9袋/盒" },
- { specId: "s2", pkgSpec: "10g*12袋/盒" },
- ],
- },
- {
- manufacturerId: "m2",
- produceEntName: "昆明华润圣火药业有限公司",
- specs: [{ specId: "s3", pkgSpec: "6g*10袋/盒" }],
- },
- ],
- 阿莫西林胶囊: [
- {
- manufacturerId: "m3",
- produceEntName: "华润双鹤药业股份有限公司",
- specs: [
- { specId: "s4", pkgSpec: "0.25g*24粒/盒" },
- { specId: "s5", pkgSpec: "0.5g*12粒/盒" },
- ],
- },
- ],
- },
- mockProducts: [
- { drugEntBaseInfoId: "p1", physicName: "感冒灵颗粒" },
- { drugEntBaseInfoId: "p2", physicName: "阿莫西林胶囊" },
- ],
- };
- },
- computed: {
- filteredRegions() {
- if (!this.regionSearchText) return this.regions;
- const lower = this.regionSearchText.toLowerCase();
- return this.regions.filter((r) => (r.regionName || r.name || r || "").toString().toLowerCase().indexOf(lower) > -1);
- },
- filteredProducts() {
- const rawProducts = this.products && this.products.length ? this.products : this.mockProducts || [];
- const hasAllOption = rawProducts.some((p) => !p.drugEntBaseInfoId);
- const source = hasAllOption ? rawProducts : [this.allProductOption, ...rawProducts];
- if (!this.productSearchText) return source;
- const lower = this.productSearchText.toLowerCase();
- return source.filter((p) => (p.physicName || "").toLowerCase().indexOf(lower) > -1);
- },
- filteredManufacturers() {
- const source = this.getManufacturerSource();
- if (!this.manufacturerSearchText) return source;
- const lower = this.manufacturerSearchText.toLowerCase();
- return source.filter((m) => (m.produceEntName || "").toLowerCase().includes(lower));
- },
- filteredSpecs() {
- const source = this.getSpecSource();
- if (!this.specSearchText) return source;
- const lower = this.specSearchText.toLowerCase();
- return source.filter((s) => (s.pkgSpec || "").toLowerCase().includes(lower));
- },
- cascaderDisplayText() {
- const productName = this.selectedProduct?.drugEntBaseInfoId ? this.selectedProduct.physicName : "";
- const pkgSpec = this.selectedSpec?.specId ? this.selectedSpec.pkgSpec : "";
- if (productName && pkgSpec) return `${productName}/${pkgSpec}`;
- if (productName) return productName;
- if (pkgSpec) return pkgSpec;
- return "选择药品";
- },
- shouldShowAreaSearch() {
- return this.showAreaSearch !== false && this.showAreaSearch !== "false";
- },
- _tableBodyHeight() {
- if (this.tableBodyHeight) return this.tableBodyHeight;
- return 6 * 80;
- },
- columnList() {
- if (this.params) {
- const type = this.params?.type;
- if (type === "1" || type === "2") return this.columns.filter((i) => i.key !== "selfExaminationRate");
- }
- return this.columns;
- },
- },
- watch: {
- dropdownRegionOpen(val) {
- if (val) {
- this.regionSearchText = "";
- this.initRegionList();
- }
- },
- dropdownOpen(val) {
- if (val) {
- this.productSearchText = "";
- this.initProductList();
- }
- },
- dropdownManufacturerOpen(val) {
- if (val) {
- this.manufacturerSearchText = "";
- this.initManufacturerList();
- }
- },
- dropdownSpecOpen(val) {
- if (val) {
- this.specSearchText = "";
- this.initSpecList();
- }
- },
- product() {
- this.applyInitialFilters();
- },
- drugEntBaseInfoId() {
- this.applyInitialFilters();
- },
- spec() {
- this.applyInitialFilters();
- },
- entName() {
- this.applyInitialFilters();
- },
- regionCode() {
- this.applyInitialFilters();
- },
- regionName() {
- this.applyInitialFilters();
- },
- selectedProduct(val) {
- this.resetFetch();
- },
- region: {
- handler(val) {
- this.resetFetch();
- },
- deep: true,
- },
- params: {
- handler(newVal, oldVal) {
- if (newVal !== oldVal && JSON.stringify(newVal) === JSON.stringify(oldVal)) return;
- this.resetFetch();
- },
- deep: true,
- },
- products: {
- handler(newVal, oldVal) {
- this.applyInitialFilters();
- if (this.dropdownOpen) {
- this.initProductList();
- }
- },
- deep: true,
- },
- regions: {
- handler(newVal, oldVal) {
- this.applyInitialFilters();
- if (this.dropdownRegionOpen) {
- this.initRegionList();
- }
- },
- deep: true,
- },
- entNames: {
- handler() {
- this.applyInitialFilters();
- if (this.dropdownManufacturerOpen) {
- this.initManufacturerList();
- }
- },
- deep: true,
- },
- manufacturer(val) {
- this.resetFetch();
- },
- selectedSpec(val) {
- this.resetFetch();
- },
- },
- created() {
- // this.fetchLoading = false;
- // this.isLoading = false;
- // this.loading = false;
- // this.list =
- // this.initial && this.initial.length ? this.initial : this.genRows(7);
- // if (this.list.length > this.totalCount) {
- // this.list = this.list.slice(0, this.totalCount);
- // }
- // if (!this.showHeader) {
- // this.fetchList();
- // } else {
- // Promise.all([this.getProviceList(), this.getDrugInfoName()]).then(
- // () => { }
- // );
- // }
- this._suppressFetch = true;
- this.applyInitialFilters();
- this._suppressFetch = false;
- this.initProductList();
- this.initManufacturerList();
- this.initSpecList();
- this.fetchList();
- },
- methods: {
- renderCell(item, col) {
- const raw = item[col.key];
- if (raw === undefined || raw === null || raw === "") return "--";
- if (raw === "NA") return "NA";
- const reg = /rate/i;
- if (reg.test(col.key)) {
- const num = Number(raw);
- if (!Number.isFinite(num)) return "--";
- return Math.floor(num) + (col.unit || "");
- }
- return raw;
- },
- initRegionList() {
- this.regionList = this.filteredRegions.slice(0, 10);
- },
- loadMoreRegions(e) {
- if (this.regionList.length >= this.filteredRegions.length) return;
- const currentLen = this.regionList.length;
- const more = this.filteredRegions.slice(currentLen, currentLen + 10);
- this.regionList = this.regionList.concat(more);
- },
- onRegionSearch() {
- this.initRegionList();
- },
- initProductList() {
- this.productList = this.filteredProducts.slice(0, 10);
- },
- loadMoreProducts() {
- if (this.productList.length >= this.filteredProducts.length) return;
- const currentLen = this.productList.length;
- const more = this.filteredProducts.slice(currentLen, currentLen + 10);
- this.productList = this.productList.concat(more);
- },
- onProductSearch() {
- this.initProductList();
- },
- applyInitialFilters() {
- const regionCode = this.regionCode == null ? "" : String(this.regionCode);
- if (regionCode || this.regionName) {
- const region = (this.regions || []).find((item) => String(item.regionCode || "") === regionCode || item.regionName === this.regionName);
- this.region = region || { regionCode: this.regionCode || null, regionName: this.regionName || "全片区" };
- }
- const productName = this.product || "";
- const drugEntBaseInfoId = this.drugEntBaseInfoId == null ? "" : String(this.drugEntBaseInfoId);
- if (productName || drugEntBaseInfoId) {
- const product = this.filteredProducts.find((item) => String(item.drugEntBaseInfoId || "") === drugEntBaseInfoId || item.physicName === productName);
- this.selectedProduct = product || {
- drugEntBaseInfoId: this.drugEntBaseInfoId || null,
- physicName: productName || "全品种",
- pkgSpec: [],
- };
- this.activeProductForSpec = this.selectedProduct;
- }
- if (this.spec) {
- const spec = this.getSpecSource().find((item) => item.pkgSpec === this.spec || String(item.specId || "") === String(this.spec));
- this.selectedSpec = spec || { specId: this.spec, pkgSpec: this.spec };
- }
- if (this.entName) {
- const manufacturer = this.getManufacturerSource().find(
- (item) => item.produceEntName === this.entName || String(item.manufacturerId || "") === this.entName,
- );
- this.manufacturer = manufacturer || { manufacturerId: this.entName, produceEntName: this.entName };
- }
- },
- getManufacturerSource() {
- if (this.entNames && this.entNames.length) {
- return this.entNames.map((item) => ({
- manufacturerId: item.manufacturerId || item.entNameId || item.produceEntName || item.entName || null,
- produceEntName: item.produceEntName || item.entName || String(item),
- }));
- }
- const key = this.selectedProduct.physicName || "";
- const manufacturers = this.mockProductManufacturerSpecMap[key] || [];
- return [this.allManufacturerOption, ...manufacturers];
- },
- getSpecSource() {
- const activeProduct = this.activeProductForSpec || this.selectedProduct;
- if (!activeProduct || !activeProduct.drugEntBaseInfoId) {
- return [this.allSpecOption];
- }
- const specs = (activeProduct.pkgSpec || activeProduct.specs || []).map((item, index) => {
- if (typeof item === "string" || typeof item === "number") {
- return {
- specId: item,
- pkgSpec: String(item),
- };
- }
- const specValue = item.specId || item.pkgSpec || item.pkgSpec || item.pkgpkgSpec || index;
- return {
- ...item,
- specId: specValue,
- pkgSpec: item.pkgSpec || item.pkgpkgSpec || item.pkgSpec || String(specValue),
- };
- });
- return [this.allSpecOption, ...specs];
- },
- initManufacturerList() {
- this.manufacturerList = this.filteredManufacturers.slice(0, 10);
- },
- loadMoreManufacturers() {
- if (this.manufacturerList.length >= this.filteredManufacturers.length) return;
- const currentLen = this.manufacturerList.length;
- const more = this.filteredManufacturers.slice(currentLen, currentLen + 10);
- this.manufacturerList = this.manufacturerList.concat(more);
- },
- onManufacturerSearch() {
- this.initManufacturerList();
- },
- initSpecList() {
- this.specList = this.filteredSpecs.slice(0, 10);
- },
- loadMoreSpecs() {
- if (this.specList.length >= this.filteredSpecs.length) return;
- const currentLen = this.specList.length;
- const more = this.filteredSpecs.slice(currentLen, currentLen + 10);
- this.specList = this.specList.concat(more);
- },
- onSpecSearch() {
- this.initSpecList();
- },
- getUid,
- getSummary(col, ci) {
- if (ci == 0) return "合计";
- const raw = this.summaryData[ci];
- if (raw === undefined || raw === null || raw === "") return "--";
- if (raw === "NA") return "NA";
- const num = Number(raw);
- if (!Number.isFinite(num)) return "--";
- return Math.floor(num) + (col.unit || "");
- },
- closeAllTooltip() {
- this.headerTooltip = {};
- },
- toggleHeaderTooltip(i) {
- const value = !this.headerTooltip[i];
- this.headerTooltip = {};
- this.$set(this.headerTooltip, i, value);
- },
- // getUid(len = 16) {
- // const ts = Date.now().toString(36);
- // const rand = Math.random().toString(36).slice(2);
- // return (ts + rand).slice(0, len);
- // },
- // getProviceList() {
- // request('/common/getProviceList', {
- // path: 'customerScanningRate/wigets/ScanRateTable.vue',
- // }).then(res => {
- // if (res.code == 200) {
- // const _data = res.data;
- // this.regions = _data;
- // this.region = this.regions[0];
- // }
- // })
- // },
- // getDrugInfoName() {
- // request('/bills/getDrugInfoName', {
- // path: 'customerScanningRate/wigets/ScanRateTable.vue',
- // }).then(res => {
- // if (res.code == 200) {
- // const _data = res.data;
- // this.products = _data;
- // this.product = this.products[0];
- // }
- // })
- // },
- toggleDropdown() {
- if (this.fetchLoading) return;
- const next = !this.dropdownOpen;
- if (!next) {
- this.dropdownOpen = false;
- return;
- }
- this.$emit("dropdown-open", this);
- this.$nextTick(() => {
- this.dropdownRegionOpen = false;
- this.dropdownManufacturerOpen = false;
- this.dropdownSpecOpen = false;
- this.dropdownOpen = true;
- });
- },
- toggleDropdownRegion() {
- if (this.fetchLoading) return;
- const next = !this.dropdownRegionOpen;
- if (!next) {
- this.dropdownRegionOpen = false;
- return;
- }
- this.$emit("dropdown-open", this);
- this.$nextTick(() => {
- this.dropdownOpen = false;
- this.dropdownManufacturerOpen = false;
- this.dropdownSpecOpen = false;
- this.dropdownRegionOpen = true;
- });
- },
- toggleDropdownManufacturer() {
- if (this.fetchLoading) return;
- const next = !this.dropdownManufacturerOpen;
- if (!next) {
- this.dropdownManufacturerOpen = false;
- return;
- }
- this.$emit("dropdown-open", this);
- this.$nextTick(() => {
- this.dropdownOpen = false;
- this.dropdownRegionOpen = false;
- this.dropdownSpecOpen = false;
- this.dropdownManufacturerOpen = true;
- });
- },
- toggleDropdownSpec() {
- if (this.fetchLoading) return;
- const next = !this.dropdownSpecOpen;
- if (!next) {
- this.dropdownSpecOpen = false;
- return;
- }
- this.$emit("dropdown-open", this);
- this.$nextTick(() => {
- this.dropdownOpen = false;
- this.dropdownRegionOpen = false;
- this.dropdownManufacturerOpen = false;
- this.dropdownSpecOpen = true;
- });
- },
- selectProduct(type, p) {
- // this.$emit("change-product", p);
- if (type === "product") {
- this.selectedProduct = p;
- this.activeProductForSpec = p;
- this.dropdownOpen = false;
- this.manufacturer = this.allManufacturerOption;
- this.selectedSpec = this.allSpecOption;
- this.manufacturerSearchText = "";
- this.specSearchText = "";
- this.initManufacturerList();
- this.initSpecList();
- } else if (type === "region") {
- this.region = p;
- this.dropdownRegionOpen = false;
- } else if (type === "manufacturer") {
- this.manufacturer = p;
- this.dropdownManufacturerOpen = false;
- this.selectedSpec = this.allSpecOption;
- this.specSearchText = "";
- this.initSpecList();
- } else if (type === "spec") {
- this.selectedSpec = p;
- this.dropdownSpecOpen = false;
- }
- this.emitFilterChange();
- },
- selectCascadeProduct(p) {
- this.activeProductForSpec = p || this.allProductOption;
- this.selectedProduct = this.activeProductForSpec;
- // this.manufacturer = this.allManufacturerOption;
- this.selectedSpec = this.allSpecOption;
- // this.manufacturerSearchText = "";
- this.specSearchText = "";
- this.initManufacturerList();
- this.initSpecList();
- this.emitFilterChange();
- },
- selectCascadeSpec(s) {
- this.selectedProduct = this.activeProductForSpec || this.allProductOption;
- this.selectedSpec = s || this.allSpecOption;
- this.dropdownOpen = false;
- this.emitFilterChange();
- },
- emitFilterChange() {
- this.$emit("filter-change", {
- regionCode: this.region?.regionCode || "",
- regionName: this.region?.regionName || "",
- drugEntBaseInfoId: this.selectedProduct?.drugEntBaseInfoId || "",
- physicName: this.selectedProduct?.physicName === "全品种" ? "" : this.selectedProduct?.physicName || "",
- spkSpec: this.selectedSpec?.pkgSpec === "全规格" ? "" : this.selectedSpec?.pkgSpec || "",
- produceEntName: this.manufacturer?.produceEntName === "全厂家" ? "" : this.manufacturer?.produceEntName || "",
- });
- },
- closeDropdown() {
- this.dropdownOpen = false;
- this.dropdownRegionOpen = false;
- this.dropdownManufacturerOpen = false;
- this.dropdownSpecOpen = false;
- },
- // genRows(n) {
- // const regions = ["广东", "广西", "湖南", "湖北", "四川", "浙江", "江苏"];
- // const res = [];
- // for (let i = 0; i < n; i++) {
- // const r = regions[i % regions.length];
- // res.push({
- // regionName: r,
- // outScanRate: `${(95 + (i % 5) * 0.5).toFixed(1)}%`,
- // inScanRate: `${(94 + (i % 5) * 0.6).toFixed(1)}%`,
- // });
- // }
- // return res;
- // },
- onScrollToLower(e) {
- if (e?.detail?.direction === "right") return;
- // if (!Array.isArray(this.list)) this.list = [];
- // if (this.list.length - 1 >= this.totalCount) return;
- // if (this.isLoading) return;
- // this.isLoading = true;
- // const remain = this.totalCount - this.list.length;
- // const toAdd = Math.min(7, remain);
- // setTimeout(() => {
- // if (this.list.length < this.totalCount) {
- // const more = this.genRows(toAdd);
- // this.list = this.list.concat(more);
- // }
- // this.isLoading = false;
- // }, 1000);
- this.fetchList();
- },
- resetFetch() {
- if (this._suppressFetch) return;
- this.tablePage = 1;
- this.list = [];
- this.loading = true;
- this.hasmore = true;
- this.fetchList();
- },
- fetchList() {
- if (this._fetchDebounceTimer) clearTimeout(this._fetchDebounceTimer);
- const delay = 500;
- this._fetchDebounceTimer = setTimeout(() => {
- if (!this.hasmore) {
- setTimeout(() => {
- this.fetchLoading = false;
- this.loading = false;
- }, 500);
- return;
- }
- if (this.fetchLoading) return;
- this.fetchLoading = true;
- const params = this.params || {};
- request(this.api, {
- ...params,
- physicName: this.selectedProduct ? (this.selectedProduct?.physicName == "全品种" ? "" : this.selectedProduct?.physicName) : "",
- produceEntName: this.manufacturer ? (this.manufacturer?.produceEntName === "全厂家" ? "" : this.manufacturer?.produceEntName) : "",
- pkgSpec: this.selectedSpec ? (this.selectedSpec?.pkgSpec === "全规格" ? "" : this.selectedSpec?.pkgSpec) : "",
- regionCode: this.region ? this.region?.regionCode || "" : "",
- pageNum: this.tablePage,
- pageSize: params.pageSize || this.tablePageSize,
- path: "customerScanningRate/wigets/ScanRateTable.vue",
- }).then((res) => {
- if (res.code == 200) {
- const _data = res.data || {};
- let pageInfo = _data || {};
- if (this.tableType !== "variety") {
- this.summaryData = [];
- this.summaryData[0] = undefined;
- this.summaryStructure.forEach((item) => {
- this.summaryData.push(_data[item.key] || "--");
- });
- pageInfo = _data?.pageInfo || {};
- }
- const { list, total } = pageInfo;
- this.totalCount = total || 0;
- if (this.list.length >= this.totalCount) {
- this.fetchLoading = false;
- this.hasmore = false;
- this.loading = false;
- return;
- }
- this.list = Array.isArray(list) ? [...this.list, ...list] : [...this.list];
- if (this.list.length < this.totalCount) {
- this.hasmore = true;
- this.tablePage++;
- } else {
- this.hasmore = false;
- }
- // this.list.push(this.list[0]);
- // this.list.push(this.list[0]);
- // this.list.push(this.list[0]);
- // this.list.push(this.list[0]);
- // this.list.push(this.list[0]);
- // this.list.push(this.list[0]);
- // this.list.push(this.list[0]);
- // this.list.push(this.list[0]);
- // this.list.push(this.list[0]);
- // this.list.push(this.list[0]);
- // this.list.push(this.list[0]);
- }
- this.fetchLoading = false;
- this.loading = false;
- });
- }, delay);
- },
- headerCellStyle(ci, col) {
- const base = this.cellBaseStyle(ci, col);
- base.height = this.cellHeight;
- base.fontSize = this.headerFontSize;
- base.position = "sticky";
- base.zIndex = 2;
- base.top = "0rpx";
- base.background = "#eaf2ff";
- // base.display = 'flex';
- // base.alignItems = 'center';
- // base.justifyContent = 'center';
- if (ci === 0) {
- base.zIndex = 3;
- }
- if (col.fixed) {
- base.left = this.stickyLeft(ci);
- }
- return base;
- },
- bodyCellStyle(ci, col) {
- const base = this.cellBaseStyle(ci, col);
- // base.lineHeight = this.cellHeight;
- base.padding = "12rpx";
- base.fontSize = this.bodyFontSize;
- if (this.bodyFontColor) {
- base.color = this.bodyFontColor;
- }
- if (col.fixed) {
- base.position = "sticky";
- base.left = this.stickyLeft(ci);
- base.zIndex = 2;
- }
- return base;
- },
- cellBaseStyle(ci, col) {
- const style = {};
- // style.height = this.cellHeight;
- if (col && col.width) {
- style.width = col.width;
- style.flex = `0 0 ${col.width}`;
- } else {
- style.flex = 1;
- }
- return style;
- },
- stickyLeft(ci) {
- const parts = [];
- for (let i = 0; i < ci; i++) {
- const c = this.columns[i];
- if (c && c.fixed && c.width) parts.push(c.width);
- }
- if (!parts.length) return "0rpx";
- if (parts.length === 1) return parts[0];
- return `calc(${parts.join(" + ")})`;
- },
- isPlainTitle(t) {
- return typeof t === "string" ? t.indexOf("<") === -1 : true;
- },
- titleNodes(t) {
- if (typeof t !== "string") return t;
- let html = String(t);
- html = html.replace(/<\s*view/g, "<div").replace(/<\/\s*view\s*>/g, "</div>");
- html = html.replace(/:style=\"\{\s*textAlign:\s*'center'\s*\}\"/g, 'style="text-align:center;"');
- return html;
- },
- handleClick(data, col) {
- if (this.tableType === "variety" || this.tableType === "customerDetail" || col.key !== "regionName") return;
- const drugEntBaseInfoId = this.selectedProduct?.drugEntBaseInfoId || data.drugEntBaseInfoId || data.druGentBaseInfoId || "";
- const physicName = this.selectedProduct?.physicName || "";
- const spkSpec = this.selectedSpec?.pkgSpec || "";
- const produceEntName = this.manufacturer?.produceEntName || "";
- uni.navigateTo({
- url:
- "/traceCodePackages/traceabilityReport/pages/customerScanningRate/detail/index?regionCode=" +
- data.regionCode +
- "®ionName=" +
- encodeURIComponent(data.regionName) +
- "&drugEntBaseInfoId=" +
- encodeURIComponent(drugEntBaseInfoId) +
- "&physicName=" +
- encodeURIComponent(physicName) +
- "&spkSpec=" +
- encodeURIComponent(spkSpec) +
- "&produceEntName=" +
- encodeURIComponent(produceEntName) +
- "&scanType=" +
- this.scanType +
- (this.params.type ? "&type=" + this.params.type : ""),
- });
- },
- },
- };
- </script>
- <style scoped>
- .section {
- margin-top: 24rpx;
- transform: translate3d(0, 0, 0);
- overflow: visible;
- }
- .section-header {
- position: relative;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- padding: 8rpx 8rpx;
- overflow: visible;
- z-index: 95;
- }
- .noAreaHeader {
- flex-direction: row;
- align-items: center;
- }
- .section-title-row {
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: space-between;
- }
- .section-title {
- position: relative;
- font-size: 30rpx;
- font-weight: bold;
- color: #2c69ff;
- }
- .section-title::after {
- content: "";
- position: absolute;
- left: -20rpx;
- bottom: 13rpx;
- width: 8rpx;
- height: 50%;
- background: #2c69ff;
- border-radius: 10px;
- }
- .selector-row {
- width: 100%;
- display: flex;
- align-items: center;
- gap: 40rpx;
- flex-wrap: nowrap;
- overflow: visible;
- justify-content: flex-end;
- }
- .selector-wrap {
- position: relative;
- flex-shrink: 0;
- z-index: 96;
- }
- .selector {
- position: relative;
- display: flex;
- align-items: center;
- color: #2c69ff;
- padding-right: 30rpx;
- }
- .selector-text {
- font-size: 30rpx;
- max-width: 160rpx;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- display: inline-block;
- }
- .selector-arrow {
- position: absolute;
- right: 0rpx;
- bottom: 14rpx;
- font-size: 30rpx;
- display: inline-block;
- width: 15rpx;
- height: 15rpx;
- border: 5rpx solid #2c69ff;
- border-top: none;
- border-left: none;
- transform-origin: 50% 50%;
- transform: rotate(45deg);
- transition: transform 0.2s;
- }
- .selector-arrow.open {
- bottom: 7rpx;
- transform: rotate(225deg);
- }
- .dropdown {
- position: absolute;
- left: 0rpx;
- top: 54rpx;
- width: 310rpx;
- max-height: 380rpx;
- background: #fff;
- border: 1rpx solid #eef0f4;
- border-radius: 12rpx;
- box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
- z-index: 999;
- overflow: hidden;
- }
- .cascader-dropdown {
- left: -84%;
- right: auto;
- width: 560rpx;
- max-width: calc(100vw - 48rpx);
- max-height: 460rpx;
- transform: translateX(-50%);
- }
- .cascader-search-row {
- display: flex;
- width: 100%;
- }
- .cascader-search {
- flex: 1;
- box-sizing: border-box;
- }
- .cascader-body {
- display: flex;
- width: 100%;
- }
- .cascader-col {
- width: 50%;
- max-height: 320rpx;
- }
- .cascader-col-right {
- border-left: 1rpx solid #f0f2f5;
- box-sizing: border-box;
- }
- .dropdown-search-bar {
- padding: 10rpx;
- border-bottom: 1rpx solid #f0f2f5;
- background: #fff;
- z-index: 94;
- }
- .dropdown-search-input {
- width: 100%;
- height: 60rpx;
- background: #f5f7fa;
- border-radius: 8rpx;
- padding: 0 20rpx;
- font-size: 28rpx;
- box-sizing: border-box;
- }
- .dropdown-scroll-view {
- flex: 1;
- width: 100%;
- max-height: 260rpx;
- /* Adjusted height to fit search bar */
- overflow-y: auto;
- }
- .cascader-body .cascader-col {
- flex: 0 0 50%;
- width: 50%;
- max-height: 320rpx;
- }
- .dropdown-item {
- padding: 16rpx 24rpx;
- font-size: 28rpx;
- color: #333;
- text-wrap: wrap;
- }
- .dropdown-item + .dropdown-item {
- border-top: 1rpx solid #f0f2f5;
- }
- .table-scroll-x {
- position: relative;
- margin-top: 12rpx;
- width: 100%;
- min-height: 120rpx;
- border-radius: 16rpx;
- overflow: visible;
- }
- .card {
- background: #fff;
- font-size: 32rpx;
- }
- .table-header {
- display: flex;
- background: #eaf2ff;
- font-weight: bold;
- color: #2c69ff;
- border-top-left-radius: 16rpx;
- border-top-right-radius: 16rpx;
- }
- /* .table-body {
- margin-top: 8rpx;
- } */
- .row {
- display: flex;
- align-items: stretch;
- border-bottom: 1rpx solid #eef0f4;
- }
- .row:last-child,
- .row:nth-last-child(2) {
- border-bottom: none;
- }
- .loading-row {
- justify-content: flex-start;
- }
- .loading-wrapper {
- width: calc(100vw - 60rpx);
- height: 76rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- position: sticky;
- top: 50%;
- left: 0;
- }
- .loading-icon {
- width: 40rpx;
- height: 40rpx;
- animation: spin 1s linear infinite;
- }
- @keyframes spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
- }
- .cell {
- flex: 1;
- box-sizing: border-box;
- width: 0;
- box-sizing: border-box;
- display: flex;
- align-items: center;
- justify-content: center;
- /* text-align: center; */
- word-break: break-all;
- }
- .table-body .cell {
- background-color: #fff;
- text-align: center;
- }
- .table-body .striped {
- background: #f7f7f7;
- }
- .underline {
- text-decoration: underline;
- color: #2c69ff;
- }
- /* hide scrollbars for H5/App while preserving scroll behavior */
- .no-scrollbar {
- position: relative;
- -ms-overflow-style: none;
- scrollbar-width: none;
- z-index: 2;
- }
- ::v-deep .no-scrollbar::-webkit-scrollbar {
- display: none !important;
- width: 0 !important;
- height: 0 !important;
- -webkit-appearance: none;
- background: transparent;
- }
- .header-tooltip-wrap {
- position: absolute;
- top: 60rpx;
- right: 0rpx;
- z-index: 93;
- }
- .header-tooltip {
- background: #000;
- color: #fff;
- border-radius: 8rpx;
- padding: 8rpx 12rpx;
- font-size: 24rpx;
- width: 270rpx;
- }
- .header-tooltip-arrow {
- position: absolute;
- top: -11rpx;
- right: 20rpx;
- width: 0;
- height: 0;
- border-left: 10rpx solid transparent;
- border-right: 10rpx solid transparent;
- border-bottom: 12rpx solid #000;
- margin: 0 auto;
- }
- .no-data-placeholder {
- opacity: 0;
- }
- .no-data {
- position: absolute;
- top: 76rpx;
- left: 0;
- width: 100%;
- z-index: 1;
- }
- </style>
|