feat 财务中心

This commit is contained in:
Ironsp
2026-10-09 17:17:56 +08:00
parent 123d259f92
commit 6af8fd7b09
3 changed files with 87 additions and 52 deletions

View File

@@ -284,3 +284,5 @@
@finance-entry-export-bg: #e6f6ec; @finance-entry-export-bg: #e6f6ec;
@finance-entry-badge-bg: #f53f3f; @finance-entry-badge-bg: #f53f3f;
@finance-chart-switch-active: #024fff; @finance-chart-switch-active: #024fff;
@finance-switch-active-bg: #e8f0ff;
@finance-switch-idle-bg: #f2f4f7;

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { ElRadioGroup, ElRadioButton } from 'element-plus' import { ElButton } from 'element-plus'
interface DistributionItem { interface DistributionItem {
name: string name: string
@@ -31,9 +31,23 @@ const AXIS = [250, 200, 150, 100, 50]
const AXIS_MAX = AXIS[0] const AXIS_MAX = AXIS[0]
const MONTHS = ['1月', '2月', '3月', '4月', '5月'] const MONTHS = ['1月', '2月', '3月', '4月', '5月']
interface PeriodOption {
label: string
value: 'month' | 'half'
}
// 设计稿 #20 右上角切换:月度账单(激活)/ 上半 // 设计稿 #20 右上角切换:月度账单(激活)/ 上半
const PERIODS: PeriodOption[] = [
{ label: '月度账单', value: 'month' },
{ label: '上半', value: 'half' },
]
const period = ref<'month' | 'half'>('month') const period = ref<'month' | 'half'>('month')
function onPeriodChange(value: 'month' | 'half'): void {
period.value = value
}
// 柱高按 Y 轴量程绝对值换算(设计稿 #20:Y 轴 250 为满量程),非占比 // 柱高按 Y 轴量程绝对值换算(设计稿 #20:Y 轴 250 为满量程),非占比
function heightPercentOf(value: number): number { function heightPercentOf(value: number): number {
if (value <= 0) return 0 if (value <= 0) return 0
@@ -47,14 +61,17 @@ function heightPercentOf(value: number): number {
业务类型分布 业务类型分布
</h3> </h3>
<el-radio-group v-model="period" class="finance-chart__switch"> <div class="finance-chart__switch">
<el-radio-button value="month"> <el-button
月度账单 v-for="item in PERIODS"
</el-radio-button> :key="item.value"
<el-radio-button value="half"> class="finance-chart__switch-btn"
上半 :class="{ 'is-active': period === item.value }"
</el-radio-button> @click="onPeriodChange(item.value)"
</el-radio-group> >
{{ item.label }}
</el-button>
</div>
<div class="finance-chart__body"> <div class="finance-chart__body">
<ul class="finance-chart__axis" aria-hidden="true"> <ul class="finance-chart__axis" aria-hidden="true">
@@ -136,30 +153,38 @@ function heightPercentOf(value: number): number {
} }
&__switch { &__switch {
.flex(row, flex-end, center);
gap: 8px;
position: absolute; position: absolute;
top: 25px; top: 25px;
right: 23px; right: 23px;
:deep(.el-radio-button__inner) { :deep(.el-button + .el-button) {
border: none; margin-left: 0;
border-radius: 0; }
padding: 0;
background: transparent;
box-shadow: none;
font-size: 11px;
font-weight: 500;
line-height: 15px;
color: @finance-muted-text;
transition: color 0.2s;
} }
:deep(.el-radio-button.is-active .el-radio-button__inner) { &__switch-btn {
background: transparent; height: 24px;
margin-left: 0;
padding: 0 12px;
border: none;
border-radius: @radius-round;
background: @finance-switch-idle-bg;
font-size: 11px;
font-weight: 500;
line-height: 24px;
color: @finance-muted-text;
transition: color 0.2s, background 0.2s;
&:hover {
background: @finance-switch-idle-bg;
color: @finance-chart-switch-active; color: @finance-chart-switch-active;
} }
:deep(.el-radio-button + .el-radio-button) { &.is-active {
margin-left: 14px; background: @finance-switch-active-bg;
color: @finance-chart-switch-active;
} }
} }

View File

@@ -2,7 +2,7 @@
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { RouterLink } from 'vue-router' import { RouterLink } from 'vue-router'
import { ElMessage, ElButton, ElRadioGroup, ElRadioButton, ElTable, ElTableColumn } from 'element-plus' import { ElMessage, ElButton, ElTable, ElTableColumn } from 'element-plus'
import { FilterPanel, PaginationBar, StatusTag } from '@/components' import { FilterPanel, PaginationBar, StatusTag } from '@/components'
import { useFinanceStore } from '@/stores/modules/finance' import { useFinanceStore } from '@/stores/modules/finance'
import type { Invoice, InvoiceStatus } from '@/types/business/finance' import type { Invoice, InvoiceStatus } from '@/types/business/finance'
@@ -36,10 +36,6 @@ function onTab(value: InvoiceStatus | 'all'): void {
void financeStore.searchInvoices() void financeStore.searchInvoices()
} }
function onTabChange(val: string | number | boolean | undefined): void {
onTab(val as InvoiceStatus | 'all')
}
function onSearch(): void { function onSearch(): void {
financeStore.setInvoiceQuery({ ...invoiceQuery.value, keyword: keyword.value }) financeStore.setInvoiceQuery({ ...invoiceQuery.value, keyword: keyword.value })
void financeStore.searchInvoices() void financeStore.searchInvoices()
@@ -112,19 +108,17 @@ onMounted(() => {
</RouterLink> </RouterLink>
</section> </section>
<el-radio-group <div class="invoices__tabs">
class="invoices__tabs" <el-button
:model-value="invoiceQuery.status"
@change="onTabChange"
>
<el-radio-button
v-for="tab in STATUS_TABS" v-for="tab in STATUS_TABS"
:key="tab.value" :key="tab.value"
:value="tab.value" class="invoices__tab"
:class="{ 'is-active': invoiceQuery.status === tab.value }"
@click="onTab(tab.value)"
> >
{{ tab.label }} {{ tab.label }}
</el-radio-button> </el-button>
</el-radio-group> </div>
<FilterPanel <FilterPanel
v-model:keyword="keyword" v-model:keyword="keyword"
@@ -326,26 +320,40 @@ onMounted(() => {
} }
&__tabs { &__tabs {
:deep(.el-radio-button__inner) { display: flex;
flex-direction: row;
align-items: flex-start;
gap: @spacing-sm;
}
&__tab {
height: 34px; height: 34px;
margin: 0;
padding: 0 @spacing-base; padding: 0 @spacing-base;
border: 1px solid @border-color; border: 1px solid @border-color;
border-radius: @radius-base; border-radius: @radius-base;
background: @white; background: @white;
color: @text-color; color: @text-color;
font-size: @font-size-base; font-size: @font-size-base;
box-shadow: none; font-weight: 400;
transition: all 0.2s;
&:hover {
border-color: @primary-color;
background: @white;
color: @primary-color;
} }
:deep(.el-radio-button + .el-radio-button) { &.is-active {
margin-left: @spacing-sm;
}
:deep(.el-radio-button.is-active .el-radio-button__inner) {
border-color: @primary-color; border-color: @primary-color;
background: @primary-color; background: @primary-color;
color: @white; color: @white;
box-shadow: -1px 0 0 0 @primary-color;
&:hover {
border-color: @primary-color;
background: @primary-color;
color: @white;
}
} }
} }