SharePopup.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <template>
  2. <view class="share-modal" v-show="visible">
  3. <view class="modal-overlay" @click="handleClose"></view>
  4. <transition name="fade-up">
  5. <view v-if="visible" class="modal-content">
  6. <view class="share-options">
  7. <view v-for="option in shareOptions" :key="option.id" class="share-item" @click="handleShare(option.id)">
  8. <view class="share-icon">
  9. <image :src="option.icon" :alt="option.title" mode="aspectFit"></image>
  10. </view>
  11. <text class="option-title">{{ option.title }}</text>
  12. </view>
  13. </view>
  14. </view>
  15. </transition>
  16. </view>
  17. </template>
  18. <script>
  19. import { generateSecureUrlParams } from '@/common/encryption.js'
  20. export default {
  21. name: 'ShareModal',
  22. props: {
  23. visible: {
  24. type: Boolean,
  25. default: false
  26. },
  27. userId: {
  28. default: 0
  29. },
  30. shareTitle: {
  31. type: String,
  32. default: ''
  33. },
  34. shareDesc: {
  35. type: String,
  36. default: '快来加入萌创星球吧!'
  37. },
  38. shareImg: {
  39. type: String,
  40. default: ''
  41. },
  42. view: {
  43. type: String,
  44. default: ''
  45. },
  46. isReportContent: {
  47. type: Boolean,
  48. default: false
  49. },
  50. link: {
  51. type: String,
  52. default: ''
  53. }
  54. },
  55. data() {
  56. return {
  57. shareOptions: [
  58. { id: 'wechat', title: '微信好友', icon: '/static/icon/sy_icon_weixin.png' },
  59. { id: 'moments', title: '朋友圈', icon: '/static/icon/sy_icon_pengyouquan.png' },
  60. // { id: 'qq', title: 'QQ好友', icon: '../../static/icon/sy_icon_qq.png' },
  61. { id: 'copy', title: '复制链接', icon: '/static/icon/sy_icon_fuzhilianjie.png' },
  62. { id: 'report', title: '举报', icon: '/static/icon/sy_icon_jubao01.png' },
  63. // { id: 'more', title: '更多', icon: '../../static/icon/sy_icon_gengduo.png' }
  64. ],
  65. from_id: 0
  66. }
  67. },
  68. computed: {
  69. shareUrl() {
  70. const urlParams = {
  71. userId: this.userId,
  72. action: this.view,
  73. timestamp: new Date().getTime(),
  74. from_id: this.from_id
  75. }
  76. const secureParams = generateSecureUrlParams(urlParams)
  77. return `${this.link || this.$shareUrl}?params=${secureParams}`
  78. }
  79. },
  80. created() {
  81. uni.$emit('check_login', () => {
  82. this.from_id = getApp().globalData.user_id
  83. })
  84. },
  85. methods: {
  86. handleClose() {
  87. this.$emit('close')
  88. },
  89. handleShare(type) {
  90. switch (type) {
  91. case 'copy':
  92. this.copyToClipboard(this.shareUrl)
  93. break
  94. case 'wechat':
  95. this.shareToWechat()
  96. break
  97. case 'moments':
  98. this.shareToMoments()
  99. break
  100. case 'report':
  101. this.handleReport()
  102. break
  103. case 'qq':
  104. this.qqShare()
  105. break
  106. case 'more':
  107. this.moreShare()
  108. break
  109. }
  110. },
  111. copyToClipboard(text) {
  112. // #ifdef H5
  113. const input = document.createElement('input')
  114. input.value = text
  115. document.body.appendChild(input)
  116. input.select()
  117. document.execCommand('copy')
  118. document.body.removeChild(input)
  119. // #endif
  120. // #ifdef APP-PLUS || MP
  121. uni.setClipboardData({
  122. data: text,
  123. success: () => {
  124. uni.showToast({
  125. title: '链接已复制',
  126. icon: 'success'
  127. })
  128. },
  129. fail: () => {
  130. uni.showToast({
  131. title: '复制失败',
  132. icon: 'none'
  133. })
  134. }
  135. })
  136. // #endif
  137. },
  138. shareToWechat() {
  139. // #ifdef APP-PLUS
  140. uni.share({
  141. provider: 'weixin',
  142. scene: 'WXSceneSession',
  143. type: 0,
  144. title: this.shareTitle,
  145. summary: this.shareDesc,
  146. imageUrl: this.shareImg || this.$icon,
  147. href: this.shareUrl,
  148. success: () => {
  149. uni.showToast({
  150. title: '分享成功',
  151. icon: 'success'
  152. })
  153. },
  154. fail: (err) => {
  155. uni.showToast({
  156. title: '分享失败',
  157. icon: 'none'
  158. })
  159. console.error('分享失败:', err)
  160. }
  161. })
  162. // #endif
  163. // #ifdef H5
  164. if (typeof wx !== 'undefined') {
  165. wx.ready(() => {
  166. wx.updateAppMessageShareData({
  167. title: this.shareTitle,
  168. desc: this.shareDesc,
  169. link: this.shareUrl,
  170. imgUrl: this.shareImg || this.$icon,
  171. success: () => {
  172. uni.showToast({
  173. title: '分享成功',
  174. icon: 'success'
  175. })
  176. },
  177. fail: (err) => {
  178. uni.showToast({
  179. title: '分享失败',
  180. icon: 'none'
  181. })
  182. console.error('分享失败:', err)
  183. }
  184. })
  185. })
  186. } else {
  187. uni.showToast({
  188. title: '请在微信中打开',
  189. icon: 'none'
  190. })
  191. }
  192. // #endif
  193. // #ifdef MP-WEIXIN
  194. uni.showShareMenu({
  195. withShareTicket: true,
  196. menus: ['shareAppMessage'],
  197. success: () => {
  198. uni.updateShareMenu({
  199. withShareTicket: true,
  200. isPrivateMessage: true
  201. })
  202. }
  203. })
  204. // #endif
  205. },
  206. shareToMoments() {
  207. // #ifdef APP-PLUS
  208. uni.share({
  209. provider: 'weixin',
  210. scene: 'WXSenceTimeline',
  211. type: 0,
  212. title: this.shareTitle,
  213. summary: this.shareDesc,
  214. imageUrl: this.shareImg || this.$icon,
  215. href: this.shareUrl,
  216. success: () => {
  217. uni.showToast({
  218. title: '分享成功',
  219. icon: 'success'
  220. })
  221. },
  222. fail: (err) => {
  223. uni.showToast({
  224. title: '分享失败',
  225. icon: 'none'
  226. })
  227. console.error('分享失败:', err)
  228. }
  229. })
  230. // #endif
  231. // #ifdef H5
  232. if (typeof wx !== 'undefined') {
  233. wx.ready(() => {
  234. wx.updateTimelineShareData({
  235. title: this.shareTitle,
  236. link: this.shareUrl,
  237. imgUrl: this.shareImg || this.$icon,
  238. success: () => {
  239. uni.showToast({
  240. title: '分享成功',
  241. icon: 'success'
  242. })
  243. },
  244. fail: (err) => {
  245. uni.showToast({
  246. title: '分享失败',
  247. icon: 'none'
  248. })
  249. console.error('分享失败:', err)
  250. }
  251. })
  252. })
  253. } else {
  254. uni.showToast({
  255. title: '请在微信中打开',
  256. icon: 'none'
  257. })
  258. }
  259. // #endif
  260. // #ifdef MP-WEIXIN
  261. uni.showShareMenu({
  262. withShareTicket: true,
  263. menus: ['shareTimeline']
  264. })
  265. // #endif
  266. },
  267. handleReport() {
  268. // 实现举报功能
  269. uni.$emit('check_login', () => {
  270. uni.navigateTo({
  271. url: '/pages/my/feedback?isReportContent=' + this.isReportContent
  272. })
  273. })
  274. },
  275. qqShare() {
  276. // #ifdef APP-PLUS
  277. uni.share({
  278. provider: 'qq',
  279. type: 0,
  280. title: this.shareTitle,
  281. summary: this.shareDesc,
  282. imageUrl: this.shareImg || this.$icon,
  283. href: this.shareUrl,
  284. success: () => {
  285. uni.showToast({
  286. title: '分享成功',
  287. icon: 'success'
  288. })
  289. }
  290. })
  291. // #endif
  292. },
  293. moreShare() {
  294. // 更多分享方式
  295. uni.showToast({
  296. title: '更多分享功能开发中',
  297. icon: 'none'
  298. })
  299. }
  300. }
  301. }
  302. </script>
  303. <style scoped>
  304. .share-modal
  305. {
  306. position: fixed;
  307. left: 0;
  308. right: 0;
  309. top: 0;
  310. bottom: 0;
  311. z-index: 1000;
  312. }
  313. .modal-overlay
  314. {
  315. position: absolute;
  316. left: 0;
  317. right: 0;
  318. top: 0;
  319. bottom: 0;
  320. background-color: rgba(0, 0, 0, 0.4);
  321. }
  322. .modal-content
  323. {
  324. position: absolute;
  325. left: 0;
  326. right: 0;
  327. bottom: 0;
  328. background-color: #f2f6f2;
  329. border-top-left-radius: 12px;
  330. border-top-right-radius: 12px;
  331. padding: 16px;
  332. }
  333. /* 弹出动画 */
  334. .fade-up-enter-active, .fade-up-leave-active
  335. {
  336. transition: all 0.3s cubic-bezier(.55, 0, .1, 1);
  337. }
  338. .fade-up-enter, .fade-up-leave-to
  339. {
  340. opacity: 0;
  341. transform: translateY(100%);
  342. }
  343. .fade-up-leave, .fade-up-enter-to
  344. {
  345. opacity: 1;
  346. transform: translateY(0);
  347. }
  348. .share-options
  349. {
  350. display: flex;
  351. flex-wrap: wrap;
  352. padding: 8px 16px;
  353. }
  354. .share-item
  355. {
  356. width: 25%;
  357. display: flex;
  358. flex-direction: column;
  359. align-items: center;
  360. gap: 8px;
  361. padding: 8px 0;
  362. }
  363. .share-icon
  364. {
  365. width: 44px;
  366. height: 44px;
  367. display: flex;
  368. justify-content: center;
  369. align-items: center;
  370. }
  371. .share-icon image
  372. {
  373. width: 100%;
  374. height: 100%;
  375. }
  376. .option-title
  377. {
  378. font-size: 13px;
  379. color: #333333;
  380. text-align: center;
  381. }
  382. </style>