Files
smartDriveEEUniApp/pages/team/team.vue
2026-01-31 21:10:40 +08:00

773 lines
18 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="page-wrap">
<!-- #ifdef APP -->
<statusBar></statusBar>
<!-- #endif -->
<!-- 头部返回 + 标题"AI销冠系统" -->
<view class="page-header">
<view class="page-header__back" @tap="onBack">
<text class="page-header__back-icon"></text>
</view>
<text class="page-header__title">AI销冠系统</text>
<!-- 占位使标题居中 -->
<view class="page-header__right"></view>
</view>
<view class="page-container">
<!-- 统计周期 -->
<view class="filter-row">
<text class="filter-row__label">统计周期</text>
<view class="filter-row__value">
<text class="filter-row__value-text">本周</text>
<text class="filter-row__value-icon"></text>
</view>
</view>
<!-- 顶部切换个人排行 / 团队排行 -->
<view class="tab-switch">
<view
class="tab-item"
:class="{ 'tab-item--active': activeTab === 'personal' }"
@tap="changeTab('personal')"
>
<text class="tab-text">个人排行</text>
</view>
<view
class="tab-item"
:class="{ 'tab-item--active': activeTab === 'team' }"
@tap="changeTab('team')"
>
<text class="tab-text">团队排行</text>
</view>
<view
class="tab-slider"
:style="{
transform: activeTab === 'personal'
? 'translateX(0)'
: 'translateX(100%)'
}"
></view>
</view>
<scroll-view scroll-y class="scroll-area">
<!-- 个人排行布局 -->
<view class="card-list" v-if="activeTab === 'personal'">
<!-- 今日服务时长 -->
<view class="rank-card">
<view class="rank-card__header">
<text class="rank-card__title">今日服务时长</text>
<view class="rank-card__more" @tap="onMore('duration')">
<text class="rank-card__more-text">更多</text>
<text class="rank-card__more-arrow"></text>
</view>
</view>
<view
class="rank-row"
v-for="item in currentData.duration"
:key="item.id"
>
<image
class="rank-row__avatar"
:src="item.avatar"
mode="aspectFill"
></image>
<view class="rank-row__main">
<text class="rank-row__name">{{ item.name }}</text>
<view class="rank-row__progress">
<view
class="rank-row__progress-inner rank-row__progress-inner--blue"
:style="{ width: item.progress + '%' }"
></view>
</view>
</view>
<text class="rank-row__value">{{ item.value }}分钟</text>
</view>
</view>
<!-- 今日服务接待量 -->
<view class="rank-card">
<view class="rank-card__header">
<text class="rank-card__title">今日服务接待量</text>
<view class="rank-card__more" @tap="onMore('customers')">
<text class="rank-card__more-text">更多</text>
<text class="rank-card__more-arrow"></text>
</view>
</view>
<view
class="rank-row"
v-for="item in currentData.customers"
:key="item.id"
>
<image
class="rank-row__avatar"
:src="item.avatar"
mode="aspectFill"
></image>
<view class="rank-row__main">
<text class="rank-row__name">{{ item.name }}</text>
<view class="rank-row__progress">
<view
class="rank-row__progress-inner rank-row__progress-inner--blue"
:style="{ width: item.progress + '%' }"
></view>
</view>
</view>
<text class="rank-row__value">{{ item.value }}组客户</text>
</view>
</view>
<!-- 本周服务 SOP 执行表现 -->
<view class="rank-card">
<view class="rank-card__header">
<text class="rank-card__title">本周服务SOP执行表现</text>
<view class="rank-card__more" @tap="onMore('sop')">
<text class="rank-card__more-text">更多</text>
<text class="rank-card__more-arrow"></text>
</view>
</view>
<view
class="rank-row"
v-for="item in currentData.sop"
:key="item.id"
>
<image
class="rank-row__avatar"
:src="item.avatar"
mode="aspectFill"
></image>
<view class="rank-row__main">
<text class="rank-row__name">{{ item.name }}</text>
<view class="rank-row__progress rank-row__progress--dashed">
<view
class="rank-row__progress-inner rank-row__progress-inner--blue"
:style="{ width: item.progress + '%' }"
></view>
<!-- 中间虚线分隔线用伪线条模拟 -->
<view class="rank-row__divider"></view>
</view>
</view>
<text class="rank-row__value">{{ item.value }}%</text>
</view>
</view>
</view>
<!-- 团队排行布局 -->
<view class="card-list" v-else>
<!-- 门店人均服务时长 -->
<view class="rank-card">
<view class="rank-card__header rank-card__header--with-sub">
<text class="rank-card__title">门店人均服务时长</text>
<text class="rank-card__sub-right">日均服务时长</text>
</view>
<view class="rank-card__tabs">
<text class="rank-card__tab rank-card__tab--active">全区</text>
<text class="rank-card__tab">门店对比</text>
</view>
<view
class="store-row"
v-for="item in teamStoreData.serviceTime"
:key="item.rank"
>
<view class="store-row__left">
<view
class="rank-badge"
:class="'rank-badge--' + item.badgeType"
>
<text class="rank-badge__text">{{ item.rank }}</text>
</view>
<text
class="store-row__name"
:class="{ 'store-row__name--me': item.isMine }"
>
{{ item.name }}
</text>
</view>
<view class="store-row__middle">
<view class="rank-row__progress">
<view
class="rank-row__progress-inner rank-row__progress-inner--blue"
:style="{ width: item.progress + '%' }"
></view>
</view>
</view>
<text
class="store-row__value"
:class="{ 'store-row__value--highlight': item.isMine }"
>
{{ item.value }}分钟
</text>
</view>
</view>
<!-- 门店工牌使用率 -->
<view class="rank-card">
<view class="rank-card__header rank-card__header--with-sub">
<text class="rank-card__title">门店工牌使用率</text>
</view>
<!-- 表头 -->
<view class="table-header">
<text class="table-header__cell table-header__cell--rank">排名</text>
<text class="table-header__cell table-header__cell--store">门店对比</text>
<text class="table-header__cell">工牌使用()</text>
<text class="table-header__cell">工牌开通()</text>
<text class="table-header__cell table-header__cell--rate">使用率</text>
</view>
<view
class="table-row"
v-for="item in teamStoreData.badgeUsage"
:key="item.rank"
>
<view class="table-row__cell table-row__cell--rank">
<view
class="rank-badge"
:class="'rank-badge--' + item.badgeType"
>
<text class="rank-badge__text">{{ item.rank }}</text>
</view>
</view>
<view class="table-row__cell table-row__cell--store">
<text
class="table-row__store"
:class="{ 'table-row__store--me': item.isMine }"
>
{{ item.name }}
</text>
</view>
<text class="table-row__cell">{{ item.used }}</text>
<text class="table-row__cell">{{ item.opened }}</text>
<text
class="table-row__cell table-row__cell--rate"
:class="{ 'table-row__cell--highlight': item.isMine }"
>
{{ item.rate }}%
</text>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
// #ifdef APP
import statusBar from "@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar";
// #endif
const defaultAvatar = "/static/logo.png";
export default {
// #ifdef APP
components: {
statusBar
},
// #endif
data() {
return {
activeTab: "personal",
personalData: {
duration: [
{ id: 1, name: "三多", value: 90, progress: 100, avatar: defaultAvatar },
{ id: 2, name: "雅秋", value: 78, progress: 78 / 90 * 100, avatar: defaultAvatar },
{ id: 3, name: "炫辰", value: 60, progress: 60 / 90 * 100, avatar: defaultAvatar }
],
customers: [
{ id: 1, name: "三多", value: 3, progress: 100, avatar: defaultAvatar },
{ id: 2, name: "曼云", value: 1, progress: 1 / 3 * 100, avatar: defaultAvatar },
{ id: 3, name: "雅秋", value: 1, progress: 1 / 3 * 100, avatar: defaultAvatar }
],
sop: [
{ id: 1, name: "三多", value: 91.5, progress: 100, avatar: defaultAvatar },
{ id: 2, name: "曼云", value: 75, progress: 75 / 91.5 * 100, avatar: defaultAvatar }
]
},
// 团队排行:门店维度数据
teamStoreData: {
serviceTime: [
{ rank: 1, name: "A门店", value: 33, progress: 100, badgeType: "gold", isMine: false },
{ rank: 2, name: "B门店", value: 28, progress: 28 / 33 * 100, badgeType: "silver", isMine: false },
{ rank: 3, name: "C门店", value: 16, progress: 16 / 33 * 100, badgeType: "bronze", isMine: false },
{ rank: 11, name: "我的门店", value: 12, progress: 12 / 33 * 100, badgeType: "normal", isMine: true }
],
badgeUsage: [
{ rank: 1, name: "A门店", used: 9, opened: 10, rate: 90, badgeType: "gold", isMine: false },
{ rank: 2, name: "B门店", used: 8, opened: 10, rate: 80, badgeType: "silver", isMine: false },
{ rank: 3, name: "C门店", used: 7, opened: 10, rate: 70, badgeType: "bronze", isMine: false },
{ rank: 5, name: "我的门店", used: 5, opened: 10, rate: 50, badgeType: "normal", isMine: true }
]
}
};
},
computed: {
currentData() {
return this.personalData;
}
},
methods: {
changeTab(tab) {
if (this.activeTab === tab) return;
this.activeTab = tab;
// 预留:切换时可请求接口
// this.loadRankData();
},
onBack() {
// 如果有页面栈则返回上一页,否则回到首页
const pages = getCurrentPages();
if (pages && pages.length > 1) {
uni.navigateBack();
} else {
uni.navigateTo({
url: "/pages-subpackage/customer/customer"
});
}
},
onMore(type) {
// 预留:跳转到更多排行榜详情页
// uni.navigateTo({ url: `/pages/team/rank-detail?type=${type}&tab=${this.activeTab}` })
console.log("点击更多", type, this.activeTab);
}
}
};
</script>
<style>
/* #ifndef APP-NVUE */
page {
display: flex;
flex-direction: column;
box-sizing: border-box;
background-color: #f4f6fb;
min-height: 100%;
height: auto;
}
/* #endif */
.page-wrap {
flex: 1;
min-height: 100vh;
background-color: #f4f6fb;
}
.page-container {
padding: 0 24rpx 32rpx;
position: relative;
min-height: 100vh;
}
.filter-row {
flex-direction: row;
display: flex;
align-items: center;
justify-content: flex-start;
padding: 16rpx 0 8rpx;
}
.filter-row__label {
font-size: 24rpx;
color: #6b7280;
margin-right: 16rpx;
}
.filter-row__value {
flex-direction: row;
display: flex;
align-items: center;
background-color: #eef2ff;
border-radius: 20rpx;
padding: 6rpx 14rpx;
}
.filter-row__value-text {
font-size: 24rpx;
color: #1d4ed8;
margin-right: 4rpx;
}
.filter-row__value-icon {
font-size: 20rpx;
color: #6b7280;
}
.page-header {
height: 88rpx;
padding: 0 24rpx;
background-color: #ffffff;
flex-direction: row;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1rpx 0 rgba(229, 231, 235, 0.9);
}
.page-header__back {
width: 80rpx;
justify-content: flex-start;
display: flex;
align-items: center;
}
.page-header__back-icon {
font-size: 40rpx;
color: #4b5563;
}
.page-header__title {
font-size: 32rpx;
font-weight: 600;
color: #111827;
}
.page-header__right {
width: 80rpx;
}
.tab-switch {
height: 80rpx;
border-radius: 40rpx;
background-color: #e5edf8;
padding: 6rpx;
display: flex;
position: relative;
margin-bottom: 24rpx;
}
.tab-item {
flex: 1;
align-items: center;
justify-content: center;
display: flex;
z-index: 2;
}
.tab-item--active .tab-text {
color: #1b68ff;
font-weight: 600;
}
.tab-text {
font-size: 26rpx;
color: #7a8aa5;
}
.tab-slider {
position: absolute;
top: 4rpx;
left: 4rpx;
width: 50%;
height: 72rpx;
border-radius: 36rpx;
background-color: #ffffff;
box-shadow: 0 10rpx 20rpx rgba(59, 120, 255, 0.15);
transition: transform 0.25s ease-out;
}
.scroll-area {
/* 高度计算100vh - 导航栏(88rpx) - filter-row和tab-switch(约120rpx) - 底部导航栏(96rpx) - container padding(32rpx) */
/* 使用绝对定位从tab-switch底部开始到底部导航栏结束 */
position: absolute;
top: 208rpx; /* 导航栏(88rpx) + filter-row(约40rpx) + tab-switch(80rpx) = 208rpx */
left: 24rpx;
right: 24rpx;
bottom: 96rpx; /* 底部导航栏高度 */
box-sizing: border-box;
}
.card-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.rank-card {
background-color: #ffffff;
border-radius: 24rpx;
padding: 24rpx 24rpx 18rpx;
box-shadow: 0 10rpx 30rpx rgba(7, 34, 85, 0.04);
}
.rank-card__header {
flex-direction: row;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 18rpx;
}
.rank-card__header--with-sub {
margin-bottom: 10rpx;
}
.rank-card__title {
font-size: 28rpx;
font-weight: 600;
color: #111827;
}
.rank-card__sub-right {
font-size: 22rpx;
color: #9ca3af;
}
.rank-card__more {
flex-direction: row;
display: flex;
align-items: center;
}
.rank-card__more-text {
font-size: 24rpx;
color: #9ca3af;
margin-right: 4rpx;
}
.rank-card__more-arrow {
font-size: 26rpx;
color: #d1d5db;
}
.rank-card__tabs {
flex-direction: row;
display: flex;
margin-bottom: 10rpx;
}
.rank-card__tab {
font-size: 24rpx;
color: #9ca3af;
margin-right: 24rpx;
}
.rank-card__tab--active {
color: #2563eb;
font-weight: 600;
}
.rank-row {
flex-direction: row;
display: flex;
align-items: center;
margin-bottom: 18rpx;
}
.rank-row:last-child {
margin-bottom: 0;
}
.rank-row__avatar {
width: 64rpx;
height: 64rpx;
border-radius: 32rpx;
margin-right: 16rpx;
}
.rank-row__main {
flex: 1;
}
.rank-row__name {
font-size: 26rpx;
color: #111827;
margin-bottom: 8rpx;
}
.rank-row__progress {
height: 16rpx;
border-radius: 8rpx;
background-color: #e5edf8;
overflow: hidden;
position: relative;
}
.rank-row__progress-inner {
height: 100%;
border-radius: 8rpx;
}
.rank-row__progress-inner--blue {
background-image: linear-gradient(90deg, #36d1ff, #246bff);
}
.rank-row__value {
font-size: 26rpx;
color: #111827;
margin-left: 16rpx;
}
/* 门店排行行 */
.store-row {
flex-direction: row;
display: flex;
align-items: center;
padding: 10rpx 0;
}
.store-row__left {
flex-direction: row;
display: flex;
align-items: center;
width: 220rpx;
}
.store-row__name {
font-size: 26rpx;
color: #111827;
margin-left: 16rpx;
}
.store-row__name--me {
color: #2563eb;
}
.store-row__middle {
flex: 1;
margin: 0 16rpx;
}
.store-row__value {
font-size: 26rpx;
color: #111827;
width: 140rpx;
text-align: right;
}
.store-row__value--highlight {
color: #2563eb;
font-weight: 600;
}
/* 排名圆圈 */
.rank-badge {
width: 40rpx;
height: 40rpx;
border-radius: 20rpx;
align-items: center;
justify-content: center;
display: flex;
background-color: #e5e7eb;
}
.rank-badge__text {
font-size: 22rpx;
color: #ffffff;
}
.rank-badge--gold {
background-image: linear-gradient(135deg, #fbbf24, #f59e0b);
box-shadow: 0 6rpx 12rpx rgba(245, 158, 11, 0.35);
}
.rank-badge--silver {
background-image: linear-gradient(135deg, #d1d5db, #9ca3af);
box-shadow: 0 6rpx 12rpx rgba(156, 163, 175, 0.35);
}
.rank-badge--bronze {
background-image: linear-gradient(135deg, #fb923c, #f97316);
box-shadow: 0 6rpx 12rpx rgba(248, 113, 22, 0.35);
}
.rank-badge--normal {
background-image: linear-gradient(135deg, #2563eb, #1d4ed8);
box-shadow: 0 6rpx 12rpx rgba(37, 99, 235, 0.35);
}
/* 表格:门店工牌使用率 */
.table-header {
flex-direction: row;
display: flex;
align-items: center;
padding: 10rpx 0;
border-bottom-width: 1rpx;
border-bottom-style: solid;
border-bottom-color: #e5e7eb;
margin-top: 4rpx;
}
.table-header__cell {
font-size: 22rpx;
color: #9ca3af;
flex: 1;
text-align: center;
}
.table-header__cell--rank {
width: 120rpx;
flex: 0 0 120rpx;
}
.table-header__cell--store {
text-align: left;
flex: 0 0 180rpx;
}
.table-header__cell--rate {
text-align: right;
}
.table-row {
flex-direction: row;
display: flex;
align-items: center;
padding: 14rpx 0;
}
.table-row__cell {
flex: 1;
font-size: 24rpx;
color: #111827;
text-align: center;
}
.table-row__cell--rank {
width: 120rpx;
flex: 0 0 120rpx;
}
.table-row__cell--store {
text-align: left;
flex: 0 0 180rpx;
}
.table-row__cell--rate {
text-align: right;
}
.table-row__cell--highlight {
color: #2563eb;
font-weight: 600;
}
.table-row__store {
font-size: 24rpx;
color: #111827;
}
.table-row__store--me {
color: #2563eb;
}
/* SOP 卡片的虚线分割线样式 */
.rank-row__progress--dashed {
position: relative;
}
.rank-row__divider {
position: absolute;
left: 50%;
top: -10rpx;
bottom: -10rpx;
border-left-width: 2rpx;
border-left-style: dashed;
border-left-color: rgba(156, 163, 175, 0.6);
}
</style>