diff --git a/src/app.wpy b/src/app.wpy
index bf0fdde..c6a7eda 100644
--- a/src/app.wpy
+++ b/src/app.wpy
@@ -20,6 +20,7 @@ import promisify from '@wepy/use-promisify'
import {wx_login} from './utils/util'
import {IM} from './utils/im'
import {initEid} from './mp_ecard_sdk/main'
+import {invitationFromOptions} from './utils/invitation'
// import {service} from './config'
wepy.use(promisify)
@@ -50,10 +51,11 @@ wepy.app({
onLaunch(e) {
let vm = this
+ const invitationID = invitationFromOptions(e)
+ if (invitationID) wx.setStorageSync('from_user_id', invitationID)
console.log(e, 'uuu=')
vm.appLogin()
console.log(e, 'e=')
- console.log(decodeURIComponent(e.query.scene), 'e=')
// if (e.path === 'pages/home/informationV2') {
// wx.redirectTo({ url: `/pages/home/information?${decodeURIComponent(e.query.scene)}` })
// }
@@ -95,15 +97,19 @@ wepy.app({
if (options.path === 'pages/tabBar/home1') {
wx.switchTab({url: `/pages/tabBar/home?${decodeURIComponent(options.query.scene)}`})
}
- if (options.query.scene) {
- let sceneList = decodeURIComponent(options.query.scene).split(/[= &]/)
+ if (options.query && options.query.scene) {
+ let sceneList = []
+ try { sceneList = decodeURIComponent(options.query.scene).split(/[= &]/) } catch (_) {}
sceneList.forEach((item, index) => {
if (item == 'userId') {
wx.setStorageSync('agent_user_id', sceneList[index + 1])
}
})
}
- if (options.query && options.query.from_user_id) {
+ const invitationScene = invitationFromOptions(options)
+ if (invitationScene) {
+ wx.setStorageSync('from_user_id', invitationScene)
+ } else if (options.query && options.query.from_user_id) {
wx.setStorageSync('from_user_id', options.query.from_user_id)
} else {
if (options.path && options.path === 'pages/sub_mall/confirmOrder') {
@@ -235,6 +241,9 @@ subPackages: [
'orderDetails',
'exclusiveService',
'myDynamic',
+'more',
+'myShare',
+'shareCard',
'logoutDefault',
'ImageCropper',
'myDynamicDetail',
diff --git a/src/images/ambassador-contact.jpg b/src/images/ambassador-contact.jpg
new file mode 100644
index 0000000..e59ceea
Binary files /dev/null and b/src/images/ambassador-contact.jpg differ
diff --git a/src/images/fulian-logo.png b/src/images/fulian-logo.png
new file mode 100644
index 0000000..4077cf1
Binary files /dev/null and b/src/images/fulian-logo.png differ
diff --git a/src/pages/tabBar/user.wpy b/src/pages/tabBar/user.wpy
index b2c0268..7cc2114 100644
--- a/src/pages/tabBar/user.wpy
+++ b/src/pages/tabBar/user.wpy
@@ -358,12 +358,6 @@ wepy.page({
type: 'navigateTo',
path: '/pages/users/upgradeVIP'
},
- {
- icon: 'https://image.fulllinkai.com/202305/18/986abe4366a59ebc8c5437a63d4f957f.png',
- title: '专属客服',
- type: 'navigateTo',
- path: '/pages/users/exclusiveService'
- },
{
icon: 'https://image.fulllinkai.com/202201/13/0c8d92e86946c9a4244a92cacd4bd8d0.png',
title: '我的发布',
@@ -371,22 +365,22 @@ wepy.page({
path: '/pages/users/myDynamic'
},
{
- icon: 'https://image.fulllinkai.com/202202/17/0cb2fe566b98d607136bde08e7f27f4d.png',
- title: '福恋指南',
+ icon: 'https://image.fulllinkai.com/202302/06/4092bc34b48ef2b8b95e9d3a5954f803.png',
+ title: '我的分享',
type: 'navigateTo',
- path: 'https://love.ufutx.com/h5/#/guide'
- },
- {
- icon: 'https://image.fulllinkai.com/202202/21/c982ff4f02b9f834e6f4cd6886547f71.png',
- title: '意见反馈',
- type: 'navigateTo',
- path: '/pages/users/report?type=user'
+ path: '/pages/users/myShare'
},
{
icon: 'https://image.fulllinkai.com/202302/06/4092bc34b48ef2b8b95e9d3a5954f803.png',
title: '分享我的卡片',
- type: 'share',
- path: ''
+ type: 'navigateTo',
+ path: '/pages/users/shareCard'
+ },
+ {
+ icon: 'https://image.fulllinkai.com/202202/17/0cb2fe566b98d607136bde08e7f27f4d.png',
+ title: '更多',
+ type: 'navigateTo',
+ path: '/pages/users/more'
},
{
icon: 'https://image.fulllinkai.com/202201/13/0b3e132b03ee70e1325229ef8885ba29.png',
@@ -776,7 +770,7 @@ wepy.page({
return
}
if (text === '分享我的卡片') {
- vm.hide = false
+ wx.navigateTo({url: '/pages/users/shareCard'})
return
}
if (url && url.includes('page')) {
diff --git a/src/pages/users/more.wpy b/src/pages/users/more.wpy
new file mode 100644
index 0000000..6a98e48
--- /dev/null
+++ b/src/pages/users/more.wpy
@@ -0,0 +1,37 @@
+
+
+ 更多服务
+
+ 专属客服{{serviceLoading ? '加载中…' : '›'}}
+ 福恋指南›
+ 意见反馈›
+
+
+
+
+
+{navigationBarTitleText:'更多',navigationBarBackgroundColor:'#FFF9F5'}
diff --git a/src/pages/users/myShare.wpy b/src/pages/users/myShare.wpy
new file mode 100644
index 0000000..a27c9e0
--- /dev/null
+++ b/src/pages/users/myShare.wpy
@@ -0,0 +1,139 @@
+
+
+ 把美好的相遇分享出去我的分享
+ 正在加载邀请数据…
+ {{error}}
+
+ {{total}}邀请用户总数{{verified}}真人认证人数
+ 申请婚恋使者
+ 邀请的用户中有7位通过真人认证,即可联系荣耀老师申请婚恋使者。
+ 已完成 {{verified}} 位,还差 {{remaining}} 位
+
+ 申请资格查询失败,请稍后重试
+ 邀请用户
+ 还没有邀请用户,分享福恋给身边的朋友吧。
+ {{item.nickname || '福恋用户'}}{{item.verified ? '真人认证已通过' : '未完成真人认证'}}
+ 列表加载失败,请重试
+
+ 邀请订单仅婚恋使者可查看
+ 邀请朋友加入福恋
+ 把福恋分享给认真寻找爱情的人。
+
+ {{posterError}}
+
+
+
+
+ 联系荣耀老师
+ 添加微信时请备注“申请婚恋使者”,与老师沟通申请事宜。
+
+ 点击放大二维码,或保存后在微信扫一扫中选择相册识别。
+
+
+
+
+
+
+
+
+
+{navigationBarTitleText:'我的分享',navigationBarBackgroundColor:'#FFF9F5'}
diff --git a/src/pages/users/shareCard.wpy b/src/pages/users/shareCard.wpy
new file mode 100644
index 0000000..56151b9
--- /dev/null
+++ b/src/pages/users/shareCard.wpy
@@ -0,0 +1,74 @@
+
+
+ 我的个人名片
+ 让合适的人,认识你
+ 分享你的照片与介绍,开启一次认真相识。
+ 正在准备你的卡片…
+ {{error}}
+
+
+
+
+
+ {{profile.nickname}}{{profile.details}}
+ {{profile.introduction}}
+ {{posterError || '正在生成海报…'}}
+
+
+
+
+
+
+
+
+
+{navigationBarTitleText:'分享我的卡片',navigationBarBackgroundColor:'#FFF9F5'}
diff --git a/src/styles/sharing.less b/src/styles/sharing.less
new file mode 100644
index 0000000..8114e33
--- /dev/null
+++ b/src/styles/sharing.less
@@ -0,0 +1,29 @@
+.sharing-page { min-height:100vh; background:#FFF9F5; color:#4E4145; padding:36rpx 32rpx 64rpx; box-sizing:border-box; }
+.eyebrow { color:#D92554; font-size:24rpx; margin-bottom:16rpx; }
+.sharing-title { color:#A3294D; font-size:40rpx; font-weight:600; margin-bottom:16rpx; }
+.sharing-subtitle { font-size:26rpx; color:#97717D; line-height:1.7; margin-bottom:32rpx; }
+.sharing-poster { display:block; width:100%; border-radius:20rpx; }
+.sharing-preview { border-radius:24rpx; background:#FFF1F4; padding:28rpx; }
+.sharing-photo { width:100%; height:400rpx; border-radius:16rpx; margin:24rpx 0; }
+.sharing-logo { width:64rpx; height:64rpx; }
+.sharing-intro { margin:28rpx 0; font-size:28rpx; line-height:1.8; }
+.sharing-actions { display:flex; flex-wrap:wrap; gap:16rpx; margin-top:28rpx; }
+.sharing-page button { background:#D92554; color:#FFF9F5; border-radius:16rpx; font-size:28rpx; margin:0; }
+.sharing-page button[disabled] { background:#EED3DB; color:#4E4145; }
+.sharing-actions button { flex:1; min-width:240rpx; }
+.sharing-state { font-size:26rpx; color:#97717D; padding:32rpx 0; line-height:1.8; }
+.sharing-state button { margin-top:20rpx; }
+.sharing-section { margin-top:36rpx; font-size:32rpx; color:#A3294D; font-weight:600; }
+.sharing-stats { display:flex; gap:20rpx; padding:30rpx; background:#FFF1F4; border-radius:24rpx; }
+.sharing-stat { flex:1; font-size:26rpx; }
+.sharing-number { display:block; font-size:48rpx; color:#A3294D; margin-bottom:12rpx; }
+.sharing-row { display:flex; justify-content:space-between; gap:20rpx; padding:28rpx 0; border-bottom:1rpx solid #EED3DB; font-size:28rpx; }
+.sharing-row text:first-child { overflow-wrap:anywhere; }
+.sharing-status { color:#97717D; flex-shrink:0; }
+.application-mask { position:fixed; inset:0; z-index:1000; background:rgba(78,65,69,.6); display:flex; align-items:center; justify-content:center; padding:24rpx; box-sizing:border-box; }
+.application-dialog { width:100%; max-width:640rpx; max-height:90vh; overflow-y:auto; background:#FFF9F5; border-radius:28rpx; padding:28rpx; box-sizing:border-box; }
+.application-title { color:#A3294D; font-size:34rpx; font-weight:600; text-align:center; }
+.application-note { color:#4E4145; font-size:28rpx; line-height:1.7; margin:18rpx 0; }
+.application-code { display:block; width:100%; }
+.application-hint { font-size:24rpx; color:#97717D; line-height:1.6; margin:20rpx 0; }
+.sharing-page .application-close { margin-top:16rpx; color:#A3294D; background:#FFF1F4; }
diff --git a/src/utils/invitation.js b/src/utils/invitation.js
new file mode 100644
index 0000000..2396d24
--- /dev/null
+++ b/src/utils/invitation.js
@@ -0,0 +1,10 @@
+export function invitationFromOptions(options) {
+ const query = (options && options.query) || {}
+ let sceneID = ''
+ try {
+ const match = decodeURIComponent(query.scene || '').match(/(?:^|&)from_user_id=([1-9][0-9]*)(?:&|$)/)
+ if (match) sceneID = match[1]
+ } catch (_) {}
+ const value = String(sceneID || query.from_user_id || '')
+ return /^[1-9][0-9]*$/.test(value) && Number.isSafeInteger(Number(value)) ? value : ''
+}
diff --git a/src/utils/sharing.js b/src/utils/sharing.js
new file mode 100644
index 0000000..2ac2c3f
--- /dev/null
+++ b/src/utils/sharing.js
@@ -0,0 +1,116 @@
+// Sharing destinations stay separate: inviting someone never advertises the
+// inviter as a person seeking a relationship.
+export function sharingPath(id, personal) {
+ const userID = Number(id)
+ if (!Number.isSafeInteger(userID) || userID <= 0) throw new Error('请先登录后分享')
+ return personal
+ ? `/pages/home/information?id=${userID}`
+ : `/pages/tabBar/welcome?from_user_id=${userID}`
+}
+
+export function storeSharingCode(data, kind) {
+ const extension = data.mime === 'image/png' ? 'png' : data.mime === 'image/jpeg' ? 'jpg' : ''
+ if (!extension || !data.image) return Promise.reject(new Error('分享码格式无效'))
+ const filePath = `${wx.env.USER_DATA_PATH}/fulian-${kind}.${extension}`
+ return new Promise((resolve,reject) => wx.getFileSystemManager().writeFile({filePath,data:data.image,encoding:'base64',success:()=>resolve(filePath),fail:reject}))
+}
+
+function imageInfo(src) {
+ return new Promise((resolve, reject) => wx.getImageInfo({src, success: resolve, fail: reject}))
+}
+
+function textLines(ctx, text, x, y, width, lineHeight, maxLines) {
+ let line = '', row = 0
+ const chars = Array.from(String(text || '').replace(/\s+/g, ' ').trim())
+ for (let i = 0; i < chars.length; i++) {
+ if (ctx.measureText(line + chars[i]).width > width) {
+ if (row === maxLines - 1) {
+ while (line && ctx.measureText(line + '…').width > width) line = line.slice(0, -1)
+ ctx.fillText(line + '…', x, y + row * lineHeight)
+ return
+ }
+ // Keep closing punctuation with the preceding character on the next line.
+ const carry = /[,。!?;:、)】》」』]/.test(chars[i]) ? line.slice(-1) : ''
+ if (carry) line = line.slice(0, -1)
+ ctx.fillText(line, x, y + row * lineHeight)
+ row++
+ line = carry
+ }
+ line += chars[i]
+ }
+ ctx.fillText(line, x, y + row * lineHeight)
+}
+
+export async function renderSharingPoster(page, profile, personal) {
+ if (!profile.qrcode) throw new Error('分享码暂未准备好,请稍后重试')
+ const sources = ['/images/fulian-logo.png', profile.qrcode]
+ if (personal && profile.photo) sources.push(profile.photo)
+ const images = await Promise.all(sources.map(imageInfo))
+ const ctx = wx.createCanvasContext('fulianPoster', page.$wx)
+ ctx.setFillStyle('#FFF9F5'); ctx.fillRect(0, 0, 750, 1120)
+ ctx.drawImage(images[0].path, 48, 42, 64, 64)
+ ctx.setFillStyle('#A3294D'); ctx.setFontSize(30); ctx.fillText('福恋', 126, 84)
+ ctx.setFontSize(22); ctx.fillText('上福恋认真找对象', 440, 82)
+ if (personal) {
+ ctx.setFillStyle('#FFF1F4'); ctx.fillRect(48, 138, 654, 430)
+ if (images[2]) {
+ const im = images[2], ratio = Math.max(654 / im.width, 430 / im.height)
+ const sw = 654 / ratio, sh = 430 / ratio
+ ctx.drawImage(im.path, (im.width - sw) / 2, (im.height - sh) / 2, sw, sh, 48, 138, 654, 430)
+ }
+ ctx.setFillStyle('#A3294D'); ctx.setFontSize(40)
+ textLines(ctx, profile.nickname || '我的个人卡片', 48, 628, 654, 48, 1)
+ ctx.setFillStyle('#97717D'); ctx.setFontSize(24)
+ textLines(ctx, profile.details, 48, 674, 654, 32, 1)
+ ctx.setFillStyle('#4E4145'); ctx.setFontSize(28)
+ textLines(ctx, profile.introduction || '很高兴在福恋与你相遇。', 48, 730, 654, 38, 2)
+ if (profile.idealMate) {
+ ctx.setFontSize(24)
+ textLines(ctx, '期待的你:' + profile.idealMate, 48, 824, 654, 32, 1)
+ }
+ } else {
+ ctx.setFillStyle('#FFF1F4'); ctx.fillRect(48, 160, 654, 640)
+ ctx.setFillStyle('#A3294D'); ctx.setFontSize(52)
+ ctx.fillText('把福恋分享给', 88, 300); ctx.fillText('认真寻找爱情的人', 88, 380)
+ ctx.setFillStyle('#4E4145'); ctx.setFontSize(28)
+ textLines(ctx, '认识新朋友,开启认真交往的第一步。', 88, 470, 560, 46, 2)
+ ctx.setFontSize(25)
+ textLines(ctx, `${profile.nickname || '一位朋友'} 邀请你加入福恋`, 88, 690, 560, 38, 2)
+ }
+ ctx.setFillStyle('#EED3DB'); ctx.fillRect(48, 860, 654, 1)
+ ctx.drawImage(images[1].path, 48, 906, 164, 164)
+ ctx.setFillStyle('#A3294D'); ctx.setFontSize(28)
+ ctx.fillText(personal ? '扫码认识我' : '扫码加入福恋', 244, 965)
+ ctx.setFillStyle('#97717D'); ctx.setFontSize(23)
+ ctx.fillText(personal ? '查看我的完整个人资料' : '上福恋认真找对象', 244, 1010)
+ await new Promise(resolve => ctx.draw(false, resolve))
+ return new Promise((resolve, reject) => wx.canvasToTempFilePath({
+ canvasId: 'fulianPoster', width:750, height:1120, destWidth:750, destHeight:1120,
+ success: result => resolve(result.tempFilePath), fail: reject
+ }, page.$wx))
+}
+
+export function saveSharingPoster(filePath) {
+ return new Promise((resolve, reject) => wx.saveImageToPhotosAlbum({filePath, success:resolve, fail:reject}))
+}
+
+export async function renderSharingCover(page, profile, personal) {
+ const logo=await imageInfo('/images/fulian-logo.png')
+ const photo=personal && profile.photo ? await imageInfo(profile.photo) : null
+ const ctx=wx.createCanvasContext('fulianPoster',page.$wx)
+ ctx.setFillStyle('#FFF9F5'); ctx.fillRect(0,0,750,600)
+ ctx.drawImage(logo.path,40,32,64,64)
+ ctx.setFillStyle('#A3294D'); ctx.setFontSize(28); ctx.fillText('上福恋认真找对象',124,74)
+ if (photo) {
+ const ratio=Math.max(290/photo.width,360/photo.height),sw=290/ratio,sh=360/ratio
+ ctx.drawImage(photo.path,(photo.width-sw)/2,(photo.height-sh)/2,sw,sh,420,150,290,360)
+ }
+ ctx.setFillStyle('#A3294D');ctx.setFontSize(42)
+ textLines(ctx,personal ? profile.nickname || '我的个人卡片' : '邀请你加入福恋',40,210,personal ? 350 : 670,56,2)
+ ctx.setFillStyle('#4E4145');ctx.setFontSize(28)
+ textLines(ctx,personal ? profile.introduction || '期待与你认真相识' : '把福恋分享给认真寻找爱情的人',40,350,personal ? 350 : 670,44,3)
+ ctx.setFillStyle('#97717D');ctx.setFontSize(25)
+ ctx.fillText(personal ? '点击认识我' : '点击开启认真相识',40,550)
+ await new Promise(resolve=>ctx.draw(false,resolve))
+ return new Promise((resolve,reject)=>wx.canvasToTempFilePath({canvasId:'fulianPoster',width:750,height:600,destWidth:750,destHeight:600,success:res=>resolve(res.tempFilePath),fail:reject},page.$wx))
+}
diff --git a/tools/sharing/check-contact.cjs b/tools/sharing/check-contact.cjs
new file mode 100644
index 0000000..c7d4999
--- /dev/null
+++ b/tools/sharing/check-contact.cjs
@@ -0,0 +1,27 @@
+const fs=require('fs'),path=require('path'),vm=require('vm'),assert=require('assert')
+const source=fs.readFileSync(path.join(__dirname,'../../src/pages/users/myShare.wpy'),'utf8').match(/