index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. <template>
  2. <Water></Water>
  3. <view
  4. class="detail-page"
  5. :style="{ height: pageHeight }"
  6. v-if="!!data && !loading"
  7. >
  8. <view
  9. class="detail-card detail-bg"
  10. :style="{ backgroundImage: 'url(' + bg + ')' }"
  11. >
  12. <view class="detail-drug-title">{{ data.physicName }}</view>
  13. <view class="detail-row detail-traceability">
  14. <view class="detail-label">追溯码:</view>
  15. <view class="detail-value detail-wrap">{{
  16. formatTraceCode(traceCode)
  17. }}</view>
  18. <view class="detail-copy-btn" @click="copy(traceCode)">
  19. <image
  20. src="../../../static/images/copy.png"
  21. mode="scaleToFill"
  22. :style="{ width: '30rpx', height: '30rpx' }"
  23. />
  24. </view>
  25. </view>
  26. <view class="detail-row">
  27. <view class="detail-label">生产企业:</view>
  28. <view
  29. class="detail-value detail-wrap detail-company-value"
  30. :class="{ 'detail-clamp': !isExpanded }"
  31. :style="{ marginBottom: needToggle ? '40rpx' : undefined }"
  32. id="companyValue"
  33. >
  34. <text>{{ data.produceEntName }}</text>
  35. </view>
  36. <text
  37. v-if="needToggle"
  38. class="detail-expand-tag"
  39. @click="toggleExpand"
  40. >{{ isExpanded ? "收起" : "展开" }}</text
  41. >
  42. </view>
  43. <view class="detail-row">
  44. <view class="detail-label">批准文号:</view>
  45. <view class="detail-value">{{ data.approveNo }}</view>
  46. </view>
  47. <view class="detail-row">
  48. <view class="detail-label">生产日期:</view>
  49. <view class="detail-value">{{ data.produceDate }}</view>
  50. </view>
  51. <view class="detail-row">
  52. <view class="detail-label">产品批号:</view>
  53. <view class="detail-value">{{ data.produceBatchNo }}</view>
  54. </view>
  55. <view class="detail-row">
  56. <view class="detail-label">
  57. 剂<text :style="{ opacity: 0 }">剂型</text>型:</view
  58. >
  59. <view class="detail-value">{{ data.prepnDesc }}</view>
  60. </view>
  61. <view class="detail-row">
  62. <view class="detail-label">包装规格:</view>
  63. <view class="detail-value">{{ data.prepnSpec }}</view>
  64. </view>
  65. <view class="detail-row">
  66. <view class="detail-label">有效期至:</view>
  67. <view class="detail-value">{{ data.validEndDate }}</view>
  68. </view>
  69. </view>
  70. <view
  71. class="detail-timeline detail-bg"
  72. :style="{ backgroundImage: 'url(' + bg + ')' }"
  73. >
  74. <view class="detail-section-title">扫码跟踪</view>
  75. <view
  76. class="detail-timeline-item"
  77. :class="{ 'detail-timeline-item-first': i === 0 }"
  78. v-for="(value, i) in data.enterpriseList"
  79. :key="i"
  80. >
  81. <view class="detail-left">
  82. <view class="detail-dot" :class="{ 'detail-fill': i == 0 }"></view>
  83. <view
  84. class="detail-line"
  85. v-if="i < data.enterpriseList.length - 1"
  86. ></view>
  87. </view>
  88. <view class="detail-right">
  89. <view class="detail-title" :class="{ 'detail-bold': i == 0 }">{{
  90. value.entName
  91. }}</view>
  92. <view class="detail-desc"
  93. >[{{ value.entTypeName }}]&nbsp;{{ getRegion(value) }}</view
  94. >
  95. <view
  96. class="detail-extra"
  97. v-if="!!value.inBoundBillsTime || !!value.outBoundBillsTime"
  98. >
  99. <view class="detail-extra-item" v-if="!!value.inBoundBillsTime">
  100. <!-- <image src="../../../static/images/warehousing.png" mode="scaleToFill"
  101. :style="{ width: '34rpx', height: '34rpx', marginTop: '4rpx' }" /> -->
  102. <text
  103. >{{ value.inBoundTypeName }}:{{
  104. formatDate(value.inBoundBillsTime)
  105. }}</text
  106. >
  107. </view>
  108. <view class="detail-extra-item" v-if="!value.inBoundBillsTime"
  109. >无入库信息</view
  110. >
  111. <view class="detail-extra-item">{{
  112. !!value.outBoundBillsTime
  113. ? value.outBoundTypeName +
  114. ":" +
  115. formatDate(value.outBoundBillsTime)
  116. : "无出库信息"
  117. }}</view>
  118. </view>
  119. </view>
  120. </view>
  121. </view>
  122. </view>
  123. <view
  124. class="detail-page-empty"
  125. :style="{ height: pageHeight }"
  126. v-if="!data && !loading"
  127. >
  128. <image
  129. src="../../../static/images/empty.png"
  130. mode="scaleToFill"
  131. :style="{ width: '264rpx', height: '200rpx' }"
  132. />
  133. <text
  134. :style="{
  135. fontSize: '24rpx',
  136. color: '#99a1ad',
  137. marginTop: '60rpx',
  138. textAlign: 'center',
  139. }"
  140. >所查询药品所在生产企业尚未纳入扫码范围,后续将进一步扩大,敬请期待</text
  141. >
  142. </view>
  143. <view
  144. class="detail-page-empty"
  145. :style="{ height: pageHeight }"
  146. v-if="loading"
  147. >
  148. <image
  149. src="../../../static/images/loading.png"
  150. mode="scaleToFill"
  151. :style="{ width: '50rpx', height: '50rpx' }"
  152. class="detail-loading"
  153. />
  154. </view>
  155. </template>
  156. <script>
  157. import bg from "../../../static/images/bg.png";
  158. import { formatDate } from "../../../utils/utils.js";
  159. import request from "../../../request/index.js";
  160. import Water from "@/components/water/water.vue";
  161. export default {
  162. components: {
  163. Water,
  164. },
  165. data() {
  166. return {
  167. data: null,
  168. loading: true,
  169. statusBarHeight: 20,
  170. traceCode: "",
  171. pageHeight: 0,
  172. bg,
  173. isExpanded: true,
  174. needToggle: false,
  175. };
  176. },
  177. onLoad(options) {
  178. const info = uni.getSystemInfoSync();
  179. this.statusBarHeight = info.statusBarHeight || 20;
  180. this.pageHeight = (info.windowHeight && info.windowHeight + "px") || "100%";
  181. if (options.traceCode) {
  182. this.traceCode = options.traceCode;
  183. }
  184. this.getData(options.traceCode);
  185. },
  186. onReady() {
  187. this.$nextTick(() => {
  188. const info = uni.getSystemInfoSync();
  189. const lineHeightPx = (info.windowWidth / 750) * 40;
  190. this.isExpanded = true; // 展开测量高度
  191. uni
  192. .createSelectorQuery()
  193. .in(this)
  194. .select("#companyValue")
  195. .boundingClientRect((rect) => {
  196. if (!rect || !lineHeightPx) return;
  197. const lines = Math.round(rect.height / lineHeightPx);
  198. this.needToggle = lines > 2;
  199. this.isExpanded = !this.needToggle;
  200. })
  201. .exec();
  202. });
  203. },
  204. methods: {
  205. formatDate(date) {
  206. return formatDate({ date }, "YYYY-MM-DD");
  207. },
  208. getRegion(value = {}) {
  209. const { provinceDesc, cityDesc, areaDesc } = value;
  210. return (
  211. [provinceDesc, cityDesc, areaDesc].filter(Boolean).join(",") || ""
  212. );
  213. },
  214. getData(traceCode) {
  215. request("/bills/scanCode", {
  216. tracCode: traceCode,
  217. path: "/traceabilityCodeQuery/pages/detail/index.vue",
  218. }).then((res) => {
  219. if (res.code == 200) {
  220. const _data = res.data;
  221. this.data = _data || null;
  222. if (
  223. typeof _data === "object" &&
  224. Object.values(_data).filter((i) => !!i).length == 0
  225. ) {
  226. this.data = null;
  227. }
  228. this.loading = false;
  229. } else {
  230. uni.showToast({ title: "查询失败", icon: "none" });
  231. }
  232. });
  233. },
  234. copy(text) {
  235. const data = String(text || this.traceCode);
  236. uni.setClipboardData({
  237. data,
  238. success: () => {
  239. console.log(
  240. "packages/traceabilityCodeQuery/pages/detail/index.vue" +
  241. "storage" +
  242. "clipboardData",
  243. );
  244. uni.showToast({
  245. title: "已复制追溯码",
  246. icon: "none",
  247. duration: 2000,
  248. });
  249. },
  250. });
  251. },
  252. toggleExpand() {
  253. if (!this.needToggle) return;
  254. this.isExpanded = !this.isExpanded;
  255. },
  256. formatTraceCode(traceCode) {
  257. return String(traceCode || "")
  258. .replace(/(.{5})/g, "$1 ")
  259. .trim();
  260. },
  261. },
  262. };
  263. </script>
  264. <style>
  265. .detail-bg {
  266. background-repeat: no-repeat;
  267. background-position: center top;
  268. background-size: 100% auto;
  269. }
  270. .detail-page {
  271. box-sizing: border-box;
  272. background: rgb(226 226 226);
  273. padding-bottom: 50rpx;
  274. overflow: auto;
  275. }
  276. .detail-page-empty {
  277. box-sizing: border-box;
  278. display: flex;
  279. flex-direction: column;
  280. align-items: center;
  281. justify-content: center;
  282. padding: 0 136rpx 280rpx;
  283. }
  284. .detail-card {
  285. padding: 40rpx;
  286. }
  287. .detail-drug-title {
  288. font-size: 45rpx;
  289. font-weight: 800;
  290. color: #000;
  291. margin-bottom: 16rpx;
  292. }
  293. .detail-row {
  294. position: relative;
  295. display: flex;
  296. margin-top: 12rpx;
  297. align-items: baseline;
  298. }
  299. .detail-label {
  300. flex-shrink: 0;
  301. display: inline-block;
  302. vertical-align: baseline;
  303. width: 150rpx;
  304. font-size: 28rpx;
  305. }
  306. .detail-value {
  307. display: inline-block;
  308. vertical-align: baseline;
  309. font-size: 28rpx;
  310. }
  311. .detail-company-value {
  312. line-height: 40rpx;
  313. }
  314. .detail-company-value.detail-clamp {
  315. display: -webkit-box;
  316. -webkit-box-orient: vertical;
  317. -webkit-line-clamp: 2;
  318. overflow: hidden;
  319. }
  320. .detail-expand-tag {
  321. position: absolute;
  322. left: 150rpx;
  323. bottom: 0;
  324. color: #2c69ff;
  325. font-size: 28rpx;
  326. }
  327. .detail-value.detail-wrap {
  328. word-break: break-all;
  329. white-space: normal;
  330. }
  331. .detail-copy-btn {
  332. margin-left: 16rpx;
  333. display: flex;
  334. align-items: center;
  335. color: #2c69ff;
  336. }
  337. .detail-copy-text {
  338. margin-left: 6rpx;
  339. font-size: 26rpx;
  340. }
  341. .detail-traceability {
  342. font-size: 24rpx;
  343. margin: -10rpx 0 45rpx;
  344. }
  345. .detail-traceability .detail-label {
  346. width: auto;
  347. }
  348. .detail-section-title {
  349. padding: 30rpx 46rpx;
  350. border-bottom: 1rpx solid #55555526;
  351. font-size: 35rpx;
  352. font-weight: 900;
  353. }
  354. .detail-timeline {
  355. margin-top: 20rpx;
  356. padding-bottom: 50rpx;
  357. }
  358. .detail-timeline-item {
  359. position: relative;
  360. display: flex;
  361. margin-left: 36rpx;
  362. }
  363. .detail-timeline-item-first {
  364. margin-top: 20rpx;
  365. }
  366. .detail-left {
  367. position: absolute;
  368. top: 34rpx;
  369. left: 0;
  370. height: 100%;
  371. display: flex;
  372. align-items: center;
  373. flex-direction: column;
  374. }
  375. .detail-dot {
  376. width: 16rpx;
  377. height: 16rpx;
  378. border-radius: 50%;
  379. border: 2rpx solid #2c69ff;
  380. }
  381. .detail-dot.detail-fill {
  382. background: #2c69ff;
  383. border-color: #2c69ff;
  384. }
  385. .detail-line {
  386. width: 2rpx;
  387. flex: 1;
  388. background: #8face68f;
  389. margin-right: 2rpx;
  390. }
  391. .detail-right {
  392. flex: 1;
  393. padding: 20rpx 0;
  394. margin-left: 45rpx;
  395. border-bottom: 1rpx solid #55555526;
  396. }
  397. .detail-title {
  398. font-size: 32rpx;
  399. color: #333;
  400. }
  401. .detail-title.detail-bold {
  402. font-weight: 800;
  403. }
  404. .detail-desc,
  405. .detail-extra {
  406. margin-top: 6rpx;
  407. font-size: 30rpx;
  408. color: #727a86;
  409. }
  410. .detail-extra {
  411. display: flex;
  412. align-items: center;
  413. font-size: 26rpx;
  414. }
  415. .detail-extra-item {
  416. flex: 1;
  417. display: flex;
  418. align-items: center;
  419. }
  420. .detail-extra-item:nth-child(2) {
  421. margin-left: 20rpx;
  422. }
  423. .detail-title,
  424. .detail-desc,
  425. .detail-extra {
  426. padding-right: 20rpx;
  427. }
  428. .detail-text-red {
  429. color: red;
  430. }
  431. .detail-loading {
  432. animation: spin 1s linear infinite;
  433. }
  434. @keyframes spin {
  435. from {
  436. transform: rotate(0deg);
  437. }
  438. to {
  439. transform: rotate(360deg);
  440. }
  441. }
  442. </style>