高考版优化
审核模式优化
This commit is contained in:
@@ -3,6 +3,10 @@ const { request } = require('../../utils/request.js')
|
||||
const analytics = require('../../utils/analytics.js')
|
||||
const { buildRecoMiniProgramPath } = require('../../utils/recoMiniProgram.js')
|
||||
const aiChatIntent = require('../../utils/aiChatIntent.js')
|
||||
const { isAuditHideAiMode, redirectIfMiniprogramAudit } = require('../../utils/miniprogramAuditGate.js')
|
||||
const { buildDeepHeroDesc, filterCategories } = require('../../utils/deepPricingFilter.js')
|
||||
const payment = require('../../utils/payment')
|
||||
const { hasPhone, bindPhoneByCode } = require('../../utils/phoneAuth.js')
|
||||
|
||||
/** 功能类推荐卡「阅读数」按发布日起算时的锚点日期 */
|
||||
const FEATURE_RECO_PUBLISHED_AT = '2026-01-01'
|
||||
@@ -12,6 +16,45 @@ const REPORT_CTA_MIN_USER_TURNS = 10
|
||||
/** 报告条仅展示一次(点过或离开带条页面后不再出现) */
|
||||
const STORAGE_REPORT_CTA_DISMISSED = 'ai_chat_deep_report_cta_dismissed'
|
||||
|
||||
function embeddedDeepCategories(scope) {
|
||||
const app = getApp()
|
||||
const gd = (app && app.globalData) || {}
|
||||
const list = scope === 'enterprise' ? gd.deepPricingEnterprise : gd.deepPricingPersonal
|
||||
return Array.isArray(list) && list.length ? list : null
|
||||
}
|
||||
|
||||
function rememberCategoryKeys(cat, seen) {
|
||||
const id = String((cat && cat.id) || '')
|
||||
const pk = String((cat && cat.productKey) || '')
|
||||
if (id) seen.add('id:' + id)
|
||||
if (pk) seen.add('pk:' + pk)
|
||||
}
|
||||
|
||||
function categorySeenInSet(cat, seen) {
|
||||
const id = String((cat && cat.id) || '')
|
||||
const pk = String((cat && cat.productKey) || '')
|
||||
if (id && seen.has('id:' + id)) return true
|
||||
if (pk && seen.has('pk:' + pk)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
function mergeDeepPricingFromRuntime(scope, apiList) {
|
||||
let list = Array.isArray(apiList) ? apiList.slice() : []
|
||||
const emb = embeddedDeepCategories(scope)
|
||||
if (!emb || !emb.length) return list
|
||||
if (!list.length) return emb.slice()
|
||||
if (list.length === 1 && emb.length > list.length) return emb.slice()
|
||||
const seen = new Set()
|
||||
list.forEach((c) => rememberCategoryKeys(c, seen))
|
||||
emb.forEach((c) => {
|
||||
if (!categorySeenInSet(c, seen)) {
|
||||
list.push(c)
|
||||
rememberCategoryKeys(c, seen)
|
||||
}
|
||||
})
|
||||
return list
|
||||
}
|
||||
|
||||
// 兜底封面(base64 SVG,紫色渐变 + sparkle)
|
||||
const DEFAULT_COVER = 'data:image/svg+xml;utf8,' + encodeURIComponent(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 440 220">' +
|
||||
@@ -63,12 +106,33 @@ Page({
|
||||
inlineRecoInterval: 3,
|
||||
inlineRecoRoll: 0.5,
|
||||
inlineRecoIconCount: 3,
|
||||
inlineRecoIconsPool: ['✨', '💬', '📌']
|
||||
inlineRecoIconsPool: ['✨', '💬', '📌'],
|
||||
/** 聊天区固定高度(px),与底部固定 composer 对齐,避免 scroll-view 被内容撑开压到输入条下 */
|
||||
chatScrollHeightPx: 500,
|
||||
auditPurchaseMode: false,
|
||||
auditPurchaseLoading: false,
|
||||
auditPurchaseError: '',
|
||||
auditPurchaseTab: 'personal',
|
||||
auditPurchasePersonal: [],
|
||||
auditPurchaseEnterprise: [],
|
||||
auditDeepHeroDesc: '',
|
||||
auditHasPhone: false,
|
||||
auditPurchasing: false,
|
||||
auditSuccessModal: {
|
||||
visible: false,
|
||||
title: '',
|
||||
content: ''
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const { ensureRuntimeThenGate } = require('../../utils/miniprogramAuditGate.js')
|
||||
ensureRuntimeThenGate(() => {
|
||||
this._fallbackChatScrollHeight()
|
||||
const app = getApp()
|
||||
const boot = () => {
|
||||
if (isAuditHideAiMode(app && app.globalData)) {
|
||||
redirectIfMiniprogramAudit('功能升级中')
|
||||
return
|
||||
}
|
||||
const cid = parseInt(options && options.cid, 10)
|
||||
if (cid > 0) {
|
||||
this.setData({ conversationId: cid })
|
||||
@@ -97,7 +161,12 @@ Page({
|
||||
} else {
|
||||
pullRuntime()
|
||||
}
|
||||
})
|
||||
}
|
||||
if (app && typeof app.getRuntimeConfig === 'function') {
|
||||
app.getRuntimeConfig().then(boot).catch(boot)
|
||||
} else {
|
||||
boot()
|
||||
}
|
||||
},
|
||||
|
||||
/** 分享链接里带 inviterId 时,绑定分销关系(复用现有分销系统) */
|
||||
@@ -143,20 +212,21 @@ Page({
|
||||
tryUrl('/api/ai/my-report/latest', () => tryUrl('/api/ai/report/my-latest'))
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this._measureChatScrollHeight()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
const { redirectIfMiniprogramAudit } = require('../../utils/miniprogramAuditGate.js')
|
||||
if (redirectIfMiniprogramAudit()) return
|
||||
try {
|
||||
const tb = typeof this.getTabBar === 'function' ? this.getTabBar() : null
|
||||
if (tb && typeof tb.updateSelected === 'function') {
|
||||
tb.updateSelected()
|
||||
} else {
|
||||
const c = typeof this.selectComponent === 'function' ? this.selectComponent('#custom-tab-bar') : null
|
||||
if (c && typeof c.refreshFromConfig === 'function') c.refreshFromConfig()
|
||||
else if (c && typeof c.updateSelected === 'function') c.updateSelected()
|
||||
}
|
||||
} catch (e) {}
|
||||
const app = getApp()
|
||||
if (isAuditHideAiMode(app && app.globalData)) {
|
||||
redirectIfMiniprogramAudit('功能升级中')
|
||||
return
|
||||
}
|
||||
if (this.data.auditPurchaseMode) {
|
||||
this.setData({ auditPurchaseMode: false })
|
||||
}
|
||||
const tb = typeof this.getTabBar === 'function' ? this.getTabBar() : null
|
||||
if (tb && typeof tb.updateSelected === 'function') tb.updateSelected()
|
||||
try {
|
||||
const st = wx.getStorageSync('token')
|
||||
if (st && app && app.globalData && !app.globalData.token) {
|
||||
@@ -166,10 +236,21 @@ Page({
|
||||
this.loadQuickQuestions()
|
||||
this.loadMyReportStatus()
|
||||
this._refreshReportCta()
|
||||
if (typeof wx.onWindowResize === 'function' && !this._chatResizeListening) {
|
||||
this._winResizeHandler = () => this._scheduleMeasureChatScroll()
|
||||
wx.onWindowResize(this._winResizeHandler)
|
||||
this._chatResizeListening = true
|
||||
}
|
||||
this._scheduleMeasureChatScroll()
|
||||
},
|
||||
|
||||
/** 离开对话页时若报告条正在展示,记为已曝光,避免反复出现 */
|
||||
onHide() {
|
||||
if (this._chatResizeListening && this._winResizeHandler && typeof wx.offWindowResize === 'function') {
|
||||
wx.offWindowResize(this._winResizeHandler)
|
||||
this._chatResizeListening = false
|
||||
this._winResizeHandler = null
|
||||
}
|
||||
if (this.data.showReportCta) {
|
||||
this._dismissReportCtaPermanently()
|
||||
}
|
||||
@@ -179,6 +260,246 @@ Page({
|
||||
this.setData({ draft: e.detail.value })
|
||||
},
|
||||
|
||||
onInputFocus() {
|
||||
this._scheduleMeasureChatScroll()
|
||||
},
|
||||
|
||||
onAuditPurchaseSwitchTab(e) {
|
||||
const tab = (e.currentTarget.dataset.tab || '') === 'enterprise' ? 'enterprise' : 'personal'
|
||||
this.setData({ auditPurchaseTab: tab })
|
||||
},
|
||||
|
||||
_enterAuditPurchaseMode(options) {
|
||||
const tab = options && options.tab === 'enterprise' ? 'enterprise' : this.data.auditPurchaseTab
|
||||
const app = getApp()
|
||||
const ep = (app && app.globalData && app.globalData.enterprisePermissions) || null
|
||||
this.setData({
|
||||
auditPurchaseMode: true,
|
||||
auditPurchaseTab: tab,
|
||||
auditDeepHeroDesc: buildDeepHeroDesc(ep),
|
||||
auditHasPhone: hasPhone()
|
||||
})
|
||||
this._loadAuditPurchasePricing()
|
||||
},
|
||||
|
||||
_loadAuditPurchasePricing() {
|
||||
this.setData({ auditPurchaseLoading: true, auditPurchaseError: '' })
|
||||
Promise.all([this._requestAuditDeepPricing('personal'), this._requestAuditDeepPricing('enterprise')])
|
||||
.then(([personal, enterprise]) => {
|
||||
const pErr = personal && personal.__error
|
||||
const eErr = enterprise && enterprise.__error
|
||||
const pList = Array.isArray(personal) ? personal : []
|
||||
const eList = Array.isArray(enterprise) ? enterprise : []
|
||||
const app = getApp()
|
||||
const ep = (app && app.globalData && app.globalData.enterprisePermissions) || null
|
||||
this.setData({
|
||||
auditPurchasePersonal: filterCategories(pList, ep),
|
||||
auditPurchaseEnterprise: filterCategories(eList, ep),
|
||||
auditDeepHeroDesc: buildDeepHeroDesc(ep),
|
||||
auditPurchaseLoading: false,
|
||||
auditPurchaseError: pErr && eErr
|
||||
? (personal.__errorMsg || enterprise.__errorMsg || '加载失败,请稍后重试')
|
||||
: ((!pList.length && !eList.length) ? '暂无可展示的套餐' : '')
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.setData({
|
||||
auditPurchaseLoading: false,
|
||||
auditPurchaseError: '加载失败,请稍后重试'
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
_requestAuditDeepPricing(scope, triedRuntime) {
|
||||
const app = getApp()
|
||||
const apiBase = (((app && app.globalData && app.globalData.apiBase) || '') + '').replace(/\/$/, '')
|
||||
if (!apiBase) {
|
||||
if (!triedRuntime && app && typeof app.getRuntimeConfig === 'function') {
|
||||
return app
|
||||
.getRuntimeConfig()
|
||||
.catch(() => null)
|
||||
.then(() => this._requestAuditDeepPricing(scope, true))
|
||||
}
|
||||
return Promise.resolve({ __error: true, __errorMsg: '服务地址未配置' })
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
wx.request({
|
||||
url: `${apiBase}/api/config/deep-pricing`,
|
||||
method: 'GET',
|
||||
data: { scope },
|
||||
timeout: 25000,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 200 && Array.isArray(res.data.data && res.data.data.categories)) {
|
||||
const merged = mergeDeepPricingFromRuntime(scope, res.data.data.categories)
|
||||
resolve(merged)
|
||||
} else {
|
||||
const emb = embeddedDeepCategories(scope)
|
||||
if (emb && emb.length) {
|
||||
resolve(emb.slice())
|
||||
return
|
||||
}
|
||||
resolve({
|
||||
__error: true,
|
||||
__errorMsg: (res && res.data && res.data.message) || '响应异常'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
const emb = embeddedDeepCategories(scope)
|
||||
if (emb && emb.length) {
|
||||
resolve(emb.slice())
|
||||
return
|
||||
}
|
||||
resolve({
|
||||
__error: true,
|
||||
__errorMsg: (err && err.errMsg) || '网络异常'
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
handleAuditPurchaseTap(e) {
|
||||
const tab = e.currentTarget.dataset.tab
|
||||
const index = e.currentTarget.dataset.index
|
||||
this._handleAuditPurchase(tab, index)
|
||||
},
|
||||
|
||||
onGetPhoneNumberForAuditPurchase(e) {
|
||||
const tab = e.currentTarget.dataset.tab
|
||||
const index = e.currentTarget.dataset.index
|
||||
const { code, errMsg } = e.detail || {}
|
||||
if (errMsg && errMsg.indexOf('getPhoneNumber:fail') === 0) {
|
||||
if (!hasPhone()) {
|
||||
wx.showToast({ title: '需要授权手机号才能继续', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this._handleAuditPurchase(tab, index)
|
||||
return
|
||||
}
|
||||
if (!code) {
|
||||
if (hasPhone()) {
|
||||
this._handleAuditPurchase(tab, index)
|
||||
} else {
|
||||
wx.showToast({ title: '获取手机号失败', icon: 'none' })
|
||||
}
|
||||
return
|
||||
}
|
||||
bindPhoneByCode(code)
|
||||
.then(() => {
|
||||
this.setData({ auditHasPhone: true })
|
||||
this._handleAuditPurchase(tab, index)
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
|
||||
_handleAuditPurchase(tab, index) {
|
||||
if (index === undefined || index === null) return
|
||||
const isEnterprise = tab === 'enterprise'
|
||||
const list = isEnterprise ? this.data.auditPurchaseEnterprise : this.data.auditPurchasePersonal
|
||||
const category = list[index]
|
||||
if (!category) return
|
||||
this.setData({ auditPurchaseTab: isEnterprise ? 'enterprise' : 'personal' })
|
||||
if (category.actionType === 'buy' && category.productKey) {
|
||||
this._purchaseAuditPersonal(category)
|
||||
} else {
|
||||
this._applyAuditConsult(category)
|
||||
}
|
||||
},
|
||||
|
||||
_purchaseAuditPersonal(category) {
|
||||
if (this.data.auditPurchasing) return
|
||||
this.setData({ auditPurchasing: true })
|
||||
wx.showLoading({ title: '处理中...', mask: true })
|
||||
const deepProductId = category.id || category.productKey || ''
|
||||
const title = category.title || '个人深度服务(1v1深度解读)'
|
||||
payment.purchasePersonalDeepService({
|
||||
deepProductId,
|
||||
description: title,
|
||||
success: () => {
|
||||
wx.hideLoading()
|
||||
this.setData({ auditPurchasing: false })
|
||||
this._reportAuditCrmLead(category, 'buy')
|
||||
const successMsg = (category.successMessage || '购买成功!我们的顾问会尽快与您联系,为您提供专属深度解读服务。').trim()
|
||||
this._showAuditSuccessModal('购买成功', successMsg)
|
||||
},
|
||||
fail: () => {
|
||||
wx.hideLoading()
|
||||
this.setData({ auditPurchasing: false })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
_applyAuditConsult(category) {
|
||||
const successMsg = (category.successMessage || '感谢您的申请,我们的顾问会尽快与您联系!').trim()
|
||||
wx.showLoading({ title: '提交中...', mask: true })
|
||||
this._reportAuditCrmLead(category, 'consult', () => {
|
||||
wx.hideLoading()
|
||||
this._showAuditSuccessModal('申请成功', successMsg)
|
||||
})
|
||||
},
|
||||
|
||||
_showAuditSuccessModal(title, content) {
|
||||
this.setData({
|
||||
auditSuccessModal: {
|
||||
visible: true,
|
||||
title: title || '成功',
|
||||
content: content || ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
catchTapAuditSuccess() {},
|
||||
|
||||
closeAuditSuccessModal() {
|
||||
this.setData({ 'auditSuccessModal.visible': false })
|
||||
},
|
||||
|
||||
_reportAuditCrmLead(category, actionType, onDone) {
|
||||
const app = getApp()
|
||||
const apiBase = (((app && app.globalData && app.globalData.apiBase) || '') + '').replace(/\/$/, '')
|
||||
const apiKey = (category.consultWechat || '').trim()
|
||||
const isConsult = actionType === 'consult'
|
||||
const deepConsult = isConsult
|
||||
const finish = () => {
|
||||
if (typeof onDone === 'function') onDone()
|
||||
}
|
||||
if (!apiBase) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
if (!deepConsult && !apiKey) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
const isEnterprise = this.data.auditPurchaseTab === 'enterprise'
|
||||
const source = (isEnterprise ? '企业深度服务' : '个人深度服务') + (category.title ? `-${category.title}` : '')
|
||||
const remark =
|
||||
actionType === 'buy'
|
||||
? '完成付款'
|
||||
: isEnterprise
|
||||
? '申请咨询并降低30%成本'
|
||||
: '申请咨询'
|
||||
wx.request({
|
||||
url: `${apiBase}/api/crm/report`,
|
||||
method: 'POST',
|
||||
header: {
|
||||
Authorization: `Bearer ${wx.getStorageSync('token') || ''}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: {
|
||||
apiKey,
|
||||
source,
|
||||
remark,
|
||||
siteTags: category.title || '',
|
||||
deepConsult: deepConsult ? true : false
|
||||
},
|
||||
success() {},
|
||||
fail() {},
|
||||
complete: finish
|
||||
})
|
||||
},
|
||||
|
||||
// ---------- 数据加载 ----------
|
||||
|
||||
loadArticles() {
|
||||
@@ -368,24 +689,32 @@ Page({
|
||||
const apply = (body) => {
|
||||
if (!body || body.code !== 200) return false
|
||||
const d = body.data || {}
|
||||
this.setData({
|
||||
mbtiType: d.mbtiType || '',
|
||||
nickname: d.nickname || '',
|
||||
quickQuestions: this.buildQuickQuestions(d.questions || [])
|
||||
})
|
||||
this.setData(
|
||||
{
|
||||
mbtiType: d.mbtiType || '',
|
||||
nickname: d.nickname || '',
|
||||
quickQuestions: this.buildQuickQuestions(d.questions || [])
|
||||
},
|
||||
() => this._scheduleMeasureChatScroll()
|
||||
)
|
||||
return true
|
||||
}
|
||||
const applyDirect = (d) => {
|
||||
if (!d || !Array.isArray(d.questions) || d.questions.length === 0) return false
|
||||
this.setData({
|
||||
mbtiType: d.mbtiType || '',
|
||||
nickname: d.nickname || '',
|
||||
quickQuestions: this.buildQuickQuestions(d.questions)
|
||||
})
|
||||
this.setData(
|
||||
{
|
||||
mbtiType: d.mbtiType || '',
|
||||
nickname: d.nickname || '',
|
||||
quickQuestions: this.buildQuickQuestions(d.questions)
|
||||
},
|
||||
() => this._scheduleMeasureChatScroll()
|
||||
)
|
||||
return true
|
||||
}
|
||||
const fallbackLocal = () => {
|
||||
this.setData({ quickQuestions: this.buildQuickQuestions([]) })
|
||||
this.setData({ quickQuestions: this.buildQuickQuestions([]) }, () =>
|
||||
this._scheduleMeasureChatScroll()
|
||||
)
|
||||
}
|
||||
const tryLegacy = () => {
|
||||
request({
|
||||
@@ -468,6 +797,8 @@ Page({
|
||||
const msgs = (d.messages || []).filter(m => m.role !== 'system')
|
||||
this.setData({ messages: msgs }, () => {
|
||||
this.scrollToBottom()
|
||||
setTimeout(() => this.scrollToBottom(), 120)
|
||||
this._scheduleMeasureChatScroll()
|
||||
this._refreshReportCta()
|
||||
})
|
||||
}
|
||||
@@ -586,7 +917,7 @@ Page({
|
||||
sending: true
|
||||
},
|
||||
() => {
|
||||
this.setData({ scrollTarget: 'msg-typing' })
|
||||
this.scrollToBottom()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -658,6 +989,8 @@ Page({
|
||||
setPayload,
|
||||
() => {
|
||||
this.scrollToBottom()
|
||||
setTimeout(() => this.scrollToBottom(), 120)
|
||||
setTimeout(() => this.scrollToBottom(), 380)
|
||||
this._refreshReportCta()
|
||||
}
|
||||
)
|
||||
@@ -895,14 +1228,77 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
scrollToBottom() {
|
||||
if (this.data.sending) {
|
||||
this.setData({ scrollTarget: 'msg-typing' })
|
||||
return
|
||||
_fallbackChatScrollHeight() {
|
||||
try {
|
||||
const sys = wx.getWindowInfo ? wx.getWindowInfo() : wx.getSystemInfoSync()
|
||||
const wh = sys.windowHeight || 600
|
||||
const sw = sys.screenWidth || 375
|
||||
const rpx2px = (r) => (r / 750) * sw
|
||||
const inset = sys.safeAreaInsets || {}
|
||||
const safeBottom = typeof inset.bottom === 'number' ? inset.bottom : 0
|
||||
// Tab 区 + 固定 composer(含快捷语行)在 rpx 上的经验预留
|
||||
const reserve = rpx2px(168 + 300) + safeBottom
|
||||
const h = Math.max(200, Math.floor(wh - reserve - rpx2px(20)))
|
||||
this.setData({ chatScrollHeightPx: h })
|
||||
} catch (e) {
|
||||
this.setData({ chatScrollHeightPx: 500 })
|
||||
}
|
||||
const msgs = this.data.messages
|
||||
const last = msgs.length ? msgs[msgs.length - 1] : null
|
||||
this.setData({ scrollTarget: last ? ('msg-' + last.id) : 'bottom' })
|
||||
},
|
||||
|
||||
_scheduleMeasureChatScroll() {
|
||||
if (this._chatLayoutTimer) clearTimeout(this._chatLayoutTimer)
|
||||
this._chatLayoutTimer = setTimeout(() => {
|
||||
this._chatLayoutTimer = null
|
||||
this._measureChatScrollHeight()
|
||||
}, 80)
|
||||
},
|
||||
|
||||
_measureChatScrollHeight(retry) {
|
||||
wx.nextTick(() => {
|
||||
wx.createSelectorQuery()
|
||||
.in(this)
|
||||
.select('.chat-scroll')
|
||||
.boundingClientRect()
|
||||
.select('.composer-wrap')
|
||||
.boundingClientRect()
|
||||
.exec((res) => {
|
||||
const scrollRect = res && res[0]
|
||||
const composerRect = res && res[1]
|
||||
if (!scrollRect || !composerRect || typeof composerRect.top !== 'number') {
|
||||
this._fallbackChatScrollHeight()
|
||||
return
|
||||
}
|
||||
const gapPx = 6
|
||||
const h = composerRect.top - scrollRect.top - gapPx
|
||||
if (h > 120) {
|
||||
this.setData({ chatScrollHeightPx: Math.round(h) }, () => {
|
||||
this.scrollToBottom()
|
||||
})
|
||||
} else if (!retry) {
|
||||
setTimeout(() => this._measureChatScrollHeight(true), 200)
|
||||
} else {
|
||||
this._fallbackChatScrollHeight()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 滚到底部(对齐微信聊天:新发/收信后总能看到最新消息)。
|
||||
* scroll-view 的 scroll-into-view 在连续设为同一 id 时不会触发,需先清空再设;
|
||||
* 锚点统一用 #bottom(位于打字条、报告条之后),避免滚到最后一条气泡 id 仍被底部输入区挡住。
|
||||
*/
|
||||
scrollToBottom() {
|
||||
const go = () => {
|
||||
this.setData({ scrollTarget: 'bottom' })
|
||||
}
|
||||
this.setData({ scrollTarget: '' }, () => {
|
||||
if (typeof wx !== 'undefined' && typeof wx.nextTick === 'function') {
|
||||
wx.nextTick(go)
|
||||
} else {
|
||||
setTimeout(go, 20)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 对话内上传简历:直传 OSS 后走 /api/ai/chat,由神仙 AI 结合测评与正文回复(不跳转匹配工作页) */
|
||||
|
||||
Reference in New Issue
Block a user