Files
digital-psychology/apps/user-h5/src/components/ShareSheet.vue
T
jackyu66gitandCursor bd22d9dddd feat: P1 合盘/星座/问答与测测完整设计包及模拟器取证工具
落地 synastry/star/ask API 与 H5 页面,补齐 cece-frontend-re complete-design 证据文档,并加入 Android 模拟器截图抓取脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 11:37:53 +08:00

112 lines
3.4 KiB
Vue

<template>
<div v-if="open" class="mask" @click.self="$emit('close')">
<div class="sheet">
<header>
<h3>分享卡</h3>
<button type="button" class="x" @click="$emit('close')">关闭</button>
</header>
<ShareCard v-bind="card" />
<p v-if="status" class="status">{{ status }}</p>
<div class="btns">
<button type="button" :disabled="busy" @click="copyLink">复制链接</button>
<button v-if="canNative" type="button" class="ghost" :disabled="busy" @click="nativeShare">系统分享</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import ShareCard from './ShareCard.vue'
import { buildSharePath, copyText, type SharePayload } from '../lib/shareLink'
const props = defineProps<{
open: boolean
payload: SharePayload | null
}>()
defineEmits<{ close: [] }>()
const busy = ref(false)
const status = ref('')
const card = computed(() => {
const p = props.payload
if (!p) return {}
if (p.type === 'portrait') {
return {
type: 'portrait' as const,
title: p.title,
line: p.line,
keywords: p.keywords,
ctaLabel: '查看完整成长报告',
}
}
return {
type: 'relation' as const,
title: '我的方式 vs TA 的方式',
rows: [
p.me ? `我的沟通方式:${p.me}` : '',
p.other ? `TA 的沟通方式:${p.other}` : '',
p.diff ? `了解彼此 → ${p.diff}` : '了解彼此 → 查看关系理解',
].filter(Boolean),
keywords: p.keywords,
ctaLabel: '了解彼此 → 查看关系理解',
}
})
const canNative = computed(() => typeof navigator !== 'undefined' && typeof navigator.share === 'function')
function absoluteShareURL(): string {
if (!props.payload) return location.origin
return `${location.origin}${buildSharePath(props.payload)}`
}
async function copyLink() {
busy.value = true
status.value = ''
const ok = await copyText(absoluteShareURL())
status.value = ok ? '链接已复制' : '复制失败,请手动复制地址栏'
busy.value = false
}
async function nativeShare() {
if (!props.payload || !navigator.share) return
busy.value = true
status.value = ''
try {
await navigator.share({
title: '愈心谷',
text: props.payload.type === 'relation' ? '看看我们的相处方式' : '看看我的个人画像',
url: absoluteShareURL(),
})
status.value = '已唤起系统分享'
} catch {
status.value = ''
} finally {
busy.value = false
}
}
</script>
<style scoped>
.mask{
position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:40;
display:flex;align-items:flex-end;justify-content:center;padding:12px;
}
.sheet{
width:100%;max-width:430px;background:#fff5f3;border-radius:20px 20px 12px 12px;
padding:16px;max-height:90vh;overflow:auto;
}
header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}
h3{font-size:16px;margin:0}
.x{border:none;background:transparent;color:#999;font-size:13px}
.btns{display:flex;gap:8px;margin-top:14px}
.btns button{
flex:1;border:none;border-radius:22px;padding:11px;color:#fff;font-weight:600;
background:linear-gradient(135deg,#ff7a6e,var(--yxg-pri));
}
.btns button.ghost{background:#fff;color:var(--yxg-pri);border:1px solid #f0c0bc}
.btns button:disabled{opacity:.6}
.status{font-size:12px;color:#888;margin-top:10px;text-align:center}
</style>