feat: 企业版合作模式(后端、管理端、小程序)
- API:合作模式配置、用户选择、企业版入口与迁移 SQL - 管理端:合作方式管理、企业/用户侧设置与 CSV 导出等 - 小程序:合作模式弹窗与结果/简历等页对接;开发脚本小调整 Made-with: Cursor
This commit is contained in:
@@ -58,8 +58,8 @@ App({
|
||||
defaultEnterpriseId: null,
|
||||
// API 基础地址:默认走线上;本机/内网调试可在开发者工具执行
|
||||
// wx.setStorageSync('apiBaseOverride', 'https://你的调试域名') 后重启小程序
|
||||
apiBase: 'https://mbtiapi.quwanzhi.com',
|
||||
//apiBase: 'http://mbti.com',
|
||||
//apiBase: 'https://mbtiapi.quwanzhi.com',
|
||||
apiBase: 'http://mbti.com',
|
||||
// VIP信息
|
||||
vipInfo: null,
|
||||
// 测试次数
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
const { request } = require('../../utils/request.js')
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
modes: {
|
||||
type: Array,
|
||||
value: []
|
||||
}
|
||||
},
|
||||
|
||||
data: {
|
||||
selected: '',
|
||||
submitting: false
|
||||
},
|
||||
|
||||
observers: {
|
||||
visible(v) {
|
||||
if (v) {
|
||||
const modes = this.properties.modes || []
|
||||
const first = modes.length ? modes[0].code : ''
|
||||
this.setData({ selected: first || '', submitting: false })
|
||||
}
|
||||
},
|
||||
modes(modes) {
|
||||
if (this.properties.visible && modes && modes.length) {
|
||||
const cur = this.data.selected
|
||||
const ok = modes.some((m) => m && m.code === cur)
|
||||
if (!ok) this.setData({ selected: modes[0].code })
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
noop() {},
|
||||
|
||||
onMask() {},
|
||||
|
||||
onRadioChange(e) {
|
||||
const v = e.detail && e.detail.value ? String(e.detail.value) : ''
|
||||
if (v) this.setData({ selected: v })
|
||||
},
|
||||
|
||||
onConfirm() {
|
||||
const code = (this.data.selected || '').trim()
|
||||
if (!code) {
|
||||
wx.showToast({ title: '请选择一项', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (this.data.submitting) return
|
||||
this.setData({ submitting: true })
|
||||
request({
|
||||
url: '/api/user/cooperation-preference',
|
||||
method: 'POST',
|
||||
data: { modeCode: code },
|
||||
success: (res) => {
|
||||
const body = res.data || {}
|
||||
if (res.statusCode === 200 && body.code === 200) {
|
||||
wx.showToast({ title: body.message || '已提交', icon: 'success' })
|
||||
this.triggerEvent('success', body.data || {})
|
||||
} else {
|
||||
wx.showToast({ title: body.message || '提交失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
wx.showToast({ title: '网络错误', icon: 'none' })
|
||||
},
|
||||
complete: () => {
|
||||
this.setData({ submitting: false })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<view class="cmd-mask" wx:if="{{visible}}" catchtouchmove="noop" catchtap="onMask">
|
||||
<view class="cmd-panel" catchtap="noop">
|
||||
<text class="cmd-title">请选择合作模式</text>
|
||||
<text class="cmd-desc">三项测评已完成,请选择与您更契合的合作方式(提交后可在企业侧活动中使用)。</text>
|
||||
|
||||
<radio-group class="cmd-list" bindchange="onRadioChange">
|
||||
<label class="cmd-item" wx:for="{{modes}}" wx:key="code">
|
||||
<radio value="{{item.code}}" checked="{{selected === item.code}}" color="#ff6b8a" />
|
||||
<view class="cmd-item-main">
|
||||
<text class="cmd-item-title">{{item.title}}</text>
|
||||
<text class="cmd-item-desc">{{item.description}}</text>
|
||||
</view>
|
||||
</label>
|
||||
</radio-group>
|
||||
|
||||
<view class="cmd-actions">
|
||||
<button class="cmd-btn cmd-btn--primary" hover-class="none" loading="{{submitting}}" bindtap="onConfirm">确认提交</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,104 @@
|
||||
.cmd-mask {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 12500;
|
||||
background: rgba(15, 23, 42, 0.48);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 48rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cmd-panel {
|
||||
width: 100%;
|
||||
max-width: 620rpx;
|
||||
background: #fff;
|
||||
border-radius: 28rpx;
|
||||
padding: 40rpx 36rpx 36rpx;
|
||||
box-shadow: 0 24rpx 80rpx rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
|
||||
.cmd-title {
|
||||
display: block;
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.cmd-desc {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.55;
|
||||
color: #6b7280;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.cmd-list {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cmd-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16rpx;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 2rpx solid #f3f4f6;
|
||||
}
|
||||
|
||||
.cmd-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.cmd-item radio {
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.cmd-item-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.cmd-item-title {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.cmd-item-desc {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.45;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.cmd-actions {
|
||||
margin-top: 28rpx;
|
||||
}
|
||||
|
||||
.cmd-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 16rpx;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.cmd-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.cmd-btn--primary {
|
||||
background: linear-gradient(135deg, #ff6b8a 0%, #ff8e53 100%);
|
||||
color: #fff;
|
||||
}
|
||||
@@ -54,6 +54,7 @@ function mergeSceneBlocks(apiData, baseResult) {
|
||||
}
|
||||
}
|
||||
const { getEnterpriseIdForApiPayload } = require('../../utils/enterpriseContext.js')
|
||||
const enterpriseCooperation = require('../../utils/enterpriseCooperation.js')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -119,7 +120,9 @@ Page({
|
||||
reportTitle: '分析报告',
|
||||
aiAnalysisText: '智能分析',
|
||||
showInviteCodeDialog: false,
|
||||
hideInviteCodeEntry: false
|
||||
hideInviteCodeEntry: false,
|
||||
showCooperationModal: false,
|
||||
cooperationModes: []
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
@@ -278,6 +281,7 @@ Page({
|
||||
aiAnalysisText: tc.aiAnalysisText || '智能分析'
|
||||
})
|
||||
}
|
||||
enterpriseCooperation.maybeShowCooperationModal(this)
|
||||
},
|
||||
|
||||
// 面相/骨相 Tab 切换
|
||||
@@ -598,6 +602,10 @@ Page({
|
||||
inviteCodeGate.finishInviteCodeGate(this, true)
|
||||
},
|
||||
|
||||
onCooperationSuccess() {
|
||||
this.setData({ showCooperationModal: false, cooperationModes: [] })
|
||||
},
|
||||
|
||||
/** 支付成功后:仅绑定手机号,不重复发起支付 */
|
||||
onPostPayBindPhone(e) {
|
||||
const { code, errMsg } = e.detail || {}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"enableShareAppMessage": true,
|
||||
"enableShareTimeline": true,
|
||||
"usingComponents": {
|
||||
"invite-code-dialog": "/components/invite-code-dialog/invite-code-dialog"
|
||||
"invite-code-dialog": "/components/invite-code-dialog/invite-code-dialog",
|
||||
"cooperation-mode-dialog": "/components/cooperation-mode-dialog/cooperation-mode-dialog"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,4 +465,9 @@
|
||||
bind:skip="onInviteCodeSkip"
|
||||
bind:success="onInviteCodeSuccess"
|
||||
/>
|
||||
<cooperation-mode-dialog
|
||||
visible="{{showCooperationModal}}"
|
||||
modes="{{cooperationModes}}"
|
||||
bind:success="onCooperationSuccess"
|
||||
/>
|
||||
</view>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"navigationBarTitleText": "推广中心",
|
||||
"navigationBarBackgroundColor": "#FFFFFF",
|
||||
"navigationBarTextStyle": "black",
|
||||
"usingComponents": {
|
||||
"invite-code-dialog": "/components/invite-code-dialog/invite-code-dialog"
|
||||
}
|
||||
{
|
||||
"navigationBarTitleText": "推广中心",
|
||||
"navigationBarBackgroundColor": "#FFFFFF",
|
||||
"navigationBarTextStyle": "black",
|
||||
"usingComponents": {
|
||||
"invite-code-dialog": "/components/invite-code-dialog/invite-code-dialog"
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ const {
|
||||
markCamera
|
||||
} = require('../../utils/resultJourneyState.js')
|
||||
const resultScrollSync = require('../../utils/resultSectionScrollSync.js')
|
||||
const enterpriseCooperation = require('../../utils/enterpriseCooperation.js')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -77,7 +78,9 @@ Page({
|
||||
activeSection: '',
|
||||
showInviteCodeDialog: false,
|
||||
/** 审核/提审时隐藏填写邀请码入口(与 runtime 开关同步) */
|
||||
hideInviteCodeEntry: false
|
||||
hideInviteCodeEntry: false,
|
||||
showCooperationModal: false,
|
||||
cooperationModes: []
|
||||
},
|
||||
|
||||
onTapSectionNav(e) {
|
||||
@@ -136,6 +139,7 @@ Page({
|
||||
if (raw) this.applyResult(raw)
|
||||
}
|
||||
this._syncJourney()
|
||||
enterpriseCooperation.maybeShowCooperationModal(this)
|
||||
},
|
||||
|
||||
goCompleteProfile() {
|
||||
@@ -445,6 +449,10 @@ Page({
|
||||
inviteCodeGate.finishInviteCodeGate(this, true)
|
||||
},
|
||||
|
||||
onCooperationSuccess() {
|
||||
this.setData({ showCooperationModal: false, cooperationModes: [] })
|
||||
},
|
||||
|
||||
// 付费解锁按钮:就地触发微信手机号授权,然后调用 unlockFullReport
|
||||
onGetPhoneNumberForMbtiPay(e) {
|
||||
const { code, errMsg } = e.detail || {}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"navigationBarBackgroundColor": "#FF6B8A",
|
||||
"navigationBarTextStyle": "white",
|
||||
"usingComponents": {
|
||||
"invite-code-dialog": "/components/invite-code-dialog/invite-code-dialog"
|
||||
"invite-code-dialog": "/components/invite-code-dialog/invite-code-dialog",
|
||||
"cooperation-mode-dialog": "/components/cooperation-mode-dialog/cooperation-mode-dialog"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,4 +336,9 @@
|
||||
bind:skip="onInviteCodeSkip"
|
||||
bind:success="onInviteCodeSuccess"
|
||||
/>
|
||||
<cooperation-mode-dialog
|
||||
visible="{{showCooperationModal}}"
|
||||
modes="{{cooperationModes}}"
|
||||
bind:success="onCooperationSuccess"
|
||||
/>
|
||||
</view>
|
||||
|
||||
@@ -5,6 +5,7 @@ const inviteCodeGate = require('../../utils/inviteCodeGate.js')
|
||||
const { shouldHideInviteCodeEntry } = require('../../utils/miniprogramAuditGate.js')
|
||||
const { getEnterpriseIdForApiPayload } = require('../../utils/enterpriseContext.js')
|
||||
const { triggerTestResultCompleted } = require('../../utils/pushHook')
|
||||
const enterpriseCooperation = require('../../utils/enterpriseCooperation.js')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -27,7 +28,9 @@ Page({
|
||||
paying: false,
|
||||
profileGate: false,
|
||||
showInviteCodeDialog: false,
|
||||
hideInviteCodeEntry: false
|
||||
hideInviteCodeEntry: false,
|
||||
showCooperationModal: false,
|
||||
cooperationModes: []
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
@@ -66,6 +69,7 @@ Page({
|
||||
if (id && this.data.profileGate) {
|
||||
this.loadFromHistory(String(id))
|
||||
}
|
||||
enterpriseCooperation.maybeShowCooperationModal(this)
|
||||
},
|
||||
|
||||
goCompleteProfile() {
|
||||
@@ -291,6 +295,10 @@ Page({
|
||||
inviteCodeGate.finishInviteCodeGate(this, true)
|
||||
},
|
||||
|
||||
onCooperationSuccess() {
|
||||
this.setData({ showCooperationModal: false, cooperationModes: [] })
|
||||
},
|
||||
|
||||
goHome() {
|
||||
const scope = (app.globalData && app.globalData.appScope) || 'personal'
|
||||
if (scope === 'enterprise') {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"navigationBarTitleText": "简历综合分析",
|
||||
"usingComponents": {
|
||||
"invite-code-dialog": "/components/invite-code-dialog/invite-code-dialog"
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"navigationBarTitleText": "简历综合分析",
|
||||
"usingComponents": {
|
||||
"invite-code-dialog": "/components/invite-code-dialog/invite-code-dialog",
|
||||
"cooperation-mode-dialog": "/components/cooperation-mode-dialog/cooperation-mode-dialog"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,5 +107,10 @@
|
||||
bind:skip="onInviteCodeSkip"
|
||||
bind:success="onInviteCodeSuccess"
|
||||
/>
|
||||
<cooperation-mode-dialog
|
||||
visible="{{showCooperationModal}}"
|
||||
modes="{{cooperationModes}}"
|
||||
bind:success="onCooperationSuccess"
|
||||
/>
|
||||
</view>
|
||||
|
||||
|
||||
105
miniprogram/utils/enterpriseCooperation.js
Normal file
105
miniprogram/utils/enterpriseCooperation.js
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 企业版合作模式弹框:仅在 enterprise 定价 + 企业上下文 + 非审核隐藏 + 三项完成且未选择时触发
|
||||
*/
|
||||
|
||||
const { getEffectiveEnterpriseId } = require('./enterpriseContext.js')
|
||||
const { shouldHideInviteCodeEntry } = require('./miniprogramAuditGate.js')
|
||||
|
||||
let flowLock = false
|
||||
|
||||
function shouldRunCooperationFlow(app) {
|
||||
const gd = app && app.globalData
|
||||
if (!gd) return false
|
||||
try {
|
||||
if (shouldHideInviteCodeEntry(gd)) return false
|
||||
} catch (e) {}
|
||||
return gd.appScope === 'enterprise' || getEffectiveEnterpriseId() != null
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {WechatMiniprogram.Page.TrivialInstance | null} page
|
||||
*/
|
||||
function maybeShowCooperationModal(page) {
|
||||
const app = getApp()
|
||||
if (!page || typeof page.setData !== 'function') return
|
||||
if (flowLock) return
|
||||
if (!app.ensureLogin) return
|
||||
|
||||
app.ensureLogin().then((ok) => {
|
||||
if (!ok) return
|
||||
|
||||
flowLock = true
|
||||
const unlock = () => {
|
||||
flowLock = false
|
||||
}
|
||||
|
||||
Promise.resolve(app.getRuntimeConfig ? app.getRuntimeConfig() : null)
|
||||
.then((cfg) => {
|
||||
if (!cfg || cfg.pricingType !== 'enterprise') {
|
||||
unlock()
|
||||
return
|
||||
}
|
||||
if (!shouldRunCooperationFlow(app)) {
|
||||
unlock()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const phoneAuth = require('./phoneAuth.js')
|
||||
if (!phoneAuth.isReportProfileComplete()) {
|
||||
wx.showToast({ title: '请先完善手机号与资料', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
wx.navigateTo({
|
||||
url: '/pages/user-profile/index?from=cooperation_gate',
|
||||
fail: () => {}
|
||||
})
|
||||
}, 400)
|
||||
unlock()
|
||||
return
|
||||
}
|
||||
} catch (e) {
|
||||
unlock()
|
||||
return
|
||||
}
|
||||
|
||||
const apiBase = (app.globalData && app.globalData.apiBase) || ''
|
||||
const token = wx.getStorageSync('token') || (app.globalData && app.globalData.token) || ''
|
||||
if (!apiBase || !token) {
|
||||
unlock()
|
||||
return
|
||||
}
|
||||
|
||||
wx.request({
|
||||
url: `${apiBase.replace(/\/$/, '')}/api/user/cooperation-status`,
|
||||
method: 'GET',
|
||||
header: { Authorization: `Bearer ${token}` },
|
||||
complete: unlock,
|
||||
success: (res) => {
|
||||
const body = res.data || {}
|
||||
if (res.statusCode !== 200 || body.code !== 200 || !body.data) return
|
||||
const d = body.data
|
||||
|
||||
const eid = d.enterpriseId != null ? Number(d.enterpriseId) : 0
|
||||
if (!eid) return
|
||||
|
||||
if (d.chosen) return
|
||||
if (!d.allDone) return
|
||||
|
||||
const modes = Array.isArray(d.modes) ? d.modes : []
|
||||
if (modes.length === 0) return
|
||||
|
||||
page.setData({
|
||||
showCooperationModal: true,
|
||||
cooperationModes: modes
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(unlock)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
shouldRunCooperationFlow,
|
||||
maybeShowCooperationModal
|
||||
}
|
||||
Reference in New Issue
Block a user