shish 3 дней назад
Родитель
Сommit
c0524ff018

+ 48 - 11
ghsApp/src/admin/stat/cgItem.vue

@@ -1,3 +1,6 @@
+<!--
+  采购花材排行:入库日净量 + 隔天通过日扣数量/金额(可为负)。
+-->
 <template>
 <view class="app-content">
 	<view class="ex-page">
@@ -18,12 +21,26 @@
 						<t-th><view @click="rankByFn('num')">总数量<i class="iconfont iconsanjiao_xia"></i></view></t-th>
 						<t-th><view @click="rankByFn('amount')">总金额<i class="iconfont iconsanjiao_xia"></i></view></t-th>
 						</t-tr>
-						<block v-if="profile.list && !$util.isEmpty(profile.list)">
-							<view v-for="(item, index) in profile.list" :key="index">
+						<view style="color:#1c89e0;font-size:24upx;margin:10upx;line-height:1.5;">
+							数量、金额已按售后扣减(隔天售后记在通过日,可为负)。入库日不含隔天退货量。
+						</view>
+						<view v-if="Number(nextDayDeduct) > 0" style="color:#e67e22;font-size:26upx;margin:0 10upx 10upx 10upx;font-weight:bold;">
+							本时段隔天售后扣减:¥{{ nextDayDeduct }}
+						</view>
+						<block v-if="itemList && !$util.isEmpty(itemList)">
+							<view v-for="(item, index) in itemList" :key="index">
 								<t-tr>
 									<t-td><view style="color:black;font-size:26upx;">{{item.name}}</view></t-td>
-									<t-td><view style="color:black;font-size:28upx;">{{item.num ? parseFloat(item.num) : 0}}</view></t-td>
-									<t-td><view style="color:black;font-size:28upx;">¥{{item.amount ? parseFloat(item.amount) : 0}}</view></t-td>
+									<t-td>
+										<view :style="Number(item.num) < 0 ? 'color:#e74c3c;font-weight:bold;font-size:28upx;' : 'color:black;font-size:28upx;'">
+											{{ formatNum(item.num) }}
+										</view>
+									</t-td>
+									<t-td>
+										<view :style="Number(item.amount) < 0 ? 'color:#e74c3c;font-weight:bold;font-size:28upx;' : 'color:black;font-size:28upx;'">
+											¥{{ formatNum(item.amount) }}
+										</view>
+									</t-td>
 								</t-tr>
 							</view>
 						</block>
@@ -44,8 +61,21 @@ import AppSearchModule from "@/components/module/app-search";
 export default {
 	components: { AppWrapperEmpty,ShopSelect,DateSelect,AppSearchModule},
 	mixins: [list],
-	computed: {	
-
+	computed: {
+		/** 过滤全零行;搜索按名称/拼音 */
+		itemList() {
+			const kw = (this.searchContent || '').trim().toLowerCase()
+			const list = (this.profile && this.profile.list) ? this.profile.list : []
+			return list.filter(item => {
+				const num = Number(item.num) || 0
+				const amount = Number(item.amount) || 0
+				if (num === 0 && amount === 0) return false
+				if (!kw) return true
+				const name = (item.name || '').toLowerCase()
+				const py = (item.py || '').toLowerCase()
+				return name.indexOf(kw) >= 0 || py.indexOf(kw) >= 0
+			})
+		}
 	},
 	data() {
 		return {
@@ -55,7 +85,9 @@ export default {
 				endTime: "",
 				sort:"num"
 			},
-			profile:[]
+			profile:{},
+			searchContent: '',
+			nextDayDeduct: 0
 		};
 	},
 	onPullDownRefresh() {
@@ -65,8 +97,12 @@ export default {
 		});
 	},
 	methods: {
+		formatNum(val) {
+			const n = parseFloat(val)
+			return isNaN(n) ? 0 : n
+		},
 		searchFn(e){
-
+			this.searchContent = e
 		},
 		selectShopFn(val){
 			this.init();
@@ -85,9 +121,10 @@ export default {
 			let that = this
 			uni.showLoading()
 			this.params.sort = rank
-			getStatCgItem(this.params).then(res => {
+			return getStatCgItem(this.params).then(res => {
 				uni.hideLoading()
-				that.profile = res.data
+				that.profile = res.data || {}
+				that.nextDayDeduct = that.formatNum(res.data && res.data.nextDayDeduct)
 			})
 		}
 	}
@@ -174,4 +211,4 @@ export default {
 		}
 	}
 }
-</style>
+</style>

+ 46 - 10
ghsApp/src/admin/stat/cgStat.vue

@@ -1,3 +1,6 @@
+<!--
+  供货业绩:按供货商汇总采购金额(含隔天售后通过日扣减,金额可为负)。
+-->
 <template>
 <view class="app-content">
 	<view class="ex-page">
@@ -14,12 +17,24 @@
 				<block>
 					<t-table :headerBackgroundColor="'#F0F2F6'">
 						<t-tr header><t-th>供货商</t-th><t-th>订单数</t-th><t-th>金额</t-th><t-th>操作</t-th></t-tr>
-						<view style="font-size:30upx;margin:10upx 0 10upx 10upx;">总金额:{{ totalActPrice }}</view>
-						<view v-for="(item, index) in storageData" :key="index">
+						<view style="font-size:30upx;margin:10upx 0 10upx 10upx;">总金额:
+							<text :style="totalActPrice < 0 ? 'color:#e74c3c;font-weight:bold;' : ''">{{ totalActPrice }}</text>
+						</view>
+						<view style="color:#1c89e0;font-size:24upx;margin:0 10upx 10upx 10upx;line-height:1.5;">
+							金额已按售后扣减(隔天售后记在通过日,可为负;订单数不减)。
+						</view>
+						<view v-if="Number(nextDayDeduct) > 0" style="color:#e67e22;font-size:26upx;margin:0 10upx 10upx 10upx;font-weight:bold;">
+							本时段隔天售后扣减:¥{{ nextDayDeduct }}
+						</view>
+						<view v-for="(item, index) in displayList" :key="index">
 							<t-tr>
 								<t-td><view>{{item.name}}</view></t-td>
-								<t-td><view>{{parseFloat(item.num)}}</view></t-td>
-								<t-td><view>{{parseFloat(item.actPrice)}}</view></t-td>
+								<t-td><view>{{ formatNum(item.num) }}</view></t-td>
+								<t-td>
+									<view :style="Number(item.actPrice) < 0 ? 'color:#e74c3c;font-weight:bold;' : ''">
+										{{ formatNum(item.actPrice) }}
+									</view>
+								</t-td>
 								<t-td></t-td>
 							</t-tr>
 						</view>
@@ -42,7 +57,20 @@ export default {
 	components: { AppWrapperEmpty,ShopSelect,AppSearchModule,DateSelect },
 	mixins: [list],
 	computed: {		
-		...mapGetters(["getDictionariesInfo","getLoginInfo"])
+		...mapGetters(["getDictionariesInfo","getLoginInfo"]),
+		/** 有订单或金额非零才展示,避免全零行淹没隔天负金额 */
+		displayList() {
+			const kw = (this.searchContent || '').trim().toLowerCase()
+			return (this.storageData || []).filter(item => {
+				const num = Number(item.num) || 0
+				const act = Number(item.actPrice) || 0
+				if (num === 0 && act === 0) return false
+				if (!kw) return true
+				const name = (item.name || '').toLowerCase()
+				const py = (item.py || item.mobile || '').toLowerCase()
+				return name.indexOf(kw) >= 0 || py.indexOf(kw) >= 0
+			})
+		}
 	},
 	data() {
 		return {
@@ -63,7 +91,8 @@ export default {
 			params:{ itemId: '', searchTime:'', startTime: "", endTime: "", shopId: "", },
 			storageData:[],
 			searchContent:'',
-			totalActPrice:0
+			totalActPrice:0,
+			nextDayDeduct: 0
 		};
 	},
 	onPullDownRefresh() {
@@ -79,9 +108,13 @@ export default {
 	},
 	methods: {
 		...mapActions(['setUserShopAll']),
+		/** 数字展示:不用 truthy,避免 0/负数被当成空 */
+		formatNum(val) {
+			const n = parseFloat(val)
+			return isNaN(n) ? 0 : n
+		},
 		searchFn(e){
 			this.searchContent = e;
-			this.filterStat()
 		},
 		selectShopFn(val){
 			this.params.shopId = val.id;
@@ -98,13 +131,16 @@ export default {
 		getList() {
 			let that = this
 			uni.showLoading()
-			getStatCgGhsProfile(this.params).then(res => {
+			return getStatCgGhsProfile(this.params).then(res => {
 				uni.hideLoading()
 				if (!this.$util.isEmpty(res.data.list)) {
 					that.storageData = res.data.list
-					that.totalActPrice = res.data.totalActPrice?parseFloat(res.data.totalActPrice):0
+					that.totalActPrice = that.formatNum(res.data.totalActPrice)
+					that.nextDayDeduct = that.formatNum(res.data.nextDayDeduct)
 				}else{
 					that.storageData = []
+					that.totalActPrice = 0
+					that.nextDayDeduct = 0
 				}
 			})
 		}
@@ -196,4 +232,4 @@ export default {
 		}
 	}
 }
-</style>
+</style>

+ 37 - 10
ghsApp/src/admin/stat/cgYj.vue

@@ -1,3 +1,6 @@
+<!--
+  采购人业绩:按采购员汇总(隔天售后通过日扣金额,订单数不减;金额可为负)。
+-->
 <template>
 <view class="app-content">
 	<view class="ex-page">
@@ -18,12 +21,22 @@
 						<t-th>订单数</t-th>
 						<t-th>金额</t-th>
 						</t-tr>
-						<block v-if="profile.staffCg && !$util.isEmpty(profile.staffCg)">
-							<view v-for="(cgItem, index) in profile.staffCg" :key="index">
+						<view style="color:#1c89e0;font-size:24upx;margin:10upx;line-height:1.5;">
+							金额已按售后扣减(隔天售后记在通过日,可为负;订单数不减)。
+						</view>
+						<view v-if="Number(nextDayDeduct) > 0" style="color:#e67e22;font-size:26upx;margin:0 10upx 10upx 10upx;font-weight:bold;">
+							本时段隔天售后扣减:¥{{ nextDayDeduct }}
+						</view>
+						<block v-if="staffList && !$util.isEmpty(staffList)">
+							<view v-for="(cgItem, index) in staffList" :key="index">
 								<t-tr>
 									<t-td><view style="color:black;font-size:26upx;">{{cgItem.cgStaffName?cgItem.cgStaffName:'未记名'}}</view></t-td>
-									<t-td><view style="color:black;font-size:28upx;">{{cgItem.num?parseFloat(cgItem.num):0}}</view></t-td>
-									<t-td><view style="color:black;font-size:28upx;">¥{{cgItem.amount?parseFloat(cgItem.amount):0}}</view></t-td>
+									<t-td><view style="color:black;font-size:28upx;">{{ formatNum(cgItem.num) }}</view></t-td>
+									<t-td>
+										<view :style="Number(cgItem.amount) < 0 ? 'color:#e74c3c;font-weight:bold;font-size:28upx;' : 'color:black;font-size:28upx;'">
+											¥{{ formatNum(cgItem.amount) }}
+										</view>
+									</t-td>
 								</t-tr>
 							</view>
 						</block>
@@ -44,8 +57,16 @@ import AppSearchModule from "@/components/module/app-search";
 export default {
 	components: { AppWrapperEmpty,ShopSelect,DateSelect,AppSearchModule},
 	mixins: [list],
-	computed: {	
-
+	computed: {
+		/** 过滤全零行,保留隔天产生的负金额行 */
+		staffList() {
+			const list = (this.profile && this.profile.staffCg) ? this.profile.staffCg : []
+			return list.filter(item => {
+				const num = Number(item.num) || 0
+				const amount = Number(item.amount) || 0
+				return num !== 0 || amount !== 0
+			})
+		}
 	},
 	data() {
 		return {
@@ -55,7 +76,8 @@ export default {
 				endTime: "",
 				sort:"num"
 			},
-			profile:[]
+			profile:{},
+			nextDayDeduct: 0
 		};
 	},
 	onPullDownRefresh() {
@@ -65,6 +87,10 @@ export default {
 		});
 	},
 	methods: {
+		formatNum(val) {
+			const n = parseFloat(val)
+			return isNaN(n) ? 0 : n
+		},
 		searchFn(e){
 
 		},
@@ -85,9 +111,10 @@ export default {
 			let that = this
 			uni.showLoading()
 			this.params.sort = rank
-			getStatCgYj(this.params).then(res => {
+			return getStatCgYj(this.params).then(res => {
 				uni.hideLoading()
-				that.profile = res.data
+				that.profile = res.data || {}
+				that.nextDayDeduct = that.formatNum(res.data && res.data.nextDayDeduct)
 			})
 		}
 	}
@@ -174,4 +201,4 @@ export default {
 		}
 	}
 }
-</style>
+</style>

+ 3 - 1
ghsApp/src/admin/stat/classProfit.vue

@@ -60,7 +60,9 @@
 							<t-td><view style="color:black;font-size:28upx;color:#3385FF;">{{profit.totalProfit}}</view></t-td>
 						</t-tr>
 
-						<view style="margin-left:60upx;">以上暂未统计零售端【花掌柜】数据</view>
+						<view style="margin:10upx 60upx;color:#1c89e0;font-size:24upx;line-height:1.5;">
+							采购成本、销售收入已按售后扣减(隔天售后记在通过日,分类成本/收入可为负)。采购仅退款、销售仅退款含隔天通过日金额。与 PC 收支分类同源接口。
+						</view>
 
 						<view v-if="!$util.isEmpty(tbSaleIncome)">
 							<t-tr>

+ 60 - 115
ghsApp/src/admin/stat/customSale.vue

@@ -1,3 +1,6 @@
+<!--
+  小程序客户业绩:按客户汇总金额/成本/利润;隔天售后记通过日扣减(后端 statCustomCg)。
+-->
 <template>
 <view class="app-content">
 	<view class="ex-page">
@@ -15,49 +18,49 @@
 			<view class="space-view ex-table">
 				<block>
 					<t-table :headerBackgroundColor="'#F0F2F6'">
-						<!-- 合计在表头上方:五项同一行,颜色用主题蓝,和灰色表头区分 -->
-						<view class="stat-summary">
-							<view class="stat-summary-item">
-								<view class="stat-summary-label">笔数</view>
-								<view class="stat-summary-value">{{profile.totalNum||0}}</view>
-							</view>
-							<view class="stat-summary-item">
-								<view class="stat-summary-label">扎数</view>
-								<view class="stat-summary-value">{{totalCount}}</view>
-							</view>
-							<view class="stat-summary-item">
-								<view class="stat-summary-label">金额</view>
-								<view class="stat-summary-value">¥{{profile.totalAmount||0}}</view>
-							</view>
-							<view class="stat-summary-item">
-								<view class="stat-summary-label">成本</view>
-								<view class="stat-summary-value">¥{{profile.totalCost||0}}</view>
-							</view>
-							<view class="stat-summary-item">
-								<view class="stat-summary-label">利润</view>
-								<view class="stat-summary-value">¥{{profile.totalProfit||0}}</view>
-							</view>
-						</view>
-						<!-- 客户名改为行标题独占一行,表头只保留 5 列指标,避免 6 列均分把长名称挤成单字换行 -->
 						<t-tr header>
+							<t-th>客户</t-th>
 							<t-th>笔数</t-th>
 							<t-th>扎数</t-th>
 							<t-th>金额</t-th>
 							<t-th>成本</t-th>
 							<t-th>利润</t-th>
 						</t-tr>
-							<block v-if="profile.list && !$util.isEmpty(profile.list)">
-								<view v-for="(item, index) in profile.list" :key="index" class="sale-item">
-									<view class="sale-item-name">{{item.customName?item.customName:'-'}}</view>
-									<t-tr>
-										<t-td><view class="sale-cell">{{parseFloat(item.num)}}笔</view></t-td>
-										<t-td><view class="sale-cell">{{item.count?parseFloat(item.count):0}}扎</view></t-td>
-										<t-td><view class="sale-cell">¥{{parseFloat(item.amount)}}</view></t-td>
-										<t-td><view class="sale-cell">¥{{parseFloat(item.cost)}}</view></t-td>
-										<t-td><view class="sale-cell">¥{{parseFloat(item.profit)}}</view></t-td>
-									</t-tr>
-								</view>
-							</block>
+						<view style="color:#1c89e0;font-size:24upx;margin:10upx;line-height:1.5;">
+							金额、利润已按售后扣减(隔天售后记在通过日,可为负;笔数不减)。扎数:当天售后未从开单扎数扣除,隔天退货会在通过日扣扎数。成本仍取开单剩余成本。
+						</view>
+						<view v-if="Number(nextDayDeduct) > 0" style="color:#e67e22;font-size:26upx;margin:0 10upx 10upx 10upx;font-weight:bold;">
+							本时段隔天售后扣减:¥{{ nextDayDeduct }}
+						</view>
+						<block v-if="displayList && !$util.isEmpty(displayList)">
+							<view v-for="(item, index) in displayList" :key="index">
+								<t-tr>
+									<t-td><view style="color:#333333;font-size:28upx;">{{item.customName?item.customName:'-'}}</view></t-td>
+									<t-td><view style="color:#333333;font-size:28upx;">{{parseFloat(item.num)||0}}</view></t-td>
+									<t-td>
+										<!-- 小程序 :style 不能调方法,用三元写死样式 -->
+										<view :style="Number(item.count) < 0 ? 'color:#e74c3c;font-weight:bold;font-size:28upx;' : 'color:#333333;font-size:28upx;'">{{formatNum(item.count)}}</view>
+									</t-td>
+									<t-td>
+										<view :style="Number(item.amount) < 0 ? 'color:#e74c3c;font-weight:bold;font-size:28upx;' : 'color:#333333;font-size:28upx;'">¥{{formatNum(item.amount)}}</view>
+									</t-td>
+									<t-td><view style="color:#333333;font-size:28upx;">¥{{formatNum(item.cost)}}</view></t-td>
+									<t-td>
+										<view :style="Number(item.profit) < 0 ? 'color:#e74c3c;font-weight:bold;font-size:28upx;' : 'color:#333333;font-size:28upx;'">¥{{formatNum(item.profit)}}</view>
+									</t-td>
+								</t-tr>
+							</view>
+						</block>
+						<t-tr><t-td>
+							<view :style="Number(profile.totalAmount) < 0 || Number(profile.totalProfit) < 0 ? 'color:#e74c3c;font-weight:bold;font-size:28upx;' : 'color:black;font-weight:bold;font-size:28upx;'">
+								{{profile.totalNum||0}}笔¥{{formatNum(profile.totalAmount)}} 成本¥{{ formatNum(profile.totalCost) }} 利润¥{{ formatNum(profile.totalProfit) }}
+							</view>
+						</t-td></t-tr>
+						<t-tr><t-td>
+							<view style="color:#1c89e0;font-size:28upx;">
+								从2025年11月20号起是准确数据,之前的数据不准确
+							</view>
+						</t-td></t-tr>
 					</t-table>
 				</block>
 			</view>
@@ -67,10 +70,6 @@
 </view>
 </template>
 <script>
-/**
- * ghsApp 客户业绩:按客户汇总笔数/扎数/金额/成本/利润。
- * 工作台「客户业绩」进入;长客户名单独占满一行,避免与指标挤在同一行导致排版走样。
- */
 import AppWrapperEmpty from "@/components/app-wrapper-empty";
 import { getStatKhCgProfile } from "@/api/stat/index";
 import { list } from "@/mixins";
@@ -83,35 +82,25 @@ export default {
 	mixins: [list],
 	computed: {
 		...mapGetters(["getDictionariesInfo","getLoginInfo"]),
-		// 总扎数:优先用接口 totalCount;list 经 PHP 关联数组 JSON 后可能是对象,不能直接 forEach
-		totalCount() {
-			const profile = Array.isArray(this.profile) ? {} : (this.profile || {})
-			if (profile.totalCount != null && profile.totalCount !== '') {
-				return parseFloat(profile.totalCount) || 0
-			}
-			const list = profile.list
-			let rows = []
-			if (Array.isArray(list)) {
-				rows = list
-			} else if (list && typeof list === 'object') {
-				rows = Object.keys(list).map(key => list[key])
+		/** 列表统一成数组,避免接口返回对象时 v-for 异常 */
+		displayList() {
+			const raw = (this.profile && this.profile.list) || [];
+			if (Array.isArray(raw)) {
+				return raw;
 			}
-			let sum = 0
-			for (let i = 0; i < rows.length; i++) {
-				sum += parseFloat(rows[i] && rows[i].count) || 0
-			}
-			return parseFloat(sum.toFixed(2)) || 0
-		}
+			return Object.keys(raw).map(k => raw[k]);
+		},
 	},
 	data() {
 		return {
 			params:{
-				searchTime:'',
+				searchTime:'today',
 				startTime: "",
 				endTime: "",
 			},
-			profile:[],
+			profile:{},
 			searchText:'',
+			nextDayDeduct: 0,
 		};
 	},
 	onPullDownRefresh() {
@@ -122,6 +111,10 @@ export default {
 	},
 	methods: {
 		...mapActions(['setUserShopAll']),
+		formatNum(val) {
+			const n = parseFloat(val);
+			return isNaN(n) ? 0 : n;
+		},
 		searchFn(e){
 			this.searchText = e;
 		},
@@ -135,9 +128,13 @@ export default {
 		getList() {
 			let that = this
 			uni.showLoading()
-			getStatKhCgProfile(this.params).then(res => {
+			return getStatKhCgProfile(this.params).then(res => {
+				uni.hideLoading()
+				const data = (res && res.data) ? res.data : {}
+				that.profile = data
+				that.nextDayDeduct = that.formatNum(data.nextDayDeduct)
+			}).catch(() => {
 				uni.hideLoading()
-				that.profile = res.data
 			});
 		}
 	}
@@ -206,58 +203,6 @@ export default {
 			::v-deep.icon-info {
 				color: #333333;
 			}
-			/* 名称与指标拆成两行:名称用满屏宽完整展示,指标 5 列对齐表头且不再被撑高 */
-			.sale-item {
-				border-bottom: 1upx solid #eeeeee;
-				::v-deep .t-tr {
-					border-bottom: none;
-					padding-top: 8upx;
-					padding-bottom: 16upx;
-				}
-			}
-			/* 客户名用主题蓝加粗,和下面黑色指标数字区分开,当作每组数据的行标题 */
-			.sale-item-name {
-				padding: 20upx 40upx 4upx;
-				color: #3385FF;
-				font-size: 28upx;
-				font-weight: bold;
-				line-height: 1.4;
-				word-break: break-all;
-			}
-			.sale-cell {
-				color: #333333;
-				font-size: 28upx;
-				white-space: nowrap;
-			}
-			/* 合计五项同一行;排不下才换行。数值用主题蓝,和灰色表头区分 */
-			.stat-summary {
-				display: flex;
-				flex-direction: row;
-				flex-wrap: wrap;
-				background: #F7F8FA;
-				padding: 16upx 8upx 4upx;
-				border-bottom: 1upx solid #eeeeee;
-			}
-			.stat-summary-item {
-				flex: 1;
-				min-width: 120upx;
-				display: flex;
-				flex-direction: column;
-				align-items: center;
-				margin-bottom: 12upx;
-			}
-			.stat-summary-label {
-				font-size: 24upx;
-				color: #666666;
-				margin-bottom: 6upx;
-			}
-			.stat-summary-value {
-				font-size: 24upx;
-				font-weight: bold;
-				color: #333333;
-				text-align: center;
-				word-break: break-all;
-			}
 		}
 	}
 	.bar-view {
@@ -280,4 +225,4 @@ export default {
 		}
 	}
 }
-</style>
+</style>

+ 20 - 109
ghsApp/src/admin/stat/distSale.vue

@@ -15,31 +15,8 @@
 			<view class="space-view ex-table">
 				<block>
 					<t-table :headerBackgroundColor="'#F0F2F6'">
-						<!-- 合计在表头上方:五项同一行,和客户业绩页同一套排版 -->
-						<view class="stat-summary">
-							<view class="stat-summary-item">
-								<view class="stat-summary-label">笔数</view>
-								<view class="stat-summary-value">{{profile.totalNum||0}}</view>
-							</view>
-							<view class="stat-summary-item">
-								<view class="stat-summary-label">扎数</view>
-								<view class="stat-summary-value">{{totalCount}}</view>
-							</view>
-							<view class="stat-summary-item">
-								<view class="stat-summary-label">收入</view>
-								<view class="stat-summary-value">¥{{profile.totalAmount||0}}</view>
-							</view>
-							<view class="stat-summary-item">
-								<view class="stat-summary-label">成本</view>
-								<view class="stat-summary-value">¥{{profile.totalCost||0}}</view>
-							</view>
-							<view class="stat-summary-item">
-								<view class="stat-summary-label">利润</view>
-								<view class="stat-summary-value">¥{{profile.totalProfit||0}}</view>
-							</view>
-						</view>
-						<!-- 片区名改为行标题独占一行,表头只保留 5 列指标,避免长名称把行高撑乱 -->
 						<t-tr header>
+							<t-th>片区</t-th>
 							<t-th>笔数</t-th>
 							<t-th>扎数</t-th>
 							<t-th>金额</t-th>
@@ -47,17 +24,28 @@
 							<t-th>利润</t-th>
 						</t-tr>
 							<block v-if="profile.list && !$util.isEmpty(profile.list)">
-								<view v-for="(item, index) in profile.list" :key="index" class="sale-item">
-									<view class="sale-item-name">{{item.distName?item.distName:'-'}}</view>
+								<view v-for="(item, index) in profile.list" :key="index">
 									<t-tr>
-										<t-td><view class="sale-cell">{{parseFloat(item.num)}}笔</view></t-td>
-										<t-td><view class="sale-cell">{{item.count?parseFloat(item.count):0}}扎</view></t-td>
-										<t-td><view class="sale-cell">¥{{parseFloat(item.amount)}}</view></t-td>
-										<t-td><view class="sale-cell">¥{{parseFloat(item.cost)}}</view></t-td>
-										<t-td><view class="sale-cell">¥{{parseFloat(item.profit)}}</view></t-td>
+										<t-td><view style="color:#333333;font-size:28upx;">{{item.distName?item.distName:'-'}}</view></t-td>
+										<t-td><view style="color:#333333;font-size:28upx;">{{parseFloat(item.num)}}</view></t-td>
+										<t-td><view style="color:#333333;font-size:28upx;">{{item.count?parseFloat(item.count):0}}</view></t-td>
+										<t-td><view style="color:#333333;font-size:28upx;">¥{{parseFloat(item.amount)}}</view></t-td>
+										<t-td><view style="color:#333333;font-size:28upx;">¥{{parseFloat(item.cost)}}</view></t-td>
+										<t-td><view style="color:#333333;font-size:28upx;">¥{{parseFloat(item.profit)}}</view></t-td>
 									</t-tr>
 								</view>
 							</block>
+							<t-tr><t-td><view style="color:black;font-weight:bold;font-size:28upx;">{{profile.totalNum||0}}笔¥{{profile.totalAmount||0}} 成本¥{{ profile.totalCost||0 }} 利润¥{{ profile.totalProfit||0 }}</view></t-td></t-tr>
+							<t-tr><t-td>
+								<view style="color:#1c89e0;font-size:28upx;">
+									从2025年11月20号起是准确数据,之前的数据不准确
+								</view>
+							</t-td></t-tr>
+							<t-tr><t-td>
+								<view style="color:#1c89e0;font-size:28upx;">
+									金额、利润已按售后扣减(隔天售后记在通过日,可为负)。扎数:当天售后未从开单扎数扣除,隔天退货会在通过日扣扎数。成本仍取开单剩余成本。
+								</view>
+							</t-td></t-tr>
 					</t-table>
 				</block>
 			</view>
@@ -67,10 +55,6 @@
 </view>
 </template>
 <script>
-/**
- * ghsApp 片区业绩:按片区汇总笔数/扎数/金额/成本/利润。
- * 工作台进入;排版对齐客户业绩页,长片区名单独占满一行。
- */
 import AppWrapperEmpty from "@/components/app-wrapper-empty";
 import { getStatDist } from "@/api/stat";
 import { list } from "@/mixins";
@@ -81,28 +65,7 @@ import {mapActions, mapGetters} from "vuex";
 export default {
 	components: { AppWrapperEmpty,ShopSelect,AppSearchModule,DateSelect },
 	mixins: [list],
-	computed: {
-		...mapGetters(["getDictionariesInfo","getLoginInfo"]),
-		// 总扎数:优先用接口 totalCount;list 经 PHP 关联数组 JSON 后可能是对象,不能直接 forEach
-		totalCount() {
-			const profile = Array.isArray(this.profile) ? {} : (this.profile || {})
-			if (profile.totalCount != null && profile.totalCount !== '') {
-				return parseFloat(profile.totalCount) || 0
-			}
-			const list = profile.list
-			let rows = []
-			if (Array.isArray(list)) {
-				rows = list
-			} else if (list && typeof list === 'object') {
-				rows = Object.keys(list).map(key => list[key])
-			}
-			let sum = 0
-			for (let i = 0; i < rows.length; i++) {
-				sum += parseFloat(rows[i] && rows[i].count) || 0
-			}
-			return parseFloat(sum.toFixed(2)) || 0
-		}
-	},
+	computed: {		...mapGetters(["getDictionariesInfo","getLoginInfo"])	},
 	data() {
 		return {
 			params:{
@@ -206,58 +169,6 @@ export default {
 			::v-deep.icon-info {
 				color: #333333;
 			}
-			/* 名称与指标拆成两行:片区名用满屏宽完整展示,指标 5 列对齐表头 */
-			.sale-item {
-				border-bottom: 1upx solid #eeeeee;
-				::v-deep .t-tr {
-					border-bottom: none;
-					padding-top: 8upx;
-					padding-bottom: 16upx;
-				}
-			}
-			/* 片区名用主题蓝加粗,和下面黑色指标数字区分开 */
-			.sale-item-name {
-				padding: 20upx 40upx 4upx;
-				color: #3385FF;
-				font-size: 28upx;
-				font-weight: bold;
-				line-height: 1.4;
-				word-break: break-all;
-			}
-			.sale-cell {
-				color: #333333;
-				font-size: 28upx;
-				white-space: nowrap;
-			}
-			/* 合计五项同一行;排不下才换行 */
-			.stat-summary {
-				display: flex;
-				flex-direction: row;
-				flex-wrap: wrap;
-				background: #F7F8FA;
-				padding: 16upx 8upx 4upx;
-				border-bottom: 1upx solid #eeeeee;
-			}
-			.stat-summary-item {
-				flex: 1;
-				min-width: 120upx;
-				display: flex;
-				flex-direction: column;
-				align-items: center;
-				margin-bottom: 12upx;
-			}
-			.stat-summary-label {
-				font-size: 24upx;
-				color: #666666;
-				margin-bottom: 6upx;
-			}
-			.stat-summary-value {
-				font-size: 24upx;
-				font-weight: bold;
-				color: #333333;
-				text-align: center;
-				word-break: break-all;
-			}
 		}
 	}
 	.bar-view {

+ 39 - 61
ghsApp/src/admin/stat/productSale.vue

@@ -11,38 +11,40 @@
 			<view class="space-view ex-table">
 				<block v-if="!$util.isEmpty(profile)">
 					<t-table :headerBackgroundColor="'#F0F2F6'">
-						<!-- 合计在表头上方:三项同一行,标签在上数值在下 -->
-						<view class="stat-summary">
-							<view class="stat-summary-row">
-								<view class="stat-summary-item">
-									<view class="stat-summary-label">扎数</view>
-									<view class="stat-summary-value">{{profile.totalNum||0}}</view>
-								</view>
-								<view class="stat-summary-item">
-									<view class="stat-summary-label">金额</view>
-									<view class="stat-summary-value">¥{{profile.totalAmount||0}}</view>
-								</view>
-								<view class="stat-summary-item">
-									<view class="stat-summary-label">毛利</view>
-									<view class="stat-summary-value">¥{{profile.totalMl?parseFloat(profile.totalMl):0}}</view>
-								</view>
-							</view>
-						</view>
 						<t-tr header>
 						<t-th>花材</t-th>
-						<t-th><view @click="rankByFn('num')">数量<i class="iconfont iconsanjiao_xia"></i></view></t-th>
-						<t-th><view @click="rankByFn('amount')">金额<i class="iconfont iconsanjiao_xia"></i></view></t-th>
+						<t-th><view @click="rankByFn('num')">总数量<i class="iconfont iconsanjiao_xia"></i></view></t-th>
+						<t-th><view @click="rankByFn('amount')">总金额<i class="iconfont iconsanjiao_xia"></i></view></t-th>
 						<t-th><view @click="rankByFn('gross')">毛利<i class="iconfont iconsanjiao_xia"></i></view></t-th>
 						<t-th><view @click="rankByFn('mll')">毛利率<i class="iconfont iconsanjiao_xia"></i></view></t-th>
 						</t-tr>
-						<block v-if="profile.list && !$util.isEmpty(profile.list)">
-							<view v-for="(item, index) in profile.list" :key="index">
+						<t-tr>
+							<t-td>
+								<view style="color:black;font-weight:bold;font-size:28upx;">
+									<text :style="Number(profile.totalNum) < 0 ? 'color:#e74c3c;' : ''">{{profile.totalNum||0}}</text>扎
+									¥<text :style="Number(profile.totalAmount) < 0 ? 'color:#e74c3c;' : ''">{{profile.totalAmount||0}}</text>
+									总毛利 ¥<text :style="Number(profile.totalMl) < 0 ? 'color:#e74c3c;' : ''">{{profile.totalMl?parseFloat(profile.totalMl):0}}</text>
+								</view>
+							</t-td>
+						</t-tr>
+						<view style="color:#1c89e0;font-size:24upx;margin:0 10upx 10upx 10upx;line-height:1.5;">
+							数量、金额、毛利已按售后扣减(隔天售后记在通过日,可为负)。仅退款只扣金额不扣扎数。与 PC 花材销量同源接口。
+						</view>
+						<view v-if="Number(nextDayDeduct) > 0" style="color:#e67e22;font-size:26upx;margin:0 10upx 10upx 10upx;font-weight:bold;">
+							本时段隔天售后扣减:¥{{ nextDayDeduct }}
+						</view>
+						<block v-if="displayList && !$util.isEmpty(displayList)">
+							<view v-for="(item, index) in displayList" :key="index">
 								<t-tr>
 									<t-td><view style="color:black;font-size:26upx;" @click.stop="pageTo({url:'/admin/item/detail?id='+item.productId})">{{item.name}}</view></t-td>
-									<t-td><view style="color:black;font-size:28upx;" @click.stop="pageTo({url:'/admin/item/detail?id='+item.productId})">{{item.num ? parseFloat(item.num) : 0}}</view></t-td>
-									<t-td><view style="color:black;font-size:28upx;" @click.stop="pageTo({url:'/admin/item/detail?id='+item.productId})">¥{{item.amount ? parseFloat(item.amount) : 0}}</view></t-td>
 									<t-td>
-										<view style="color:black;font-size:28upx;" @click.stop="pageTo({url:'/admin/item/detail?id='+item.productId})">¥{{item.gross ? parseFloat(item.gross) : 0}}</view>
+										<view :style="Number(item.num) < 0 ? 'color:#e74c3c;font-weight:bold;font-size:28upx;' : 'color:black;font-size:28upx;'" @click.stop="pageTo({url:'/admin/item/detail?id='+item.productId})">{{item.num ? parseFloat(item.num) : 0}}</view>
+									</t-td>
+									<t-td>
+										<view :style="Number(item.amount) < 0 ? 'color:#e74c3c;font-weight:bold;font-size:28upx;' : 'color:black;font-size:28upx;'" @click.stop="pageTo({url:'/admin/item/detail?id='+item.productId})">¥{{item.amount ? parseFloat(item.amount) : 0}}</view>
+									</t-td>
+									<t-td>
+										<view :style="Number(item.gross) < 0 ? 'color:#e74c3c;font-weight:bold;font-size:28upx;' : 'color:black;font-size:28upx;'" @click.stop="pageTo({url:'/admin/item/detail?id='+item.productId})">¥{{item.gross ? parseFloat(item.gross) : 0}}</view>
 									</t-td>
 									<t-td>
 										<view style="color:black;font-size:28upx;" @click.stop="pageTo({url:'/admin/item/detail?id='+item.productId})">{{item.mll ? item.mll : 0}}%</view>
@@ -71,10 +73,6 @@
 </view>
 </template>
 <script>
-/**
- * ghsApp 花材销售排行:按花材汇总数量/金额/毛利/毛利率。
- * 工作台进入;合计条与客户业绩页同一套「标签+数值」排版。
- */
 import AppWrapperEmpty from "@/components/app-wrapper-empty";
 import { getStatItemShow } from "@/api/stat";
 import { getAllStaff } from "@/api/shop-admin";
@@ -85,8 +83,15 @@ import AppSearchModule from "@/components/module/app-search";
 export default {
 	components: { AppWrapperEmpty,ShopSelect,DateSelect,AppSearchModule},
 	mixins: [list],
-	computed: {	
-
+	computed: {
+		/** 列表统一成数组,避免接口返回对象时 v-for 异常 */
+		displayList() {
+			const raw = (this.profile && this.profile.list) || [];
+			if (Array.isArray(raw)) {
+				return raw;
+			}
+			return Object.keys(raw).map(k => raw[k]);
+		},
 	},
 	data() {
 		return {
@@ -100,7 +105,8 @@ export default {
 			},
 			profile:[],
 			staffData:[],
-			selectStaff:{id:0,name:''}
+			selectStaff:{id:0,name:''},
+			nextDayDeduct: 0
 		};
 	},
 	onPullDownRefresh() {
@@ -155,7 +161,8 @@ export default {
 			this.params.staffName = this.selectStaff.name
 			getStatItemShow(this.params).then(res => {
 				uni.hideLoading()
-				that.profile = res.data
+				that.profile = res.data || {}
+				that.nextDayDeduct = (res.data && res.data.nextDayDeduct) ? res.data.nextDayDeduct : 0
 			});
 		}
 	}
@@ -219,35 +226,6 @@ export default {
 			::v-deep.icon-info {
 				color: #333333;
 			}
-			/* 合计条:扎数/收入/总毛利三项均分,标签在上数值在下 */
-			.stat-summary {
-				background: #F7F8FA;
-				padding: 20upx 16upx 8upx;
-				border-bottom: 1upx solid #eeeeee;
-			}
-			.stat-summary-row {
-				display: flex;
-				flex-direction: row;
-			}
-			.stat-summary-item {
-				flex: 1;
-				display: flex;
-				flex-direction: column;
-				align-items: center;
-				margin-bottom: 12upx;
-			}
-			.stat-summary-label {
-				font-size: 22upx;
-				color: #999999;
-				margin-bottom: 8upx;
-			}
-			.stat-summary-value {
-				font-size: 28upx;
-				font-weight: bold;
-				color: #333333;
-				text-align: center;
-				word-break: break-all;
-			}
 		}
 	}
 	.bar-view {

+ 16 - 2
ghsApp/src/admin/stat/profit.vue

@@ -1,3 +1,6 @@
+<!--
+  经营利润:收入含「采购隔天退货」(通过日,退货并退款+仅退款合计);花材采购仍按入库日实付。
+-->
 <template>
 <view class="app-content">
 	<view class="ex-page">
@@ -12,10 +15,21 @@
 							<t-th>类型</t-th>
 							<t-th>金额</t-th>
 						</t-tr>
+						<view style="color:#1c89e0;font-size:24upx;margin:10upx;line-height:1.5;">
+							「采购隔天退货」按售后通过日记账(含退货并退款与仅退款);花材采购按入库日。
+						</view>
 						<block v-for="(item, inIndex) in profit.income" :key="inIndex">
 							<t-tr v-if="Number(item.amount)!=0">
-								<t-td><view style="color:black;font-size:30upx;">{{item.name}}</view></t-td>
-								<t-td><view style="color:black;font-size:30upx;">{{item.amount}}</view></t-td>
+								<t-td>
+									<view :style="item.id === 'cgNextDay' ? 'color:#e67e22;font-size:30upx;font-weight:bold;' : 'color:black;font-size:30upx;'">
+										{{item.name}}
+									</view>
+								</t-td>
+								<t-td>
+									<view :style="item.id === 'cgNextDay' ? 'color:#e67e22;font-size:30upx;font-weight:bold;' : 'color:black;font-size:30upx;'">
+										{{item.amount}}
+									</view>
+								</t-td>
 							</t-tr>
 						</block>
 						<!--同个节点v-for key重复会报错,所以再包一层-->