From 0102df95a17160596aec6f7c0d2a32f5ffdfae07 Mon Sep 17 00:00:00 2001
From: liyu1201 <1492230202@qq.com>
Date: Wed, 12 Nov 2025 16:50:14 +0800
Subject: [PATCH] feat(lqTkjlb): 优化门店排行榜表格展示与截图功能
---
antis-ncc-admin/src/views/lqTkjlb/Report.vue | 373 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------------------------------------------------------------------------------------
antis-ncc-admin/src/views/statisticsList/form10.vue | 36 ++++++++++++++++++++++++------------
绿纤uni-app/pages/index/index.vue | 1231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
绿纤uni-app/pages/member-consume/member-consume copy.vue | 2963 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
绿纤uni-app/pages/member-consume/member-consume.vue | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
5 files changed, 3960 insertions(+), 754 deletions(-)
create mode 100644 绿纤uni-app/pages/member-consume/member-consume copy.vue
diff --git a/antis-ncc-admin/src/views/lqTkjlb/Report.vue b/antis-ncc-admin/src/views/lqTkjlb/Report.vue
index 721a356..5ee68a5 100644
--- a/antis-ncc-admin/src/views/lqTkjlb/Report.vue
+++ b/antis-ncc-admin/src/views/lqTkjlb/Report.vue
@@ -295,62 +295,37 @@
暂无门店数据
-
-
-
- 参与门店总数:
- {{ storeData.length }}家
-
-
- 总目标数:
- {{ getTotalStoreTarget() }}
-
-
- 总完成数:
- {{ getTotalStoreCompleted() }}
-
-
- 总完成率:
- {{ getTotalCompletionRate() }}%
-
-
-
-
-
+
+
-
- {{ scope.$index + 1 }}
-
+ {{ scope.row.StoreName }}
-
-
-
-
+
-
-
-
- {{ scope.row.CompletionRate }}%
-
-
+ {{ scope.row.TotalTarget }}
+
+
+
+
+ {{ scope.row.CompletedTarget }}
-
+
-
+ {{ formatCompletionRate(scope.row.CompletionRate) }}%
+
+
+
+
+
{{ scope.$index + 1 }}
+
-
@@ -776,15 +751,24 @@ export default {
return rateB - rateA // 降序排列
})
},
- // 门店前三名
+ // 门店前三名(按完成率排序)
topStoreData() {
return this.storeData.slice(0, 3)
},
// 按完成率排序的门店数据(用于总门店排行榜)
sortedStoreData() {
- return [...this.storeData].sort((a, b) => {
+ const sorted = [...this.storeData].sort((a, b) => {
return b.CompletionRate - a.CompletionRate // 按完成率降序排列
})
+ // 添加总计行
+ sorted.push({
+ StoreName: '总计',
+ TotalTarget: this.getTotalStoreTarget(),
+ CompletedTarget: this.getTotalStoreCompleted(),
+ CompletionRate: this.getTotalCompletionRate(),
+ isTotal: true
+ })
+ return sorted
},
// 个人前三名
topPersonData() {
@@ -1151,6 +1135,12 @@ export default {
return date.toLocaleString('zh-CN')
},
+ // 格式化完成率,保留1位小数
+ formatCompletionRate(rate) {
+ if (rate === null || rate === undefined) return '0.0'
+ return parseFloat(rate).toFixed(1)
+ },
+
// 计算门店总目标
getStoreTotalTarget(store) {
if (!store.TeamList) return 0
@@ -1259,7 +1249,7 @@ export default {
const totalTarget = this.getTotalStoreTarget()
const totalCompleted = this.getTotalStoreCompleted()
if (totalTarget === 0) return 0
- return Math.round((totalCompleted / totalTarget) * 100)
+ return (totalCompleted / totalTarget) * 100
},
// 获取总完成率样式类
@@ -1512,28 +1502,40 @@ export default {
header.style.backgroundColor = '#f8f9fa'
})
- // 确保表格容器宽度足够
+ // 确保表格容器和表格保持实际渲染宽度,避免被拉伸
const tableContainers = element.querySelectorAll('.store-ranking-table, .no-expansion-table, .store-table, .person-table')
tableContainers.forEach(container => {
- container.style.width = '100%'
- container.style.minWidth = '100%'
- container.style.maxWidth = 'none'
+ // 保持表格容器的原始宽度,避免被拉伸
+ const originalWidth = container.offsetWidth || container.clientWidth
+ if (originalWidth) {
+ container.style.width = originalWidth + 'px'
+ container.style.minWidth = originalWidth + 'px'
+ container.style.maxWidth = originalWidth + 'px'
+ }
container.style.overflow = 'visible'
})
// 等待样式应用
await new Promise(resolve => setTimeout(resolve, 500))
- // 强制重新计算表格布局
+ // 对于门店排行榜表格,保持auto宽度,不要拉伸
elementTables.forEach(table => {
- // 强制重新计算表格宽度
- table.style.width = 'auto'
- table.style.minWidth = 'auto'
- // 触发重新布局
- table.offsetHeight
- // 重新设置宽度
- table.style.width = '100%'
- table.style.minWidth = '100%'
+ const tableContainer = table.closest('.store-ranking-table')
+ if (tableContainer) {
+ // 门店排行榜表格保持auto宽度
+ table.style.width = 'auto'
+ table.style.minWidth = 'auto'
+ table.style.maxWidth = 'none'
+ } else {
+ // 其他表格保持原有逻辑
+ table.style.width = 'auto'
+ table.style.minWidth = 'auto'
+ // 触发重新布局
+ table.offsetHeight
+ // 重新设置宽度
+ table.style.width = '100%'
+ table.style.minWidth = '100%'
+ }
})
// 计算实际内容高度和宽度
@@ -1543,25 +1545,39 @@ export default {
element.clientHeight
)
- // 计算实际内容宽度,特别处理表格
- let actualWidth = Math.max(
- element.scrollWidth,
- element.offsetWidth,
- element.clientWidth
- )
-
- // 检查表格是否需要更宽的宽度
- const widthCheckTables = element.querySelectorAll('.el-table')
- widthCheckTables.forEach(table => {
- const tableWidth = Math.max(
- table.scrollWidth,
- table.offsetWidth,
- table.clientWidth
- )
- if (tableWidth > actualWidth) {
- actualWidth = tableWidth
+ // 对于门店排行榜,使用表格的实际宽度,而不是整个容器宽度
+ const storeRankingTable = element.querySelector('.store-ranking-table .el-table')
+ let actualWidth
+ let isStoreRanking = false
+
+ if (storeRankingTable) {
+ // 门店排行榜:使用表格的实际宽度,不添加额外边距
+ actualWidth = storeRankingTable.offsetWidth || storeRankingTable.clientWidth || storeRankingTable.scrollWidth
+ isStoreRanking = true
+ } else {
+ // 其他情况:使用元素的实际渲染宽度
+ actualWidth = element.offsetWidth || element.clientWidth
+
+ // 对于其他表格,使用表格的实际宽度
+ const widthCheckTables = element.querySelectorAll('.el-table')
+ widthCheckTables.forEach(table => {
+ const tableWidth = table.offsetWidth || table.clientWidth
+ if (tableWidth > actualWidth) {
+ actualWidth = tableWidth
+ }
+ })
+
+ // 确保宽度不超过容器的实际宽度
+ const containerWidth = (element.parentElement && element.parentElement.offsetWidth) || element.offsetWidth
+ if (actualWidth > containerWidth) {
+ actualWidth = containerWidth
}
- })
+ }
+
+ // 进一步限制宽度,避免截图过大
+ const maxWidth = Math.min(actualWidth, window.innerWidth || 1200)
+ // 只有门店排行榜除以2
+ actualWidth = isStoreRanking ? maxWidth / 2 : maxWidth
console.log('容器尺寸信息:', {
scrollWidth: element.scrollWidth,
@@ -1574,22 +1590,49 @@ export default {
actualWidth: actualWidth
})
+ // 对于门店排行榜,创建一个只包含表格的临时容器用于截图
+ let screenshotElement = element
+ let shouldRemoveTempContainer = false
+
+ if (storeRankingTable) {
+ // 创建临时容器,只包含表格
+ const tempContainer = document.createElement('div')
+ tempContainer.style.position = 'absolute'
+ tempContainer.style.left = '-9999px'
+ tempContainer.style.width = actualWidth + 'px'
+ tempContainer.style.padding = '0'
+ tempContainer.style.margin = '0'
+ tempContainer.style.backgroundColor = '#ffffff'
+
+ // 克隆表格容器
+ const tableContainer = storeRankingTable.closest('.store-ranking-table')
+ if (tableContainer) {
+ const clonedContainer = tableContainer.cloneNode(true)
+ // 确保克隆的容器宽度也是auto
+ clonedContainer.style.width = 'auto'
+ clonedContainer.style.minWidth = 'auto'
+ clonedContainer.style.maxWidth = 'none'
+ tempContainer.appendChild(clonedContainer)
+ document.body.appendChild(tempContainer)
+ screenshotElement = tempContainer
+ shouldRemoveTempContainer = true
+ }
+ }
+
// 配置截图选项
const options = {
allowTaint: true,
useCORS: true,
- scale: 1.0, // 降低缩放比例提高兼容性
+ scale: 2,
backgroundColor: '#ffffff',
- logging: true, // 开启日志便于调试
+ logging: true,
imageTimeout: 30000,
removeContainer: true,
- foreignObjectRendering: false, // 关闭foreignObject渲染提高兼容性
+ foreignObjectRendering: false,
scrollX: 0,
scrollY: 0,
width: actualWidth,
- height: actualHeight,
- windowWidth: actualWidth,
- windowHeight: actualHeight,
+ height: shouldRemoveTempContainer ? screenshotElement.scrollHeight : actualHeight,
ignoreElements: (element) => {
// 忽略可能影响截图的元素
return element.classList.contains('el-loading-mask') ||
@@ -1644,12 +1687,28 @@ export default {
// 确保表格容器宽度足够
const clonedTableContainers = clonedDoc.querySelectorAll('.store-ranking-table, .no-expansion-table, .store-table, .person-table')
clonedTableContainers.forEach(container => {
- container.style.width = '100%'
- container.style.minWidth = '100%'
- container.style.maxWidth = 'none'
+ // 保持表格容器的原始宽度,避免被拉伸
+ const originalWidth = container.offsetWidth || container.clientWidth || container.scrollWidth
+ if (originalWidth) {
+ container.style.width = originalWidth + 'px'
+ container.style.minWidth = originalWidth + 'px'
+ container.style.maxWidth = originalWidth + 'px'
+ } else {
+ container.style.width = '100%'
+ container.style.minWidth = '100%'
+ container.style.maxWidth = 'none'
+ }
container.style.overflow = 'visible'
})
+ // 对于门店排行榜表格,确保表格本身保持auto宽度
+ const clonedStoreRankingTables = clonedDoc.querySelectorAll('.store-ranking-table .el-table')
+ clonedStoreRankingTables.forEach(table => {
+ table.style.width = 'auto'
+ table.style.minWidth = 'auto'
+ table.style.maxWidth = 'none'
+ })
+
// 确保所有报表区域完整显示
const reportSections = clonedDoc.querySelectorAll('.report-section')
reportSections.forEach(section => {
@@ -1700,6 +1759,37 @@ export default {
col.style.maxHeight = 'none'
})
+ // 确保排行榜卡片完整显示
+ const rankingSections = clonedDoc.querySelectorAll('.ranking-section')
+ rankingSections.forEach(section => {
+ section.style.height = 'auto'
+ section.style.overflow = 'visible'
+ section.style.maxHeight = 'none'
+ section.style.display = 'block'
+ section.style.visibility = 'visible'
+ section.style.opacity = '1'
+ })
+
+ const rankingCards = clonedDoc.querySelectorAll('.ranking-card')
+ rankingCards.forEach(card => {
+ card.style.height = 'auto'
+ card.style.overflow = 'visible'
+ card.style.maxHeight = 'none'
+ card.style.display = 'flex'
+ card.style.visibility = 'visible'
+ card.style.opacity = '1'
+ card.style.position = 'static'
+ })
+
+ const rankingCardsContainer = clonedDoc.querySelectorAll('.ranking-cards')
+ rankingCardsContainer.forEach(container => {
+ container.style.display = 'flex'
+ container.style.visibility = 'visible'
+ container.style.opacity = '1'
+ container.style.height = 'auto'
+ container.style.overflow = 'visible'
+ })
+
// 确保瀑布流容器完整显示
const waterfallContainer = clonedDoc.querySelector('.waterfall-container')
if (waterfallContainer) {
@@ -1786,13 +1876,18 @@ export default {
console.log('开始生成截图,配置选项:', options)
// 生成截图
- const canvas = await html2canvas.default(element, options)
+ const canvas = await html2canvas.default(screenshotElement, options)
console.log('截图生成完成,画布尺寸:', {
width: canvas.width,
height: canvas.height
})
+ // 清理临时容器
+ if (shouldRemoveTempContainer && screenshotElement.parentElement) {
+ screenshotElement.parentElement.removeChild(screenshotElement)
+ }
+
// 恢复原始样式
element.style.height = originalStyles.height
element.style.overflow = originalStyles.overflow
@@ -1815,6 +1910,10 @@ export default {
this.$message.success('截图生成成功')
} catch (error) {
console.error('截图生成失败:', error)
+ // 清理临时容器(如果存在)
+ if (shouldRemoveTempContainer && screenshotElement && screenshotElement.parentElement) {
+ screenshotElement.parentElement.removeChild(screenshotElement)
+ }
this.$message.error('截图生成失败: ' + error.message)
} finally {
this.screenshotLoading = false
@@ -1859,9 +1958,9 @@ export default {
.app-container {
padding: 12px;
// background-color: #f5f5f5;
- min-height: 100vh;
+ // height: 100vh;
overflow-y: scroll;
- box-sizing: border-box;
+ // box-sizing: border-box;
}
.page-header {
@@ -2833,35 +2932,66 @@ export default {
.store-ranking-table {
.el-table {
- border-radius: 8px;
- overflow: hidden;
- font-size: 14px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- background: #ffffff; // 确保表格背景为纯白色
+ border: 1px solid #e4e7ed;
+ font-size: 12px;
+ background: #ffffff;
+ border-collapse: separate;
+ border-spacing: 0;
}
.el-table th {
- background: #f8f9fa; // 表头使用浅灰色背景
- color: #606266;
- font-weight: 600;
- padding: 12px 0;
- font-size: 14px;
+ background: #92d04f !important;
+ color: #000 !important;
+ font-weight: 500;
+ padding: 6px 4px;
+ font-size: 12px;
+ border-bottom: 1px solid #7ab83a;
+ border-right: 1px solid #7ab83a;
+ line-height: 1.4;
+
+ &:last-child {
+ border-right: none;
+ }
+ }
+
+ // 确保表头背景色正确应用
+ ::v-deep .el-table__header-wrapper {
+ .el-table__header {
+ th {
+ background-color: #92d04f !important;
+ color: #000 !important;
+ padding: 6px 4px;
+ font-size: 12px;
+ line-height: 1.4;
+ }
+ }
}
.el-table td {
- padding: 10px 0;
- font-size: 14px;
- background: #ffffff; // 确保表格单元格背景为纯白色
+ padding: 6px 4px;
+ font-size: 12px;
+ background: #ffffff;
+ border-bottom: 1px solid #f0f0f0;
+ border-right: 1px solid #f0f0f0;
+ color: #303133;
+ line-height: 1.4;
+
+ &:last-child {
+ border-right: none;
+ }
}
- // 确保斑马纹效果正常显示
- .el-table--striped .el-table__body tr.el-table__row--striped td {
- background: #fafafa; // 斑马纹使用更浅的灰色
+ .el-table__body tr:hover > td {
+ background-color: #ffffff !important;
+ }
+
+ .el-table__body tr:last-child td {
+ border-bottom: none;
}
.ranking-number {
font-weight: 600;
- font-size: 14px;
+ font-size: 12px;
&.ranking-top {
color: #F56C6C;
@@ -2907,7 +3037,7 @@ export default {
.ranking {
font-weight: 600;
- font-size: 14px;
+ font-size: 12px;
&.ranking-top {
color: #F56C6C;
@@ -2921,6 +3051,35 @@ export default {
color: #606266;
}
}
+
+ // 总计行样式
+ ::v-deep .el-table__body-wrapper {
+ .el-table__body {
+ tbody tr:last-child {
+ background-color: #92d04f !important;
+
+ td {
+ background-color: #92d04f !important;
+ border-bottom: none !important;
+ padding: 6px 4px;
+ font-size: 12px;
+ line-height: 1.4;
+
+ .total-label {
+ font-weight: 600;
+ color: #000;
+ font-size: 12px;
+ }
+
+ .total-value {
+ font-weight: 600;
+ color: #000;
+ font-size: 12px;
+ }
+ }
+ }
+ }
+ }
}
// 未拓客人员表格样式
diff --git a/antis-ncc-admin/src/views/statisticsList/form10.vue b/antis-ncc-admin/src/views/statisticsList/form10.vue
index 11b5178..5481a56 100644
--- a/antis-ncc-admin/src/views/statisticsList/form10.vue
+++ b/antis-ncc-admin/src/views/statisticsList/form10.vue
@@ -236,7 +236,7 @@
width="120">
{{ scope.row.SalesQuantity || 0 }}
- -
+ {{ scope.row.SalesQuantity || 0 }}
¥{{ formatMoney(scope.row.SalesAmount) }}
- -
+ ¥{{ formatMoney(scope.row.SalesAmount) }}
{{ scope.row.BillingCount || 0 }}
- -
+ {{ scope.row.BillingCount || 0 }}
{{ scope.row.SalesCount || 0 }}
- -
+ {{ scope.row.SalesCount || 0 }}
@@ -492,22 +492,34 @@ export default {
isStore: true,
StoreId: store.StoreId,
StoreName: store.StoreName,
- children: []
+ children: [],
+ // 门店合计字段(初始化为0)
+ SalesQuantity: 0,
+ SalesAmount: 0,
+ BillingCount: 0,
+ SalesCount: 0
}
- // 如果有品项列表,创建品项子节点
+ // 如果有品项列表,创建品项子节点并计算合计
if (store.ItemList && store.ItemList.length > 0) {
store.ItemList.forEach(item => {
- storeNode.children.push({
+ const itemNode = {
id: `item_${idCounter++}`,
isStore: false,
ItemId: item.ItemId,
ItemName: item.ItemName,
- SalesQuantity: item.SalesQuantity,
- SalesAmount: item.SalesAmount,
- BillingCount: item.BillingCount,
- SalesCount: item.SalesCount
- })
+ SalesQuantity: item.SalesQuantity || 0,
+ SalesAmount: item.SalesAmount || 0,
+ BillingCount: item.BillingCount || 0,
+ SalesCount: item.SalesCount || 0
+ }
+ storeNode.children.push(itemNode)
+
+ // 累加门店合计值
+ storeNode.SalesQuantity += Number(itemNode.SalesQuantity) || 0
+ storeNode.SalesAmount += Number(itemNode.SalesAmount) || 0
+ storeNode.BillingCount += Number(itemNode.BillingCount) || 0
+ storeNode.SalesCount += Number(itemNode.SalesCount) || 0
})
}
diff --git a/绿纤uni-app/pages/index/index.vue b/绿纤uni-app/pages/index/index.vue
index a4c1e3a..7e7c6b1 100644
--- a/绿纤uni-app/pages/index/index.vue
+++ b/绿纤uni-app/pages/index/index.vue
@@ -7,32 +7,32 @@
{{ loadingText }}
-
+
-
+