Files
lucky_shop/pages_tool/card/leads.vue

343 lines
7.6 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="lead-page">
<!-- 平级入口: 与我的人脉页互链(导航合并第一步) -->
<view class="page-switch" @tap="goContacts">
<text class="switch-label">我的人脉</text>
<text class="switch-arrow">›</text>
</view>
<!-- 状态筛选 -->
<scroll-view scroll-x class="tab-bar">
<view class="tab-inner">
<view v-for="tab in tabs" :key="tab.value" class="tab-item"
:class="{ active: status === tab.value }" @tap="switchTab(tab.value)">
{{ tab.label }}
</view>
</view>
</scroll-view>
<!-- 客户列表 -->
<view class="lead-list">
<view v-for="(item, index) in list" :key="item.id || index" class="lead-item">
<view class="lead-top" @tap="callPhone(item.mobile)">
<view class="lead-name">{{ item.name || '未留姓名' }}</view>
<view class="lead-status" :class="'st-' + item.status">{{ item.status_text || '新客户' }}</view>
</view>
<view class="lead-mobile" v-if="item.mobile" @tap="callPhone(item.mobile)">📱 {{ item.mobile }}</view>
<view class="lead-note" v-if="item.note">「{{ item.note }}」</view>
<view class="lead-meta">
<text class="meta-card">来自名片:{{ item.card_name }}</text>
<text class="meta-time">{{ item.create_time_txt }}</text>
</view>
<view class="lead-actions">
<view class="action-btn copy" v-if="item.wechat" @tap="copyText(item.wechat)">复制微信</view>
<view class="action-btn primary" @tap="changeStatus(item, index)">更新状态</view>
</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="'暂无意向客户,访客在你的名片页留资后会出现在这里'"></ns-empty>
</view>
</view>
</template>
<script>
// 与后端 Lead::STATUSES 一致: 0=新客户 1=已联系 2=已成交 3=无效
const STATUS_MAP = [
{ value: 0, label: '新客户' },
{ value: 1, label: '已联系' },
{ value: 2, label: '已成交' },
{ value: 3, label: '无效' }
];
export default {
data() {
return {
tabs: [{ value: '', label: '全部' }].concat(STATUS_MAP),
status: '',
list: [],
page: 1,
pageSize: 20,
pageCount: 1,
isLoading: false
};
},
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: {
// 顶部平级入口: $util.redirectTo(鸿蒙禁裸 uni.switchTab, 静默失败)
goContacts() {
this.$util.redirectTo('/pages_tool/card/contacts');
},
switchTab(value) {
if (this.status === value) return;
this.status = value;
this.refresh();
},
refresh(done) {
this.page = 1;
this.loadPage(1, done);
},
loadPage(pageNo, done) {
this.isLoading = true;
const data = { page: pageNo, page_size: this.pageSize };
// 空字符串 = 全部, 后端对 '' 不加 status 条件
if (this.status !== '') {
data.status = this.status;
}
this.$api.sendRequest({
url: '/card/api/lead/lists',
data: data,
success: res => {
if (res.code == 0) {
const d = res.data || {};
const rows = d.list || [];
this.pageCount = d.page_count || 1;
this.page = pageNo;
this.list = pageNo === 1 ? rows : this.list.concat(rows);
} else {
uni.showToast({ title: res.message || '加载失败', icon: 'none' });
}
this.isLoading = false;
typeof done === 'function' && done();
},
fail: () => {
this.isLoading = false;
uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
typeof done === 'function' && done();
}
});
},
changeStatus(item, index) {
uni.showActionSheet({
itemList: STATUS_MAP.map(s => s.label),
success: res => {
const target = STATUS_MAP[res.tapIndex];
if (!target || target.value === item.status) return;
this.doSetStatus(item, index, target.value);
}
});
},
doSetStatus(item, index, status) {
this.$api.sendRequest({
url: '/card/api/lead/setStatus',
data: { id: item.id, status: status },
success: res => {
if (res.code == 0) {
// 就地更新, 不整页刷新
this.list[index].status = status;
this.list[index].status_text = STATUS_MAP.find(s => s.value === status).label;
uni.showToast({ title: '已更新', icon: 'success' });
} else {
uni.showToast({ title: res.message || '更新失败', icon: 'none' });
}
},
fail: () => {
uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
}
});
},
callPhone(mobile) {
if (!mobile) return;
uni.makePhoneCall({ phoneNumber: mobile });
},
copyText(text) {
uni.setClipboardData({
data: text,
success: () => uni.showToast({ title: '已复制', icon: 'success' })
});
}
}
};
</script>
<style lang="scss" scoped>
.lead-page {
min-height: 100vh;
background-color: #f6f7f9;
padding-bottom: 40rpx;
box-sizing: border-box;
}
.page-switch {
display: flex;
align-items: center;
justify-content: flex-end;
padding: 16rpx 24rpx 0;
.switch-label {
font-size: 26rpx;
color: var(--main-color, #0054a5);
}
.switch-arrow {
font-size: 26rpx;
color: var(--main-color, #0054a5);
margin-left: 4rpx;
}
}
.tab-bar {
background-color: #ffffff;
white-space: nowrap;
.tab-inner {
display: inline-flex;
padding: 0 12rpx;
}
.tab-item {
display: inline-block;
padding: 24rpx 28rpx;
font-size: 28rpx;
color: #666;
position: relative;
&.active {
color: var(--main-color, #0054a5);
font-weight: 600;
&::after {
content: '';
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 10rpx;
width: 40rpx;
height: 6rpx;
border-radius: 3rpx;
background-color: var(--main-color, #0054a5);
}
}
}
}
.lead-list {
padding: 20rpx 24rpx 0;
}
.lead-item {
background-color: #ffffff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
.lead-top {
display: flex;
align-items: center;
.lead-name {
flex: 1;
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.lead-status {
font-size: 22rpx;
padding: 6rpx 18rpx;
border-radius: 22rpx;
&.st-0 {
color: var(--main-color, #0054a5);
background-color: var(--main-color-shallow, #e8f1fb);
}
&.st-1 {
color: #e6a23c;
background-color: #fdf3e6;
}
&.st-2 {
color: #1daa39;
background-color: #e8f8ea;
}
&.st-3 {
color: #999;
background-color: #f2f2f2;
}
}
}
.lead-mobile {
font-size: 28rpx;
color: #333;
margin-top: 14rpx;
}
.lead-note {
font-size: 26rpx;
color: #666;
margin-top: 12rpx;
line-height: 1.5;
}
.lead-meta {
display: flex;
justify-content: space-between;
font-size: 22rpx;
color: #999;
margin-top: 14rpx;
.meta-card {
max-width: 60%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.lead-actions {
display: flex;
justify-content: flex-end;
margin-top: 20rpx;
padding-top: 20rpx;
border-top: 1rpx solid #f0f0f0;
.action-btn {
font-size: 24rpx;
border-radius: 30rpx;
padding: 10rpx 30rpx;
margin-left: 20rpx;
&.copy {
color: #666;
border: 1rpx solid #ddd;
}
&.primary {
color: #ffffff;
background-color: var(--main-color, #0054a5);
}
}
}
}
.load-more {
text-align: center;
color: #999;
font-size: 24rpx;
padding: 24rpx 0;
.load-end {
color: #ccc;
}
}
</style>