|
@@ -3,7 +3,9 @@ import { clientDel, clientGet, clientPost, clientPut } from '@/utils/request.ts'
|
|
|
import { onMounted, ref, reactive } from 'vue'
|
|
import { onMounted, ref, reactive } from 'vue'
|
|
|
import type { BaseResponse } from '@/utils/type.ts'
|
|
import type { BaseResponse } from '@/utils/type.ts'
|
|
|
import { ElMessage, ElMessageBox, ElUpload } from 'element-plus'
|
|
import { ElMessage, ElMessageBox, ElUpload } from 'element-plus'
|
|
|
-import { Search, Plus, Edit, Delete, RefreshCw, Upload, Download } from 'lucide-vue-next'
|
|
|
|
|
|
|
+import { Search, Plus, Edit, Delete, RefreshCw, Upload, Download, TrendingUp, BarChart3 } from 'lucide-vue-next'
|
|
|
|
|
+import * as echarts from 'echarts'
|
|
|
|
|
+import { nextTick } from 'vue'
|
|
|
|
|
|
|
|
interface WaterUsage {
|
|
interface WaterUsage {
|
|
|
id: number;
|
|
id: number;
|
|
@@ -15,8 +17,8 @@ interface WaterUsage {
|
|
|
createTime: string;
|
|
createTime: string;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-interface WaterUsageReponse extends BaseResponse{
|
|
|
|
|
- data:WaterUsage
|
|
|
|
|
|
|
+interface WaterUsageReponse extends BaseResponse {
|
|
|
|
|
+ data: WaterUsage
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
interface WaterUsageList extends BaseResponse {
|
|
interface WaterUsageList extends BaseResponse {
|
|
@@ -37,6 +39,13 @@ const BASE_URL = import.meta.env.VITE_APP_BASE_API
|
|
|
|
|
|
|
|
const uploadRef = ref() // 上传组件引用
|
|
const uploadRef = ref() // 上传组件引用
|
|
|
|
|
|
|
|
|
|
+const trendDialogVisible = ref(false)
|
|
|
|
|
+const trendLoading = ref(false)
|
|
|
|
|
+const trendData = ref<{ statTime: string, totalWater: number, totalAmount: number }[]>([])
|
|
|
|
|
+const selectedCustomerCode = ref<number>(0)
|
|
|
|
|
+const selectedCustomerName = ref<string>('')
|
|
|
|
|
+const chartRef = ref<HTMLDivElement>()
|
|
|
|
|
+
|
|
|
// 分页参数
|
|
// 分页参数
|
|
|
const pagination = reactive({
|
|
const pagination = reactive({
|
|
|
pageNum: 1,
|
|
pageNum: 1,
|
|
@@ -62,40 +71,40 @@ const formData = ref<Partial<WaterUsage>>({
|
|
|
const getList = async () => {
|
|
const getList = async () => {
|
|
|
loading.value = true
|
|
loading.value = true
|
|
|
try {
|
|
try {
|
|
|
- const arr:{
|
|
|
|
|
- value:string,
|
|
|
|
|
|
|
+ const arr: {
|
|
|
|
|
+ value: string,
|
|
|
column: string,
|
|
column: string,
|
|
|
- type:string
|
|
|
|
|
|
|
+ type: string
|
|
|
}[] = [];
|
|
}[] = [];
|
|
|
- if(searchForm.customerCode){
|
|
|
|
|
|
|
+ if (searchForm.customerCode) {
|
|
|
arr.push({
|
|
arr.push({
|
|
|
value: searchForm.customerCode,
|
|
value: searchForm.customerCode,
|
|
|
column: 'customer_code',
|
|
column: 'customer_code',
|
|
|
type: 'like'
|
|
type: 'like'
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
- if(searchForm.customerName){
|
|
|
|
|
|
|
+ if (searchForm.customerName) {
|
|
|
arr.push({
|
|
arr.push({
|
|
|
value: searchForm.customerName,
|
|
value: searchForm.customerName,
|
|
|
column: 'customer_name',
|
|
column: 'customer_name',
|
|
|
type: 'like'
|
|
type: 'like'
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
- if(searchForm.statisticalTime){
|
|
|
|
|
|
|
+ if (searchForm.statisticalTime) {
|
|
|
arr.push({
|
|
arr.push({
|
|
|
value: searchForm.statisticalTime,
|
|
value: searchForm.statisticalTime,
|
|
|
column: 'statistical_time',
|
|
column: 'statistical_time',
|
|
|
type: 'like'
|
|
type: 'like'
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
- const res = await clientGet<{pageNum: number, pageSize: number, conditionJson: string},WaterUsageList>('/park/waterUsageData/findByPage',{
|
|
|
|
|
|
|
+ const res = await clientGet<{ pageNum: number, pageSize: number, conditionJson: string }, WaterUsageList>('/park/waterUsageData/findByPage', {
|
|
|
params: {
|
|
params: {
|
|
|
pageNum: pagination.pageNum,
|
|
pageNum: pagination.pageNum,
|
|
|
pageSize: pagination.pageSize,
|
|
pageSize: pagination.pageSize,
|
|
|
conditionJson: encodeURIComponent(JSON.stringify(arr))
|
|
conditionJson: encodeURIComponent(JSON.stringify(arr))
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
- if(res.code !== 200){
|
|
|
|
|
|
|
+ if (res.code !== 200) {
|
|
|
ElMessage.error(res.msg)
|
|
ElMessage.error(res.msg)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
@@ -135,7 +144,7 @@ const handleAdd = () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const getById = async (id: number) => {
|
|
const getById = async (id: number) => {
|
|
|
- const res = await clientGet<null,WaterUsageReponse>(`/park/waterUsageData/getById/${id}`)
|
|
|
|
|
|
|
+ const res = await clientGet<null, WaterUsageReponse>(`/park/waterUsageData/getById/${id}`)
|
|
|
return res.data // res.data 就是 WaterUsage
|
|
return res.data // res.data 就是 WaterUsage
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -148,6 +157,7 @@ const handleEdit = async (row: WaterUsage) => {
|
|
|
formData.value = { ...data }
|
|
formData.value = { ...data }
|
|
|
dialogVisible.value = true
|
|
dialogVisible.value = true
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
|
|
+ console.log(error)
|
|
|
ElMessage.error('获取用水记录详情失败')
|
|
ElMessage.error('获取用水记录详情失败')
|
|
|
} finally {
|
|
} finally {
|
|
|
loading.value = false
|
|
loading.value = false
|
|
@@ -159,11 +169,11 @@ const handleSave = async () => {
|
|
|
try {
|
|
try {
|
|
|
let res: BaseResponse
|
|
let res: BaseResponse
|
|
|
if (isEdit.value) {
|
|
if (isEdit.value) {
|
|
|
- res = await clientPut<WaterUsage,BaseResponse>('/park/waterUsageData/updateById', formData.value as WaterUsage)
|
|
|
|
|
|
|
+ res = await clientPut<WaterUsage, BaseResponse>('/park/waterUsageData/updateById', formData.value as WaterUsage)
|
|
|
} else {
|
|
} else {
|
|
|
- res = await clientPost<WaterUsage,BaseResponse>('/park/waterUsageData/save', formData.value as WaterUsage)
|
|
|
|
|
|
|
+ res = await clientPost<WaterUsage, BaseResponse>('/park/waterUsageData/save', formData.value as WaterUsage)
|
|
|
}
|
|
}
|
|
|
- if(res.code !== 200){
|
|
|
|
|
|
|
+ if (res.code !== 200) {
|
|
|
ElMessage.error(res.msg)
|
|
ElMessage.error(res.msg)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
@@ -183,19 +193,19 @@ const handleDelete = async (row: WaterUsage) => {
|
|
|
type: 'warning'
|
|
type: 'warning'
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- const res = await clientDel<string[],BaseResponse>('/park/waterUsageData/deleteBatchById',{
|
|
|
|
|
- params:{
|
|
|
|
|
|
|
+ const res = await clientDel<string[], BaseResponse>('/park/waterUsageData/deleteBatchById', {
|
|
|
|
|
+ params: {
|
|
|
ids: [row.id]
|
|
ids: [row.id]
|
|
|
}
|
|
}
|
|
|
- },'comma')
|
|
|
|
|
|
|
+ }, 'comma')
|
|
|
|
|
|
|
|
- if(res.code !== 200){
|
|
|
|
|
|
|
+ if (res.code !== 200) {
|
|
|
ElMessage.error(res.msg)
|
|
ElMessage.error(res.msg)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
ElMessage.success(res.msg)
|
|
ElMessage.success(res.msg)
|
|
|
getList()
|
|
getList()
|
|
|
- } catch {}
|
|
|
|
|
|
|
+ } catch { }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleBatchDelete = async () => {
|
|
const handleBatchDelete = async () => {
|
|
@@ -209,19 +219,19 @@ const handleBatchDelete = async () => {
|
|
|
cancelButtonText: '取消',
|
|
cancelButtonText: '取消',
|
|
|
type: 'warning'
|
|
type: 'warning'
|
|
|
})
|
|
})
|
|
|
- const res = await clientDel<string[],BaseResponse>('/park/waterUsageData/deleteBatchById',{
|
|
|
|
|
- params:{
|
|
|
|
|
|
|
+ const res = await clientDel<string[], BaseResponse>('/park/waterUsageData/deleteBatchById', {
|
|
|
|
|
+ params: {
|
|
|
ids: selectedIds.value
|
|
ids: selectedIds.value
|
|
|
}
|
|
}
|
|
|
- },'comma')
|
|
|
|
|
- if(res.code !== 200){
|
|
|
|
|
|
|
+ }, 'comma')
|
|
|
|
|
+ if (res.code !== 200) {
|
|
|
ElMessage.error(res.msg)
|
|
ElMessage.error(res.msg)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
ElMessage.success(res.msg)
|
|
ElMessage.success(res.msg)
|
|
|
selectedIds.value = []
|
|
selectedIds.value = []
|
|
|
getList()
|
|
getList()
|
|
|
- } catch {}
|
|
|
|
|
|
|
+ } catch { }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleSelectionChange = (selection: WaterUsage[]) => {
|
|
const handleSelectionChange = (selection: WaterUsage[]) => {
|
|
@@ -239,6 +249,138 @@ const handleSizeChange = (size: number) => {
|
|
|
getList()
|
|
getList()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const customerTrendByCode = async (customerCode: number) => {
|
|
|
|
|
+ trendLoading.value = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await clientGet<{ customerCode: number }, TrendResponse>('/park/waterUsageData/customer-trend/' + customerCode)
|
|
|
|
|
+ if (res.code !== 200) {
|
|
|
|
|
+ ElMessage.error(res.msg)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ trendData.value = res.data
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+ renderChart()
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.log(error)
|
|
|
|
|
+ ElMessage.error('获取客户用水趋势失败')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ trendLoading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const renderChart = () => {
|
|
|
|
|
+ if (!chartRef.value || trendData.value.length === 0) return
|
|
|
|
|
+
|
|
|
|
|
+ const chart = echarts.init(chartRef.value)
|
|
|
|
|
+
|
|
|
|
|
+ const option = {
|
|
|
|
|
+ title: {
|
|
|
|
|
+ text: `${selectedCustomerName.value} 用水趋势分析`,
|
|
|
|
|
+ left: 'center',
|
|
|
|
|
+ textStyle: {
|
|
|
|
|
+ fontSize: 16,
|
|
|
|
|
+ fontWeight: 'bold'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'axis',
|
|
|
|
|
+ axisPointer: {
|
|
|
|
|
+ type: 'cross'
|
|
|
|
|
+ },
|
|
|
|
|
+ formatter: (params: any) => {
|
|
|
|
|
+ let result = `${params[0].axisValue}<br/>`
|
|
|
|
|
+ params.forEach((param: any) => {
|
|
|
|
|
+ const unit = param.seriesName === '用水量' ? '吨' : '元'
|
|
|
|
|
+ result += `${param.marker}${param.seriesName}: ${param.value}${unit}<br/>`
|
|
|
|
|
+ })
|
|
|
|
|
+ return result
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ legend: {
|
|
|
|
|
+ data: ['用水量', '金额'],
|
|
|
|
|
+ top: 30
|
|
|
|
|
+ },
|
|
|
|
|
+ grid: {
|
|
|
|
|
+ left: '3%',
|
|
|
|
|
+ right: '4%',
|
|
|
|
|
+ bottom: '3%',
|
|
|
|
|
+ top: '15%',
|
|
|
|
|
+ containLabel: true
|
|
|
|
|
+ },
|
|
|
|
|
+ xAxis: {
|
|
|
|
|
+ type: 'category',
|
|
|
|
|
+ boundaryGap: false,
|
|
|
|
|
+ data: trendData.value.map(item => item.statTime),
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ rotate: 45
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ yAxis: [
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'value',
|
|
|
|
|
+ name: '用水量(吨)',
|
|
|
|
|
+ position: 'left',
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ formatter: '{value} 吨'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'value',
|
|
|
|
|
+ name: '金额(元)',
|
|
|
|
|
+ position: 'right',
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ formatter: '{value} 元'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '用水量',
|
|
|
|
|
+ type: 'line',
|
|
|
|
|
+ yAxisIndex: 0,
|
|
|
|
|
+ data: trendData.value.map(item => item.totalWater),
|
|
|
|
|
+ smooth: true,
|
|
|
|
|
+ symbol: 'circle',
|
|
|
|
|
+ symbolSize: 6,
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ width: 3
|
|
|
|
|
+ },
|
|
|
|
|
+ areaStyle: {
|
|
|
|
|
+ opacity: 0.3
|
|
|
|
|
+ },
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#409EFF'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '金额',
|
|
|
|
|
+ type: 'line',
|
|
|
|
|
+ yAxisIndex: 1,
|
|
|
|
|
+ data: trendData.value.map(item => item.totalAmount),
|
|
|
|
|
+ smooth: true,
|
|
|
|
|
+ symbol: 'circle',
|
|
|
|
|
+ symbolSize: 6,
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ width: 3
|
|
|
|
|
+ },
|
|
|
|
|
+ areaStyle: {
|
|
|
|
|
+ opacity: 0.3
|
|
|
|
|
+ },
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#67C23A'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ chart.setOption(option)
|
|
|
|
|
+
|
|
|
|
|
+ // 响应式调整
|
|
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
|
|
+ chart.resize()
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/** ===== Excel 相关接口 ===== **/
|
|
/** ===== Excel 相关接口 ===== **/
|
|
|
|
|
|
|
|
// 导出 Excel
|
|
// 导出 Excel
|
|
@@ -249,7 +391,7 @@ const handleExport = async () => {
|
|
|
params: { conditionJson },
|
|
params: { conditionJson },
|
|
|
responseType: 'blob'
|
|
responseType: 'blob'
|
|
|
})
|
|
})
|
|
|
- const blob = new Blob([res as any], { type: 'application/vnd.ms-excel' })
|
|
|
|
|
|
|
+ const blob = new Blob([res as Blob], { type: 'application/vnd.ms-excel' })
|
|
|
const url = window.URL.createObjectURL(blob)
|
|
const url = window.URL.createObjectURL(blob)
|
|
|
const a = document.createElement('a')
|
|
const a = document.createElement('a')
|
|
|
a.href = url
|
|
a.href = url
|
|
@@ -257,6 +399,7 @@ const handleExport = async () => {
|
|
|
a.click()
|
|
a.click()
|
|
|
window.URL.revokeObjectURL(url)
|
|
window.URL.revokeObjectURL(url)
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
|
|
+ console.log(e)
|
|
|
ElMessage.error('导出失败')
|
|
ElMessage.error('导出失败')
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -267,7 +410,7 @@ const handleDownloadTemplate = async () => {
|
|
|
const res = await clientGet<null, Blob>('/park/waterUsageData/downloadTemplateWord', {
|
|
const res = await clientGet<null, Blob>('/park/waterUsageData/downloadTemplateWord', {
|
|
|
responseType: 'blob'
|
|
responseType: 'blob'
|
|
|
})
|
|
})
|
|
|
- const blob = new Blob([res as any], { type: 'application/vnd.ms-excel' })
|
|
|
|
|
|
|
+ const blob = new Blob([res as Blob], { type: 'application/vnd.ms-excel' })
|
|
|
const url = window.URL.createObjectURL(blob)
|
|
const url = window.URL.createObjectURL(blob)
|
|
|
const a = document.createElement('a')
|
|
const a = document.createElement('a')
|
|
|
a.href = url
|
|
a.href = url
|
|
@@ -275,12 +418,13 @@ const handleDownloadTemplate = async () => {
|
|
|
a.click()
|
|
a.click()
|
|
|
window.URL.revokeObjectURL(url)
|
|
window.URL.revokeObjectURL(url)
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
|
|
+ console.log(e)
|
|
|
ElMessage.error('下载模板失败')
|
|
ElMessage.error('下载模板失败')
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 导入 Excel
|
|
// 导入 Excel
|
|
|
-const handleImportSuccess = (res: any) => {
|
|
|
|
|
|
|
+const handleImportSuccess = (res: BaseResponse) => {
|
|
|
if (res.code === 200) {
|
|
if (res.code === 200) {
|
|
|
ElMessage.success('导入成功')
|
|
ElMessage.success('导入成功')
|
|
|
getList()
|
|
getList()
|
|
@@ -289,7 +433,22 @@ const handleImportSuccess = (res: any) => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-onMounted(()=>{
|
|
|
|
|
|
|
+const handleViewTrend = (row: WaterUsage) => {
|
|
|
|
|
+ selectedCustomerCode.value = row.customerCode
|
|
|
|
|
+ selectedCustomerName.value = row.customerName
|
|
|
|
|
+ trendDialogVisible.value = true
|
|
|
|
|
+ customerTrendByCode(row.customerCode)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface TrendResponse extends BaseResponse {
|
|
|
|
|
+ data: {
|
|
|
|
|
+ statTime: string,
|
|
|
|
|
+ totalWater: number,
|
|
|
|
|
+ totalAmount: number
|
|
|
|
|
+ }[]
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
getList();
|
|
getList();
|
|
|
})
|
|
})
|
|
|
</script>
|
|
</script>
|
|
@@ -305,10 +464,10 @@ onMounted(()=>{
|
|
|
<!-- 搜索区域 -->
|
|
<!-- 搜索区域 -->
|
|
|
<el-card class="mb-4" shadow="never">
|
|
<el-card class="mb-4" shadow="never">
|
|
|
<el-form :model="searchForm" inline class="search-form">
|
|
<el-form :model="searchForm" inline class="search-form">
|
|
|
- <el-form-item label="公司名称">
|
|
|
|
|
|
|
+ <el-form-item label="客户代码">
|
|
|
<el-input v-model="searchForm.customerCode" placeholder="请输入客户代码" clearable style="width: 200px" />
|
|
<el-input v-model="searchForm.customerCode" placeholder="请输入客户代码" clearable style="width: 200px" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- <el-form-item label="用水记录名称">
|
|
|
|
|
|
|
+ <el-form-item label="客户名称">
|
|
|
<el-input v-model="searchForm.customerName" placeholder="请输入客户名称" clearable style="width: 200px" />
|
|
<el-input v-model="searchForm.customerName" placeholder="请输入客户名称" clearable style="width: 200px" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="统计时间">
|
|
<el-form-item label="统计时间">
|
|
@@ -330,7 +489,7 @@ onMounted(()=>{
|
|
|
<el-button type="success" @click="handleExport" :icon="Download">导出Excel</el-button>
|
|
<el-button type="success" @click="handleExport" :icon="Download">导出Excel</el-button>
|
|
|
<el-upload
|
|
<el-upload
|
|
|
ref="uploadRef"
|
|
ref="uploadRef"
|
|
|
- :action="BASE_URL+'/park/waterUsageData/importExcel'"
|
|
|
|
|
|
|
+ :action="BASE_URL + '/park/waterUsageData/importExcel'"
|
|
|
:show-file-list="false"
|
|
:show-file-list="false"
|
|
|
accept=".xls,.xlsx"
|
|
accept=".xls,.xlsx"
|
|
|
:on-success="handleImportSuccess"
|
|
:on-success="handleImportSuccess"
|
|
@@ -351,10 +510,12 @@ onMounted(()=>{
|
|
|
<el-table-column prop="statisticalTime" label="统计时间" min-width="120" />
|
|
<el-table-column prop="statisticalTime" label="统计时间" min-width="120" />
|
|
|
<el-table-column prop="amount" label="金额" width="100" />
|
|
<el-table-column prop="amount" label="金额" width="100" />
|
|
|
<el-table-column prop="createTime" label="创建时间" width="180" />
|
|
<el-table-column prop="createTime" label="创建时间" width="180" />
|
|
|
- <el-table-column label="操作" width="150" fixed="right">
|
|
|
|
|
|
|
+ <!-- 修改操作列,添加趋势分析按钮 -->
|
|
|
|
|
+ <el-table-column label="操作" width="200" fixed="right">
|
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
|
<el-button type="primary" size="small" @click="handleEdit(row)" :icon="Edit" link>编辑</el-button>
|
|
<el-button type="primary" size="small" @click="handleEdit(row)" :icon="Edit" link>编辑</el-button>
|
|
|
<el-button type="danger" size="small" @click="handleDelete(row)" :icon="Delete" link>删除</el-button>
|
|
<el-button type="danger" size="small" @click="handleDelete(row)" :icon="Delete" link>删除</el-button>
|
|
|
|
|
+ <el-button type="success" size="small" @click="handleViewTrend(row)" :icon="TrendingUp" link>趋势</el-button>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
</el-table>
|
|
</el-table>
|
|
@@ -415,6 +576,64 @@ onMounted(()=>{
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
</el-dialog>
|
|
</el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 添加趋势分析对话框 -->
|
|
|
|
|
+ <el-dialog
|
|
|
|
|
+ v-model="trendDialogVisible"
|
|
|
|
|
+ title="用水趋势分析"
|
|
|
|
|
+ fullscreen
|
|
|
|
|
+ :close-on-click-modal="false"
|
|
|
|
|
+ top="5vh"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div v-loading="trendLoading" class="trend-content">
|
|
|
|
|
+ <div class="mb-4 p-4 bg-blue-50 rounded-lg">
|
|
|
|
|
+ <div class="flex items-center gap-2 mb-2">
|
|
|
|
|
+ <BarChart3 class="w-5 h-5 text-blue-600" />
|
|
|
|
|
+ <span class="font-semibold text-blue-800">客户信息</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="text-sm text-gray-600">
|
|
|
|
|
+ <span class="mr-4">客户代码: <strong>{{ selectedCustomerCode }}</strong></span>
|
|
|
|
|
+ <span>客户名称: <strong>{{ selectedCustomerName }}</strong></span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div v-if="trendData.length > 0" class="chart-container">
|
|
|
|
|
+ <div ref="chartRef" class="w-full h-96"></div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 数据统计卡片 -->
|
|
|
|
|
+ <div class="mt-6 grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
|
|
|
+ <div class="bg-gradient-to-r from-blue-500 to-blue-600 text-white p-4 rounded-lg">
|
|
|
|
|
+ <div class="text-sm opacity-90">总用水量</div>
|
|
|
|
|
+ <div class="text-2xl font-bold">
|
|
|
|
|
+ {{ trendData.reduce((sum, item) => sum + item.totalWater, 0).toFixed(2) }} 吨
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="bg-gradient-to-r from-green-500 to-green-600 text-white p-4 rounded-lg">
|
|
|
|
|
+ <div class="text-sm opacity-90">总金额</div>
|
|
|
|
|
+ <div class="text-2xl font-bold">
|
|
|
|
|
+ ¥{{ trendData.reduce((sum, item) => sum + item.totalAmount, 0).toFixed(2) }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="bg-gradient-to-r from-purple-500 to-purple-600 text-white p-4 rounded-lg">
|
|
|
|
|
+ <div class="text-sm opacity-90">平均用水量</div>
|
|
|
|
|
+ <div class="text-2xl font-bold">
|
|
|
|
|
+ {{ (trendData.reduce((sum, item) => sum + item.totalWater, 0) / trendData.length).toFixed(2) }} 吨
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div v-else-if="!trendLoading" class="text-center py-12">
|
|
|
|
|
+ <div class="text-gray-400 text-lg">暂无趋势数据</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <div class="dialog-footer">
|
|
|
|
|
+ <el-button @click="trendDialogVisible = false">关闭</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
@@ -425,4 +644,16 @@ onMounted(()=>{
|
|
|
.dialog-footer {
|
|
.dialog-footer {
|
|
|
text-align: right;
|
|
text-align: right;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+/* 添加趋势分析相关样式 */
|
|
|
|
|
+.trend-content {
|
|
|
|
|
+ min-height: 400px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.chart-container {
|
|
|
|
|
+ background: white;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|