项目初始化

This commit is contained in:
2026-03-24 18:20:42 +08:00
parent 0a1e643f2a
commit 80e3cb1b58
18 changed files with 458 additions and 1170 deletions

View File

@@ -10,291 +10,302 @@
<teleport to="body">
<div v-if="modalVisible" class="modal-overlay" @click.self="closeModal">
<div class="modal-content">
<IndexV01
:account-data="selectedAccountData"
@close="closeModal"
/>
<IndexV01 :account-data="selectedAccountData" @close="closeModal" />
</div>
</div>
</teleport>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'
import { ErpAccount, erpAccountListAll } from '@jeesite/erp/api/erp/account';
import IndexV01 from './detail/indexV01.vue'
import { ref, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
import { ErpAccount, erpAccountListAll } from '@jeesite/erp/api/erp/account';
import IndexV01 from './detail/indexV01.vue';
const vList = ref<ErpAccount[]>()
const totalAmount = ref(0)
const chartRef = ref(null)
let chartInstance = null
const resizeHandler = () => chartInstance?.resize()
const vList = ref<ErpAccount[]>([]);
const totalAmount = ref(0);
const chartRef = ref<HTMLDivElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
const resizeHandler = () => chartInstance?.resize();
const modalVisible = ref(false)
const selectedAccountData = ref({})
const modalVisible = ref(false);
const selectedAccountData = ref({});
const formatAmount = (amount: any) => {
return Number(amount).toFixed(2)
}
const formatAmount = (amount: any) => {
return Number(amount).toFixed(2);
};
async function getList() {
try {
const res = await erpAccountListAll()
vList.value = res || []
calculateTotalAmount(vList.value)
initPieChart()
} catch (error) {
console.error('获取余额数据失败:', error)
vList.value = []
totalAmount.value = 0
}
}
function calculateTotalAmount(data: any[]) {
if (!Array.isArray(data)) {
totalAmount.value = 0
return
}
totalAmount.value = data.reduce((sum, item) => {
const value = Number(item.currentBalance) || 0
return sum + value
}, 0)
}
const closeModal = () => {
modalVisible.value = false
selectedAccountData.value = {}
}
const initPieChart = () => {
const el = chartRef.value
if (!el) return
if (chartInstance) {
chartInstance.dispose()
}
chartInstance = echarts.init(el)
const pieData = vList.value.map(item => ({
name: item.accountName || '未知账户',
value: (Number(item.currentBalance) || 0) / 10000,
originalValue: Number(item.currentBalance) || 0,
rawData: item
})).filter(item => item.value > 0)
const colorList = [
'#409EFF', '#36CFc9', '#67C23A', '#E6A23C', '#F56C6C',
'#909399', '#722ED1', '#EB2F96', '#1890FF', '#52C41A',
'#FAAD14', '#F5222D', '#8C8C8C', '#A062D4', '#F7BA1E'
]
const option = {
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(145, 200, 255, 0.9)',
borderColor: '#409EFF',
borderWidth: 1,
textStyle: { color: '#0a3b70', fontSize: 12 },
padding: [10, 15],
borderRadius: 6,
formatter: function(params: any) {
return `账户:${params.name}<br/>余额:${Number(params.data.originalValue).toFixed(2)}元<br/>占比:${params.percent.toFixed(2)}%`
}
},
legend: {
orient: 'horizontal',
top: '10%',
left: 'center',
textStyle: { fontSize: 11, color: '#e0e6ff' },
itemWidth: 12,
itemHeight: 12,
itemGap: 10,
pageIconColor: '#409EFF',
pageTextStyle: { color: '#e0e6ff', fontSize: 10 },
pageButtonItemGap: 6,
pageButtonGap: 10,
type: 'scroll'
},
series: [
{
name: '余额',
type: 'pie',
radius: ['30%', '55%'],
center: ['50%', '65%'],
avoidLabelOverlap: true,
itemStyle: {
borderRadius: 4,
borderColor: 'rgba(15, 52, 96, 0.9)',
borderWidth: 1
},
label: {
show: true,
position: 'outside',
fontSize: 10,
color: '#e0e6ff',
formatter: function(params: any) {
return `${params.name} ${Number(params.value).toFixed(2)}万元 (${params.percent.toFixed(2)}%)`
},
overflow: 'truncate',
ellipsis: '...',
distance: 8
},
labelLine: {
show: true,
length: 12,
length2: 8,
lineStyle: { color: '#e0e6ff', width: 1 },
smooth: 0.2,
minTurnAngle: 45
},
data: pieData,
color: colorList
}
]
}
chartInstance.setOption(option)
chartInstance.on('click', (params: any) => {
selectedAccountData.value = {
name: params.name,
originalValue: params.data.originalValue,
value: params.data.value,
percent: params.percent,
rawData: params.data.rawData
async function getList() {
try {
const res = await erpAccountListAll();
vList.value = res || [];
calculateTotalAmount(vList.value);
initPieChart();
} catch (error) {
console.error('获取余额数据失败:', error);
vList.value = [];
totalAmount.value = 0;
}
modalVisible.value = true
})
chartInstance.on('legendselectchanged', (params: any) => {
const selectedNames = Object.keys(params.selected).filter(name => params.selected[name])
const selectedData = vList.value.filter(item => {
const accountName = item.accountName || '未知账户'
return selectedNames.includes(accountName)
})
calculateTotalAmount(selectedData)
})
}
onMounted(async () => {
await getList()
window.addEventListener('resize', resizeHandler)
})
onUnmounted(() => {
window.removeEventListener('resize', resizeHandler)
if (chartInstance) {
chartInstance.dispose()
chartInstance = null
}
})
function calculateTotalAmount(data: any[]) {
if (!Array.isArray(data)) {
totalAmount.value = 0;
return;
}
totalAmount.value = data.reduce((sum, item) => {
const value = Number(item.currentBalance) || 0;
return sum + value;
}, 0);
}
const closeModal = () => {
modalVisible.value = false;
selectedAccountData.value = {};
};
const initPieChart = () => {
const el = chartRef.value;
if (!el) return;
if (chartInstance) {
chartInstance.dispose();
}
chartInstance = echarts.init(el);
const pieData = vList.value
.map((item) => ({
name: item.accountName || '未知账户',
value: (Number(item.currentBalance) || 0) / 10000,
originalValue: Number(item.currentBalance) || 0,
rawData: item,
}))
.filter((item) => item.value > 0);
const colorList = [
'#409EFF',
'#36CFc9',
'#67C23A',
'#E6A23C',
'#F56C6C',
'#909399',
'#722ED1',
'#EB2F96',
'#1890FF',
'#52C41A',
'#FAAD14',
'#F5222D',
'#8C8C8C',
'#A062D4',
'#F7BA1E',
];
const option = {
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(145, 200, 255, 0.9)',
borderColor: '#409EFF',
borderWidth: 1,
textStyle: { color: '#0a3b70', fontSize: 12 },
padding: [10, 15],
borderRadius: 6,
formatter: function (params: any) {
return `账户:${params.name}<br/>余额:${Number(params.data.originalValue).toFixed(2)}元<br/>占比:${params.percent.toFixed(2)}%`;
},
},
legend: {
orient: 'horizontal',
top: '10%',
left: 'center',
textStyle: { fontSize: 11, color: '#e0e6ff' },
itemWidth: 12,
itemHeight: 12,
itemGap: 10,
pageIconColor: '#409EFF',
pageTextStyle: { color: '#e0e6ff', fontSize: 10 },
pageButtonItemGap: 6,
pageButtonGap: 10,
type: 'scroll',
},
series: [
{
name: '余额',
type: 'pie',
radius: ['30%', '55%'],
center: ['50%', '65%'],
avoidLabelOverlap: true,
itemStyle: {
borderRadius: 4,
borderColor: 'rgba(15, 52, 96, 0.9)',
borderWidth: 1,
},
label: {
show: true,
position: 'outside',
fontSize: 10,
color: '#e0e6ff',
formatter: function (params: any) {
return `${params.name} ${Number(params.value).toFixed(2)}万元 (${params.percent.toFixed(2)}%)`;
},
overflow: 'truncate',
ellipsis: '...',
distance: 8,
},
labelLine: {
show: true,
length: 12,
length2: 8,
lineStyle: { color: '#e0e6ff', width: 1 },
smooth: 0.2,
minTurnAngle: 45,
},
data: pieData,
color: colorList,
},
],
};
chartInstance.setOption(option);
chartInstance.on('click', (params: any) => {
selectedAccountData.value = {
name: params.name,
originalValue: params.data.originalValue,
value: params.data.value,
percent: params.percent,
rawData: params.data.rawData,
};
modalVisible.value = true;
});
chartInstance.on('legendselectchanged', (params: any) => {
const selectedNames = Object.keys(params.selected).filter((name) => params.selected[name]);
const selectedData = vList.value.filter((item) => {
const accountName = item.accountName || '未知账户';
return selectedNames.includes(accountName);
});
calculateTotalAmount(selectedData);
});
};
onMounted(async () => {
await getList();
window.addEventListener('resize', resizeHandler);
});
onUnmounted(() => {
window.removeEventListener('resize', resizeHandler);
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
});
</script>
<style scoped>
.chart-card {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
background: rgba(0, 0, 0, 0.1) url("@jeesite/assets/chart/box/16.png") no-repeat;
background-size: 100% 100%;
}
.chart-card {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
background: rgba(0, 0, 0, 0.1) url('@jeesite/assets/chart/box/16.png') no-repeat;
background-size: 100% 100%;
}
.chart-card-header {
height: 40px;
line-height: 40px;
padding: 0 16px;
background-color: rgba(26, 80, 139, 0.5);
border-bottom: 1px solid #1a508b;
display: flex;
align-items: center;
justify-content: space-between;
background: rgba(0, 0, 0, 0.1) url("@jeesite/assets/chart/title/03.png") no-repeat;
background-size: 100% 100%;
}
.chart-card-header {
height: 40px;
line-height: 40px;
padding: 0 16px;
background-color: rgba(26, 80, 139, 0.5);
border-bottom: 1px solid #1a508b;
display: flex;
align-items: center;
justify-content: space-between;
background: rgba(0, 0, 0, 0.1) url('@jeesite/assets/chart/title/03.png') no-repeat;
background-size: 100% 100%;
}
.chart-card-title {
font-size: 16px;
font-weight: 600;
color: #409EFF;
letter-spacing: 0.5px;
}
.chart-card-title {
font-size: 16px;
font-weight: 600;
color: #409eff;
letter-spacing: 0.5px;
}
.total-amount {
font-size: 14px;
color: #e0e6ff;
font-weight: 500;
}
.total-amount {
font-size: 14px;
color: #e0e6ff;
font-weight: 500;
}
.pie-chart-container {
flex: 1;
width: 100%;
height: calc(100% - 40px);
margin: 0;
padding: 0;
}
.pie-chart-container {
flex: 1;
width: 100%;
height: calc(100% - 40px);
margin: 0;
padding: 0;
}
:global(.modal-overlay) {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
:global(.modal-overlay) {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
:global(.modal-content) {
width: 90vw;
height: 90vh;
max-width: 1400px;
max-height: 800px;
background: transparent !important;
border: none !important;
border-radius: 8px;
overflow: hidden;
box-shadow: none !important;
}
:global(.modal-content) {
width: 90vw;
height: 90vh;
max-width: 1400px;
max-height: 800px;
background: transparent !important;
border: none !important;
border-radius: 8px;
overflow: hidden;
box-shadow: none !important;
}
:deep(.echarts-tooltip) {
background-color: rgba(145, 200, 255, 0.9) !important;
border-color: #409EFF !important;
color: #0a3b70 !important;
border-radius: 6px !important;
}
:deep(.echarts-tooltip) {
background-color: rgba(145, 200, 255, 0.9) !important;
border-color: #409eff !important;
color: #0a3b70 !important;
border-radius: 6px !important;
}
:deep(.echarts-legend-scroll) {
background-color: transparent !important;
}
:deep(.echarts-legend-scroll) {
background-color: transparent !important;
}
:deep(.echarts-legend-scroll-text) {
color: #e0e6ff !important;
font-size: 11px !important;
}
:deep(.echarts-legend-scroll-text) {
color: #e0e6ff !important;
font-size: 11px !important;
}
:deep(.echarts-legend-scroll-button) {
border-color: #1a508b !important;
}
:deep(.echarts-legend-scroll-button) {
border-color: #1a508b !important;
}
:deep(.echarts-legend-scroll-button-icon) {
color: #409EFF !important;
}
:deep(.echarts-legend-scroll-button-icon) {
color: #409eff !important;
}
:deep(.ec-label) {
z-index: 9999 !important;
white-space: nowrap !important;
font-size: 10px !important;
color: #e0e6ff !important;
}
:deep(.ec-label) {
z-index: 9999 !important;
white-space: nowrap !important;
font-size: 10px !important;
color: #e0e6ff !important;
}
:deep(.ec-label-line) {
stroke: #e0e6ff !important;
stroke-width: 1px !important;
}
</style>
:deep(.ec-label-line) {
stroke: #e0e6ff !important;
stroke-width: 1px !important;
}
</style>