index.vue 11 KB

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