From 1cda06573e6eeda0b9ba070baeef3176bb6c6087 Mon Sep 17 00:00:00 2001 From: jinhhanhan <1683105490@qq.com> Date: Mon, 3 Aug 2026 16:24:18 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E6=97=A7?= =?UTF-8?q?=E7=9A=84=E9=82=80=E8=AF=B7=E7=A0=81=E6=9D=A5=E6=BA=90=E8=BF=BD?= =?UTF-8?q?=E8=B8=AA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 删除App.vue中全局邀请码持久化逻辑与相关工具函数 2. 简化http请求的方法与请求头处理逻辑 3. 移除diy-form组件内的邀请码暂存、提交与展示逻辑 4. 清理不再使用的工具类邀请码辅助函数 --- App.vue | 67 ------------------- common/js/http.js | 61 ++++-------------- common/js/util.js | 122 ----------------------------------- components-diy/diy-form.vue | 125 +++--------------------------------- 4 files changed, 21 insertions(+), 354 deletions(-) diff --git a/App.vue b/App.vue index 02ccbf1..740e5f5 100644 --- a/App.vue +++ b/App.vue @@ -90,43 +90,6 @@ return fallback } - // ============================================================ - // ✅ 推荐码 / 邀请码(来源追踪)全局兜底 - // 规则:只写非空值,不主动清空(diy-form 提交成功后自己清) - // ============================================================ - const FORM_INVITE_KEY = 'form_invite_pending_code' - function _persistInviteCodeFromOptions(options) { - try { - var code = '' - // 1) onLaunch/onShow options.query:优先 recommend_code,次选 invite(历史兼容) - if (options && options.query) { - if (options.query.recommend_code) { - code = String(options.query.recommend_code) - } else if (options.query.invite) { - code = String(options.query.invite) - } - } - // 2) H5 兜底:从当前 location.search 取(同样优先 recommend_code) - if (!code) { - // #ifdef H5 - try { - var sp = (new URL(window.location.href)).searchParams - var s1 = sp.get('recommend_code') - var s2 = sp.get('invite') - if (s1) code = String(s1) - else if (s2) code = String(s2) - } catch (e) {} - // #endif - } - // 3) 只写非空,不擦除已有暂存 - if (code) { - uni.setStorageSync(FORM_INVITE_KEY, code) - } - } catch (e) { - // 极端失败静默,diy-form 仍有 P2/P3 兜底读取 - } - } - // ============================================================ // 主题/语言启动 // ============================================================ @@ -545,36 +508,6 @@ // #ifdef MP-ALIPAY if (options.query && options.query.m) uni.setStorageSync('source_member', options.query.m); // #endif - - // ✅ 邀请码(来源追踪):onLaunch 冷启动兜底解析 URL query → storage - _persistInviteCodeFromOptions(options); - }, - onShow: function(options) { - // ✅ 邀请码(来源追踪):onShow 热启动/后台切回 兜底解析 - // 注:onShow 的 options 仅部分平台(MP-WEIXIN, MP-HARMONY, MP-ALIPAY 等)提供 query - // 若 options 为空,则 helper 内部自动 fallback 到 H5 location.search 或已有 storage - _persistInviteCodeFromOptions(options || {}); - - // #ifdef MP - // 小程序端:每次回到前台 → 自动授权登录刷新会话 token + memberInfo(与行业模板一致) - if (typeof this.getAuthInfo === 'function') { - this.getAuthInfo(); - } - if (this.$store && this.$store.state && this.$store.state.token && this.$api) { - this.$api.sendRequest({ - url: '/api/member/info', - success: (res) => { - if (res && res.code >= 0 && res.data && this.$store && this.$store.commit) { - this.$store.commit('setMemberInfo', res.data); - } - } - }); - } - // #endif - - // #ifdef MP-ALIPAY - if (options && options.query && options.query.m) uni.setStorageSync('source_member', options.query.m); - // #endif }, onHide: function() {}, methods: { diff --git a/common/js/http.js b/common/js/http.js index ab32bb5..84f37f3 100644 --- a/common/js/http.js +++ b/common/js/http.js @@ -173,19 +173,8 @@ export default { _sendRequestNow(params) { - // ✅ 关键修复 1:支持调用方指定 params.method(GET/POST/PUT/DELETE); - // 没指定时按老规则:有 data → POST,没 data → GET - var method; - if (params.method && typeof params.method === 'string') { - method = String(params.method).toUpperCase(); - if (['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'].indexOf(method) === -1) { - method = 'GET'; - } - } else { - method = params.data != undefined ? 'POST' : 'GET'; - } - - var url = (Config.baseUrl + params.url).replace(/(? { + fail: () => { console.error('[HTTP][FAIL] uploadBase64', JSON.stringify(res)); typeof params.fail == 'function' && params.fail(res); } @@ -277,9 +252,8 @@ export default { uni.request({ url: Config.baseUrl + '/api/upload/headimgPull', method: 'POST', - // ✅ 修复:application/json header: { - 'content-type': 'application/json' + 'content-type': 'application/x-www-form-urlencoded;application/json' }, data: { app_type: appType, @@ -292,7 +266,7 @@ export default { console.log('[HTTP][RESPONSE] pullImage', 'statusCode=', res.statusCode, JSON.stringify(res.data)); typeof params.success == 'function' && params.success(res.data); }, - fail: (res) => { + fail: () => { console.error('[HTTP][FAIL] pullImage', JSON.stringify(res)); typeof params.fail == 'function' && params.fail(res); } @@ -300,17 +274,6 @@ export default { }, upload(params) { console.log('[HTTP][REQUEST] upload', Config.baseUrl + params.url, 'name=', params.name || 'file'); - // ✅ 关键修复:uni.uploadFile **绝对不能手动设置 Content-Type**! - // 因为 uni.uploadFile 会自动设置成 multipart/form-data; boundary=----xxxxxx - // 手动设置会覆盖掉 boundary,后端无法解析分段上传,导致文件/表单字段解析为空 - var uploadHeader = {}; - // 只合并调用方传的自定义 header,不默认加 content-type - if (params.header && typeof params.header === 'object') { - Object.assign(uploadHeader, params.header); - // 兜底:如果调用方误传了 content-type,就删掉,让 uni 自动设置 multipart/form-data - delete uploadHeader['content-type']; - delete uploadHeader['Content-Type']; - } uni.uploadFile({ url: Config.baseUrl + params.url, filePath: params.filePath, @@ -319,10 +282,10 @@ export default { formData: { app_type: appType, app_type_name: appTypeName, - token: store.state.token || '', - uniacid: uni.getStorageSync('uniacid') || Config.uniacid }, - header: uploadHeader, + header: { + 'content-type': 'application/x-www-form-urlencoded;application/json' + }, success: (res) => { console.log('[HTTP][RESPONSE] upload', Config.baseUrl + params.url, 'statusCode=', res.statusCode, res.data); typeof params.success == 'function' && params.success(JSON.parse(res.data)); diff --git a/common/js/util.js b/common/js/util.js index 535d835..fee16a6 100644 --- a/common/js/util.js +++ b/common/js/util.js @@ -76,113 +76,6 @@ function _getCurrentUniacid() { return _pickCurrentUniacid(storageVal, configVal); } - - -/** - * 从当前登录态读取 memberInfo → 返回后端接口真实生成的推荐码 - * 读取优先级:storage.memberInfo.recommend_code > Vuex store.state.memberInfo.recommend_code - * 没登录 / 未同意授权 / 后端还没返回码 → 返回 '' - * @returns {string} - */ -function _getCurrentRecommendCode() { - // P1: storage 中 memberInfo.recommend_code(DIY 推荐码组件 _persistRecommendInfo 已写入) - try { - var info = null; - if (typeof uni !== 'undefined' && uni && typeof uni.getStorageSync === 'function') { - info = uni.getStorageSync('memberInfo'); - } - if (info && typeof info === 'object') { - if (info.recommend_code && String(info.recommend_code).trim()) { - return String(info.recommend_code).trim(); - } - // 兼容旧字段 invite_code(商家之前保存的配置) - if (info.invite_code && String(info.invite_code).trim()) { - return String(info.invite_code).trim(); - } - } - } catch (e) {} - - // P2: Vuex store.state.memberInfo(某些平台 storage 还没落地时,内存里可能已经好了) - try { - if (store && store.state && store.state.memberInfo && typeof store.state.memberInfo === 'object') { - var s = store.state.memberInfo; - if (s.recommend_code && String(s.recommend_code).trim()) { - return String(s.recommend_code).trim(); - } - if (s.invite_code && String(s.invite_code).trim()) { - return String(s.invite_code).trim(); - } - } - } catch (e) {} - - return ''; -} - -/** - * 从 URL 中移除指定 query 参数(防止 URL 里已存在旧的 recommend_code=xxx 时,append 之后重复) - * - 仅移除 key,不影响其他参数 - * - 同时处理 hash(xxx#yyy) 在 query 后面的情况(保留 hash 原位置) - * @param {string} url - * @param {string} key - * @returns {string} - */ -function _removeQueryParam(url, key) { - if (!url || !key) return url || ''; - // 先拆 hash - var hashIdx = url.indexOf('#'); - var hash = ''; - var main = url; - if (hashIdx !== -1) { - hash = url.substring(hashIdx); - main = url.substring(0, hashIdx); - } - var qIdx = main.indexOf('?'); - if (qIdx === -1) return url; // 没 query - var path = main.substring(0, qIdx); - var qs = main.substring(qIdx + 1); - if (!qs) return url; - var pairs = qs.split('&').filter(function(p) { - if (!p) return false; - var eq = p.indexOf('='); - var k = (eq === -1) ? p : p.substring(0, eq); - // 大小写不敏感(URL 参数通常大小写不敏感) - return String(k).toLowerCase() !== String(key).toLowerCase(); - }); - var newMain = pairs.length ? (path + '?' + pairs.join('&')) : path; - return newMain + hash; -} - -/** - * 给 URL 追加当前登录会员的推荐码(方案 B 标准函数,推荐使用) - * 规则: - * - 当前登录且能拿到后端返回的 recommend_code → 移除 URL 里已存在的旧值 - * → 同时追加 recommend_code + invite 两个 key(双参数兼容,后端收哪个都能用) - * - 未登录 / 推荐码为空 → 原样返回 - * - 兼容内部相对路径 '/pages/xxx?x=1' 与 外部绝对 URL 'https://host/xx#yyy' - * @param {string} url - * @returns {string} - */ -function _appendRecommendCodeIfLoggedIn(url) { - if (!url) return ''; - var code = _getCurrentRecommendCode(); - if (!code) return url; // 没登录或没码:不动 - // 1) 先清 URL 里已存在的旧参数(两个 key 都清,避免重复追加或旧码覆盖新码) - var cleaned = _removeQueryParam(url, 'recommend_code'); - cleaned = _removeQueryParam(cleaned, 'invite'); - // 2) 双 key 同时追加:标准名 recommend_code + 历史兼容名 invite - // 这样不管后端 parseCode 接口收哪个 key,都能正确命中 - return _appendQueryParamsBase(cleaned, { recommend_code: code, invite: code }); -} - -/** - * 旧版函数别名(保持对 _appendInviteCodeIfLoggedIn 的调用兼容) - * 之前业务代码调用 _appendInviteCodeIfLoggedIn 的,现在全部委托给新的 _appendRecommendCodeIfLoggedIn - * 确保:一次改动,全链路统一使用后端真实码 + 双 key 兼容 - */ -function _appendInviteCodeIfLoggedIn(url) { - return _appendRecommendCodeIfLoggedIn(url); -} - /** * 把 URL 上的 page_params 还原成对象。是 _toParamValueString 的逆操作。 * 容错策略:任何解析失败都安全降级到 defaultValue,不抛异常。 @@ -1362,11 +1255,6 @@ export default { let url = this.normalizeUrl(decodeEntity(link.wap_url)); url = this.appendQueryParams(url, params); - // ✅ 外链跳转自动拼邀请码:当前登录有邀请码 → 去 URL 旧 invite → 追加成新值 - // H5: window.location.href 新页面打开时 location.search 能收到 - // MP: webview src URL 打开目标 H5 页面时能收到 - url = _appendInviteCodeIfLoggedIn(url); - // #ifdef H5 // console.log(`url ---->`, url, link) window.location.href = url; @@ -1479,16 +1367,6 @@ export default { // resolveLinkUrl 已把 wap_url 中的 query 合并到 link.page_params // 这里过滤掉 link 元数据字段, 避免 wap_url/name/parent/title 等被错误附加到目标 URL // title/enable_set_title/page_params 仍需透传到目标 URL,page_params 放最后 - - // ✅ 内部页:把推荐码写进 page_params(最终会被拼成 URL query,diy-form onLoad options 能收到) - // 仅在当前登录已拿到推荐码时追加,不覆盖上游已有值;双写 recommend_code+ invite(历史兼容) - var _curRecommendCode = _getCurrentRecommendCode(); - if (_curRecommendCode) { - if (!link.page_params || typeof link.page_params !== 'object') link.page_params = {}; - if (!link.page_params.recommend_code) link.page_params.recommend_code = _curRecommendCode; - if (!link.page_params.invite) link.page_params.invite = _curRecommendCode; - } - const { wap_url, name, parent, type, appid, page, mobile, image, ...navParams } = link; this.redirectTo(link.wap_url, { ...navParams, diff --git a/components-diy/diy-form.vue b/components-diy/diy-form.vue index 7f639ae..985ba59 100644 --- a/components-diy/diy-form.vue +++ b/components-diy/diy-form.vue @@ -19,12 +19,6 @@ :formConfig="mergedFormConfig" ref="nsNewform" > - - - - 本次提交推荐邀请码:{{ _inviteCodePending }} - - @@ -63,15 +57,11 @@ export default { mixins: [DiyMinx], data() { return { - formComponents: [], // 转换后的表单组件 - // ✅ 表单提交邀请码(来源追踪):3 级读取 → 提交自动注入 → 成功清暂存 - _inviteCodePending: '' + formComponents: [] // 转换后的表单组件 }; }, created() { this.initForm(); - // ✅ 初始化:从 URL query / storage / H5 location 读取邀请码暂存 - this._initInviteCode(); }, watch: { 'value': { @@ -306,48 +296,22 @@ export default { // 若父级监听了 @submit,则把提交控制权交给父级(用于父级有自定义提交字段/反馈逻辑的页面,如 formdata.vue 传 uniacid) if (this.$listeners && this.$listeners.submit) { - // ✅ 父级自定义流程:把 recommend_code + invite_code 都透传给父级,双保险(formData 数组里 + 第二参数对象里) - var formDataArr = this.getFormData(); - try { - if (this._inviteCodePending && Array.isArray(formDataArr)) { - formDataArr = formDataArr.slice(); - formDataArr.push({ - controller: 'hidden', - name: '_recommend_code', - value: this._inviteCodePending, - val: this._inviteCodePending - }); - formDataArr.push({ - controller: 'hidden', - name: '_invite_code', - value: this._inviteCodePending, - val: this._inviteCodePending - }); - } - } catch (e) {} - this.$emit('submit', formDataArr, { - recommend_code: this._inviteCodePending, - invite_code: this._inviteCodePending - }); + this.$emit('submit', this.getFormData()); return; } // 获得保单数据 const submitFormData = this.getFormData(); - var reqData = { - form_id: this.value.form_id || 0, - form_data: typeof submitFormData === 'string' ? submitFormData : JSON.stringify(submitFormData), - component_id: this.value.id || '', - form_name: this.value.form_name || '默认表单' - }; - if (this._inviteCodePending) { - reqData.recommend_code = this._inviteCodePending; // 方案 B 标准 - reqData.invite_code = this._inviteCodePending; // 历史兼容 - } + // 提交到后端 this.$api.sendRequest({ url: '/form/api/form/create', - data: reqData, + data: { + form_id: this.value.form_id || 0, + form_data: typeof submitFormData === 'string' ? submitFormData : JSON.stringify(submitFormData), + component_id: this.value.id || '', + form_name: this.value.form_name || '默认表单' + }, success: function(res) { if (res.code === 0) { uni.showToast({ @@ -358,8 +322,6 @@ export default { if (self.$refs.nsNewform && self.$refs.nsNewform.setFormData) { self.$refs.nsNewform.setFormData(); } - // ✅ 提交成功后清暂存邀请码(防止后续重复提交归属错人) - self._clearInviteCode(); } else { uni.showToast({ title: res.message || self.$lang('submitFail'), @@ -368,61 +330,6 @@ export default { } } }); - }, - - /* ============================================================ - ✅ 邀请码(来源追踪)辅助方法 - 链路:分享链接 ?invite=XXX → 进入本页 3 级取码 → 暂存 data + storage - → 提交时自动拼入 request → 成功清暂存 - ============================================================ */ - FORM_INVITE_STORAGE_KEY: 'form_invite_pending_code', - _initInviteCode() { - var key = this.FORM_INVITE_STORAGE_KEY; - var code = ''; - // P1:当前页面的 onLoad options(通过 getCurrentPages 顶页的 options 拿到) - try { - var pages = (typeof getCurrentPages === 'function') ? getCurrentPages() : []; - if (pages && pages.length) { - var top = pages[pages.length - 1]; - if (top && top.options) { - if (top.options.recommend_code) { - code = String(top.options.recommend_code); - } else if (top.options.invite) { - code = String(top.options.invite); - } - } - } - } catch (e) {} - - // P2:storage(App.vue onLaunch/onShow 提前写入,全局兜底) - if (!code) { - try { code = uni.getStorageSync(key) || ''; } catch (e) { code = ''; } - } - - // P3:H5 冷启动从 location.search 兜底(同样优先 recommend_code) - if (!code) { - // #ifdef H5 - try { - var sp = (new URL(window.location.href)).searchParams; - var s1 = sp.get('recommend_code'); - var s2 = sp.get('invite'); - if (s1) code = String(s1); - else if (s2) code = String(s2); - } catch (e) {} - // #endif - } - - // 写回 data + storage 持久化(跨页面跳转也不丢) - if (code) { - this._inviteCodePending = code; - try { uni.setStorageSync(key, code); } catch (e) {} - } - }, - - // 清邀请码(提交成功后调用,避免串号) - _clearInviteCode() { - this._inviteCodePending = ''; - try { uni.removeStorageSync(this.FORM_INVITE_STORAGE_KEY); } catch (e) {} } } }; @@ -434,20 +341,6 @@ export default { padding: 0; border-radius: 10rpx; - /* ✅ 邀请码归属提示:灰色小字,零感知(只有通过邀请链接才显示) */ - .form-invite-attribution { - width: 100%; - text-align: center; - box-sizing: border-box; - padding: 20rpx 30rpx 10rpx; - .attribution-text { - font-size: 22rpx; - color: #bbbbbb; - line-height: 1.5; - letter-spacing: 0.5rpx; - } - } - .form-banner { width: 100%; overflow: hidden;