index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. <template>
  2. <div class="shareModel">
  3. <div class="shareContent" id="shareContent">
  4. <img src="./images/share-bg.png" class="shareBg" />
  5. <div class="share_content__title">
  6. <p class="large">我在管乐迷发布了演奏作品</p>
  7. <p class="small">赶快扫码看看吧!</p>
  8. </div>
  9. <div class="share_music__container, sectionFile">
  10. <div class="uploadImg">
  11. <img src="../img/audio-pan.png" class="audioPan" crossorigin="anonymous" />
  12. <img class="muploader" :src="musicDetail.img ? musicDetail.img + '?t=' + new Date().getTime() : musicBg" crossorigin="anonymous" />
  13. <img class="imgLabel" :src="musicDetail.videoUrl && musicDetail.videoUrl.lastIndexOf('mp4') !== -1 ? require('./images/videoLabel.png') : require('./images/audioLabel.png')" />
  14. </div>
  15. <div class="musicDetail">
  16. <p class="musicName van-ellipsis">
  17. {{ musicDetail.musicSheetName }}
  18. </p>
  19. <p class="username">演奏者:{{ musicDetail.username }}</p>
  20. </div>
  21. </div>
  22. <div class="downloadSection">
  23. <div class="qrcode" :class="{animate: isAnimate}"
  24. @mousedown="startOpenOutLink"
  25. @touchstart="startOpenOutLink"
  26. @mouseup="canceOpenOutLink"
  27. @mouseleave="canceOpenOutLink"
  28. @touchend="canceOpenOutLink">
  29. <!-- <canvas ref="canvasRef" class="qrcodeCanvas"></canvas> -->
  30. <vue-qr v-if="config" :text="config" :margin="10" :size="250" />
  31. <img src="../../../assets/images/icon_app.png" class="qrcodeLogo" />
  32. </div>
  33. <div class="qrtips">
  34. <div class="tit">温馨提示:</div>
  35. <p class="tip">
  36. 保存图片到相册后,请在微信里扫码查看~
  37. </p>
  38. <img src="../../../assets/images/logo-black.png" class="iconLogo" />
  39. </div>
  40. </div>
  41. </div>
  42. <div class="shareBottom">
  43. <van-icon name="cross" class="iconClose" @click="$emit('close')" />
  44. <div class="share__header">海报已生成!快来分享吧!</div>
  45. <van-grid :columnNum="5" :border="false" class="gridSection">
  46. <van-grid-item :icon="iconDownload" text="保存本地" @click="onSavePath('')"></van-grid-item>
  47. <van-grid-item :icon="iconWechat" text="微信好友" @click="onSavePath('wechat')"></van-grid-item>
  48. <van-grid-item :icon="iconFriendRing" text="朋友圈" @click="onSavePath('wechat_circle')"></van-grid-item>
  49. <van-grid-item :icon="iconLink" text="复制链接" @click="copyText(config)"></van-grid-item>
  50. </van-grid>
  51. <div class="btn van-hairline--top" @click="$emit('close')">
  52. 取消
  53. </div>
  54. </div>
  55. </div>
  56. </template>
  57. <script>
  58. import html2canvas from "html2canvas";
  59. import { postMessage } from "@/helpers/native-message";
  60. import VueQr from "vue-qr";
  61. export default {
  62. name: "share-model",
  63. components: { VueQr },
  64. props: ["musicDetail"],
  65. data() {
  66. return {
  67. iconDownload: require("./images/icon-download.png"),
  68. iconWechat: require("./images/icon-wechat.png"),
  69. iconFriendRing: require("./images/icon-friend-ring.png"),
  70. iconLink: require("./images/icon-link.png"),
  71. musicBg: require("./images/music-bg.png"),
  72. saveLoading: false,
  73. image: null,
  74. canvasRef: null,
  75. config: null,
  76. _time: null,
  77. isAnimate: false
  78. };
  79. },
  80. mounted() {
  81. this.config = location.origin + location.pathname + "#/shareCreation?id=" + this.musicDetail.id;
  82. console.log(this.musicDetail);
  83. },
  84. methods: {
  85. startOpenOutLink(){
  86. this.isAnimate = true
  87. this._time = setTimeout(() => {
  88. this.isAnimate = false
  89. postMessage({
  90. api: 'openOutLink',
  91. content : {
  92. "url" : this.config
  93. }
  94. })
  95. }, 1000);
  96. },
  97. canceOpenOutLink(){
  98. this.isAnimate = false
  99. clearTimeout(this._time)
  100. },
  101. async saveImg() {
  102. this.$toast.loading({
  103. message: "图片生成中...",
  104. forbidClick: true,
  105. });
  106. setTimeout(() => {
  107. this.saveLoading = false;
  108. }, 100);
  109. postMessage(
  110. {
  111. api: "savePicture",
  112. content: {
  113. base64: this.image,
  114. },
  115. },
  116. (res) => {
  117. if (res?.content?.status === "success") {
  118. this.$toast.success("已保存到相册");
  119. } else {
  120. this.$toast.fail("保存失败");
  121. }
  122. }
  123. );
  124. },
  125. async onSaveWe(type) {
  126. // this.$toast.loading({
  127. // message: "图片分享中...",
  128. // forbidClick: true,
  129. // });
  130. setTimeout(() => {
  131. this.saveLoading = false;
  132. }, 100);
  133. postMessage(
  134. {
  135. api: "shareTripartite",
  136. content: {
  137. title: "我在管乐迷发布了演奏作品",
  138. desc: this.musicDetail.desc,
  139. // image: this.image,
  140. video: "",
  141. type: "link",
  142. url: this.config,
  143. thumb: encodeURI(decodeURI(this.musicDetail.img)),
  144. shareType: type,
  145. },
  146. },
  147. (res) => {
  148. if (res?.content?.status) {
  149. this.$toast.success("分享成功");
  150. } else {
  151. this.$toast.fail("分享失败");
  152. }
  153. }
  154. );
  155. },
  156. onSavePath(type) {
  157. // 判断是否在保存中...
  158. if (this.saveLoading) {
  159. return;
  160. }
  161. this.saveLoading = true;
  162. // 判断是否已经生成图片
  163. if (this.image) {
  164. if (type) {
  165. this.onSaveWe(type);
  166. } else {
  167. this.saveImg();
  168. }
  169. } else {
  170. const container = document.getElementById("shareContent");
  171. html2canvas(container, {
  172. allowTaint: true,
  173. useCORS: true,
  174. backgroundColor: null,
  175. })
  176. .then(async (canvas) => {
  177. const url = canvas.toDataURL("image/png");
  178. this.image = url;
  179. if (type) {
  180. this.onSaveWe(type);
  181. } else {
  182. this.saveImg();
  183. }
  184. })
  185. .catch(() => {
  186. this.toast.clear();
  187. this.saveLoading = false;
  188. });
  189. }
  190. },
  191. copyText(text) {
  192. // 数字没有 .length 不能执行selectText 需要转化成字符串
  193. const textString = text.toString();
  194. let input = document.querySelector("#copy-input");
  195. if (!input) {
  196. input = document.createElement("input");
  197. input.id = "copy-input";
  198. input.readOnly = true; // 防止ios聚焦触发键盘事件
  199. input.style.position = "fixed";
  200. input.style.left = "-1000px";
  201. input.style.zIndex = "-1000";
  202. // 为了处理,页面滑动到底部的问题
  203. document.body.appendChild(input);
  204. // document.querySelector('#input-copy-container')?.appendChild(input)
  205. }
  206. input.value = textString;
  207. // ios必须先选中文字且不支持 input.select();
  208. selectText(input, 0, textString.length);
  209. if (document.execCommand("copy")) {
  210. document.execCommand("copy");
  211. this.$toast("复制成功");
  212. }
  213. input.blur();
  214. // input自带的select()方法在苹果端无法进行选择,所以需要自己去写一个类似的方法
  215. // 选择文本。createTextRange(setSelectionRange)是input方法
  216. function selectText(textbox, startIndex, stopIndex) {
  217. if (textbox.createTextRange) {
  218. //ie
  219. const range = textbox.createTextRange();
  220. range.collapse(true);
  221. range.moveStart("character", startIndex); //起始光标
  222. range.moveEnd("character", stopIndex - startIndex); //结束光标
  223. range.select(); //不兼容苹果
  224. } else {
  225. //firefox/chrome
  226. textbox.setSelectionRange(startIndex, stopIndex);
  227. textbox.focus();
  228. }
  229. }
  230. },
  231. },
  232. };
  233. </script>
  234. <style lang="less">
  235. .shareContent {
  236. position: relative;
  237. width: 3.35rem;
  238. height: 3.55rem;
  239. margin: 0 auto 0.56rem;
  240. .shareBg {
  241. position: absolute;
  242. top: 0;
  243. left: 0;
  244. z-index: 1;
  245. width: 3.35rem;
  246. height: 3.55rem;
  247. }
  248. .share_content__title {
  249. position: relative;
  250. z-index: 3;
  251. padding-top: 0.54rem;
  252. padding-left: 0.14rem;
  253. color: #ffffff;
  254. text-shadow: 0px 0.02rem 0.04rem rgba(10, 181, 192, 0.7);
  255. width: 170px;
  256. .large {
  257. font-size: 0.19rem;
  258. font-weight: 600;
  259. line-height: 0.26rem;
  260. }
  261. .small {
  262. padding-top: 0.06rem;
  263. font-size: 0.14rem;
  264. line-height: 0.2rem;
  265. }
  266. }
  267. }
  268. .sectionFile {
  269. position: relative;
  270. z-index: 3;
  271. margin: 25px 0.15rem 0.14rem;
  272. padding: 12px;
  273. display: flex;
  274. background: rgba(255, 255, 255, 0.79);
  275. border-radius: 0.1rem;
  276. .muploader {
  277. position: relative;
  278. z-index: 9;
  279. width: 56px;
  280. height: 56px;
  281. object-fit: cover;
  282. border-radius: 8px;
  283. }
  284. .uploadImg {
  285. position: relative;
  286. border-radius: 8px;
  287. margin-right: 0.16rem;
  288. line-height: 0;
  289. .audioPan {
  290. position: absolute;
  291. top: 0;
  292. right: -6px;
  293. z-index: 1;
  294. width: 56px;
  295. height: 56px;
  296. }
  297. .imgLabel{
  298. position: absolute;
  299. right: 0;
  300. top: 0;
  301. width: 28px;
  302. height: 14px;
  303. z-index: 10;
  304. }
  305. }
  306. .musicDetail {
  307. display: flex;
  308. justify-content: center;
  309. flex-direction: column;
  310. .musicName {
  311. font-size: 0.16rem;
  312. font-weight: 600;
  313. color: #131415;
  314. line-height: 22px;
  315. max-width: 200px;
  316. }
  317. .username {
  318. padding-top: 0.04rem;
  319. font-size: 13px;
  320. color: #aaa;
  321. line-height: 0.2rem;
  322. }
  323. }
  324. }
  325. .downloadSection {
  326. display: flex;
  327. align-items: center;
  328. margin: 0 0.2rem;
  329. position: relative;
  330. z-index: 3;
  331. .qrcode {
  332. position: relative;
  333. width: 0.84rem;
  334. height: 0.84rem;
  335. padding: 0.03rem;
  336. border-radius: 0.04rem;
  337. // opacity: 0.53;
  338. border: 1px solid #2bd1b2;
  339. flex-shrink: 0;
  340. overflow: hidden;
  341. &.animate{
  342. transform: scale(1.1)
  343. }
  344. .qrcodeCanvas {
  345. width: 100% !important;
  346. height: 100% !important;
  347. }
  348. & > img {
  349. width: 0.84rem;
  350. height: 0.84rem;
  351. }
  352. .qrcodeLogo {
  353. position: absolute;
  354. top: 50%;
  355. left: 50%;
  356. z-index: 10;
  357. margin-left: -0.08rem;
  358. margin-top: -0.08rem;
  359. width: 0.16rem;
  360. height: 0.16rem;
  361. border-radius: 0.04rem;
  362. }
  363. }
  364. .qrtips {
  365. margin-left: 0.1rem;
  366. padding-left: 0.09rem;
  367. border-left: 1px dashed #d8d8d8;
  368. .tit{
  369. color: #01C1B5;
  370. line-height: .24rem;
  371. font-size: .13rem;
  372. }
  373. .tip {
  374. font-size: 0.12rem;
  375. color: #777777;
  376. line-height: 0.17rem;
  377. }
  378. .iconLogo {
  379. width: 0.68rem;
  380. height: 0.19rem;
  381. margin: .05rem 0 0;
  382. display: block;
  383. }
  384. .downTip {
  385. font-size: 0.12rem;
  386. font-weight: 500;
  387. color: #333333;
  388. line-height: 0.17rem;
  389. }
  390. }
  391. }
  392. .shareBottom {
  393. position: relative;
  394. background-color: #fff;
  395. border-radius: 0.12rem 0.12rem 0 0;
  396. //padding-bottom: env(safe-area-inset-bottom);
  397. padding-bottom: 0.1rem;
  398. .iconClose {
  399. position: absolute;
  400. top: 0.12rem;
  401. right: 0.12rem;
  402. font-size: 0.16rem;
  403. color: #999999;
  404. }
  405. }
  406. .share__header {
  407. padding: 0.12rem 0.15rem 0.08rem;
  408. font-size: 0.16rem;
  409. color: #333333;
  410. line-height: 22px;
  411. }
  412. .gridSection {
  413. justify-content: space-between;
  414. padding: 0 .13rem;
  415. .van-grid-item__icon {
  416. font-size: 0.44rem;
  417. }
  418. .van-grid-item__text {
  419. font-size: 0.12rem;
  420. color: #646566;
  421. }
  422. }
  423. .btn {
  424. font-size: 0.16rem;
  425. color: #aaaaaa;
  426. line-height: 0.55rem;
  427. text-align: center;
  428. }
  429. </style>