diff --git a/common/js/recommend-diy-data.js b/common/js/recommend-diy-data.js new file mode 100644 index 0000000..67e1dbe --- /dev/null +++ b/common/js/recommend-diy-data.js @@ -0,0 +1,98 @@ +/** + * 通用 mixin - 推荐系统 diy 组件的内部 ajax 数据获取 + * + * 用法: + * import recommendData from '@/common/js/recommend-diy-data.js'; + * export default { mixins: [recommendData], ... } + * + * 它会: + * 1. 在 mounted 时异步调 /recommend/api/home/homeSummary 一次 + * 2. 暴露 this.recommendInfo 给组件用 + * 3. 处理失败(token 失效、未登录)显示占位 + * + * 为什么不用 vuex / pinia: + * diy 组件可能在多个页面多次实例化,各自独立拉一次,可避免跨页面污染。 + */ +import http from '@/common/js/http.js'; +import { $lang } from '@/common/js/lang.js'; + +export default { + data() { + return { + recommendInfo: null, // 后端原始响应 data + recommendLoading: false, + recommendError: '', + _lastFetchTs: 0, // 防抖:同时多次 mounted 不重复请求 + }; + }, + computed: { + // 余额(可提现 + 冻结 + 累计收益) + balance() { + return (this.recommendInfo && this.recommendInfo.balance) || {}; + }, + // 累计统计 + statistics() { + return (this.recommendInfo && this.recommendInfo.statistics) || {}; + }, + // 推荐码 + code() { + return (this.recommendInfo && this.recommendInfo.code) || {}; + }, + // 考察期 + probation() { + return (this.recommendInfo && this.recommendInfo.probation) || {}; + }, + // 最近推荐 + recentRecords() { + return (this.recommendInfo && this.recommendInfo.recent_records) || []; + }, + // 奖励规则 + rewardRules() { + return (this.recommendInfo && this.recommendInfo.reward_rules) || []; + }, + // 是否登录(可避免显示错误的余额) + hasRecommendData() { + return !!this.recommendInfo; + }, + }, + mounted() { + this.fetchRecommendInfo(); + }, + methods: { + // 异步拉首页聚合数据(去抖,每 10s 最多一次) + fetchRecommendInfo(force = false) { + const now = Date.now(); + if (!force && now - this._lastFetchTs < 10000) { + return; + } + this._lastFetchTs = now; + this.recommendLoading = true; + this.recommendError = ''; + + // 通过 http 通用方法走 token(与全局其他请求一致) + http.get('/recommend/api/home/homeSummary').then(res => { + this.recommendLoading = false; + if (res && res.code === 0 && res.data) { + this.recommendInfo = res.data; + } else { + // 未登录或 token 失败,留 recommendInfo=null 让模板显示占位 + this.recommendError = (res && res.message) || $lang ? $lang('components-diy.diy-recommend.loadFail') : '加载失败'; + } + }).catch(err => { + this.recommendLoading = false; + this.recommendError = (err && (err.errMsg || err.message)) || 'network error'; + }); + }, + // 刷新(用户下拉触发) + refreshRecommendInfo() { + return this.fetchRecommendInfo(true); + }, + // 数字格式化工具 + formatMoney(n) { + if (n == null || n === '') return '0'; + const num = parseFloat(n); + if (isNaN(num)) return '0'; + return num.toLocaleString('zh-CN', { maximumFractionDigits: 2 }); + }, + }, +}; diff --git a/components-diy/diy-recommend-action.vue b/components-diy/diy-recommend-action.vue new file mode 100644 index 0000000..7ac0692 --- /dev/null +++ b/components-diy/diy-recommend-action.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/components-diy/diy-recommend-balance.vue b/components-diy/diy-recommend-balance.vue new file mode 100644 index 0000000..f990e5b --- /dev/null +++ b/components-diy/diy-recommend-balance.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/components-diy/diy-recommend-code.vue b/components-diy/diy-recommend-code.vue new file mode 100644 index 0000000..9d5024c --- /dev/null +++ b/components-diy/diy-recommend-code.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/components-diy/diy-recommend-records.vue b/components-diy/diy-recommend-records.vue new file mode 100644 index 0000000..e91fe07 --- /dev/null +++ b/components-diy/diy-recommend-records.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/components-diy/diy-recommend-rules.vue b/components-diy/diy-recommend-rules.vue new file mode 100644 index 0000000..2f76844 --- /dev/null +++ b/components-diy/diy-recommend-rules.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/components-diy/diy-recommend-stat.vue b/components-diy/diy-recommend-stat.vue new file mode 100644 index 0000000..ce254f1 --- /dev/null +++ b/components-diy/diy-recommend-stat.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/lang/en-us/components-diy/diy-recommend.js b/lang/en-us/components-diy/diy-recommend.js new file mode 100644 index 0000000..ccc3beb --- /dev/null +++ b/lang/en-us/components-diy/diy-recommend.js @@ -0,0 +1,38 @@ +/** + * 推荐系统 diy 组件 6 个组件共用 i18n 文案 (English) + * 同 zh-cn 一致,文案翻译为英文 + */ +export const lang = { + 'diy-recommend-code': { + myCode: 'My Referral Code', + copy: 'Copy', + copySuccess: 'Copied', + shareTip: 'Copy the link and share it with enterprises to bind', + }, + 'diy-recommend-balance': { + available: 'Withdrawable', + frozen: 'Frozen', + probationDays: '{days} days remaining', + passedProbation: 'Probation passed', + totalEarned: 'Total earned', + }, + 'diy-recommend-stat': { + totalRecommend: 'Referred', + totalEntered: 'Entered', + totalEarned: 'Earned', + }, + 'diy-recommend-records': { + recent: 'Recent Referrals', + viewAll: 'View all', + empty: 'No referrals yet', + }, + 'diy-recommend-rules': { + title: 'Reward Rules', + }, + 'diy-recommend-action': { + withdrawNow: 'Withdraw Now', + withdrawList: 'Records', + applyEnterprise: 'Apply', + }, + loadFail: 'Failed to load, please retry', +}; diff --git a/lang/zh-cn/components-diy/diy-recommend.js b/lang/zh-cn/components-diy/diy-recommend.js new file mode 100644 index 0000000..10440f0 --- /dev/null +++ b/lang/zh-cn/components-diy/diy-recommend.js @@ -0,0 +1,52 @@ +/** + * 推荐系统 diy 组件 6 个组件共用 i18n 文案 + * 加载路径: pages-tool 中 $lang('components-diy.diy-recommend-code.xxx') + * 模块前缀已经写好,这里只导出组件名下的 key + */ +export const lang = { + // ---- diy-recommend-code ---- + 'diy-recommend-code': { + myCode: '我的推荐码', + copy: '复制', + copySuccess: '已复制', + shareTip: '复制推荐链接分享给企业,对方填表时填入即可绑定', + }, + + // ---- diy-recommend-balance ---- + 'diy-recommend-balance': { + available: '可提现余额', + frozen: '待结算(冻结中)', + probationDays: '考察期 {days} 天', + passedProbation: '已过考察期', + totalEarned: '累计总收益', + }, + + // ---- diy-recommend-stat ---- + 'diy-recommend-stat': { + totalRecommend: '累计推荐', + totalEntered: '已入驻', + totalEarned: '累计收益', + }, + + // ---- diy-recommend-records ---- + 'diy-recommend-records': { + recent: '最近推荐', + viewAll: '查看全部', + empty: '暂无推荐记录', + }, + + // ---- diy-recommend-rules ---- + 'diy-recommend-rules': { + title: '奖励规则', + }, + + // ---- diy-recommend-action ---- + 'diy-recommend-action': { + withdrawNow: '立即提现', + withdrawList: '提现记录', + applyEnterprise: '入驻申请', + }, + + // ---- 通用 ---- + loadFail: '加载失败,请稍后重试', +}; diff --git a/pages.json b/pages.json index 2a33b8f..602e5a7 100644 --- a/pages.json +++ b/pages.json @@ -2422,7 +2422,8 @@ "uni-*(\\W.*)": "@/uni_modules/uni$1/components/uni$1/uni$1.vue", "mescroll-uni": "@/components/mescroll/my-list-mescroll.vue", "ns-goods-sku-*(\\W.*)": "@/components/ns-goods-sku/ns-goods-sku$1.vue", - "diy-*(\\W.*)": "@/components-diy/diy$1.vue" + "diy-*(\\W.*)": "@/components-diy/diy$1.vue", + "diy-recommend-*": "@/components-diy/diy-recommend-$1.vue" } }, "preloadRule": { diff --git a/scripts/verify_diy_recommend.js b/scripts/verify_diy_recommend.js new file mode 100644 index 0000000..d3a6dba --- /dev/null +++ b/scripts/verify_diy_recommend.js @@ -0,0 +1,30 @@ +// Quick parse check for the 6 diy-recommend components +const fs = require('fs'); +const path = require('path'); + +const files = [ + 'diy-recommend-code.vue', + 'diy-recommend-balance.vue', + 'diy-recommend-stat.vue', + 'diy-recommend-records.vue', + 'diy-recommend-rules.vue', + 'diy-recommend-action.vue', +]; + +const base = path.join(__dirname, '..', 'components-diy'); +let ok = 0; +for (const f of files) { + const p = path.join(base, f); + if (!fs.existsSync(p)) { console.log('✗', f, 'MISSING'); continue; } + const content = fs.readFileSync(p, 'utf8'); + // Check has template/script/style + const hasT = /