index.vue 11 KB

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