zbb 1 тиждень тому
батько
коміт
e5d8df4b1e
5 змінених файлів з 271 додано та 310 видалено
  1. 2 6
      manifest.json
  2. 5 5
      pages/mine/card.vue
  3. 1 1
      pages/stats/index.vue
  4. 3 30
      pages/todos/index.vue
  5. 260 268
      utils/poster.js

+ 2 - 6
manifest.json

@@ -1,5 +1,5 @@
 {
-    "name" : "wechat-crm",
+    "name" : "布尔销销乐",
     "appid" : "__UNI__A78DC03",
     "description" : "",
     "versionName" : "1.0.0",
@@ -58,11 +58,7 @@
             "minified" : true
         },
         "usingComponents" : true,
-        "permission" : {
-            "scope.writePhotosAlbum" : {
-                "desc" : "用于保存二维码图片到相册"
-            }
-        },
+        "permission" : {},
         "tabBar" : {
             "color" : "#999999",
             "selectedColor" : "#667eea",

+ 5 - 5
pages/mine/card.vue

@@ -98,7 +98,7 @@
 
 		<!-- 隐藏的 Canvas 用于生成名片快照 -->
 		<canvas id="posterCanvas" type="2d"
-			style="position: fixed; left: -9999px; top: -9999px; width: 750px; height: 800px;"></canvas>
+			style="position: fixed; left: -9999px; top: -9999px; width: 750px; height: 780px;"></canvas>
 
 		<!-- 隐藏的 Canvas 用于生成二维码海报 -->
 		<view class="hidden-canvas-box">
@@ -184,8 +184,7 @@
 		</uni-popup>
 
 		<!-- 二维码弹窗 -->
-		<uni-popup ref="qrPopup" type="top">
-			<NavBar title="" color="#FFFFFF" :fixed="true" :bg="'transparent'"></NavBar>
+		<uni-popup ref="qrPopup" type="center">
 			<view class="qr-popup">
 				<view class="qr-header">
 					<text class="qr-title">名片二维码</text>
@@ -294,7 +293,7 @@
 		try {
 
 			// 生成名片快照
-			const snapshotPath = await generateCardPoster(cardInfo.value)
+			const snapshotPath = await generateCardPoster(cardInfo.value, qrInfo.value)
 			console.log(snapshotPath, "snapshotPathsnapshotPathsnapshotPath");
 			// 用属性接收快照路径
 			cardSnapshot.value = snapshotPath
@@ -330,7 +329,7 @@
 	}
 
 	const shareUserCard = async () => {
-		const snapshotPath = await generateCardPoster(cardInfo.value)
+		const snapshotPath = await generateCardPoster(cardInfo.value, qrInfo.value)
 		wx.showShareImageMenu({
 			path: snapshotPath,
 			entrancePath:'pages/splash/splash?userId=' + cardInfo.value.userId,
@@ -784,6 +783,7 @@
 	// 二维码弹窗样式
 	.qr-popup {
 		margin: 100rpx auto;
+		
 		width: 600rpx;
 		background: #ffffff;
 		border-radius: 24rpx;

+ 1 - 1
pages/stats/index.vue

@@ -2,7 +2,7 @@
 	<view class="stats-container">
 		<view class="placeholder">
 			<text class="icon"></text>
-			<text class="text">统计阿斯达DSDSAFSAD </text>
+			<text class="text"></text>
 		</view>
 	</view>
 </template>

+ 3 - 30
pages/todos/index.vue

@@ -195,9 +195,7 @@
 		ref,
 		reactive
 	} from 'vue'
-
 	const currentTab = ref('leads')
-
 	const tabCounts = reactive({
 		leads: 0,
 		customer: 0,
@@ -205,39 +203,14 @@
 		contract: 0,
 		payment: 0
 	})
-
 	const leadsList = reactive([])
-
 	const customerList = reactive([])
-
-	const opportunityList = reactive([{
-		title: '独守空房几十块雷锋精神',
-		stageType: 'negotiating',
-		stageName: '独守空房几',
-		companyName: '独守空房几十块',
-		contactName: '几十块雷锋精'
-	}, ])
-	const contractList = reactive([{
-		title: '独守空房几十块雷锋精神',
-		statusType: 'overdue',
-		statusName: '撒方方达',
-		companyName: '是的范德萨范德萨',
-		contractNo: 'NO741852960122'
-	}])
-
-	const paymentList = reactive([{
-		title: '独守空房几十块雷锋精神',
-		statusType: 'overdue',
-		statusName: '独守',
-		companyName: '独守空房几十块雷锋精神独守空房几十块雷锋精神',
-		date: '2026-03-06',
-		amount: '3680.9'
-	}, ])
-
+	const opportunityList = reactive([])
+	const contractList = reactive([])
+	const paymentList = reactive([ ])
 	const switchTab = (tab) => {
 		currentTab.value = tab
 	}
-
 	const viewDetail = () => {
 		uni.showToast({
 			title: '',

+ 260 - 268
utils/poster.js

@@ -1,306 +1,301 @@
 /**
  * 名片海报生成工具
- * 使用 Canvas 手动绘制名片
+ * 使用 Canvas 2D 绘制名片海报
  */
 
-/**
- * 生成名片海报
- * @param {Object} options - 配置选项
- * @param {Object} options.cardInfo - 名片信息
- * @returns {Promise<String>} - 生成的图片临时路径
- */
-export const generateCardPoster = async (cardInfo) => {
-	return new Promise((resolve, reject) => {
-		try {
-			// 创建离屏 canvas
-			const query = uni.createSelectorQuery()
-			query.select('#posterCanvas')
-				.fields({
-					node: true,
-					size: true
-				})
-				.exec(async (res) => {
-					if (!res[0]) {
-						reject(new Error('Canvas 节点未找到'))
-						return
-					}
-
-					const canvas = res[0].node
-					const ctx = canvas.getContext('2d')
-
-					// 设置 canvas 尺寸(高分辨率)
-					const width = 750
-					const height = 800
-					const dpr = uni.getSystemInfoSync().pixelRatio
-
-					canvas.width = width * dpr
-					canvas.height = height * dpr
-					ctx.scale(dpr, dpr)
-
-					// 1. 绘制背景渐变
-					const gradient = ctx.createLinearGradient(0, 0, 0, height)
-					gradient.addColorStop(0, '#4A90E2')
-					gradient.addColorStop(0.5, '#6FB3F2')
-					gradient.addColorStop(1, '#87CEEB')
-					ctx.fillStyle = gradient
-					ctx.fillRect(0, 0, width, height)
-
-					// 2. 绘制白色卡片背景
-					ctx.fillStyle = '#ffffff'
-					drawRoundRect(ctx, 40, 40, 670, 720, 24)
-					ctx.fill()
-
-					// 3. 绘制头像
-					const avatarPath = cardInfo.avatar ||
-						'/static/image/public/avatar-default.png'
-					await drawAvatar(ctx, avatarPath, 375, 160, 160)
-
-					// 4. 绘制姓名和职位
-					ctx.fillStyle = '#202020'
-					ctx.font = 'bold 44px sans-serif'
-					ctx.textAlign = 'center'
-					ctx.fillText(cardInfo.nickName || '用户', width / 2, 380)
-
-					// 职位标签背景
-					ctx.fillStyle = 'rgba(68, 110, 255, 0.1)'
-					drawRoundRect(ctx, (width - 180) / 2, 400, 180, 44, 8)
-					ctx.fill()
-
-					ctx.fillStyle = '#446eff'
-					ctx.font = '26px sans-serif'
-					ctx.textAlign = 'center'
-					ctx.fillText(cardInfo.postName || '职位', width / 2, 430)
-
-					// 5. 绘制公司名称
-					ctx.fillStyle = '#666666'
-					ctx.font = '30px sans-serif'
-					ctx.textAlign = 'center'
-					wrapText(ctx, cardInfo.companyName || '公司名称', width / 2, 480, 500, 36)
-
-					// 6. 绘制联系方式
-					const contactY = 560
-					const contactGap = 65
-					ctx.textAlign = 'left'
-					// 电话
-					if (cardInfo.phonenumber) {
-						await drawIconText(ctx, 'phone', cardInfo.phonenumber, width / 2 - 130,
-							contactY)
-					}
+const W = 750
+const H = 780
+const PADDING = 40
 
-					// 邮箱
-					if (cardInfo.email) {
-						await drawIconText(ctx, 'email', cardInfo.email, width / 2 - 130,
-							contactY + contactGap)
-					}
-
-					// 地址
-					if (cardInfo.companyAddress) {
-						await drawIconText(ctx, 'location', cardInfo.companyAddress, width / 2 -
-							130, contactY + contactGap * 2, )
-					}
-
-					// 导出图片
-					setTimeout(() => {
-						uni.canvasToTempFilePath({
-							canvas: canvas,
-							success: (res) => {
-								console.log('海报生成成功:', res.tempFilePath)
-								resolve(res.tempFilePath)
-							},
-							fail: (err) => {
-								console.error('海报导出失败:', err)
-								reject(err)
-							}
-						})
-					}, 300)
-				})
-		} catch (error) {
-			console.error('海报生成失败:', error)
-			reject(error)
-		}
-	})
-}
-
-/**
- * 绘制圆角矩形(兼容微信小程序)
- */
-const drawRoundRect = (ctx, x, y, width, height, radius) => {
+const roundRect = (ctx, x, y, w, h, r) => {
 	ctx.beginPath()
-	ctx.moveTo(x + radius, y)
-	ctx.lineTo(x + width - radius, y)
-	ctx.quadraticCurveTo(x + width, y, x + width, y + radius)
-	ctx.lineTo(x + width, y + height - radius)
-	ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height)
-	ctx.lineTo(x + radius, y + height)
-	ctx.quadraticCurveTo(x, y + height, x, y + height - radius)
-	ctx.lineTo(x, y + radius)
-	ctx.quadraticCurveTo(x, y, x + radius, y)
+	ctx.moveTo(x + r, y)
+	ctx.lineTo(x + w - r, y)
+	ctx.quadraticCurveTo(x + w, y, x + w, y + r)
+	ctx.lineTo(x + w, y + h - r)
+	ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h)
+	ctx.lineTo(x + r, y + h)
+	ctx.quadraticCurveTo(x, y + h, x, y + h - r)
+	ctx.lineTo(x, y + r)
+	ctx.quadraticCurveTo(x, y, x + r, y)
 	ctx.closePath()
 }
 
-/**
- * 绘制头像
- */
-const drawAvatar = async (ctx, avatarUrl, x, y, size) => {
+const loadImage = (src) => {
 	return new Promise((resolve) => {
-		if (avatarUrl.startsWith('http')) {
-			// 网络图片
-			downloadAndDraw(ctx, avatarUrl, x, y, size, resolve)
-		} else {
-			drawCircularImage(ctx, avatarUrl, x, y, size, resolve)
-		}
+		if (!src) return resolve(null)
+		const img = uni.createImage()
+		img.src = src
+		img.onload = () => resolve(img)
+		img.onerror = () => resolve(null)
 	})
 }
 
-/**
- * 下载并绘制网络图片
- */
-const downloadAndDraw = async (ctx, url, x, y, size, resolve) => {
+const downloadImage = async (url) => {
 	try {
-		const res = await uni.downloadFile({
-			url: url
-		})
-
-		if (res.statusCode === 200) {
-			drawCircularImage(ctx, res.tempFilePath, x, y, size, resolve)
-		} else {
-			console.error('图片下载失败:', res.statusCode)
-			resolve()
-		}
-	} catch (error) {
-		console.error('下载图片异常:', error)
-		resolve()
-	}
+		if (!url || !url.startsWith('http')) return url
+		const res = await uni.downloadFile({ url })
+		if (res.statusCode === 200) return res.tempFilePath
+		return null
+	} catch { return null }
 }
 
-/**
- * 绘制圆形图片
- */
-const drawCircularImage = (ctx, imageUrl, x, y, size, callback) => {
-	const image = uni.createImage()
-	image.src = imageUrl
-	image.onload = () => {
-		ctx.save()
-		ctx.beginPath()
-		ctx.arc(x, y, size / 2, 0, 2 * Math.PI)
-		ctx.clip()
-		ctx.drawImage(image, x - size / 2, y - size / 2, size, size)
-		ctx.restore()
-		if (callback) callback()
-	}
-	image.onerror = (err) => {
-		console.error('图片加载失败:', err)
-		if (callback) callback()
-	}
+const drawCircleAvatar = (ctx, img, cx, cy, r) => {
+	ctx.save()
+	ctx.beginPath()
+	ctx.arc(cx, cy, r, 0, Math.PI * 2)
+	ctx.clip()
+	if (img) ctx.drawImage(img, cx - r, cy - r, r * 2, r * 2)
+	ctx.restore()
 }
 
-/**
- * 绘制图标和文字
- */
-const drawIconText = async (ctx, type, text, x, y, maxWidth = 350) => {
-	return new Promise((resolve) => {
-		const iconSize = 32
-		const iconY = y - iconSize / 2
-
-		// 图标颜色
-		const iconColors = {
-			phone: '#4080FF',
-			email: '#4080FF',
-			location: '#4080FF'
-		}
-
-		ctx.fillStyle = iconColors[type] || '#4080FF'
-
-		// 绘制图标
-		const icons = {
-			phone: '',
-			email: '',
-			location: ''
-		}
-
-		ctx.font = `${iconSize}px sans-serif`
-		ctx.textAlign = 'center'
-		ctx.fillText(icons[type] || '●', x - maxWidth / 2 + 30, iconY + iconSize / 2 + 10)
-
-		// 绘制文字
-		ctx.fillStyle = '#666666'
-		ctx.font = '26px sans-serif'
-		ctx.textAlign = 'left'
-
-		// 文字截断处理
-		let displayText = text
-		if (text.length > 35) {
-			displayText = text.substring(0, 33) + '...'
-		}
-		ctx.fillText(displayText, x - maxWidth / 2 + 70, y + 8)
-
-		setTimeout(resolve, 50)
-	})
+const drawAvatarFallback = (ctx, cardInfo, cx, cy, r) => {
+	ctx.save()
+	ctx.beginPath()
+	ctx.arc(cx, cy, r, 0, Math.PI * 2)
+	ctx.fillStyle = '#4080FF'
+	ctx.fill()
+	ctx.strokeStyle = 'rgba(255,255,255,0.6)'
+	ctx.lineWidth = 4
+	ctx.stroke()
+	const initial = cardInfo.nickName ? cardInfo.nickName.charAt(0) : '?'
+	ctx.fillStyle = '#FFFFFF'
+	ctx.font = `bold ${r}px sans-serif`
+	ctx.textAlign = 'center'
+	ctx.textBaseline = 'middle'
+	ctx.fillText(initial, cx, cy)
+	ctx.restore()
 }
 
-/**
- * 绘制换行文字
- */
-const wrapText = (ctx, text, x, y, maxWidth, lineHeight) => {
+const drawContactRow = (ctx, emoji, 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()
+	// Emoji
+	ctx.font = '22px sans-serif'
+	ctx.textAlign = 'center'
+	ctx.textBaseline = 'middle'
+	ctx.fillText(emoji, x + 20, y + 20)
+	// 文字
+	ctx.font = '26px sans-serif'
+	ctx.textAlign = 'left'
+	ctx.textBaseline = 'middle'
+	ctx.fillStyle = '#555555'
+	let display = text
+	if (text.length > 24) display = text.substring(0, 22) + '...'
+	ctx.fillText(display, x + 52, y + 20)
+}
 
-	const words = text.split('')
-	let line = ''
-	let currentY = y
-
-	for (let i = 0; i < words.length; i++) {
-		const testLine = line + words[i]
-		const metrics = ctx.measureText(testLine)
-
-		if (metrics.width > maxWidth && i > 0) {
-			ctx.fillText(line, x, currentY)
-			line = words[i]
-			currentY += lineHeight
+const fillTextWrap = (ctx, text, x, y, maxWidth, lineHeight) => {
+	if (!text) return y
+	ctx.textAlign = 'center'
+	ctx.textBaseline = 'top'
+	const chars = text.split('')
+	let line = '', cy = y
+	for (let i = 0; i < chars.length; i++) {
+		const test = line + chars[i]
+		if (ctx.measureText(test).width > maxWidth && i > 0) {
+			ctx.fillText(line, x, cy)
+			line = chars[i]
+			cy += lineHeight
 		} else {
-			line = testLine
+			line = test
 		}
 	}
-	ctx.fillText(line, x, currentY)
+	ctx.fillText(line, x, cy)
+	return cy + lineHeight
 }
 
-/**
- * 保存海报到相册
- */
-export const savePosterToAlbum = async (tempFilePath) => {
+export const generateCardPoster = async (cardInfo = {}) => {
+	return new Promise((resolve, reject) => {
+		const query = uni.createSelectorQuery()
+		query.select('#posterCanvas')
+			.fields({ node: true, size: true })
+			.exec(async (res) => {
+				if (!res || !res[0]) {
+					reject(new Error('Canvas 节点未找到'))
+					return
+				}
+
+				const canvas = res[0].node
+				const ctx = canvas.getContext('2d')
+				const dpr = uni.getSystemInfoSync().pixelRatio || 2
+				canvas.width = W * dpr
+				canvas.height = H * dpr
+				ctx.scale(dpr, dpr)
+
+				// ===== 1. 渐变背景 =====
+				const bgGrad = ctx.createLinearGradient(0, 0, 0, H)
+				bgGrad.addColorStop(0, '#4A90E2')
+				bgGrad.addColorStop(0.5, '#6FB3F2')
+				bgGrad.addColorStop(1, '#B0E0E6')
+				ctx.fillStyle = bgGrad
+				ctx.fillRect(0, 0, W, H)
+
+				// ===== 2. 装饰圆点 =====
+				ctx.fillStyle = 'rgba(255,255,255,0.10)'
+				ctx.beginPath(); ctx.arc(620, 160, 100, 0, Math.PI * 2); ctx.fill()
+				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 = 660
+				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()
+
+				// 背景图
+				try {
+					const cardBg = await loadImage('/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) {}
+
+				// ===== 5. 头像(右上角)=====
+				const avatarCx = innerX + innerW - 100
+				const avatarCy = innerY + 70
+				const avatarR = 60
+				ctx.save()
+				ctx.beginPath()
+				ctx.arc(avatarCx, avatarCy, avatarR + 4, 0, Math.PI * 2)
+				ctx.fillStyle = 'rgba(255,255,255,0.5)'
+				ctx.fill()
+				ctx.restore()
+
+				let avatarImg = null
+				if (cardInfo.avatar) {
+					const localPath = await downloadImage(cardInfo.avatar)
+					avatarImg = await loadImage(localPath)
+				}
+				if (avatarImg) {
+					drawCircleAvatar(ctx, avatarImg, avatarCx, avatarCy, avatarR)
+				} else {
+					drawAvatarFallback(ctx, cardInfo, avatarCx, avatarCy, avatarR)
+				}
+
+				// ===== 6. 姓名 + 职位 + 公司 =====
+				const nameX = innerX + 44
+				const nameY = innerY + 50
+
+				ctx.fillStyle = '#202020'
+				ctx.font = 'bold 44px sans-serif'
+				ctx.textAlign = 'left'
+				ctx.textBaseline = 'top'
+				const name = cardInfo.nickName || '用户'
+				ctx.fillText(name, nameX, nameY)
+				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.textAlign = 'left'
+					ctx.textBaseline = 'top'
+					ctx.fillText(cardInfo.postName, tagX + 13, nameY + 11)
+				}
+
+				// 公司名称(最大宽度避开右上方头像)
+				ctx.fillStyle = '#666666'
+				ctx.font = '26px sans-serif'
+				ctx.textAlign = 'left'
+				ctx.textBaseline = 'top'
+				const companyMaxW = innerW - 160
+				const companyName = cardInfo.companyName || '公司名称'
+				// 如果公司名太长,手动截断
+				ctx.font = '26px sans-serif'
+				if (ctx.measureText(companyName).width > companyMaxW) {
+					let display = companyName
+					while (ctx.measureText(display + '...').width > companyMaxW && display.length > 0) {
+						display = display.substring(0, display.length - 1)
+					}
+					ctx.fillText(display + '...', innerX + 44, nameY + 60)
+				} else {
+					ctx.fillText(companyName, innerX + 44, nameY + 60)
+				}
+
+				// ===== 7. 联系方式 =====
+				const contactY = innerY + 160
+				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()
+
+				drawContactRow(ctx, '📞', cardInfo.phonenumber, contactX, contactY + 20)
+				drawContactRow(ctx, '✉', cardInfo.email, contactX, contactY + 20 + contactGap)
+				drawContactRow(ctx, '📍', cardInfo.companyAddress, contactX, contactY + 20 + contactGap * 2)
+
+				// ===== 8. 导出 =====
+				setTimeout(() => {
+					uni.canvasToTempFilePath({
+						canvas,
+						success: (res) => resolve(res.tempFilePath),
+						fail: (err) => reject(err)
+					})
+				}, 400)
+			})
+	})
+}
+
+export const savePosterToAlbum = (tempFilePath) => {
 	return new Promise((resolve, reject) => {
 		uni.authorize({
 			scope: 'scope.writePhotosAlbum',
-			success: async () => {
+			success: () => {
 				uni.saveImageToPhotosAlbum({
 					filePath: tempFilePath,
 					success: () => resolve(),
 					fail: (err) => reject(err)
 				})
 			},
-			fail: (err) => {
+			fail: () => {
 				uni.showModal({
 					title: '提示',
-					content: '需要授权才能保存到相册',
+					content: '需要授权相册权限才能保存名片',
+					confirmText: '去设置',
 					success: (res) => {
-						if (res.confirm) {
-							uni.openSetting({
-								success: (settingRes) => {
-									if (settingRes.authSetting[
-											'scope.writePhotosAlbum'
-											]) {
-										savePosterToAlbum(
-											tempFilePath).then(
-											resolve).catch(
-											reject)
-									} else {
-										reject(new Error('用户拒绝授权'))
-									}
-								},
-								fail: (err) => reject(err)
-							})
-						} else {
-							reject(new Error('用户取消授权'))
-						}
+						if (res.confirm) uni.openSetting({})
+						reject(new Error('未授权'))
 					}
 				})
 			}
@@ -308,7 +303,4 @@ export const savePosterToAlbum = async (tempFilePath) => {
 	})
 }
 
-export default {
-	generateCardPoster,
-	savePosterToAlbum
-}
+export default { generateCardPoster, savePosterToAlbum }