重构
This commit is contained in:
@ -129,6 +129,15 @@ export function getAmmeterRevenueData(params) {
|
||||
})
|
||||
}
|
||||
|
||||
// 电表报表
|
||||
export function getAmmeterData(params) {
|
||||
return request({
|
||||
url: `/ems/statsReport/getAmmeterData`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 一周冲放曲线
|
||||
export function getSevenChargeData(data) {
|
||||
return request({
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<view class="site-switch-header">
|
||||
<view class="selector-row">
|
||||
<uni-data-picker
|
||||
placeholder="请选择"
|
||||
popup-title="业态选择"
|
||||
@ -10,6 +11,8 @@
|
||||
:ellipsis="false"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<view class="site-count-badge">{{ displaySiteCount }}</view>
|
||||
</view>
|
||||
<view class="info">
|
||||
<view class="list">
|
||||
<uni-icons type="location" color="#fff" size="20"></uni-icons>
|
||||
@ -41,6 +44,19 @@
|
||||
runningTime: {
|
||||
type: String,
|
||||
default: '-'
|
||||
},
|
||||
siteCount: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
displaySiteCount() {
|
||||
const count = Number(this.siteCount || 0)
|
||||
if (!Number.isFinite(count) || count <= 0) {
|
||||
return '0'
|
||||
}
|
||||
return count > 99 ? '99+' : String(count)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -58,6 +74,30 @@
|
||||
padding-bottom: 100rpx;
|
||||
color: #fff;
|
||||
|
||||
.selector-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.site-count-badge {
|
||||
min-width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 0 12rpx;
|
||||
border-radius: 999rpx;
|
||||
background: #ff4d4f;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24rpx;
|
||||
line-height: 1;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
box-sizing: border-box;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 6rpx 14rpx rgba(255, 77, 79, 0.28);
|
||||
}
|
||||
|
||||
.info {
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
|
||||
@ -3,9 +3,11 @@ module.exports = {
|
||||
// todo 打包项目时切换baseUrl
|
||||
// baseUrl: 'http://localhost:8089',
|
||||
// 测试环境
|
||||
baseUrl: 'http://110.40.171.179:8089',
|
||||
// baseUrl: 'http://110.40.171.179:8089',
|
||||
// 生产环境
|
||||
//baseUrl: 'http://1.15.120.242:8089',
|
||||
baseUrl: 'http://111.229.210.50:8089',
|
||||
|
||||
// 应用信息
|
||||
appInfo: {
|
||||
// 应用名称
|
||||
|
||||
@ -120,6 +120,12 @@
|
||||
"navigationBarTitleText": "单体电池",
|
||||
"onReachBottomDistance": 100
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/work/report/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "报表"
|
||||
}
|
||||
}
|
||||
|
||||
],
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view class="home-container">
|
||||
<site-switch-header :site-id="siteId" :site-type-options="siteTypeOptions" :site-address="baseInfo.siteAddress"
|
||||
:running-time="baseInfo.runningTime" @change="selectedSite" />
|
||||
:running-time="baseInfo.runningTime" :site-count="siteOptions.length" @change="selectedSite" />
|
||||
|
||||
<view class="base-info">
|
||||
<view class="map-card">
|
||||
|
||||
@ -55,8 +55,8 @@
|
||||
register: false,
|
||||
globalConfig: getApp().globalData.config,
|
||||
loginForm: {
|
||||
username: "admin",
|
||||
password: "admin123",
|
||||
username: "",
|
||||
password: "",
|
||||
code: "",
|
||||
uuid: ""
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view class="work-container">
|
||||
<site-switch-header :site-id="siteId" :site-type-options="siteTypeOptions" :site-address="baseInfo.siteAddress"
|
||||
:running-time="baseInfo.runningTime" @change="selectedSite" />
|
||||
:running-time="baseInfo.runningTime" :site-count="siteCount" @change="selectedSite" />
|
||||
<!-- 静态信息 -->
|
||||
<view class="base-info">
|
||||
<uni-group mode="card" class="install-data">
|
||||
@ -26,7 +26,8 @@
|
||||
<uni-grid :column="4" :showBorder="false" @change="toDetail">
|
||||
<uni-grid-item v-for="(item,index) in siteGirdList" :index="index" :key="index+'work'">
|
||||
<view class="grid-item-box work-box">
|
||||
<view class="icon iconfont" :class="item.icon" size="30"></view>
|
||||
<image v-if="item.image" :src="item.image" class="icon icon-image" mode="aspectFit" />
|
||||
<view v-else class="icon iconfont" :class="item.icon" size="30"></view>
|
||||
<text class="text">{{item.text}}</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
@ -187,14 +188,26 @@
|
||||
icon: 'icon-dantidianchi',
|
||||
text: '单体电池',
|
||||
categoryName: 'BATTERY'
|
||||
},
|
||||
{
|
||||
page: 'report',
|
||||
icon: 'icon-service',
|
||||
image: '/static/images/work/report.svg',
|
||||
text: '报表',
|
||||
categoryName: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['belongSite', 'currentSiteId']),
|
||||
siteCount() {
|
||||
return (this.siteTypeOptions || []).reduce((count, typeItem) => {
|
||||
return count + ((typeItem.children || []).length)
|
||||
}, 0)
|
||||
},
|
||||
siteGirdList() {
|
||||
return this.gridList.filter(i => this.deviceCategoryOptions.includes(i.categoryName))
|
||||
return this.gridList.filter(i => !i.categoryName || this.deviceCategoryOptions.includes(i.categoryName))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -571,6 +584,13 @@
|
||||
color: #547ef4;
|
||||
}
|
||||
|
||||
.icon-image {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 26rpx;
|
||||
padding-top: 10rpx;
|
||||
@ -578,8 +598,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.base-lists {
|
||||
font-size: 24rpx;
|
||||
line-height: 40rpx;
|
||||
|
||||
580
pages/work/report/index.vue
Normal file
580
pages/work/report/index.vue
Normal file
@ -0,0 +1,580 @@
|
||||
<template>
|
||||
<view class="report-page">
|
||||
<view class="toolbar">
|
||||
<uni-datetime-picker v-model="dateRange" type="daterange" :end="defaultDateRange[1]" :clear-icon="false"
|
||||
rangeSeparator="至" @change="changeDateRange" />
|
||||
<view class="button-group">
|
||||
<button size="mini" class="small" :disabled="loading" @click="resetDateRange">重置</button>
|
||||
<button type="primary" size="mini" class="large" :disabled="loading" @click="getReportData">查询</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loading" class="report-state">报表数据加载中...</view>
|
||||
<view v-else-if="reportList.length === 0" class="report-state">暂无报表数据</view>
|
||||
<view v-else class="report-list">
|
||||
<uni-collapse class="report-collapse">
|
||||
<uni-collapse-item v-for="item in reportList" :key="item.dataTime" class="report-card">
|
||||
<template v-slot:title>
|
||||
<view class="report-card__header">
|
||||
<view class="report-card__title-main">
|
||||
<view class="report-card__date">{{item.dataTime}}</view>
|
||||
<view class="report-card__tags">
|
||||
<text v-if="item.dayType" class="report-tag">{{item.dayType}}</text>
|
||||
<text v-if="item.weatherDesc" class="report-tag weather">{{item.weatherDesc}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="report-summary report-summary--compact">
|
||||
<view class="summary-item emphasis">
|
||||
<text class="label">实际收益</text>
|
||||
<text class="value">{{formatReportValue(item.actualRevenue)}}</text>
|
||||
</view>
|
||||
<view class="summary-item">
|
||||
<text class="label">效率(%)</text>
|
||||
<text class="value">{{formatReportValue(item.effect)}}</text>
|
||||
</view>
|
||||
<view class="summary-item">
|
||||
<text class="label">充电总量(kWh)</text>
|
||||
<text class="value">{{formatReportValue(item.activeTotalKwh)}}</text>
|
||||
</view>
|
||||
<view class="summary-item">
|
||||
<text class="label">放电总量(kWh)</text>
|
||||
<text class="value">{{formatReportValue(item.reActiveTotalKwh)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="report-card__content">
|
||||
<view class="report-group">
|
||||
<view class="report-group__title">电量数据</view>
|
||||
<view class="report-metric-row">
|
||||
<view class="report-metric-row__label">充电量</view>
|
||||
<view class="report-metric-grid">
|
||||
<view v-for="metric in buildMetricItems(item, 'chargeKwh')" :key="item.dataTime + metric.key"
|
||||
class="report-metric">
|
||||
<text class="metric-label">{{metric.label}}</text>
|
||||
<text class="metric-value">{{formatReportValue(metric.value)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="report-metric-row">
|
||||
<view class="report-metric-row__label">放电量</view>
|
||||
<view class="report-metric-grid">
|
||||
<view v-for="metric in buildMetricItems(item, 'dischargeKwh')"
|
||||
:key="item.dataTime + metric.key" class="report-metric">
|
||||
<text class="metric-label">{{metric.label}}</text>
|
||||
<text class="metric-value">{{formatReportValue(metric.value)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="report-group">
|
||||
<view class="report-group__title">收益数据</view>
|
||||
<view class="report-metric-row">
|
||||
<view class="report-metric-row__label">充电价格</view>
|
||||
<view class="report-metric-grid">
|
||||
<view v-for="metric in buildMetricItems(item, 'chargePrice')"
|
||||
:key="item.dataTime + metric.key + 'price'" class="report-metric">
|
||||
<text class="metric-label">{{metric.label}}</text>
|
||||
<text class="metric-value">{{formatReportValue(metric.value)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="report-metric-row">
|
||||
<view class="report-metric-row__label">放电价格</view>
|
||||
<view class="report-metric-grid">
|
||||
<view v-for="metric in buildMetricItems(item, 'dischargePrice')"
|
||||
:key="item.dataTime + metric.key + 'rePrice'" class="report-metric">
|
||||
<text class="metric-label">{{metric.label}}</text>
|
||||
<text class="metric-value">{{formatReportValue(metric.value)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-collapse-item>
|
||||
</uni-collapse>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
formatDate
|
||||
} from '@/utils/filters'
|
||||
import {
|
||||
getAmmeterData,
|
||||
getAmmeterRevenueData
|
||||
} from '@/api/ems/site.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
siteId: '',
|
||||
dateRange: [],
|
||||
defaultDateRange: [],
|
||||
reportList: [],
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initDateRange() {
|
||||
const now = new Date()
|
||||
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1)
|
||||
this.defaultDateRange = [formatDate(firstDay), formatDate(now)]
|
||||
this.dateRange = [...this.defaultDateRange]
|
||||
},
|
||||
changeDateRange(value) {
|
||||
this.dateRange = value || []
|
||||
},
|
||||
resetDateRange() {
|
||||
this.dateRange = [...this.defaultDateRange]
|
||||
this.getReportData()
|
||||
},
|
||||
createEmptyReportRow(dataTime) {
|
||||
return {
|
||||
dataTime,
|
||||
dayType: '',
|
||||
weatherDesc: '',
|
||||
activePeakKwh: null,
|
||||
activeHighKwh: null,
|
||||
activeFlatKwh: null,
|
||||
activeValleyKwh: null,
|
||||
activeTotalKwh: null,
|
||||
reActivePeakKwh: null,
|
||||
reActiveHighKwh: null,
|
||||
reActiveFlatKwh: null,
|
||||
reActiveValleyKwh: null,
|
||||
reActiveTotalKwh: null,
|
||||
effect: null,
|
||||
activePeakPrice: null,
|
||||
activeHighPrice: null,
|
||||
activeFlatPrice: null,
|
||||
activeValleyPrice: null,
|
||||
activeTotalPrice: null,
|
||||
reActivePeakPrice: null,
|
||||
reActiveHighPrice: null,
|
||||
reActiveFlatPrice: null,
|
||||
reActiveValleyPrice: null,
|
||||
reActiveTotalPrice: null,
|
||||
actualRevenue: null
|
||||
}
|
||||
},
|
||||
mergeReportRows(ammeterRows = [], revenueRows = []) {
|
||||
const reportMap = {}
|
||||
const ensureRow = (date) => {
|
||||
const key = String(date || '').trim()
|
||||
if (!key) return null
|
||||
if (!reportMap[key]) {
|
||||
reportMap[key] = this.createEmptyReportRow(key)
|
||||
}
|
||||
return reportMap[key]
|
||||
}
|
||||
;(ammeterRows || []).forEach(item => {
|
||||
const row = ensureRow(item.dataTime)
|
||||
if (!row) return
|
||||
Object.assign(row, {
|
||||
activePeakKwh: item.activePeakKwh,
|
||||
activeHighKwh: item.activeHighKwh,
|
||||
activeFlatKwh: item.activeFlatKwh,
|
||||
activeValleyKwh: item.activeValleyKwh,
|
||||
activeTotalKwh: item.activeTotalKwh,
|
||||
reActivePeakKwh: item.reActivePeakKwh,
|
||||
reActiveHighKwh: item.reActiveHighKwh,
|
||||
reActiveFlatKwh: item.reActiveFlatKwh,
|
||||
reActiveValleyKwh: item.reActiveValleyKwh,
|
||||
reActiveTotalKwh: item.reActiveTotalKwh,
|
||||
effect: item.effect
|
||||
})
|
||||
})
|
||||
;(revenueRows || []).forEach(item => {
|
||||
const row = ensureRow(item.dataTime)
|
||||
if (!row) return
|
||||
Object.assign(row, {
|
||||
dayType: item.dayType,
|
||||
weatherDesc: item.weatherDesc,
|
||||
activePeakPrice: item.activePeakPrice,
|
||||
activeHighPrice: item.activeHighPrice,
|
||||
activeFlatPrice: item.activeFlatPrice,
|
||||
activeValleyPrice: item.activeValleyPrice,
|
||||
activeTotalPrice: item.activeTotalPrice,
|
||||
reActivePeakPrice: item.reActivePeakPrice,
|
||||
reActiveHighPrice: item.reActiveHighPrice,
|
||||
reActiveFlatPrice: item.reActiveFlatPrice,
|
||||
reActiveValleyPrice: item.reActiveValleyPrice,
|
||||
reActiveTotalPrice: item.reActiveTotalPrice,
|
||||
actualRevenue: item.actualRevenue
|
||||
})
|
||||
})
|
||||
return Object.values(reportMap).sort((a, b) => String(b.dataTime).localeCompare(String(a.dataTime)))
|
||||
},
|
||||
buildMetricItems(item, type) {
|
||||
const configMap = {
|
||||
chargeKwh: [{
|
||||
key: 'activePeakKwh',
|
||||
label: '尖',
|
||||
value: item.activePeakKwh
|
||||
},
|
||||
{
|
||||
key: 'activeHighKwh',
|
||||
label: '峰',
|
||||
value: item.activeHighKwh
|
||||
},
|
||||
{
|
||||
key: 'activeFlatKwh',
|
||||
label: '平',
|
||||
value: item.activeFlatKwh
|
||||
},
|
||||
{
|
||||
key: 'activeValleyKwh',
|
||||
label: '谷',
|
||||
value: item.activeValleyKwh
|
||||
},
|
||||
{
|
||||
key: 'activeTotalKwh',
|
||||
label: '总',
|
||||
value: item.activeTotalKwh
|
||||
}
|
||||
],
|
||||
dischargeKwh: [{
|
||||
key: 'reActivePeakKwh',
|
||||
label: '尖',
|
||||
value: item.reActivePeakKwh
|
||||
},
|
||||
{
|
||||
key: 'reActiveHighKwh',
|
||||
label: '峰',
|
||||
value: item.reActiveHighKwh
|
||||
},
|
||||
{
|
||||
key: 'reActiveFlatKwh',
|
||||
label: '平',
|
||||
value: item.reActiveFlatKwh
|
||||
},
|
||||
{
|
||||
key: 'reActiveValleyKwh',
|
||||
label: '谷',
|
||||
value: item.reActiveValleyKwh
|
||||
},
|
||||
{
|
||||
key: 'reActiveTotalKwh',
|
||||
label: '总',
|
||||
value: item.reActiveTotalKwh
|
||||
}
|
||||
],
|
||||
chargePrice: [{
|
||||
key: 'activePeakPrice',
|
||||
label: '尖',
|
||||
value: item.activePeakPrice
|
||||
},
|
||||
{
|
||||
key: 'activeHighPrice',
|
||||
label: '峰',
|
||||
value: item.activeHighPrice
|
||||
},
|
||||
{
|
||||
key: 'activeFlatPrice',
|
||||
label: '平',
|
||||
value: item.activeFlatPrice
|
||||
},
|
||||
{
|
||||
key: 'activeValleyPrice',
|
||||
label: '谷',
|
||||
value: item.activeValleyPrice
|
||||
},
|
||||
{
|
||||
key: 'activeTotalPrice',
|
||||
label: '总',
|
||||
value: item.activeTotalPrice
|
||||
}
|
||||
],
|
||||
dischargePrice: [{
|
||||
key: 'reActivePeakPrice',
|
||||
label: '尖',
|
||||
value: item.reActivePeakPrice
|
||||
},
|
||||
{
|
||||
key: 'reActiveHighPrice',
|
||||
label: '峰',
|
||||
value: item.reActiveHighPrice
|
||||
},
|
||||
{
|
||||
key: 'reActiveFlatPrice',
|
||||
label: '平',
|
||||
value: item.reActiveFlatPrice
|
||||
},
|
||||
{
|
||||
key: 'reActiveValleyPrice',
|
||||
label: '谷',
|
||||
value: item.reActiveValleyPrice
|
||||
},
|
||||
{
|
||||
key: 'reActiveTotalPrice',
|
||||
label: '总',
|
||||
value: item.reActiveTotalPrice
|
||||
}
|
||||
]
|
||||
}
|
||||
return configMap[type] || []
|
||||
},
|
||||
formatReportValue(value) {
|
||||
if (!(value || value === 0 || value === '0')) return '-'
|
||||
const num = Number(value)
|
||||
if (!Number.isFinite(num)) return value
|
||||
return num.toFixed(3).replace(/\.?0+$/, '')
|
||||
},
|
||||
getReportData() {
|
||||
if (!this.siteId || !this.dateRange.length) return
|
||||
const [startTime = '', endTime = ''] = this.dateRange || []
|
||||
this.loading = true
|
||||
Promise.all([
|
||||
getAmmeterData({
|
||||
siteId: this.siteId,
|
||||
startTime,
|
||||
endTime,
|
||||
pageSize: 200,
|
||||
pageNum: 1
|
||||
}),
|
||||
getAmmeterRevenueData({
|
||||
siteId: this.siteId,
|
||||
startTime,
|
||||
endTime,
|
||||
pageSize: 200,
|
||||
pageNum: 1
|
||||
})
|
||||
]).then(([ammeterResponse, revenueResponse]) => {
|
||||
const ammeterRows = ammeterResponse?.rows || []
|
||||
const revenueRows = revenueResponse?.rows || []
|
||||
this.reportList = this.mergeReportRows(ammeterRows, revenueRows)
|
||||
}).catch(() => {
|
||||
this.reportList = []
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.siteId = options.siteId || ''
|
||||
this.initDateRange()
|
||||
this.getReportData()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.report-page {
|
||||
min-height: 100%;
|
||||
padding: 24rpx;
|
||||
background: #f5f6f8;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
padding: 24rpx;
|
||||
border-radius: 20rpx;
|
||||
background: #fff;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
margin-top: 20rpx;
|
||||
|
||||
uni-button {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
text-align: center;
|
||||
font-size: 26rpx;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.small {
|
||||
width: 120rpx;
|
||||
}
|
||||
|
||||
.large {
|
||||
width: 160rpx;
|
||||
background-color: #547ef4;
|
||||
|
||||
&[disabled][type=primary] {
|
||||
background-color: #89a8ffe6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.report-state {
|
||||
padding: 80rpx 0;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
.report-card {
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.report-collapse {
|
||||
:deep(.uni-collapse) {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
:deep(.uni-collapse-item) {
|
||||
margin-bottom: 20rpx;
|
||||
border: 0;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
:deep(.uni-collapse-item__title-wrap) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.uni-collapse-item__title-box) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.uni-collapse-item__content) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.report-card__header {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.report-card__title-main {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 20rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.report-card__date {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
.report-card__tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.report-tag {
|
||||
padding: 8rpx 18rpx;
|
||||
border-radius: 999rpx;
|
||||
background: #eef3ff;
|
||||
color: #547ef4;
|
||||
font-size: 22rpx;
|
||||
line-height: 1;
|
||||
|
||||
&.weather {
|
||||
background: #f5f7fa;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
|
||||
.report-summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 18rpx;
|
||||
}
|
||||
|
||||
.report-summary--compact {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
padding: 18rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #f7f8fa;
|
||||
|
||||
&.emphasis {
|
||||
background: linear-gradient(135deg, #547ef4, #6ea1ff);
|
||||
|
||||
.label,
|
||||
.value {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #8a919f;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #1f2329;
|
||||
line-height: 1.2;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.report-group:not(:last-child) {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.report-card__content {
|
||||
padding: 0 24rpx 24rpx;
|
||||
}
|
||||
|
||||
.report-group__title {
|
||||
margin-bottom: 16rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
.report-metric-row:not(:last-child) {
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.report-metric-row__label {
|
||||
margin-bottom: 12rpx;
|
||||
font-size: 22rpx;
|
||||
color: #8a919f;
|
||||
}
|
||||
|
||||
.report-metric-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.report-metric {
|
||||
padding: 14rpx 10rpx;
|
||||
border-radius: 12rpx;
|
||||
background: #f7f8fa;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
font-size: 20rpx;
|
||||
color: #8a919f;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
color: #1f2329;
|
||||
line-height: 1.3;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
12
static/images/work/report.svg
Normal file
12
static/images/work/report.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="20" y="12" width="88" height="104" rx="16" fill="#EEF4FF"/>
|
||||
<path d="M44 12H88L108 32V40H44V12Z" fill="#DCE8FF"/>
|
||||
<path d="M88 12V28C88 30.2091 89.7909 32 92 32H108" fill="#C8DAFF"/>
|
||||
<rect x="36" y="52" width="56" height="8" rx="4" fill="#9FB9FF"/>
|
||||
<rect x="36" y="68" width="40" height="8" rx="4" fill="#C5D6FF"/>
|
||||
<rect x="38" y="88" width="10" height="16" rx="5" fill="#7AA2FF"/>
|
||||
<rect x="56" y="78" width="10" height="26" rx="5" fill="#5B87F7"/>
|
||||
<rect x="74" y="70" width="10" height="34" rx="5" fill="#2F67E8"/>
|
||||
<rect x="92" y="82" width="10" height="22" rx="5" fill="#89ACFF"/>
|
||||
<rect x="20" y="12" width="88" height="104" rx="16" stroke="#5B87F7" stroke-width="4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 807 B |
Reference in New Issue
Block a user