zbb 6 日 前
コミット
e25f664db7

+ 74 - 0
components/companyIntroduce/index.vue

@@ -0,0 +1,74 @@
+<template>
+	<view class="company-section">
+		<view class="company-title">
+			<image class="img" src="/static/image/public/qyjj-icon.png" mode=""></image>
+			<text class="name">{{companyInfo.name || cardInfo.companyName}}</text>
+		</view>
+		<rich-text class="company-text" :nodes="companyInfo.introduce" v-if="companyInfo.introduce"></rich-text>
+		<EmptyState
+			v-else
+			text=""
+			sub-text="暂时没有企业信息"
+			image="/static/image/public/no-data.png"
+		/>
+	</view>
+</template>
+
+<script setup>
+	import EmptyState from '@/components/empty-state/index.vue'
+	import {
+		onShareAppMessage
+	} from '@dcloudio/uni-app';
+	import {
+		useUserStore
+	} from '@/store/modules/user.js'
+	import {
+		storeToRefs
+	} from 'pinia'
+	const userStore = useUserStore()
+	const {
+		companyInfo,
+		cardInfo
+	} = storeToRefs(userStore)
+</script>
+<style lang="scss" scoped>
+	// 企业简介
+	.company-section {
+		background: #ffffff;
+		.company-title {
+			display: flex;
+			align-items: center;
+			gap: 16rpx;
+			margin: 38rpx 0 30rpx;
+			.img{
+				width: 124rpx;
+				height: 40rpx;
+				
+			}
+			.name{
+				font-size: 32rpx;
+				font-weight: 500;
+				color: #202020;
+				position: relative;
+				bottom: 4rpx;
+			}
+		}
+
+		.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;
+			}
+		}
+	}
+</style>

+ 1 - 1
config/env.production.js

@@ -6,7 +6,7 @@ export default {
 	env: 'production',
 	
 	// API 配置
-	baseUrl: 'https://api.example.com',
+	baseUrl: 'https://oaapp-api.shubote.cn',
 	timeout: 10000,
 	
 	// 应用信息

+ 1 - 1
config/index.js

@@ -4,7 +4,7 @@
  */
 
 // 手动修改此处切换环境:development | test | production
-const CURRENT_ENV = 'development'
+const CURRENT_ENV = 'production'
 
 // 同步导入所有环境配置
 import developmentConfig from './env.development.js'

+ 6 - 38
pages/index/index.vue

@@ -25,7 +25,7 @@
 
 				<!-- 右上:头像 -->
 				<view class="avatar-wrapper">
-					<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="130"
+					<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="142"
 						:badge-src="'/static/image/public/badge-icon.png'" :badge-size="56" />
 				</view>
 
@@ -49,12 +49,7 @@
 		</view>
 		<view class="card-content">
 			<!-- 企业简介 -->
-			<view class="company-section">
-				<view class="company-title">
-					企业简介
-				</view>
-				<rich-text class="company-text" :nodes="companyInfo.introduce"></rich-text>
-			</view>
+			<CompanyIntroduce></CompanyIntroduce>
 		</view>
 		<!-- 隐藏的 Canvas 用于生成名片快照 -->
 		<canvas id="posterCanvas" type="2d"
@@ -79,6 +74,7 @@
 	import UserAvatar from '@/components/user-avatar/index.vue'
 	import EmptyState from '@/components/empty-state/index.vue'
 	import ShareButton from '@/components/shareButton/index.vue'
+	import CompanyIntroduce from '@/components/companyIntroduce/index.vue'
 	import {
 		useUserStore
 	} from '@/store/modules/user.js'
@@ -96,9 +92,9 @@
 	let appName = ref('')
 	onShareAppMessage(() => {
 		return {
-			userName: '小程序',
 			path: 'pages/splash/splash?userId=' + cardInfo.value.userId,
-			imageUrl: cardImage.value
+			imageUrl: cardImage.value,
+			title:`${cardInfo.value.nickName} 向你分享了名片`
 		};
 	});
 	const makeCall = () => {
@@ -261,7 +257,7 @@
 
 			.avatar-wrapper {
 				position: absolute;
-				top: 32rpx;
+				top:74rpx;
 				right: 36rpx;
 				z-index: 2;
 			}
@@ -300,34 +296,6 @@
 		position: relative;
 		bottom: 24rpx;
 		z-index: 2;
-
-		// 企业简介
-		.company-section {
-			background: #ffffff;
-
-			.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;
-				}
-			}
-		}
 	}
 
 	// 隐藏的 canvas 容器

+ 64 - 16
pages/login/login.vue

@@ -1,13 +1,15 @@
 <template>
 	<view class="login-container">
 		<!-- 顶部导航栏 -->
-		<view class="nav-bar">
-			<text class="nav-title">登录</text>
-		</view>
+		<NavBar :title="''" :show_back="false" color="#fff" :fixed="true" :bg="'transparent'">
+			<template #left>
+				<!-- <view class="left-title">{{appName}}</view> -->
+			</template>
+		</NavBar>
 		<!-- 顶部空白区域 -->
-		<view style="height: 100rpx;"></view>
+		<view style="height: 50rpx;"></view>
 
-		<!-- Logo 区域 -->	
+		<!-- Logo 区域 -->
 		<view class="logo-section">
 			<image class="logo-icon" src="/static/image/public/logo.png" mode="aspectFill"></image>
 			<text class="app-name">{{appName}}</text>
@@ -17,14 +19,16 @@
 		<view class="form-section">
 			<!-- 手机号输入 -->
 			<view class="input-item">
-				<uni-easyinput v-model="phoneNumber" placeholder="请输入手机号" type="number" maxlength="11"
-					:clearable="false" class="phone-input" />
+		<!-- 		<uni-easyinput v-model="phoneNumber" placeholder="请输入手机号" type="number" maxlength="11"
+					:clearable="false" class="phone-input" /> -->
+				<input class="account" v-model="phoneNumber" placeholder="请输入手机号" type="number"  maxlength="11" />
 			</view>
 
 			<!-- 验证码输入 -->
 			<view class="input-item verify-code-row">
-				<uni-easyinput v-model="verifyCode" placeholder="验证码" type="number" maxlength="6" :clearable="false"
-					class="code-input" />
+				<!-- <uni-easyinput v-model="verifyCode" placeholder="验证码" type="number" maxlength="6" :clearable="false"
+					class="code-input" /> -->
+				<input class="account" v-model="verifyCode" placeholder="请输入验证码" type="number"  maxlength="6" />
 				<text class="get-code-btn" :class="{ disabled: countdown > 0 }" @click="getVerifyCode">
 					{{ countdown > 0 ? `${countdown}s` : '获取验证码' }}
 				</text>
@@ -50,8 +54,6 @@
 			<!-- #endif -->
 		</view>
 
-
-
 		<!-- 用户协议弹窗 -->
 		<view class="agreement-popup" v-if="showUserAgreementPopup" @click="closeUserAgreement">
 			<view class="popup-mask"></view>
@@ -90,11 +92,11 @@
 				</view>
 			</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>
@@ -103,6 +105,7 @@
 </template>
 
 <script setup>
+	import NavBar from '@/components/nav-bar/index.vue'
 	import yonghuxieyi from "./yonghuxieyi.vue"
 	import yonghuzhengce from "./yonghuzhengce.vue"
 
@@ -455,16 +458,16 @@
 			display: flex;
 			align-items: center;
 			gap: 6rpx;
+
 			.code-input {
 				flex: 1;
 				margin-right: 20rpx;
 			}
 
 			.get-code-btn {
-				font-size: 26rpx;
-				color: #1890ff;
+				font-size: 28rpx;
+				color: #155DFC;
 				white-space: nowrap;
-
 				&.disabled {
 					color: #999999;
 				}
@@ -710,6 +713,51 @@
 			transform: translateY(0);
 		}
 	}
+
+	.input-container {
+		position: relative;
+		width: 670rpx;
+
+		// margin-bottom: 40rpx;
+		.error-message {
+			color: #ff4444;
+			font-size: 24rpx;
+			margin-left: 10rpx;
+			position: relative;
+			height: 100%;
+			top: -10rpx;
+			left: 0;
+		}
+
+		.password-eye {
+			position: absolute;
+			right: 0rpx;
+			top: 16rpx;
+			width: 100rpx;
+			height: 65rpx;
+			z-index: 2;
+			cursor: pointer;
+		}
+	}
+
+
+	.account {
+		width: 640rpx;
+		height: 92rpx;
+		background: #fff;
+		border-radius: 20rpx;
+		padding-left: 30rpx;
+		font-size: 28rpx;
+		background-color: #F5F5F5;
+		border: 2rpx solid transparent;
+		transition: border-color 0.3s;
+
+		&.error-border {
+			border-color: #ff4444;
+			// background-color: #fff5f5;
+		}
+	}
+
 	// 隐藏的 canvas 容器
 	.hidden-canvas-box {
 		position: fixed;

+ 6 - 38
pages/mine/card.vue

@@ -25,7 +25,7 @@
 
 				<!-- 右上:头像 -->
 				<view class="avatar-wrapper">
-					<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="130"
+					<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="142"
 						:badge-src="'/static/image/public/badge-icon.png'" :badge-size="56" />
 				</view>
 
@@ -49,12 +49,7 @@
 		</view>
 		<view class="card-content">
 			<!-- 企业简介 -->
-			<view class="company-section">
-				<view class="company-title">
-					企业简介
-				</view>
-				<rich-text class="company-text" :nodes="companyInfo.introduce"></rich-text>
-			</view>
+			<CompanyIntroduce></CompanyIntroduce>
 		</view>
 	</view>
 </template>
@@ -68,6 +63,7 @@
 	import UserAvatar from '@/components/user-avatar/index.vue'
 	import EmptyState from '@/components/empty-state/index.vue'
 	import ShareButton from '@/components/shareButton/index.vue'
+		import CompanyIntroduce from '@/components/companyIntroduce/index.vue'
 	import {
 		useUserStore
 	} from '@/store/modules/user.js'
@@ -87,9 +83,9 @@
 
 	onShareAppMessage(() => {
 		return {
-			userName: '小程序',
 			path: 'pages/splash/splash?userId=' + cardInfo.value.userId,
-			imageUrl: cardImage.value
+			imageUrl: cardImage.value,
+			title:`${cardInfo.value.nickName} 向你分享了名片`
 		};
 	});
 
@@ -254,7 +250,7 @@
 
 			.avatar-wrapper {
 				position: absolute;
-				top: 32rpx;
+				top: 74rpx;
 				right: 36rpx;
 				z-index: 2;
 			}
@@ -293,33 +289,5 @@
 		position: relative;
 		bottom: 24rpx;
 		z-index: 2;
-
-		// 企业简介
-		.company-section {
-			background: #ffffff;
-
-			.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;
-				}
-			}
-		}
 	}
 </style>

+ 4 - 36
pages/mine/userCard.vue

@@ -25,7 +25,7 @@
 
 				<!-- 右上:头像 -->
 				<view class="avatar-wrapper">
-					<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="130"
+					<UserAvatar :src="cardInfo.avatar" :name="cardInfo.nickName" :size="142"
 						:badge-src="'/static/image/public/badge-icon.png'" :badge-size="56" />
 				</view>
 
@@ -48,12 +48,7 @@
 		</view>
 		<view class="card-content">
 			<!-- 企业简介 -->
-			<view class="company-section">
-				<view class="company-title">
-					企业简介
-				</view>
-				<rich-text class="company-text" :nodes="companyInfo.introduce"></rich-text>
-			</view>
+			<CompanyIntroduce></CompanyIntroduce>
 		</view>
 		<!-- 隐藏的 Canvas 用于生成名片快照 -->
 		<canvas id="posterCanvas" type="2d"
@@ -75,6 +70,7 @@
 	import UserAvatar from '@/components/user-avatar/index.vue'
 	import EmptyState from '@/components/empty-state/index.vue'
 	import ShareButton from '@/components/shareButton/index.vue'
+	import CompanyIntroduce from '@/components/companyIntroduce/index.vue'
 	import {
 		useUserStore
 	} from '@/store/modules/user.js'
@@ -256,7 +252,7 @@
 		
 			.avatar-wrapper {
 				position: absolute;
-				top: 32rpx;
+				top: 74rpx;
 				right: 36rpx;
 				z-index: 2;
 			}
@@ -293,34 +289,6 @@
 		box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.04);
 		position: relative;
 		z-index: 2;
-
-		// 企业简介
-		.company-section {
-			background: #ffffff;
-
-			.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;
-				}
-			}
-		}
 	}
 	// 隐藏的 canvas 容器
 	.hidden-canvas-box {

+ 2 - 0
pages/splash/splash.vue

@@ -89,7 +89,9 @@
 			if (loginToggle) {
 				await userStore.queryCardInfo(userId)
 				await userStore.queryCompanyInfo(userId)
+				// #ifdef MP-WEIXIN
 				await userStore.queryCardPoster()
+				// #ifdef
 				uni.reLaunch({
 					url: !!userId ? '/pages/mine/userCard' : '/pages/index/index'
 				})

BIN
static/image/public/no-data.png


BIN
static/image/public/qyjj-icon.png


+ 45 - 75
utils/poster.js

@@ -27,8 +27,9 @@ const roundRect = (ctx, x, y, w, h, r) => {
  * 微信小程序 Canvas 2D 中不能用 uni.createImage()
  */
 const createCanvasImage = (canvas, src) => {
+	
 	return new Promise((resolve) => {
-		if (!src) return resolve(null)
+		if (!src || !canvas) return resolve(null)
 		const img = canvas.createImage()
 		img.onload = () => resolve(img)
 		img.onerror = () => {
@@ -120,30 +121,27 @@ const drawAvatarFallback = (ctx, cardInfo, cx, cy, r) => {
 	ctx.restore()
 }
 
-const drawContactRow = (ctx, emoji, text, x, y) => {
+const drawContactRow = (ctx, iconImg, text, x, y) => {
 	if (!text) return
-	ctx.save()
-	ctx.beginPath()
-	ctx.arc(x + 20, y + 20, 20, 0, Math.PI * 2)
-	ctx.fillStyle = 'rgba(64, 128, 255, 0.08)'
-	ctx.fill()
-	ctx.restore()
-	ctx.font = '22px sans-serif'
-	ctx.textAlign = 'center'
-	ctx.textBaseline = 'middle'
-	ctx.fillText(emoji, x + 20, y + 20)
+	// 图标(40x40)
+	const iconSize = 32
+	const iconX = x
+	const iconY = y
+	if (iconImg) {
+		ctx.drawImage(iconImg, iconX, iconY, iconSize, iconSize)
+	}
 	ctx.font = '26px sans-serif'
 	ctx.textAlign = 'left'
 	ctx.textBaseline = 'middle'
-	ctx.fillStyle = '#555555'
+	ctx.fillStyle = '#202020'
 	const textMaxWidth = 480
-	const textX = x + 52
+	const textX = x + iconSize + 12
 	if (ctx.measureText(text).width > textMaxWidth) {
 		ctx.textBaseline = 'top'
-		const lineY = y + 10
+		const lineY = y + 4
 		fillTextWrap(ctx, text, textX, lineY, textMaxWidth, 40, 'left')
 	} else {
-		ctx.fillText(text, textX, y + 20)
+		ctx.fillText(text, textX, y + iconSize / 2)
 	}
 }
 
@@ -187,9 +185,9 @@ export const generateCardPoster = async (cardInfo = {}, qrInfo = {}) => {
 
 				// ===== 1. 渐变背景 =====
 				const bgGrad = ctx.createLinearGradient(0, 0, 0, H)
-				bgGrad.addColorStop(0, '#4A90E2')
-				bgGrad.addColorStop(0.5, '#6FB3F2')
-				bgGrad.addColorStop(1, '#B0E0E6')
+				bgGrad.addColorStop(0, '#155DFC')
+				bgGrad.addColorStop(0.5, '#155DFC')
+				bgGrad.addColorStop(1, '#155DFC')
 				ctx.fillStyle = bgGrad
 				ctx.fillRect(0, 0, W, H)
 
@@ -199,44 +197,24 @@ export const generateCardPoster = async (cardInfo = {}, qrInfo = {}) => {
 				ctx.beginPath(); ctx.arc(680, 320, 60, 0, Math.PI * 2); ctx.fill()
 				ctx.beginPath(); ctx.arc(-20, 640, 80, 0, Math.PI * 2); ctx.fill()
 
-				// ===== 3. 白色卡片 =====
-				const cardX = PADDING, cardY = 60
-				const cardW = W - PADDING * 2, cardH = 480
-				ctx.save()
-				ctx.shadowColor = 'rgba(0,0,0,0.08)'
-				ctx.shadowBlur = 24
-				ctx.shadowOffsetY = 6
-				ctx.fillStyle = '#FFFFFF'
-				roundRect(ctx, cardX, cardY, cardW, cardH, 24)
-				ctx.fill()
-				ctx.restore()
-
-				// ===== 4. 内部名片卡片 =====
-				const innerX = cardX + 32
-				const innerY = cardY + 32
-				const innerW = cardW - 64
-				const innerH = cardH - 64
-				ctx.save()
-				ctx.shadowColor = 'rgba(0,0,0,0.05)'
-				ctx.shadowBlur = 16
-				ctx.shadowOffsetY = 3
-				ctx.fillStyle = '#FFFFFF'
-				roundRect(ctx, innerX, innerY, innerW, innerH, 16)
-				ctx.fill()
-				ctx.restore()
+				// ===== 3. 白色卡片(去掉白色背景,只保留圆角裁剪区域)=====
+				const cardX = 20, cardY = 60
+				const cardW = W - 40, cardH = 480
+				const innerPad = 16
+				const innerX = cardX + innerPad
+				const innerY = cardY + innerPad
+				const innerW = cardW - innerPad * 2
+				const innerH = cardH - innerPad * 2
 
-				// 背景图
+				// 背景图(静态资源直接传路径,canvas.createImage 支持加载本地包内资源)
 				try {
-					const bgSrc = await resolveImageSrc('/static/image/home/usecard-bg.png')
-					if (bgSrc) {
-						const cardBg = await createCanvasImage(canvas, bgSrc)
-						if (cardBg) {
-							ctx.save()
-							roundRect(ctx, innerX, innerY, innerW, innerH, 16)
-							ctx.clip()
-							ctx.drawImage(cardBg, innerX, innerY, innerW, innerH)
-							ctx.restore()
-						}
+					const cardBg = await createCanvasImage(canvas, '/static/image/home/usecard-bg.png')
+					if (cardBg) {
+						ctx.save()
+						roundRect(ctx, innerX, innerY, innerW, innerH, 16)
+						ctx.clip()
+						ctx.drawImage(cardBg, innerX, innerY, innerW, innerH)
+						ctx.restore()
 					}
 				} catch (e) {
 					console.warn('加载名片背景图失败:', e)
@@ -244,7 +222,7 @@ export const generateCardPoster = async (cardInfo = {}, qrInfo = {}) => {
 
 				// ===== 5. 头像(右上角)=====
 				const avatarCx = innerX + innerW - 100
-				const avatarCy = innerY + 70
+				const avatarCy = innerY + 115
 				const avatarR = 60
 				ctx.save()
 				ctx.beginPath()
@@ -266,7 +244,7 @@ export const generateCardPoster = async (cardInfo = {}, qrInfo = {}) => {
 
 				// ===== 6. 姓名 + 职位 + 公司 =====
 				const nameX = innerX + 44
-				const nameY = innerY + 50
+				const nameY = innerY + 70
 
 				ctx.fillStyle = '#202020'
 				ctx.font = 'bold 44px sans-serif'
@@ -277,21 +255,15 @@ export const generateCardPoster = async (cardInfo = {}, qrInfo = {}) => {
 				const nameW = ctx.measureText(name).width
 
 				if (cardInfo.postName) {
-					const tagX = nameX + nameW + 14
 					ctx.font = '22px sans-serif'
-					const tagTextW = ctx.measureText(cardInfo.postName).width
-					const tagW = tagTextW + 26
-					ctx.fillStyle = 'rgba(68, 110, 255, 0.10)'
-					roundRect(ctx, tagX, nameY + 6, tagW, 34, 8)
-					ctx.fill()
-					ctx.fillStyle = '#446EFF'
+					ctx.fillStyle = '#202020'
 					ctx.textAlign = 'left'
 					ctx.textBaseline = 'top'
-					ctx.fillText(cardInfo.postName, tagX + 13, nameY + 11)
+					ctx.fillText(cardInfo.postName, nameX + nameW + 14, nameY + 11)
 				}
 
 				// 公司名称
-				ctx.fillStyle = '#666666'
+				ctx.fillStyle = '#202020'
 				ctx.font = '26px sans-serif'
 				ctx.textAlign = 'left'
 				ctx.textBaseline = 'top'
@@ -306,16 +278,14 @@ export const generateCardPoster = async (cardInfo = {}, qrInfo = {}) => {
 				const contactX = innerX + 44
 				const contactGap = 60
 
-				ctx.strokeStyle = '#EEEEEE'
-				ctx.lineWidth = 1
-				ctx.beginPath()
-				ctx.moveTo(contactX, contactY)
-				ctx.lineTo(innerX + innerW - 44, contactY)
-				ctx.stroke()
+				// 预加载联系方式图标
+				const phoneIcon = await createCanvasImage(canvas, '/static/image/public/phone-icon.png')
+				const emailIcon = await createCanvasImage(canvas, '/static/image/public/email-icon.png')
+				const addressIcon = await createCanvasImage(canvas, '/static/image/public/address-icon.png')
 
-				drawContactRow(ctx, '📞', cardInfo.phonenumber, contactX, contactY + 20)
-				drawContactRow(ctx, '✉', cardInfo.email, contactX, contactY + 20 + contactGap)
-				drawContactRow(ctx, '📍', cardInfo.companyAddress, contactX, contactY + 20 + contactGap * 2)
+				drawContactRow(ctx, phoneIcon, cardInfo.phonenumber, contactX, contactY + 20)
+				drawContactRow(ctx, emailIcon, cardInfo.email, contactX, contactY + 20 + contactGap)
+				drawContactRow(ctx, addressIcon, cardInfo.companyAddress, contactX, contactY + 20 + contactGap * 2)
 
 				// ===== 8. 导出 =====
 				setTimeout(() => {