index.vue 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803
  1. <template>
  2. <view class="report-export-page">
  3. <view class="report-export-search-bar-container">
  4. <view class="report-export-search-bar">
  5. <view class="report-export-search-input-wrap">
  6. <image src="../../../static/images/search.png" mode="scaleToFill" class="report-export-search-icon" />
  7. <input class="report-export-search-input" v-model="keyword" placeholder="搜索公司" />
  8. <view class="report-export-search-btn" @click="onSearch">搜索</view>
  9. </view>
  10. <view class="report-export-filter-btn" @click="onFilter">
  11. <image src="../../../static/images/filter.png" mode="scaleToFill" class="report-export-filter-icon" />
  12. <text class="report-export-filter-text">筛选</text>
  13. </view>
  14. </view>
  15. </view>
  16. <view class="scan-rate-ranges-container">
  17. <view class="scan-rate-ranges">
  18. <view class="scan-rate-ranges-content">
  19. <view v-for="(value, i) in options.status" :key="value" class="scan-rate-range" :class="{
  20. 'scan-rate-active': filterForm.status == value,
  21. }" @click.stop="pickFilterOption('status', value)">{{ value }}</view>
  22. </view>
  23. </view>
  24. </view>
  25. <view v-if="loading" class="report-export-list-scroll" :style="{ height: 'auto' }">
  26. <view class="report-export-loading-row">
  27. <view class="report-export-loading-wrapper">
  28. <image class="report-export-loading-icon" src="../../../static/images/loading.png" />
  29. </view>
  30. </view>
  31. </view>
  32. <scroll-view v-else-if="list.length > 0" class="report-export-list-scroll" scroll-y="true"
  33. :style="{ height: listViewHeight }" @scrolltolower="onScrollToLower">
  34. <view class="report-export-item-card" v-for="(item, i) in list" :key="item.id">
  35. <view class="report-export-item-header">
  36. <text class="report-export-item-title" @click="showModal(item)">{{ getCustomerName(item) }}报表</text>
  37. <text class="report-export-status" :class="'report-export-status-' + item.status">{{
  38. getStatusText(item.status) }}</text>
  39. </view>
  40. <view class="report-export-item-row">品种:{{ item.physicName || "" }}</view>
  41. <view class="report-export-item-row">规格:{{ item.pkgSpec || "" }}</view>
  42. <view class="report-export-item-row">时间:{{ formatDate(item.beginTime) }}--{{
  43. formatDate(item.endTime)
  44. }}</view>
  45. <view v-if="item.status == 2" class="report-export-progress-wrap">
  46. <view class="report-export-progress">
  47. <view class="report-export-progress-fill" :style="{ width: 60 + '%' }"></view>
  48. <text class="report-export-progress-text">{{ 60 }}</text>
  49. </view>
  50. </view>
  51. <view v-if="item.status == 1" class="report-export-action-row">
  52. <view class="report-export-action-btn report-export-primary" @click="onStart(item)">
  53. 确认开始计算
  54. </view>
  55. </view>
  56. <view v-else-if="item.status == 3" class="report-export-action-row">
  57. <view class="report-export-action-btn report-export-info" @click="toDetail(item)">查看</view>
  58. <view class="report-export-action-btn report-export-primary" @click="onSend(item)">
  59. 导出
  60. </view>
  61. </view>
  62. </view>
  63. <view class="report-export-loading-row" v-if="hasmore">
  64. <view class="report-export-loading-wrapper">
  65. <image class="report-export-loading-icon" src="../../../static/images/loading.png" />
  66. </view>
  67. </view>
  68. </scroll-view>
  69. <EmptyView v-else text="暂无导出任务" />
  70. <view class="report-export-footer">
  71. <view class="report-export-footer-btn" @click="onCreate">新建任务</view>
  72. </view>
  73. <view v-show="createModalOpen || filterModalOpen || emailModalOpen" class="report-export-create-modal-mask"
  74. @click="closeCreate">
  75. </view>
  76. <view class="report-export-create-modal" :class="{ 'report-export-create-modal--open': createModalOpen }">
  77. <view class="report-export-create-modal-title" @click="closeDropdownAll">
  78. <text>{{ taskTitle }}</text>
  79. <view class="report-export-create-modal-close" @click.stop="closeCreate">×</view>
  80. </view>
  81. <view class="report-export-create-modal-body" :style="{ paddingBottom: 0 }" @click="closeDropdownAll">
  82. <scroll-view scroll-y="true" :style="{
  83. height: '750rpx',
  84. paddingBottom: '20rpx',
  85. }">
  86. <view class="report-export-create-modal-row">
  87. <text class="report-export-create-modal-label">省份</text>
  88. <view class="report-export-create-modal-inputwrap"
  89. @click.stop="modalType !== 'read' && openDropdown('region')">
  90. <view class="report-export-create-modal-static">{{
  91. form.region && form.region.regionName
  92. ? form.region.regionName
  93. : "请选择省份"
  94. }}</view>
  95. <view class="report-export-input-arrow" v-if="modalType !== 'read'"></view>
  96. <view class="report-export-create-modal-dropdown" v-if="dropdown.region" @click.stop
  97. :style="dropdownPosStyle()">
  98. <view class="report-export-dropdown-search-row">
  99. <input class="report-export-dropdown-search-input" v-model="searchForm.region"
  100. :disabled="modalType === 'read'" @input="modalType !== 'read' && onInput('region')"
  101. @focus="onSearchFocus" @blur="onSearchBlur" />
  102. </view>
  103. <scroll-view class="report-export-create-modal-dropdown-list" scroll-y>
  104. <view class="report-export-create-modal-dropdown-item" v-for="(op, i) in filteredOptions('region')"
  105. :key="'r-' + i" @click.stop="modalType !== 'read' && pickOption('region', op)">{{ op.regionName }}
  106. </view>
  107. <view v-if="!filteredOptions('region') || filteredOptions('region').length === 0"
  108. class="report-export-create-modal-dropdown-item">
  109. 未查找到该省份</view>
  110. </scroll-view>
  111. </view>
  112. </view>
  113. </view>
  114. <view class="report-export-create-modal-row">
  115. <text class="report-export-create-modal-label">客户类型</text>
  116. <view class="report-export-create-modal-inputwrap"
  117. @click.stop="modalType !== 'read' && openDropdown('customerType')">
  118. <view class="report-export-create-modal-static">{{
  119. form.customerType || "请选择客户类型"
  120. }}</view>
  121. <view class="report-export-input-arrow" v-if="modalType !== 'read'"></view>
  122. <view class="report-export-create-modal-dropdown" v-if="dropdown.customerType" @click.stop
  123. :style="dropdownPosStyle()">
  124. <view class="report-export-create-modal-dropdown-item"
  125. v-for="(op, i) in filteredOptions('customerType')" :key="'t-' + i" @click.stop="
  126. modalType !== 'read' && pickOption('customerType', op)
  127. ">{{ op }}</view>
  128. <view v-if="filteredOptions('customerType').length === 0"
  129. class="report-export-create-modal-dropdown-item">未查找到该客户类型</view>
  130. </view>
  131. </view>
  132. </view>
  133. <view class="report-export-create-modal-row">
  134. <text class="report-export-create-modal-label">客户</text>
  135. <view class="report-export-create-modal-inputwrap"
  136. @click.stop="modalType !== 'read' && openDropdown('customer')">
  137. <view class="report-export-create-modal-static" :style="{ maxHeight: 'auto', overflowY: 'visible' }">
  138. <block v-if="Array.isArray(form.customer) && form.customer.length">
  139. <view :style="{
  140. maxHeight: '100rpx',
  141. overflowY: 'auto',
  142. paddingRight: '40rpx',
  143. }">
  144. <text v-for="(op, i) in form.customer" :key="'cust-tag-' + i" class="customer-chip">
  145. {{ op.customerName }}
  146. <text class="customer-chip-close" @click.stop="
  147. modalType !== 'read' &&
  148. removeCustomer(op.customerName)
  149. ">×</text>
  150. </text>
  151. </view>
  152. </block>
  153. <text v-else>请选择客户</text>
  154. <text v-if="
  155. form.customer &&
  156. form.customer.length &&
  157. modalType !== 'read'
  158. " class="customer-clear-all" @click.stop="modalType !== 'read' && clearAllCustomers()">×</text>
  159. </view>
  160. <view class="report-export-input-arrow" v-if="modalType !== 'read'"></view>
  161. <view class="report-export-create-modal-dropdown" v-if="dropdown.customer" @click.stop :style="[
  162. dropdownPosStyle(),
  163. { maxHeight: 'none', overflow: 'visible' },
  164. ]">
  165. <view class="report-export-dropdown-search-row">
  166. <input class="report-export-dropdown-search-input" v-model="customerSearch"
  167. :disabled="modalType === 'read'" @input="modalType !== 'read' && onInput('customer')"
  168. @focus="onSearchFocus" @blur="onSearchBlur" />
  169. </view>
  170. <scroll-view class="report-export-create-modal-dropdown-list" scroll-y lower-threshold="20"
  171. @scrolltolower="getCustomerOptions">
  172. <view class="report-export-create-modal-dropdown-item" :class="{
  173. 'report-export-selected': isCustomerSelected(
  174. op.customerName
  175. ),
  176. }" v-for="(op, i) in customers" :key="'cust-' + i" @click.stop="
  177. modalType !== 'read' && pickOption('customer', op)
  178. ">{{ op.customerName }}</view>
  179. <view v-if="customers.length === 0 && !customerHasMore"
  180. class="report-export-create-modal-dropdown-item">未查找到该客户</view>
  181. <view v-if="customerHasMore" class="report-export-loading-row">
  182. <view class="report-export-loading-wrapper">
  183. <image class="report-export-loading-icon" src="../../../static/images/loading.png" />
  184. </view>
  185. </view>
  186. </scroll-view>
  187. </view>
  188. </view>
  189. </view>
  190. <view class="report-export-create-modal-row">
  191. <text class="report-export-create-modal-label">其它客户</text>
  192. <view class="report-export-create-modal-inputwrap">
  193. <input class="report-export-dropdown-search-input" v-model="form.otherCustomer"
  194. placeholder="请填写(多个客户用半角逗号隔开)" placeholder-class="report-export-dropdown-other-customer-input"
  195. :disabled="modalType === 'read'" :style="{ color: '#666', padding: 0, border: 'none' }" />
  196. </view>
  197. </view>
  198. <view class="report-export-create-modal-row">
  199. <text class="report-export-create-modal-label">品种</text>
  200. <view class="report-export-create-modal-inputwrap"
  201. @click.stop="modalType !== 'read' && openDropdown('product')">
  202. <view class="report-export-create-modal-static">{{
  203. form.product?.physicName || "请选择品种"
  204. }}</view>
  205. <view class="report-export-input-arrow" v-if="modalType !== 'read'"></view>
  206. <view class="report-export-create-modal-dropdown" :style="[{ top: 'auto', bottom: '84rpx' }]"
  207. v-if="dropdown.product" @click.stop>
  208. <view class="report-export-dropdown-search-row">
  209. <input class="report-export-dropdown-search-input" v-model="searchForm.product"
  210. :disabled="modalType === 'read'" @input="modalType !== 'read' && onInput('product')"
  211. @focus="onSearchFocus" @blur="onSearchBlur" />
  212. </view>
  213. <scroll-view class="report-export-create-modal-dropdown-list" scroll-y
  214. @scrolltolower="loadMoreProducts('create')">
  215. <view class="report-export-create-modal-dropdown-item" v-for="(op, i) in displayProducts"
  216. :key="'p-' + i" @click.stop="
  217. modalType !== 'read' && pickOption('product', op)
  218. ">{{ op.physicName }}
  219. </view>
  220. <view v-if="displayProducts?.length === 0" class="report-export-create-modal-dropdown-item">
  221. 未查找到该品种</view>
  222. </scroll-view>
  223. </view>
  224. </view>
  225. </view>
  226. <view class="report-export-create-modal-row">
  227. <text class="report-export-create-modal-label">规格</text>
  228. <view class="report-export-create-modal-inputwrap"
  229. :class="{ 'report-export-input-disabled': !form.product?.drugEntBaseInfoId }"
  230. @click.stop="modalType !== 'read' && form.product?.drugEntBaseInfoId && openDropdown('pkgSpec')">
  231. <view class="report-export-create-modal-static">{{
  232. form.pkgSpec || "请选择规格(请先选择品种)"
  233. }}</view>
  234. <view class="report-export-input-arrow" v-if="modalType !== 'read'"></view>
  235. <view class="report-export-create-modal-dropdown" v-if="dropdown.pkgSpec" @click.stop
  236. :style="[{ top: 'auto', bottom: '84rpx' }]">
  237. <view class="report-export-create-modal-dropdown-item" v-for="(op, i) in filteredOptions('pkgSpec')"
  238. :key="'t-' + i" @click.stop="
  239. modalType !== 'read' && pickOption('pkgSpec', op)
  240. ">{{ op }}</view>
  241. <view v-if="filteredOptions('pkgSpec')?.length === 0" class="report-export-create-modal-dropdown-item">
  242. 未查找到该品种规格</view>
  243. </view>
  244. </view>
  245. </view>
  246. <view class="report-export-create-modal-row">
  247. <text class="report-export-create-modal-label">时间</text>
  248. <view class="report-export-create-modal-inputwrap" @click="openDatePicker">
  249. <view class="report-export-create-modal-static report-export-input-simulator"
  250. :style="{ color: dateRange && dateRange.length ? '#333' : '#606266' }">
  251. <uni-icons type="calendar" size="22" color="#c0c4cc"></uni-icons>
  252. <view style="flex: 1; text-align: center;">{{ dateRange && dateRange.length ? dateRange[0] : '开始时间' }}
  253. </view>
  254. <view>-</view>
  255. <view style="flex: 1; text-align: center;">{{ dateRange && dateRange.length ? dateRange[1] : '结束时间' }}
  256. </view>
  257. </view>
  258. <view class="report-export-input-simulator-clear" v-if="dateRange && dateRange.length"
  259. @click.stop="dateRange = []">
  260. <uni-icons type="clear" size="22" color="#c0c4cc"></uni-icons>
  261. </view>
  262. </view>
  263. </view>
  264. </scroll-view>
  265. <!-- Hidden Picker for API access -->
  266. <view style="position: absolute; left: -10000px; top: 0;">
  267. <uni-datetime-picker ref="datePicker" type="daterange" v-model="dateRange" @change="onDateChange" />
  268. </view>
  269. </view>
  270. <view class="report-export-create-modal-footer" @click="closeDropdownAll">
  271. <view class="report-export-create-modal-btn" @click.stop="confirmCreate" v-if="modalType !== 'read'">{{
  272. modalType === "create" ? "新建任务" : "修改任务" }}</view>
  273. <view :style="{ display: 'flex', 'justify-content': 'space-between' }" v-else>
  274. <view class="report-export-create-modal-btn" :style="{ flex: '0 0 40%' }" @click.stop="closeCreate">关闭</view>
  275. <view class="report-export-create-modal-btn" :style="{ background: 'red', flex: '0 0 40%' }"
  276. @click.stop="confirmDelete">删除</view>
  277. </view>
  278. </view>
  279. </view>
  280. <view class="report-export-create-modal" :class="{ 'report-export-create-modal--open': filterModalOpen }">
  281. <view class="report-export-create-modal-title" @click="closeDropdownAll">筛选条件</view>
  282. <view class="report-export-create-modal-body" @click="closeDropdownAll">
  283. <view class="report-export-create-modal-row">
  284. <text class="report-export-create-modal-label">品种:</text>
  285. <view class="report-export-create-modal-inputwrap" @click.stop="openFilterDropdown('product')">
  286. <view class="report-export-create-modal-static">{{
  287. filterForm.product?.physicName || "请选择品种"
  288. }}</view>
  289. <view class="report-export-create-modal-dropdown" :style="[{ top: 'auto', bottom: '84rpx' }]"
  290. v-if="filterDropdown.product" @click.stop>
  291. <view class="report-export-dropdown-search-row">
  292. <input class="report-export-dropdown-search-input" v-model="filterSearchForm.product"
  293. @input="onFilterInput('product')" @focus="onSearchFocus" @blur="onSearchBlur" />
  294. </view>
  295. <scroll-view class="report-export-create-modal-dropdown-list" scroll-y
  296. @scrolltolower="loadMoreProducts('filter')">
  297. <view class="report-export-create-modal-dropdown-item" v-for="(op, i) in filterDisplayProducts"
  298. :key="'prd-' + i" @click.stop="pickFilterOption('product', op)">{{ op.physicName }}</view>
  299. <view v-if="filterDisplayProducts?.length === 0" class="report-export-create-modal-dropdown-item">
  300. 未查找到该品种
  301. </view>
  302. </scroll-view>
  303. </view>
  304. </view>
  305. </view>
  306. <!-- <view class="report-export-create-modal-row">
  307. <text class="report-export-create-modal-label">规格</text>
  308. <view class="report-export-create-modal-inputwrap"
  309. :class="{ 'report-export-input-disabled': !filterForm.product?.drugEntBaseInfoId }"
  310. @click.stop="filterForm.product?.drugEntBaseInfoId && openFilterDropdown('pkgSpec')">
  311. <view class="report-export-create-modal-static">{{
  312. filterForm.pkgSpec || "请选择品种规格(请先选择品种)"
  313. }}</view>
  314. <view class="report-export-input-arrow"></view>
  315. <view class="report-export-create-modal-dropdown" v-if="filterDropdown.pkgSpec" @click.stop
  316. :style="[{ top: 'auto', bottom: '84rpx' }]">
  317. <view class="report-export-create-modal-dropdown-item" v-for="(op, i) in filteredOptions('pkgSpec')"
  318. :key="'t-' + i" @click.stop="
  319. pickFilterOption('pkgSpec', op)
  320. ">{{ op }}</view>
  321. <view v-if="filteredOptions('pkgSpec')?.length === 0" class="report-export-create-modal-dropdown-item">
  322. 未查找到该品种规格</view>
  323. </view>
  324. </view>
  325. </view> -->
  326. <view class="report-export-create-modal-row">
  327. <text class="report-export-create-modal-label">时间:</text>
  328. <view class="report-export-create-modal-inputwrap" v-if="filterModalOpen">
  329. <uni-datetime-picker type="daterange" :border="false" v-model="filterDateRange"
  330. @change="onFilterDateChange" />
  331. </view>
  332. </view>
  333. </view>
  334. <view class="report-export-create-modal-footer" @click="closeDropdownAll">
  335. <view class="report-export-create-modal-btn" @click.stop="confirmFilter">确认</view>
  336. </view>
  337. </view>
  338. <view class="report-export-create-modal report-export-create-email-modal"
  339. :class="{ 'report-export-create-modal--open': emailModalOpen }" :style="{ transform: 'translateY(1000%)' }">
  340. <view class="report-export-create-modal-title">
  341. <text>发送至邮箱</text>
  342. <view class="report-export-create-modal-close" @click.stop="closeEmailModal">×</view>
  343. </view>
  344. <view class="report-export-create-modal-body" :style="{ padding: '40rpx' }">
  345. <!-- <view class="report-export-email-input-wrap" :class="{ 'report-export-input-error': emailError }">
  346. <input class="report-export-email-input" v-model="emailForm.email" placeholder="请填写邮箱"
  347. placeholder-style="color: #c0c4cc" @input="emailError = false" />
  348. <text class="report-export-email-suffix">@999.com.cn</text>
  349. </view> -->
  350. <up-input v-model="emailForm.email" border="none" :customStyle="{
  351. backgroundColor: '#ebf3fb',
  352. height: '80rpx',
  353. paddingLeft: '20rpx',
  354. }" placeholder="请填写邮箱">
  355. <template #suffix style="margin-right: 30rpx"> @999.com.cn </template>
  356. </up-input>
  357. <text v-if="emailError" class="report-export-error-text">请输入邮箱</text>
  358. </view>
  359. <view class="report-export-create-modal-footer" :style="{
  360. padding: '20rpx 40rpx 40rpx',
  361. display: 'flex',
  362. 'justify-content': 'space-between',
  363. }">
  364. <view class="report-export-create-modal-btn" :style="{
  365. background: '#fff',
  366. color: '#666',
  367. border: '1rpx solid #dcdfe6',
  368. flex: '0 0 45%',
  369. }" @click.stop="closeEmailModal">取消</view>
  370. <view class="report-export-create-modal-btn" :style="{ flex: '0 0 45%' }" @click.stop="handleSendEmail">发送
  371. </view>
  372. </view>
  373. </view>
  374. </view>
  375. </template>
  376. <script>
  377. import EmptyView from "../../../wigets/empty.vue";
  378. import { formatDate, getPaginatedList } from "../../../utils/utils.js";
  379. import request from "../../../request/index.js";
  380. const customerTypeMap = {
  381. VIP客户: "VIP",
  382. 二级客户: "二级",
  383. 三级客户: "三级",
  384. };
  385. const statusMap = {
  386. "全部": 0,
  387. "待开始": 1,
  388. "进行中": 2,
  389. "已完成": 3,
  390. };
  391. export default {
  392. components: {
  393. EmptyView,
  394. },
  395. data() {
  396. return {
  397. keyword: "",
  398. totalCount: 0,
  399. loading: true,
  400. hasmore: true,
  401. fetchLoading: false,
  402. pageNum: 1,
  403. pageSize: 10,
  404. list: [],
  405. listViewHeight: "400px",
  406. createModalOpen: false,
  407. form: {
  408. region: { regionName: "" },
  409. customerType: "",
  410. customer: [],
  411. product: { physicName: "" },
  412. otherCustomer: "",
  413. },
  414. dateRange: [],
  415. computeTimer: null,
  416. dropdown: {
  417. region: false,
  418. customerType: false,
  419. customer: false,
  420. product: false,
  421. pkgSpec: false,
  422. },
  423. options: {
  424. region: [],
  425. customerType: ["VIP客户", "二级客户", "三级客户"],
  426. product: [],
  427. pkgSpec: [],
  428. status: ["全部", "待开始", "进行中", "已完成"],
  429. },
  430. // dropdown search inputs (create modal)
  431. searchForm: {
  432. region: "",
  433. product: "",
  434. },
  435. // Pagination state for products
  436. productPage: 0,
  437. productPageSize: 10,
  438. displayProducts: [], // Currently displayed products
  439. // dropdown search inputs (filter modal)
  440. filterSearchForm: {
  441. product: "",
  442. },
  443. // Pagination state for filter products
  444. filterProductPage: 0,
  445. filterDisplayProducts: [],
  446. customers: [],
  447. customerPage: 1,
  448. customerPageSize: 10,
  449. customerTotal: null,
  450. customerHasMore: true,
  451. customerLoading: false,
  452. customerDebounceTimer: null,
  453. taskTitle: "创建任务",
  454. filterModalOpen: false,
  455. filterForm: {
  456. product: { physicName: "" },
  457. status: "全部",
  458. pkgSpec: "",
  459. },
  460. filterDropdown: { product: false, pkgSpec: false },
  461. filterDateRange: [],
  462. modalType: "",
  463. dropdownUpward: false,
  464. customerSearch: "",
  465. currentItem: {},
  466. emailModalOpen: false,
  467. emailForm: {
  468. email: "",
  469. },
  470. emailError: false,
  471. };
  472. },
  473. created() {
  474. const userInfo = uni.getStorageSync("userInfo");
  475. const staffEmail = userInfo?.staffEmail || "";
  476. this.emailForm.email = staffEmail.split("@")[0];
  477. console.log("reportExport\index.vue,storage,userInfo", userInfo);
  478. this.initList();
  479. this.getProviceList();
  480. this.getDrugInfoName();
  481. this.fetchList();
  482. try {
  483. uni.onKeyboardHeightChange(({ height }) => {
  484. this.dropdownUpward = height > 0;
  485. });
  486. } catch (e) { }
  487. },
  488. computed: {
  489. taskTitle() {
  490. if (this.modalType === "create") {
  491. return "创建任务";
  492. } else if (this.modalType == "edit") {
  493. return "修改任务";
  494. } else if (this.modalType == "read") {
  495. return "查看任务";
  496. }
  497. },
  498. },
  499. watch: {
  500. "form.customerType"(val) {
  501. this.customerPage = 1;
  502. this.customerTotal = null;
  503. this.customerHasMore = true;
  504. this.customers = [];
  505. if (this.customerDebounceTimer) clearTimeout(this.customerDebounceTimer);
  506. this.getCustomerOptions();
  507. },
  508. "form.region": {
  509. handler() {
  510. this.customerPage = 1;
  511. this.customerTotal = null;
  512. this.customerHasMore = true;
  513. this.customers = [];
  514. if (this.customerDebounceTimer)
  515. clearTimeout(this.customerDebounceTimer);
  516. this.getCustomerOptions();
  517. },
  518. deep: true,
  519. },
  520. "emailForm.email": {
  521. handler(newVal, oldVal) {
  522. if (!oldVal && !newVal && newVal !== oldVal) {
  523. this.emailError = false;
  524. } else {
  525. this.emailError = !newVal;
  526. }
  527. },
  528. deep: true,
  529. },
  530. "filterForm.status"(val) {
  531. this.resetFetch()
  532. }
  533. },
  534. methods: {
  535. getCustomerName(data = {}) {
  536. const { customerName = "", otherCustomer = "" } = data;
  537. let name = customerName || "";
  538. if (otherCustomer) {
  539. if (name) {
  540. name += `,${otherCustomer}`;
  541. } else {
  542. name = otherCustomer;
  543. }
  544. }
  545. const arr = name.split(",");
  546. if (arr.length == 0) return "--";
  547. return arr.length == 1 ? arr[0].trim() : `${arr.length}个公司`;
  548. },
  549. formatDate(date) {
  550. return formatDate({ date }, "YYYY-MM-DD");
  551. },
  552. getStatusText(status) {
  553. if (status == "1") {
  554. return "导出待开始";
  555. } else if (status == "2") {
  556. return "导出进行中";
  557. } else if (status == "3" || status == "4") {
  558. return "导出已完成";
  559. }
  560. return "未知状态";
  561. },
  562. getDrugInfoName() {
  563. request("/bills/getDrugInfoName", {
  564. path: "/reportExport/index.vue",
  565. }).then((res) => {
  566. if (res.code == 200) {
  567. const _data = res.data;
  568. this.options.product = _data;
  569. }
  570. });
  571. },
  572. getGuigeList(physicName) {
  573. if (!physicName) return;
  574. request("/report/getDrugSpec", { physicName, path: "/reportExport/index.vue" }).then(res => {
  575. if (res.code == 200) {
  576. this.options.pkgSpec = res.data || [];
  577. }
  578. });
  579. },
  580. getProviceList() {
  581. request("/common/getProviceList", {
  582. path: "/reportExport/index.vue",
  583. }).then((res) => {
  584. if (res.code == 200) {
  585. const _data = res.data;
  586. this.options.region = _data;
  587. }
  588. });
  589. },
  590. getCustomerOptions() {
  591. if (!this.customerHasMore || this.customerLoading) return;
  592. this.customerLoading = true;
  593. request("/customer/info/getCustomerInfo", {
  594. pageNum: this.customerPage,
  595. pageSize: this.customerPageSize,
  596. customerName: this.customerSearch || "",
  597. customerLevel: customerTypeMap?.[this.form.customerType] || undefined,
  598. provinceCode: this.form?.region?.regionCode || undefined,
  599. path: "/reportExport/index.vue",
  600. }).then((res) => {
  601. if (res.code == 200) {
  602. const _data = res.data || {};
  603. this.customerTotal = _data.total || 0;
  604. const list = Array.isArray(_data.list) ? _data.list : [];
  605. this.customers = [...this.customers, ...list];
  606. if (this.customers.length >= this.customerTotal) {
  607. this.customerHasMore = false;
  608. } else {
  609. this.customerPage++;
  610. this.customerHasMore = true;
  611. }
  612. }
  613. this.customerLoading = false;
  614. });
  615. },
  616. initList() {
  617. const info = uni.getSystemInfoSync();
  618. const h = info.windowHeight || 800;
  619. this.listViewHeight = Math.max(0, h - 280) + "px";
  620. // this.list = this.genItems(Math.min(10, this.totalCount));
  621. // this.animateAll();
  622. },
  623. onFilter() {
  624. this.filterModalOpen = true;
  625. },
  626. showModal(item = {}) {
  627. const map = {
  628. 1: "edit",
  629. 2: "read",
  630. 3: "read",
  631. };
  632. this.modalType = map[item?.status] || "create";
  633. if (this.modalType === "create") {
  634. this.taskTitle = "创建任务";
  635. } else {
  636. if (this.modalType == "edit") {
  637. this.taskTitle = "修改任务";
  638. } else if (this.modalType == "read") {
  639. this.taskTitle = "查看任务";
  640. }
  641. const cNames = item.customerName.split(",")
  642. const cCodes = (item.customerId || '').split(",")
  643. this.form = {
  644. customer: cCodes.map((i, index) => ({
  645. customerName: cNames[index],
  646. customerCode: i,
  647. })),
  648. region: {
  649. regionCode: item.regionCode,
  650. regionName: item.regionName,
  651. },
  652. customerType:
  653. item.customerType == 1
  654. ? "VIP客户"
  655. : item.customerType == 2
  656. ? "二级客户"
  657. : item.customerType == 3
  658. ? "三级客户"
  659. : "",
  660. product: {
  661. physicName: item.physicName,
  662. drugEntBaseInfoId: item.drugEntBaseInfoId,
  663. },
  664. pkgSpec: item?.pkgSpec || "",
  665. otherCustomer: item?.otherCustomer || "",
  666. };
  667. if (item.physicName) {
  668. this.getGuigeList(item.physicName);
  669. }
  670. this.dateRange = [item.beginTime, item.endTime];
  671. }
  672. this.createModalOpen = true;
  673. this.currentItem = item;
  674. },
  675. fetchList() {
  676. // this.list = [];
  677. // setTimeout(() => {
  678. // const n = Math.min(10, this.totalCount || 10);
  679. // this.list = this.genItems(n);
  680. // if (!this.totalCount) this.totalCount = this.list.length;
  681. // this.animateAll();
  682. // this.fetchLoading = false;
  683. // }, 1000);
  684. if (!this.hasmore) {
  685. setTimeout(() => {
  686. this.fetchLoading = false;
  687. this.loading = false;
  688. }, 500);
  689. return;
  690. }
  691. if (this.fetchLoading) return;
  692. this.fetchLoading = true;
  693. request("/report/getReportTaskPage", {
  694. reportType: 2,
  695. taskType: statusMap?.[this.filterForm?.status] || "",
  696. drugEntBaseInfoId: this.filterForm?.product?.drugEntBaseInfoId,
  697. pkgSpec: this.filterForm?.pkgSpec || "",
  698. beginTime: this.filterDateRange[0],
  699. endTime: this.filterDateRange[1],
  700. customerName: this.keyword || "",
  701. pageNum: this.pageNum,
  702. pageSize: this.pageSize,
  703. path: "reportExport/index.vue",
  704. }).then((res) => {
  705. if (res.code == 200) {
  706. const _data = res.data || {};
  707. const { list, total } = _data;
  708. this.totalCount = total || 0;
  709. this.list = Array.isArray(list)
  710. ? [...this.list, ...list]
  711. : [...this.list];
  712. if (this.list.length < this.totalCount) {
  713. this.hasmore = true;
  714. this.pageNum++;
  715. } else {
  716. this.hasmore = false;
  717. }
  718. // this.list.push({
  719. // ...this.list[1],
  720. // status: 3,
  721. // });
  722. // this.list.push({
  723. // ...this.list[0],
  724. // finishTime: "2025-12-15",
  725. // status: 3,
  726. // });
  727. // this.animateAll();
  728. }
  729. this.fetchLoading = false;
  730. this.loading = false;
  731. });
  732. },
  733. resetFetch() {
  734. this.loading = true;
  735. this.list = [];
  736. this.pageNum = 1;
  737. this.hasmore = true;
  738. this.fetchList();
  739. },
  740. toDetail(item) {
  741. uni.navigateTo({
  742. url: `/traceCodePackages/traceabilityReport/pages/reportExport/detail/index?id=${item.id
  743. }&title=${encodeURIComponent(
  744. this.getCustomerName(item.customerName)
  745. )}`,
  746. });
  747. },
  748. onSearch() {
  749. this.resetFetch();
  750. },
  751. onCreate() {
  752. this.modalType = "create";
  753. this.taskTitle = "创建任务";
  754. this.createModalOpen = true;
  755. },
  756. closeCreate() {
  757. this.filterModalOpen = false;
  758. this.createModalOpen = false;
  759. this.closeDropdownAll();
  760. this.form = {
  761. region: { regionName: "" },
  762. customerType: "",
  763. customer: [],
  764. product: { physicName: "" },
  765. otherCustomer: "",
  766. };
  767. this.searchForm = {
  768. region: "",
  769. product: "",
  770. };
  771. this.dateRange = [];
  772. if (this.computeTimer) {
  773. clearTimeout(this.computeTimer);
  774. this.computeTimer = null;
  775. }
  776. },
  777. confirmCreate() {
  778. let flag = false;
  779. Object.values(this.dropdown).forEach((v) => {
  780. if (v) {
  781. flag = true;
  782. }
  783. });
  784. if (flag) {
  785. this.closeDropdownAll();
  786. return;
  787. }
  788. if (!this.isFormComplete()) {
  789. // uni.showToast({ title: "请完整填写任务内容", icon: "none" });
  790. return;
  791. }
  792. // uni.showToast({ title: "已创建", icon: "none" });
  793. uni.showLoading({
  794. title: this.modalType === "create" ? "创建中..." : "修改中...",
  795. });
  796. const url =
  797. this.modalType === "create" ? `/report/save` : `/report/update`;
  798. const customerId = (this.form?.customer || [])
  799. .map((v) => v?.customerCode || "")
  800. .join(",");
  801. const customerName = (this.form?.customer || [])
  802. .map((v) => v?.customerName || "")
  803. .join(",");
  804. request(url, {
  805. id:
  806. this.modalType === "create" ? undefined : this.currentItem?.id || "",
  807. reportType: 2,
  808. regionCode: this.form?.region?.regionCode || "",
  809. customerType: customerTypeMap?.[this.form?.customerType] || "",
  810. customerId,
  811. customerName,
  812. otherCustomer: this.form?.otherCustomer || "",
  813. drugEntBaseInfoId: this.form?.product?.drugEntBaseInfoId || "",
  814. pkgSpec: this.form?.pkgSpec || "",
  815. physicName: this.form?.product?.physicName || "",
  816. beginTime: this.formatDate(this.dateRange[0], 'YYYY-MM-DD'),
  817. endTime: this.formatDate(this.dateRange[1], 'YYYY-MM-DD'),
  818. path: "",
  819. }).then((res) => {
  820. if (res.code == 200) {
  821. uni.hideLoading();
  822. uni.showToast({
  823. title: this.modalType === "create" ? "已创建" : "已修改",
  824. icon: "none",
  825. });
  826. this.resetFetch();
  827. this.closeCreate();
  828. }
  829. });
  830. },
  831. isFormComplete() {
  832. const f = this.form;
  833. const drOk =
  834. Array.isArray(this.dateRange) &&
  835. this.dateRange.length === 2 &&
  836. this.dateRange[0] &&
  837. this.dateRange[1];
  838. let customerOk = Array.isArray(f.customer)
  839. ? f.customer.length > 0
  840. : !!f.customer;
  841. customerOk = customerOk || f.otherCustomer
  842. if (!customerOk) {
  843. uni.showToast({ title: "请选择至少一个客户或其他客户", icon: "none" });
  844. return false
  845. }
  846. if (!f.product?.physicName) {
  847. uni.showToast({ title: "请选择品种", icon: "none" });
  848. return false
  849. }
  850. if (!drOk) {
  851. uni.showToast({ title: "请选择时间范围", icon: "none" });
  852. return false
  853. }
  854. return true
  855. // return (
  856. // f.region?.regionName &&
  857. // f.customerType &&
  858. // customerOk &&
  859. // f.product?.physicName &&
  860. // drOk
  861. // );
  862. },
  863. confirmDelete() {
  864. uni.showModal({
  865. title: "确认删除吗?",
  866. success: (res) => {
  867. if (res.confirm) {
  868. uni.showLoading();
  869. request("/report/delete", {
  870. id: this.currentItem?.id || "",
  871. path: "reportExport/index.vue",
  872. }).then((res) => {
  873. uni.hideLoading();
  874. if (res.code == 200) {
  875. uni.showToast({
  876. title: "删除成功",
  877. icon: "none",
  878. duration: 2000,
  879. });
  880. this.resetFetch();
  881. this.closeCreate();
  882. } else {
  883. uni.showToast({
  884. title: res?.msg || "删除失败",
  885. icon: "none",
  886. duration: 2000,
  887. });
  888. }
  889. });
  890. }
  891. },
  892. });
  893. },
  894. onInput(k) {
  895. this.closeDropdownAll();
  896. this.dropdown[k] = true;
  897. if (k === "customer") {
  898. this.customerPage = 1;
  899. this.customerTotal = null;
  900. this.customerHasMore = true;
  901. this.customers = [];
  902. if (this.customerDebounceTimer)
  903. clearTimeout(this.customerDebounceTimer);
  904. this.customerDebounceTimer = setTimeout(() => {
  905. this.getCustomerOptions();
  906. }, 500);
  907. return;
  908. }
  909. if (k === "product") {
  910. this.initProductList('create');
  911. }
  912. },
  913. closeDropdownAll() {
  914. this.dropdown = {
  915. region: false,
  916. customerType: false,
  917. customer: false,
  918. product: false,
  919. pkgSpec: false,
  920. };
  921. this.filterDropdown.product = false;
  922. this.filterDropdown.pkgSpec = false;
  923. },
  924. openFilterDropdown(k) {
  925. this.closeDropdownAll();
  926. this.filterDropdown[k] = true;
  927. if (k === "product") {
  928. this.initProductList('filter');
  929. }
  930. },
  931. toggleFilterDropdown(k) {
  932. this.filterDropdown[k] = !this.filterDropdown[k];
  933. },
  934. onFilterInput(k) {
  935. this.closeDropdownAll();
  936. this.filterDropdown[k] = true;
  937. if (k === "product") {
  938. this.initProductList('filter');
  939. }
  940. },
  941. pickFilterOption(k, v) {
  942. if (!["status"].includes(k) && this.filterForm[k] === v) {
  943. this.filterForm[k] = "";
  944. this.filterDropdown[k] = false;
  945. return;
  946. }
  947. this.filterForm[k] = v;
  948. if (k === "product") {
  949. this.filterForm.pkgSpec = "";
  950. this.getGuigeList(v.physicName);
  951. }
  952. this.filterDropdown[k] = false;
  953. },
  954. filteredFilterOptions(k) {
  955. const arr = k === "product" ? this.products || [] : [];
  956. const q = String(this.filterForm[k] || "").trim();
  957. if (!q) return arr;
  958. return arr.filter((it) => it.indexOf(q) !== -1);
  959. },
  960. onFilterDateChange() { },
  961. confirmFilter() {
  962. this.filterModalOpen = false;
  963. },
  964. openDropdown(k) {
  965. this.closeDropdownAll();
  966. this.dropdown[k] = true;
  967. if (k === "customer") {
  968. if (!this.customers || this.customers.length === 0) {
  969. this.customerPage = 1;
  970. this.customerTotal = null;
  971. this.customerHasMore = true;
  972. this.getCustomerOptions();
  973. }
  974. }
  975. if (k === "product") {
  976. this.initProductList('create');
  977. }
  978. },
  979. toggleDropdown(k) {
  980. this.dropdown[k] = !this.dropdown[k];
  981. },
  982. pickOption(k, v) {
  983. if (k === "customer") {
  984. if (!Array.isArray(this.form.customer)) this.form.customer = [];
  985. const idx = this.form.customer.indexOf(v);
  986. if (idx === -1) {
  987. this.form.customer.push(v);
  988. } else {
  989. this.form.customer.splice(idx, 1);
  990. }
  991. return;
  992. }
  993. this.form[k] = v;
  994. if (k === "product") {
  995. this.form.pkgSpec = "";
  996. this.getGuigeList(v.physicName);
  997. }
  998. this.dropdown[k] = false;
  999. },
  1000. isCustomerSelected(name) {
  1001. const arr = this.form.customer;
  1002. if (!Array.isArray(arr)) return false;
  1003. return arr.some((i) => i.customerName === name);
  1004. },
  1005. removeCustomer(name) {
  1006. if (!Array.isArray(this.form.customer)) return;
  1007. const idx = this.form.customer.findIndex((i) => i.customerName === name);
  1008. if (idx !== -1) this.form.customer.splice(idx, 1);
  1009. },
  1010. clearAllCustomers() {
  1011. this.form.customer = [];
  1012. },
  1013. filteredOptions(k, type) {
  1014. const arr = this.options[k] || [];
  1015. let q = "";
  1016. if (type === "filter") {
  1017. q = this.filterSearchForm[k] || "";
  1018. } else {
  1019. q = this.searchForm[k] || "";
  1020. }
  1021. q = String(q).trim().toLocaleLowerCase();
  1022. if (!q) return arr;
  1023. return arr.filter((it) => {
  1024. if (typeof it === "string")
  1025. return it.toLocaleLowerCase().indexOf(q) !== -1;
  1026. if (it && typeof it === "object") {
  1027. const name =
  1028. k === "region"
  1029. ? it.regionName || it.name || ""
  1030. : it.physicName || it.productName || "";
  1031. return String(name).toLocaleLowerCase().indexOf(q) !== -1;
  1032. }
  1033. return false;
  1034. });
  1035. },
  1036. // Initialize product list for pagination
  1037. initProductList(mode) {
  1038. const allProducts = this.filteredOptions('product', mode === 'filter' ? 'filter' : undefined);
  1039. const res = getPaginatedList({
  1040. page: 0,
  1041. size: 10,
  1042. initData: allProducts,
  1043. data: []
  1044. });
  1045. if (mode === 'filter') {
  1046. this.filterProductPage = res.page;
  1047. this.filterDisplayProducts = res.data;
  1048. } else {
  1049. this.productPage = res.page;
  1050. this.displayProducts = res.data;
  1051. }
  1052. },
  1053. // Load more products on scroll
  1054. loadMoreProducts(mode) {
  1055. const allProducts = this.filteredOptions('product', mode === 'filter' ? 'filter' : undefined);
  1056. const isFilter = mode === 'filter';
  1057. const currentPage = isFilter ? this.filterProductPage : this.productPage;
  1058. const currentData = isFilter ? this.filterDisplayProducts : this.displayProducts;
  1059. const res = getPaginatedList({
  1060. page: currentPage,
  1061. size: 10,
  1062. initData: allProducts,
  1063. data: currentData
  1064. });
  1065. if (res.data) {
  1066. if (isFilter) {
  1067. this.filterProductPage = res.page;
  1068. this.filterDisplayProducts = res.data;
  1069. } else {
  1070. this.productPage = res.page;
  1071. this.displayProducts = res.data;
  1072. }
  1073. }
  1074. },
  1075. onSearchFocus() {
  1076. this.dropdownUpward = true;
  1077. },
  1078. onSearchBlur() {
  1079. this.dropdownUpward = false;
  1080. },
  1081. dropdownPosStyle() {
  1082. // if (this.dropdownUpward) {
  1083. // return { bottom: "84rpx" };
  1084. // }
  1085. return { top: "84rpx" };
  1086. },
  1087. openDatePicker() {
  1088. if (this.modalType === 'read') return;
  1089. this.$refs.datePicker.show();
  1090. },
  1091. onDateChange() { },
  1092. onStart(item) {
  1093. uni.showModal({
  1094. title: "是否开始导出报表?",
  1095. success: (res) => {
  1096. if (res.confirm) {
  1097. uni.showLoading();
  1098. request("/report/confirm", {
  1099. id: item?.id || "",
  1100. path: "reportExport/index.vue",
  1101. }).then((res) => {
  1102. uni.hideLoading();
  1103. if (res.code == 200) {
  1104. uni.showToast({
  1105. title: "已开始导出",
  1106. icon: "none",
  1107. duration: 2000,
  1108. });
  1109. this.resetFetch();
  1110. this.closeCreate();
  1111. } else {
  1112. uni.showToast({
  1113. title: res?.msg || "请求失败",
  1114. icon: "none",
  1115. duration: 2000,
  1116. });
  1117. }
  1118. });
  1119. }
  1120. },
  1121. });
  1122. },
  1123. onSend(item) {
  1124. this.currentItem = item;
  1125. this.emailError = false;
  1126. this.emailModalOpen = true;
  1127. },
  1128. closeEmailModal() {
  1129. this.emailModalOpen = false;
  1130. },
  1131. handleSendEmail() {
  1132. const email = String(this.emailForm.email || '').trim()
  1133. if (!email) {
  1134. this.emailError = true;
  1135. return;
  1136. }
  1137. this.closeEmailModal();
  1138. uni.showLoading({
  1139. title: "发送中...",
  1140. });
  1141. const url = "/report/sendEmail";
  1142. request(url, {
  1143. taskId: this.currentItem?.id,
  1144. emailAddress: email + "@999.com.cn",
  1145. path: "reportExport/index.vue",
  1146. }).then((res) => {
  1147. uni.hideLoading();
  1148. if (res.code === 200) {
  1149. uni.showToast({
  1150. title: "发送成功",
  1151. icon: "none",
  1152. duration: 2000,
  1153. });
  1154. } else {
  1155. uni.showToast({
  1156. title: res?.msg || "发送失败",
  1157. icon: "none",
  1158. duration: 2000,
  1159. });
  1160. }
  1161. });
  1162. },
  1163. onScrollToLower() {
  1164. // if (this.isLoading) return;
  1165. // if (this.list.length >= this.totalCount) return;
  1166. // this.isLoading = true;
  1167. // const remain = this.totalCount - this.list.length;
  1168. // const toAdd = Math.min(10, remain);
  1169. // setTimeout(() => {
  1170. // const more = this.genItems(toAdd);
  1171. // this.list = this.list.concat(more);
  1172. // this.animateNew(more);
  1173. // this.isLoading = false;
  1174. // }, 800);
  1175. this.fetchList();
  1176. },
  1177. genItems(n) {
  1178. const res = [];
  1179. const base = this.list.length;
  1180. for (let i = 0; i < n; i++) {
  1181. const idx = base + i + 1;
  1182. const statusIdx = idx % 3;
  1183. const target = [60, 0, 100][statusIdx];
  1184. const sText = ["导出进行中", "导出待开始", "导出已完成"][statusIdx];
  1185. const sClass = ["ing", "wait", "done"][statusIdx];
  1186. const action = ["", "start", "done"][statusIdx];
  1187. res.push({
  1188. title: `第${idx}条公司报表`,
  1189. product: "感冒灵",
  1190. range: "2025-11-27—2025-12-27",
  1191. statusText: sText,
  1192. statusClass: sClass,
  1193. showProgress: statusIdx !== 1,
  1194. progress: 0,
  1195. progressTarget: target,
  1196. action,
  1197. });
  1198. }
  1199. return res;
  1200. },
  1201. // animateAll() {
  1202. // this.list.forEach((it) => this.animateItem(it));
  1203. // },
  1204. // animateNew(arr) {
  1205. // arr.forEach((it) => this.animateItem(it));
  1206. // },
  1207. // animateItem(item) {
  1208. // if (!item.showProgress) return;
  1209. // item.progress = 60;
  1210. // const target = Number(item.progressTarget) || 0;
  1211. // const step = Math.max(1, Math.round(target / 60));
  1212. // const timer = setInterval(() => {
  1213. // item.progress += step;
  1214. // if (item.progress >= target) {
  1215. // item.progress = target;
  1216. // clearInterval(timer);
  1217. // }
  1218. // }, 16);
  1219. // },
  1220. },
  1221. };
  1222. </script>
  1223. <style>
  1224. .report-export-page {
  1225. position: relative;
  1226. box-sizing: border-box;
  1227. /* padding: 170rpx 0 calc(50rpx + env(safe-area-inset-bottom)); */
  1228. padding-top: 290rpx;
  1229. }
  1230. .report-export-search-bar-container {
  1231. position: fixed;
  1232. top: 100rpx;
  1233. left: 0;
  1234. padding: 24rpx 24rpx 16rpx;
  1235. background-color: #f3f6f9;
  1236. z-index: 10;
  1237. box-sizing: border-box;
  1238. width: 100%;
  1239. }
  1240. .report-export-search-bar {
  1241. display: flex;
  1242. align-items: center;
  1243. background: #fff;
  1244. border-radius: 16rpx;
  1245. padding: 16rpx;
  1246. }
  1247. .report-export-search-input-wrap {
  1248. flex: 1;
  1249. position: relative;
  1250. background: #f3f6f9;
  1251. border-radius: 40rpx;
  1252. display: flex;
  1253. align-items: center;
  1254. height: 90rpx;
  1255. padding: 0 12rpx;
  1256. }
  1257. .report-export-search-icon {
  1258. width: 28rpx;
  1259. height: 28rpx;
  1260. margin-left: 16rpx;
  1261. }
  1262. .report-export-search-input {
  1263. flex: 1;
  1264. height: 90rpx;
  1265. padding: 0 16rpx;
  1266. font-size: 28rpx;
  1267. }
  1268. .report-export-search-btn {
  1269. height: 72rpx;
  1270. line-height: 72rpx;
  1271. padding: 0 34rpx;
  1272. border-radius: 36rpx;
  1273. background: #2c69ff;
  1274. color: #fff;
  1275. font-size: 34rpx;
  1276. margin-left: 12rpx;
  1277. }
  1278. .report-export-filter-btn {
  1279. margin-left: 12rpx;
  1280. display: flex;
  1281. flex-direction: column;
  1282. align-items: center;
  1283. justify-content: center;
  1284. }
  1285. .report-export-filter-icon {
  1286. width: 40rpx;
  1287. height: 40rpx;
  1288. }
  1289. .report-export-filter-text {
  1290. margin-top: 8rpx;
  1291. font-size: 24rpx;
  1292. color: #2c69ff;
  1293. }
  1294. .scan-rate-ranges-container {
  1295. position: fixed;
  1296. top: 262rpx;
  1297. left: 0;
  1298. width: 100%;
  1299. height: 120rpx;
  1300. background: #f3f6f9;
  1301. z-index: 6;
  1302. }
  1303. .scan-rate-ranges {
  1304. padding: 0rpx 24rpx 20rpx;
  1305. }
  1306. .scan-rate-ranges-content {
  1307. display: flex;
  1308. background: #fff;
  1309. padding: 20rpx 30rpx;
  1310. border-radius: 16rpx;
  1311. justify-content: space-between;
  1312. }
  1313. .scan-rate-range {
  1314. padding: 12rpx 35rpx;
  1315. border-radius: 5rpx;
  1316. background: rgb(240, 240, 240);
  1317. font-size: 26rpx;
  1318. }
  1319. .scan-rate-range.scan-rate-active {
  1320. background: #2c69ff;
  1321. color: #fff;
  1322. }
  1323. .report-export-list-scroll {
  1324. position: fixed;
  1325. top: 380rpx;
  1326. left: 0;
  1327. box-sizing: border-box;
  1328. width: 100%;
  1329. padding: 0 24rpx env(safe-area-inset-bottom);
  1330. box-sizing: border-box;
  1331. }
  1332. .report-export-item-card {
  1333. background: #fff;
  1334. border-radius: 16rpx;
  1335. padding: 24rpx;
  1336. margin-bottom: 16rpx;
  1337. }
  1338. .report-export-item-header {
  1339. width: 100%;
  1340. display: flex;
  1341. align-items: flex-start;
  1342. flex-wrap: wrap;
  1343. }
  1344. .report-export-item-title {
  1345. flex: 1;
  1346. width: 0;
  1347. word-break: break-word;
  1348. margin-right: 20rpx;
  1349. font-size: 38rpx;
  1350. color: #2c69ff;
  1351. display: -webkit-box;
  1352. -webkit-box-orient: vertical;
  1353. -webkit-line-clamp: 2;
  1354. overflow: hidden;
  1355. }
  1356. .report-export-status {
  1357. flex-shrink: 0;
  1358. padding: 8rpx 12rpx;
  1359. border-radius: 10rpx;
  1360. font-size: 22rpx;
  1361. color: #fff;
  1362. }
  1363. .report-export-status.report-export-status-2 {
  1364. background: #67c23a;
  1365. }
  1366. .report-export-status.report-export-status-1 {
  1367. background: #888;
  1368. }
  1369. .report-export-status.report-export-status-3,
  1370. .report-export-status.report-export-status-4 {
  1371. background: #2c69ff;
  1372. }
  1373. .report-export-item-row {
  1374. margin-top: 12rpx;
  1375. font-size: 28rpx;
  1376. color: #999;
  1377. }
  1378. .report-export-progress-wrap {
  1379. margin-top: 20rpx;
  1380. }
  1381. .report-export-progress {
  1382. margin-top: 8rpx;
  1383. height: 32rpx;
  1384. background: #e6ebf5;
  1385. border-radius: 999rpx;
  1386. overflow: hidden;
  1387. position: relative;
  1388. }
  1389. .report-export-progress-fill {
  1390. position: absolute;
  1391. left: 0;
  1392. top: 0;
  1393. height: 100%;
  1394. background: #2c69ff;
  1395. border-radius: 999rpx;
  1396. width: 0;
  1397. transition: width 0.2s linear;
  1398. }
  1399. .report-export-progress-text {
  1400. position: absolute;
  1401. left: 50%;
  1402. top: 50%;
  1403. transform: translate(-50%, -50%);
  1404. font-size: 26rpx;
  1405. color: #fff;
  1406. z-index: 2;
  1407. }
  1408. .report-export-action-row {
  1409. margin-top: 16rpx;
  1410. display: flex;
  1411. align-items: center;
  1412. justify-content: flex-end;
  1413. }
  1414. .report-export-action-btn {
  1415. height: 50rpx;
  1416. line-height: 50rpx;
  1417. padding: 0 28rpx;
  1418. border-radius: 12rpx;
  1419. font-size: 30rpx;
  1420. margin: 0;
  1421. }
  1422. .report-export-action-btn+.report-export-action-btn {
  1423. margin-left: 12rpx;
  1424. }
  1425. .report-export-action-btn.report-export-primary {
  1426. background: #2c69ff;
  1427. color: #fff;
  1428. }
  1429. .report-export-action-btn.report-export-info {
  1430. background: #67c23a;
  1431. color: #fff;
  1432. }
  1433. .report-export-loading-row {
  1434. justify-content: center;
  1435. }
  1436. .report-export-loading-wrapper {
  1437. width: 100%;
  1438. height: 76rpx;
  1439. display: flex;
  1440. align-items: center;
  1441. justify-content: center;
  1442. }
  1443. .report-export-loading-icon {
  1444. width: 40rpx;
  1445. height: 40rpx;
  1446. animation: spin 1s linear infinite;
  1447. }
  1448. @keyframes spin {
  1449. from {
  1450. transform: rotate(0deg);
  1451. }
  1452. to {
  1453. transform: rotate(360deg);
  1454. }
  1455. }
  1456. .report-export-footer {
  1457. position: fixed;
  1458. left: 0;
  1459. right: 0;
  1460. bottom: 0;
  1461. padding: 12rpx 30rpx calc(50rpx + env(safe-area-inset-bottom));
  1462. background: #fff;
  1463. }
  1464. .report-export-footer-btn {
  1465. height: 86rpx;
  1466. border-radius: 999rpx;
  1467. background: #2c69ff;
  1468. color: #fff;
  1469. display: flex;
  1470. align-items: center;
  1471. justify-content: center;
  1472. font-size: 30rpx;
  1473. box-shadow: 0 8rpx 24rpx rgba(44, 105, 255, 0.35);
  1474. }
  1475. .report-export-create-modal-mask {
  1476. position: fixed;
  1477. left: 0;
  1478. right: 0;
  1479. top: 0;
  1480. bottom: 0;
  1481. background: rgba(0, 0, 0, 0.35);
  1482. z-index: 90;
  1483. }
  1484. .report-export-create-modal {
  1485. position: fixed;
  1486. left: 0;
  1487. right: 0;
  1488. bottom: -100%;
  1489. width: 100%;
  1490. background: #fff;
  1491. border-top-left-radius: 45rpx;
  1492. border-top-right-radius: 45rpx;
  1493. z-index: 92;
  1494. box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.12);
  1495. /* transform: translateY(100%); */
  1496. transition: bottom 0.25s ease-out;
  1497. }
  1498. .report-export-create-modal--open {
  1499. /* transform: translateY(0) !important; */
  1500. bottom: 0 !important;
  1501. }
  1502. .report-export-create-modal-title {
  1503. position: relative;
  1504. padding: 28rpx 28rpx 12rpx;
  1505. font-size: 34rpx;
  1506. color: #333;
  1507. text-align: center;
  1508. line-height: 60rpx;
  1509. font-weight: 500;
  1510. }
  1511. .report-export-create-modal-close {
  1512. position: absolute;
  1513. right: 32rpx;
  1514. top: 50%;
  1515. transform: translateY(-50%);
  1516. padding: 10rpx;
  1517. color: #909399;
  1518. font-size: 55rpx;
  1519. line-height: 1;
  1520. }
  1521. .report-export-create-modal-body {
  1522. padding: 0 32rpx 100rpx;
  1523. }
  1524. .report-export-create-modal-row {
  1525. display: flex;
  1526. flex-direction: column;
  1527. margin-top: 32rpx;
  1528. position: relative;
  1529. }
  1530. .report-export-create-modal-label {
  1531. width: auto;
  1532. font-size: 28rpx;
  1533. color: #333;
  1534. margin-bottom: 16rpx;
  1535. font-weight: 500;
  1536. }
  1537. .report-export-create-modal-inputwrap {
  1538. flex: 1;
  1539. position: relative;
  1540. border: 1rpx solid #dcdfe6;
  1541. border-radius: 12rpx;
  1542. display: flex;
  1543. align-items: center;
  1544. min-height: 80rpx;
  1545. padding: 0 24rpx;
  1546. background-color: #fff;
  1547. }
  1548. .report-export-create-modal-static {
  1549. position: relative;
  1550. flex: 1;
  1551. max-height: 115rpx;
  1552. font-size: 28rpx;
  1553. color: #606266;
  1554. line-height: 40rpx;
  1555. padding: 20rpx 0;
  1556. overflow-y: auto;
  1557. padding-right: 20rpx;
  1558. }
  1559. .report-export-input-simulator {
  1560. display: flex;
  1561. justify-content: center;
  1562. align-items: center;
  1563. }
  1564. .report-export-create-modal-input {
  1565. flex: 1;
  1566. height: 80rpx;
  1567. font-size: 28rpx;
  1568. color: #606266;
  1569. }
  1570. /deep/.report-export-dropdown-other-customer-input {
  1571. color: #666;
  1572. }
  1573. /* .report-export-create-modal-select-arrow {
  1574. width: 18rpx;
  1575. height: 18rpx;
  1576. border: 5rpx solid #999;
  1577. border-top: none;
  1578. border-left: none;
  1579. transform: rotate(45deg);
  1580. margin: 0 8rpx 10rpx 12rpx;
  1581. }
  1582. .report-export-create-modal-select-arrow.disabled {
  1583. opacity: 0.4;
  1584. pointer-events: none;
  1585. } */
  1586. .report-export-create-modal-dropdown {
  1587. position: absolute;
  1588. left: 0;
  1589. right: 0;
  1590. background: #fff;
  1591. border: 1rpx solid #eef0f4;
  1592. border-radius: 12rpx;
  1593. box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
  1594. z-index: 6;
  1595. max-height: 510rpx;
  1596. overflow-y: auto;
  1597. -webkit-overflow-scrolling: touch;
  1598. }
  1599. .report-export-create-modal-dropdown-list {
  1600. max-height: 350rpx;
  1601. }
  1602. .report-export-dropdown-search-row {
  1603. padding: 10rpx;
  1604. }
  1605. .report-export-dropdown-search-input {
  1606. width: 100%;
  1607. height: 60rpx;
  1608. border: 1rpx solid #eaeef4;
  1609. border-radius: 8rpx;
  1610. box-sizing: border-box;
  1611. padding: 0 16rpx;
  1612. font-size: 28rpx;
  1613. }
  1614. .report-export-create-modal-dropdown-item {
  1615. padding: 16rpx 20rpx;
  1616. font-size: 28rpx;
  1617. color: #333;
  1618. }
  1619. .report-export-selected {
  1620. background: #f0f5ff;
  1621. }
  1622. .report-export-input-arrow {
  1623. width: 16rpx;
  1624. height: 16rpx;
  1625. border-right: 3rpx solid #c0c4cc;
  1626. border-bottom: 3rpx solid #c0c4cc;
  1627. transform: rotate(45deg);
  1628. margin-top: -6rpx;
  1629. }
  1630. .customer-chip {
  1631. display: inline-block;
  1632. position: relative;
  1633. line-height: 50rpx;
  1634. border: 1rpx solid #eaeef4;
  1635. border-radius: 8rpx;
  1636. padding: 0rpx 44rpx 0rpx 16rpx;
  1637. margin-right: 8rpx;
  1638. margin-bottom: 8rpx;
  1639. font-size: 22rpx;
  1640. color: #333;
  1641. }
  1642. .customer-chip-close {
  1643. position: absolute;
  1644. right: 6rpx;
  1645. top: 0;
  1646. color: #666;
  1647. font-size: 28rpx;
  1648. padding: 0 8rpx;
  1649. border-radius: 6rpx;
  1650. }
  1651. .customer-clear-all {
  1652. position: absolute;
  1653. right: 6rpx;
  1654. top: 50%;
  1655. transform: translateY(-50%);
  1656. line-height: 50rpx;
  1657. padding: 8rpx 10rpx;
  1658. font-size: 40rpx;
  1659. }
  1660. .report-export-create-modal-estimated {
  1661. font-size: 28rpx;
  1662. color: #666;
  1663. }
  1664. .report-export-create-modal-footer {
  1665. padding: 18rpx 28rpx calc(50rpx + env(safe-area-inset-bottom));
  1666. }
  1667. .report-export-create-modal-btn {
  1668. line-height: 90rpx;
  1669. border-radius: 20rpx;
  1670. background: #2c69ff;
  1671. color: #fff;
  1672. text-align: center;
  1673. font-size: 30rpx;
  1674. }
  1675. .report-export-create-email-modal {
  1676. transform: translateY(200%);
  1677. height: auto;
  1678. top: 30%;
  1679. bottom: auto;
  1680. border-radius: 16rpx;
  1681. width: 600rpx;
  1682. left: 75rpx;
  1683. }
  1684. .report-export-email-input-wrap {
  1685. display: flex;
  1686. align-items: center;
  1687. background: #f5f7fa;
  1688. border-radius: 8rpx;
  1689. padding: 0 20rpx;
  1690. height: 80rpx;
  1691. border: 1rpx solid transparent;
  1692. }
  1693. .report-export-input-error {
  1694. border-color: #ff4d4f;
  1695. }
  1696. .report-export-email-input {
  1697. flex: 1;
  1698. font-size: 28rpx;
  1699. color: #333;
  1700. height: 100%;
  1701. }
  1702. .report-export-email-suffix {
  1703. font-size: 28rpx;
  1704. color: #333;
  1705. margin-left: 10rpx;
  1706. }
  1707. .report-export-error-text {
  1708. font-size: 24rpx;
  1709. color: #ff4d4f;
  1710. margin-top: 10rpx;
  1711. display: block;
  1712. }
  1713. .report-export-input-disabled {
  1714. background-color: #f5f7fa;
  1715. cursor: not-allowed;
  1716. opacity: 0.7;
  1717. }
  1718. </style>