| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-row :gutter="16" class="mb-16"> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <div class="section-title">统一会计科目体系</div> |
| | | <el-tree |
| | | :data="accountTree" |
| | | node-key="code" |
| | | :props="{ label: 'label', children: 'children' }" |
| | | highlight-current |
| | | default-expand-all |
| | | <div style="padding: 20px;"> |
| | | <!-- 页面标题和筛选条件 --> |
| | | <div class="w-full md:w-auto flex items-center gap-3"> |
| | | <el-form :inline="true"> |
| | | <el-form-item label="年份"> |
| | | <el-date-picker |
| | | v-model="selectedYear" |
| | | type="year" |
| | | placeholder="请选择年份" |
| | | format="YYYY" |
| | | value-format="YYYY" |
| | | clearable |
| | | @change="fetchData()" |
| | | style="width: 200px" |
| | | :disabled-date="(date) => date.getFullYear() > new Date().getFullYear()" |
| | | /> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <div class="section-title">凭证模板</div> |
| | | <el-table :data="voucherTemplates" border size="small"> |
| | | <el-table-column prop="name" label="模板名称" min-width="140" /> |
| | | <el-table-column prop="bizScene" label="业务场景" min-width="140" /> |
| | | <el-table-column prop="debit" label="借方科目" min-width="160" /> |
| | | <el-table-column prop="credit" label="贷方科目" min-width="160" /> |
| | | <el-table-column prop="auxDims" label="辅助核算维度" min-width="180"> |
| | | <template #default="scope"> |
| | | <el-space wrap> |
| | | <el-tag v-for="dim in scope.row.auxDims" :key="dim" size="small" type="info">{{ dim }}</el-tag> |
| | | </el-space> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button |
| | | type="primary" |
| | | icon="Refresh" |
| | | @click="resetFilters" |
| | | size="default" |
| | | > |
| | | 重置 |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <el-row :gutter="16" class="mb-16"> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <div class="section-title">业务流程 → 凭证自动生成</div> |
| | | <div class="toolbar"> |
| | | <el-text type="info">演示数据仅用于展示结构与字段</el-text> |
| | | <main class="container mx-auto px-4 pb-10"> |
| | | <!-- 固定资产指标卡片 --> |
| | | <div class="kpi-grid"> |
| | | <!-- 设备总数 --> |
| | | <div class="kpi-card"> |
| | | <div class="kpi-left"> |
| | | <span class="kpi-dot kpi-dot-blue"></span> |
| | | <span class="kpi-title">设备总数</span> |
| | | <div class="kpi-value">{{ assetInfo.totalEquipment }}个</div> |
| | | </div> |
| | | <el-table :data="generatedVouchers" border size="small"> |
| | | <el-table-column prop="date" label="日期" width="110" /> |
| | | <el-table-column prop="bizScene" label="业务场景" min-width="120" /> |
| | | <el-table-column prop="summary" label="摘要" min-width="160" /> |
| | | <el-table-column prop="amount" label="金额(¥)" width="110" /> |
| | | <el-table-column prop="status" label="状态" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === '已生成' ? 'success' : 'warning'">{{ scope.row.status }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <div class="section-title">多维辅助核算</div> |
| | | <div class="dims"> |
| | | <el-tag type="success">客户</el-tag> |
| | | <el-tag type="warning">项目</el-tag> |
| | | <el-tag type="info">部门</el-tag> |
| | | <el-tag type="primary">管理员</el-tag> |
| | | <div class="kpi-icon-wrap kpi-icon-blue"> |
| | | <img :src="iconBlue" alt="" class="kpi-icon" /> |
| | | </div> |
| | | <el-table :data="auxSummary" size="small" border> |
| | | <el-table-column prop="dimension" label="维度" width="100" /> |
| | | <el-table-column prop="category" label="类别" min-width="140" /> |
| | | <el-table-column prop="debit" label="借方(本期)" width="110" /> |
| | | <el-table-column prop="credit" label="贷方(本期)" width="110" /> |
| | | <el-table-column prop="balance" label="余额" width="100" /> |
| | | </el-table> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <el-row :gutter="16" class="mb-16"> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <div class="section-title">结账任务(月/季/年)</div> |
| | | <div class="toolbar"> |
| | | <el-space> |
| | | <el-button size="small" @click="runClose('月结')">执行月结</el-button> |
| | | <el-button size="small" @click="runClose('季报')">执行季报</el-button> |
| | | <el-button size="small" @click="runClose('年度结账')">执行年度结账</el-button> |
| | | </el-space> |
| | | <!-- 资产原值 --> |
| | | <div class="kpi-card"> |
| | | <div class="kpi-left"> |
| | | <span class="kpi-dot kpi-dot-orange"></span> |
| | | <span class="kpi-title">资产原值</span> |
| | | <div class="kpi-value">¥{{ formatCurrency(assetInfo.totalOriginalValue) }}</div> |
| | | </div> |
| | | <el-timeline style="margin-top: 6px;"> |
| | | <el-timeline-item |
| | | v-for="item in closingTasks" |
| | | :key="item.id" |
| | | :type="item.type" |
| | | :timestamp="item.time" |
| | | placement="top" |
| | | > |
| | | <div class="close-item"> |
| | | <div class="title">{{ item.name }}</div> |
| | | <el-tag :type="item.status === '完成' ? 'success' : 'info'" size="small">{{ item.status }}</el-tag> |
| | | <div class="kpi-icon-wrap kpi-icon-orange"> |
| | | <img :src="iconWalletOrange" alt="" class="kpi-icon" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 累计折旧 --> |
| | | <div class="kpi-card"> |
| | | <div class="kpi-left"> |
| | | <span class="kpi-dot kpi-dot-green"></span> |
| | | <span class="kpi-title">累计折旧</span> |
| | | <div class="kpi-value">¥{{ formatCurrency(assetInfo.totalDepreciation) }}</div> |
| | | </div> |
| | | <div class="kpi-icon-wrap kpi-icon-green"> |
| | | <img :src="iconGreen" alt="" class="kpi-icon" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 库存资产 --> |
| | | <div class="kpi-card"> |
| | | <div class="kpi-left"> |
| | | <span class="kpi-dot kpi-dot-pink"></span> |
| | | <span class="kpi-title">库存资产</span> |
| | | <div class="kpi-value">¥{{ formatCurrency(assetInfo.inventoryValue) }}</div> |
| | | </div> |
| | | <div class="kpi-icon-wrap kpi-icon-pink"> |
| | | <img :src="iconPink" alt="" class="kpi-icon" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 净值 --> |
| | | <div class="kpi-card"> |
| | | <div class="kpi-left"> |
| | | <span class="kpi-dot kpi-dot-yellow"></span> |
| | | <span class="kpi-title">净值</span> |
| | | <div class="kpi-value">¥{{ formatCurrency(assetInfo.totalNetValue) }}</div> |
| | | </div> |
| | | <div class="kpi-icon-wrap kpi-icon-yellow"> |
| | | <img :src="iconYellow" alt="" class="kpi-icon" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 负债 --> |
| | | <div class="kpi-card"> |
| | | <div class="kpi-left"> |
| | | <span class="kpi-dot kpi-dot-red"></span> |
| | | <span class="kpi-title">负债</span> |
| | | <div class="kpi-value">¥{{ formatCurrency(assetInfo.debt) }}</div> |
| | | </div> |
| | | <div class="kpi-icon-wrap kpi-icon-red"> |
| | | <img :src="iconWalletRed" alt="" class="kpi-icon" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 固定资产统计图表 --> |
| | | <div class="chart-row"> |
| | | <!-- 设备类型分布 --> |
| | | <el-card class="chart-card"> |
| | | <h2 class="section-title">设备类型分布</h2> |
| | | <div class="chart-content"> |
| | | <div class="pie-wrap"> |
| | | <Echarts |
| | | :legend="typeDistributionLegend" |
| | | :chartStyle="chartStylePie" |
| | | :series="typeDistributionSeries" |
| | | :tooltip="pieTooltip" |
| | | style="height: 260px; width: 100%;" |
| | | /> |
| | | </div> |
| | | <div class="type-cards"> |
| | | <div class="type-card" v-for="(item, index) in typeDistributionData" :key="index"> |
| | | <span class="type-name">{{ item.name }}</span> |
| | | <span class="type-count">{{ item.count }}</span> |
| | | </div> |
| | | </el-timeline-item> |
| | | </el-timeline> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <div class="section-title">审计留痕与审批权限</div> |
| | | <el-table :data="auditTrail" border size="small"> |
| | | <el-table-column prop="time" label="时间" width="160" /> |
| | | <el-table-column prop="action" label="动作" min-width="160" /> |
| | | <el-table-column prop="bizScene" label="关联业务" min-width="140" /> |
| | | <el-table-column prop="role" label="执行角色" width="120" /> |
| | | <el-table-column prop="result" label="结果" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.result === '通过' ? 'success' : (scope.row.result === '驳回' ? 'danger' : 'info')"> |
| | | {{ scope.row.result }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <!-- 设备金额分析 --> |
| | | <el-card class="chart-card"> |
| | | <h2 class="section-title">设备金额分析</h2> |
| | | <div class="bar-chart-wrap"> |
| | | <Echarts |
| | | ref="barChart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="lineLegend" |
| | | :series="typeDistributionBarSeries" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis" |
| | | :yAxis="yAxisBar" |
| | | style="height: 260px; width: 100%;" |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | <!-- 设备数据表 --> |
| | | <el-card class="table-card"> |
| | | <h2 class="section-title">设备数据表</h2> |
| | | <el-table |
| | | :data="equipmentList" |
| | | stripe |
| | | style="width: 100%" |
| | | :header-cell-style="{ background: '#f5f7fa', color: '#606266' }" |
| | | > |
| | | <el-table-column type="index" label="序号" width="60" :index="(index) => (pagination.currentPage - 1) * pagination.pageSize + index + 1" /> |
| | | <el-table-column prop="deviceName" label="设备名称" width="250" /> |
| | | <el-table-column prop="deviceModel" label="规格型号" min-width="150" /> |
| | | <el-table-column prop="supplierName" label="供应商" min-width="120" /> |
| | | <el-table-column prop="unit" label="单位" width="120" /> |
| | | <el-table-column prop="number" label="数量" width="120" /> |
| | | <el-table-column prop="originalValue" label="原值(元)" width="120"> |
| | | <template #default="{ row }"> |
| | | {{ formatCurrency(row.taxIncludingPriceTotal) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="depreciation" label="累计折旧(元)" width="140"> |
| | | <template #default="{ row }"> |
| | | {{ formatCurrency(row.deprAmount) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="netValue" label="净值(元)" width="120"> |
| | | <template #default="{ row }"> |
| | | {{ formatCurrency(row.netValue) }} |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <div class="pagination-container"> |
| | | <el-pagination |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | :current-page="pagination.currentPage" |
| | | :page-sizes="[10, 20, 50, 100]" |
| | | :page-size="pagination.pageSize" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :total="pagination.total" |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | </main> |
| | | |
| | | </div> |
| | | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue' |
| | | import { ref, computed, onMounted, reactive } from 'vue'; |
| | | import 'element-plus/dist/index.css'; |
| | | import Echarts from "@/components/Echarts/echarts.vue"; |
| | | import { getAccountingTotal, getDeviceTypeDistribution, getCalculateDepreciation } from "@/api/financialManagement/accounting"; |
| | | import dayjs from "dayjs"; |
| | | import iconBlue from '@/assets/icons/png/blue@2x.png'; |
| | | import iconWalletOrange from '@/assets/icons/png/walletOrange@2x.png'; |
| | | import iconGreen from '@/assets/icons/png/green@2x.png'; |
| | | import iconPink from '@/assets/icons/png/pink@2x.png'; |
| | | import iconYellow from '@/assets/icons/png/yellow@2x.png'; |
| | | import iconWalletRed from '@/assets/icons/png/walletRed@2x.png'; |
| | | |
| | | // 科目树(示例) |
| | | const accountTree = ref([ |
| | | { code: '1001', label: '资产', children: [ |
| | | { code: '100101', label: '库存现金' }, |
| | | { code: '100102', label: '银行存款' }, |
| | | { code: '1122', label: '应收账款' }, |
| | | { code: '1601', label: '固定资产' }, |
| | | ]}, |
| | | { code: '2001', label: '负债', children: [ |
| | | { code: '2202', label: '应付账款' }, |
| | | { code: '2241', label: '其他应付款' }, |
| | | ]}, |
| | | { code: '3001', label: '所有者权益', children: [ |
| | | { code: '3103', label: '本年利润' }, |
| | | ]}, |
| | | { code: '4001', label: '成本费用', children: [ |
| | | { code: '5601', label: '制造费用' }, |
| | | { code: '6602', label: '管理费用' }, |
| | | ]}, |
| | | ]) |
| | | // 筛选条件 |
| | | const dateRange = ref(null); |
| | | const equipmentType = ref(''); |
| | | const selectedYear = ref(dayjs().format('YYYY')); // 默认当前年份 |
| | | |
| | | // 凭证模板(示例) |
| | | const voucherTemplates = ref([ |
| | | { name: '销售收入确认', bizScene: '销售出库', debit: '1122 应收账款', credit: '6001 主营业务收入', auxDims: ['客户','项目'] }, |
| | | { name: '采购应付确认', bizScene: '采购入库', debit: '1403 在途物资', credit: '2202 应付账款', auxDims: ['项目','部门'] }, |
| | | { name: '费用报销', bizScene: '费用单', debit: '6602 管理费用', credit: '1002 银行存款', auxDims: ['部门'] }, |
| | | { name: '固定资产折旧', bizScene: '月末折旧', debit: '6602 管理费用', credit: '1602 累计折旧', auxDims: ['部门'] }, |
| | | ]) |
| | | |
| | | // 自动生成的凭证(示例) |
| | | const generatedVouchers = ref([ |
| | | { date: '2025-10-01', bizScene: '销售出库', summary: '确认应收与收入', amount: 128000, status: '已生成' }, |
| | | { date: '2025-10-03', bizScene: '采购入库', summary: '确认到货应付', amount: 56000, status: '已生成' }, |
| | | { date: '2025-10-05', bizScene: '费用单', summary: '办公费用报销', amount: 3200, status: '已生成' }, |
| | | ]) |
| | | // 固定资产信息 |
| | | const assetInfo = ref({ |
| | | totalEquipment: 0, // deviceTotal |
| | | totalOriginalValue: 0, // deviceAmount |
| | | totalDepreciation: 0, // deprAmount |
| | | totalNetValue: 0, // netValue |
| | | debt: 0, // 负债 |
| | | inventoryValue: 0 // 库存资产 |
| | | }); |
| | | |
| | | // 无模拟生成操作,仅展示静态示例数据 |
| | | // 设备类型总数(用于图表显示) |
| | | const deviceTypeTotalCount = ref(0); |
| | | |
| | | // 辅助核算示例汇总(无个人姓名,仅维度类别) |
| | | const auxSummary = ref([ |
| | | { dimension: '客户', category: '重点客户集合', debit: 320000, credit: 210000, balance: 110000 }, |
| | | { dimension: '项目', category: '项目A', debit: 150000, credit: 120000, balance: 30000 }, |
| | | { dimension: '部门', category: '运营中心', debit: 42000, credit: 18000, balance: 24000 }, |
| | | { dimension: '管理员', category: '系统角色', debit: 0, credit: 0, balance: 0 }, |
| | | ]) |
| | | // 设备列表 |
| | | const equipmentList = ref([]); |
| | | const pagination = ref({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0 |
| | | }); |
| | | |
| | | // 结账任务 |
| | | const closingTasks = ref([ |
| | | { id: 1, name: '2025年10月 月结', time: '2025-10-31 18:00', status: '完成', type: 'success' }, |
| | | { id: 2, name: '2025年Q4 季报', time: '2025-12-31 18:00', status: '计划', type: 'info' }, |
| | | { id: 3, name: '2025年度 年度结账', time: '2025-12-31 23:00', status: '计划', type: 'info' }, |
| | | ]) |
| | | // 图表配置 |
| | | const chartStyle = { |
| | | width: '100%', |
| | | height: '100%', |
| | | position: 'relative', |
| | | }; |
| | | |
| | | function runClose(kind) { |
| | | closingTasks.value.unshift({ |
| | | id: Date.now(), |
| | | name: `${new Date().getFullYear()}年${kind}`, |
| | | time: new Date().toISOString().replace('T',' ').slice(0,16), |
| | | status: '完成', |
| | | type: 'success', |
| | | }) |
| | | } |
| | | const grid = { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | }; |
| | | |
| | | // 审计留痕(不含个人姓名,仅角色/机制) |
| | | const auditTrail = ref([ |
| | | { time: '2025-10-01 09:12', action: '销售出库触发凭证生成', bizScene: '销售出库', role: '系统自动化', result: '通过' }, |
| | | { time: '2025-10-03 14:20', action: '采购入库触发应付确认', bizScene: '采购入库', role: '系统自动化', result: '通过' }, |
| | | { time: '2025-10-05 10:03', action: '费用单审批', bizScene: '费用单', role: '财务审批', result: '通过' }, |
| | | { time: '2025-10-08 16:45', action: '凭证过账', bizScene: '总账', role: '会计审核', result: '通过' }, |
| | | { time: '2025-10-31 18:05', action: '月结完成并锁账', bizScene: '总账', role: '系统自动化', result: '通过' }, |
| | | ]) |
| | | const lineLegend = { |
| | | show: false, |
| | | }; |
| | | |
| | | // 折线图提示框 |
| | | const tooltip = reactive({ |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'line', |
| | | lineStyle: { color: '#aaa' } |
| | | }, |
| | | // 自定义内容 |
| | | formatter: function (params) { |
| | | if (!params || !params.length) return ''; |
| | | const axisLabel = params[0].axisValueLabel || params[0].axisValue || ''; |
| | | const rows = params |
| | | .map(p => { |
| | | const colorDot = `<span style="display:inline-block;margin-right:6px;width:8px;height:8px;border-radius:50%;background:${p.color}"></span>`; |
| | | return `${colorDot}${p.seriesName}: ${p.value}`; |
| | | }) |
| | | .join('<br/>'); |
| | | return `<div>${axisLabel}</div><div>${rows}</div>`; |
| | | } |
| | | }); |
| | | |
| | | const xAxis = ref([ |
| | | { |
| | | type: 'category', |
| | | axisTick: { show: true, alignWithLabel: true }, |
| | | data: [], |
| | | }, |
| | | ]); |
| | | |
| | | const yAxis = [ |
| | | { |
| | | type: 'value', |
| | | name: '数量/金额', // 左侧y轴 |
| | | position: 'left', |
| | | min: 0, |
| | | // 坐标轴名称样式 |
| | | nameTextStyle: { |
| | | color: '#000', |
| | | fontSize: 14, |
| | | }, |
| | | } |
| | | ]; |
| | | |
| | | const chartStylePie = { |
| | | width: '100%', |
| | | height: '100%' // 设置图表容器的高度 |
| | | }; |
| | | |
| | | const pieColors = ['#165DFF', '#14C9C9', '#8543E0', '#1890FF', '#13C2C2', '#2FC25B']; // 可根据实际调整 |
| | | |
| | | // 饼图数据 |
| | | const typeDistributionData = ref([]); |
| | | const departmentDistributionData = ref([]); |
| | | |
| | | // 饼图图例(悬停显示名称+占比,图例放下方卡片展示) |
| | | const typeDistributionLegend = computed(() => ({ |
| | | show: false, |
| | | data: typeDistributionData.value.map(item => item.name) |
| | | })); |
| | | |
| | | |
| | | // 饼图系列 |
| | | const typeDistributionSeries = computed(() => [ |
| | | { |
| | | type: 'pie', |
| | | radius: ['0%', '65%'], |
| | | center: ['50%', '45%'], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderColor: '#fff', |
| | | borderWidth: 2 |
| | | }, |
| | | label: { show: false }, |
| | | data: typeDistributionData.value, |
| | | color: pieColors |
| | | } |
| | | ]); |
| | | |
| | | // 折线图数据 |
| | | const typeDistributionLineSeries = ref([]); |
| | | // 柱状图数据(设备金额分析) |
| | | const typeDistributionBarSeries = computed(() => [ |
| | | { |
| | | name: '销售额', |
| | | type: 'bar', |
| | | data: typeDistributionData.value.map(item => (item.amountNum != null ? item.amountNum : 0)), |
| | | itemStyle: { color: '#13C2C2' } |
| | | } |
| | | ]); |
| | | // 柱状图 Y 轴 |
| | | const yAxisBar = [ |
| | | { |
| | | type: 'value', |
| | | name: '销售额(万元)', |
| | | position: 'left', |
| | | min: 0, |
| | | nameTextStyle: { color: '#000', fontSize: 14 }, |
| | | splitLine: { lineStyle: { color: '#f0f0f0' } } |
| | | } |
| | | ]; |
| | | |
| | | |
| | | // 饼图提示框(图内样式:名称 + 占比) |
| | | const pieTooltip = reactive({ |
| | | trigger: 'item', |
| | | formatter: function(params) { |
| | | if (!params.data) return params.name; |
| | | const pct = params.percent != null ? params.percent.toFixed(0) : 0; |
| | | return `${params.name} ${pct}%`; |
| | | } |
| | | }); |
| | | |
| | | // 选项数据 |
| | | const equipmentTypeOptions = ref([]); |
| | | |
| | | // 获取数据 |
| | | const fetchData = async () => { |
| | | try { |
| | | // 获取固定资产汇总信息 |
| | | const assetInfoRes = await getAccountingTotal({ |
| | | startDate: dateRange.value ? dateRange.value[0] : null, |
| | | endDate: dateRange.value ? dateRange.value[1] : null, |
| | | equipmentType: equipmentType.value, |
| | | year: selectedYear.value |
| | | }); |
| | | |
| | | if (assetInfoRes.code === 200) { |
| | | // 映射后端字段到前端字段 |
| | | const data = assetInfoRes.data; |
| | | assetInfo.value = { |
| | | totalEquipment: data.deviceTotal || 0, // 设备总数 |
| | | totalOriginalValue: data.deviceAmount || 0, // 资产原值 |
| | | totalDepreciation: data.deprAmount || 0, // 累计折旧 |
| | | totalNetValue: data.netValue || 0, // 净值 |
| | | debt: data.debt || 0, // 负债 |
| | | inventoryValue: data.inventoryValue || 0 // 库存资产 |
| | | }; |
| | | } |
| | | |
| | | // 获取设备类型分布数据(饼图和折线图) |
| | | const distributionRes = await getDeviceTypeDistribution({ |
| | | startDate: dateRange.value ? dateRange.value[0] : null, |
| | | endDate: dateRange.value ? dateRange.value[1] : null, |
| | | equipmentType: equipmentType.value, |
| | | year: selectedYear.value |
| | | }); |
| | | |
| | | if (distributionRes.code === 200) { |
| | | const data = distributionRes.data; |
| | | |
| | | // 更新设备类型总数 |
| | | deviceTypeTotalCount.value = data.totalCount || 0; |
| | | |
| | | // 转换饼图数据格式 |
| | | if (data.details && data.details.length > 0) { |
| | | typeDistributionData.value = data.details.map(item => ({ |
| | | name: item.type || '', |
| | | value: Number(item.count || 0), |
| | | count: Number(item.count || 0), |
| | | amount: `¥${formatCurrency(item.amount || 0)}`, |
| | | amountNum: Number(item.amount || 0) |
| | | })); |
| | | } else if (data.categories && data.categories.length > 0) { |
| | | // 如果没有 details,使用 categories、countData 和 amountData 构建 |
| | | typeDistributionData.value = data.categories.map((category, index) => ({ |
| | | name: category, |
| | | value: Number(data.countData[index] || 0), |
| | | count: Number(data.countData[index] || 0), |
| | | amount: `¥${formatCurrency(data.amountData[index] || 0)}`, |
| | | amountNum: Number(data.amountData[index] || 0) |
| | | })); |
| | | } else { |
| | | typeDistributionData.value = []; |
| | | } |
| | | |
| | | // 更新x轴数据 |
| | | xAxis.value[0].data = data.categories || typeDistributionData.value.map(item => item.name); |
| | | |
| | | // 构建折线图数据 |
| | | typeDistributionLineSeries.value = [ |
| | | { |
| | | name: '设备数量', |
| | | type: 'line', |
| | | data: data.countData || typeDistributionData.value.map(item => item.count) |
| | | } |
| | | ]; |
| | | } |
| | | |
| | | // 获取设备列表(折旧计算数据) |
| | | const equipmentListRes = await getCalculateDepreciation({ |
| | | current: pagination.value.currentPage, |
| | | size: pagination.value.pageSize, |
| | | startDate: dateRange.value ? dateRange.value[0] : null, |
| | | endDate: dateRange.value ? dateRange.value[1] : null, |
| | | equipmentType: equipmentType.value, |
| | | year: selectedYear.value |
| | | }); |
| | | |
| | | if (equipmentListRes.code === 200) { |
| | | // 如果返回的是分页数据 |
| | | if (equipmentListRes.data.records) { |
| | | equipmentList.value = equipmentListRes.data.records; |
| | | pagination.value.total = equipmentListRes.data.total; |
| | | } else if (Array.isArray(equipmentListRes.data)) { |
| | | // 如果返回的是数组 |
| | | equipmentList.value = equipmentListRes.data; |
| | | pagination.value.total = equipmentListRes.data.length; |
| | | } else { |
| | | equipmentList.value = []; |
| | | pagination.value.total = 0; |
| | | } |
| | | } |
| | | } catch (error) { |
| | | console.error('获取固定资产数据失败:', error); |
| | | } |
| | | }; |
| | | |
| | | // 初始化 |
| | | onMounted(() => { |
| | | // 获取列表数据 |
| | | fetchData(); |
| | | }); |
| | | |
| | | // 格式化货币 |
| | | const formatCurrency = (value) => { |
| | | if (!value) return '0.00'; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ','); |
| | | }; |
| | | |
| | | // 获取状态标签类型 |
| | | const getStatusTagType = (status) => { |
| | | switch (status) { |
| | | case '在用': |
| | | return 'success'; |
| | | case '闲置': |
| | | return 'info'; |
| | | case '维修中': |
| | | return 'warning'; |
| | | case '报废': |
| | | return 'danger'; |
| | | default: |
| | | return 'info'; |
| | | } |
| | | }; |
| | | |
| | | // 重置筛选条件 |
| | | const resetFilters = () => { |
| | | dateRange.value = null; |
| | | equipmentType.value = ''; |
| | | selectedYear.value = dayjs().format('YYYY'); // 重置为当前年份 |
| | | fetchData(); |
| | | }; |
| | | |
| | | // 分页处理 |
| | | const handleSizeChange = (size) => { |
| | | pagination.value.pageSize = size; |
| | | fetchData(); |
| | | }; |
| | | |
| | | const handleCurrentChange = (page) => { |
| | | pagination.value.currentPage = page; |
| | | fetchData(); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .app-container { |
| | | padding: 16px; |
| | | :root { |
| | | --el-color-primary: #1890ff; |
| | | } |
| | | .page-header { |
| | | margin-bottom: 12px; |
| | | h2 { margin: 0 0 6px 0; font-weight: 600; } |
| | | p { margin: 0; color: #666; } |
| | | |
| | | /* 页面背景 */ |
| | | main { |
| | | background: #f5f5f5; |
| | | padding: 0; |
| | | margin: 0 -20px; |
| | | padding: 0 20px 20px; |
| | | } |
| | | |
| | | /* KPI 卡片网格 */ |
| | | .kpi-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(3, 1fr); |
| | | gap: 16px; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | @media (max-width: 1024px) { |
| | | .kpi-grid { |
| | | grid-template-columns: repeat(2, 1fr); |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 640px) { |
| | | .kpi-grid { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | } |
| | | |
| | | /* KPI 卡片 - 白底、圆角、阴影 */ |
| | | .kpi-card { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | padding: 16px 20px; |
| | | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); |
| | | min-height: 100px; |
| | | } |
| | | |
| | | .kpi-left { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .kpi-dot { |
| | | display: inline-block; |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 50%; |
| | | margin-right: 6px; |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | .kpi-dot-blue { background: #1890ff; } |
| | | .kpi-dot-orange { background: #fa8c16; } |
| | | .kpi-dot-green { background: #52c41a; } |
| | | .kpi-dot-pink { background: #eb2f96; } |
| | | .kpi-dot-yellow { background: #facc14; } |
| | | .kpi-dot-red { background: #f5222d; } |
| | | |
| | | .kpi-title { |
| | | font-size: 14px; |
| | | color: #333; |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | .kpi-value { |
| | | font-size: 24px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | margin-top: 8px; |
| | | line-height: 1.2; |
| | | } |
| | | |
| | | /* 右侧图标方块 */ |
| | | .kpi-icon-wrap { |
| | | width: 48px; |
| | | height: 48px; |
| | | border-radius: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .kpi-icon-blue { background: #e6f7ff; } |
| | | .kpi-icon-orange { background: #fff7e6; } |
| | | .kpi-icon-green { background: #f6ffed; } |
| | | .kpi-icon-pink { background: #fff0f6; } |
| | | .kpi-icon-yellow { background: #fffbe6; } |
| | | .kpi-icon-red { background: #fff1f0; } |
| | | |
| | | .kpi-icon { |
| | | width: 28px; |
| | | height: 28px; |
| | | object-fit: contain; |
| | | } |
| | | |
| | | /* 图表区域两列 */ |
| | | .chart-row { |
| | | display: grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | @media (max-width: 1024px) { |
| | | .chart-row { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | } |
| | | |
| | | .chart-card { |
| | | border-radius: 8px; |
| | | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); |
| | | |
| | | :deep(.el-card__body) { |
| | | padding: 16px 20px; |
| | | } |
| | | } |
| | | |
| | | .chart-content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | } |
| | | |
| | | .pie-wrap { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 260px; |
| | | } |
| | | |
| | | .type-cards { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 12px; |
| | | justify-content: center; |
| | | margin-top: 12px; |
| | | } |
| | | |
| | | .type-card { |
| | | background: #fafafa; |
| | | border-radius: 6px; |
| | | padding: 8px 16px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | min-width: 80px; |
| | | } |
| | | |
| | | .type-name { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | .type-count { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .bar-chart-wrap { |
| | | width: 100%; |
| | | height: 260px; |
| | | } |
| | | |
| | | /* 区块标题 - 左侧蓝色竖线 */ |
| | | .section-title { |
| | | position: relative; |
| | | padding-left: 10px; |
| | | margin-bottom: 10px; |
| | | font-size: 18px; |
| | | color: #333; |
| | | padding-left: 12px; |
| | | margin-bottom: 16px; |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .section-title::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 0; top: 0.2em; |
| | | width: 4px; height: 1.2em; |
| | | background: #002FA7; |
| | | left: 0; |
| | | top: 2px; |
| | | content: ''; |
| | | width: 4px; |
| | | height: 18px; |
| | | background: #1890ff; |
| | | border-radius: 2px; |
| | | } |
| | | .mb-16 { margin-bottom: 16px; } |
| | | .toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; } |
| | | .dims { display: flex; gap: 8px; margin-bottom: 10px; } |
| | | .close-item { display: flex; align-items: center; gap: 8px; } |
| | | |
| | | /* 表格卡片 */ |
| | | .table-card { |
| | | border-radius: 8px; |
| | | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); |
| | | |
| | | :deep(.el-card__body) { |
| | | padding: 16px 20px; |
| | | } |
| | | } |
| | | |
| | | .pagination-container { |
| | | margin-top: 20px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | align-items: center; |
| | | } |
| | | |
| | | :deep(.el-pagination) { |
| | | --el-pagination-button-bg-color: #fff; |
| | | } |
| | | |
| | | :deep(.el-pager li.is-active) { |
| | | background: #1890ff; |
| | | } |
| | | </style> |
| | | |
| | | |