diff --git a/pages.json b/pages.json index 3d748c1..42dd7ba 100644 --- a/pages.json +++ b/pages.json @@ -1513,7 +1513,14 @@ "navigationBarTitleText": "我的人脉", "enablePullDownRefresh": true } - }, + }, + { + "path": "card/views", + "style": { + "navigationBarTitleText": "谁看了我", + "enablePullDownRefresh": true + } + }, { "path": "card/contact-edit", "style": { diff --git a/pages_tool/card/detail.vue b/pages_tool/card/detail.vue index a43ce3a..405eb29 100644 --- a/pages_tool/card/detail.vue +++ b/pages_tool/card/detail.vue @@ -35,11 +35,11 @@ 编辑资料 - + 名片样式 - + 切换名片 @@ -213,7 +213,9 @@ export default { qrUrl: '', // 交换名片: 待处理请求(真实, exchange/lists, 只含 status=0) exchangeList: [], - exchangeCount: 0 + exchangeCount: 0, + // 商家是否开启「员工端样式自选」(GET design/presets 的 enabled); 默认 true(失败开放) + designEnabled: true }; }, computed: { @@ -345,12 +347,14 @@ export default { this.cardId = decodeURIComponent(options.card_id || ''); this.loadDetail(); this.loadExchange(); + this.loadDesignEnabled(); }, onShow() { // 首次进入由 onLoad 负责加载; 从编辑页返回时(onShow 再次触发)刷新 if (this._loadedOnce) { this.loadDetail(); this.loadExchange(); + this.loadDesignEnabled(); } this._loadedOnce = true; }, @@ -558,6 +562,16 @@ export default { }); // #endif }, + // 查询商家是否开启「员工端样式自选」(控制名片样式入口显隐) + loadDesignEnabled() { + this.$cardApi.cardDesignPresets({ + success: res => { + const d = res.data || {}; + if (res.code == 0) this.designEnabled = d.enabled !== false; + }, + fail: () => {} // 失败开放(默认 true, 入口保留) + }); + }, // 「名片样式」: 进入样式选择页(名片模板 / 分享封面) onStyleTap() { const url = this.cardId diff --git a/pages_tool/card/list.vue b/pages_tool/card/list.vue index 697cc84..c92d4a2 100644 --- a/pages_tool/card/list.vue +++ b/pages_tool/card/list.vue @@ -10,6 +10,7 @@ 新建 认领 + 访客 @@ -20,6 +21,13 @@ + + + 全部 + 待跟进 + 已完成 + + 📱 {{ item.mobile }} + + ✓ 已完成 + + + 跟进中 + {{ item.is_favorite == 1 ? '★' : '☆' }} + 完成 › @@ -47,9 +63,34 @@ — 没有更多了 — - - + + + + ⏳ + 申请审核中 + 您的建卡申请正在等待商家审批 + + + ✅ + 已通过 + 下拉刷新查看我的名片 + + + 📇 + 已拒绝:{{ apply.note || '商家未填写理由' }} + 还没有名片 + 提交信息,申请商家为您建卡 + + + + + {{ applying ? '提交中…' : '提交申请' }} + + + + @@ -96,9 +137,16 @@ export default { showClaim: false, claimInput: '', claimMode: 'code', // code=领取码(主通道), id=名片编号(旧入口兼容) - pendingClaimCode: '' // 扫码带参预填的码 - }; - }, + pendingClaimCode: '', // 扫码带参预填的码 + // 跟进筛选(2.11.3): all / due(待跟进) / done(已完成) + followTab: 'all', + // 建卡申请状态(2.11.15) + apply: null, // { status, status_text, note, has_card } + applyName: '', + applyMobile: '', + applying: false + }; + }, onLoad(options) { // 扫码带参预填(文档 §5): 二维码/链接带 claim_code 或 card_id 直接进入认领 if (options && (options.claim_code || options.card_id)) { @@ -135,6 +183,68 @@ export default { this.onlyFav = !this.onlyFav; this.refresh(); }, + // 跟进筛选切换(全部/待跟进/已完成, 2.11.3) + switchFollow(tab) { + this.followTab = tab; + this.refresh(); + }, + // 标记跟进完成(2.11.3): followDone, 成功后该卡从待跟进消失 + markDone(item) { + const cardId = this.cardIdOf(item); + if (!cardId) return; + uni.showLoading({ title: '标记中…' }); + this.$cardApi.cardFollowDone({ + data: { card_id: cardId }, + success: res => { + uni.hideLoading(); + if (res.code == 0) { + uni.showToast({ title: '已标记完成', icon: 'success' }); + this.refresh(); + } else { + this.handleApiError(res); + } + }, + fail: () => { + uni.hideLoading(); + uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' }); + } + }); + }, + // 空态: 查询我的建卡申请状态(2.11.15) + loadApplyStatus() { + this.$cardApi.applyStatus({ + success: res => { + if (res.code == 0) this.apply = res.data || null; + }, + fail: () => {} + }); + }, + // 提交建卡申请(2.11.15) + submitApply() { + const name = (this.applyName || '').trim(); + const mobile = (this.applyMobile || '').trim(); + if (!name) { uni.showToast({ title: '请输入姓名', icon: 'none' }); return; } + if (!mobile) { uni.showToast({ title: '请输入手机号', icon: 'none' }); return; } + this.applying = true; + this.$cardApi.applySubmit({ + data: { name: name, mobile: mobile }, + success: res => { + this.applying = false; + if (res.code == 0) { + uni.showToast({ title: '已提交,等待审核', icon: 'none' }); + this.apply = { status: 0, status_text: '待审核' }; + this.applyName = ''; + this.applyMobile = ''; + } else { + this.handleApiError(res); + } + }, + fail: () => { + this.applying = false; + uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' }); + } + }); + }, /** * 切换收藏(双向 toggle): 直接调, 用返回的 is_favorite 回填。 * 乐观更新 + 失败回滚; 仅看收藏模式下取消收藏则从列表移除该行。 @@ -187,6 +297,8 @@ export default { keyword: this.keyword.trim() }; if (this.onlyFav) reqData.favorite = 1; + if (this.followTab === 'due') reqData.follow_up = 'due'; + else if (this.followTab === 'done') reqData.follow_up = 'done'; this.$cardApi.cardLists({ data: reqData, success: res => { @@ -197,6 +309,8 @@ export default { this.count = data.count || 0; this.page = pageNo; this.list = pageNo === 1 ? rows : this.list.concat(rows); + // 空态(首页无卡): 拉取建卡申请状态, 驱动空态申请 UI(2.11.15) + if (pageNo === 1 && rows.length === 0) this.loadApplyStatus(); this.emptyText = this.keyword.trim() ? '未找到匹配的名片' : '暂无名片,可点击右上角「新建」或「认领」添加'; @@ -242,6 +356,12 @@ export default { url: '/pages_tool/card/edit' }); }, + // 访客轨迹(2.11.4): 进入「谁看了我」总入口(不带 card_id) + goViews() { + uni.navigateTo({ + url: '/pages_tool/card/views' + }); + }, // 认领: 打开弹层(支持领取码/名片编号 + 扫码带参预填, 与 card_claim_api 文档同步) onClaimTap() { this.claimInput = this.pendingClaimCode || ''; @@ -521,6 +641,85 @@ export default { white-space: nowrap; } +/* 列表行: 跟进徽标 + 完成按钮 */ +.card-follow { + margin-top: 8rpx; + + .card-follow-done { + font-size: 22rpx; + color: #1aad19; + } + + .card-follow-due { + font-size: 22rpx; + color: #f5a623; + } +} + +.card-done-btn { + flex-shrink: 0; + margin-left: 16rpx; + padding: 8rpx 20rpx; + background-color: var(--main-color, #0054a5); + color: #fff; + font-size: 24rpx; + border-radius: 24rpx; +} + +/* 空态: 建卡申请 */ +.card-empty { + text-align: center; + padding: 120rpx 48rpx; + + .empty-icon { + font-size: 96rpx; + } + + .empty-title { + margin-top: 24rpx; + font-size: 32rpx; + font-weight: 600; + color: #333; + } + + .empty-sub { + margin-top: 12rpx; + font-size: 26rpx; + color: #999; + } + + .apply-form { + margin-top: 48rpx; + + .apply-input { + height: 84rpx; + background-color: #fff; + border-radius: 14rpx; + padding: 0 24rpx; + font-size: 28rpx; + color: #333; + margin-bottom: 24rpx; + text-align: left; + } + + .apply-submit { + height: 88rpx; + border-radius: 999rpx; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #2aa5ff 0%, #0d7bf0 100%); + color: #fff; + font-size: 30rpx; + font-weight: 600; + + &.disabled { + opacity: 0.6; + } + } + } +} + /* 认领弹层(与 card_claim_api 文档同步) */ .claim-mask { position: fixed; diff --git a/pages_tool/card/style.vue b/pages_tool/card/style.vue index eef8555..461f060 100644 --- a/pages_tool/card/style.vue +++ b/pages_tool/card/style.vue @@ -1,654 +1,682 @@ diff --git a/tests/card-api.test.js b/tests/card-api.test.js index 3a74a26..74e4485 100644 --- a/tests/card-api.test.js +++ b/tests/card-api.test.js @@ -46,6 +46,8 @@ const DOC_URLS = [ '/card/api/card/recognize', '/card/api/card/retry', '/card/api/card/recognizeBatch', + '/card/api/card/followDone', // 跟进完成态 2.11.3 + '/card/api/card/views', // 访客轨迹 2.11.4 // 人脉库 /card/api/contact/* '/card/api/contact/lists', '/card/api/contact/add', @@ -59,7 +61,11 @@ const DOC_URLS = [ // 交换名片 /card/api/exchange/* '/card/api/exchange/lists', '/card/api/exchange/agree', - '/card/api/exchange/ignore' + '/card/api/exchange/ignore', + '/card/api/apply/submit', // 员工建卡申请 2.11.15 + '/card/api/apply/status', // 员工建卡申请 2.11.15 + '/card/api/design/presets', // 名片预设列表(同源后台 CardTemplatePresets, 后端 v2 无 /card/ 段) + '/card/api/design/save' // 应用预设/保存本卡样式 ]; // helper 方法名 ↔ URL 常量名(收口层必须两端齐全) @@ -82,11 +88,17 @@ const HELPER_URL_PAIRS = [ ['leadSetStatus', 'LEAD_SET_STATUS'], ['exchangeLists', 'EXCHANGE_LISTS'], ['exchangeAgree', 'EXCHANGE_AGREE'], - ['exchangeIgnore', 'EXCHANGE_IGNORE'] + ['exchangeIgnore', 'EXCHANGE_IGNORE'], + ['cardFollowDone', 'CARD_FOLLOW_DONE'], + ['cardViews', 'CARD_VIEWS'], + ['applySubmit', 'APPLY_SUBMIT'], + ['applyStatus', 'APPLY_STATUS'], + ['cardDesignPresets', 'CARD_DESIGN_PRESETS'], + ['cardDesignSave', 'CARD_DESIGN_SAVE'] ]; // 文档 3.3/4.2/4.4/3.7: 嵌套对象/数组入参必须走 JSON body(sendJson) -const JSON_BODY_HELPERS = ['cardUpdate', 'cardCreate', 'contactAdd', 'contactUpdate', 'cardRecognizeBatch', 'exchangeAgree', 'exchangeIgnore']; +const JSON_BODY_HELPERS = ['cardUpdate', 'cardCreate', 'contactAdd', 'contactUpdate', 'cardRecognizeBatch', 'exchangeAgree', 'exchangeIgnore', 'cardDesignSave']; // ============================================================= // 1. 端点与后端文档逐一比对