reviewPopup.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. <template>
  2. <popup class="price-sheet-popup" :overlay-style="{'z-index': 9999}" teleport="body" v-model:show="showPopup">
  3. <div class="price-sheet-content">
  4. <div class="price-sheet-content-inner">
  5. <div class="sheet-content">
  6. <div class="review-image">
  7. <div class="vs-wrap" v-if="preImg">
  8. <img src="@/assets/img/home/vs.png" alt="" />
  9. </div>
  10. <div class="review-image-item" v-if="preImg">
  11. <div class="review-image-item-title">农事前</div>
  12. <img class="review-image-item-img" :src="preImg" alt="" />
  13. </div>
  14. <div class="review-image-item" v-if="resImg">
  15. <div class="review-image-item-title">农事后</div>
  16. <img class="review-image-item-img" :src="resImg" alt="" />
  17. </div>
  18. </div>
  19. <!-- 报价详情区域 -->
  20. <div class="quotation-info">
  21. <div class="info-item">
  22. <span class="info-label">执行农资</span>
  23. <span class="info-value">{{ quotationData.serviceMain || "--" }}</span>
  24. </div>
  25. <div class="info-item">
  26. <span class="info-label">农事名称</span>
  27. <span class="info-value">{{ quotationData?.farmWorkName || "--" }}</span>
  28. </div>
  29. <!-- <div class="info-item flex-wrap">
  30. <div class="info-label">复核成效</div>
  31. <div class="info-value">
  32. 促进分蘖芽萌发、加快分蘖生长,同时补充氮素等关键养分,增强植株长势,为形成足够穗数、提高群体产量打基础。
  33. </div>
  34. </div> -->
  35. <div class="info-item flex-wrap">
  36. <div class="info-label">药肥处方</div>
  37. <div class="info-value">
  38. <div class="rescription" v-if="quotationData?.prescriptionList">
  39. <span
  40. v-for="(fertilizer, fertilizerI) in quotationData.prescriptionList"
  41. :key="fertilizerI"
  42. >
  43. <span
  44. v-for="(pest, pestI) in fertilizer.pesticideFertilizerList"
  45. :key="'sub' + pestI"
  46. >
  47. {{ pest.defaultName || pest.pesticideFertilizerName }}
  48. <span
  49. v-if="
  50. pestI !== fertilizer.pesticideFertilizerList.length - 1 ||
  51. fertilizerI !== quotationData.prescriptionList.length - 1
  52. "
  53. >
  54. +
  55. </span>
  56. </span>
  57. </span>
  58. </div>
  59. <div class="rescription" v-else>无处方</div>
  60. </div>
  61. </div>
  62. </div>
  63. <div class="bottom-info">
  64. <div class="bottom-l">
  65. <div class="l-img">
  66. <img src="@/assets/img/home/bird.png" alt="" />
  67. </div>
  68. <div class="l-text">
  69. <div class="l-text-title">飞鸟管家</div>
  70. <div class="l-text-time">扫描二维码,查看更多详情</div>
  71. </div>
  72. </div>
  73. <div class="bottom-r">
  74. <img src="@/assets/img/home/qrcode.png" alt="" />
  75. </div>
  76. </div>
  77. </div>
  78. </div>
  79. <!-- 底部操作按钮 -->
  80. <div class="bottom-actions" @click.stop="showPopup = false">
  81. <div class="action-buttons">
  82. <div class="action-btn blue-btn" @click.stop="handleShare">
  83. <div class="icon-circle">
  84. <img src="@/assets/img/home/bird.png" alt="" />
  85. </div>
  86. <span class="btn-label">飞鸟用户</span>
  87. </div>
  88. <div class="action-btn green-btn" @click.stop="handleWechat">
  89. <div class="icon-circle">
  90. <img src="@/assets/img/home/wechat.png" alt="" />
  91. </div>
  92. <span class="btn-label">微信</span>
  93. </div>
  94. <!-- <div class="action-btn orange-btn">
  95. <div class="icon-circle">
  96. <el-icon :size="24"><Download /></el-icon>
  97. </div>
  98. <span class="btn-label">保存图片</span>
  99. </div> -->
  100. </div>
  101. <div class="cancel-btn" @click="handleCancel">取消</div>
  102. </div>
  103. </div>
  104. </popup>
  105. </template>
  106. <script setup>
  107. import { Popup } from "vant";
  108. import { ref, computed, onActivated } from "vue";
  109. import { useRouter } from "vue-router";
  110. import { ElMessage } from "element-plus";
  111. import wx from "weixin-js-sdk";
  112. import html2canvas from "html2canvas";
  113. const router = useRouter();
  114. const showPopup = ref(false);
  115. const contentEl = ref(null);
  116. const preImg = ref("");
  117. const resImg = ref("");
  118. // 报价数据
  119. const quotationData = ref({});
  120. onActivated(() => {});
  121. const recordId = ref("");
  122. const handleShowPopup = async (id, preImgVal, resImgVal) => {
  123. recordId.value = id;
  124. await getDetail();
  125. preImg.value = preImgVal;
  126. resImg.value = resImgVal;
  127. showPopup.value = true;
  128. };
  129. async function getDetail() {
  130. const { data } = await VE_API.z_farm_work_record.getDetail({ id: recordId.value });
  131. quotationData.value = data[0];
  132. }
  133. const handleShare = () => {
  134. const userId = quotationData.value.farmMiniUserId;
  135. const parmasPage = {
  136. farmWorkOrderId: quotationData.value.orderId,
  137. farmMiniUserId: userId,
  138. farmMiniUserName: quotationData.value.farmMiniUserName,
  139. farmId: quotationData.value.farmId,
  140. farmWorkName: quotationData.value.farmWorkName,
  141. id: quotationData.value.id,
  142. imageList: [resImg.value],
  143. type: "reviewWork",
  144. };
  145. if (userId) {
  146. router.push(
  147. `/chat_frame?userId=${userId}&farmId=${
  148. parmasPage.farmId
  149. }&pageParams=${JSON.stringify(parmasPage)}`
  150. );
  151. } else {
  152. ElMessage.warning("尚未绑定用户,暂时无法分享");
  153. }
  154. };
  155. const handleWechat = () => {
  156. console.log("handleWechat");
  157. // router.push({
  158. // path: "/completed_work",
  159. // query: { id: quotationData.value.id, farmWorkOrderId: quotationData.value.orderId, isAssign: true },
  160. // });
  161. const query = {
  162. askInfo: { title: "农事执行成果", content: "是否分享该农事执行成果给好友" },
  163. shareText: "向您分享了农事执行成果",
  164. id: recordId.value,
  165. postImg: resImg.value,
  166. };
  167. wx.miniProgram.navigateTo({
  168. url: `/pages/subPages/share_page/index?pageParams=${JSON.stringify(query)}&type=reviewWork`,
  169. });
  170. };
  171. const handleSaveImage = async () => {
  172. try {
  173. if (!contentEl.value) return;
  174. const element = contentEl.value;
  175. const scroller = element.querySelector(".sheet-content");
  176. // 记录原样式
  177. const prev = {
  178. elementOverflow: element.style.overflow,
  179. elementMaxHeight: element.style.maxHeight,
  180. elementHeight: element.style.height,
  181. scrollerOverflow: scroller ? scroller.style.overflow : undefined,
  182. scrollerMaxHeight: scroller ? scroller.style.maxHeight : undefined,
  183. scrollerHeight: scroller ? scroller.style.height : undefined,
  184. };
  185. // 展开内容,去除滚动限制,确保截图包含全部内容
  186. element.style.overflow = "visible";
  187. element.style.maxHeight = "none";
  188. element.style.height = "auto";
  189. if (scroller) {
  190. scroller.style.overflow = "visible";
  191. scroller.style.maxHeight = "none";
  192. scroller.style.height = "auto";
  193. }
  194. // 计算完整尺寸
  195. const width = element.scrollWidth;
  196. const height = element.scrollHeight;
  197. const canvas = await html2canvas(element, {
  198. backgroundColor: "#ffffff",
  199. useCORS: true,
  200. allowTaint: true,
  201. scale: Math.min(2, window.devicePixelRatio || 2),
  202. width,
  203. height,
  204. windowWidth: width,
  205. windowHeight: height,
  206. scrollX: 0,
  207. scrollY: 0,
  208. });
  209. const dataUrl = canvas.toDataURL("image/png");
  210. const link = document.createElement("a");
  211. link.href = dataUrl;
  212. link.download = "服务报价单.png";
  213. document.body.appendChild(link);
  214. link.click();
  215. document.body.removeChild(link);
  216. // 还原样式
  217. element.style.overflow = prev.elementOverflow;
  218. element.style.maxHeight = prev.elementMaxHeight;
  219. element.style.height = prev.elementHeight;
  220. if (scroller) {
  221. scroller.style.overflow = prev.scrollerOverflow;
  222. scroller.style.maxHeight = prev.scrollerMaxHeight;
  223. scroller.style.height = prev.scrollerHeight;
  224. }
  225. } catch (e) {
  226. console.error("保存图片失败", e);
  227. }
  228. };
  229. const handleCancel = () => {
  230. showPopup.value = false;
  231. };
  232. defineExpose({
  233. handleShowPopup,
  234. });
  235. </script>
  236. <style lang="scss" scoped>
  237. .price-sheet-popup {
  238. z-index: 9999 !important;
  239. width: 90%;
  240. max-height: 90vh;
  241. background: none;
  242. border-radius: 12px;
  243. overflow: hidden;
  244. display: flex;
  245. flex-direction: column;
  246. backdrop-filter: 4px;
  247. ::v-deep {
  248. .van-popup__close-icon {
  249. color: #000;
  250. font-size: 18px;
  251. top: 12px;
  252. right: 12px;
  253. }
  254. }
  255. }
  256. .price-sheet-content {
  257. display: flex;
  258. flex-direction: column;
  259. max-height: 90vh;
  260. // height: 95vh;
  261. .price-sheet-content-inner {
  262. background: #fff;
  263. border-radius: 12px;
  264. display: flex;
  265. flex-direction: column;
  266. height: 100%;
  267. overflow: hidden;
  268. }
  269. }
  270. .sheet-content {
  271. padding: 12px 12px 10px 12px;
  272. flex: 1;
  273. overflow-y: auto;
  274. overflow-x: hidden;
  275. position: relative;
  276. }
  277. .bottom-info {
  278. padding: 16px 16px 16px 16px;
  279. border-top: 1px dotted rgba(0, 0, 0, 0.3);
  280. display: flex;
  281. align-items: center;
  282. justify-content: space-between;
  283. .bottom-l {
  284. display: flex;
  285. align-items: center;
  286. gap: 8px;
  287. .l-img {
  288. img {
  289. width: 40px;
  290. }
  291. }
  292. .l-text-time {
  293. font-size: 12px;
  294. }
  295. }
  296. .bottom-r {
  297. width: 40px;
  298. height: 40px;
  299. img {
  300. width: 100%;
  301. height: 100%;
  302. object-fit: cover;
  303. }
  304. }
  305. }
  306. .review-image {
  307. position: relative;
  308. display: flex;
  309. align-items: center;
  310. justify-content: center;
  311. gap: 8px;
  312. .vs-wrap {
  313. position: absolute;
  314. left: 50%;
  315. top: 50%;
  316. transform: translate(-50%, -50%);
  317. width: 40px;
  318. height: 40px;
  319. z-index: 10;
  320. img {
  321. width: 100%;
  322. height: 100%;
  323. object-fit: cover;
  324. }
  325. }
  326. .review-image-item {
  327. position: relative;
  328. flex: 1;
  329. .review-image-item-title {
  330. position: absolute;
  331. top: 0;
  332. left: 0;
  333. background: rgba(54, 52, 52, 0.6);
  334. padding: 4px 10px;
  335. border-radius: 8px 0 8px 0;
  336. backdrop-filter: 4px;
  337. font-size: 12px;
  338. color: #fff;
  339. }
  340. .review-image-item-img {
  341. width: 100%;
  342. height: 250px;
  343. object-fit: cover;
  344. }
  345. }
  346. }
  347. // 报价详情区域
  348. .quotation-info {
  349. margin-top: 12px;
  350. .flex-wrap {
  351. display: flex;
  352. .info-label {
  353. flex: none;
  354. }
  355. }
  356. .info-item {
  357. font-size: 14px;
  358. color: #000;
  359. margin-bottom: 8px;
  360. .info-label {
  361. padding-right: 8px;
  362. color: rgba(0, 0, 0, 0.5);
  363. }
  364. .info-value {
  365. color: #000;
  366. }
  367. &.catalog-label {
  368. font-weight: bold;
  369. margin-top: 10px;
  370. margin-bottom: 10px;
  371. position: relative;
  372. .info-label {
  373. color: #000;
  374. }
  375. }
  376. }
  377. .total-bar {
  378. display: flex;
  379. align-items: center;
  380. justify-content: center;
  381. background: rgba(33, 153, 248, 0.1);
  382. border: 1px solid rgba(33, 153, 248, 0.5);
  383. height: 38px;
  384. border-radius: 4px;
  385. .total-label {
  386. font-size: 14px;
  387. color: #000000;
  388. }
  389. .total-value {
  390. font-size: 22px;
  391. font-weight: bold;
  392. color: #2199f8;
  393. }
  394. .total-unit {
  395. font-size: 14px;
  396. color: #000;
  397. margin-left: 4px;
  398. }
  399. }
  400. }
  401. // 药肥费用区域
  402. .fertilizer-cost-section {
  403. margin-bottom: 10px;
  404. .section-header {
  405. display: flex;
  406. justify-content: space-between;
  407. align-items: center;
  408. margin-bottom: 8px;
  409. .section-title {
  410. font-size: 14px;
  411. color: #000;
  412. }
  413. .section-total {
  414. font-size: 16px;
  415. font-weight: bold;
  416. color: #000;
  417. .unit-text {
  418. padding-left: 2px;
  419. font-size: 12px;
  420. font-weight: normal;
  421. }
  422. }
  423. }
  424. .cost-table {
  425. border: 1px solid rgba(225, 225, 225, 0.5);
  426. border-radius: 5px;
  427. overflow: hidden;
  428. .table-header {
  429. display: flex;
  430. background: rgba(241, 241, 241, 0.4);
  431. padding: 8px 6px;
  432. font-size: 12px;
  433. color: #767676;
  434. border-bottom: 1px solid rgba(225, 225, 225, 0.5);
  435. .col-1 {
  436. width: 40px;
  437. text-align: center;
  438. }
  439. .col-2 {
  440. flex: 1;
  441. text-align: center;
  442. }
  443. .col-3 {
  444. width: 52px;
  445. text-align: center;
  446. }
  447. .col-4 {
  448. width: 56px;
  449. text-align: center;
  450. }
  451. .col-5 {
  452. width: 52px;
  453. text-align: center;
  454. }
  455. .col-6 {
  456. width: 52px;
  457. text-align: center;
  458. }
  459. }
  460. .table-row {
  461. display: flex;
  462. padding: 8px 6px;
  463. font-size: 11px;
  464. color: rgba(0, 0, 0, 0.6);
  465. background: #fff;
  466. border-bottom: 1px solid rgba(225, 225, 225, 0.3);
  467. &:last-child {
  468. border-bottom: none;
  469. }
  470. .col-1,
  471. .col-2,
  472. .col-3,
  473. .col-4,
  474. .col-5,
  475. .col-6 {
  476. display: flex;
  477. align-items: center;
  478. justify-content: center;
  479. }
  480. .col-1 {
  481. width: 40px;
  482. }
  483. .col-2 {
  484. flex: 1;
  485. }
  486. .col-3 {
  487. width: 52px;
  488. }
  489. .col-4 {
  490. width: 56px;
  491. }
  492. .col-5 {
  493. width: 52px;
  494. }
  495. .col-6 {
  496. width: 52px;
  497. }
  498. }
  499. }
  500. }
  501. // 服务费用区域
  502. .service-cost-section {
  503. position: relative;
  504. .section-header {
  505. display: flex;
  506. justify-content: space-between;
  507. align-items: center;
  508. margin-bottom: 12px;
  509. .section-title {
  510. font-size: 14px;
  511. color: #000;
  512. }
  513. .section-total {
  514. font-size: 16px;
  515. font-weight: bold;
  516. color: #000;
  517. .unit-text {
  518. padding-left: 2px;
  519. font-size: 12px;
  520. font-weight: normal;
  521. }
  522. }
  523. }
  524. .service-details {
  525. display: flex;
  526. align-items: center;
  527. border: 1px solid rgba(206, 206, 206, 0.5);
  528. padding: 8px 0;
  529. border-radius: 4px;
  530. margin-bottom: 10px;
  531. .detail-item {
  532. font-size: 14px;
  533. flex: 1;
  534. text-align: center;
  535. .detail-label {
  536. color: rgba(0, 0, 0, 0.2);
  537. margin-top: 6px;
  538. }
  539. .detail-value {
  540. color: rgba(0, 0, 0, 0.8);
  541. }
  542. }
  543. .detail-item + .detail-item {
  544. position: relative;
  545. &::before {
  546. content: "";
  547. position: absolute;
  548. left: 0;
  549. top: 50%;
  550. transform: translateY(-50%);
  551. width: 1px;
  552. height: 20px;
  553. background: rgba(0, 0, 0, 0.1);
  554. }
  555. }
  556. }
  557. }
  558. .edit-btn-box {
  559. display: flex;
  560. justify-content: end;
  561. position: absolute;
  562. right: 0;
  563. top: -8px;
  564. z-index: 10;
  565. }
  566. .edit-btn {
  567. background: rgba(33, 153, 248, 0.1);
  568. color: #2199f8;
  569. padding: 6px 16px;
  570. border-radius: 20px;
  571. font-size: 14px;
  572. width: fit-content;
  573. cursor: pointer;
  574. }
  575. // 底部操作按钮
  576. .bottom-actions {
  577. flex-shrink: 0;
  578. .action-buttons {
  579. padding: 16px;
  580. display: flex;
  581. justify-content: space-around;
  582. .action-btn {
  583. display: flex;
  584. flex-direction: column;
  585. align-items: center;
  586. cursor: pointer;
  587. .icon-circle {
  588. width: 48px;
  589. height: 48px;
  590. border-radius: 50%;
  591. display: flex;
  592. align-items: center;
  593. justify-content: center;
  594. color: #fff;
  595. margin-bottom: 4px;
  596. .el-icon {
  597. color: #fff;
  598. }
  599. img {
  600. width: 50px;
  601. }
  602. }
  603. &.blue-btn .icon-circle {
  604. background: #2199f8;
  605. }
  606. &.green-btn .icon-circle {
  607. background: #07c160;
  608. }
  609. &.orange-btn .icon-circle {
  610. background: #ff790b;
  611. }
  612. .btn-label {
  613. font-size: 12px;
  614. color: #fff;
  615. }
  616. }
  617. }
  618. .cancel-btn {
  619. text-align: center;
  620. font-size: 18px;
  621. color: #fff;
  622. cursor: pointer;
  623. }
  624. }
  625. </style>