zbb 3 veckor sedan
förälder
incheckning
c6ab4ee923
7 ändrade filer med 331 tillägg och 1518 borttagningar
  1. 58 0
      components/shareButton/index.vue
  2. 78 593
      pages/index/index.vue
  3. 92 715
      pages/mine/card.vue
  4. 2 2
      pages/mine/index.vue
  5. 59 191
      pages/mine/userCard.vue
  6. 26 14
      pages/splash/splash.vue
  7. 16 3
      store/modules/user.js

+ 58 - 0
components/shareButton/index.vue

@@ -0,0 +1,58 @@
+<template>
+	<view class="control-card">
+		<button class="item" open-type="share"> 发名片 </button>
+	</view>
+</template>
+
+<script setup>
+	import {
+		onShareAppMessage
+	} from '@dcloudio/uni-app';
+	import {
+		useUserStore
+	} from '@/store/modules/user.js'
+	import {
+		storeToRefs
+	} from 'pinia'
+	const userStore = useUserStore()
+	const {
+		cardInfo,
+		cardImage
+	} = storeToRefs(userStore)
+	onShareAppMessage(() => {
+		return {
+			userName: '小程序',
+			path: 'pages/splash/splash?userId=' + cardInfo.value.userId,
+			imageUrl: cardImage.value
+		};
+	});
+</script>
+
+<style lang="scss" scoped>
+	.control-card {
+		background-color: #fff;
+		border-radius: 0 0 24rpx 24rpx;
+		position: relative;
+		z-index: 1;
+		bottom: 24rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-around;
+		padding: 48rpx 40rpx 24rpx;
+
+		.item {
+			display: flex;
+			width: 100%;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			gap: 4rpx;
+			background-color: #155DFC;
+			font-size: 28rpx !important;
+			border-radius: 40rpx;
+			height: 80rpx;
+			color: #fff;
+		}
+	}
+
+</style>

+ 78 - 593
pages/index/index.vue

@@ -3,7 +3,7 @@
 
 		<!-- 顶部背景 -->
 		<image class="top-bg" src="/static/image/home/top-bg.png" />
-		<NavBar :title="'我的名片'" :show_back="false" color="#fff" :fixed="true" :bg="'transparent'">
+		<NavBar :title="'首页'" :show_back="false" color="#fff" :fixed="true" :bg="'transparent'">
 			<template #left>
 				<!-- <view class="left-title">{{appName}}</view> -->
 			</template>
@@ -45,11 +45,7 @@
 					</view>
 				</view>
 			</view>
-
-
-			<view class="control-card">
-				<view class="item" @click="shareUserCard">发名片</view>
-			</view>
+			<ShareButton />
 		</view>
 		<view class="card-content">
 			<!-- 企业简介 -->
@@ -60,7 +56,6 @@
 				<rich-text class="company-text" :nodes="companyInfo.introduce"></rich-text>
 			</view>
 		</view>
-
 		<!-- 隐藏的 Canvas 用于生成名片快照 -->
 		<canvas id="posterCanvas" type="2d"
 			style="position: fixed; left: -9999px; top: -9999px; width: 750px; height: 780px;"></canvas>
@@ -69,114 +64,6 @@
 		<view class="hidden-canvas-box">
 			<canvas id="qrPosterCanvas" type="2d" style="width: 600px; height: 700px;"></canvas>
 		</view>
-
-		<!-- 名片分享弹窗 -->
-		<uni-popup ref="cardPopup" type="center">
-			<view class="card-popup">
-				<!-- 顶部装饰条 -->
-				<view class="popup-decor">
-					<view class="decor-line"></view>
-				</view>
-
-				<!-- 标题 -->
-				<view class="popup-title-box">
-					<text class="popup-title">我的名片</text>
-					<text class="popup-desc">分享给朋友或保存到相册</text>
-				</view>
-
-				<!-- 名片卡片 -->
-				<view class="card-box">
-					<view class="user-card">
-						<!-- 背景图 -->
-						<image class="user-card-bg" src="/static/image/home/usecard-bg.png" mode="aspectFill" />
-
-						<!-- 左上:姓名 + 职位 -->
-						<view class="user-header">
-							<view class="name-row">
-								<text class="user-name">{{ cardInfo.nickName || '用户' }}</text>
-								<text class="user-role">{{ cardInfo.postName || '职位' }}</text>
-							</view>
-							<text class="company-name">{{ cardInfo.companyName || '公司名称' }}</text>
-						</view>
-
-						<!-- 右上:头像 -->
-						<view class="user-avatar-box">
-							<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="120"
-								:badge-src="'/static/image/public/badge-icon.png'" :badge-size="50" />
-						</view>
-
-						<!-- 左下:联系方式 -->
-						<view class="user-contact">
-							<view class="contact-row" @click="makeCall">
-								<view class="contact-icon">
-									<uni-icons type="phone" :size="16" color="#4080FF"></uni-icons>
-								</view>
-								<text class="contact-text">{{ cardInfo.phonenumber || '暂无电话' }}</text>
-							</view>
-							<view class="contact-row">
-								<view class="contact-icon">
-									<uni-icons type="email" :size="16" color="#4080FF"></uni-icons>
-								</view>
-								<text class="contact-text">{{ cardInfo.email || '暂无邮箱' }}</text>
-							</view>
-							<view class="contact-row">
-								<view class="contact-icon">
-									<uni-icons type="location" :size="16" color="#4080FF"></uni-icons>
-								</view>
-								<text class="contact-text">{{ cardInfo.companyAddress || '暂无地址' }}</text>
-							</view>
-						</view>
-					</view>
-				</view>
-
-				<!-- 操作按钮 -->
-				<view class="popup-btns">
-					<view class="btn btn-cancel" @click="closeCardPopup">
-						<text class="btn-text">取消</text>
-					</view>
-					<view class="btn btn-confirm" @click="shareCardConfirm">
-						<text class="btn-text">分享名片</text>
-					</view>
-				</view>
-
-				<!-- 底部全宽按钮 -->
-				<view class="popup-footer">
-					<view class="btn btn-full" @click="shareCardConfirm">
-						<uni-icons type="paperplane" size="22" color="#ffffff"></uni-icons>
-						<text class="btn-text">立即分享名片给朋友</text>
-					</view>
-				</view>
-			</view>
-		</uni-popup>
-
-		<!-- 二维码弹窗 -->
-		<uni-popup ref="qrPopup" type="center">
-			<view class="qr-popup">
-				<view class="qr-header">
-					<text class="qr-title">名片二维码</text>
-					<text class="qr-subtitle">扫一扫查看我的名片信息</text>
-				</view>
-				<view class="qr-content">
-					<image v-if="qrInfo && qrInfo.image"
-						:src="qrInfo.image.startsWith('data:') ? qrInfo.image : 'data:image/png;base64,' + qrInfo.image"
-						class="qr-image" mode="aspectFit" />
-					<view v-else class="qr-loading">
-						<text>加载中...</text>
-					</view>
-				</view>
-				<view class="qr-actions">
-					<view class="action-btn" @click="saveQRCode">
-						<uni-icons type="download" size="24" color="#4080FF"></uni-icons>
-						<text class="action-text">保存到相册</text>
-					</view>
-					<view class="action-btn" @click="handleShare">
-						<uni-icons type="paperplane" size="24" color="#4080FF"></uni-icons>
-						<!-- <button open-type="share" class="action-text">分享给好友</button> -->
-						<text class="action-text">分享给好友</text>
-					</view>
-				</view>
-			</view>
-		</uni-popup>
 	</view>
 </template>
 
@@ -191,58 +78,29 @@
 	import NavBar from '@/components/nav-bar/index.vue'
 	import UserAvatar from '@/components/user-avatar/index.vue'
 	import EmptyState from '@/components/empty-state/index.vue'
+	import ShareButton from '@/components/shareButton/index.vue'
 	import {
 		useUserStore
 	} from '@/store/modules/user.js'
 	import {
 		storeToRefs
 	} from 'pinia'
-	import {
-		generateCardPoster,
-		savePosterToAlbum,
-	} from '@/utils/poster.js'
-	import {
-		getCurrentConfig
-	} from '@/config/index.js'
+
 	// 使用 Pinia 管理用户状态
 	const userStore = useUserStore()
 	const {
 		cardInfo,
 		companyInfo,
-		qrInfo,
+		cardImage
 	} = storeToRefs(userStore)
-
-	const currentTab = ref('company')
-	// 名片快照图片路径
-	const cardSnapshot = ref('')
-	// 二维码海报图片路径
-	const qrCodePoster = ref('')
-	// 二维码弹窗引用
-	const qrPopup = ref(null)
-	const cardPopup = ref(null)
-
-	const productList = ref([])
-	let imageUrl = ref('')
+	let appName = ref('')
 	onShareAppMessage(() => {
 		return {
 			userName: '小程序',
 			path: 'pages/splash/splash?userId=' + cardInfo.value.userId,
-			imageUrl: imageUrl.value
+			imageUrl: cardImage.value
 		};
 	});
-	let appName = ref('')
-	onMounted(async () => {
-		const config = await getCurrentConfig()
-		appName.value = config.appName
-		// 如果没有数据,重新获取
-		if (!cardInfo.value.nickName) {
-			await userStore.queryCardInfo()
-		}
-		if (!companyInfo.value.name) {
-			await userStore.queryCompanyInfo()
-		}
-	})
-
 	const makeCall = () => {
 		if (cardInfo.value.phonenumber) {
 			uni.makePhoneCall({
@@ -255,178 +113,6 @@
 			})
 		}
 	}
-
-	// 生成名片快照并保存
-	const saveCard = async () => {
-		uni.showLoading({
-			title: '生成快照中...',
-			mask: true
-		})
-
-		try {
-
-			// 生成名片快照
-			const snapshotPath = await generateCardPoster(cardInfo.value, qrInfo.value)
-			console.log(snapshotPath, "snapshotPathsnapshotPathsnapshotPath");
-			// 用属性接收快照路径
-			cardSnapshot.value = snapshotPath
-
-			console.log('名片快照路径:', cardSnapshot.value)
-
-			// 保存到相册
-			await savePosterToAlbum(snapshotPath)
-
-			uni.hideLoading()
-			uni.showToast({
-				title: '已保存到相册',
-				icon: 'success'
-			})
-		} catch (error) {
-			console.error('保存失败:', error)
-			uni.hideLoading()
-			uni.showToast({
-				title: '保存失败,请重试',
-				icon: 'none'
-			})
-		}
-	}
-
-	// 显示二维码
-	const showQRCode = async () => {
-		// 打开弹窗
-		qrPopup.value.open()
-	}
-
-	const close = async () => {
-		cardPopup.value.close()
-	}
-
-	const shareUserCard = async () => {
-		const snapshotPath = await generateCardPoster(cardInfo.value, qrInfo.value)
-		imageUrl.value = snapshotPath
-
-
-
-
-
-
-
-		// wx.showShareImageMenu({
-		// 	path: snapshotPath,
-		// 	entrancePath: 'pages/splash/splash?userId=' + cardInfo.value.userId,
-		// 	success: () => {
-		// 		console.log('分享菜单已调起');
-		// 	},
-		// 	fail: (err) => {
-		// 		console.error('调起分享菜单失败', err);
-		// 	}
-		// });
-
-	}
-
-
-	const base64ToImage = async (base64Data) => {
-		return new Promise((resolve, reject) => {
-			try {
-				const filePath = `${wx.env.USER_DATA_PATH}/temp_image.png`;
-				let pureBase64Data = base64Data
-				if (pureBase64Data.startsWith('data:image')) {
-					pureBase64Data = pureBase64Data.replace(/^data:image\/\w+;base64,/, '');
-				}
-				const fs = wx.getFileSystemManager();
-				fs.writeFile({
-					filePath: filePath,
-					data: pureBase64Data,
-					encoding: 'base64',
-					success: (res) => {
-						console.log("resresweresresresres", res);
-						resolve(filePath)
-					},
-					fail: (err) => {
-						console.error('写入临时文件失败', err);
-					}
-				});
-			} catch (error) {
-				console.error('base64 转换异常:', error)
-				reject(error)
-			}
-		})
-	}
-	const handleShare = async () => {
-		const qrFilePath = await base64ToTempFile(qrInfo.value.image)
-		wx.showShareImageMenu({
-			path: qrFilePath,
-			entrancePath: 'pages/splash/splash?userId=' + cardInfo.value.userId,
-			success: () => {
-				console.log('分享菜单已调起');
-			},
-			fail: (err) => {
-				console.error('调起分享菜单失败', err);
-			}
-		});
-	}
-	// 保存二维码到相册
-	const saveQRCode = async () => {
-		if (!qrInfo.value.image) {
-			uni.showToast({
-				title: '二维码图片不存在',
-				icon: 'none'
-			})
-			return
-		}
-
-		try {
-			// 将 base64 转换为临时文件
-			const qrFilePath = await base64ToTempFile(qrInfo.value.image)
-			// 保存到相册
-			await savePosterToAlbum(qrFilePath)
-			uni.showToast({
-				title: '已保存到相册',
-				icon: 'success'
-			})
-		} catch (error) {
-			console.error('保存失败:', error)
-			uni.showToast({
-				title: '保存失败,请重试',
-				icon: 'none'
-			})
-		}
-	}
-
-
-
-	const switchTab = (tab) => {
-		currentTab.value = tab
-	}
-	// 将 base64 转换为临时文件路径
-	const base64ToTempFile = async (base64Data) => {
-		return new Promise((resolve, reject) => {
-			try {
-				// 移除 data:image/png;base64, 前缀(如果有)
-				const pureBase64 = base64Data.replace(/^data:image\/\w+;base64,/, '')
-
-				const fileName = `${Date.now()}_qrcode.png`
-				const filePath = `${wx.env.USER_DATA_PATH}/${fileName}`
-				const fs = uni.getFileSystemManager()
-				fs.writeFile({
-					filePath: filePath,
-					data: pureBase64,
-					encoding: 'base64',
-					success: () => {
-						console.log('base64 转文件成功:', filePath)
-						resolve(filePath)
-					},
-					fail: (err) => {
-						console.error('base64 转文件失败:', err)
-						reject(err)
-					}
-				})
-			} catch (error) {
-				console.error('base64 转换异常:', error)
-				reject(error)
-			}
-		})
-	}
 </script>
 
 <style lang="scss" scoped>
@@ -514,124 +200,93 @@
 		}
 	}
 
+	.page-top {
+		margin: 0 24rpx 24rpx;
 
-	.user-card {
-		position: relative;
-		z-index: 2;
-		width: 100%;
-		height: 400rpx;
-		box-sizing: border-box;
-		padding: 80rpx 40rpx 32rpx;
-		position: relative;
-
-		.user-card-bg {
-			position: absolute;
-			top: 0;
-			left: 0;
+		.user-card {
+			position: relative;
+			z-index: 2;
 			width: 100%;
-			height: 100%;
-			z-index: 0;
-		}
-
-		view,
-		text {
+			height: 400rpx;
+			box-sizing: border-box;
+			padding: 80rpx 40rpx 32rpx;
 			position: relative;
-			z-index: 1;
-		}
 
-		.user-header {
-			.name-row {
-				display: flex;
-				align-items: center;
-				margin-bottom: 16rpx;
-
-				.user-name {
-					font-size: 44rpx;
-					font-weight: 700;
-					color: #202020;
-					line-height: 1.2;
-				}
-
-				.user-role {
-					margin-left: 16rpx;
-					padding: 6rpx 16rpx;
-					background: rgba(68, 110, 255, 0.10);
-					border-radius: 8rpx;
-					font-size: 24rpx;
-					font-weight: 500;
-					color: #446eff;
-				}
+			.user-card-bg {
+				position: absolute;
+				top: 0;
+				left: 0;
+				width: 100%;
+				height: 100%;
+				z-index: 0;
 			}
 
-			.company-name {
-				font-size: 28rpx;
-				font-weight: 500;
-				color: #666666;
-				line-height: 1.4;
+			view,
+			text {
+				position: relative;
+				z-index: 1;
 			}
-		}
-
-		.avatar-wrapper {
-			position: absolute;
-			top: 32rpx;
-			right: 36rpx;
-			z-index: 2;
-		}
 
-		.user-contact {
-			margin-top: 32rpx;
+			.user-header {
+				.name-row {
+					display: flex;
+					align-items: center;
+					margin-bottom: 16rpx;
 
-			.contact-row {
-				display: flex;
-				align-items: center;
-				margin-bottom: 16rpx;
+					.user-name {
+						font-size: 44rpx;
+						font-weight: 700;
+						color: #202020;
+						line-height: 1.2;
+					}
 
-				uni-icons {
-					margin-right: 12rpx;
-					flex-shrink: 0;
+					.user-role {
+						margin-left: 16rpx;
+						padding: 6rpx 16rpx;
+						background: rgba(68, 110, 255, 0.10);
+						border-radius: 8rpx;
+						font-size: 24rpx;
+						font-weight: 500;
+						color: #446eff;
+					}
 				}
 
-				.contact-text {
-					font-size: 26rpx;
+				.company-name {
+					font-size: 28rpx;
+					font-weight: 500;
 					color: #666666;
+					line-height: 1.4;
 				}
 			}
-		}
 
-	}
-
-	/* 用户信息卡片 */
-	.page-top {
-		margin: 0 24rpx 24rpx;
+			.avatar-wrapper {
+				position: absolute;
+				top: 32rpx;
+				right: 36rpx;
+				z-index: 2;
+			}
 
+			.user-contact {
+				margin-top: 32rpx;
 
-		.control-card {
-			background-color: #fff;
-			border-radius: 0 0 24rpx 24rpx;
-			position: relative;
-			z-index: 1;
-			bottom: 24rpx;
-			display: flex;
-			align-items: center;
-			justify-content: space-around;
-			padding: 48rpx 40rpx 24rpx;
+				.contact-row {
+					display: flex;
+					align-items: center;
+					margin-bottom: 16rpx;
 
-			.item {
-				display: flex;
-				width: 100%;
-				flex-direction: column;
-				align-items: center;
-				justify-content: center;
-				gap: 4rpx;
-				background-color: #155DFC;
-				font-size: 28rpx !important;
-				border-radius: 40rpx;
-				height: 80rpx;
-				color: #fff;
+					uni-icons {
+						margin-right: 12rpx;
+						flex-shrink: 0;
+					}
 
+					.contact-text {
+						font-size: 26rpx;
+						color: #666666;
+					}
+				}
 			}
-		}
 
+		}
 
 	}
 
@@ -645,26 +300,29 @@
 		position: relative;
 		bottom: 24rpx;
 		z-index: 2;
+
 		// 企业简介
 		.company-section {
 			background: #ffffff;
-			.company-title{
+
+			.company-title {
 				font-weight: bold;
 				font-size: 32rpx;
 				margin-bottom: 12rpx;
 			}
+
 			.company-text {
 				display: block;
 				font-size: 28rpx;
 				color: #666666;
 				line-height: 1.8;
 				margin-bottom: 24rpx;
-		
+
 				::v-deep img {
 					max-width: 100% !important;
 					height: auto !important;
 				}
-		
+
 				&:last-child {
 					margin-bottom: 0;
 				}
@@ -672,90 +330,6 @@
 		}
 	}
 
-	// Tab 切换区域
-	.tab-section {
-		border-radius: 24rpx 24rpx 0 0;
-		padding: 0 40rpx;
-
-		.tab-wrapper {
-			display: flex;
-			border-bottom: 1rpx solid #f0f0f0;
-
-			.tab-item {
-				flex: 1;
-				display: flex;
-				flex-direction: column;
-				align-items: center;
-				padding: 32rpx 0;
-				position: relative;
-
-				.tab-text {
-					font-size: 28rpx;
-					color: #999999;
-					font-weight: 500;
-				}
-
-				&.active .tab-text {
-					color: #333333;
-					font-weight: 600;
-				}
-
-				.tab-indicator {
-					position: absolute;
-					bottom: 0;
-					width: 60rpx;
-					height: 4rpx;
-					background: linear-gradient(90deg, #4A90E2 0%, #6FB3F2 100%);
-					border-radius: 2rpx;
-				}
-			}
-		}
-	}
-
-	// 产品列表
-	.product-list {
-		padding: 24rpx 40rpx;
-
-		.product-item {
-			display: flex;
-			padding: 24rpx 0;
-			border-bottom: 1rpx solid #f0f0f0;
-
-			&:last-child {
-				border-bottom: none;
-			}
-
-			.product-image {
-				width: 160rpx;
-				height: 160rpx;
-				border-radius: 12rpx;
-				background: #f5f5f5;
-				flex-shrink: 0;
-				margin-right: 24rpx;
-			}
-
-			.product-info {
-				flex: 1;
-				display: flex;
-				flex-direction: column;
-				justify-content: center;
-
-				.product-title {
-					font-size: 28rpx;
-					color: #333333;
-					margin-bottom: 12rpx;
-					line-height: 1.5;
-				}
-
-				.product-desc {
-					font-size: 24rpx;
-					color: #999999;
-				}
-			}
-		}
-	}
-
-
 	// 隐藏的 canvas 容器
 	.hidden-canvas-box {
 		position: fixed;
@@ -765,93 +339,4 @@
 		height: 1px;
 		overflow: hidden;
 	}
-
-	// 二维码弹窗样式
-	.qr-popup {
-		margin: 100rpx auto;
-
-		width: 600rpx;
-		background: #ffffff;
-		border-radius: 24rpx;
-		padding: 40rpx;
-		box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);
-
-		.qr-header {
-			text-align: center;
-			margin-bottom: 40rpx;
-
-			.qr-title {
-				display: block;
-				font-size: 36rpx;
-				font-weight: 600;
-				color: #202020;
-				margin-bottom: 12rpx;
-			}
-
-			.qr-subtitle {
-				display: block;
-				font-size: 26rpx;
-				color: #999999;
-			}
-		}
-
-		.qr-content {
-			display: flex;
-			justify-content: center;
-			align-items: center;
-			min-height: 400rpx;
-			margin-bottom: 40rpx;
-
-			.qr-image {
-				width: 400rpx;
-				height: 400rpx;
-				border-radius: 16rpx;
-				box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
-			}
-
-			.qr-loading {
-				display: flex;
-				justify-content: center;
-				align-items: center;
-			}
-		}
-
-		.qr-actions {
-			display: flex;
-			justify-content: space-around;
-
-			.action-btn {
-				display: flex;
-				flex-direction: column;
-				align-items: center;
-				gap: 12rpx;
-				padding: 20rpx 40rpx;
-				border-radius: 16rpx;
-				background: rgba(64, 128, 255, 0.08);
-
-				.action-text {
-					&::after {
-						border: none;
-					}
-
-					line-height: 1;
-					background-color: transparent;
-					font-size: 26rpx;
-					color: #4080FF;
-					font-weight: 500;
-				}
-			}
-		}
-	}
-
-	.resetButton {
-		background-color: transparent !important;
-		border: none !important;
-		display: inline !important;
-
-		&::after {
-			border: none !important;
-		}
-
-	}
 </style>

+ 92 - 715
pages/mine/card.vue

@@ -3,7 +3,11 @@
 
 		<!-- 顶部背景 -->
 		<image class="top-bg" src="/static/image/home/top-bg.png" />
-		<NavBar title="我的名片" color="#FFFFFF" :fixed="true" :bg="'transparent'"></NavBar>
+		<NavBar :title="'我的名片'" color="#fff" :fixed="true" :bg="'transparent'">
+			<template #left>
+				<!-- <view class="left-title">{{appName}}</view> -->
+			</template>
+		</NavBar>
 		<!-- 名片卡片 -->
 		<view class="page-top">
 			<view class="user-card" id="user-card">
@@ -21,7 +25,8 @@
 
 				<!-- 右上:头像 -->
 				<view class="avatar-wrapper">
-					<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="130" :badge-src="'/static/image/public/badge-icon.png'" :badge-size="56" />
+					<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="130"
+						:badge-src="'/static/image/public/badge-icon.png'" :badge-size="56" />
 				</view>
 
 				<!-- 左下:联系方式 -->
@@ -40,22 +45,7 @@
 					</view>
 				</view>
 			</view>
-
-
-			<view class="control-card">
-				<view class="item" @click="shareUserCard">
-					<image src="/static/image/public/card-sharing.png"></image>
-					<text class="text">分享名片</text>
-				</view>
-				<view class="item" @click="saveCard">
-					<image src="/static/image/public/card-save.png"></image>
-					<text class="text">保存名片</text>
-				</view>
-				<view class="item" @click="showQRCode">
-					<image src="/static/image/public/card-qr.png"></image>
-					<text class="text">名片码</text>
-				</view>
-			</view>
+			<ShareButton />
 		</view>
 		<view class="card-content">
 			<!-- 企业简介 -->
@@ -66,141 +56,6 @@
 				<rich-text class="company-text" :nodes="companyInfo.introduce"></rich-text>
 			</view>
 		</view>
-
-		<!-- 隐藏的 Canvas 用于生成名片快照 -->
-		<canvas id="posterCanvas" type="2d"
-			style="position: fixed; left: -9999px; top: -9999px; width: 750px; height: 600px;"></canvas>
-
-		<!-- 隐藏的 Canvas 用于生成二维码海报 -->
-		<view class="hidden-canvas-box">
-			<canvas id="qrPosterCanvas" type="2d" style="width: 600px; height: 700px;"></canvas>
-		</view>
-
-		<!-- 名片分享弹窗 -->
-		<uni-popup ref="cardPopup" type="center">
-			<view class="card-popup">
-				<!-- 顶部装饰条 -->
-				<view class="popup-decor">
-					<view class="decor-line"></view>
-				</view>
-				
-				<!-- 标题 -->
-				<view class="popup-title-box">
-					<text class="popup-title">我的名片</text>
-					<text class="popup-desc">分享给朋友或保存到相册</text>
-				</view>
-				
-				<!-- 名片卡片 -->
-				<view class="card-box">
-					<view class="user-card">
-						<!-- 背景图 -->
-						<image class="user-card-bg" src="/static/image/home/usecard-bg.png" mode="aspectFill" />
-					
-						<!-- 左上:姓名 + 职位 -->
-						<view class="user-header">
-							<view class="name-row">
-								<text class="user-name">{{ cardInfo.nickName || '用户' }}</text>
-								<text class="user-role">{{ cardInfo.postName || '职位' }}</text>
-							</view>
-							<text class="company-name">{{ cardInfo.companyName || '公司名称' }}</text>
-						</view>
-					
-						<!-- 右上:头像 -->
-						<view class="user-avatar-box">
-							<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="120" :badge-src="'/static/image/public/badge-icon.png'" :badge-size="50" />
-						</view>
-					
-						<!-- 左下:联系方式 -->
-						<view class="user-contact">
-							<view class="contact-row" @click="makeCall">
-								<view class="contact-icon">
-									<uni-icons type="phone" :size="16" color="#4080FF"></uni-icons>
-								</view>
-								<text class="contact-text">{{ cardInfo.phonenumber || '暂无电话' }}</text>
-							</view>
-							<view class="contact-row">
-								<view class="contact-icon">
-									<uni-icons type="email" :size="16" color="#4080FF"></uni-icons>
-								</view>
-								<text class="contact-text">{{ cardInfo.email || '暂无邮箱' }}</text>
-							</view>
-							<view class="contact-row">
-								<view class="contact-icon">
-									<uni-icons type="location" :size="16" color="#4080FF"></uni-icons>
-								</view>
-								<text class="contact-text">{{ cardInfo.companyAddress || '暂无地址' }}</text>
-							</view>
-						</view>
-
-						<!-- 名片底部二维码 -->
-						<view class="card-qr-section">
-							<view class="qr-divider"></view>
-							<view class="qr-wrapper">
-								<view class="qr-icon-area">
-									<image
-										v-if="qrInfo && qrInfo.image"
-										:src="(qrInfo.image || '').startsWith('data:') ? qrInfo.image : 'data:image/png;base64,' + qrInfo.image"
-										class="qr-code-img"
-										mode="aspectFit"
-									/>
-									<view v-else class="qr-placeholder">
-										<text class="qr-placeholder-text">名片码</text>
-									</view>
-								</view>
-								<text class="qr-hint">扫一扫查看我的名片</text>
-							</view>
-						</view>
-					</view>
-				</view>
-				
-				<!-- 操作按钮 -->
-				<view class="popup-btns">
-					<view class="btn btn-cancel" @click="closeCardPopup">
-						<text class="btn-text">取消</text>
-					</view>
-					<view class="btn btn-confirm" @click="shareCardConfirm">
-						<text class="btn-text">分享名片</text>
-					</view>
-				</view>
-				
-				<!-- 底部全宽按钮 -->
-				<view class="popup-footer">
-					<view class="btn btn-full" @click="shareCardConfirm">
-						<uni-icons type="paperplane" size="22" color="#ffffff"></uni-icons>
-						<text class="btn-text">立即分享名片给朋友</text>
-					</view>
-				</view>
-			</view>
-		</uni-popup>
-
-		<!-- 二维码弹窗 -->
-		<uni-popup ref="qrPopup" type="center">
-			<view class="qr-popup">
-				<view class="qr-header">
-					<text class="qr-title">名片二维码</text>
-					<text class="qr-subtitle">扫一扫查看我的名片信息</text>
-				</view>
-				<view class="qr-content">
-					<image v-if="qrInfo && qrInfo.image"
-						:src="qrInfo.image.startsWith('data:') ? qrInfo.image : 'data:image/png;base64,' + qrInfo.image"
-						class="qr-image" mode="aspectFit" />
-					<view v-else class="qr-loading">
-						<text>加载中...</text>
-					</view>
-				</view>
-				<view class="qr-actions">
-					<view class="action-btn" @click="saveQRCode">
-						<uni-icons type="download" size="24" color="#4080FF"></uni-icons>
-						<text class="action-text">保存到相册</text>
-					</view>
-					<view class="action-btn" @click="handleShare">
-						<uni-icons type="paperplane" size="24" color="#4080FF"></uni-icons>
-						<!-- <button open-type="share" class="action-text">分享给好友</button> -->
-						<text class="action-text">分享给好友</text>
-					</view>
-				</view>
-			</view>
-		</uni-popup>
 	</view>
 </template>
 
@@ -209,12 +64,10 @@
 		ref,
 		onMounted
 	} from 'vue'
-	import {
-		onShareAppMessage
-	} from '@dcloudio/uni-app';
 	import NavBar from '@/components/nav-bar/index.vue'
 	import UserAvatar from '@/components/user-avatar/index.vue'
 	import EmptyState from '@/components/empty-state/index.vue'
+	import ShareButton from '@/components/shareButton/index.vue'
 	import {
 		useUserStore
 	} from '@/store/modules/user.js'
@@ -222,48 +75,25 @@
 		storeToRefs
 	} from 'pinia'
 	import {
-		generateCardPoster,
-		savePosterToAlbum,
-	} from '@/utils/poster.js'
-
+		onShareAppMessage
+	} from '@dcloudio/uni-app';
 	// 使用 Pinia 管理用户状态
 	const userStore = useUserStore()
 	const {
 		cardInfo,
 		companyInfo,
-		qrInfo,
+		cardImage
 	} = storeToRefs(userStore)
 
-	const currentTab = ref('company')
-	// 名片快照图片路径
-	const cardSnapshot = ref('')
-	// 二维码海报图片路径
-	const qrCodePoster = ref('')
-	// 二维码弹窗引用
-	const qrPopup = ref(null)
-	const cardPopup = ref(null)
-
-	const productList = ref([])
 	onShareAppMessage(() => {
 		return {
 			userName: '小程序',
 			path: 'pages/splash/splash?userId=' + cardInfo.value.userId,
+			imageUrl: cardImage.value
 		};
 	});
-	onMounted(async () => {
-		// 如果没有数据,重新获取
-		if (!cardInfo.value.nickName) {
-			await userStore.queryCardInfo()
-		}
-		if (!companyInfo.value.name) {
-			await userStore.queryCompanyInfo()
-		}
-		// 预加载名片二维码,确保分享弹窗底部显示名片码
-		if (!qrInfo.value || !qrInfo.value.image) {
-			await userStore.queryCardQrcode()
-		}
-	})
 
+	let appName = ref('')
 	const makeCall = () => {
 		if (cardInfo.value.phonenumber) {
 			uni.makePhoneCall({
@@ -276,192 +106,6 @@
 			})
 		}
 	}
-
-	// 生成名片快照并保存
-	const saveCard = async () => {
-		uni.showLoading({
-			title: '生成快照中...',
-			mask: true
-		})
-
-		try {
-			// 确保二维码已加载
-			if (!qrInfo.value || !qrInfo.value.image) {
-				await userStore.queryCardQrcode()
-			}
-
-			// 生成名片快照
-			const snapshotPath = await generateCardPoster(cardInfo.value, qrInfo.value)
-			console.log(snapshotPath, "snapshotPathsnapshotPathsnapshotPath");
-			// 用属性接收快照路径
-			cardSnapshot.value = snapshotPath
-
-			console.log('名片快照路径:', cardSnapshot.value)
-
-			// 保存到相册
-			await savePosterToAlbum(snapshotPath)
-
-			uni.hideLoading()
-			uni.showToast({
-				title: '已保存到相册',
-				icon: 'success'
-			})
-		} catch (error) {
-			console.error('保存失败:', error)
-			uni.hideLoading()
-			uni.showToast({
-				title: '保存失败,请重试',
-				icon: 'none'
-			})
-		}
-	}
-
-	// 显示二维码
-	const showQRCode = async () => {
-		// 打开弹窗
-		qrPopup.value.open()
-	}
-
-	const close = async () => {
-		cardPopup.value.close()
-	}
-
-	const shareUserCard = async () => {
-		uni.showLoading({
-			title: '生成分享图片...',
-			mask: true
-		})
-		try {
-			// 确保二维码已加载
-			if (!qrInfo.value || !qrInfo.value.image) {
-				await userStore.queryCardQrcode()
-			}
-			const snapshotPath = await generateCardPoster(cardInfo.value, qrInfo.value)
-			uni.hideLoading()
-			wx.showShareImageMenu({
-				path: snapshotPath,
-				entrancePath:'pages/splash/splash?userId=' + cardInfo.value.userId,
-				success: () => {
-					console.log('分享菜单已调起');
-				},
-				fail: (err) => {
-					console.error('调起分享菜单失败', err);
-				}
-			});
-		} catch (error) {
-			console.error('分享失败:', error)
-			uni.hideLoading()
-			uni.showToast({
-				title: '分享失败,请重试',
-				icon: 'none'
-			})
-		}
-
-	}
-
-
-	const base64ToImage = async (base64Data) => {
-		return new Promise((resolve, reject) => {
-			try {
-				const filePath = `${wx.env.USER_DATA_PATH}/temp_image.png`;
-				let pureBase64Data = base64Data
-				if (pureBase64Data.startsWith('data:image')) {
-					pureBase64Data = pureBase64Data.replace(/^data:image\/\w+;base64,/, '');
-				}
-				const fs = wx.getFileSystemManager();
-				fs.writeFile({
-					filePath: filePath,
-					data: pureBase64Data,
-					encoding: 'base64',
-					success: (res) => {
-						console.log("resresweresresresres", res);
-						resolve(filePath)
-					},
-					fail: (err) => {
-						console.error('写入临时文件失败', err);
-					}
-				});
-			} catch (error) {
-				console.error('base64 转换异常:', error)
-				reject(error)
-			}
-		})
-	}
-	const handleShare = async () => {
-		const qrFilePath = await base64ToTempFile(qrInfo.value.image)
-		wx.showShareImageMenu({
-			path: qrFilePath,
-			entrancePath:'pages/splash/splash?userId=' + cardInfo.value.userId,
-			success: () => {
-				console.log('分享菜单已调起');
-			},
-			fail: (err) => {
-				console.error('调起分享菜单失败', err);
-			}
-		});
-	}
-	// 保存二维码到相册
-	const saveQRCode = async () => {
-		if (!qrInfo.value.image) {
-			uni.showToast({
-				title: '二维码图片不存在',
-				icon: 'none'
-			})
-			return
-		}
-
-		try {
-			// 将 base64 转换为临时文件
-			const qrFilePath = await base64ToTempFile(qrInfo.value.image)
-			// 保存到相册
-			await savePosterToAlbum(qrFilePath)
-			uni.showToast({
-				title: '已保存到相册',
-				icon: 'success'
-			})
-		} catch (error) {
-			console.error('保存失败:', error)
-			uni.showToast({
-				title: '保存失败,请重试',
-				icon: 'none'
-			})
-		}
-	}
-
-
-
-	const switchTab = (tab) => {
-		currentTab.value = tab
-	}
-	// 将 base64 转换为临时文件路径
-	const base64ToTempFile = async (base64Data) => {
-		return new Promise((resolve, reject) => {
-			try {
-				// 移除 data:image/png;base64, 前缀(如果有)
-				const pureBase64 = base64Data.replace(/^data:image\/\w+;base64,/, '')
-
-				const fileName = `${Date.now()}_qrcode.png`
-				const filePath = `${wx.env.USER_DATA_PATH}/${fileName}`
-				const fs = uni.getFileSystemManager()
-				fs.writeFile({
-					filePath: filePath,
-					data: pureBase64,
-					encoding: 'base64',
-					success: () => {
-						console.log('base64 转文件成功:', filePath)
-						resolve(filePath)
-					},
-					fail: (err) => {
-						console.error('base64 转文件失败:', err)
-						reject(err)
-					}
-				})
-			} catch (error) {
-				console.error('base64 转换异常:', error)
-				reject(error)
-			}
-		})
-	}
 </script>
 
 <style lang="scss" scoped>
@@ -469,6 +113,13 @@
 		background: #f5f6f8;
 	}
 
+	.left-title {
+		font-size: 44rpx;
+		font-weight: bold;
+		color: #ffffff;
+		text-shadow: 2px 2px 0 black;
+	}
+
 	.top-bg {
 		width: 750rpx;
 		height: 634rpx;
@@ -542,130 +193,96 @@
 		}
 	}
 
+	.page-top {
+		margin: 0 24rpx 24rpx;
 
-	.user-card {
-		position: relative;
-		z-index: 2;
-		width: 100%;
-		height: 400rpx;
-		box-sizing: border-box;
-		padding: 80rpx 40rpx 32rpx;
-		position: relative;
-
-		.user-card-bg {
-			position: absolute;
-			top: 0;
-			left: 0;
+		.user-card {
+			position: relative;
+			z-index: 2;
 			width: 100%;
-			height: 100%;
-			z-index: 0;
-		}
-
-		view,
-		text {
+			height: 400rpx;
+			box-sizing: border-box;
+			padding: 80rpx 40rpx 32rpx;
 			position: relative;
-			z-index: 1;
-		}
-
-		.user-header {
-			.name-row {
-				display: flex;
-				align-items: center;
-				margin-bottom: 16rpx;
 
-				.user-name {
-					font-size: 44rpx;
-					font-weight: 700;
-					color: #202020;
-					line-height: 1.2;
-				}
-
-				.user-role {
-					margin-left: 16rpx;
-					padding: 6rpx 16rpx;
-					background: rgba(68, 110, 255, 0.10);
-					border-radius: 8rpx;
-					font-size: 24rpx;
-					font-weight: 500;
-					color: #446eff;
-				}
+			.user-card-bg {
+				position: absolute;
+				top: 0;
+				left: 0;
+				width: 100%;
+				height: 100%;
+				z-index: 0;
 			}
 
-			.company-name {
-				font-size: 28rpx;
-				font-weight: 500;
-				color: #666666;
-				line-height: 1.4;
+			view,
+			text {
+				position: relative;
+				z-index: 1;
 			}
-		}
-
-		.avatar-wrapper {
-			position: absolute;
-			top: 32rpx;
-			right: 36rpx;
-			z-index: 2;
-		}
 
-		.user-contact {
-			margin-top: 32rpx;
+			.user-header {
+				.name-row {
+					display: flex;
+					align-items: center;
+					margin-bottom: 16rpx;
 
-			.contact-row {
-				display: flex;
-				align-items: center;
-				margin-bottom: 16rpx;
+					.user-name {
+						font-size: 44rpx;
+						font-weight: 700;
+						color: #202020;
+						line-height: 1.2;
+					}
 
-				uni-icons {
-					margin-right: 12rpx;
-					flex-shrink: 0;
+					.user-role {
+						margin-left: 16rpx;
+						padding: 6rpx 16rpx;
+						background: rgba(68, 110, 255, 0.10);
+						border-radius: 8rpx;
+						font-size: 24rpx;
+						font-weight: 500;
+						color: #446eff;
+					}
 				}
 
-				.contact-text {
-					font-size: 26rpx;
+				.company-name {
+					font-size: 28rpx;
+					font-weight: 500;
 					color: #666666;
+					line-height: 1.4;
 				}
 			}
-		}
-
-	}
 
-	/* 用户信息卡片 */
-	.page-top {
-		margin: 0 24rpx 24rpx;
+			.avatar-wrapper {
+				position: absolute;
+				top: 32rpx;
+				right: 36rpx;
+				z-index: 2;
+			}
 
+			.user-contact {
+				margin-top: 32rpx;
 
-		.control-card {
-			background-color: #fff;
-			border-radius: 0 0 24rpx 24rpx;
-			position: relative;
-			z-index: 1;
-			bottom: 24rpx;
-			display: flex;
-			align-items: center;
-			justify-content: space-around;
-			padding-top: 48rpx;
-			padding-bottom: 24rpx;
-
-			.item {
-				display: flex;
-				flex-direction: column;
-				align-items: center;
-				justify-content: center;
-				gap: 4rpx;
+				.contact-row {
+					display: flex;
+					align-items: center;
+					margin-bottom: 16rpx;
 
-				image {
-					width: 64rpx;
-					height: 64rpx;
-				}
+					uni-icons {
+						margin-right: 12rpx;
+						flex-shrink: 0;
+					}
 
-				.text {
-					font-size: 26rpx !important;
-					color: #202020;
+					.contact-text {
+						font-size: 26rpx;
+						color: #666666;
+					}
 				}
 			}
-		}
 
+		}
 
 	}
+
 	// 名片内容区域
 	.card-content {
 		margin: 0 24rpx;
@@ -676,273 +293,33 @@
 		position: relative;
 		bottom: 24rpx;
 		z-index: 2;
+
 		// 企业简介
 		.company-section {
 			background: #ffffff;
-			.company-title{
+
+			.company-title {
 				font-weight: bold;
 				font-size: 32rpx;
 				margin-bottom: 12rpx;
 			}
+
 			.company-text {
 				display: block;
 				font-size: 28rpx;
 				color: #666666;
 				line-height: 1.8;
 				margin-bottom: 24rpx;
-		
+
 				::v-deep img {
 					max-width: 100% !important;
 					height: auto !important;
 				}
-		
+
 				&:last-child {
 					margin-bottom: 0;
 				}
 			}
 		}
 	}
-
-	// Tab 切换区域
-	.tab-section {
-		border-radius: 24rpx 24rpx 0 0;
-		padding: 0 40rpx;
-
-		.tab-wrapper {
-			display: flex;
-			border-bottom: 1rpx solid #f0f0f0;
-
-			.tab-item {
-				flex: 1;
-				display: flex;
-				flex-direction: column;
-				align-items: center;
-				padding: 32rpx 0;
-				position: relative;
-
-				.tab-text {
-					font-size: 28rpx;
-					color: #999999;
-					font-weight: 500;
-				}
-
-				&.active .tab-text {
-					color: #333333;
-					font-weight: 600;
-				}
-
-				.tab-indicator {
-					position: absolute;
-					bottom: 0;
-					width: 60rpx;
-					height: 4rpx;
-					background: linear-gradient(90deg, #4A90E2 0%, #6FB3F2 100%);
-					border-radius: 2rpx;
-				}
-			}
-		}
-	}
-
-	// 产品列表
-	.product-list {
-		padding: 24rpx 40rpx;
-
-		.product-item {
-			display: flex;
-			padding: 24rpx 0;
-			border-bottom: 1rpx solid #f0f0f0;
-
-			&:last-child {
-				border-bottom: none;
-			}
-
-			.product-image {
-				width: 160rpx;
-				height: 160rpx;
-				border-radius: 12rpx;
-				background: #f5f5f5;
-				flex-shrink: 0;
-				margin-right: 24rpx;
-			}
-
-			.product-info {
-				flex: 1;
-				display: flex;
-				flex-direction: column;
-				justify-content: center;
-
-				.product-title {
-					font-size: 28rpx;
-					color: #333333;
-					margin-bottom: 12rpx;
-					line-height: 1.5;
-				}
-
-				.product-desc {
-					font-size: 24rpx;
-					color: #999999;
-				}
-			}
-		}
-	}
-
-
-	// 隐藏的 canvas 容器
-	.hidden-canvas-box {
-		position: fixed;
-		left: -9999px;
-		top: -9999px;
-		width: 1px;
-		height: 1px;
-		overflow: hidden;
-	}
-
-	// 二维码弹窗样式
-	.qr-popup {
-		margin: 100rpx auto;
-		
-		width: 600rpx;
-		background: #ffffff;
-		border-radius: 24rpx;
-		padding: 40rpx;
-		box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);
-
-		.qr-header {
-			text-align: center;
-			margin-bottom: 40rpx;
-
-			.qr-title {
-				display: block;
-				font-size: 36rpx;
-				font-weight: 600;
-				color: #202020;
-				margin-bottom: 12rpx;
-			}
-
-			.qr-subtitle {
-				display: block;
-				font-size: 26rpx;
-				color: #999999;
-			}
-		}
-
-		.qr-content {
-			display: flex;
-			justify-content: center;
-			align-items: center;
-			min-height: 400rpx;
-			margin-bottom: 40rpx;
-
-			.qr-image {
-				width: 400rpx;
-				height: 400rpx;
-				border-radius: 16rpx;
-				box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
-			}
-
-			.qr-loading {
-				display: flex;
-				justify-content: center;
-				align-items: center;
-			}
-		}
-
-		.qr-actions {
-			display: flex;
-			justify-content: space-around;
-
-			.action-btn {
-				display: flex;
-				flex-direction: column;
-				align-items: center;
-				gap: 12rpx;
-				padding: 20rpx 40rpx;
-				border-radius: 16rpx;
-				background: rgba(64, 128, 255, 0.08);
-
-				.action-text {
-					&::after {
-						border: none;
-					}
-
-					line-height: 1;
-					background-color: transparent;
-					font-size: 26rpx;
-					color: #4080FF;
-					font-weight: 500;
-				}
-			}
-		}
-	}
-
-	// 分享弹窗卡片底部二维码
-	.card-qr-section {
-		position: relative;
-		z-index: 1;
-		display: flex;
-		flex-direction: column;
-		align-items: center;
-		padding: 8rpx 0 4rpx;
-
-		.qr-divider {
-			width: 80%;
-			height: 1rpx;
-			background: #e8e8e8;
-			margin-bottom: 12rpx;
-		}
-
-		.qr-wrapper {
-			display: flex;
-			flex-direction: column;
-			align-items: center;
-			gap: 6rpx;
-
-			.qr-icon-area {
-				width: 96rpx;
-				height: 96rpx;
-				border-radius: 12rpx;
-				overflow: hidden;
-				border: 2rpx solid #e8e8e8;
-				background: #ffffff;
-				display: flex;
-				align-items: center;
-				justify-content: center;
-
-				.qr-code-img {
-					width: 100%;
-					height: 100%;
-				}
-
-				.qr-placeholder {
-					width: 100%;
-					height: 100%;
-					display: flex;
-					align-items: center;
-					justify-content: center;
-					background: #f5f5f5;
-
-					.qr-placeholder-text {
-						font-size: 18rpx;
-						color: #cccccc;
-					}
-				}
-			}
-
-			.qr-hint {
-				font-size: 18rpx;
-				color: #b0b0b0;
-			}
-		}
-	}
-
-	.resetButton {
-		background-color: transparent !important;
-		border: none !important;
-		display: inline !important;
-
-		&::after {
-			border: none !important;
-		}
-
-	}
 </style>

+ 2 - 2
pages/mine/index.vue

@@ -11,8 +11,8 @@
 			<!-- 用户信息 -->
 			<view class="user-info-section">
 				<view class="avatar-wrapper">
-					<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="120"
-						:badge-src="'/static/image/public/badge-icon.png'" :badge-size="40" />
+					<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="136"
+						:badge-src="'/static/image/public/badge-icon.png'" :badge-size="52" />
 				</view>
 				<view class="user-details">
 					<view class="name-row">

+ 59 - 191
pages/mine/userCard.vue

@@ -3,14 +3,18 @@
 
 		<!-- 顶部背景 -->
 		<image class="top-bg" src="/static/image/home/top-bg.png" />
-		<NavBar title="名片信息" :show_back="false" color="#FFFFFF" :fixed="true" :bg="'transparent'"></NavBar>
+		<NavBar :title="'名片信息'" :show_back="false" color="#fff" :fixed="true" :bg="'transparent'">
+			<template #left>
+				<!-- <view class="left-title">{{appName}}</view> -->
+			</template>
+		</NavBar>
 		<!-- 名片卡片 -->
 		<view class="page-top">
-			<view class="user-card">
+			<view class="user-card" id="user-card">
 				<!-- 背景图 -->
 				<image class="user-card-bg" src="/static/image/home/usecard-bg.png" />
 
-				<!-- 左上姓名 + 职位 -->
+				<!-- 左上:姓名 + 职位 -->
 				<view class="user-header">
 					<view class="name-row">
 						<text class="user-name">{{ cardInfo.nickName || '用户' }}</text>
@@ -19,13 +23,13 @@
 					<text class="company-name">{{ cardInfo.companyName || '公司名称' }}</text>
 				</view>
 
-				<!-- 右上头像 -->
+				<!-- 右上:头像 -->
 				<view class="avatar-wrapper">
 					<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="130"
 						:badge-src="'/static/image/public/badge-icon.png'" :badge-size="56" />
 				</view>
 
-				<!-- 左下联系方式 -->
+				<!-- 左下:联系方式 -->
 				<view class="user-contact">
 					<view class="contact-row" @click="makeCall">
 						<uni-icons type="phone" :size="18" color="#666666"></uni-icons>
@@ -51,6 +55,14 @@
 				<rich-text class="company-text" :nodes="companyInfo.introduce"></rich-text>
 			</view>
 		</view>
+		<!-- 隐藏的 Canvas 用于生成名片快照 -->
+		<canvas id="posterCanvas" type="2d"
+			style="position: fixed; left: -9999px; top: -9999px; width: 750px; height: 780px;"></canvas>
+		
+		<!-- 隐藏的 Canvas 用于生成二维码海报 -->
+		<view class="hidden-canvas-box">
+			<canvas id="qrPosterCanvas" type="2d" style="width: 600px; height: 700px;"></canvas>
+		</view>
 	</view>
 </template>
 
@@ -59,44 +71,33 @@
 		ref,
 		onMounted
 	} from 'vue'
-	import {
-		onShareAppMessage
-	} from '@dcloudio/uni-app';
 	import NavBar from '@/components/nav-bar/index.vue'
 	import UserAvatar from '@/components/user-avatar/index.vue'
 	import EmptyState from '@/components/empty-state/index.vue'
+	import ShareButton from '@/components/shareButton/index.vue'
 	import {
 		useUserStore
 	} from '@/store/modules/user.js'
 	import {
 		storeToRefs
 	} from 'pinia'
-
+	import {
+		onShareAppMessage
+	} from '@dcloudio/uni-app';
 	// 使用 Pinia 管理用户状态
 	const userStore = useUserStore()
 	const {
 		cardInfo,
-		companyInfo
+		companyInfo,
+		cardImage
 	} = storeToRefs(userStore)
-
-	const currentTab = ref('company')
-
-	const productList = ref([])
-
-	onMounted(async () => {
-		// 如果没有数据,重新获取
-		if (!cardInfo.value.nickName) {
-			await userStore.queryCardInfo()
-		}
-		if (!companyInfo.value.name) {
-			await userStore.queryCompanyInfo()
-		}
-	})
-
+	let appName = ref('')
+	
 	onShareAppMessage(() => {
 		return {
 			userName: '小程序',
 			path: 'pages/splash/splash?userId=' + cardInfo.value.userId,
+			imageUrl: cardImage.value
 		};
 	});
 	const makeCall = () => {
@@ -112,40 +113,18 @@
 		}
 	}
 
-	const shareCard = () => {
-		uni.showShareMenu({
-			withShareTicket: true
-		})
-		uni.showToast({
-			title: '分享名片',
-			icon: 'none'
-		})
-	}
-
-	const saveCard = () => {
-		uni.showToast({
-			title: '已保存到手机相册',
-			icon: 'success'
-		})
-	}
-
-	const showQRCode = () => {
-		uni.showToast({
-			title: '名片码',
-			icon: 'none'
-		})
-	}
-
-	const switchTab = (tab) => {
-		currentTab.value = tab
-	}
 </script>
 
 <style lang="scss" scoped>
 	.card-container {
 		background: #f5f6f8;
 	}
-
+	.left-title {
+		font-size: 44rpx;
+		font-weight: bold;
+		color: #ffffff;
+		text-shadow: 2px 2px 0 black;
+	}
 	.top-bg {
 		width: 750rpx;
 		height: 634rpx;
@@ -154,7 +133,6 @@
 		left: 0;
 		z-index: 1;
 	}
-
 	// 顶部背景区域
 	.header-bg {
 		background: linear-gradient(135deg, #4A90E2 0%, #6FB3F2 50%, #87CEEB 100%);
@@ -218,13 +196,8 @@
 			}
 		}
 	}
-
-
-
-	/* 用户信息卡片 */
 	.page-top {
 		margin: 0 24rpx 24rpx;
-
 		.user-card {
 			position: relative;
 			z-index: 2;
@@ -233,7 +206,7 @@
 			box-sizing: border-box;
 			padding: 80rpx 40rpx 32rpx;
 			position: relative;
-
+		
 			.user-card-bg {
 				position: absolute;
 				top: 0;
@@ -242,26 +215,26 @@
 				height: 100%;
 				z-index: 0;
 			}
-
+		
 			view,
 			text {
 				position: relative;
 				z-index: 1;
 			}
-
+		
 			.user-header {
 				.name-row {
 					display: flex;
 					align-items: center;
 					margin-bottom: 16rpx;
-
+		
 					.user-name {
 						font-size: 44rpx;
 						font-weight: 700;
 						color: #202020;
 						line-height: 1.2;
 					}
-
+		
 					.user-role {
 						margin-left: 16rpx;
 						padding: 6rpx 16rpx;
@@ -272,7 +245,7 @@
 						color: #446eff;
 					}
 				}
-
+		
 				.company-name {
 					font-size: 28rpx;
 					font-weight: 500;
@@ -280,70 +253,37 @@
 					line-height: 1.4;
 				}
 			}
-
+		
 			.avatar-wrapper {
 				position: absolute;
 				top: 32rpx;
 				right: 36rpx;
 				z-index: 2;
 			}
-
+		
 			.user-contact {
 				margin-top: 32rpx;
-
+		
 				.contact-row {
 					display: flex;
 					align-items: center;
 					margin-bottom: 16rpx;
-
+		
 					uni-icons {
 						margin-right: 12rpx;
 						flex-shrink: 0;
 					}
-
+		
 					.contact-text {
 						font-size: 26rpx;
 						color: #666666;
 					}
 				}
 			}
-
-		}
-
-		.control-card {
-			background-color: #fff;
-			border-radius: 0 0 24rpx 24rpx;
-			position: relative;
-			z-index: 1;
-			bottom: 24rpx;
-			display: flex;
-			align-items: center;
-			justify-content: space-around;
-			padding-top: 48rpx;
-			padding-bottom: 24rpx;
-
-			.item {
-				display: flex;
-				flex-direction: column;
-				align-items: center;
-				justify-content: center;
-				gap: 4rpx;
-
-				image {
-					width: 64rpx;
-					height: 64rpx;
-				}
-
-				text {
-					font-size: 26rpx;
-					color: #202020;
-				}
-			}
+		
 		}
-
-
+		
 	}
-
 	// 名片内容区域
 	.card-content {
 		margin: 0 24rpx;
@@ -352,115 +292,43 @@
 		background: #ffffff;
 		box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.04);
 		position: relative;
-		bottom: 24rpx;
 		z-index: 2;
+
 		// 企业简介
 		.company-section {
 			background: #ffffff;
-			.company-title{
+
+			.company-title {
 				font-weight: bold;
 				font-size: 32rpx;
 				margin-bottom: 12rpx;
 			}
+
 			.company-text {
 				display: block;
 				font-size: 28rpx;
 				color: #666666;
 				line-height: 1.8;
 				margin-bottom: 24rpx;
-		
+
 				::v-deep img {
 					max-width: 100% !important;
 					height: auto !important;
 				}
-		
+
 				&:last-child {
 					margin-bottom: 0;
 				}
 			}
 		}
 	}
-
-	// Tab 切换区域
-	.tab-section {
-		border-radius: 24rpx 24rpx 0 0;
-		padding: 0 40rpx;
-
-		.tab-wrapper {
-			display: flex;
-			border-bottom: 1rpx solid #f0f0f0;
-
-			.tab-item {
-				flex: 1;
-				display: flex;
-				flex-direction: column;
-				align-items: center;
-				padding: 32rpx 0;
-				position: relative;
-
-				.tab-text {
-					font-size: 28rpx;
-					color: #999999;
-					font-weight: 500;
-				}
-
-				&.active .tab-text {
-					color: #333333;
-					font-weight: 600;
-				}
-
-				.tab-indicator {
-					position: absolute;
-					bottom: 0;
-					width: 60rpx;
-					height: 4rpx;
-					background: linear-gradient(90deg, #4A90E2 0%, #6FB3F2 100%);
-					border-radius: 2rpx;
-				}
-			}
-		}
-	}
-
-	// 产品列表
-	.product-list {
-		padding: 24rpx 40rpx;
-
-		.product-item {
-			display: flex;
-			padding: 24rpx 0;
-			border-bottom: 1rpx solid #f0f0f0;
-
-			&:last-child {
-				border-bottom: none;
-			}
-
-			.product-image {
-				width: 160rpx;
-				height: 160rpx;
-				border-radius: 12rpx;
-				background: #f5f5f5;
-				flex-shrink: 0;
-				margin-right: 24rpx;
-			}
-
-			.product-info {
-				flex: 1;
-				display: flex;
-				flex-direction: column;
-				justify-content: center;
-
-				.product-title {
-					font-size: 28rpx;
-					color: #333333;
-					margin-bottom: 12rpx;
-					line-height: 1.5;
-				}
-
-				.product-desc {
-					font-size: 24rpx;
-					color: #999999;
-				}
-			}
-		}
+	// 隐藏的 canvas 容器
+	.hidden-canvas-box {
+		position: fixed;
+		left: -9999px;
+		top: -9999px;
+		width: 1px;
+		height: 1px;
+		overflow: hidden;
 	}
 </style>

+ 26 - 14
pages/splash/splash.vue

@@ -1,10 +1,7 @@
 <template>
 	<view class="splash-container">
-		<!-- 背景装饰 -->
 		<view class="splash-bg-1"></view>
 		<view class="splash-bg-2"></view>
-
-		<!-- Logo 区域 -->
 		<view class="logo-section">
 			<view class="logo-wrapper">
 				<view class="logo-glow"></view>
@@ -13,8 +10,6 @@
 			<text class="app-name">{{appName}}</text>
 			<text class="app-slogan">您的客户关系管理助手</text>
 		</view>
-
-		<!-- 加载动画 -->
 		<view class="loading-section">
 			<text class="loading-text">
 				正在加载
@@ -25,14 +20,22 @@
 				</view>
 			</text>
 		</view>
-
-		<!-- 版本信息 -->
 		<view class="version-info">
 			<text class="version-text">{{version}}</text>
 			<view class="version-text">
 				浙江舒博特网络科技有限公司提供技术支持
 			</view>
 		</view>
+	<!-- 隐藏的 Canvas 用于生成名片快照 -->
+	<canvas id="posterCanvas" type="2d"
+		style="position: fixed; left: -9999px; top: -9999px; width: 750px; height: 780px;"></canvas>
+	
+	<!-- 隐藏的 Canvas 用于生成二维码海报 -->
+	<view class="hidden-canvas-box">
+		<canvas id="qrPosterCanvas" type="2d" style="width: 600px; height: 700px;"></canvas>
+	</view>
+	
+
 	</view>
 </template>
 
@@ -47,6 +50,7 @@
 	import {
 		useUserStore
 	} from '@/store/modules/user.js'
+
 	import {
 		onLoad,
 	} from "@dcloudio/uni-app";
@@ -67,20 +71,16 @@
 	onLoad((options) => {
 		initApp(options?.userId)
 	})
-	
+
 	/**
 	 * 初始化应用
 	 */
 	const initApp = async (userId = null) => {
 		try {
-			// 模拟加载延迟(提升用户体验)
-			await sleep(500)
 			loadingText.value = '检查登录状态...'
-			// 如果没有 token,直接跳转登录页
 			const loginToggle = isLogin() || !!userId
 			if (!loginToggle) {
 				console.log('未登录,token 不存在')
-				await sleep(800)
 				uni.reLaunch({
 					url: '/pages/login/login'
 				})
@@ -89,13 +89,14 @@
 			if (loginToggle) {
 				await userStore.queryCardInfo(userId)
 				await userStore.queryCompanyInfo(userId)
-				await userStore.queryCardQrcode(userId)
 				uni.reLaunch({
 					url: !!userId ? '/pages/mine/userCard' : '/pages/index/index'
 				})
+				await userStore.queryCardQrcode(userId)
+				await userStore.queryCardPoster()
+
 			} else {
 				clearUserInfo()
-				await sleep(800)
 				uni.reLaunch({
 					url: '/pages/login/login'
 				})
@@ -122,6 +123,7 @@
 	.splash-container {
 		min-height: 100vh;
 		background: linear-gradient(135deg, #4A90E2 0%, #6FB3F2 50%, #87CEEB 100%);
+		// background-color: #fff;
 		position: relative;
 		overflow: hidden;
 		display: flex;
@@ -360,4 +362,14 @@
 			transform: translate(-30rpx, 30rpx) scale(1.08);
 		}
 	}
+
+	// 隐藏的 canvas 容器
+	.hidden-canvas-box {
+		position: fixed;
+		left: -9999px;
+		top: -9999px;
+		width: 1px;
+		height: 1px;
+		overflow: hidden;
+	}
 </style>

+ 16 - 3
store/modules/user.js

@@ -10,8 +10,11 @@ import {
 	getCompanyInfo
 } from '@/api/card.js'
 import {
-formatRichText
+	formatRichText
 } from '@/utils/index.js'
+import {
+	generateCardPoster,
+} from '@/utils/poster.js'
 export const useUserStore = defineStore('user', {
 	state: () => ({
 		cardInfo: {
@@ -33,7 +36,8 @@ export const useUserStore = defineStore('user', {
 		},
 		qrInfo: {
 
-		}
+		},
+		cardImage:"",
 	}),
 	actions: {
 		// 获取用户卡片信息
@@ -67,10 +71,19 @@ export const useUserStore = defineStore('user', {
 					userId: this.cardInfo.userId
 				}
 				let res = await getCardQrcode(parmas)
-				this.qrInfo = res.data ||{}
+				this.qrInfo = res.data || {}
 				resolve(res.data)
 			})
 		},
+		queryCardPoster() {
+			return new Promise(async (resolve, reject) => {
+				const cardImage = await generateCardPoster(this.cardInfo)
+				this.cardImage = cardImage || ''
+				resolve(cardImage)
+			})
+		},
+
+
 		// 退出登录,重置状态
 		logout() {
 			this.$reset()