Commit b6d9ce25 authored by 刘杰's avatar 刘杰

企业微信前端

parent 240d8e4b
...@@ -258,8 +258,7 @@ ...@@ -258,8 +258,7 @@
<!-- 项目运营表格 --> <!-- 项目运营表格 -->
<view class="table-container"> <view class="table-container">
<text class="table-title">模拟老板运营情况</text> <text class="table-title">模拟老板运营情况</text>
<scroll-view scroll-x class="table-scroll"> <scroll-view scroll-x @scroll="scroll" :show-scrollbar="true" scroll-left="20" class="table-scroll">
<!-- <view class="table-scroll"> -->
<uni-table :data="currentData.tableData" border :loading="loading" emptyText="暂无数据"> <uni-table :data="currentData.tableData" border :loading="loading" emptyText="暂无数据">
<uni-tr> <uni-tr>
<uni-th align="center" width="70">模拟老板</uni-th> <uni-th align="center" width="70">模拟老板</uni-th>
...@@ -284,10 +283,19 @@ ...@@ -284,10 +283,19 @@
<uni-td>{{ item.manpower }}</uni-td> <uni-td>{{ item.manpower }}</uni-td>
</uni-tr> </uni-tr>
</uni-table> </uni-table>
<!-- </view> -->
</scroll-view> </scroll-view>
</view> </view>
</scroll-view> </scroll-view>
<u-popup :show="showOrderEvent" mode="bottom">
<scroll-view scroll-y="true" class="scroll-Y">
<orderEventList ref="orderEventList" :isEdit="showOrderEvent" @close="orderEventClose"></orderEventList>
<!-- <orderInsertContent ref="orderInsertContent" :isEdit="showEdit" @close="orderEditClose">
</orderInsertContent> -->
</scroll-view>
</u-popup>
</view> </view>
</template> </template>
...@@ -323,6 +331,7 @@ ...@@ -323,6 +331,7 @@
sortType: 'receivable', sortType: 'receivable',
projectSortType: 'receivable', projectSortType: 'receivable',
timeData: {}, timeData: {},
showOrderEvent: false
} }
}, },
computed: { computed: {
...@@ -379,6 +388,9 @@ ...@@ -379,6 +388,9 @@
}) })
}, },
methods: { methods: {
orderEventClose() {
this.showOrderEvent = false
},
async initData() { async initData() {
this.staffCode = uni.getStorageSync(`staffCode`); this.staffCode = uni.getStorageSync(`staffCode`);
this.staffKind = uni.getStorageSync('uc_staff_kind'); this.staffKind = uni.getStorageSync('uc_staff_kind');
...@@ -862,6 +874,7 @@ ...@@ -862,6 +874,7 @@
uni.navigateTo({ uni.navigateTo({
url: '/pages/abnormal/list/index' url: '/pages/abnormal/list/index'
}); });
// this.showOrderEvent = true
}, },
navigateToDailyReport() { navigateToDailyReport() {
console.log('点击了日报跳转按钮'); console.log('点击了日报跳转按钮');
...@@ -903,6 +916,12 @@ ...@@ -903,6 +916,12 @@
.scroll-list { .scroll-list {
height: calc(100vh - 60px); height: calc(100vh - 60px);
touch-action: pan-x;
/* 允许横向滚动 */
overflow-x: auto;
/* 允许横向滚动 */
-webkit-overflow-scrolling: touch;
/* 启用 iOS 的平滑滚动 */
} }
.report-container { .report-container {
...@@ -1522,7 +1541,7 @@ ...@@ -1522,7 +1541,7 @@
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
touch-action: pan-y; touch-action: pan-y;
&::-webkit-scrollbar { &::-webkit-scrollbar {
height: 6px; height: 6px;
background: #f5f5f5; background: #f5f5f5;
...@@ -1543,10 +1562,10 @@ ...@@ -1543,10 +1562,10 @@
} }
.table-container { .table-container {
display: block; width: 100%;
white-space: nowrap;
background-color: #fff; background-color: #fff;
border-radius: 8px; border-radius: 8px;
overflow-x: hidden;
margin-bottom: 10px; margin-bottom: 10px;
...@@ -1559,14 +1578,22 @@ ...@@ -1559,14 +1578,22 @@
} }
.table-scroll { .table-scroll {
scroll-margin-left: 0;
width: 95%;
margin-bottom: 10px; margin-bottom: 10px;
display: block;
width: 100%;
height: 180px;
overflow-x: auto; overflow-x: auto;
overflow-y: auto; /* 允许横向滚动 */
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
/* 启用 iOS 的平滑滚动 */
touch-action: pan-x;
/* 允许横向滚动 */
border: 1px solid #e8e8e8;
/* 可选:为滚动条添加边框 */
max-width: 100%;
/* 确保容器宽度适应 */
order-radius: 3px;
white-space: nowrap;
/deep/ .uni-table { /deep/ .uni-table {
min-width: 100%; min-width: 100%;
width: max-content; width: max-content;
......
<template>
<view class="report-container">
<!-- 顶部统计概览 -->
<!-- <view class="top-stats">
<view class="stat-item">
<text class="label">占位区域</text>
<text class="value">1280</text>
</view>
<view class="stat-item">
<text class="label">占位区域</text>
<text class="value">3562</text>
</view>
<view class="stat-item">
<text class="label">占位区域</text>
<text class="value">85.6%</text>
</view>
<view class="stat-item">
<text class="label">占位区域</text>
<text class="value">¥896.5w</text>
</view>
</view> -->
<!-- 顶部时间导航 -->
<view class="time-nav">
<view class="nav-item" :class="{active: activeTime === 'month'}" @click="switchTime('month')">本月</view>
<view class="nav-item" :class="{active: activeTime === 'week'}" @click="switchTime('week')">本周</view>
<view class="nav-item" :class="{active: activeTime === 'yesterday'}" @click="switchTime('yesterday')">昨日</view>
<view class="nav-item" :class="{active: activeTime === 'today'}" @click="switchTime('today')">今日</view>
</view>
<!-- 项目概览卡片 -->
<view class="project-card">
<view class="overview-content">
<!-- 左侧环状进度 -->
<view class="progress-circle" @click="navigateToDailyReport">
<view class="circle-wrapper" :style="{
background: `conic-gradient(#52C41A 0% ${currentData.overview.submitRate}%, rgba(245,245,245,0.3) ${currentData.overview.submitRate}% 100%)`
}">
<view class="circle-text">
<!-- <text class="percentage">{{currentData.overview.submitRate}}%</text> -->
<view class="submit-info">
<view class="count-row">
<text class="submitted"> {{currentData.overview.submitCount}}</text>
<text class="should-submitted"> /{{currentData.overview.totalCount}}</text>
</view>
<text class="label">日报提交</text>
</view>
</view>
</view>
</view>
<!-- 右侧数据统计 -->
<view class="statistics">
<view class="stat-row">
<view class="stat-item">
<view class="stat-block light-green">
<text class="number">{{currentData.overview.stats.projectCount}}</text>
<text class="rate">项目</text>
</view>
</view>
<view class="stat-item">
<view class="stat-block light-orange">
<text class="number">{{currentData.overview.stats.orderCount}}</text>
<text class="rate">单量</text>
</view>
</view>
</view>
<view class="stat-row">
<view class="stat-item">
<view class="stat-block green">
<text class="number">{{currentData.overview.stats.volumeCount}}</text>
<text class="rate">方量m³</text>
</view>
</view>
<view class="stat-item">
<view class="stat-block orange">
<text class="number">{{currentData.overview.stats.weightCount}}</text>
<text class="rate">重量T</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 应收应付信息 -->
<view class="finance-info">
<!-- 应收模块 -->
<view class="finance-block">
<view class="finance-item receivable">
<!-- 左侧图标 -->
<view class="left-section">
<view class="icon"></view>
</view>
<!-- 右侧内容 -->
<view class="right-section">
<view class="main-info">
<view class="amount-section">
<text class="amount">{{currentData.finance.receivable.total}}</text>
<text class="label">应收</text>
</view>
<view class="extra-info">
<view class="info-row">
<text class="label">预计毛利</text>
<text class="value">{{currentData.finance.receivable.profit}}</text>
</view>
<view class="info-row">
<text class="label">毛利率</text>
<text class="value">{{currentData.finance.receivable.profitRate}}%</text>
</view>
</view>
</view>
<view class="progress-bar">
<view class="progress-bar" style="position: relative; height: 4px;width: 100%; background: #52C41A; border-radius: 2px; overflow: hidden;">
<view class="progress" :style="{
backgroundColor: '#FF4D4F',
opacity:0.9,
width: 100-currentData.finance.receivable.profitRate + '%',
position: 'absolute',
height: '100%',
left: 0,
top: '1px'
}"></view>
</view>
</view>
</view>
</view>
</view>
<!-- 应付模块 -->
<view class="finance-block">
<view class="finance-item payable">
<!-- 左侧图标 -->
<view class="left-section">
<view class="icon"></view>
</view>
<!-- 右侧内容 -->
<view class="right-section">
<view class="main-info">
<view class="amount-section">
<text class="amount">{{currentData.finance.payable.total}}</text>
<text class="label">应付</text>
</view>
<view class="extra-info">
<view class="info-row">
<text class="label">现结</text>
<text class="value cash">{{currentData.finance.payable.cash}}</text>
</view>
<view class="info-row">
<text class="label">月结</text>
<text class="value monthly">{{currentData.finance.payable.monthly}}</text>
</view>
</view>
</view>
<view class="progress-bar" style="position: relative; height: 4px; background: #f0f0f0; border-radius: 2px; overflow: hidden;">
<view class="progress cash" :style="{
backgroundColor: '#FFA940',
width: cashRatio,
position: 'absolute',
height: '100%',
left: 0,
top: 0
}"></view>
<view class="progress monthly" :style="{
backgroundColor: '#13C2C2',
position: 'absolute',
height: '100%',
left: cashRatio,
width: monthlyRatio,
top: 0
}"></view>
</view>
</view>
</view>
</view>
</view>
<!-- 模拟老板对比图表区域 -->
<view class="chart-container">
<view class="chart-header">
<text class="chart-title">模拟老板对比</text>
<view class="sort-buttons">
<text class="sort-btn" :class="{active: sortType === 'receivable'}" @click="sortChart('receivable','myLeaderChart')">
<text class="sort-icon"></text>
应收
</text>
<text class="sort-btn" :class="{active: sortType === 'payable'}" @click="sortChart('payable','myLeaderChart')">
<text class="sort-icon"></text>
应付
</text>
<text class="sort-btn" :class="{active: sortType === 'profitRate'}" @click="sortChart('profitRate','myLeaderChart')">
<text class="sort-icon"></text>
利润
</text>
</view>
</view>
<scroll-view class="chart-scroll" scroll-x>
<view class="chart-wrapper" id="myLeaderChart"></view>
</scroll-view>
</view>
<!-- 项目对比图表区域 -->
<!-- todo: 当前排序和模拟老板干涉 -->
<view class="chart-container">
<view class="chart-header">
<text class="chart-title">项目对比(前10)</text>
<view class="sort-buttons">
<text class="sort-btn" :class="{active: sortType === 'receivable'}" @click="sortChart('receivable','myProjectChart')">
<text class="sort-icon"></text>
应收
</text>
<text class="sort-btn" :class="{active: sortType === 'payable'}" @click="sortChart('payable','myProjectChart')">
<text class="sort-icon"></text>
应付
</text>
<text class="sort-btn" :class="{active: sortType === 'profitRate'}" @click="sortChart('profitRate','myProjectChart')">
<text class="sort-icon"></text>
利润
</text>
</view>
</view>
<scroll-view class="chart-scroll" scroll-x>
<view class="chart-wrapper" id="myProjectChart"></view>
</scroll-view>
</view>
<!-- 项目运营表格 -->
<view class="table-container">
<text class="table-title">模拟老板运营情况</text>
<view class="table-scroll">
<uni-table
:data="currentData.tableData"
border
stripe
:loading="loading"
emptyText="暂无数据"
>
<uni-tr>
<uni-th align="center" width="60">模拟老板</uni-th>
<uni-th align="center" width="50">应收(万)</uni-th>
<uni-th align="center" width="50">应付(万)</uni-th>
<uni-th align="center" width="50">应付现结(万)</uni-th>
<uni-th align="center" width="50">毛利率(%)</uni-th>
<uni-th align="center" width="50">单量</uni-th>
<uni-th align="center" width="50">方(m³)</uni-th>
<uni-th align="center" width="50">吨(T)</uni-th>
<uni-th align="center" width="50">人力</uni-th>
</uni-tr>
<uni-tr v-for="(item, index) in currentData.tableData" :key="index">
<uni-td>{{ item.boss }}</uni-td>
<uni-td>{{ item.receivable }}</uni-td>
<uni-td>{{ item.payable }}</uni-td>
<uni-td>{{ item.cashPayable }}</uni-td>
<uni-td>{{ item.profitRate }}%</uni-td>
<uni-td>{{ item.orderCount }}</uni-td>
<uni-td>{{ item.volume }}</uni-td>
<uni-td>{{ item.weight }}</uni-td>
<uni-td>{{ item.manpower }}</uni-td>
</uni-tr>
</uni-table>
</view>
</view>
</view>
</template>
<script>
import * as echarts from 'echarts'
import { uniIcons } from '@dcloudio/uni-ui'
export default {
components: {
uniIcons
},
data() {
return {
loading: true,
activeTime: 'today',
myLeaderChart: null,
myProjectChart: null,
sortType: 'receivable',
// 所有时间维度的数据集合
timeData: {
month: {
overview: {
submitRate: 35.68,
submitCount: 156,
totalCount: 200,
stats: {
projectCount: 256,
orderCount: 328,
volumeCount: 45,
weightCount: 89
}
},
finance: {
receivable: {
total: 892650,
profit: 678900,
profitRate: 92.35
},
payable: {
total: 8920,
cash: 3580,
monthly: 5340
}
},
leaderChartData: {
bossNames: ['老板A', '老板B', '老板C', '老板D', '老板E', '老板F'],
receivable: [320, 450, 380, 420, 390, 410],
payable: [220, 350, 280, 320, 290, 310],
profitRate: [45.5, 38.2, 42.8, 35.6, 48.9, 40.2]
},
projectChartData: {
projectNames: ['项目A', '项目B', '项目C', '项目D', '项目E', '项目F'],
receivable: [320, 450, 380, 420, 390, 410],
payable: [220, 350, 280, 320, 290, 310],
profitRate: [45.5, 38.2, 42.8, 35.6, 48.9, 40.2]
},
tableData: [
{
boss: '老板A',
receivable: 156800,
payable: 128900,
cashPayable: 38670,
profitRate: 17.8,
orderCount: 156,
volume: 890,
weight: 1250,
manpower: 25
},
{
boss: '老板B',
receivable: 198500,
payable: 158400,
cashPayable: 47520,
profitRate: 20.2,
orderCount: 189,
volume: 1020,
weight: 1580,
manpower: 32
},
{
boss: '老板C',
receivable: 145600,
payable: 116480,
cashPayable: 34944,
profitRate: 20.0,
orderCount: 142,
volume: 760,
weight: 980,
manpower: 18
},
{
boss: '老板D',
receivable: 178900,
payable: 143120,
cashPayable: 42936,
profitRate: 20.0,
orderCount: 168,
volume: 920,
weight: 1350,
manpower: 28
},
{
boss: '老板E',
receivable: 134500,
payable: 107600,
cashPayable: 32280,
profitRate: 20.0,
orderCount: 128,
volume: 680,
weight: 850,
manpower: 15
}
]
},
week: {
overview: {
submitRate: 28.45,
submitCount: 95,
totalCount: 200,
stats: {
projectCount: 168,
orderCount: 235,
volumeCount: 28,
weightCount: 65
}
},
finance: {
receivable: {
total: 568950,
profit: 458900,
profitRate: 88.69
},
payable: {
total: 5860,
cash: 2680,
monthly: 3180
}
},
leaderChartData: {
bossNames: ['老板A', '老板B', '老板C', '老板D', '老板E', '老板F'],
receivable: [220, 350, 280, 320, 290, 310],
payable: [120, 250, 180, 220, 190, 210],
profitRate: [42.5, 35.2, 39.8, 32.6, 45.9, 37.2]
},
projectChartData: {
projectNames: ['项目A', '项目B', '项目C', '项目D', '项目E', '项目F'],
receivable: [320, 450, 380, 420, 390, 410],
payable: [220, 350, 280, 320, 290, 310],
profitRate: [45.5, 38.2, 42.8, 35.6, 48.9, 40.2]
},
tableData: [
{
boss: '老板A',
receivable: 1534.5,
payable: 1286.3,
cashPayable: 3867,
profitRate: 17.8,
orderCount: 156,
volume: 890,
weight: 1250,
manpower: 25
},
{
boss: '老板B',
receivable: 1286.3,
payable: 1025.3,
cashPayable: 2680,
profitRate: 20.2,
orderCount: 189,
volume: 1020,
weight: 1580,
manpower: 32
},
{
boss: '老板C',
receivable: 956.8,
payable: 765.8,
cashPayable: 2405,
profitRate: 20.0,
orderCount: 142,
volume: 760,
weight: 980,
manpower: 18
},
{
boss: '老板D',
receivable: 843.2,
payable: 674.2,
cashPayable: 2405,
profitRate: 20.0,
orderCount: 168,
volume: 920,
weight: 1350,
manpower: 28
},
{
boss: '老板E',
receivable: 732.1,
payable: 585.1,
cashPayable: 1302.76,
profitRate: 20.0,
orderCount: 128,
volume: 680,
weight: 850,
manpower: 15
}
]
},
yesterday: {
overview: {
submitRate: 25.35,
submitCount: 85,
totalCount: 200,
stats: {
projectCount: 138,
orderCount: 186,
volumeCount: 22,
weightCount: 58
}
},
finance: {
receivable: {
total: 498950,
profit: 398580,
profitRate: 85.69
},
payable: {
total: 4810,
cash: 2405,
monthly: 2405
}
},
leaderChartData: {
bossNames: ['老板A', '老板B', '老板C', '老板D', '老板E', '老板F'],
receivable: [180, 290, 220, 260, 230, 250],
payable: [80, 190, 120, 160, 130, 150],
profitRate: [40.5, 33.2, 37.8, 30.6, 43.9, 35.2]
},
projectChartData: {
projectNames: ['项目A', '项目B', '项目C', '项目D', '项目E', '项目F'],
receivable: [320, 450, 380, 420, 390, 410],
payable: [220, 350, 280, 320, 290, 310],
profitRate: [45.5, 38.2, 42.8, 35.6, 48.9, 40.2]
},
tableData: [
{
boss: '老板A',
receivable: 934.5,
payable: 886.3,
cashPayable: 2405,
profitRate: 85.69,
orderCount: 186,
volume: 22,
weight: 58,
manpower: 15
},
{
boss: '老板B',
receivable: 886.3,
payable: 796.3,
cashPayable: 2405,
profitRate: 85.69,
orderCount: 189,
volume: 22,
weight: 58,
manpower: 18
},
{
boss: '老板C',
receivable: 756.8,
payable: 666.8,
cashPayable: 2405,
profitRate: 85.69,
orderCount: 142,
volume: 22,
weight: 58,
manpower: 18
},
{
boss: '老板D',
receivable: 643.2,
payable: 553.2,
cashPayable: 2405,
profitRate: 85.69,
orderCount: 168,
volume: 22,
weight: 58,
manpower: 28
},
{
boss: '老板E',
receivable: 532.1,
payable: 442.1,
cashPayable: 1302.76,
profitRate: 85.69,
orderCount: 128,
volume: 22,
weight: 58,
manpower: 15
}
]
},
today: {
overview: {
submitRate: 22.35,
submitCount: 80,
totalCount: 200,
stats: {
projectCount: 128,
orderCount: 156,
volumeCount: 19,
weightCount: 55
}
},
finance: {
receivable: {
total: 488950,
profit: 58674,
profitRate: 12.2
},
payable: {
total: 430276,
cash: 130276,
monthly: 300000
}
},
leaderChartData: {
bossNames: ['老板A', '老板B', '老板C', '老板D', '老板E', '老板F'],
receivable: [150, 260, 190, 230, 200, 220, 180, 240, 210, 170],
payable: [250, 160, 90, 310, 100, 120, 80, 140, 110, 70],
profitRate: [38.5, 31.2, 35.8, 28.6, 41.9, 33.2, 36.5, 29.8, 34.5, 32.1]
},
projectChartData: {
projectNames: ['项目A', '项目B', '项目C', '项目D', '项目E', '项目F'],
receivable: [320, 450, 380, 420, 390, 410],
payable: [220, 150, 280, 320, 290, 310],
profitRate: [45.5, 38.2, 42.8, 35.6, 48.9, 40.2]
},
tableData: [
{
boss: '老板A',
receivable: 834.5,
payable: 786.3,
cashPayable: 130276,
profitRate: 12.2,
orderCount: 156,
volume: 19,
weight: 55,
manpower: 15
},
{
boss: '老板B',
receivable: 786.3,
payable: 738.3,
cashPayable: 130276,
profitRate: 12.2,
orderCount: 189,
volume: 19,
weight: 55,
manpower: 18
},
{
boss: '老板C',
receivable: 656.8,
payable: 608.8,
cashPayable: 130276,
profitRate: 12.2,
orderCount: 142,
volume: 19,
weight: 55,
manpower: 18
},
{
boss: '老板D',
receivable: 543.2,
payable: 495.2,
cashPayable: 130276,
profitRate: 12.2,
orderCount: 168,
volume: 19,
weight: 55,
manpower: 28
},
{
boss: '老板E',
receivable: 432.1,
payable: 384.1,
cashPayable: 130276,
profitRate: 12.2,
orderCount: 128,
volume: 19,
weight: 55,
manpower: 15
}
]
}
}
}
},
computed: {
// 当前选中时间的数据
currentData() {
//todo 调用后端接口,直接返回对应时间区间的数据
return this.timeData[this.activeTime]
},
// 应付现结比例
cashRatio() {
const { cash, total } = this.currentData.finance.payable
return (cash / total * 100).toFixed(2) + '%'
},
// 应付月结比例
monthlyRatio() {
const { monthly, total } = this.currentData.finance.payable
return (monthly / total * 100).toFixed(2) + '%'
}
},
mounted() {
this.initLeaderChart()
this.initProjectChart()
// 在 Vue 的下一个 DOM 更新循环中执行
this.$nextTick(() => {
// 检查当前数据的表格数据是否存在且长度大于 0
if (this.currentData.tableData && this.currentData.tableData.length > 0) {
// 如果表格数据存在,设置 loading 状态为 false,表示数据已加载完成
this.loading = false
}
// 对领导图表进行排序,按应收数据排序
this.sortChart('receivable', 'myLeaderChart')
// 对项目图表进行排序,按应收数据排序
this.sortChart('receivable', 'myProjectChart')
// 监听窗口的 resize 事件,调用 onResize 方法以适应图表大小
window.addEventListener('resize', this.onResize)
})
},
methods: {
initLeaderChart() {
// 获取数据点数量
const dataCount = this.currentData.leaderChartData.receivable.length;
// 计算所需的最小宽度(假设每个数据点需要100px)
const minWidth = Math.max(dataCount * 60, 500);
// 设置容器宽度
const wrapper = document.getElementById('myLeaderChart');
if (wrapper) {
wrapper.style.width = `${minWidth}px`;
wrapper.style.padding = '0 0 5px 0';
}
this.myLeaderChart = echarts.init(document.getElementById('myLeaderChart'))
const option = {
tooltip: {
trigger: 'item',
axisPointer: {
type: 'shadow'
},
formatter: function(params) {
return params.map(item => {
if (item.seriesName === '利润率') {
return `${item.seriesName}: ${item.value}%`
}
return `${item.seriesName}: ${item.value}万元`
}).join('<br/>')
}
},
legend: {
data: ['应收', '应付', '利润率'],
bottom: 0,
// itemGap: 20
},
grid: {
left: '3%',
right: '4%',
top: '5%',
bottom: '12%',
width: 'auto',
containLabel: true
},
xAxis: {
type: 'category',
axisLine: {
show: true,
lineStyle: { color: '#E8E8E8' }
},
axisLabel: {
interval: 0,
color: '#666',
fontSize: 12
}
},
yAxis: [
{
type: 'value',
name: '金额(万元)',
position: 'left',
axisLine: {
show: true
},
splitLine: {
show: false
},
nameTextStyle: { color: '#666' },
axisLabel: { color: '#666' }
},
{
type: 'value',
name: '利润率(%)',
position: 'right',
min: 0,
max: 100,
axisLine: {
show: true
},
splitLine: {
show: false
},
nameTextStyle: { color: '#666' },
axisLabel: { color: '#666' }
}
],
series: [
{
name: '应收',
type: 'bar',
barGap: '0%',
barWidth: '35%',
itemStyle: {
color: '#52C41A',
borderRadius: [4, 4, 0, 0]
}
},
{
name: '应付',
type: 'bar',
barWidth: '35%',
itemStyle: {
color: '#FF4D4F',
borderRadius: [4, 4, 0, 0]
}
},
{
name: '利润率',
type: 'line',
yAxisIndex: 1,
symbol: 'circle',
symbolSize: 6,
itemStyle: {
color: '#1890FF'
},
lineStyle: {
width: 2
}
}
]
}
this.myLeaderChart.setOption(option)
},
initProjectChart() {
// 获取数据点数量
const dataCount = this.currentData.projectChartData.receivable.length;
// 计算所需的最小宽度(假设每个数据点需要100px)
const minWidth = Math.max(dataCount * 60, 500);
// 设置容器宽度
const wrapper = document.getElementById('myProjectChart');
if (wrapper) {
wrapper.style.width = `${minWidth}px`;
wrapper.style.padding = '0 0 5px 0';
}
this.myProjectChart = echarts.init(document.getElementById('myProjectChart'))
const option = {
tooltip: {
trigger: 'item',
axisPointer: {
type: 'shadow'
},
formatter: function(params) {
return params.map(item => {
if (item.seriesName === '利润率') {
return `${item.seriesName}: ${item.value}%`
}
return `${item.seriesName}: ${item.value}万元`
}).join('<br/>')
}
},
legend: {
data: ['应收', '应付', '利润率'],
bottom: 0,
// itemGap: 20
},
grid: {
left: '3%',
right: '4%',
top: '5%',
bottom: '12%',
width: 'auto',
containLabel: true
},
xAxis: {
type: 'category',
axisLine: {
show: true,
lineStyle: { color: '#E8E8E8' }
},
axisLabel: {
interval: 0,
color: '#666',
fontSize: 12
}
},
yAxis: [
{
type: 'value',
name: '金额(万元)',
position: 'left',
axisLine: {
show: true
},
splitLine: {
show: false
},
nameTextStyle: { color: '#666' },
axisLabel: { color: '#666' }
},
{
type: 'value',
name: '利润率(%)',
position: 'right',
min: 0,
max: 100,
axisLine: {
show: true
},
splitLine: {
show: false
},
nameTextStyle: { color: '#666' },
axisLabel: { color: '#666' }
}
],
series: [
{
name: '应收',
type: 'bar',
barGap: '0%',
barWidth: '35%',
itemStyle: {
color: '#52C41A',
borderRadius: [4, 4, 0, 0]
}
},
{
name: '应付',
type: 'bar',
barWidth: '35%',
itemStyle: {
color: '#FF4D4F',
borderRadius: [4, 4, 0, 0]
}
},
{
name: '利润率',
type: 'line',
yAxisIndex: 1,
symbol: 'circle',
symbolSize: 6,
itemStyle: {
color: '#1890FF'
},
lineStyle: {
width: 2
}
}
]
}
this.myProjectChart.setOption(option)
},
switchTime(time) {
this.activeTime = time
// 更新图表数据
this.updateChartSeries()
},
onResize() {
if (this.myLeaderChart) {
this.myLeaderChart.resize()
}
if(this.myProjectChart){
this.myProjectChart.resize()
}
},
sortChart(type,chart) {
this.sortType = type
let data = null
let xAxisNames = null
if(chart === 'myLeaderChart'){
data = this.currentData.leaderChartData
xAxisNames = this.currentData.leaderChartData.bossNames
}else if(chart === 'myProjectChart'){
data = this.currentData.projectChartData
xAxisNames = this.currentData.projectChartData.projectNames
}
// 创建排序用的数组
const sortArray = xAxisNames.map((name, index) => ({
name,
receivable: data.receivable[index],
payable: data.payable[index],
profitRate: data.profitRate[index]
}))
// 排序
sortArray.sort((a, b) => {
// 降序排列
return b[type] - a[type]
})
// 更新图表
if(chart === 'myLeaderChart'){
this.myLeaderChart.setOption({
xAxis: {
data: sortArray.map(item => item.name)
},
series: [{
name: '应收',
data: sortArray.map(item => item.receivable)
}, {
name: '应付',
data: sortArray.map(item => item.payable)
}, {
name: '利润率',
data: sortArray.map(item => item.profitRate)
}]
})
}else if(chart === 'myProjectChart'){
this.myProjectChart.setOption({
xAxis: {
data: sortArray.map(item => item.name)
},
series: [{
name: '应收',
data: sortArray.map(item => item.receivable)
}, {
name: '应付',
data: sortArray.map(item => item.payable)
}, {
name: '利润率',
data: sortArray.map(item => item.profitRate)
}]
})
}
},
updateChartSeries() {
this.myLeaderChart.setOption({
series: [
{ data: this.currentData.leaderChartData.receivable },
{ data: this.currentData.leaderChartData.payable },
{ data: this.currentData.leaderChartData.profitRate }
]
})
this.myProjectChart.setOption({
series: [
{ data: this.currentData.projectChartData.receivable },
{ data: this.currentData.projectChartData.payable },
{ data: this.currentData.projectChartData.profitRate }
]
})
},
navigateToDailyReport() {
// 使用uni-app的导航方法跳转到日报查看页面
uni.navigateTo({
url: '/pages/dailyReport/index'
});
}
},
beforeDestroy() {
if (this.myLeaderChart) {
this.myLeaderChart.dispose()
}
window.removeEventListener('resize', this.onResize)
}
}
</script>
<style lang="scss">
// 可以定义全局变量
$primary-color: #52C41A;
$danger-color: #FF4D4F;
$warning-color: #FFA940;
$info-color: #13C2C2;
$text-primary: #333;
$text-secondary: #666;
$secondary-color: #1890FF;
$circle-size: 120px;
// 在样式文件顶部添加重置样式
page {
width: 100%;
height: 100%;
background-color: #F5F7FA;
}
uni-page-body {
width: 100%;
height: 100%;
}
.report-container {
width: 100%;
box-sizing: border-box;
background-color: #F5F7FA;
min-height: 100vh;
> view {
width: 100%;
max-width: 1200px;
margin: 0 auto;
background-color: transparent;
}
.top-stats {
display: flex;
justify-content: space-between;
// padding: 12px 15px;
background: linear-gradient(135deg, #f6f8fc 0%, #ffffff 100%);
margin-bottom: 15px;
box-shadow: 0 1px 4px rgba(0,0,0,0.05);
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
.label {
font-size: 12px;
color: #666;
margin-bottom: 4px;
}
.value {
font-size: 16px;
font-weight: 500;
color: #333;
}
}
}
.time-nav {
display: flex;
background-color: #fff;
border-radius: 8px;
margin-bottom: 15px;
box-shadow: 0 2px 12px rgba(0,0,0,0.04);
.nav-item {
flex: 1;
text-align: center;
padding: 6px 0;
font-size: 14px;
color: $text-secondary;
position: relative;
transition: all 0.3s ease;
&.active {
color: #1890FF;
font-weight: bold;
&::after {
content: '';
position: absolute;
left: 50%;
bottom: -2px;
transform: translateX(-50%);
width: 12px;
height: 2px;
background-color: #1890FF;
border-radius: 1px;
}
}
&:not(:last-child) {
border-right: 1px solid #f0f0f0;
}
}
}
.project-card {
display: flex;
background-color: #fff !important;
width: 100%;
box-sizing: border-box;
padding: 15px 15px 0px 15px ;
border-radius: 8px;
margin-bottom: 15px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
.overview-content {
display: flex;
width: 100%;
align-items: center;
padding: 0;
.progress-circle {
width: 120px;
height: 120px;
position: relative;
margin-right: 20px;
.circle-wrapper {
width: 100%;
height: 100%;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
&::after {
content: '';
position: absolute;
top: 12px;
left: 12px;
right: 12px;
bottom: 12px;
background: #fff;
border-radius: 50%;
transition: all 0.3s ease;
}
}
.circle-text {
position: relative;
z-index: 1;
text-align: center;
// .percentage {
// display: block;
// font-size: 24px;
// font-weight: bold;
// color: #52C41A;
// margin-bottom: 4px;
// }
.submit-info {
.label {
display: block;
font-size: 14px;
color: #333;
font-weight: 500;
margin-bottom: 4px;
}
.count-row {
display: flex;
flex-direction: row;
gap: 2px;
font-size: 12px;
justify-self: center;
.submitted {
display: block;
font-size: 20px;
font-weight: bold;
color: #52C41A;
margin-bottom: 4px;
}
.should-submitted {
font-size: 18px;
color: #333232;
}
}
}
}
}
.statistics {
flex: 1;
.stat-row {
display: flex;
gap: 12px;
margin-bottom: 15px;
.stat-item {
flex: 1;
.stat-block {
background-color: #f6ffed;
border-radius: 4px;
padding: 8px 12px;
display: block;
text-align: center;
&.light-green { background-color: rgba(82, 196, 26, 0.1); }
&.light-orange { background-color: rgba(255, 169, 64, 0.1); }
&.green { background-color: rgba(0, 200, 180, 0.1); }
&.orange { background-color: rgba(255, 148, 64, 0.1); }
}
.number {
display: block;
font-size: 20px;
font-weight: bold;
color: #333;
margin-bottom: 4px;
}
.rate {
display: block;
font-size: 14px;
color: #999;
}
}
}
}
}
}
.finance-info {
display: flex;
flex-direction: column;
gap: 15px;
margin-bottom: 15px;
.finance-block {
width: 100%;
}
.finance-item {
background-color: #fff;
padding: 15px;
border-radius: 8px;
display: flex;
align-items: flex-start;
.left-section {
width: 60px;
margin-right: 20px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
padding: 10px 0;
.icon {
width: 60px;
height: 60px;
border-radius: 12px;
font-size: 28px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
}
}
.right-section {
flex: 1;
.main-info {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 15px;
.amount-section {
display: flex;
flex-direction: column;
.amount {
font-size: 24px;
font-weight: bold;
margin-bottom: 4px;
line-height: 1.2;
}
.label {
font-size: 14px;
}
}
.extra-info {
display: flex;
flex-direction: column;
text-align: right;
.info-row {
margin-bottom: 4px;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 13px;
color: #666;
margin-right: 8px;
}
.value {
font-size: 13px;
}
}
}
}
.progress-bar {
display: flex;
position: relative;
height: 4px;
background: #f0f0f0;
border-radius: 2px;
overflow: hidden;
.progress {
position: absolute;
top: 0;
bottom: 0;
height: 100% !important;
border-radius: 2px;
transition: width 0.3s ease;
&.cash { background-color: #FFA940; }
&.monthly { background-color: #13C2C2; }
}
}
}
&.receivable {
.left-section .icon {
background-color: #52C41A;
}
.right-section {
.amount-section {
.amount, .label {
color: #52C41A;
}
}
.extra-info .info-row .value {
color: #52C41A;
}
.progress-bar .progress {
background-color: #52C41A;
}
}
}
&.payable {
.left-section .icon {
background-color: #FF4D4F;
}
.right-section {
.amount-section {
.amount, .label {
color: #FF4D4F;
}
}
.extra-info {
.info-row {
.value {
&.cash {
color: #FFA940;
}
&.monthly {
color: #13C2C2;
}
}
}
}
.progress-bar {
position: relative;
.progress {
&.cash, &.monthly {
position: absolute;
height: 100%;
}
}
}
}
}
}
}
.chart-container {
display: block;
background-color: #fff;
border-radius: 8px;
margin-bottom: 15px;
padding-bottom: 15px;
.chart-header {
padding: 15px 15px 0px 15px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
.chart-title {
font-size: 16px;
font-weight: bold;
color: #333;
}
.sort-buttons {
display: flex;
gap: 1px;
.sort-btn {
font-size: 12px;
color: #666;
padding: 4px 10px;
border-radius: 12px;
background: #F5F5F5;
border: 1px solid #E8E8E8;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
margin-left: 8px;
.sort-icon {
font-family: "custom-icon" !important;
font-size: 12px;
font-style: normal;
-webkit-font-smoothing: antialiased;
}
&:hover {
color: #52C41A;
border-color: #52C41A;
background: #F6FFED;
}
&.active {
color: #fff;
background: #52C41A;
border-color: #52C41A;
box-shadow: 0 2px 4px rgba(82, 196, 26, 0.2);
}
}
}
}
.chart-scroll {
width: 100%;
overflow-x: auto;
// -webkit-overflow-scrolling: touch;
// &::-webkit-scrollbar {
// height: 6px;
// background: #f5f5f5;
// }
// &::-webkit-scrollbar-thumb {
// background: rgba(0, 0, 0, 0.1);
// border-radius: 3px;
// }
.chart-wrapper {
height: 220px;
width: 100%;
padding: 0 0 5px 0;
min-width: 100%;
}
}
}
.table-container {
display: block;
background-color: #fff;
border-radius: 8px;
overflow-x: hidden;
.table-title {
display: block;
font-size: 16px;
font-weight: bold;
color: #333;
padding: 15px 15px 5px 15px;
}
.table-scroll {
display: block;
width: 100%;
height: 180px;
overflow-x: auto;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
/deep/ .uni-table {
min-width: 100%;
width: max-content;
border: 1px solid #e8e8e8;
&::before, &::after {
display: none !important;
}
.uni-table-tr {
white-space: nowrap;
.uni-table-th {
padding: 10px 8px !important;
}
.uni-table-td {
padding: 6px 4px !important;
}
.uni-table-th, .uni-table-td {
text-align: center !important;
font-size: 14px;
color: #666;
border-bottom: 1px solid #e8e8e8;
border-right: 1px solid #e8e8e8;
min-width: 60px;
}
&:first-child {
border-top: 1px solid #e8e8e8;
background-color: #fafafa;
.uni-table-th {
border-top: none;
}
}
.uni-table-th:last-child,
.uni-table-td:last-child {
border-right: none;
}
&:last-child {
.uni-table-td {
border-bottom: none;
}
}
}
}
}
}
}
@font-face {
font-family: "custom-icon";
src: url('/static/fonts/icomoon.ttf') format('truetype');
}
.sort-icon::before {
content: '\ea4d';
font-family: "custom-icon" !important;
font-size: 12px;
font-style: normal;
-webkit-font-smoothing: antialiased;
}
// 添加基础修复
uni-table {
width: 100% !important;
&::before, &::after {
display: none !important; // 去除默认线条
}
}
// 解决iOS滚动条问题
.chart-scroll {
-webkit-overflow-scrolling: touch;
}
.common-block {
display: block;
}
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
// 应用示例:
.progress-circle .circle-wrapper {
@extend .flex-center;
}
.payable {
.progress-bar {
.progress {
height: 100%;
transition: all 0.3s ease;
&.cash {
left: 0;
z-index: 1;
}
&.monthly {
z-index: 2;
opacity: 0.8;
}
}
}
}
.table-container {
.table-scroll {
::v-deep {
.uni-table-th,
.uni-table-td {
padding: 12px 8px !important;
text-align: center !important;
font-size: 14px !important;
color: #666 !important;
}
}
}
}
</style>
<template>
<view class="abnormal-report">
<!-- 状态统计卡片 -->
<view class="report-stats">
<view class="stat-item" @click="selectReport('new')">
<text class="number" style="color: #faad14;">{{ searchDatas.new }}</text>
<text class="label">未审批</text>
<text class="number2" style="color: #faad14;">¥{{ searchDatas.newAmount }}</text>
</view>
<view class="stat-item" @click="selectReport('approving')">
<text class="number" style="color: #1890ff;">{{ searchDatas.approving }}</text>
<text class="label">审批中</text>
<text class="number2" style="color: #1890ff;">¥{{ searchDatas.approvingAmount }}</text>
</view>
<view class="stat-item" @click="selectReport('approved')">
<text class="number" style="color:#52c41a;">{{ searchDatas.approved }}</text>
<text class="label">审批完成</text>
<text class="number2" style="color:#52c41a;">¥{{ searchDatas.approvedAmount }}</text>
</view>
<view class="stat-item" @click="selectReport('total')">
<text class="number" style="color:#ff4d4f;">{{ searchDatas.total }}</text>
<text class="label">总计</text>
<text class="number2" style="color:#ff4d4f;">¥{{ searchDatas.totalAmount }}</text>
</view>
</view>
<!-- 异常订单列表 -->
<scroll-view class="order-list" scroll-y>
<view class="order-card" v-for="(order, index) in orderList" :key="index" @click="goToDetail(order.id)">
<view class="card-header">
<view class="left">
<text class="order-id">{{ order.orderEventNo }}</text>
<text class="status" :class="getStatusClass(order.approvalStatus)">
{{ order.approvalStatus }}
</text>
</view>
<view class="right">
<text class="time">{{ order.eventTime }}</text>
</view>
</view>
<view class="card-content">
<view class="info-row">
<text class="label">客户:</text>
<text class="value">{{ order.customerName }}</text>
</view>
<view class="info-row">
<text class="label">二级异常:</text>
<text class="value">{{ order.childrenException }}</text>
</view>
<view class="info-row">
<text class="label">涉及金额:</text>
<text class="value amount">¥{{ order.involvingAmount }}</text>
</view>
<view class="info-row">
<text class="label">处理状态:</text>
<text class="value" :class="getProcessClass(order.orderEventStatus)">
{{ order.orderEventStatus }}
</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import {
searchOrderEvent
} from '../../api/apiList'
export default {
data() {
return {
staffCode: "",
staffKind: "",
activeStatus: "total",
searchDatas: {},
orderList: []
}
},
mounted() {
this.initData()
},
methods: {
selectReport(active) {
this.activeStatus = active
this.orderList = []
this.initData()
},
initData() {
this.staffCode = uni.getStorageSync('staffCode');
this.staffKind = uni.getStorageSync('uc_staff_kind');
if (!this.staffCode || !this.staffKind) {
uni.showToast({
title: '获取用户信息失败',
icon: 'none'
});
return;
}
const data = {
aux: {
staffCode: this.staffCode,
staffKind: this.staffKind,
activeStatus: this.activeStatus
}
}
searchOrderEvent(data).then(res => {
const data = res.data.data;
this.searchDatas = data;
this.orderList = data.orderList
})
},
getStatusClass(status) {
const classMap = {
'未审批': 'status-pending',
'审批中': 'status-processing',
'审批完成': 'status-completed'
}
return classMap[status] || ''
},
getProcessClass(status) {
const classMap = {
'待处理': 'process-pending',
'处理中': 'process-ongoing',
'已处理': 'process-completed'
}
return classMap[status] || ''
},
goToDetail(id) {
// 跳转到详情页面
uni.navigateTo({
url: `/pages/abnormal/detail/index?id=${id}`
})
}
}
}
</script>
<style lang="scss">
.abnormal-report {
padding: 0 12px;
background: #f5f7fa;
// min-height: 100vh;
.report-stats {
display: flex;
justify-content: space-around;
background: #fff;
padding: 20px 15px;
border-radius: 8px;
margin-bottom: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
.stat-item {
text-align: center;
// padding: 15px 24px;
background: #f9f9f9;
// border-radius: 6px;
width: 25%;
transition: background 0.3s, transform 0.3s;
/* 添加过渡效果 */
&:hover {
background: #e6f7ff;
/* 鼠标悬停时的背景色 */
transform: scale(1.05);
/* 鼠标悬停时的放大效果 */
}
.number {
display: block;
font-size: 28px;
font-weight: bold;
color: #333;
margin-bottom: 8px;
}
.number2 {
display: block;
font-size: 10px;
// font-weight: bold;
color: #333;
margin-bottom: 8px;
}
.label {
font-size: 14px;
color: #999;
}
}
}
.order-list {
height: calc(100vh - 120px);
.order-card {
background: #fff;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
.left {
display: flex;
align-items: center;
gap: 10px;
.order-id {
font-size: 16px;
font-weight: bold;
color: #333;
}
.status {
padding: 4px 12px;
border-radius: 10px;
font-size: 12px;
&.status-pending {
background: rgba(250, 173, 20, 0.1);
color: #faad14;
border: 1px solid rgba(250, 173, 20, 0.2);
}
&.status-processing {
background: rgba(24, 144, 255, 0.1);
color: #1890ff;
border: 1px solid rgba(24, 144, 255, 0.2);
}
&.status-completed {
background: rgba(82, 196, 26, 0.1);
color: #52c41a;
border: 1px solid rgba(82, 196, 26, 0.2);
}
}
}
.time {
font-size: 14px;
color: #999;
float: right;
}
}
.card-content {
.info-row {
display: flex;
margin-bottom: 8px;
align-items: center;
&:last-child {
margin-bottom: 0;
}
.label {
width: 80px;
font-size: 14px;
color: #666;
}
.value {
flex: 1;
font-size: 14px;
color: #333;
&.amount {
color: #ff4d4f;
font-weight: 500;
}
&.process-pending {
color: #faad14;
}
&.process-ongoing {
color: #1890ff;
}
&.process-completed {
color: #52c41a;
}
}
}
}
}
}
}
.right{
float: right;
}
</style>
\ No newline at end of file
...@@ -102,7 +102,7 @@ export default { ...@@ -102,7 +102,7 @@ export default {
mounted() { mounted() {
this.orderQuery() this.orderQuery()
this.getOptionEnumInit() // this.getOptionEnumInit()
}, },
methods: { methods: {
...@@ -189,7 +189,7 @@ export default { ...@@ -189,7 +189,7 @@ export default {
OrderQuery({ OrderQuery({
"args": { "args": {
pageNum: 1, pageNum: 1,
pageSize: 10000, pageSize: 10,
restrictions restrictions
} }
}).then(res => { }).then(res => {
......
...@@ -36,7 +36,9 @@ ...@@ -36,7 +36,9 @@
{{ order.approvalStatus }} {{ order.approvalStatus }}
</text> </text>
</view> </view>
<view class="right">
<text class="time">{{ order.eventTime }}</text> <text class="time">{{ order.eventTime }}</text>
</view>
</view> </view>
<view class="card-content"> <view class="card-content">
...@@ -158,7 +160,7 @@ ...@@ -158,7 +160,7 @@
text-align: center; text-align: center;
// padding: 15px 24px; // padding: 15px 24px;
background: #f9f9f9; background: #f9f9f9;
border-radius: 6px; // border-radius: 6px;
width: 25%; width: 25%;
transition: background 0.3s, transform 0.3s; transition: background 0.3s, transform 0.3s;
/* 添加过渡效果 */ /* 添加过渡效果 */
...@@ -180,7 +182,7 @@ ...@@ -180,7 +182,7 @@
.number2 { .number2 {
display: block; display: block;
font-size: 28px; font-size: 10px;
// font-weight: bold; // font-weight: bold;
color: #333; color: #333;
margin-bottom: 8px; margin-bottom: 8px;
...@@ -301,4 +303,8 @@ ...@@ -301,4 +303,8 @@
} }
} }
} }
.right{
float: right;
}
</style> </style>
\ No newline at end of file
...@@ -72,7 +72,7 @@ ...@@ -72,7 +72,7 @@
updateTopUserList() { updateTopUserList() {
if (this.uc_staff_kind === 'boss') { if (this.uc_staff_kind === 'boss') {
this.topUserList = [{ this.topUserList = [ {
name: '老板看板', name: '老板看板',
key: 'bossCard', key: 'bossCard',
url: '/pages/index/index' url: '/pages/index/index'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment