| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680 |
- <template>
- <view class="leads-container">
- <NavBar title="" color="#020202" :fixed="true" :bg="'#fff'">
- <template #left>
- <uni-search-bar placeholder="请输入" style="width: 70%" bgColor="#EEEEEE" clearButton="none"
- cancelButton="none" @confirm="" />
- </template>
- <template #bottom>
- <view class="top-tabs">
- <scroll-view class="tab-scroll" scroll-x show-scrollbar="false">
- <view class="tab-list">
- <view class="tab-item" :class="{ active: currentTab === 'leads' }" @click="switchTab('leads')">
- <text class="tab-text">线索</text>
- <uni-badge v-if="tabCounts.leads > 0" :text="tabCounts.leads" type="error"
- size="small"></uni-badge>
- </view>
- <view class="tab-item" :class="{ active: currentTab === 'customer' }"
- @click="switchTab('customer')">
- <text class="tab-text">客户</text>
- <uni-badge v-if="tabCounts.customer > 0" :text="tabCounts.customer" type="error"
- size="small"></uni-badge>
- </view>
- <view class="tab-item" :class="{ active: currentTab === 'opportunity' }"
- @click="switchTab('opportunity')">
- <text class="tab-text">商机</text>
- <uni-badge v-if="tabCounts.opportunity > 0" :text="tabCounts.opportunity" type="error"
- size="small"></uni-badge>
- </view>
- <view class="tab-item" :class="{ active: currentTab === 'contract' }"
- @click="switchTab('contract')">
- <text class="tab-text">合同</text>
- <uni-badge v-if="tabCounts.contract > 0" :text="tabCounts.contract" type="error"
- size="small"></uni-badge>
- </view>
- <view class="tab-item" :class="{ active: currentTab === 'payment' }" @click="switchTab('payment')">
- <text class="tab-text">回款</text>
- <uni-badge v-if="tabCounts.payment > 0" :text="tabCounts.payment" type="error"
- size="small"></uni-badge>
- </view>
- </view>
- </scroll-view>
- <view class="menu-btn">
- <uni-icons type="bars" size="22" color="#333333"></uni-icons>
- </view>
- </view>
-
-
- </template>
- </NavBar>
-
-
- <scroll-view v-if="currentTab === 'leads'" class="content-scroll" scroll-y>
- <view class="leads-list">
- <view class="leads-item" v-for="(item, index) in leadsList" :key="index" @click="viewDetail">
- <view class="row-title">
- <text class="title-text">{{ item.title }}</text>
- <text class="source-tag" :class="item.sourceType">{{ item.sourceName }}</text>
- </view>
- <view class="row-user">
- <image class="user-avatar" src="/static/avatar-default.png" mode="aspectFill" />
- <text class="user-name">啊啊啊啊</text>
- </view>
- <view class="row-info">
- <text class="info-item phone">手机:{{ item.phone }}</text>
- <text class="info-item follow">上次跟进:{{ item.lastFollow }}</text>
- <text v-if="item.overdue" class="info-item overdue"> {{ item.overdueTime }}</text>
- <uni-icons class="arrow-icon" type="right" size="14" color="#cccccc"></uni-icons>
- </view>
- </view>
- </view>
- </scroll-view>
- <scroll-view v-else-if="currentTab === 'customer'" class="content-scroll" scroll-y>
- <view class="customer-list">
- <view class="company-group" v-for="(company, index) in customerList" :key="index">
- <view class="company-header" @click="viewDetail">
- <text class="company-name">{{ company.name }}</text>
- <uni-icons type="right" size="14" color="#cccccc"></uni-icons>
- </view>
- <view class="contact-item" v-for="(contact, cIndex) in company.contacts" :key="cIndex">
- <image class="contact-avatar" src="/static/avatar-default.png" mode="aspectFill" />
- <view class="contact-info">
- <text class="contact-name">{{ contact.name }}</text>
- <text class="contact-intent">{{ contact.intent }}</text>
- </view>
- <text class="contact-follow">{{ contact.lastFollow }}</text>
- <uni-icons type="phone-filled" size="20" color="#ff6b35"></uni-icons>
- </view>
- </view>
- </view>
- </scroll-view>
- <scroll-view v-else-if="currentTab === 'opportunity'" class="content-scroll" scroll-y>
- <view class="opportunity-list">
- <view class="opportunity-item" v-for="(item, index) in opportunityList" :key="index"
- @click="viewDetail">
- <view class="opp-header">
- <text class="opp-title">{{ item.title }}</text>
- <text class="stage-tag" :class="item.stageType">{{ item.stageName }}</text>
- </view>
- <view class="opp-company">
- <uni-icons type="shop" size="16" color="#999999"></uni-icons>
- <text class="company-name">{{ item.companyName }}</text>
- <uni-icons type="right" size="14" color="#cccccc"></uni-icons>
- </view>
- <view class="opp-contact">
- <uni-icons type="person" size="16" color="#999999"></uni-icons>
- <text class="contact-name">{{ item.contactName }}</text>
- <uni-icons type="phone-filled" size="16" color="#ff6b35"></uni-icons>
- </view>
- </view>
- </view>
- </scroll-view>
- <scroll-view v-else-if="currentTab === 'contract'" class="content-scroll" scroll-y>
- <view class="contract-list">
- <view class="contract-item" v-for="(item, index) in contractList" :key="index" @click="viewDetail">
- <view class="contract-header">
- <text class="contract-title">{{ item.title }}</text>
- <text class="status-tag" :class="item.statusType">{{ item.statusName }}</text>
- </view>
- <view class="contract-company">
- <uni-icons type="shop" size="16" color="#999999"></uni-icons>
- <text class="company-name">{{ item.companyName }}</text>
- </view>
- <view class="contract-no">
- <uni-icons type="paperclip" size="16" color="#999999"></uni-icons>
- <text class="no-text">{{ item.contractNo }}</text>
- </view>
- </view>
- </view>
- </scroll-view>
- <scroll-view v-else-if="currentTab === 'payment'" class="content-scroll" scroll-y>
- <view class="payment-list">
- <view class="payment-item" v-for="(item, index) in paymentList" :key="index" @click="viewDetail">
- <view class="payment-header">
- <text class="payment-title">{{ item.title }}</text>
- <text class="status-tag" :class="item.statusType">{{ item.statusName }}</text>
- </view>
- <view class="payment-company">
- <uni-icons type="shop" size="16" color="#999999"></uni-icons>
- <text class="company-name">{{ item.companyName }}</text>
- </view>
- <view class="payment-footer">
- <view class="payment-left">
- <uni-icons type="calendar" size="16" color="#999999"></uni-icons>
- <text class="date-text">{{ item.date }}</text>
- </view>
- <text class="payment-amount" :class="item.statusType">{{ item.amount }}</text>
- </view>
- </view>
- </view>
- </scroll-view>
- </view>
- </template>
- <script setup>
- import NavBar from '@/components/nav-bar/index.vue'
- import {
- ref,
- reactive
- } from 'vue'
- const currentTab = ref('leads')
- const tabCounts = reactive({
- leads: 12,
- customer: 2,
- opportunity: 4,
- contract: 0,
- payment: 0
- })
- const leadsList = reactive([])
- const customerList = reactive([])
- const opportunityList = reactive([{
- title: '独守空房几十块雷锋精神',
- stageType: 'negotiating',
- stageName: '独守空房几',
- companyName: '独守空房几十块',
- contactName: '几十块雷锋精'
- }, ])
- const contractList = reactive([{
- title: '独守空房几十块雷锋精神',
- statusType: 'overdue',
- statusName: '撒方方达',
- companyName: '是的范德萨范德萨',
- contractNo: 'NO741852960122'
- }])
- const paymentList = reactive([{
- title: '独守空房几十块雷锋精神',
- statusType: 'overdue',
- statusName: '独守',
- companyName: '独守空房几十块雷锋精神独守空房几十块雷锋精神',
- date: '2026-03-06',
- amount: '3680.9'
- }, ])
- const switchTab = (tab) => {
- currentTab.value = tab
- }
- const viewDetail = () => {
- uni.showToast({
- title: '',
- icon: 'none'
- })
- }
- </script>
- <style lang="scss" scoped>
- scroll-view {
- box-sizing: border-box;
- }
- .leads-container {
- height: 100vh;
- background: #f5f6f8;
- }
- .top-tabs {
- display: flex;
- align-items: center;
- background: #ffffff;
- padding: 0 20rpx;
- position: sticky;
- top: 0;
- .tab-scroll {
- flex: 1;
- white-space: nowrap;
- .tab-list {
- display: inline-flex;
- align-items: center;
- height: 88rpx;
- }
- .tab-item {
- display: inline-flex;
- align-items: center;
- padding: 0 20rpx;
- position: relative;
- .tab-text {
- font-size: 28rpx;
- color: #999999;
- }
- &.active .tab-text {
- font-size: 30rpx;
- font-weight: 600;
- color: #333333;
- }
- }
- }
- .menu-btn {
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 20rpx;
- flex-shrink: 0;
- }
- }
- .content-scroll {
- flex: 1;
- overflow-y: auto;
- padding: 20rpx;
- box-sizing: border-box;
- }
- .leads-list {
- .leads-item {
- background: #ffffff;
- border-radius: 16rpx;
- padding: 24rpx 28rpx;
- margin-bottom: 20rpx;
- .row-title {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16rpx;
- .title-text {
- font-size: 30rpx;
- font-weight: 600;
- color: #333333;
- flex: 1;
- }
- .source-tag {
- font-size: 22rpx;
- padding: 4rpx 12rpx;
- border-radius: 8rpx;
- flex-shrink: 0;
- margin-left: 16rpx;
- &.official {
- color: #ff6b35;
- background: rgba(255, 107, 53, 0.08);
- }
- &.miniapp {
- color: #52c41a;
- background: rgba(82, 196, 26, 0.08);
- }
- }
- }
- .row-user {
- display: flex;
- align-items: center;
- margin-bottom: 16rpx;
- .user-avatar {
- width: 48rpx;
- height: 48rpx;
- border-radius: 50%;
- margin-right: 12rpx;
- flex-shrink: 0;
- }
- .user-name {
- font-size: 26rpx;
- color: #666666;
- }
- }
- .row-info {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- .info-item {
- font-size: 24rpx;
- color: #999999;
- margin-right: 24rpx;
- &.phone {
- color: #666666;
- }
- &.overdue {
- color: #ff4d4f;
- }
- }
- .arrow-icon {
- margin-left: auto;
- flex-shrink: 0;
- }
- }
- }
- }
- .customer-list {
- .company-group {
- background: #ffffff;
- border-radius: 16rpx;
- margin-bottom: 20rpx;
- overflow: hidden;
- .company-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 24rpx 28rpx;
- background: #f8f9fa;
- .company-name {
- font-size: 30rpx;
- font-weight: 600;
- color: #333333;
- }
- }
- .contact-item {
- display: flex;
- align-items: center;
- padding: 24rpx 28rpx;
- border-bottom: 1rpx solid #f0f0f0;
- &:last-child {
- border-bottom: none;
- }
- .contact-avatar {
- width: 60rpx;
- height: 60rpx;
- border-radius: 50%;
- margin-right: 16rpx;
- flex-shrink: 0;
- }
- .contact-info {
- flex: 1;
- .contact-name {
- display: block;
- font-size: 28rpx;
- color: #333333;
- margin-bottom: 8rpx;
- }
- .contact-intent {
- display: block;
- font-size: 24rpx;
- color: #999999;
- }
- }
- .contact-follow {
- font-size: 24rpx;
- color: #999999;
- margin: 0 24rpx;
- }
- }
- }
- }
- .opportunity-list {
- .opportunity-item {
- background: #ffffff;
- border-radius: 16rpx;
- padding: 24rpx 28rpx;
- margin-bottom: 20rpx;
- .opp-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16rpx;
- .opp-title {
- font-size: 30rpx;
- font-weight: 600;
- color: #333333;
- flex: 1;
- }
- .stage-tag {
- font-size: 22rpx;
- padding: 4rpx 12rpx;
- border-radius: 8rpx;
- flex-shrink: 0;
- margin-left: 16rpx;
- &.negotiating {
- color: #ff6b35;
- background: rgba(255, 107, 53, 0.08);
- }
- &.preliminary {
- color: #ff9500;
- background: rgba(255, 149, 0, 0.08);
- }
- &.demand {
- color: #1890ff;
- background: rgba(24, 144, 255, 0.08);
- }
- }
- }
- .opp-company {
- display: flex;
- align-items: center;
- margin-bottom: 12rpx;
- uni-icons {
- margin-right: 8rpx;
- }
- .company-name {
- flex: 1;
- font-size: 26rpx;
- color: #666666;
- }
- }
- .opp-contact {
- display: flex;
- align-items: center;
- uni-icons {
- margin-right: 8rpx;
- }
- .contact-name {
- flex: 1;
- font-size: 24rpx;
- color: #999999;
- }
- }
- }
- }
- .contract-list {
- .contract-item {
- background: #ffffff;
- border-radius: 16rpx;
- padding: 24rpx 28rpx;
- margin-bottom: 20rpx;
- .contract-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16rpx;
- .contract-title {
- font-size: 30rpx;
- font-weight: 600;
- color: #333333;
- flex: 1;
- }
- .status-tag {
- font-size: 22rpx;
- padding: 4rpx 12rpx;
- border-radius: 8rpx;
- flex-shrink: 0;
- margin-left: 16rpx;
- &.overdue {
- color: #ff4d4f;
- background: rgba(255, 77, 79, 0.08);
- }
- &.warning {
- color: #ff9500;
- background: rgba(255, 149, 0, 0.08);
- }
- &.normal {
- color: #52c41a;
- background: rgba(82, 196, 26, 0.08);
- }
- }
- }
- .contract-company {
- display: flex;
- align-items: center;
- margin-bottom: 12rpx;
- uni-icons {
- margin-right: 8rpx;
- }
- .company-name {
- font-size: 26rpx;
- color: #666666;
- }
- }
- .contract-no {
- display: flex;
- align-items: center;
- uni-icons {
- margin-right: 8rpx;
- }
- .no-text {
- font-size: 24rpx;
- color: #999999;
- }
- }
- }
- }
- .payment-list {
- .payment-item {
- background: #ffffff;
- border-radius: 16rpx;
- padding: 24rpx 28rpx;
- margin-bottom: 20rpx;
- .payment-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16rpx;
- .payment-title {
- font-size: 30rpx;
- font-weight: 600;
- color: #333333;
- flex: 1;
- }
- .status-tag {
- font-size: 22rpx;
- padding: 4rpx 12rpx;
- border-radius: 8rpx;
- flex-shrink: 0;
- margin-left: 16rpx;
- &.overdue {
- color: #ff4d4f;
- background: rgba(255, 77, 79, 0.08);
- }
- &.warning {
- color: #ff9500;
- background: rgba(255, 149, 0, 0.08);
- }
- &.normal {
- color: #52c41a;
- background: rgba(82, 196, 26, 0.08);
- }
- }
- }
- .payment-company {
- display: flex;
- align-items: center;
- margin-bottom: 16rpx;
- uni-icons {
- margin-right: 8rpx;
- }
- .company-name {
- font-size: 26rpx;
- color: #666666;
- }
- }
- .payment-footer {
- display: flex;
- justify-content: space-between;
- align-items: center;
- .payment-left {
- display: flex;
- align-items: center;
- uni-icons {
- margin-right: 8rpx;
- }
- .date-text {
- font-size: 24rpx;
- color: #999999;
- }
- }
- .payment-amount {
- font-size: 32rpx;
- font-weight: 600;
- &.overdue {
- color: #ff4d4f;
- }
- &.warning {
- color: #ff9500;
- }
- &.normal {
- color: #ff6b35;
- }
- }
- }
- }
- }
- </style>
|