From ce06ca62a0dd65d4a8fb57126948449c804ad77e Mon Sep 17 00:00:00 2001
From: MrShi <1878285526@qq.com>
Date: 星期二, 19 五月 2026 19:40:08 +0800
Subject: [PATCH] 提交
---
small-program/shop/pages/revenue-analysis/revenue-analysis.vue | 565 ++++++++++++++++++++++++++++++++++++++++---------------
1 files changed, 408 insertions(+), 157 deletions(-)
diff --git a/small-program/shop/pages/revenue-analysis/revenue-analysis.vue b/small-program/shop/pages/revenue-analysis/revenue-analysis.vue
index 62da5c5..ecaaccc 100644
--- a/small-program/shop/pages/revenue-analysis/revenue-analysis.vue
+++ b/small-program/shop/pages/revenue-analysis/revenue-analysis.vue
@@ -7,17 +7,41 @@
:key="item.value"
class="quick-tab"
:class="{ active: currentRange === item.value }"
- @tap="currentRange = item.value"
+ @click="currentRange = item.value"
>
{{ item.label }}
</view>
</view>
- <view class="date-bar">
- <text class="date-placeholder">寮�濮嬫椂闂�</text>
- <text class="date-separator">-</text>
- <text class="date-placeholder">缁撴潫鏃堕棿</text>
- </view>
+ <view v-if="currentRange === 'custom'" class="date-bar">
+ <view class="date-item" @click="showStartDatePicker = true">
+ <text :class="startDate ? 'date-text' : 'date-placeholder'">{{ formatPickerDate(startDate) || '寮�濮嬫椂闂�' }}</text>
+ </view>
+ <text class="date-separator">-</text>
+ <view class="date-item" @click="showEndDatePicker = true">
+ <text :class="endDate ? 'date-text' : 'date-placeholder'">{{ formatPickerDate(endDate) || '缁撴潫鏃堕棿' }}</text>
+ </view>
+ </view>
+
+ <!-- 寮�濮嬫棩鏈熼�夋嫨鍣� -->
+ <u-datetime-picker
+ :show="showStartDatePicker"
+ mode="date"
+ v-model="startDate"
+ @confirm="onStartDateConfirm"
+ @cancel="showStartDatePicker = false"
+ placeholder="閫夋嫨寮�濮嬫棩鏈�"
+ ></u-datetime-picker>
+
+ <!-- 缁撴潫鏃ユ湡閫夋嫨鍣� -->
+ <u-datetime-picker
+ :show="showEndDatePicker"
+ mode="date"
+ v-model="endDate"
+ @confirm="onEndDateConfirm"
+ @cancel="showEndDatePicker = false"
+ placeholder="閫夋嫨缁撴潫鏃ユ湡"
+ ></u-datetime-picker>
</view>
<view class="section-card metrics-card">
@@ -47,23 +71,11 @@
<view class="chart-content">
<view class="chart-area">
- <view
- class="echart-host"
- :id="chartDomId"
- :prop="chartOptionText"
- :change:prop="chartRenderer.renderChart"
- ></view>
- <view class="chart-fallback"></view>
- </view>
-
- <view class="legend-list">
- <view v-for="item in luggageDistribution" :key="item.name" class="legend-item">
- <view class="legend-left">
- <view class="legend-dot" :style="{ background: item.color }"></view>
- <text class="legend-name">{{ item.name }}</text>
- </view>
- <text class="legend-value">{{ item.value }} | {{ item.percent }}%</text>
- </view>
+ <qiun-data-charts
+ type="ring"
+ :opts="opts"
+ :chartData="chartData"
+ @getIndex="onRingChartClick" />
</view>
</view>
</view>
@@ -74,8 +86,12 @@
export default {
data() {
return {
- currentRange: 'custom',
- chartDomId: 'luggage-distribution-chart',
+ currentRange: 'today',
+ startDate: '',
+ endDate: '',
+ showStartDatePicker: false,
+ showEndDatePicker: false,
+ minEndDate: '',
quickTabs: [
{ label: '浠婃棩', value: 'today' },
{ label: '杩�7鏃�', value: '7days' },
@@ -84,83 +100,308 @@
{ label: '鑷畾涔�', value: 'custom' }
],
metrics: [
- { label: '瀵勫瓨璁㈠崟(涓�)', value: '125', icon: '/shop/static/icon/yingshou_ic_jicun@2x.png' },
- { label: '瀵勯�佽鍗�(涓�)', value: '30', icon: '/shop/static/icon/yingshou_ic_jisong@2x.png' },
- { label: '鎬昏鍗�(涓�)', value: '155', icon: '/shop/static/icon/yingshou_ic_zongdingdan@2x.png' },
- { label: '鎬诲畬鎴愯鍗�(涓�)', value: '143', icon: '/shop/static/icon/yingshou_ic_zongwancheng@2x.png' },
- { label: '鎬昏惀鏀�(鍏�)', value: '300,000.00', icon: '/shop/static/icon/yingshou_ic_zongyingshou@2x.png' },
- { label: '鍒嗗簵鍒嗘垚(鍏�)', value: '10,000.32', icon: '/shop/static/icon/yingshou_ic_fendian@2x.png' },
- { label: '閫�娆捐鍗�(涓�)', value: '10', icon: '/shop/static/icon/yingshou_ic_tuikuan@2x.png' },
- { label: '璐d换鎵f(鍏�)', value: '300.00', icon: '/shop/static/icon/yingshou_ic_koukuan@2x.png' }
+ { label: '瀵勫瓨璁㈠崟(涓�)', value: '0', icon: '/shop/static/icon/yingshou_ic_jicun@2x.png' },
+ { label: '瀵勯�佽鍗�(涓�)', value: '0', icon: '/shop/static/icon/yingshou_ic_jisong@2x.png' },
+ { label: '鎬昏鍗�(涓�)', value: '0', icon: '/shop/static/icon/yingshou_ic_zongdingdan@2x.png' },
+ { label: '鎬诲畬鎴愯鍗�(涓�)', value: '0', icon: '/shop/static/icon/yingshou_ic_zongwancheng@2x.png' },
+ { label: '鎬昏惀鏀�(鍏�)', value: '0.00', icon: '/shop/static/icon/yingshou_ic_zongyingshou@2x.png' },
+ { label: '鍒嗗簵鍒嗘垚(鍏�)', value: '0.00', icon: '/shop/static/icon/yingshou_ic_fendian@2x.png' },
+ { label: '閫�娆捐鍗�(涓�)', value: '0', icon: '/shop/static/icon/yingshou_ic_tuikuan@2x.png' },
+ { label: '璐d换鎵f(鍏�)', value: '0.00', icon: '/shop/static/icon/yingshou_ic_koukuan@2x.png' }
],
- luggageDistribution: [
- { name: '澶у彿琛屾潕绠�', value: 35, percent: 35, color: '#3B82F6' },
- { name: '鐗瑰ぇ鍙疯鏉庣', value: 20, percent: 20, color: '#64D7C7' },
- { name: '涓彿琛屾潕绠�', value: 18, percent: 18, color: '#FFD15C' },
- { name: '灏忓彿琛屾潕绠�', value: 12, percent: 12, color: '#FF8A47' },
- { name: '澶у彿鑳屽寘', value: 10, percent: 10, color: '#F54786' },
- { name: '灏忓彿鑳屽寘', value: 5, percent: 5, color: '#EE6CB2' }
- ]
- };
- },
- computed: {
- chartOptionText() {
- return JSON.stringify({
- color: this.luggageDistribution.map((item) => item.color),
- series: [
- {
- name: '琛屾潕绫诲瀷鍒嗗竷',
- type: 'pie',
- radius: ['58%', '78%'],
- center: ['50%', '50%'],
- avoidLabelOverlap: false,
- label: { show: false },
- labelLine: { show: false },
- itemStyle: {
- borderColor: '#ffffff',
- borderWidth: 4
- },
- data: this.luggageDistribution.map((item) => ({
- value: item.value,
- name: item.name
- }))
+ luggageDistribution: [],
+ selectedLuggageText: '',
+
+ chartData: {},
+ opts: {
+ rotate: false,
+ rotateLock: false,
+ padding: [5,5,5,5],
+ dataLabel: false,
+ dataLine: false,
+ enableScroll: false,
+ legend: {
+ show: true,
+ position: "right",
+ lineHeight: 25
+ },
+ title: {
+ show: false,
+ name: ''
+ },
+ subtitle: {
+ show: false,
+ name: ''
+ },
+ extra: {
+ ring: {
+ ringWidth: 20,
+ activeOpacity: 0.5,
+ activeRadius: 10,
+ offsetAngle: 0,
+ labelWidth: 15,
+ border: true,
+ borderWidth: 3,
+ borderColor: "#FFFFFF",
+ linearType: "custom"
}
- ]
- });
- }
- }
- };
-</script>
-
-<script>
- import echarts from "echarts"
-
- export default {
- data() {
- return {
- chart: null
- };
- },
- methods: {
- renderChart(optionText) {
- if (!optionText) {
- return;
+ }
}
+ }
+ },
+ onLoad() {
+ this.initDateRange();
+ this.getDriverKpiData();
+ },
+ watch: {
+ currentRange(newVal) {
+ if (newVal !== 'custom') {
+ this.initDateRange();
+ this.getDriverKpiData();
+ }
+ },
+ startDate(newVal) {
+ if (newVal) {
+ // 缁撴潫鏃ユ湡鏈�灏忎负寮�濮嬫棩鏈�
+ this.minEndDate = new Date(newVal).getTime();
+ } else {
+ this.minEndDate = '';
+ }
+ }
+ },
+ methods: {
+ formatDate(date) {
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
+ },
+ formatPickerDate(value) {
+ if (!value) {
+ return '';
+ }
- this.$nextTick(() => {
- const dom = document.getElementById('luggage-distribution-chart');
- if (!dom) {
+ if (typeof value === 'string') {
+ if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
+ return value;
+ }
+
+ if (/^\d+$/.test(value)) {
+ const timestampDate = new Date(Number(value));
+ return Number.isNaN(timestampDate.getTime()) ? '' : this.formatDate(timestampDate);
+ }
+
+ return value;
+ }
+
+ const date = new Date(value);
+ return Number.isNaN(date.getTime()) ? '' : this.formatDate(date);
+ },
+ isStartDateAfterEndDate(startDate, endDate) {
+ if (!startDate || !endDate) {
+ return false;
+ }
+
+ return new Date(startDate).getTime() > new Date(endDate).getTime();
+ },
+ initDateRange() {
+ const now = new Date();
+ const today = this.formatDate(now);
+
+ switch (this.currentRange) {
+ case 'today':
+ this.startDate = today;
+ this.endDate = today;
+ break;
+ case '7days': {
+ const start = new Date();
+ start.setDate(start.getDate() - 6);
+ this.startDate = this.formatDate(start);
+ this.endDate = today;
+ break;
+ }
+ case '30days': {
+ const start = new Date();
+ start.setDate(start.getDate() - 29);
+ this.startDate = this.formatDate(start);
+ this.endDate = today;
+ break;
+ }
+ case 'halfYear': {
+ const start = new Date();
+ start.setMonth(start.getMonth() - 6);
+ this.startDate = this.formatDate(start);
+ this.endDate = today;
+ break;
+ }
+ default:
+ break;
+ }
+ },
+ async getDriverKpiData() {
+ if (this.currentRange === 'custom' && (!this.startDate || !this.endDate)) {
+ uni.showToast({ title: '璇烽�夋嫨鏃ユ湡鑼冨洿', icon: 'none' });
return;
}
- if (!this.chart) {
- this.chart = echarts.init(dom);
+ uni.showLoading({ title: '鍔犺浇涓�...', mask: true });
+
+ try {
+ const [kpiRes, luggageRes] = await Promise.all([
+ this.$u.api.driverKpi({
+ startDate: this.startDate,
+ endDate: this.endDate
+ }),
+ this.$u.api.shopLuggageType({
+ startDate: this.startDate,
+ endDate: this.endDate
+ })
+ ]);
+
+ if (kpiRes.code === 200) {
+ this.processDriverKpiData(kpiRes.data);
+ }
+
+ if (luggageRes.code === 200) {
+ this.processLuggageTypeData(luggageRes.data);
+ }
+ } catch (err) {
+ console.error('鑾峰彇鏁版嵁澶辫触:', err);
+ uni.showToast({ title: '鑾峰彇鏁版嵁澶辫触', icon: 'none' });
+ } finally {
+ uni.hideLoading();
+ }
+ },
+ processDriverKpiData(data) {
+ console.log('KPI鏁版嵁:', data);
+
+ const formatAmount = (cents) => {
+ if (typeof cents !== 'number') return '0.00';
+ return (cents / 100).toFixed(2);
+ };
+
+ this.metrics = [
+ { label: '瀵勫瓨璁㈠崟(涓�)', value: data.localOrderCount || '0', icon: '/shop/static/icon/yingshou_ic_jicun@2x.png' },
+ { label: '瀵勯�佽鍗�(涓�)', value: data.remoteOrderCount || '0', icon: '/shop/static/icon/yingshou_ic_jisong@2x.png' },
+ { label: '鎬昏鍗�(涓�)', value: data.totalOrderCount || '0', icon: '/shop/static/icon/yingshou_ic_zongdingdan@2x.png' },
+ { label: '鎬诲畬鎴愯鍗�(涓�)', value: data.finishedOrderCount || '0', icon: '/shop/static/icon/yingshou_ic_zongwancheng@2x.png' },
+ { label: '鎬昏惀鏀�(鍏�)', value: formatAmount(data.totalRevenue), icon: '/shop/static/icon/yingshou_ic_zongyingshou@2x.png' },
+ { label: '鍒嗗簵鍒嗘垚(鍏�)', value: formatAmount(data.shopFeeTotal), icon: '/shop/static/icon/yingshou_ic_fendian@2x.png' },
+ { label: '閫�娆捐鍗�(涓�)', value: data.refundOrderCount || '0', icon: '/shop/static/icon/yingshou_ic_tuikuan@2x.png' },
+ { label: '璐d换鎵f(鍏�)', value: formatAmount(data.deductTotal), icon: '/shop/static/icon/yingshou_ic_koukuan@2x.png' }
+ ];
+ },
+ processLuggageTypeData(data) {
+ const colorList = ["#3B82F6", "#64D7C7", "#FFD15C", "#FF8A47", "#F54786", "#EE6666", "#91CB74", "#73C0DE", "#3CA272"];
+
+ this.luggageDistribution = data.map((item, index) => ({
+ name: item.luggageName,
+ value: item.orderCount,
+ count: item.luggageCount,
+ percent: item.orderCount > 0 ? Math.round((item.orderCount / data.reduce((sum, curr) => sum + curr.orderCount, 0)) * 100) : 0,
+ color: colorList[index % colorList.length]
+ }));
+
+ this.chartData = {
+ series: [
+ {
+ data: this.luggageDistribution.map(item => ({
+ name: item.name,
+ value: item.value
+ }))
+ }
+ ]
+ };
+
+ this.selectedLuggageText = '';
+ this.opts.title.name = '';
+ this.opts.subtitle.name = '';
+ },
+ onRingChartClick(e) {
+ const currentIndex = e && typeof e.currentIndex === 'number' ? e.currentIndex : -1;
+ const currentItem = this.luggageDistribution[currentIndex];
+
+ if (!currentItem) {
+ return;
}
- this.chart.setOption(JSON.parse(optionText), true);
- });
+ this.selectedLuggageText = `${currentItem.name}锛�${currentItem.count || 0}浠讹紝${currentItem.value || 0}鍗昤;
+ this.opts = {
+ ...this.opts,
+ title: {
+ ...this.opts.title,
+ name: `${currentItem.count || 0}浠禶,
+ fontSize: 18,
+ color: '#1f2430'
+ },
+ subtitle: {
+ ...this.opts.subtitle,
+ name: currentItem.name,
+ fontSize: 11,
+ color: '#7a828f'
+ }
+ };
+ uni.showToast({
+ title: `${currentItem.name} ${currentItem.count || 0}浠禶,
+ icon: 'none'
+ });
+ },
+ onStartDateConfirm(e) {
+ const nextStartDate = this.formatPickerDate(e.value);
+
+ if (this.isStartDateAfterEndDate(nextStartDate, this.endDate)) {
+ this.startDate = '';
+ this.showStartDatePicker = false;
+ uni.showToast({ title: '寮�濮嬫棩鏈熶笉鑳藉ぇ浜庢埅姝㈡棩鏈�', icon: 'none' });
+ return;
+ }
+
+ this.startDate = nextStartDate;
+ this.showStartDatePicker = false;
+ this.minEndDate = nextStartDate ? new Date(nextStartDate).getTime() : '';
+
+ if (this.endDate) {
+ this.getDriverKpiData();
+ }
+ },
+ onEndDateConfirm(e) {
+ const nextEndDate = this.formatPickerDate(e.value);
+
+ if (this.isStartDateAfterEndDate(this.startDate, nextEndDate)) {
+ this.endDate = '';
+ this.showEndDatePicker = false;
+ uni.showToast({ title: '鎴鏃ユ湡涓嶈兘灏忎簬寮�濮嬫棩鏈�', icon: 'none' });
+ return;
+ }
+
+ this.endDate = nextEndDate;
+ this.showEndDatePicker = false;
+
+ if (this.startDate) {
+ this.getDriverKpiData();
+ }
+ },
+ confirmDateRange() {
+ if (!this.startDate || !this.endDate) {
+ uni.showToast({ title: '璇烽�夋嫨瀹屾暣鐨勬棩鏈熻寖鍥�', icon: 'none' });
+ return;
+ }
+
+ const start = new Date(this.startDate);
+ const end = new Date(this.endDate);
+
+ if (start > end) {
+ uni.showToast({ title: '寮�濮嬫棩鏈熶笉鑳藉ぇ浜庢埅姝㈡棩鏈�', icon: 'none' });
+ return;
+ }
+
+ const oneYear = 365 * 24 * 60 * 60 * 1000;
+ if (end - start > oneYear) {
+ uni.showToast({ title: '鏃ユ湡鍖洪棿涓嶈兘瓒呰繃涓�骞�', icon: 'none' });
+ return;
+ }
+
+ this.showDatePicker = false;
+ this.getDriverKpiData();
+ }
}
- }
};
</script>
@@ -198,6 +439,67 @@
font-weight: 500;
text-align: center;
color: #50555f;
+ }
+
+ /* 鏃ユ湡閫夋嫨鍣ㄦ牱寮� */
+ .date-picker-wrap {
+ padding: 24rpx;
+ background: #ffffff;
+ border-radius: 24rpx 24rpx 0 0;
+ }
+
+ .date-picker-title {
+ font-size: 32rpx;
+ font-weight: 500;
+ color: #333333;
+ text-align: center;
+ margin-bottom: 32rpx;
+ }
+
+ .date-picker-content {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 32rpx;
+ gap: 24rpx;
+ }
+
+ .date-picker-separator {
+ font-size: 28rpx;
+ color: #999999;
+ padding: 0 16rpx;
+ }
+
+ .date-picker-footer {
+ display: flex;
+ gap: 16rpx;
+ padding-top: 24rpx;
+ border-top: 1rpx solid #eeeeee;
+ }
+
+ .date-picker-btn {
+ flex: 1;
+ height: 72rpx;
+ line-height: 72rpx;
+ text-align: center;
+ border-radius: 36rpx;
+ font-size: 28rpx;
+ transition: all 0.3s;
+ }
+
+ .cancel-btn {
+ background: #f5f7fb;
+ color: #666666;
+ }
+
+ .confirm-btn {
+ background: linear-gradient(135deg, #ff8b14 0%, #ff4d0a 100%);
+ color: #ffffff;
+ }
+
+ .date-text {
+ font-size: 28rpx;
+ color: #333333;
}
.quick-tab.active {
@@ -264,9 +566,9 @@
flex-shrink: 0;
width: 68rpx;
height: 68rpx;
- border-radius: 14rpx;
- background: #ffffff;
- box-shadow: 0 6rpx 18rpx rgba(20, 42, 74, 0.08);
+ // border-radius: 14rpx;
+ // background: #ffffff;
+ // box-shadow: 0 6rpx 18rpx rgba(20, 42, 74, 0.08);
box-sizing: border-box;
}
@@ -309,71 +611,20 @@
.chart-area {
position: relative;
flex-shrink: 0;
- width: 270rpx;
- height: 270rpx;
+ width: 100%;
+ height: 350rpx;
}
- .echart-host,
- .chart-fallback {
+ .chart-detail {
+ margin-top: 20rpx;
+ font-size: 28rpx;
+ line-height: 40rpx;
+ color: #5f6775;
+ text-align: center;
+ }
+
+ .echart-host {
width: 100%;
height: 100%;
- }
-
- .chart-fallback {
- position: absolute;
- left: 0;
- top: 0;
- border-radius: 50%;
- background: conic-gradient(#3b82f6 0 35%, #64d7c7 35% 55%, #ffd15c 55% 73%, #ff8a47 73% 85%, #f54786 85% 95%, #ee6cb2 95% 100%);
- -webkit-mask: radial-gradient(circle, transparent 0 42%, #000 43%);
- mask: radial-gradient(circle, transparent 0 42%, #000 43%);
- opacity: 0.2;
- pointer-events: none;
- }
-
- .legend-list {
- flex: 1;
- min-width: 0;
- }
-
- .legend-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12rpx;
- }
-
- .legend-item + .legend-item {
- margin-top: 18rpx;
- }
-
- .legend-left {
- display: flex;
- align-items: center;
- gap: 14rpx;
- min-width: 0;
- }
-
- .legend-dot {
- flex-shrink: 0;
- width: 20rpx;
- height: 20rpx;
- border-radius: 50%;
- }
-
- .legend-name,
- .legend-value {
- font-size: 30rpx;
- line-height: 42rpx;
- }
-
- .legend-name {
- color: #6f7683;
- }
-
- .legend-value {
- flex-shrink: 0;
- font-weight: 600;
- color: #2f333d;
}
</style>
--
Gitblit v1.9.3