351 lines
6.3 KiB
Vue
351 lines
6.3 KiB
Vue
<template>
|
||
<view class="contentStyle" style="">
|
||
<view class="status-bar"></view>
|
||
<uni-nav-bar title="缴费记录" left-icon="left" @clickLeft="goBack" backgroundColor="transparent"
|
||
:border="false">
|
||
</uni-nav-bar>
|
||
<Loading mask="true" click="true" ref="loading"></Loading>
|
||
|
||
<!-- 表单区域 -->
|
||
<scroll-view class="page" scroll-y="true">
|
||
|
||
<!-- 累计缴费 -->
|
||
<view class="total-box">
|
||
<view class="total-title">累计缴费</view>
|
||
<view class="total-price">¥800.00</view>
|
||
|
||
<!-- 折线图 Canvas -->
|
||
<canvas canvas-id="chargeChart" class="chart-canvas"></canvas>
|
||
</view>
|
||
|
||
<!-- 缴费明细 -->
|
||
<view class="list-wrapper">
|
||
<!-- 按月份分组渲染 -->
|
||
<view class="month-group" v-for="(item, idx) in monthGroups" :key="idx">
|
||
<view class="month-title">
|
||
{{ item.month }}月 缴费 ¥{{ item.total }}元
|
||
</view>
|
||
|
||
<view class="item-list">
|
||
<view class="bill-item" v-for="bill in item.list" :key="bill.id">
|
||
<view class="bill-icon">
|
||
<text class="icon">{{ bill.icon }}</text>
|
||
</view>
|
||
<view class="bill-info">
|
||
<view class="name">{{ bill.name }}</view>
|
||
<view class="account">{{ bill.account }}</view>
|
||
</view>
|
||
<view class="bill-price">-{{ bill.amount }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
|
||
|
||
</scroll-view>
|
||
</view>
|
||
|
||
|
||
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
onReady: function(e) {},
|
||
components: {
|
||
|
||
},
|
||
|
||
computed: {
|
||
|
||
|
||
// 自动按月份分组
|
||
monthGroups() {
|
||
let map = {}
|
||
this.billList.forEach(bill => {
|
||
if (!map[bill.month]) {
|
||
map[bill.month] = {
|
||
month: bill.month,
|
||
total: 0,
|
||
list: []
|
||
}
|
||
}
|
||
map[bill.month].list.push(bill)
|
||
map[bill.month].total += bill.amount
|
||
})
|
||
return Object.values(map).sort((a, b) => b.month - a.month)
|
||
}
|
||
},
|
||
created() {
|
||
|
||
},
|
||
onShow() {
|
||
|
||
},
|
||
|
||
mounted() {},
|
||
|
||
onReady() {
|
||
this.$nextTick(() => {
|
||
|
||
this.drawLineChart();
|
||
})
|
||
},
|
||
data() {
|
||
return {
|
||
/* 设定状态栏默认高度 */
|
||
statusBarHeight: 20,
|
||
|
||
// 折线图数据
|
||
chart: {
|
||
months: ['1月', '2月', '3月', '4月', '5月', '6月'],
|
||
values: [100, 140, 230, 100, 130, 100]
|
||
},
|
||
|
||
// ====================== 核心:5月6月放在一个list里 ======================
|
||
billList: [
|
||
// 5月数据
|
||
{
|
||
id: 1,
|
||
month: 5,
|
||
name: '燃气费',
|
||
account: '123123123123',
|
||
amount: 25,
|
||
icon: ''
|
||
},
|
||
{
|
||
id: 2,
|
||
month: 5,
|
||
name: '水费',
|
||
account: '123123123123',
|
||
amount: 25,
|
||
icon: ''
|
||
},
|
||
|
||
// 6月数据
|
||
{
|
||
id: 3,
|
||
month: 6,
|
||
name: '燃气费',
|
||
account: '123123123123',
|
||
amount: 30,
|
||
icon: ''
|
||
},
|
||
{
|
||
id: 4,
|
||
month: 6,
|
||
name: '水费',
|
||
account: '123123123123',
|
||
amount: 10,
|
||
icon: ''
|
||
},
|
||
{
|
||
id: 5,
|
||
month: 6,
|
||
name: '电费',
|
||
account: '123123123123',
|
||
amount: 30,
|
||
icon: '⚡'
|
||
}
|
||
]
|
||
|
||
|
||
}
|
||
},
|
||
|
||
|
||
methods: {
|
||
drawLineChart() {
|
||
const ctx = uni.createCanvasContext('chargeChart', this)
|
||
const canvasPage = uni.getSystemInfoSync();
|
||
const w = canvasPage.windowWidth - 30;
|
||
const h = canvasPage.windowHeight * 0.3;
|
||
|
||
const padding = 40
|
||
const stepX = (w - 60) / (this.chart.months.length - 1)
|
||
const maxVal = 250
|
||
|
||
ctx.setFillStyle('#999')
|
||
ctx.setFontSize(12)
|
||
ctx.setTextAlign('center')
|
||
|
||
// 绘制网格
|
||
ctx.beginPath()
|
||
ctx.setStrokeStyle('#d8d8d8')
|
||
ctx.setLineWidth(1)
|
||
for (let i = 0; i <= 5; i++) {
|
||
let y = padding + (h - padding * 2) * (1 - i / 5)
|
||
ctx.moveTo(40, y)
|
||
ctx.lineTo(w - 20, y)
|
||
ctx.fillText(i * 50 + '元', 20, y + 4)
|
||
}
|
||
ctx.stroke()
|
||
|
||
// 绘制X轴月份
|
||
this.chart.months.forEach((m, i) => {
|
||
let x = 40 + i * stepX
|
||
ctx.fillText(m, x, h - 20)
|
||
})
|
||
|
||
// 绘制折线
|
||
let points = []
|
||
this.chart.values.forEach((val, i) => {
|
||
let x = 40 + i * stepX
|
||
let y = padding + (h - padding * 2) * (1 - val / maxVal)
|
||
points.push({
|
||
x,
|
||
y
|
||
})
|
||
})
|
||
|
||
ctx.beginPath()
|
||
ctx.setStrokeStyle('#4080FF')
|
||
ctx.setLineWidth(2)
|
||
points.forEach((p, i) => {
|
||
if (i === 0) ctx.moveTo(p.x, p.y)
|
||
else ctx.lineTo(p.x, p.y)
|
||
})
|
||
ctx.stroke()
|
||
|
||
// 绘制圆点
|
||
points.forEach((p, i) => {
|
||
ctx.beginPath()
|
||
ctx.arc(p.x, p.y, 3, 0, 2 * Math.PI)
|
||
ctx.setFillStyle('#fff')
|
||
ctx.fill()
|
||
ctx.setStrokeStyle('#4080FF')
|
||
ctx.setLineWidth(2)
|
||
ctx.stroke()
|
||
|
||
ctx.setFillStyle('#4080FF')
|
||
ctx.setFontSize(13)
|
||
ctx.fillText(this.chart.values[i] + '元', p.x, p.y - 10)
|
||
})
|
||
|
||
ctx.draw()
|
||
},
|
||
goBack() {
|
||
uni.redirectTo({
|
||
url: '/pages/payment-list/paymentIndex',
|
||
success: res => {},
|
||
fail: () => {},
|
||
complete: () => {}
|
||
});
|
||
},
|
||
|
||
},
|
||
|
||
|
||
}
|
||
</script>
|
||
<style lang="scss">
|
||
page {
|
||
background: #f2f1f6;
|
||
}
|
||
</style>
|
||
|
||
<style scoped>
|
||
.contentStyle {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
/* 关键:占满屏幕高度 */
|
||
background-color: #f9f9f9;
|
||
}
|
||
|
||
.page {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 0px 20px;
|
||
box-sizing: border-box;
|
||
background-color: #fff;
|
||
}
|
||
|
||
|
||
.total-box {
|
||
padding: 15px;
|
||
/* margin-bottom: 10px; */
|
||
}
|
||
|
||
.total-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.total-price {
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
margin-top: 6px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.chart-canvas {
|
||
width: calc(100vw - 30px);
|
||
height: 30vh;
|
||
}
|
||
|
||
.list-wrapper {
|
||
padding: 0 15px;
|
||
}
|
||
|
||
.month-group {
|
||
padding-bottom: 15px;
|
||
border-bottom: 1px solid #f2f2f2;
|
||
}
|
||
|
||
.month-group:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.month-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.bill-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 15px;
|
||
|
||
}
|
||
|
||
.bill-icon {
|
||
width: 40px;
|
||
height: 40px;
|
||
background: #e6efff;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #4080FF;
|
||
margin-right: 12px;
|
||
font-size: 18px;
|
||
}
|
||
|
||
.bill-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.name {
|
||
font-size: 14px;
|
||
color: #333;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.account {
|
||
font-size: 12px;
|
||
color: #999;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.bill-price {
|
||
font-size: 16px;
|
||
color: #333;
|
||
}.status-bar{
|
||
width: 100vw;
|
||
height: 46px;
|
||
}
|
||
</style> |