completepage: 🍻 收入和支出页面完成
This commit is contained in:
parent
42662ccb26
commit
998906ba27
|
@ -20,7 +20,7 @@ VITE_BASE_API_RETRY=5
|
|||
VITE_BASE_API_RETRY_DELAY=3000
|
||||
|
||||
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
|
||||
VITE_CDN=false
|
||||
VITE_CDN=true
|
||||
|
||||
# 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件)
|
||||
# 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
|
||||
|
|
|
@ -42,7 +42,7 @@ export const buildEnvironment = () => {
|
|||
manualChunks: id => {
|
||||
// 如果是包含在包中则打包成 vendor
|
||||
if (id.includes('node_modules')) {
|
||||
return `vendor`;
|
||||
return id.toString().split('node_modules/')[1].split('/')[1].toString();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 35 KiB |
|
@ -8,6 +8,11 @@ export const fetchGetUserBillList = (data: any) => {
|
|||
return http.request<BaseResult<ResultTable>>('get', `bill/noManage/getUserBillList/${data.currentPage}/${data.pageSize}`, { params: data });
|
||||
};
|
||||
|
||||
/** 账单信息---账单收入和支出 */
|
||||
export const fetchGetExpendOrIncome = (data: any) => {
|
||||
return http.request<BaseResult<object>>('get', 'bill/noManage/getExpendOrIncome', { params: data });
|
||||
};
|
||||
|
||||
/** 账单信息---添加账单信息 */
|
||||
export const fetchAddUserBill = (data: any) => {
|
||||
return http.request<BaseResult<object>>('post', 'bill/noManage/addUserBill', { data });
|
|
@ -0,0 +1,166 @@
|
|||
<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>
|
|
@ -0,0 +1,104 @@
|
|||
<script lang="ts" setup>
|
||||
import { useDark, useECharts } from '@pureadmin/utils';
|
||||
import { computed, nextTick, type PropType, ref, watch } from 'vue';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
const props = defineProps({
|
||||
analyseData: {
|
||||
type: Array as PropType<Array<number>>,
|
||||
default: () => [],
|
||||
},
|
||||
analyseXAxis: {
|
||||
type: Array as PropType<Array<number>>,
|
||||
default: () => [],
|
||||
},
|
||||
color: {
|
||||
type: Array as PropType<Array<string>>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const { isDark } = useDark();
|
||||
|
||||
const theme = computed(() => (isDark.value ? 'dark' : 'light'));
|
||||
|
||||
const chartRef = ref();
|
||||
const { setOptions } = useECharts(chartRef, {
|
||||
theme,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props,
|
||||
async () => {
|
||||
await nextTick(); // 确保DOM更新完成后再执行
|
||||
setOptions({
|
||||
container: '.bar-card',
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'none',
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: '30px',
|
||||
bottom: '30px',
|
||||
left: '50px',
|
||||
right: 0,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: props.analyseXAxis,
|
||||
axisLabel: {
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
padding: 0,
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
splitLine: {
|
||||
show: true, // 去网格线
|
||||
},
|
||||
position: 'left',
|
||||
name: $t('unitMoney'),
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
itemStyle: {
|
||||
color: props.color[0],
|
||||
borderRadius: [10, 10, 0, 0],
|
||||
},
|
||||
data: props.analyseData,
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 15,
|
||||
itemStyle: {
|
||||
color: props.color[1],
|
||||
borderRadius: [10, 10, 0, 0],
|
||||
},
|
||||
data: props.analyseData,
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="chartRef" style="width: 100%; height: 365px" />
|
||||
</template>
|
|
@ -0,0 +1,64 @@
|
|||
<script lang="ts" setup>
|
||||
import { useDark, useECharts } from '@pureadmin/utils';
|
||||
import { computed, nextTick, type PropType, ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
categoryData: {
|
||||
type: Array as PropType<Array<number>>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const { isDark } = useDark();
|
||||
|
||||
const theme = computed(() => (isDark.value ? 'dark' : 'light'));
|
||||
const chartRef = ref();
|
||||
const { setOptions } = useECharts(chartRef, {
|
||||
theme,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props,
|
||||
async () => {
|
||||
await nextTick(); // 确保DOM更新完成后再执行
|
||||
setOptions({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
textStyle: {
|
||||
color: '#606266',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
bottom: 0,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: [16, 130],
|
||||
roseType: 'area',
|
||||
center: ['50%', '43%'],
|
||||
labelLine: { length: 3, length2: 6, smooth: true },
|
||||
itemStyle: {
|
||||
borderRadius: [5, 5, 5, 5],
|
||||
},
|
||||
label: {
|
||||
position: 'outer',
|
||||
alignTo: 'edge',
|
||||
edgeDistance: 10,
|
||||
},
|
||||
data: props.categoryData,
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="chartRef" style="width: 100%; height: 373px" />
|
||||
</template>
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" class="empty-icon" viewBox="0 0 1024 1024"><path d="M855.6 427.2H168.5c-12.7 0-24.4 6.9-30.6 18L4.4 684.7C1.5 689.9 0 695.8 0 701.8v287.1c0 19.4 15.7 35.1 35.1 35.1H989c19.4 0 35.1-15.7 35.1-35.1V701.8c0-6-1.5-11.8-4.4-17.1L886.2 445.2c-6.2-11.1-17.9-18-30.6-18M673.4 695.6c-16.5 0-30.8 11.5-34.3 27.7-12.7 58.5-64.8 102.3-127.2 102.3s-114.5-43.8-127.2-102.3c-3.5-16.1-17.8-27.7-34.3-27.7H119c-26.4 0-43.3-28-31.1-51.4l81.7-155.8c6.1-11.6 18-18.8 31.1-18.8h622.4c13 0 25 7.2 31.1 18.8l81.7 155.8c12.2 23.4-4.7 51.4-31.1 51.4zm146.5-486.1c-1-1.8-2.1-3.7-3.2-5.5-9.8-16.6-31.1-22.2-47.8-12.6L648.5 261c-17 9.8-22.7 31.6-12.6 48.4.9 1.4 1.7 2.9 2.5 4.4 9.5 17 31.2 22.8 48 13L807 257.3c16.7-9.7 22.4-31 12.9-47.8m-444.5 51.6L255 191.6c-16.7-9.6-38-4-47.8 12.6-1.1 1.8-2.1 3.6-3.2 5.5-9.5 16.8-3.8 38.1 12.9 47.8L337.3 327c16.9 9.7 38.6 4 48-13.1.8-1.5 1.7-2.9 2.5-4.4 10.2-16.8 4.5-38.6-12.4-48.4M512 239.3h2.5c19.5.3 35.5-15.5 35.5-35.1v-139c0-19.3-15.6-34.9-34.8-35.1h-6.4C489.6 30.3 474 46 474 65.2v139c0 19.5 15.9 35.4 35.5 35.1z"/></svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,169 @@
|
|||
<script lang="ts" setup>
|
||||
import { randomGradient, useDark } from '@pureadmin/utils';
|
||||
import { markRaw, type PropType, ref } from 'vue';
|
||||
import { useRenderFlicker } from '@/components/Flicker';
|
||||
import ReCol from '@/components/MyCol';
|
||||
import CharLine from '@/components/Analyse/char-line.vue';
|
||||
import CharPie from '@/components/Analyse/char-pie.vue';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
import { currentMouth, currentWeek, currentYear } from '@/enums/dateEnums';
|
||||
import AnalyseTable from '@/components/Analyse/analyse-table.vue';
|
||||
|
||||
interface Title {
|
||||
analyse: string;
|
||||
category: string;
|
||||
table: string;
|
||||
top: string;
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
title: { type: Object as PropType<Title> },
|
||||
financialType: { type: Number as PropType<any> },
|
||||
color: {
|
||||
type: Array as PropType<Array<string>>,
|
||||
},
|
||||
date: {
|
||||
type: Array as PropType<Array<number>>,
|
||||
default: () => [],
|
||||
},
|
||||
analyseXAxis: {
|
||||
type: Array as PropType<Array<number>>,
|
||||
default: () => [],
|
||||
},
|
||||
analyseData: {
|
||||
type: Array as PropType<Array<number>>,
|
||||
default: () => [],
|
||||
},
|
||||
categoryData: {
|
||||
type: Array as PropType<Array<number>>,
|
||||
default: () => [],
|
||||
},
|
||||
topData: {
|
||||
type: Array as PropType<Array<number>>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(['emitDateRange']);
|
||||
|
||||
const { isDark } = useDark();
|
||||
|
||||
// 选择查询日期范围
|
||||
const dateRange = ref(props.date);
|
||||
|
||||
// 日期范围选择
|
||||
const shortcuts = [
|
||||
{ text: $t('thisWeek'), value: currentWeek },
|
||||
{ text: $t('thisMonth'), value: currentMouth },
|
||||
{ text: $t('thisYear'), value: currentYear },
|
||||
];
|
||||
|
||||
/** 选择查询日期 */
|
||||
const onChangeDateRange = () => {
|
||||
const value = dateRange.value;
|
||||
emits('emitDateRange', value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<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]">
|
||||
<el-card class="bar-card" shadow="never">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-md font-medium">{{ title.analyse }}</span>
|
||||
<span>
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
:end-placeholder="$t('endDate')"
|
||||
:shortcuts="shortcuts"
|
||||
:start-placeholder="$t('startDate')"
|
||||
class="!w-[230px]"
|
||||
clearable
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
@change="onChangeDateRange"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-start mt-3">
|
||||
<char-line :analyse-data="analyseData" :analyse-x-axis="analyseXAxis" :color="color" />
|
||||
</div>
|
||||
</el-card>
|
||||
</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]">
|
||||
<el-card class="bar-card" shadow="never">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-md font-medium">{{ title.category }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-start mt-3">
|
||||
<char-pie :category-data="categoryData" />
|
||||
</div>
|
||||
</el-card>
|
||||
</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]">
|
||||
<el-card class="h-[580px]" shadow="never">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-md font-medium">{{ title.table }}</span>
|
||||
</div>
|
||||
<analyse-table :endDate="dateRange[1]" :financialType="financialType" :startDate="dateRange[0]" class="mt-3" />
|
||||
</el-card>
|
||||
</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]">
|
||||
<el-card shadow="never">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-md font-medium">{{ title.top }}</span>
|
||||
</div>
|
||||
<el-scrollbar class="mt-3 h-[505px]" max-height="505">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(item, index) in topData"
|
||||
:key="index"
|
||||
:icon="markRaw(useRenderFlicker({ background: randomGradient({ randomizeHue: true }) }))"
|
||||
:timestamp="item.date"
|
||||
center
|
||||
placement="top"
|
||||
>
|
||||
<p class="text-text_color_regular text-sm">
|
||||
{{ `${item.name} - ${item.amount}¥` }}
|
||||
</p>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-scrollbar>
|
||||
</el-card>
|
||||
</re-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-card) {
|
||||
--el-card-border-color: none;
|
||||
|
||||
/* 解决概率进度条宽度 */
|
||||
.el-progress--line {
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
/* 解决概率进度条字体大小 */
|
||||
.el-progress-bar__innerText {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* 隐藏 el-scrollbar 滚动条 */
|
||||
.el-scrollbar__bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* el-timeline 每一项上下、左右边距 */
|
||||
.el-timeline-item {
|
||||
margin: 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin: 20px 20px 0 !important;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,39 @@
|
|||
.point {
|
||||
width: var(--point-width);
|
||||
height: var(--point-height);
|
||||
background: var(--point-background);
|
||||
position: relative;
|
||||
border-radius: var(--point-border-radius);
|
||||
}
|
||||
|
||||
.point-flicker:after {
|
||||
background: var(--point-background);
|
||||
}
|
||||
|
||||
.point-flicker:before,
|
||||
.point-flicker:after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
border-radius: var(--point-border-radius);
|
||||
animation: flicker 1.2s ease-out infinite;
|
||||
}
|
||||
|
||||
@keyframes flicker {
|
||||
0% {
|
||||
transform: scale(0.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(var(--point-scale));
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import "./index.css";
|
||||
import { type Component, h, defineComponent } from "vue";
|
||||
|
||||
export interface attrsType {
|
||||
width?: string;
|
||||
height?: string;
|
||||
borderRadius?: number | string;
|
||||
background?: string;
|
||||
scale?: number | string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 圆点、方形闪烁动画组件
|
||||
* @param width 可选 string 宽
|
||||
* @param height 可选 string 高
|
||||
* @param borderRadius 可选 number | string 传0为方形、传50%或者不传为圆形
|
||||
* @param background 可选 string 闪烁颜色
|
||||
* @param scale 可选 number | string 闪烁范围,默认2,值越大闪烁范围越大
|
||||
* @returns Component
|
||||
*/
|
||||
export function useRenderFlicker(attrs?: attrsType): Component {
|
||||
return defineComponent({
|
||||
name: "ReFlicker",
|
||||
render() {
|
||||
return h(
|
||||
"div",
|
||||
{
|
||||
class: "point point-flicker",
|
||||
style: {
|
||||
"--point-width": attrs?.width ?? "12px",
|
||||
"--point-height": attrs?.height ?? "12px",
|
||||
"--point-background":
|
||||
attrs?.background ?? "var(--el-color-primary)",
|
||||
"--point-border-radius": attrs?.borderRadius ?? "50%",
|
||||
"--point-scale": attrs?.scale ?? "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => []
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import dayjs from 'dayjs';
|
||||
|
||||
export const currentWeek = [dayjs().startOf('week').add(1, 'day'), dayjs().endOf('week').add(1, 'day')];
|
||||
export const currentMouth = [dayjs().startOf('month'), dayjs().endOf('month').add(1, 'day')];
|
||||
export const currentYear = [dayjs().startOf('year'), dayjs().endOf('year')];
|
||||
export const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
|
@ -8,8 +8,7 @@ const Copyright = getConfig('Copyright');
|
|||
<template>
|
||||
<footer class="layout-footer text-[rgba(0,0,0,0.6)] dark:text-[rgba(220,220,242,0.8)]">
|
||||
{{ Copyright }}
|
||||
<a class="hover:text-primary" href="https://github.com/pure-admin" target="_blank"> {{ TITLE }} </a>
|
||||
<a href="https://beian.miit.gov.cn/" target="_blank"> 苏ICP备2023044078号-2</a>
|
||||
<a class="hover:text-primary" href="/" target="_blank"> {{ TITLE }} </a>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ export default [
|
|||
rank: 1,
|
||||
},
|
||||
children: [
|
||||
// 账单查询
|
||||
// 账单概览
|
||||
{
|
||||
path: '/financial-user/bill',
|
||||
name: 'BillUser',
|
||||
|
@ -20,7 +20,28 @@ export default [
|
|||
title: 'billManagement',
|
||||
},
|
||||
},
|
||||
// 用户分类
|
||||
|
||||
// 支出分析
|
||||
{
|
||||
path: '/financial-user/expend',
|
||||
name: 'ExpendUser',
|
||||
component: () => import('@/views/financial-user/account-bill/expend/index.vue'),
|
||||
meta: {
|
||||
icon: 'icon-park-outline:expenses',
|
||||
title: 'expendAnalyse',
|
||||
},
|
||||
},
|
||||
// 收入分析
|
||||
{
|
||||
path: '/financial-user/income',
|
||||
name: 'IncomeUser',
|
||||
component: () => import('@/views/financial-user/account-bill/income/index.vue'),
|
||||
meta: {
|
||||
icon: 'icon-park-outline:income',
|
||||
title: 'incomeAnalyse',
|
||||
},
|
||||
},
|
||||
// 账单分类管理
|
||||
{
|
||||
path: '/financial-user/category',
|
||||
name: 'CategoryUser',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { fetchAddBill, fetchDeleteBill, fetchGetBillList, fetchUpdateBill } from '@/api/v1/financial/bill';
|
||||
import { fetchAddBill, fetchDeleteBill, fetchGetBillList, fetchUpdateBill } from '@/api/v1/financial/admin/bill';
|
||||
import { pageSizes } from '@/enums/baseConstant';
|
||||
import { storeMessage } from '@/utils/message';
|
||||
import { storePagination } from '@/store/useStorePagination';
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { fetchAddBudgetCategory, fetchDeleteBudgetCategory, fetchGetBudgetCategoryList, fetchUpdateBudgetCategory } from '@/api/v1/financial/budgetCategory';
|
||||
import {
|
||||
fetchAddBudgetCategory,
|
||||
fetchDeleteBudgetCategory,
|
||||
fetchGetBudgetCategoryList,
|
||||
fetchUpdateBudgetCategory,
|
||||
} from '@/api/v1/financial/admin/budgetCategory';
|
||||
import { pageSizes } from '@/enums/baseConstant';
|
||||
import { storeMessage } from '@/utils/message';
|
||||
import { storePagination } from '@/store/useStorePagination';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { fetchAddCategory, fetchDeleteCategory, fetchGetCategoryAllList, fetchGetCategoryList, fetchUpdateCategory } from '@/api/v1/financial/category';
|
||||
import { fetchAddCategory, fetchDeleteCategory, fetchGetCategoryAllList, fetchGetCategoryList, fetchUpdateCategory } from '@/api/v1/financial/admin/category';
|
||||
import { pageSizes } from '@/enums/baseConstant';
|
||||
import { storeMessage } from '@/utils/message';
|
||||
import { storePagination } from '@/store/useStorePagination';
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
fetchDeleteDebtRepaymentPlan,
|
||||
fetchGetDebtRepaymentPlanList,
|
||||
fetchUpdateDebtRepaymentPlan,
|
||||
} from '@/api/v1/financial/debtRepaymentPlan';
|
||||
} from '@/api/v1/financial/admin/debtRepaymentPlan';
|
||||
import { pageSizes } from '@/enums/baseConstant';
|
||||
import { storeMessage } from '@/utils/message';
|
||||
import { storePagination } from '@/store/useStorePagination';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { fetchAddDebtTracking, fetchDeleteDebtTracking, fetchGetDebtTrackingList, fetchUpdateDebtTracking } from '@/api/v1/financial/debtTracking';
|
||||
import { fetchAddDebtTracking, fetchDeleteDebtTracking, fetchGetDebtTrackingList, fetchUpdateDebtTracking } from '@/api/v1/financial/admin/debtTracking';
|
||||
import { pageSizes } from '@/enums/baseConstant';
|
||||
import { storeMessage } from '@/utils/message';
|
||||
import { storePagination } from '@/store/useStorePagination';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { fetchAddSavingGoal, fetchDeleteSavingGoal, fetchGetSavingGoalList, fetchUpdateSavingGoal } from '@/api/v1/financial/savingGoal';
|
||||
import { fetchAddSavingGoal, fetchDeleteSavingGoal, fetchGetSavingGoalList, fetchUpdateSavingGoal } from '@/api/v1/financial/admin/savingGoal';
|
||||
import { pageSizes } from '@/enums/baseConstant';
|
||||
import { storeMessage } from '@/utils/message';
|
||||
import { storePagination } from '@/store/useStorePagination';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { fetchAddUserBill, fetchDeleteUserBill, fetchGetUserBillList, fetchUpdateUserBill } from '@/api/v1/financialUser/billUser';
|
||||
import { fetchAddUserBill, fetchDeleteUserBill, fetchGetUserBillList, fetchUpdateUserBill } from '@/api/v1/financial/user/billUser';
|
||||
import { pageSizes } from '@/enums/baseConstant';
|
||||
import { storeMessage } from '@/utils/message';
|
||||
import { storePagination } from '@/store/useStorePagination';
|
||||
|
@ -13,6 +13,8 @@ export const useBillUserStore = defineStore('billUserStore', {
|
|||
return {
|
||||
// 账单信息列表
|
||||
datalist: [],
|
||||
// 收入和支出
|
||||
expendWithIncomeList: [],
|
||||
// 查询表单
|
||||
form: {
|
||||
// 类型:1 - 收入,-1 - 支出
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
fetchDeleteUserBudgetCategory,
|
||||
fetchGetUserBudgetCategoryList,
|
||||
fetchUpdateUserBudgetCategory,
|
||||
} from '@/api/v1/financialUser/budgetCategoryUser';
|
||||
} from '@/api/v1/financial/user/budgetCategoryUser';
|
||||
import { getDefaultDateRange } from '@/utils/date';
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
fetchGetCategoryUserAllList,
|
||||
fetchGetCategoryUserList,
|
||||
fetchUpdateCategoryUser,
|
||||
} from '@/api/v1/financialUser/categoryUser';
|
||||
} from '@/api/v1/financial/user/categoryUser';
|
||||
import { pageSizes } from '@/enums/baseConstant';
|
||||
import { storeMessage } from '@/utils/message';
|
||||
import { storePagination } from '@/store/useStorePagination';
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
fetchDeleteUserDebtRepaymentPlan,
|
||||
fetchGetUserDebtRepaymentPlanList,
|
||||
fetchUpdateUserDebtRepaymentPlan,
|
||||
} from '@/api/v1/financialUser/debtRepaymentPlanUser';
|
||||
} from '@/api/v1/financial/user/debtRepaymentPlanUser';
|
||||
|
||||
/**
|
||||
* 债务还款计划表 Store
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
fetchDeleteUserDebtTracking,
|
||||
fetchGetUserDebtTrackingList,
|
||||
fetchUpdateUserDebtTracking,
|
||||
} from '@/api/v1/financialUser/debtTrackingUser';
|
||||
} from '@/api/v1/financial/user/debtTrackingUser';
|
||||
|
||||
/**
|
||||
* 债务追踪 Store
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
fetchDeleteUserSavingGoal,
|
||||
fetchGetUserSavingGoalList,
|
||||
fetchUpdateUserSavingGoal,
|
||||
} from '@/api/v1/financialUser/savingGoalUser';
|
||||
} from '@/api/v1/financial/user/savingGoalUser';
|
||||
import { getDefaultDateRange } from '@/utils/date';
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@ import { columns } from '@/views/configuration/emailTemplate/utils/columns';
|
|||
import PureTableBar from '@/components/TableBar/src/bar';
|
||||
import AddFill from '@iconify-icons/ri/add-circle-line';
|
||||
import PureTable from '@pureadmin/table';
|
||||
import { onAdd, onDelete, onDeleteBatch, onSearch, onUpdate, selectRows } from '@/views/configuration/emailTemplate/utils/hooks';
|
||||
import { onAdd, onDelete, onDeleteBatch, onSearch, onUpdate, selectRows, viewTemplate } from '@/views/configuration/emailTemplate/utils/hooks';
|
||||
import Delete from '@iconify-icons/ep/delete';
|
||||
import EditPen from '@iconify-icons/ep/edit-pen';
|
||||
import Refresh from '@iconify-icons/ep/refresh';
|
||||
|
@ -14,6 +14,7 @@ import { useEmailTemplateStore } from '@/store/configuration/emailTemplate.ts';
|
|||
import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
|
||||
import { auth } from '@/views/configuration/emailTemplate/utils/auth';
|
||||
import { hasAuth } from '@/router/utils';
|
||||
import View from '@iconify-icons/ep/view';
|
||||
|
||||
const tableRef = ref();
|
||||
const formRef = ref();
|
||||
|
@ -122,7 +123,12 @@ onMounted(() => {
|
|||
</template>
|
||||
|
||||
<template #operation="{ row }">
|
||||
<el-button v-if="hasAuth(auth.update)" :icon="useRenderIcon(EditPen)" :size="size" class="reset-margin" link type="primary" @click="onUpdate(row)"> {{ $t('modify') }} </el-button>
|
||||
<el-button :icon="useRenderIcon(View)" :size="size" class="reset-margin" link type="primary" @click="viewTemplate(row.body)">
|
||||
{{ $t('view') }}
|
||||
</el-button>
|
||||
<el-button v-if="hasAuth(auth.update)" :icon="useRenderIcon(EditPen)" :size="size" class="reset-margin" link type="primary" @click="onUpdate(row)">
|
||||
{{ $t('modify') }}
|
||||
</el-button>
|
||||
<el-popconfirm v-if="hasAuth(auth.deleted)" :title="`${$t('delete')} ${row.templateName}?`" @confirm="onDelete(row)">
|
||||
<template #reference>
|
||||
<el-button :icon="useRenderIcon(Delete)" :size="size" class="reset-margin" link type="primary">
|
||||
|
|
|
@ -133,3 +133,14 @@ export const onDeleteBatch = async () => {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
/** 查看模板 */
|
||||
export const viewTemplate = (template: string) => {
|
||||
addDialog({
|
||||
title: $t('view'),
|
||||
draggable: true,
|
||||
fullscreenIcon: true,
|
||||
closeOnClickModal: false,
|
||||
contentRenderer: () => <div v-html={template} />,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
<script lang="ts" setup>
|
||||
import Analyse from '@/components/Analyse/index.vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { dayjs } from '@/views/welcome/utils/utils';
|
||||
import { currentMouth, days } from '@/enums/dateEnums';
|
||||
import { fetchGetExpendOrIncome } from '@/api/v1/financial/user/billUser';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
const title = {
|
||||
analyse: $t('expenditureAnalysis'),
|
||||
category: $t('paymentLedger'),
|
||||
table: $t('dataStatistics'),
|
||||
top: $t('rankingList'),
|
||||
};
|
||||
// 分析概览
|
||||
const analyseXAxis = ref([]);
|
||||
const analyseData = ref([]);
|
||||
// 分类数据
|
||||
const categoryList = ref([]);
|
||||
// 排行榜数据
|
||||
const topList = ref([]);
|
||||
|
||||
const form = reactive({
|
||||
type: -1,
|
||||
startDate: dayjs(currentMouth[0]).format('YYYY-MM-DD'),
|
||||
endDate: dayjs(currentMouth[1]).format('YYYY-MM-DD'),
|
||||
});
|
||||
|
||||
/** 查询账单支出 */
|
||||
const onSearch = async () => {
|
||||
// 格式化请求数据
|
||||
form.startDate = dayjs(form.startDate).format('YYYY-MM-DD');
|
||||
form.endDate = dayjs(form.endDate).format('YYYY-MM-DD');
|
||||
|
||||
// 获取数据
|
||||
const result = await fetchGetExpendOrIncome(form);
|
||||
if (result.code !== 200) return;
|
||||
|
||||
// 分析概览
|
||||
analyseData.value = [];
|
||||
analyseXAxis.value = [];
|
||||
result.data.list.forEach(item => {
|
||||
const transactionDate = item.transactionDate;
|
||||
const date = `${dayjs(transactionDate).format('MM-DD')} ${days[dayjs(transactionDate).day()]}`;
|
||||
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 };
|
||||
});
|
||||
};
|
||||
|
||||
/** 选择查询日期 */
|
||||
const onChangeDateRange = async dateRange => {
|
||||
form.startDate = dateRange[0];
|
||||
form.endDate = dateRange[1];
|
||||
await onSearch();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
onSearch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Analyse
|
||||
:analyse-data="analyseData"
|
||||
:analyseXAxis="analyseXAxis"
|
||||
:category-data="categoryList"
|
||||
:color="['#41b6ff', '#F56C6C']"
|
||||
:date="[form.startDate, form.endDate]"
|
||||
:financialType="form.type"
|
||||
:title="title"
|
||||
:top-data="topList"
|
||||
@emit-date-range="onChangeDateRange"
|
||||
/>
|
||||
</template>
|
|
@ -0,0 +1,89 @@
|
|||
<script lang="ts" setup>
|
||||
import Analyse from '@/components/Analyse/index.vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { dayjs } from '@/views/welcome/utils/utils';
|
||||
import { currentMouth, days } from '@/enums/dateEnums';
|
||||
import { fetchGetExpendOrIncome } from '@/api/v1/financial/user/billUser';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
const title = {
|
||||
analyse: $t('revenueAnalysis'),
|
||||
category: $t('incomeBrackets'),
|
||||
table: $t('dataStatistics'),
|
||||
top: $t('rankingList'),
|
||||
};
|
||||
|
||||
// 分析概览
|
||||
const analyseXAxis = ref([]);
|
||||
const analyseData = ref([]);
|
||||
// 分类数据
|
||||
const categoryList = ref([]);
|
||||
// 排行榜数据
|
||||
const topList = ref([]);
|
||||
|
||||
const form = reactive({
|
||||
type: 1,
|
||||
startDate: dayjs(currentMouth[0]).format('YYYY-MM-DD'),
|
||||
endDate: dayjs(currentMouth[1]).format('YYYY-MM-DD'),
|
||||
});
|
||||
|
||||
/** 查询账单收入 */
|
||||
const onSearch = async () => {
|
||||
// 格式化请求数据
|
||||
form.startDate = dayjs(form.startDate).format('YYYY-MM-DD');
|
||||
form.endDate = dayjs(form.endDate).format('YYYY-MM-DD');
|
||||
|
||||
// 获取数据
|
||||
const result = await fetchGetExpendOrIncome(form);
|
||||
if (result.code !== 200) return;
|
||||
|
||||
// 分析概览
|
||||
analyseData.value = [];
|
||||
analyseXAxis.value = [];
|
||||
result.data.list.forEach(item => {
|
||||
const transactionDate = item.transactionDate;
|
||||
const date = `${dayjs(transactionDate).format('MM-DD')} ${days[dayjs(transactionDate).day()]}`;
|
||||
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 };
|
||||
});
|
||||
};
|
||||
|
||||
/** 选择查询日期 */
|
||||
const onChangeDateRange = async dateRange => {
|
||||
form.startDate = dateRange[0];
|
||||
form.endDate = dateRange[1];
|
||||
await onSearch();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
onSearch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Analyse
|
||||
:analyse-data="analyseData"
|
||||
:analyseXAxis="analyseXAxis"
|
||||
:category-data="categoryList"
|
||||
:color="['#41b6ff', '#67C23A']"
|
||||
:date="[form.startDate, form.endDate]"
|
||||
:financialType="form.type"
|
||||
:title="title"
|
||||
:top-data="topList"
|
||||
@emit-date-range="onChangeDateRange"
|
||||
/>
|
||||
</template>
|
|
@ -6,7 +6,7 @@ import { FormProps } from '@/views/financial-user/budget-saving/budget-category/
|
|||
import { $t } from '@/plugins/i18n';
|
||||
import { useAdminUserStore } from '@/store/system/adminUser';
|
||||
import { budget } from '@/enums/bill/budget';
|
||||
import { fetchGetAllUserParentList } from '@/api/v1/financialUser/budgetCategoryUser';
|
||||
import { fetchGetAllUserParentList } from '@/api/v1/financial/user/budgetCategoryUser';
|
||||
|
||||
const props = withDefaults(defineProps<FormProps>(), {
|
||||
formInline: () => ({
|
||||
|
|
|
@ -6,7 +6,7 @@ import { FormProps } from '@/views/financial/budget-saving/budget-category/utils
|
|||
import { $t } from '@/plugins/i18n';
|
||||
import LoadingSvg from '@/assets/svg/loading.svg';
|
||||
import { useAdminUserStore } from '@/store/system/adminUser';
|
||||
import { fetchGetAllParentList } from '@/api/v1/financial/budgetCategory';
|
||||
import { fetchGetAllParentList } from '@/api/v1/financial/admin/budgetCategory';
|
||||
import { budget } from '@/enums/bill/budget';
|
||||
|
||||
const props = withDefaults(defineProps<FormProps>(), {
|
||||
|
|
|
@ -14,7 +14,6 @@ const props = defineProps({
|
|||
});
|
||||
|
||||
const { isDark } = useDark();
|
||||
|
||||
const theme = computed(() => (isDark.value ? 'dark' : 'light'));
|
||||
|
||||
const chartRef = ref();
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
<script setup lang="ts">
|
||||
import { useDark, useECharts } from "@pureadmin/utils";
|
||||
import { type PropType, ref, computed, watch, nextTick } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
requireData: {
|
||||
type: Array as PropType<Array<number>>,
|
||||
default: () => []
|
||||
},
|
||||
questionData: {
|
||||
type: Array as PropType<Array<number>>,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
const { isDark } = useDark();
|
||||
|
||||
const theme = computed(() => (isDark.value ? "dark" : "light"));
|
||||
|
||||
const chartRef = ref();
|
||||
const { setOptions } = useECharts(chartRef, {
|
||||
theme
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props,
|
||||
async () => {
|
||||
await nextTick(); // 确保DOM更新完成后再执行
|
||||
setOptions({
|
||||
container: ".bar-card",
|
||||
color: ["#41b6ff", "#e85f33"],
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
type: "none"
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: "20px",
|
||||
left: "50px",
|
||||
right: 0
|
||||
},
|
||||
legend: {
|
||||
data: ["需求人数", "提问数量"],
|
||||
textStyle: {
|
||||
color: "#606266",
|
||||
fontSize: "0.875rem"
|
||||
},
|
||||
bottom: 0
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: "category",
|
||||
data: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"],
|
||||
axisLabel: {
|
||||
fontSize: "0.875rem"
|
||||
},
|
||||
axisPointer: {
|
||||
type: "shadow"
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: "value",
|
||||
axisLabel: {
|
||||
fontSize: "0.875rem"
|
||||
},
|
||||
splitLine: {
|
||||
show: false // 去网格线
|
||||
}
|
||||
// name: "单位: 个"
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: "需求人数",
|
||||
type: "bar",
|
||||
barWidth: 10,
|
||||
itemStyle: {
|
||||
color: "#41b6ff",
|
||||
borderRadius: [10, 10, 0, 0]
|
||||
},
|
||||
data: props.requireData
|
||||
},
|
||||
{
|
||||
name: "提问数量",
|
||||
type: "bar",
|
||||
barWidth: 10,
|
||||
itemStyle: {
|
||||
color: "#e86033ce",
|
||||
borderRadius: [10, 10, 0, 0]
|
||||
},
|
||||
data: props.questionData
|
||||
}
|
||||
]
|
||||
});
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="chartRef" style="width: 100%; height: 365px" />
|
||||
</template>
|
|
@ -0,0 +1,62 @@
|
|||
<script setup lang="ts">
|
||||
import { type PropType, ref, computed } from "vue";
|
||||
import { useDark, useECharts } from "@pureadmin/utils";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array as PropType<Array<number>>,
|
||||
default: () => []
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: "#41b6ff"
|
||||
}
|
||||
});
|
||||
|
||||
const { isDark } = useDark();
|
||||
|
||||
const theme = computed(() => (isDark.value ? "dark" : "light"));
|
||||
|
||||
const chartRef = ref();
|
||||
const { setOptions } = useECharts(chartRef, {
|
||||
theme,
|
||||
renderer: "svg"
|
||||
});
|
||||
|
||||
setOptions({
|
||||
container: ".line-card",
|
||||
xAxis: {
|
||||
type: "category",
|
||||
show: false,
|
||||
data: props.data
|
||||
},
|
||||
grid: {
|
||||
top: "15px",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0
|
||||
},
|
||||
yAxis: {
|
||||
show: false,
|
||||
type: "value"
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: props.data,
|
||||
type: "line",
|
||||
symbol: "none",
|
||||
smooth: true,
|
||||
color: props.color,
|
||||
lineStyle: {
|
||||
shadowOffsetY: 3,
|
||||
shadowBlur: 7,
|
||||
shadowColor: props.color
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="chartRef" style="width: 100%; height: 60px" />
|
||||
</template>
|
|
@ -0,0 +1,73 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
import { useDark, useECharts } from "@pureadmin/utils";
|
||||
|
||||
const { isDark } = useDark();
|
||||
|
||||
const theme = computed(() => (isDark.value ? "dark" : "light"));
|
||||
|
||||
const chartRef = ref();
|
||||
const { setOptions } = useECharts(chartRef, {
|
||||
theme,
|
||||
renderer: "svg"
|
||||
});
|
||||
|
||||
setOptions({
|
||||
container: ".line-card",
|
||||
title: {
|
||||
text: "100%",
|
||||
left: "47%",
|
||||
top: "30%",
|
||||
textAlign: "center",
|
||||
textStyle: {
|
||||
fontSize: "16",
|
||||
fontWeight: 600
|
||||
}
|
||||
},
|
||||
polar: {
|
||||
radius: ["100%", "90%"],
|
||||
center: ["50%", "50%"]
|
||||
},
|
||||
angleAxis: {
|
||||
max: 100,
|
||||
show: false
|
||||
},
|
||||
radiusAxis: {
|
||||
type: "category",
|
||||
show: true,
|
||||
axisLabel: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "bar",
|
||||
roundCap: true,
|
||||
barWidth: 2,
|
||||
showBackground: true,
|
||||
backgroundStyle: {
|
||||
color: "#dfe7ef"
|
||||
},
|
||||
data: [100],
|
||||
coordinateSystem: "polar",
|
||||
color: "#7846e5",
|
||||
itemStyle: {
|
||||
shadowBlur: 2,
|
||||
shadowColor: "#7846e5",
|
||||
shadowOffsetX: 0,
|
||||
shadowOffsetY: 0
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="chartRef" style="width: 100%; height: 60px" />
|
||||
</template>
|
|
@ -0,0 +1,3 @@
|
|||
export { default as ChartBar } from "./ChartBar.vue";
|
||||
export { default as ChartLine } from "./ChartLine.vue";
|
||||
export { default as ChartRound } from "./ChartRound.vue";
|
|
@ -1,23 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { decode } from 'js-base64';
|
||||
import { MdPreview } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/preview.css';
|
||||
|
||||
const content = ref();
|
||||
|
||||
/** 获取文档信息 */
|
||||
const onSearch = async () => {
|
||||
const response = await fetch('http://129.211.31.58:3000/api/v1/repos/auth/auth-server-java/contents/ReadMe.md');
|
||||
const json = await response.json();
|
||||
content.value = decode(json.content);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
onSearch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MdPreview id="server-read-me" :modelValue="content" />
|
||||
</template>
|
|
@ -0,0 +1,99 @@
|
|||
import { tableData } from '../../utils/data';
|
||||
import { delay } from '@pureadmin/utils';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import type { PaginationProps } from '@pureadmin/table';
|
||||
import ThumbUp from '@iconify-icons/ri/thumb-up-line';
|
||||
import Hearts from '@iconify-icons/ri/hearts-line';
|
||||
import Empty from './empty.svg?component';
|
||||
|
||||
export function useColumns() {
|
||||
const dataList = ref([]);
|
||||
const loading = ref(true);
|
||||
const columns: TableColumnList = [
|
||||
{
|
||||
sortable: true,
|
||||
label: '序号',
|
||||
prop: 'id',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
label: '需求人数',
|
||||
prop: 'requiredNumber',
|
||||
filterMultiple: false,
|
||||
filterClassName: 'pure-table-filter',
|
||||
filters: [
|
||||
{ text: '≥16000', value: 'more' },
|
||||
{ text: '<16000', value: 'less' },
|
||||
],
|
||||
filterMethod: (value, { requiredNumber }) => {
|
||||
return value === 'more' ? requiredNumber >= 16000 : requiredNumber < 16000;
|
||||
},
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
label: '提问数量',
|
||||
prop: 'questionNumber',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
label: '解决数量',
|
||||
prop: 'resolveNumber',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
label: '用户满意度',
|
||||
minWidth: 100,
|
||||
prop: 'satisfaction',
|
||||
cellRenderer: ({ row }) => (
|
||||
<div class='flex justify-center w-full'>
|
||||
<span class='flex items-center w-[60px]'>
|
||||
<span class='ml-auto mr-2'>{row.satisfaction}%</span>
|
||||
<iconifyIconOffline icon={row.satisfaction > 98 ? Hearts : ThumbUp} color='#e85f33' />
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
label: '统计日期',
|
||||
prop: 'date',
|
||||
},
|
||||
{
|
||||
label: '操作',
|
||||
fixed: 'right',
|
||||
slot: 'operation',
|
||||
},
|
||||
];
|
||||
|
||||
/** 分页配置 */
|
||||
const pagination = reactive<PaginationProps>({
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
layout: 'prev, pager, next',
|
||||
total: 0,
|
||||
align: 'center',
|
||||
});
|
||||
|
||||
function onCurrentChange(page: number) {
|
||||
console.log('onCurrentChange', page);
|
||||
loading.value = true;
|
||||
delay(300).then(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
dataList.value = tableData;
|
||||
pagination.total = dataList.value.length;
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
return {
|
||||
Empty,
|
||||
loading,
|
||||
columns,
|
||||
dataList,
|
||||
pagination,
|
||||
onCurrentChange,
|
||||
};
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" class="empty-icon" viewBox="0 0 1024 1024"><path d="M855.6 427.2H168.5c-12.7 0-24.4 6.9-30.6 18L4.4 684.7C1.5 689.9 0 695.8 0 701.8v287.1c0 19.4 15.7 35.1 35.1 35.1H989c19.4 0 35.1-15.7 35.1-35.1V701.8c0-6-1.5-11.8-4.4-17.1L886.2 445.2c-6.2-11.1-17.9-18-30.6-18M673.4 695.6c-16.5 0-30.8 11.5-34.3 27.7-12.7 58.5-64.8 102.3-127.2 102.3s-114.5-43.8-127.2-102.3c-3.5-16.1-17.8-27.7-34.3-27.7H119c-26.4 0-43.3-28-31.1-51.4l81.7-155.8c6.1-11.6 18-18.8 31.1-18.8h622.4c13 0 25 7.2 31.1 18.8l81.7 155.8c12.2 23.4-4.7 51.4-31.1 51.4zm146.5-486.1c-1-1.8-2.1-3.7-3.2-5.5-9.8-16.6-31.1-22.2-47.8-12.6L648.5 261c-17 9.8-22.7 31.6-12.6 48.4.9 1.4 1.7 2.9 2.5 4.4 9.5 17 31.2 22.8 48 13L807 257.3c16.7-9.7 22.4-31 12.9-47.8m-444.5 51.6L255 191.6c-16.7-9.6-38-4-47.8 12.6-1.1 1.8-2.1 3.6-3.2 5.5-9.5 16.8-3.8 38.1 12.9 47.8L337.3 327c16.9 9.7 38.6 4 48-13.1.8-1.5 1.7-2.9 2.5-4.4 10.2-16.8 4.5-38.6-12.4-48.4M512 239.3h2.5c19.5.3 35.5-15.5 35.5-35.1v-139c0-19.3-15.6-34.9-34.8-35.1h-6.4C489.6 30.3 474 46 474 65.2v139c0 19.5 15.9 35.4 35.5 35.1z"/></svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,60 @@
|
|||
<script lang="ts" setup>
|
||||
import { useColumns } from './columns';
|
||||
import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
|
||||
import PureTable from '@pureadmin/table';
|
||||
|
||||
const { loading, columns, dataList, pagination, Empty, onCurrentChange } = useColumns();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<pure-table
|
||||
:columns="columns"
|
||||
:data="dataList.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize)"
|
||||
:loading="loading"
|
||||
:loading-config="{ background: 'transparent' }"
|
||||
:pagination="pagination"
|
||||
alignWhole="center"
|
||||
row-key="id"
|
||||
showOverflowTooltip
|
||||
@page-current-change="onCurrentChange"
|
||||
>
|
||||
<template #empty>
|
||||
<el-empty :image-size="60" description="暂无数据">
|
||||
<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>
|
|
@ -1,23 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { decode } from 'js-base64';
|
||||
import { MdPreview } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/preview.css';
|
||||
|
||||
const content = ref();
|
||||
|
||||
/** 获取文档信息 */
|
||||
const onSearch = async () => {
|
||||
const response = await fetch('http://129.211.31.58:3000/api/v1/repos/auth/auth-web/contents/ReadMe.md');
|
||||
const json = await response.json();
|
||||
content.value = decode(json.content);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
onSearch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MdPreview id="web-read-me" :modelValue="content" />
|
||||
</template>
|
|
@ -1,30 +1,29 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { markRaw, ref } from 'vue';
|
||||
import ReCol from '@/components/MyCol';
|
||||
import { useDark } from './utils/utils';
|
||||
import WelcomeTable from './components/table/index.vue';
|
||||
import { ReNormalCountTo } from '@/components/CountTo';
|
||||
import ChartLine from '@/views/welcome/components/ChartLine.vue';
|
||||
import ChartRound from '@/views/welcome/components/ChartRound.vue';
|
||||
import { chartData } from './utils/data';
|
||||
import { getServerCommitList, getWebCommitList, serverCommitList, webCommitList } from '@/views/welcome/utils/hooks';
|
||||
import WebReadMe from '@/views/welcome/components/web-read-me.vue';
|
||||
import { TabsPaneContext } from 'element-plus';
|
||||
import ServerReadMe from '@/views/welcome/components/server-read-me.vue';
|
||||
import { useRenderFlicker } from '@/components/Flicker';
|
||||
import { ChartBar, ChartLine, ChartRound } from './components/charts';
|
||||
import Segmented, { type OptionsType } from '@/components/Segmented';
|
||||
import { barChartData, chartData, latestNewsData, progressData } from './utils/data';
|
||||
|
||||
defineOptions({
|
||||
name: 'Welcome',
|
||||
});
|
||||
|
||||
const { isDark } = useDark();
|
||||
|
||||
// 当前tab名称
|
||||
const activeName = ref('web');
|
||||
|
||||
// 修改tab名称
|
||||
const onTabClick = (tab: TabsPaneContext, _: Event) => {
|
||||
activeName.value = tab.paneName;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getWebCommitList();
|
||||
getServerCommitList();
|
||||
});
|
||||
let curWeek = ref(1); // 0上周、1本周
|
||||
const optionsBasis: Array<OptionsType> = [
|
||||
{
|
||||
label: '上周',
|
||||
},
|
||||
{
|
||||
label: '本周',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -62,64 +61,64 @@ onMounted(() => {
|
|||
</el-card>
|
||||
</re-col>
|
||||
|
||||
<el-row :gutter="24" class="w-[100%] justify-around">
|
||||
<re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 560 } }" :initial="{ opacity: 0, y: 100 }" :lg="16" :sm="24" :xl="18" :xs="24" class="mb-[18px]">
|
||||
<el-card class="h-[1178px] overflow-y-auto" shadow="never">
|
||||
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="onTabClick">
|
||||
<el-tab-pane label="前端文档" name="web">
|
||||
<web-read-me class="mt-3 h-[100%]" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="后端文档" name="server">
|
||||
<server-read-me class="mt-3 h-[100%]" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</re-col>
|
||||
<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">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-md font-medium">分析概览</span>
|
||||
<Segmented v-model="curWeek" :options="optionsBasis" />
|
||||
</div>
|
||||
<div class="flex justify-between items-start mt-3">
|
||||
<ChartBar :questionData="barChartData[curWeek].questionData" :requireData="barChartData[curWeek].requireData" />
|
||||
</div>
|
||||
</el-card>
|
||||
</re-col>
|
||||
|
||||
<re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 640 } }" :initial="{ opacity: 0, y: 100 }" :lg="8" :sm="24" :xl="6" :xs="24">
|
||||
<el-card class="mb-[18px]" shadow="never">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-md font-medium">前端git近期20次更改</span>
|
||||
</div>
|
||||
<el-scrollbar class="mt-3" max-height="504">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(item, index) in webCommitList" :key="index" :timestamp="item.date" center placement="top">
|
||||
<p class="text-text_color_regular text-sm">
|
||||
<el-link :href="item.html_url" :title="item.message" :underline="false" target="_blank">
|
||||
{{ `提交信息:${item.message},提交用户:` }}
|
||||
</el-link>
|
||||
<el-link :href="item.url" :title="item.username" :underline="false" target="_blank">
|
||||
<el-avatar :size="16" :src="item.avatar_url" class="align-middle" />
|
||||
{{ ` ${item.username}` }}
|
||||
</el-link>
|
||||
</p>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-scrollbar>
|
||||
</el-card>
|
||||
<re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 480 } }" :initial="{ opacity: 0, y: 100 }" :value="6" :xs="24" class="mb-[18px]">
|
||||
<el-card shadow="never">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-md font-medium">解决概率</span>
|
||||
</div>
|
||||
<div v-for="(item, index) in progressData" :key="index" :class="['flex', 'justify-between', 'items-start', index === 0 ? 'mt-8' : 'mt-[2.15rem]']">
|
||||
<el-progress :color="item.color" :duration="item.duration" :percentage="item.percentage" :stroke-width="21" :text-inside="true" striped striped-flow />
|
||||
<span class="text-nowrap ml-2 text-text_color_regular text-sm">
|
||||
{{ item.week }}
|
||||
</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</re-col>
|
||||
|
||||
<el-card shadow="never">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-md font-medium">后端git近期20次更改</span>
|
||||
</div>
|
||||
<el-scrollbar class="mt-3" max-height="504">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(item, index) in serverCommitList" :key="index" :timestamp="item.date" center placement="top">
|
||||
<p class="text-text_color_regular text-sm">
|
||||
<el-link :href="item.html_url" :title="item.message" :underline="false" target="_blank">
|
||||
{{ `提交信息:${item.message},提交用户:` }}
|
||||
</el-link>
|
||||
<el-link :href="item.url" :title="item.username" :underline="false" target="_blank">
|
||||
<el-avatar :size="16" :src="item.avatar_url" class="align-middle" />
|
||||
{{ ` ${item.username}` }}
|
||||
</el-link>
|
||||
</p>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-scrollbar>
|
||||
</el-card>
|
||||
</re-col>
|
||||
</el-row>
|
||||
<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">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-md font-medium">数据统计</span>
|
||||
</div>
|
||||
<WelcomeTable class="mt-3" />
|
||||
</el-card>
|
||||
</re-col>
|
||||
|
||||
<re-col v-motion :enter="{ opacity: 1, y: 0, transition: { delay: 640 } }" :initial="{ opacity: 0, y: 100 }" :value="6" :xs="24" class="mb-[18px]">
|
||||
<el-card shadow="never">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-md font-medium">最新动态</span>
|
||||
</div>
|
||||
<el-scrollbar class="mt-3" max-height="504">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(item, index) in latestNewsData"
|
||||
:key="index"
|
||||
:icon="markRaw(useRenderFlicker({ background: randomGradient({ randomizeHue: true }) }))"
|
||||
:timestamp="item.date"
|
||||
center
|
||||
placement="top"
|
||||
>
|
||||
<p class="text-text_color_regular text-sm">
|
||||
{{ `新增 ${item.requiredNumber} 条问题,${item.resolveNumber} 条已解决` }}
|
||||
</p>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-scrollbar>
|
||||
</el-card>
|
||||
</re-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { cloneDeep, dayjs, getRandomIntBetween } from './utils';
|
||||
import GroupLine from '@iconify-icons/ri/group-line';
|
||||
import Question from '@iconify-icons/ri/question-answer-line';
|
||||
import CheckLine from '@iconify-icons/ri/chat-check-line';
|
||||
import Smile from '@iconify-icons/ri/star-smile-line';
|
||||
|
||||
const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
||||
|
||||
/** 需求人数、提问数量、解决数量、用户满意度 */
|
||||
const chartData = [
|
||||
{
|
||||
|
@ -47,4 +50,83 @@ const chartData = [
|
|||
},
|
||||
];
|
||||
|
||||
export { chartData };
|
||||
/** 分析概览 */
|
||||
const barChartData = [
|
||||
{
|
||||
requireData: [2101, 5288, 4239, 4962, 6752, 5208, 7450],
|
||||
questionData: [2216, 1148, 1255, 1788, 4821, 1973, 4379],
|
||||
},
|
||||
{
|
||||
requireData: [2101, 3280, 4400, 4962, 5752, 6889, 7600],
|
||||
questionData: [2116, 3148, 3255, 3788, 4821, 4970, 5390],
|
||||
},
|
||||
];
|
||||
|
||||
/** 解决概率 */
|
||||
const progressData = [
|
||||
{
|
||||
week: '周一',
|
||||
percentage: 85,
|
||||
duration: 110,
|
||||
color: '#41b6ff',
|
||||
},
|
||||
{
|
||||
week: '周二',
|
||||
percentage: 86,
|
||||
duration: 105,
|
||||
color: '#41b6ff',
|
||||
},
|
||||
{
|
||||
week: '周三',
|
||||
percentage: 88,
|
||||
duration: 100,
|
||||
color: '#41b6ff',
|
||||
},
|
||||
{
|
||||
week: '周四',
|
||||
percentage: 89,
|
||||
duration: 95,
|
||||
color: '#41b6ff',
|
||||
},
|
||||
{
|
||||
week: '周五',
|
||||
percentage: 94,
|
||||
duration: 90,
|
||||
color: '#26ce83',
|
||||
},
|
||||
{
|
||||
week: '周六',
|
||||
percentage: 96,
|
||||
duration: 85,
|
||||
color: '#26ce83',
|
||||
},
|
||||
{
|
||||
week: '周日',
|
||||
percentage: 100,
|
||||
duration: 80,
|
||||
color: '#26ce83',
|
||||
},
|
||||
].reverse();
|
||||
|
||||
/** 数据统计 */
|
||||
const tableData = Array.from({ length: 30 }).map((_, index) => {
|
||||
return {
|
||||
id: index + 1,
|
||||
requiredNumber: getRandomIntBetween(13500, 19999),
|
||||
questionNumber: getRandomIntBetween(12600, 16999),
|
||||
resolveNumber: getRandomIntBetween(13500, 17999),
|
||||
satisfaction: getRandomIntBetween(95, 100),
|
||||
date: dayjs().subtract(index, 'day').format('YYYY-MM-DD'),
|
||||
};
|
||||
});
|
||||
|
||||
/** 最新动态 */
|
||||
const latestNewsData = cloneDeep(tableData)
|
||||
.slice(0, 26)
|
||||
.map((item, index) => {
|
||||
return Object.assign(item, {
|
||||
date: `${dayjs().subtract(index, 'day').format('YYYY-MM-DD')} ${days[dayjs().subtract(index, 'day').day()]}`,
|
||||
});
|
||||
});
|
||||
|
||||
export { chartData, barChartData, progressData, tableData, latestNewsData };
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import { ref } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
// 前端代码提交记录
|
||||
export const webCommitList = ref([]);
|
||||
// 后端代码提交记录
|
||||
export const serverCommitList = ref([]);
|
||||
|
||||
/** 获取web代码提交记录 */
|
||||
export const getWebCommitList = async () => {
|
||||
const response = await fetch('http://129.211.31.58:3000/api/v1/repos/auth/auth-web/commits?page=1&limit=20');
|
||||
const json = await response.json();
|
||||
webCommitList.value = json.map(item => ({
|
||||
date: dayjs(item?.commit?.committer?.date).format('YYYY-MM-DD HH:mm:ss'),
|
||||
url: item?.committer?.html_url,
|
||||
username: item?.committer?.username,
|
||||
avatar_url: item?.committer?.avatar_url,
|
||||
message: item?.commit?.message,
|
||||
html_url: item?.html_url,
|
||||
}));
|
||||
};
|
||||
/** 获取后端代码提交记录 */
|
||||
export const getServerCommitList = async () => {
|
||||
const response = await fetch('http://129.211.31.58:3000/api/v1/repos/auth/auth-server-java/commits?page=1&limit=20');
|
||||
const json = await response.json();
|
||||
serverCommitList.value = json.map(item => ({
|
||||
date: dayjs(item?.commit?.committer?.date).format('YYYY-MM-DD HH:mm:ss'),
|
||||
url: item?.committer?.html_url,
|
||||
username: item?.committer?.username,
|
||||
avatar_url: item?.committer?.avatar_url,
|
||||
message: item?.commit?.message,
|
||||
html_url: item?.html_url,
|
||||
}));
|
||||
};
|
Loading…
Reference in New Issue