Sfoglia il codice sorgente

fix: 修复 SharePanel.vue 导入路径错误

从 ../../utils/share 改为 ../utils/share

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MyFramework User 3 mesi fa
parent
commit
28e3f0b5f9
1 ha cambiato i file con 400 aggiunte e 0 eliminazioni
  1. 400 0
      my-uniapp-vue3/src/components/SharePanel.vue

+ 400 - 0
my-uniapp-vue3/src/components/SharePanel.vue

@@ -0,0 +1,400 @@
+<template>
+  <view v-if="visible" class="share-overlay" @click="close">
+    <view class="share-mask" />
+    <view class="share-panel" :class="{ 'h5-panel': platform === 'h5' }" @click.stop>
+      <!-- 面板头部 -->
+      <view class="panel-header">
+        <text class="panel-title">分享到</text>
+        <view class="panel-close" @click="close">
+          <text>✕</text>
+        </view>
+      </view>
+
+      <!-- 分享渠道列表 -->
+      <view class="share-channels">
+        <!-- H5 端:浏览器原生分享 -->
+        <view v-if="platform === 'h5'" class="channel-grid">
+          <view class="channel-item" @click="doWebShare">
+            <view class="channel-icon" style="background: linear-gradient(135deg, #667eea, #764ba2)">
+              <text class="icon-emoji">📤</text>
+            </view>
+            <text class="channel-label">分享给好友</text>
+          </view>
+          <view class="channel-item" @click="doCopyLink">
+            <view class="channel-icon" style="background: #6b7280">
+              <text class="icon-emoji">🔗</text>
+            </view>
+            <text class="channel-label">复制链接</text>
+          </view>
+          <view v-if="showPosterBtn" class="channel-item" @click="$emit('generatePoster')">
+            <view class="channel-icon" style="background: linear-gradient(135deg, #f093fb, #f5576c)">
+              <text class="icon-emoji">🖼️</text>
+            </view>
+            <text class="channel-label">生成海报</text>
+          </view>
+        </view>
+
+        <!-- App 端:原生分享渠道 -->
+        <view v-if="platform === 'app'" class="channel-grid">
+          <view class="channel-item" @click="doShareToWechat">
+            <view class="channel-icon wechat">
+              <text class="icon-emoji">💬</text>
+            </view>
+            <text class="channel-label">微信好友</text>
+          </view>
+          <view class="channel-item" @click="doShareToTimeline">
+            <view class="channel-icon timeline">
+              <text class="icon-emoji">🌐</text>
+            </view>
+            <text class="channel-label">朋友圈</text>
+          </view>
+          <view class="channel-item" @click="doShareToQQ">
+            <view class="channel-icon qq">
+              <text class="icon-emoji">🐧</text>
+            </view>
+            <text class="channel-label">QQ好友</text>
+          </view>
+          <view class="channel-item" @click="doShareWithSystem">
+            <view class="channel-icon" style="background: #374151">
+              <text class="icon-emoji">📲</text>
+            </view>
+            <text class="channel-label">更多</text>
+          </view>
+          <view class="channel-item" @click="doCopyLink">
+            <view class="channel-icon" style="background: #6b7280">
+              <text class="icon-emoji">🔗</text>
+            </view>
+            <text class="channel-label">复制链接</text>
+          </view>
+          <view v-if="showPosterBtn" class="channel-item" @click="$emit('generatePoster')">
+            <view class="channel-icon poster">
+              <text class="icon-emoji">🖼️</text>
+            </view>
+            <text class="channel-label">生成海报</text>
+          </view>
+        </view>
+
+        <!-- 小程序端:分享消息卡片 + 复制链接 -->
+        <view v-if="platform === 'mp-weixin'" class="channel-grid">
+          <!-- 小程序分享消息 -->
+          <button class="channel-item mp-share-btn" open-type="share" @click="onMiniProgramShare">
+            <view class="channel-icon wechat">
+              <text class="icon-emoji">💬</text>
+            </view>
+            <text class="channel-label">发送给朋友</text>
+          </button>
+          <view class="channel-item" @click="doCopyLink">
+            <view class="channel-icon" style="background: #6b7280">
+              <text class="icon-emoji">🔗</text>
+            </view>
+            <text class="channel-label">复制链接</text>
+          </view>
+          <view v-if="showPosterBtn" class="channel-item" @click="$emit('generatePoster')">
+            <view class="channel-icon poster">
+              <text class="icon-emoji">🖼️</text>
+            </view>
+            <text class="channel-label">生成海报</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- 面板底部 -->
+      <view class="panel-footer">
+        <text class="footer-tip">分享给好友,一起听好书</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue';
+import type { ShareInfo } from '../utils/share';
+import {
+  getSharePlatform,
+  shareToWechatSession,
+  shareToWechatTimeline,
+  shareToQQ,
+  shareWithSystem,
+  copyShareLink,
+  webShareApi,
+  trackShareAction,
+} from '../utils/share';
+
+const props = defineProps<{
+  visible: boolean;
+  shareInfo: ShareInfo;
+  /** 专辑/章节 ID,用于记录分享行为 */
+  audioId?: number;
+  /** 是否显示生成海报按钮 */
+  showPosterBtn?: boolean;
+}>();
+
+const emit = defineEmits<{
+  (e: 'close'): void;
+  (e: 'generatePoster'): void;
+  /** 小程序分享时触发,父组件需在 onShareAppMessage 中使用 */
+  (e: 'miniProgramShare', info: ShareInfo): void;
+  (e: 'shareSuccess', channel: string): void;
+  (e: 'shareFail', err: any): void;
+}>();
+
+const platform = ref<'app' | 'mp-weixin' | 'h5'>('h5');
+
+onMounted(() => {
+  platform.value = getSharePlatform();
+});
+
+function close() {
+  emit('close');
+}
+
+function handleShareSuccess(channel: string) {
+  if (props.audioId) {
+    trackShareAction(props.audioId, channel);
+  }
+  emit('shareSuccess', channel);
+}
+
+function handleShareError(err: any) {
+  console.error('[SharePanel] 分享失败:', err);
+  emit('shareFail', err);
+  uni.showToast({ title: '分享失败,请重试', icon: 'none' });
+}
+
+// App: 微信好友
+async function doShareToWechat() {
+  try {
+    await shareToWechatSession(props.shareInfo);
+    handleShareSuccess('wechat-session');
+  } catch (err) {
+    handleShareError(err);
+  }
+}
+
+// App: 朋友圈
+async function doShareToTimeline() {
+  try {
+    await shareToWechatTimeline(props.shareInfo);
+    handleShareSuccess('wechat-timeline');
+  } catch (err) {
+    handleShareError(err);
+  }
+}
+
+// App: QQ
+async function doShareToQQ() {
+  try {
+    await shareToQQ(props.shareInfo);
+    handleShareSuccess('qq');
+  } catch (err) {
+    handleShareError(err);
+  }
+}
+
+// App: 系统分享
+async function doShareWithSystem() {
+  try {
+    await shareWithSystem(props.shareInfo);
+    handleShareSuccess('system');
+  } catch (err) {
+    handleShareError(err);
+  }
+}
+
+// 复制链接 (全平台)
+async function doCopyLink() {
+  try {
+    await copyShareLink(props.shareInfo);
+    handleShareSuccess('copy-link');
+    close();
+  } catch (err) {
+    handleShareError(err);
+  }
+}
+
+// H5: 浏览器原生分享
+async function doWebShare() {
+  try {
+    await webShareApi(props.shareInfo);
+    handleShareSuccess('web-share');
+    close();
+  } catch (err) {
+    // 降级为复制链接
+    doCopyLink();
+  }
+}
+
+// 小程序: 发送给朋友
+function onMiniProgramShare() {
+  emit('miniProgramShare', props.shareInfo);
+  handleShareSuccess('mp-share');
+  close();
+}
+</script>
+
+<style scoped>
+.share-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 2000;
+  display: flex;
+  align-items: flex-end;
+  justify-content: center;
+}
+
+.share-mask {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.5);
+  animation: fadeIn 0.3s ease;
+}
+
+.share-panel {
+  position: relative;
+  width: 100%;
+  max-width: 500px;
+  background: #ffffff;
+  border-radius: 32rpx 32rpx 0 0;
+  padding: 0 0 env(safe-area-inset-bottom);
+  animation: slideUp 0.3s ease;
+}
+
+.panel-header {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 32rpx 32rpx 24rpx;
+  position: relative;
+}
+
+.panel-title {
+  font-size: 32rpx;
+  font-weight: 600;
+  color: #1f2937;
+}
+
+.panel-close {
+  position: absolute;
+  right: 32rpx;
+  top: 50%;
+  transform: translateY(-50%);
+  width: 48rpx;
+  height: 48rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 32rpx;
+  color: #9ca3af;
+}
+
+.share-channels {
+  padding: 16rpx 32rpx 24rpx;
+}
+
+.channel-grid {
+  display: grid;
+  grid-template-columns: repeat(4, 1fr);
+  gap: 24rpx;
+}
+
+.channel-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 12rpx;
+  cursor: pointer;
+  transition: transform 0.15s ease;
+}
+
+.channel-item:active {
+  transform: scale(0.92);
+}
+
+.mp-share-btn {
+  background: transparent;
+  border: none;
+  padding: 0;
+  margin: 0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 12rpx;
+  line-height: 1;
+  font-size: inherit;
+}
+
+.mp-share-btn::after {
+  border: none;
+}
+
+.channel-icon {
+  width: 100rpx;
+  height: 100rpx;
+  border-radius: 24rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+}
+
+.channel-icon.wechat {
+  background: linear-gradient(135deg, #07c160, #06ad56);
+}
+
+.channel-icon.timeline {
+  background: linear-gradient(135deg, #1aad19, #07c160);
+}
+
+.channel-icon.qq {
+  background: linear-gradient(135deg, #12b7f5, #0d8bdb);
+}
+
+.channel-icon.poster {
+  background: linear-gradient(135deg, #f093fb, #f5576c);
+}
+
+.icon-emoji {
+  font-size: 48rpx;
+}
+
+.channel-label {
+  font-size: 22rpx;
+  color: #4b5563;
+  text-align: center;
+  line-height: 1.3;
+}
+
+.panel-footer {
+  padding: 16rpx 32rpx 48rpx;
+  text-align: center;
+}
+
+.footer-tip {
+  font-size: 24rpx;
+  color: #9ca3af;
+}
+
+/* H5 端宽度适配 */
+.h5-panel {
+  max-width: 420px;
+  margin: 0 auto;
+  border-radius: 24rpx;
+  margin-bottom: 16rpx;
+}
+
+@keyframes fadeIn {
+  from { opacity: 0; }
+  to { opacity: 1; }
+}
+
+@keyframes slideUp {
+  from { transform: translateY(100%); }
+  to { transform: translateY(0); }
+}
+</style>