fix: 🧩 图标显示问题;增加收入和支出合计显示

This commit is contained in:
Bunny 2024-11-25 12:19:05 +08:00
parent 2a75ff30e0
commit 476d4677a5
14 changed files with 597 additions and 209 deletions

View File

@ -20,7 +20,7 @@ VITE_BASE_API_RETRY=5
VITE_BASE_API_RETRY_DELAY=3000 VITE_BASE_API_RETRY_DELAY=3000
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false # 是否在打包时使用cdn替换本地库 替换 true 不替换 false
VITE_CDN=true VITE_CDN=false
# 是否启用gzip压缩或brotli压缩分两种情况删除原始文件和不删除原始文件 # 是否启用gzip压缩或brotli压缩分两种情况删除原始文件和不删除原始文件
# 压缩时不删除原始文件的配置gzip、brotli、both同时开启 gzip 与 brotli 压缩、none不开启压缩默认 # 压缩时不删除原始文件的配置gzip、brotli、both同时开启 gzip 与 brotli 压缩、none不开启压缩默认

View File

@ -3,6 +3,16 @@ import type { BaseResult, ResultTable } from '@/api/service/types';
/** 账单信息---获取账单信息列表 */ /** 账单信息---获取账单信息列表 */
export const fetchGetBillList = (data: any) => { export const fetchGetBillList = (data: any) => {
data = {
userId: data.userId,
amount: data.amount,
type: data.type,
description: data.description,
startDate: data.startDate,
endDate: data.endDate,
pageSize: data.pageSize,
currentPage: data.currentPage,
};
return http.request<BaseResult<ResultTable>>('get', `bill/getBillList/${data.currentPage}/${data.pageSize}`, { params: data }); return http.request<BaseResult<ResultTable>>('get', `bill/getBillList/${data.currentPage}/${data.pageSize}`, { params: data });
}; };

View File

@ -3,13 +3,21 @@ import type { BaseResult, ResultTable } from '@/api/service/types';
/** 账单信息---获取账单信息列表 */ /** 账单信息---获取账单信息列表 */
export const fetchGetUserBillList = (data: any) => { export const fetchGetUserBillList = (data: any) => {
// 删除这个请求参数 data = {
delete data.date; amount: data.amount,
type: data.type,
description: data.description,
startDate: data.startDate,
endDate: data.endDate,
pageSize: data.pageSize,
currentPage: data.currentPage,
};
return http.request<BaseResult<ResultTable>>('get', `bill/noManage/getUserBillList/${data.currentPage}/${data.pageSize}`, { params: data }); return http.request<BaseResult<ResultTable>>('get', `bill/noManage/getUserBillList/${data.currentPage}/${data.pageSize}`, { params: data });
}; };
/** 账单信息---账单收入和支出 */ /** 账单信息---账单收入和支出 */
export const fetchGetExpendOrIncome = (data: any) => { export const fetchGetExpendOrIncome = (data: any) => {
data = { userId: data.userId, type: data.type, startDate: data.startDate, endDate: data.endDate };
return http.request<BaseResult<object>>('get', 'bill/noManage/getExpendOrIncome', { params: data }); return http.request<BaseResult<object>>('get', 'bill/noManage/getExpendOrIncome', { params: data });
}; };

View File

@ -1,166 +0,0 @@
<script lang="tsx" setup>
import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
import PureTable from '@pureadmin/table';
import { $t } from '@/plugins/i18n';
import { ElTag, ElText } from 'element-plus';
import { onMounted, reactive, ref, watch } from 'vue';
import { fetchGetUserBillList } from '@/api/v1/financial/user/billUser';
import Empty from './empty.svg?component';
const props = defineProps({
financialType: { type: Number as PropType<any>, default: undefined },
startDate: { type: String as PropType<any>, default: undefined },
endDate: { type: String as PropType<any>, default: undefined },
});
const columns: TableColumnList = [
{ type: 'index', index: (index: number) => index + 1, label: '序号', width: 60 },
//
{
label: $t('amount'),
prop: 'amount',
sortable: true,
formatter({ type, amount }) {
return type === -1 ? (
<ElText size={'large'} type={'danger'} style={{ fontWeight: 800 }}>
- {amount}
</ElText>
) : (
<ElText size={'large'} type={'success'} style={{ fontWeight: 800 }}>
+ {amount}
</ElText>
);
},
width: 200,
},
//
{
label: $t('categoryName'),
prop: 'categoryName',
width: 150,
formatter({ categoryName }) {
return <ElTag effect={'plain'}>{categoryName}</ElTag>;
},
},
//
{ label: $t('description'), prop: 'description' },
//
{
label: $t('transactionDate'),
prop: 'transactionDate',
width: 190,
sortable: true,
formatter({ transactionDate }) {
return (
<ElText type={'success'} style={{ fontWeight: 800 }}>
{transactionDate}
</ElText>
);
},
},
];
const form = reactive({
// 1 - -1 -
type: props.financialType,
//
startDate: props.startDate,
//
endDate: props.endDate,
pagination: {
pageSize: 10,
currentPage: 1,
layout: 'prev, pager, next',
total: 1,
align: 'center',
hideOnSinglePage: false,
},
});
const dataList = ref([]);
const loading = ref(false);
/* 初始化数据 */
async function onSearch() {
loading.value = true;
const result = await fetchGetUserBillList({ ...form, ...form.pagination });
dataList.value = result.data.list;
form.pagination.currentPage = result.data.pageNo;
form.pagination.pageSize = result.data.pageSize;
form.pagination.total = result.data.total;
loading.value = false;
}
/* 修改分页 */
async function onCurrentChange(page: number) {
form.pagination.currentPage = page;
await onSearch();
}
onMounted(() => {
watch(
() => props,
async () => {
form.startDate = props.startDate;
form.endDate = props.endDate;
await onSearch();
},
{ immediate: true, deep: true },
);
});
</script>
<template>
<pure-table
:columns="columns"
:data="dataList"
:loading="loading"
:loading-config="{ background: 'transparent' }"
:pagination="form.pagination"
alignWhole="center"
row-key="id"
showOverflowTooltip
@page-current-change="onCurrentChange"
>
<template #empty>
<el-empty :image-size="60" description="暂无数据" style="height: 430px">
<template #image>
<Empty />
</template>
</el-empty>
</template>
<template #operation="{ row }">
<el-button :icon="useRenderIcon('ri:search-line')" :title="`查看序号为${row.id}的详情`" circle plain size="small" />
</template>
</pure-table>
</template>
<style lang="scss">
.pure-table-filter {
.el-table-filter__list {
min-width: 80px;
padding: 0;
li {
line-height: 28px;
}
}
}
</style>
<style lang="scss" scoped>
:deep(.el-table) {
--el-table-border: none;
--el-table-border-color: transparent;
.el-empty__description {
margin: 0;
}
.el-scrollbar__bar {
display: none;
}
}
</style>

View File

@ -19,13 +19,9 @@ const props = defineProps({
}); });
const { isDark } = useDark(); const { isDark } = useDark();
const theme = computed(() => (isDark.value ? 'dark' : 'light')); const theme = computed(() => (isDark.value ? 'dark' : 'light'));
const chartRef = ref(); const chartRef = ref();
const { setOptions } = useECharts(chartRef, { const { setOptions } = useECharts(chartRef, { theme });
theme,
});
watch( watch(
() => props, () => props,

View File

@ -22,40 +22,23 @@ watch(
async () => { async () => {
await nextTick(); // DOM await nextTick(); // DOM
setOptions({ setOptions({
tooltip: { tooltip: { trigger: 'item' },
trigger: 'item', legend: { show: true, textStyle: { color: '#606266', fontSize: '0.875rem' }, bottom: 0 },
},
legend: {
textStyle: {
color: '#606266',
fontSize: '0.875rem',
},
bottom: 0,
},
series: [ series: [
{ {
type: 'pie', type: 'pie',
radius: [16, 130], radius: [10, 110],
roseType: 'area', roseType: 'area',
center: ['50%', '43%'], center: ['50%', '30%'],
labelLine: { length: 3, length2: 6, smooth: true }, labelLine: { length: 3, length2: 6, smooth: true },
itemStyle: { itemStyle: { borderRadius: [5, 5, 5, 5] },
borderRadius: [5, 5, 5, 5], label: { position: 'outer', alignTo: 'edge', edgeDistance: 10 },
},
label: {
position: 'outer',
alignTo: 'edge',
edgeDistance: 10,
},
data: props.categoryData, data: props.categoryData,
}, },
], ],
}); });
}, },
{ { deep: true, immediate: true },
deep: true,
immediate: true,
},
); );
</script> </script>

View File

@ -7,37 +7,60 @@ import CharLine from '@/components/Analyse/char-line.vue';
import CharPie from '@/components/Analyse/char-pie.vue'; import CharPie from '@/components/Analyse/char-pie.vue';
import { $t } from '@/plugins/i18n'; import { $t } from '@/plugins/i18n';
import { currentMouth, currentWeek, currentYear } from '@/enums/dateEnums'; import { currentMouth, currentWeek, currentYear } from '@/enums/dateEnums';
import AnalyseTable from '@/components/Analyse/analyse-table.vue'; import AnalyseTable from '@/components/Analyse/table/analyse-table.vue';
interface Title { interface Title {
//
analyse: string; analyse: string;
//
category: string; category: string;
//
table: string; table: string;
//
top: string; top: string;
} }
const props = defineProps({ const props = defineProps({
//
title: { type: Object as PropType<Title> }, title: { type: Object as PropType<Title> },
//
financialType: { type: Number as PropType<any> }, financialType: { type: Number as PropType<any> },
//
count: { type: Number as PropType<any>, default: 0 },
//
color: { color: {
type: Array as PropType<Array<string>>, type: Array as PropType<Array<string>>,
}, },
//
dataList: { type: Array as PropType<any>, default: () => [] },
//
form: { type: Object as PropType<any>, default: () => ({}) },
//
onCurrentChange: {
type: Function as PropType<any>,
default: () => {},
},
//
date: { date: {
type: Array as PropType<Array<number>>, type: Array as PropType<Array<number>>,
default: () => [], default: () => [],
}, },
// x
analyseXAxis: { analyseXAxis: {
type: Array as PropType<Array<number>>, type: Array as PropType<Array<number>>,
default: () => [], default: () => [],
}, },
//
analyseData: { analyseData: {
type: Array as PropType<Array<number>>, type: Array as PropType<Array<number>>,
default: () => [], default: () => [],
}, },
//
categoryData: { categoryData: {
type: Array as PropType<Array<number>>, type: Array as PropType<Array<number>>,
default: () => [], default: () => [],
}, },
//
topData: { topData: {
type: Array as PropType<Array<number>>, type: Array as PropType<Array<number>>,
default: () => [], default: () => [],
@ -46,10 +69,8 @@ const props = defineProps({
const emits = defineEmits(['emitDateRange']); const emits = defineEmits(['emitDateRange']);
const { isDark } = useDark(); const { isDark } = useDark();
// //
const dateRange = ref(props.date); const dateRange = ref(props.date);
// //
const shortcuts = [ const shortcuts = [
{ text: $t('thisWeek'), value: currentWeek }, { text: $t('thisWeek'), value: currentWeek },
@ -67,11 +88,13 @@ const onChangeDateRange = () => {
<template> <template>
<div> <div>
<el-row :gutter="24" justify="space-around"> <el-row :gutter="24" justify="space-around">
<!-- 分析概览 -->
<re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 400 } }" :initial="{ opacity: 0, y: 100 }" :value="18" :xs="24" class="mb-[18px]"> <re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 400 } }" :initial="{ opacity: 0, y: 100 }" :value="18" :xs="24" class="mb-[18px]">
<el-card class="bar-card" shadow="never"> <el-card class="bar-card" shadow="never">
<div class="flex justify-between"> <div class="flex justify-between">
<span class="text-md font-medium">{{ title.analyse }}</span> <span class="text-md font-medium">{{ title.analyse }}</span>
<span> <span>
<slot name="formBefore" />
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
:end-placeholder="$t('endDate')" :end-placeholder="$t('endDate')"
@ -83,6 +106,8 @@ const onChangeDateRange = () => {
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
@change="onChangeDateRange" @change="onChangeDateRange"
/> />
<slot name="formAfter" />
<el-text class="ml-2" size="large" type="danger">{{ $t('sumUp') }}{{ count.toFixed(2) }}</el-text>
</span> </span>
</div> </div>
<div class="flex justify-between items-start mt-3"> <div class="flex justify-between items-start mt-3">
@ -91,6 +116,7 @@ const onChangeDateRange = () => {
</el-card> </el-card>
</re-col> </re-col>
<!-- 分类图表-->
<re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 400 } }" :initial="{ opacity: 0, y: 100 }" :value="6" :xs="24" class="mb-[18px]"> <re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 400 } }" :initial="{ opacity: 0, y: 100 }" :value="6" :xs="24" class="mb-[18px]">
<el-card class="bar-card" shadow="never"> <el-card class="bar-card" shadow="never">
<div class="flex justify-between"> <div class="flex justify-between">
@ -102,15 +128,17 @@ const onChangeDateRange = () => {
</el-card> </el-card>
</re-col> </re-col>
<!-- 数据统计表格 -->
<re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 560 } }" :initial="{ opacity: 0, y: 100 }" :value="18" :xs="24" class="mb-[18px]"> <re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 560 } }" :initial="{ opacity: 0, y: 100 }" :value="18" :xs="24" class="mb-[18px]">
<el-card class="h-[580px]" shadow="never"> <el-card class="h-[580px]" shadow="never">
<div class="flex justify-between"> <div class="flex justify-between">
<span class="text-md font-medium">{{ title.table }}</span> <span class="text-md font-medium">{{ title.table }}</span>
</div> </div>
<analyse-table :endDate="dateRange[1]" :financialType="financialType" :startDate="dateRange[0]" class="mt-3" /> <analyse-table :data-list="dataList" :form="form" :on-current-change="onCurrentChange" class="mt-3" />
</el-card> </el-card>
</re-col> </re-col>
<!-- 排行榜 -->
<re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 560 } }" :initial="{ opacity: 0, y: 100 }" :value="6" :xs="24" class="mb-[18px]"> <re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 560 } }" :initial="{ opacity: 0, y: 100 }" :value="6" :xs="24" class="mb-[18px]">
<el-card shadow="never"> <el-card shadow="never">
<div class="flex justify-between"> <div class="flex justify-between">

View File

@ -0,0 +1,65 @@
<script lang="ts" setup>
import PureTable from '@pureadmin/table';
import Empty from './empty.svg?component';
import { columns } from '@/components/Analyse/table/columns';
const props = defineProps({
dataList: { type: Array as PropType<any>, default: () => [] },
form: { type: Object as PropType<any>, default: () => ({}) },
onCurrentChange: {
type: Function as PropType<any>,
default: () => {},
},
});
</script>
<template>
<pure-table
:columns="columns"
:data="dataList"
:height="430"
:loading="form.loading"
:loading-config="{ background: 'transparent' }"
:pagination="form.pagination"
alignWhole="center"
row-key="id"
showOverflowTooltip
@page-current-change="onCurrentChange"
>
<template #empty>
<el-empty :image-size="60" description="暂无数据" style="height: 430px">
<template #image>
<Empty />
</template>
</el-empty>
</template>
</pure-table>
</template>
<style lang="scss">
.pure-table-filter {
.el-table-filter__list {
min-width: 80px;
padding: 0;
li {
line-height: 28px;
}
}
}
</style>
<style lang="scss" scoped>
:deep(.el-table) {
--el-table-border: none;
--el-table-border-color: transparent;
.el-empty__description {
margin: 0;
}
.el-scrollbar__bar {
display: none;
}
}
</style>

View File

@ -0,0 +1,48 @@
import { $t } from '@/plugins/i18n';
export const columns: TableColumnList = [
{ type: 'index', index: (index: number) => index + 1, label: '序号', width: 60 },
// 金额
{
label: $t('amount'),
prop: 'amount',
sortable: true,
formatter({ type, amount }) {
return type === -1 ? (
<ElText size={'large'} type={'danger'} style={{ fontWeight: 800 }}>
- {amount}
</ElText>
) : (
<ElText size={'large'} type={'success'} style={{ fontWeight: 800 }}>
+ {amount}
</ElText>
);
},
width: 200,
},
// 类别
{
label: $t('categoryName'),
prop: 'categoryName',
width: 150,
formatter({ categoryName }) {
return <ElTag effect={'plain'}>{categoryName}</ElTag>;
},
},
// 描述
{ label: $t('description'), prop: 'description' },
// 交易日期
{
label: $t('transactionDate'),
prop: 'transactionDate',
width: 190,
sortable: true,
formatter({ transactionDate }) {
return (
<ElText type={'success'} style={{ fontWeight: 800 }}>
{transactionDate}
</ElText>
);
},
},
];

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,10 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import Analyse from '@/components/Analyse/index.vue'; import Analyse from '@/components/Analyse/index.vue';
import { onMounted, reactive, ref } from 'vue'; import { onMounted, reactive, ref } from 'vue';
import { $t } from '@/plugins/i18n';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { currentMouth, days } from '@/enums/dateEnums'; import { currentMouth, days } from '@/enums/dateEnums';
import { fetchGetExpendOrIncome } from '@/api/v1/financial/user/billUser'; import { fetchGetExpendOrIncome, fetchGetUserBillList } from '@/api/v1/financial/user/billUser';
import { $t } from '@/plugins/i18n';
const title = { const title = {
analyse: $t('expenditureAnalysis'), analyse: $t('expenditureAnalysis'),
@ -19,11 +19,23 @@ const analyseData = ref([]);
const categoryList = ref([]); const categoryList = ref([]);
// //
const topList = ref([]); const topList = ref([]);
//
const dataList = ref([]);
//
const count = ref(0);
//
const form = reactive({ const form = reactive({
type: -1, type: -1,
startDate: dayjs(currentMouth[0]).format('YYYY-MM-DD'), startDate: dayjs(currentMouth[0]).format('YYYY-MM-DD'),
endDate: dayjs(currentMouth[1]).format('YYYY-MM-DD'), endDate: dayjs(currentMouth[1]).format('YYYY-MM-DD'),
pagination: {
pageSize: 10,
currentPage: 1,
layout: 'prev, pager, next',
total: 1,
align: 'center',
hideOnSinglePage: false,
},
}); });
/** 查询账单支出 */ /** 查询账单支出 */
@ -33,6 +45,7 @@ const onSearch = async () => {
form.endDate = dayjs(form.endDate).format('YYYY-MM-DD'); form.endDate = dayjs(form.endDate).format('YYYY-MM-DD');
// //
await onTableSearch();
const result = await fetchGetExpendOrIncome(form); const result = await fetchGetExpendOrIncome(form);
if (result.code !== 200) return; if (result.code !== 200) return;
@ -42,6 +55,10 @@ const onSearch = async () => {
result.data.chartList.forEach(item => { result.data.chartList.forEach(item => {
const transactionDate = item.transactionDate; const transactionDate = item.transactionDate;
const date = `${dayjs(transactionDate).format('MM-DD')} ${days[dayjs(transactionDate).day()]}`; const date = `${dayjs(transactionDate).format('MM-DD')} ${days[dayjs(transactionDate).day()]}`;
//
count.value += Number(item.amount);
//
analyseData.value.push(item.amount); analyseData.value.push(item.amount);
analyseXAxis.value.push(date); analyseXAxis.value.push(date);
}); });
@ -60,6 +77,26 @@ const onSearch = async () => {
}); });
}; };
/* 初始化数据 */
async function onTableSearch() {
form.loading = true;
//
const result = await fetchGetUserBillList({ ...form, ...form.pagination });
dataList.value = result.data.list;
form.pagination.currentPage = result.data.pageNo;
form.pagination.pageSize = result.data.pageSize;
form.pagination.total = result.data.total;
form.loading = false;
}
/* 修改分页 */
async function onCurrentChange(page: number) {
form.pagination.currentPage = page;
await onSearch();
}
/** 选择查询日期 */ /** 选择查询日期 */
const onChangeDateRange = async dateRange => { const onChangeDateRange = async dateRange => {
form.startDate = dateRange[0]; form.startDate = dateRange[0];
@ -78,8 +115,12 @@ onMounted(() => {
:analyseXAxis="analyseXAxis" :analyseXAxis="analyseXAxis"
:category-data="categoryList" :category-data="categoryList"
:color="['#41b6ff', '#F56C6C']" :color="['#41b6ff', '#F56C6C']"
:count="count"
:data-list="dataList"
:date="[form.startDate, form.endDate]" :date="[form.startDate, form.endDate]"
:financialType="form.type" :financialType="form.type"
:form="form"
:on-current-change="onCurrentChange"
:title="title" :title="title"
:top-data="topList" :top-data="topList"
@emit-date-range="onChangeDateRange" @emit-date-range="onChangeDateRange"

View File

@ -1,10 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import Analyse from '@/components/Analyse/index.vue'; import Analyse from '@/components/Analyse/index.vue';
import { onMounted, reactive, ref } from 'vue'; import { onMounted, reactive, ref } from 'vue';
import { currentMouth, days } from '@/enums/dateEnums';
import { fetchGetExpendOrIncome } from '@/api/v1/financial/user/billUser';
import { $t } from '@/plugins/i18n'; import { $t } from '@/plugins/i18n';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { currentMouth, days } from '@/enums/dateEnums';
import { fetchGetExpendOrIncome, fetchGetUserBillList } from '@/api/v1/financial/user/billUser';
const title = { const title = {
analyse: $t('revenueAnalysis'), analyse: $t('revenueAnalysis'),
@ -20,20 +20,33 @@ const analyseData = ref([]);
const categoryList = ref([]); const categoryList = ref([]);
// //
const topList = ref([]); const topList = ref([]);
//
const dataList = ref([]);
//
const count = ref(0);
//
const form = reactive({ const form = reactive({
type: 1, type: 1,
startDate: dayjs(currentMouth[0]).format('YYYY-MM-DD'), startDate: dayjs(currentMouth[0]).format('YYYY-MM-DD'),
endDate: dayjs(currentMouth[1]).format('YYYY-MM-DD'), endDate: dayjs(currentMouth[1]).format('YYYY-MM-DD'),
pagination: {
pageSize: 10,
currentPage: 1,
layout: 'prev, pager, next',
total: 1,
align: 'center',
hideOnSinglePage: false,
},
}); });
/** 查询账单收入 */ /** 查询账单支出 */
const onSearch = async () => { const onSearch = async () => {
// //
form.startDate = dayjs(form.startDate).format('YYYY-MM-DD'); form.startDate = dayjs(form.startDate).format('YYYY-MM-DD');
form.endDate = dayjs(form.endDate).format('YYYY-MM-DD'); form.endDate = dayjs(form.endDate).format('YYYY-MM-DD');
// //
await onTableSearch();
const result = await fetchGetExpendOrIncome(form); const result = await fetchGetExpendOrIncome(form);
if (result.code !== 200) return; if (result.code !== 200) return;
@ -43,6 +56,10 @@ const onSearch = async () => {
result.data.chartList.forEach(item => { result.data.chartList.forEach(item => {
const transactionDate = item.transactionDate; const transactionDate = item.transactionDate;
const date = `${dayjs(transactionDate).format('MM-DD')} ${days[dayjs(transactionDate).day()]}`; const date = `${dayjs(transactionDate).format('MM-DD')} ${days[dayjs(transactionDate).day()]}`;
//
count.value += Number(item.amount);
//
analyseData.value.push(item.amount); analyseData.value.push(item.amount);
analyseXAxis.value.push(date); analyseXAxis.value.push(date);
}); });
@ -61,6 +78,25 @@ const onSearch = async () => {
}); });
}; };
/* 初始化数据 */
async function onTableSearch() {
form.loading = true;
const result = await fetchGetUserBillList({ ...form, ...form.pagination });
dataList.value = result.data.list;
form.pagination.currentPage = result.data.pageNo;
form.pagination.pageSize = result.data.pageSize;
form.pagination.total = result.data.total;
form.loading = false;
}
/* 修改分页 */
async function onCurrentChange(page: number) {
form.pagination.currentPage = page;
await onSearch();
}
/** 选择查询日期 */ /** 选择查询日期 */
const onChangeDateRange = async dateRange => { const onChangeDateRange = async dateRange => {
form.startDate = dateRange[0]; form.startDate = dateRange[0];
@ -79,8 +115,12 @@ onMounted(() => {
:analyseXAxis="analyseXAxis" :analyseXAxis="analyseXAxis"
:category-data="categoryList" :category-data="categoryList"
:color="['#41b6ff', '#67C23A']" :color="['#41b6ff', '#67C23A']"
:count="count"
:data-list="dataList"
:date="[form.startDate, form.endDate]" :date="[form.startDate, form.endDate]"
:financialType="form.type" :financialType="form.type"
:form="form"
:on-current-change="onCurrentChange"
:title="title" :title="title"
:top-data="topList" :top-data="topList"
@emit-date-range="onChangeDateRange" @emit-date-range="onChangeDateRange"

View File

@ -0,0 +1,167 @@
<script lang="ts" setup>
import Analyse from '@/components/Analyse/index.vue';
import { onMounted, reactive, ref } from 'vue';
import { $t } from '@/plugins/i18n';
import LoadingSvg from '@/assets/svg/loading.svg';
import { useAdminUserStore } from '@/store/system/adminUser';
import dayjs from 'dayjs';
import { currentMouth, days } from '@/enums/dateEnums';
import { fetchGetExpendOrIncome } from '@/api/v1/financial/user/billUser';
import { fetchGetBillList } from '@/api/v1/financial/admin/bill';
const title = {
analyse: $t('expenditureAnalysis'),
category: $t('paymentLedger'),
table: $t('dataStatistics'),
top: $t('rankingList'),
};
const adminUserStore = useAdminUserStore();
const loading = ref(false);
//
const userDataList = ref();
//
const analyseXAxis = ref([]);
const analyseData = ref([]);
//
const categoryList = ref([]);
//
const topList = ref([]);
const dataList = ref([]);
//
const count = ref(0);
//
const form = reactive({
userId: undefined,
type: -1,
startDate: dayjs(currentMouth[0]).format('YYYY-MM-DD'),
endDate: dayjs(currentMouth[1]).format('YYYY-MM-DD'),
pagination: {
pageSize: 10,
currentPage: 1,
layout: 'prev, pager, next',
total: 1,
align: 'center',
hideOnSinglePage: false,
},
loading: false,
});
/** 查询账单支出 */
const onSearch = async () => {
//
form.startDate = dayjs(form.startDate).format('YYYY-MM-DD');
form.endDate = dayjs(form.endDate).format('YYYY-MM-DD');
//
await onTableSearch();
const result = await fetchGetExpendOrIncome(form);
if (result.code !== 200) return;
//
analyseData.value = [];
analyseXAxis.value = [];
result.data.chartList.forEach(item => {
const transactionDate = item.transactionDate;
const date = `${dayjs(transactionDate).format('MM-DD')} ${days[dayjs(transactionDate).day()]}`;
//
count.value += Number(item.amount);
//
analyseData.value.push(item.amount);
analyseXAxis.value.push(date);
});
//
categoryList.value = result.data.categoryAmounts.map(categoryAmount => ({
value: categoryAmount.amount,
name: categoryAmount.categoryName,
}));
//
topList.value = result.data.list.map(item => {
const transactionDate = item.transactionDate;
const date = `${dayjs(transactionDate).format('YYYY-MM-DD')} ${days[dayjs(transactionDate).day()]}`;
return { date, amount: item.amount, name: item.categoryName };
});
};
/* 初始化数据 */
async function onTableSearch() {
form.loading = true;
//
const result = await fetchGetBillList({ ...form, ...form.pagination });
dataList.value = result.data.list;
form.pagination.currentPage = result.data.pageNo;
form.pagination.pageSize = result.data.pageSize;
form.pagination.total = result.data.total;
form.loading = false;
}
/* 修改分页 */
async function onCurrentChange(page: number) {
form.pagination.currentPage = page;
await onSearch();
}
/** 选择查询日期 */
const onChangeDateRange = dateRange => {
form.startDate = dateRange[0];
form.endDate = dateRange[1];
onSearch();
};
/** 查询用户信息 */
const onSearchUserinfo = async (keyword: string) => {
loading.value = true;
userDataList.value = await adminUserStore.queryUser({ keyword });
loading.value = false;
};
onMounted(() => {
onSearch();
});
</script>
<template>
<Analyse
:analyse-data="analyseData"
:analyseXAxis="analyseXAxis"
:category-data="categoryList"
:color="['#41b6ff', '#F56C6C']"
:count="count"
:data-list="dataList"
:date="[form.startDate, form.endDate]"
:financialType="form.type"
:form="form"
:on-current-change="onCurrentChange"
:title="title"
:top-data="topList"
@emit-date-range="onChangeDateRange"
>
<template #formBefore>
<el-select
v-model="form.userId"
:loading="loading"
:placeholder="$t('user')"
:remote-method="onSearchUserinfo"
class="!w-[180px]"
clearable
filterable
remote
remote-show-suffix
@change="onSearch"
>
<el-option v-for="item in userDataList" :key="item.id" :label="item.username" :value="item.id" />
<template #loading>
<el-icon class="is-loading">
<LoadingSvg />
</el-icon>
</template>
</el-select>
</template>
</Analyse>
</template>

View File

@ -0,0 +1,168 @@
<script lang="ts" setup>
import Analyse from '@/components/Analyse/index.vue';
import { onMounted, reactive, ref } from 'vue';
import { $t } from '@/plugins/i18n';
import LoadingSvg from '@/assets/svg/loading.svg';
import { useAdminUserStore } from '@/store/system/adminUser';
import dayjs from 'dayjs';
import { currentMouth, days } from '@/enums/dateEnums';
import { fetchGetExpendOrIncome } from '@/api/v1/financial/user/billUser';
import { fetchGetBillList } from '@/api/v1/financial/admin/bill';
const title = {
analyse: $t('revenueAnalysis'),
category: $t('incomeBrackets'),
table: $t('dataStatistics'),
top: $t('rankingList'),
};
const adminUserStore = useAdminUserStore();
const loading = ref(false);
//
const userDataList = ref();
//
const analyseXAxis = ref([]);
const analyseData = ref([]);
//
const categoryList = ref([]);
//
const topList = ref([]);
const dataList = ref([]);
//
const count = ref(0);
//
const form = reactive({
userId: undefined,
type: 1,
startDate: dayjs(currentMouth[0]).format('YYYY-MM-DD'),
endDate: dayjs(currentMouth[1]).format('YYYY-MM-DD'),
pagination: {
pageSize: 10,
currentPage: 1,
layout: 'prev, pager, next',
total: 1,
align: 'center',
hideOnSinglePage: false,
},
loading: false,
});
/** 查询账单支出 */
const onSearch = async () => {
//
form.startDate = dayjs(form.startDate).format('YYYY-MM-DD');
form.endDate = dayjs(form.endDate).format('YYYY-MM-DD');
//
await onTableSearch();
const result = await fetchGetExpendOrIncome(form);
if (result.code !== 200) return;
//
analyseData.value = [];
analyseXAxis.value = [];
result.data.chartList.forEach(item => {
const transactionDate = item.transactionDate;
const date = `${dayjs(transactionDate).format('MM-DD')} ${days[dayjs(transactionDate).day()]}`;
//
count.value += Number(item.amount);
//
analyseData.value.push(item.amount);
analyseXAxis.value.push(date);
});
//
categoryList.value = result.data.categoryAmounts.map(categoryAmount => ({
value: categoryAmount.amount,
name: categoryAmount.categoryName,
}));
//
topList.value = result.data.list.map(item => {
const transactionDate = item.transactionDate;
const date = `${dayjs(transactionDate).format('YYYY-MM-DD')} ${days[dayjs(transactionDate).day()]}`;
return { date, amount: item.amount, name: item.categoryName };
});
};
/* 查询表格数据 */
async function onTableSearch() {
form.loading = true;
//
const result = await fetchGetBillList({ ...form, ...form.pagination });
dataList.value = result.data.list;
form.pagination.currentPage = result.data.pageNo;
form.pagination.pageSize = result.data.pageSize;
form.pagination.total = result.data.total;
form.loading = false;
}
/* 修改分页 */
async function onCurrentChange(page: number) {
form.pagination.currentPage = page;
await onSearch();
}
/** 选择查询日期 */
const onChangeDateRange = dateRange => {
form.startDate = dateRange[0];
form.endDate = dateRange[1];
onSearch();
};
/** 查询用户数据 */
const onSearchUserinfo = async (keyword: string) => {
loading.value = true;
userDataList.value = await adminUserStore.queryUser({ keyword });
loading.value = false;
};
onMounted(() => {
onSearch();
});
</script>
<template>
<Analyse
:analyse-data="analyseData"
:analyseXAxis="analyseXAxis"
:category-data="categoryList"
:color="['#41b6ff', '#67C23A']"
:count="count"
:data-list="dataList"
:date="[form.startDate, form.endDate]"
:financialType="form.type"
:form="form"
:on-current-change="onCurrentChange"
:title="title"
:top-data="topList"
@emit-date-range="onChangeDateRange"
>
<template #formBefore>
<el-select
v-model="form.userId"
:loading="loading"
:placeholder="$t('user')"
:remote-method="onSearchUserinfo"
class="!w-[180px]"
clearable
filterable
remote
remote-show-suffix
@change="onSearch"
>
<el-option v-for="item in userDataList" :key="item.id" :label="item.username" :value="item.id" />
<template #loading>
<el-icon class="is-loading">
<LoadingSvg />
</el-icon>
</template>
</el-select>
</template>
</Analyse>
</template>