Files
lucky_shop/pages_tool/card/list.vue
ZF sun b3f6ff7517 feat: 名片夹运营闭环——申请状态条/需修改标/申请角标(配合商家后台 2.4.0)
- list.vue 顶部申请状态条: my_apply 待审(审核中)/被拒(可重新申请), 点按进申请页
- 被打回名片: 「需修改」红标, 点击弹层显示商家修改建议(review_note)
- 申请审批建卡: 头像「申」角标(from_apply)
- 数据源: /card/api/card/lists 增 my_apply + need_fix 派生字段
2026-10-13 19:26:04 +08:00

477 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="card-wallet">
<!-- 搜索区 -->
<view class="search-bar">
<view class="search-input-wrap">
<input class="search-input" confirm-type="search" :value="keyword"
placeholder="搜索姓名 / 手机 / 公司" @confirm="onSearch" @input="onKeywordInput" />
<text v-if="keyword" class="search-clear" @tap="clearKeyword">✕</text>
<text class="iconfont icon-sousuo3" @tap="onSearch"></text>
</view>
<view class="claim-btn create-btn" @tap="goCreate">新建</view>
<view class="claim-btn apply-btn" v-if="!isLoading && list.length === 0" @tap="goApply">申请</view>
<view class="claim-btn" @tap="onClaimTap">认领</view>
</view>
<!-- 申请状态条(Q7): 待审/被拒时常驻, 点按可重新申请 -->
<view v-if="applyBanner" class="apply-banner" :class="'apply-banner--' + applyBanner.type"
@tap="onApplyBannerTap">
<text class="apply-banner__ico">{{ applyBanner.type === 'warn' ? '!' : 'i' }}</text>
<view class="apply-banner__txt">
<text class="apply-banner__title">{{ applyBanner.title }}</text>
<text class="apply-banner__sub">{{ applyBanner.sub }}</text>
</view>
<text class="apply-banner__act">{{ applyBanner.action }}</text>
</view>
<!-- 列表 -->
<view class="card-list">
<view v-for="(item, index) in list" :key="item.id || index" class="card-item"
@tap="goDetail(item)">
<view class="card-avatar">
<text>{{ avatarText(item.name) }}</text>
<text v-if="item.from_apply == 1" class="avatar-flag">申</text>
</view>
<view class="card-info">
<view class="card-name line1">
{{ item.name || '未命名名片' }}
<text v-if="item.need_fix == 1" class="fix-flag" @tap.stop="showReviewNote(item)">需修改</text>
</view>
<view class="card-sub line1" v-if="item.job_title || item.company">
{{ [item.job_title, item.company].filter(Boolean).join(' · ') }}
</view>
<view class="card-mobile" v-if="item.mobile">📱 {{ item.mobile }}</view>
</view>
<view class="card-arrow">›</view>
</view>
<!-- 加载状态 -->
<view class="load-more" v-if="list.length > 0">
<text v-if="isLoading">加载中…</text>
<text v-else-if="page < pageCount">上拉加载更多</text>
<text v-else class="load-end">— 没有更多了 —</text>
</view>
<!-- 空态 -->
<ns-empty v-if="!isLoading && list.length === 0" :isIndex="false"
:text="emptyText"></ns-empty>
</view>
</view>
</template>
<script>
export default {
data() {
return {
list: [],
page: 1,
pageSize: 20,
pageCount: 1,
count: 0,
keyword: '',
isLoading: false,
myApply: null,
emptyText: '暂无名片,可「新建」「申请」或「认领」添加'
};
},
computed: {
// Q7: 申请状态条——只在有进行中/被拒的申请时显示(已通过且有卡不打扰)
applyBanner() {
const a = this.myApply;
if (!a) return null;
const st = parseInt(a.status, 10);
if (st === 0) {
return { type: 'info', title: '建卡申请审核中', sub: '商家处理后名片会自动出现在名片夹', action: '查看' };
}
if (st === 2) {
return { type: 'warn', title: '申请未通过', sub: '可修改姓名/手机号后重新提交申请', action: '重新申请' };
}
return null;
}
},
onLoad() {
this.refresh();
},
onPullDownRefresh() {
this.refresh(() => uni.stopPullDownRefresh());
},
onReachBottom() {
if (this.isLoading) return;
if (this.page >= this.pageCount) return;
this.loadPage(this.page + 1);
},
methods: {
avatarText(name) {
return (name || '?').trim().charAt(0).toUpperCase();
},
onKeywordInput(e) {
this.keyword = e.detail.value;
},
onSearch() {
this.refresh();
},
clearKeyword() {
this.keyword = '';
this.refresh();
},
refresh(done) {
this.page = 1;
this.loadPage(1, done);
},
loadPage(pageNo, done) {
this.isLoading = true;
this.$api.sendRequest({
url: '/card/api/card/lists',
data: {
page: pageNo,
page_size: this.pageSize,
keyword: this.keyword.trim()
},
success: res => {
if (res.code == 0) {
const data = res.data || {};
const rows = data.list || [];
this.pageCount = data.page_count || 1;
this.count = data.count || 0;
this.page = pageNo;
this.list = pageNo === 1 ? rows : this.list.concat(rows);
// 申请状态只在首页返回, 分页不覆盖
if (pageNo === 1 && data.my_apply !== undefined) {
this.myApply = data.my_apply;
}
this.emptyText = this.keyword.trim()
? '未找到匹配的名片'
: '暂无名片,可「新建」「申请」或「认领」添加';
} else {
this.handleApiError(res);
}
this.isLoading = false;
typeof done === 'function' && done();
},
fail: () => {
this.isLoading = false;
uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
typeof done === 'function' && done();
}
});
},
goDetail(item) {
if (!item.card_id) return;
uni.navigateTo({
url: '/pages_tool/card/detail?card_id=' + encodeURIComponent(item.card_id)
});
},
goCreate() {
uni.navigateTo({
url: '/pages_tool/card/edit'
});
},
// 员工已注册但没有名片时, 向商家提交建卡申请(商家后台审批后自动归属)
goApply() {
uni.navigateTo({
url: '/pages_tool/card/apply'
});
},
// Q7: 申请状态条点按——被拒时去重新申请, 待审时去看进度(申请页自带状态回查)
onApplyBannerTap() {
this.goApply();
},
// Q5: 查看商户打的修改建议
showReviewNote(item) {
uni.showModal({
title: '商家修改建议',
content: (item && item.review_note) || '商家未填写具体建议,请联系发卡商家',
showCancel: false,
confirmText: '知道了'
});
},
onClaimTap() {
uni.showModal({
title: '认领名片',
editable: true,
placeholderText: '粘贴领取码或名片编号(card_id)',
success: res => {
if (!res.confirm) return;
const cardId = (res.content || '').trim();
if (!cardId) {
uni.showToast({ title: '请输入名片编号', icon: 'none' });
return;
}
this.doClaim(cardId);
}
});
},
doClaim(codeOrCardId) {
uni.showLoading({ title: '认领中…' });
this.$api.sendRequest({
url: '/card/api/card/claim',
// 同值双传: 后端按领取码优先解析, 未命中回落 card_id
data: { claim_code: codeOrCardId, card_id: codeOrCardId },
success: res => {
uni.hideLoading();
if (res.code == 0) {
const claimed = res.data && res.data.claimed === false;
uni.showToast({
title: claimed ? '该名片已在您的名片夹中' : '认领成功',
icon: 'success'
});
this.refresh();
} else {
this.handleApiError(res);
}
},
fail: () => {
uni.hideLoading();
uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
}
});
},
handleApiError(res) {
const msg = (res && res.message) || '操作失败';
// 未登录/凭证失效时引导登录(与全站约定一致:code < 0)
if (res && res.code < 0) {
uni.showToast({ title: msg, icon: 'none' });
return;
}
uni.showToast({ title: msg, icon: 'none' });
}
}
};
</script>
<style lang="scss" scoped>
.card-wallet {
min-height: 100vh;
background-color: #f6f7f9;
padding: 20rpx 24rpx 40rpx;
box-sizing: border-box;
}
.search-bar {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.search-input-wrap {
flex: 1;
display: flex;
align-items: center;
background-color: #ffffff;
border-radius: 36rpx;
padding: 0 24rpx;
height: 72rpx;
.search-input {
flex: 1;
height: 72rpx;
font-size: 28rpx;
color: #333;
}
.search-clear {
padding: 0 12rpx;
color: #bbb;
font-size: 28rpx;
}
.iconfont {
color: #999;
font-size: 32rpx;
padding-left: 12rpx;
}
}
/* Q7 申请状态条 */
.apply-banner {
display: flex;
align-items: center;
margin: 20rpx 0 8rpx;
padding: 18rpx 22rpx;
border-radius: 16rpx;
background: #eef4ff;
&--warn {
background: #fdf3e7;
}
&__ico {
width: 36rpx;
height: 36rpx;
line-height: 36rpx;
text-align: center;
border-radius: 50%;
background: var(--main-color, #0054a5);
color: #fff;
font-size: 24rpx;
font-weight: 700;
flex: none;
}
&--warn &__ico {
background: #e6a23c;
}
&__txt {
flex: 1;
min-width: 0;
margin-left: 16rpx;
}
&__title {
display: block;
font-size: 26rpx;
font-weight: 600;
color: #1a1f36;
}
&__sub {
display: block;
font-size: 22rpx;
color: #5a6478;
margin-top: 2rpx;
}
&__act {
font-size: 24rpx;
color: var(--main-color, #0054a5);
font-weight: 600;
flex: none;
}
&--warn &__act {
color: #e6a23c;
}
}
/* Q5 需修改标 / Q6 申请角标 */
.fix-flag {
display: inline-block;
vertical-align: middle;
margin-left: 12rpx;
padding: 2rpx 12rpx;
font-size: 20rpx;
line-height: 26rpx;
color: #e34d59;
background: #fdeced;
border-radius: 8rpx;
font-weight: 600;
}
.avatar-flag {
position: absolute;
right: -6rpx;
bottom: -6rpx;
width: 32rpx;
height: 32rpx;
line-height: 32rpx;
text-align: center;
font-size: 18rpx;
color: #fff;
background: #7c5cfc;
border-radius: 50%;
border: 2rpx solid #fff;
}
.claim-btn {
margin-left: 20rpx;
height: 72rpx;
line-height: 72rpx;
padding: 0 30rpx;
background-color: var(--main-color, #0054a5);
color: #ffffff;
font-size: 28rpx;
border-radius: 36rpx;
/* 「新建」次级按钮: 主按钮同构, 淡底弱化一档 */
&.create-btn {
background-color: var(--main-color-shallow, #e8f1fb);
color: var(--main-color, #0054a5);
}
/* 「申请」: 仅空态出现的第三入口, 边框幽灵式再弱一档 */
&.apply-btn {
background-color: #ffffff;
color: #666666;
border: 1rpx solid #dcdfe6;
}
}
}
.card-list {
.card-item {
display: flex;
align-items: center;
background-color: #ffffff;
border-radius: 16rpx;
padding: 28rpx 24rpx;
margin-bottom: 20rpx;
&:active {
background-color: #f0f2f5;
}
.card-avatar {
position: relative;
width: 88rpx;
height: 88rpx;
border-radius: 50%;
background-color: var(--main-color-shallow, #e8f1fb);
color: var(--main-color, #0054a5);
font-size: 36rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-right: 24rpx;
}
.card-info {
flex: 1;
min-width: 0;
.card-name {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.card-sub {
font-size: 26rpx;
color: #666;
margin-top: 8rpx;
}
.card-mobile {
font-size: 26rpx;
color: #999;
margin-top: 8rpx;
}
}
.card-arrow {
color: #ccc;
font-size: 40rpx;
margin-left: 16rpx;
}
}
}
.load-more {
text-align: center;
color: #999;
font-size: 24rpx;
padding: 24rpx 0;
.load-end {
color: #ccc;
}
}
.line1 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>