zouyu
4 天以前 ef754c4844cdd88949117ef6cdc43325dd38d557
代码调整11
已添加2个文件
已修改3个文件
979 ■■■■■ 文件已修改
src/views/financialManagement/accounting/index.vue 547 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/expenseManagement/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/financialStatements/index.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/inventoryAccounting/index.vue 390 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/revenueManagement/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/accounting/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,547 @@
<template>
  <div style="padding: 20px;">
    <!-- é¡µé¢æ ‡é¢˜å’Œç­›é€‰æ¡ä»¶ -->
    <div class="w-full md:w-auto flex items-center gap-3" style="margin-bottom: 20px;">
      <el-button
        type="primary"
        icon="Refresh"
        @click="resetFilters"
        size="default"
      >
        æŸ¥è¯¢
      </el-button>
    </div>
    <main class="container mx-auto px-4 pb-10">
      <!-- å›ºå®šèµ„产指标卡片 -->
      <div class="grid-container">
        <!-- è®¾å¤‡æ€»æ•° -->
        <el-card class="bg2">
          <p>设备总数</p>
          <h3>
            {{ assetInfo.totalEquipment }}
          </h3>
        </el-card>
        <!-- èµ„产原值 -->
        <el-card class="bg3">
          <p>资产原值</p>
          <h3>
            Â¥{{ assetInfo.totalOriginalValue }}
          </h3>
        </el-card>
        <!-- ç´¯è®¡æŠ˜æ—§ -->
        <el-card class="bg4">
          <p>累计折旧</p>
          <h3>
            Â¥{{ assetInfo.totalDepreciation }}
          </h3>
        </el-card>
        <!-- å‡€å€¼ -->
        <el-card class="bg5">
          <p>净值</p>
          <h3>
            Â¥{{ assetInfo.totalNetValue }}
          </h3>
        </el-card>
      </div>
      <!-- å›ºå®šèµ„产统计图表 -->
      <div class="grid-layout">
        <!-- æŒ‰è®¾å¤‡ç±»åž‹ç»Ÿè®¡ -->
        <el-card style="margin-bottom: 20px;">
          <h2 class="section-title">设备类型分布</h2>
          <div class="echarts">
            <Echarts
                :legend="typeDistributionLegend"
                :chartStyle="chartStylePie"
                :series="typeDistributionSeries"
                :tooltip="pieTooltip"
                style="height: 260px; width: 35%;">
              <div class="chart-num">
                <span style="font-size: 22px;">设备类型</span>
                <span style="font-size: 36px; font-weight: 500; font-family: 'MyCustomFont', sans-serif;">{{ assetInfo.totalEquipment }}</span>
              </div>
            </Echarts>
            <Echarts
                ref="chart"
                :chartStyle="chartStyle"
                :grid="grid"
                :legend="lineLegend"
                :series="typeDistributionLineSeries"
                :tooltip="tooltip"
                :xAxis="xAxis"
                :yAxis="yAxis"
                style="height: 260px; width: 64%;"></Echarts>
          </div>
        </el-card>
      </div>
      <!-- è®¾å¤‡å°è´¦è¡¨æ ¼ -->
      <el-card style="margin-bottom: 20px;">
        <el-table
          :data="equipmentList"
          stripe
          style="width: 100%"
          :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
        >
          <el-table-column prop="id" label="资产编号" width="120" />
          <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.taxIncludingPriceTotal-row.unTaxIncludingPriceTotal) }}
            </template>
          </el-table-column>
          <el-table-column prop="netValue" label="净值(元)" width="120">
            <template #default="{ row }">
              Â¥{{ formatCurrency(row.unTaxIncludingPriceTotal) }}
            </template>
          </el-table-column>
          <el-table-column prop="status" label="状态" width="100">
            <template #default="{ row }">
              <el-tag
                :type="getStatusTagType(row.status)"
                size="small"
              >
                {{ row.status }}
              </el-tag>
            </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, computed, onMounted, reactive } from 'vue';
import 'element-plus/dist/index.css';
import Echarts from "@/components/Echarts/echarts.vue";
import { getLedgerPage, getAssetInfo } from "@/api/equipmentManagement/ledger";
import dayjs from "dayjs";
// ç­›é€‰æ¡ä»¶
const dateRange = ref(null);
const equipmentType = ref('');
// å›ºå®šèµ„产信息
const assetInfo = ref({
  totalEquipment: 0,
  totalOriginalValue: 0,
  totalDepreciation: 0,
  totalNetValue: 0
});
// è®¾å¤‡åˆ—表
const equipmentList = ref([]);
const pagination = ref({
  currentPage: 1,
  pageSize: 10,
  total: 0
});
// å›¾è¡¨é…ç½®
const chartStyle = {
  width: '100%',
  height: '100%',
  position: 'relative',
};
const grid = {
  left: '3%',
  right: '4%',
  bottom: '3%',
  containLabel: true
};
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 = ['#F04864', '#FACC14', '#8543E0', '#1890FF', '#13C2C2', '#2FC25B']; // å¯æ ¹æ®å®žé™…调整
// é¥¼å›¾æ•°æ®
const typeDistributionData = ref([]);
const departmentDistributionData = ref([]);
// é¥¼å›¾å›¾ä¾‹
const typeDistributionLegend = computed(() => ({
  show: true,
  top: 'center',
  left: '60%',
  orient: 'vertical',
  icon: 'circle',
  data: typeDistributionData.value.map(item => item.name),
  formatter: function(name) {
    const item = typeDistributionData.value.find(i => i.name === name);
    if (!item) return name;
    return `${name} | ${item.count} å° | ${item.amount}`;
  },
  textStyle: {
    color: '#333',
    fontSize: 14,
    lineHeight: 26,
  }
}));
// é¥¼å›¾ç³»åˆ—
const typeDistributionSeries = computed(() => [
  {
    type: 'pie',
    radius: ['50%', '65%'],
    center: ['25%', '50%'],
    avoidLabelOverlap: false,
    itemStyle: {
      borderColor: '#fff',
      borderWidth: 2
    },
    label: {
      show: false
    },
    data: typeDistributionData.value,
    color: pieColors
  }
]);
// æŠ˜çº¿å›¾æ•°æ®
const typeDistributionLineSeries = ref([]);
// é¥¼å›¾æç¤ºæ¡†
const pieTooltip = reactive({
  trigger: 'item',
  formatter: function(params) {
    // æ£€æŸ¥æ•°æ®æ˜¯å¦å­˜åœ¨
    if (!params.data) return params.name;
    // æ‹¼æŽ¥å®Œæ•´å†…容
    return `
      <div>
        <div style="color:${params.color};font-size:16px;">●</div>
        <div>${params.name}</div>
        <div>数量:${params.data.count} å°</div>
        <div>金额:${params.data.amount}</div>
      </div>
    `;
  }
});
// é€‰é¡¹æ•°æ®
const equipmentTypeOptions = ref([]);
// èŽ·å–æ•°æ®
const fetchData = async () => {
  try {
    // èŽ·å–å›ºå®šèµ„äº§æ±‡æ€»ä¿¡æ¯
    const assetInfoRes = await getAssetInfo({
      startDate: dateRange.value ? dateRange.value[0] : null,
      endDate: dateRange.value ? dateRange.value[1] : null,
      equipmentType: equipmentType.value
    });
    if (assetInfoRes.code === 200) {
      assetInfo.value = assetInfoRes.data;
    }
    // èŽ·å–è®¾å¤‡åˆ—è¡¨
    const equipmentListRes = await getLedgerPage({
      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
    });
    if (equipmentListRes.code === 200) {
      equipmentList.value = equipmentListRes.data.records;
      pagination.value.total = equipmentListRes.data.total;
      // æ ¹æ® equipmentList æŒ‰ deviceName è¿›è¡Œåˆ†ç±»ç»Ÿè®¡
      const deviceNameMap = {};
      equipmentList.value.forEach(item => {
        const deviceName = item.deviceName;
        if (!deviceNameMap[deviceName]) {
          deviceNameMap[deviceName] = {
            name: deviceName,
            count: 0,
            totalValue: 0
          };
        }
        deviceNameMap[deviceName].count += item.number || 1; // å‡è®¾ number ä¸ºè®¾å¤‡æ•°é‡
        deviceNameMap[deviceName].totalValue += item.taxIncludingPriceTotal || 0; // ç´¯åŠ å«ç¨Žæ€»ä»·
      });
      // è½¬æ¢ä¸º typeDistributionData æ ¼å¼
      typeDistributionData.value = Object.values(deviceNameMap).map(item => ({
        name: item.name,
        value: item.count,
        count: item.count,
        amount: `Â¥${formatCurrency(item.totalValue)}`
      }));
      // æ›´æ–°x轴数据
      xAxis.value[0].data = typeDistributionData.value.map(item => item.name);
      // æž„建折线图数据
      typeDistributionLineSeries.value = [
        {
          name: '设备数量',
          type: 'line',
          data: typeDistributionData.value.map(item => item.count)
        }
      ];
    }
  } 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 = '';
  fetchData();
};
// åˆ†é¡µå¤„理
const handleSizeChange = (size) => {
  pagination.value.pageSize = size;
  fetchData();
};
const handleCurrentChange = (page) => {
  pagination.value.currentPage = page;
  fetchData();
};
</script>
<style scoped lang="scss">
/* åŸºç¡€æ ·å¼è¡¥å…… */
:root {
  --el-color-primary: #4f46e5;
}
.el-card {
  position: relative;
  border-radius: 12px;
  padding: 14px 10px 10px 10px;
  box-shadow: 0 2px 8px #eee;
  :deep(.el-card__body) {
    padding: 10px 20px !important;
  }
  &.bg1 {
    background: url(@/assets/icons/png/1.png) no-repeat 100% 100% !important;
  }
  &.bg2 {
    background: url(@/assets/icons/png/2.png) no-repeat 100% 100% !important;
  }
  &.bg3 {
    background: url(@/assets/icons/png/3.png) no-repeat 100% 100% !important;
  }
  &.bg4 {
    background: url(@/assets/icons/png/4.png) no-repeat 100% 100% !important;
  }
  &.bg5 {
    background: url(@/assets/icons/png/5.png) no-repeat 100% 100% !important;
  }
}
.grid-container {
  /* grid å®¹å™¨åŸºç¡€æ ·å¼ */
  display: grid;
  gap: 1rem; /* gap-4 å¯¹åº” 1rem (16px) */
  margin-bottom: 2rem; /* mb-8 å¯¹åº” 2rem (32px) */
  p {
    font-size: 22px;
    margin-top: 0px;
    color: #fff;
  }
  h3 {
    font-size: 36px;
    font-weight: 500;
    font-family: 'MyCustomFont', sans-serif;
    margin: 10px 0;
    color: #fff;
  }
}
/* ç§»åŠ¨ç«¯é»˜è®¤æ ·å¼ (grid-cols-1) */
.grid-container {
  grid-template-columns: repeat(1, minmax(0, 1fr));
}
/* å°å±å¹•及以上 (sm:grid-cols-2) */
@media (min-width: 640px) {
  .grid-container {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
/* å¤§å±å¹•及以上 (lg:grid-cols-5) */
@media (min-width: 1024px) {
  .grid-container {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
}
/* å¡ç‰‡æ‚¬åœæ•ˆæžœå¢žå¼º */
.el-card:hover {
  transform: translateY(-2px);
}
.echarts {
  display: flex;
  justify-content: space-between;
}
/* å›¾è¡¨å®¹å™¨æ ·å¼ */
.el-chart {
  width: 100%;
  height: 100%;
}
.section-title {
  position: relative;
  font-size: 18px;
  color: #333;
  padding-left: 10px;
  margin-bottom: 10px;
  font-weight: 700;
}
.section-title::before {
  position: absolute;
  left: 0;
  top: 0px;
  content: '';
  width: 4px;
  height: 18px;
  background-color: #002FA7;
  border-radius: 2px;
}
.chart-num {
  position: absolute;
  z-index: 3;
  top: 92px;
  left: 92px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.pagination-container {
  margin-top: 20px;
  display: flex;
  justify-content: center;
}
</style>
src/views/financialManagement/expenseManagement/index.vue
@@ -106,6 +106,7 @@
  listPage,
  {
    expenseMethod: undefined,
    entryDate: undefined,
  },
  [
    {
@@ -226,9 +227,11 @@
const changeDaterange = (value) => {
  if (value) {
    filters.entryDate = value;
    filters.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    filters.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
  } else {
    filters.entryDate = null;
    filters.entryDateStart = undefined;
    filters.entryDateEnd = undefined;
  }
@@ -256,12 +259,6 @@
};
onMounted(() => {
  filters.entryDate = [
    dayjs().format("YYYY-MM-DD"),
    dayjs().add(1, "day").format("YYYY-MM-DD"),
  ]
  filters.entryDateStart = dayjs().format("YYYY-MM-DD")
  filters.entryDateEnd = dayjs().add(1, "day").format("YYYY-MM-DD")
  getTableData();
});
</script>
src/views/financialManagement/financialStatements/index.vue
@@ -10,7 +10,7 @@
        range-separator="至"
        start-placeholder="开始日期"
        end-placeholder="结束日期"
        :default-value="[new Date(firstDayOfMonth), new Date()]"
        clearable
        @change="handleDateChange"
        class="w-full md:w-auto"
        style="margin-right: 30px;"
@@ -137,8 +137,7 @@
import dayjs from "dayjs";
// æ—¥æœŸèŒƒå›´
const dateRange = ref([]);
const firstDayOfMonth = ref(null);
const dateRange = ref(null);
const chartStyle = {
    width: '100%',
    height: '100%', // è®¾ç½®å›¾è¡¨å®¹å™¨çš„高度
@@ -320,6 +319,9 @@
})
const getData = async () => {
  if (!dateRange.value || !dateRange.value.length) {
    return;
  }
  try {
    const {code,data} = await reportForms({entryDateStart:dateRange.value[0], entryDateEnd:dateRange.value[1]});
    if(code === 200) {
@@ -370,30 +372,22 @@
};
// åˆå§‹åŒ–日期范围(默认当月)
// åˆå§‹åŒ–
onMounted(() => {
  const today = new Date();
  const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
  firstDayOfMonth.value = firstDay;
  dateRange.value = [dayjs(firstDay).format("YYYY-MM-DD"), dayjs(today).format("YYYY-MM-DD")];
  getData()
  // ä¸è®¾ç½®é»˜è®¤æ—¥æœŸï¼Œç”±ç”¨æˆ·æ‰‹åŠ¨é€‰æ‹©
});
// å¤„理日期范围变化
const handleDateChange = (newRange) => {
  if (newRange && newRange.length === 2) {
    dateRange.value = newRange;
  if (newRange && newRange.length === 2) {
    getData()
  }
};
// é‡ç½®æ—¥æœŸèŒƒå›´
const resetDateRange = () => {
  const today = new Date();
  const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
  dateRange.value = [dayjs(firstDay).format("YYYY-MM-DD"), dayjs(today).format("YYYY-MM-DD")];
  getData()
  dateRange.value = null;
};
</script>
src/views/financialManagement/inventoryAccounting/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,390 @@
<template>
  <div class="inventory-statistics">
    <!-- ç­›é€‰è¡¨å• -->
    <div class="filter-form">
      <el-form :model="filterForm" inline>
<!--        <el-form-item label="时间范围">-->
<!--          <el-date-picker-->
<!--            v-model="filterForm.dateRange"-->
<!--            type="daterange"-->
<!--            range-separator="至"-->
<!--            start-placeholder="开始日期"-->
<!--            end-placeholder="结束日期"-->
<!--          />-->
<!--        </el-form-item>-->
<!--        <el-form-item label="供应商名称">-->
<!--          <el-input v-model="filterForm.supplierName" style="width: 240px" placeholder="请输入" clearable prefix-icon="Search" />-->
<!--        </el-form-item>-->
<!--        <el-form-item label="产品名称">-->
<!--          <el-input v-model="filterForm.productCategory" style="width: 240px" placeholder="请输入" clearable prefix-icon="Search" />-->
<!--        </el-form-item>-->
        <el-form-item>
          <el-button type="primary" @click="handleSearch">查询</el-button>
<!--          <el-button @click="handleReset">重置</el-button>-->
<!--          <el-button type="success" @click="handleExport">导出</el-button>-->
        </el-form-item>
      </el-form>
    </div>
    <!-- ç»Ÿè®¡æ±‡æ€»å¡ç‰‡ -->
    <div class="summary-cards">
      <el-row :gutter="20">
        <el-col :span="6">
          <el-card class="summary-card">
            <div class="summary-item">
              <p class="summary-title">总库存数量</p>
              <p class="summary-value">{{ summaryData.totalInventoryCount }}</p>
            </div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card class="summary-card">
            <div class="summary-item">
              <p class="summary-title">总库存金额</p>
              <p class="summary-value">Â¥{{ summaryData.totalInventoryValue }}</p>
            </div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card class="summary-card">
            <div class="summary-item">
              <p class="summary-title">库存变动数量</p>
              <p class="summary-value">{{ summaryData.inventoryChangeCount }}</p>
            </div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card class="summary-card">
            <div class="summary-item">
              <p class="summary-title">库存变动金额</p>
              <p class="summary-value">Â¥{{ summaryData.inventoryChangeValue }}</p>
            </div>
          </el-card>
        </el-col>
      </el-row>
    </div>
    <!-- å›¾è¡¨åŒºåŸŸ -->
    <div class="chart-section">
      <el-row :gutter="20">
        <el-col :span="12">
          <el-card class="chart-card">
            <template #header>
              <div class="card-header">
                <span>库存分类占比</span>
              </div>
            </template>
            <div id="category-pie-chart" style="height: 400px;"></div>
          </el-card>
        </el-col>
        <el-col :span="12">
          <el-card class="chart-card">
            <template #header>
              <div class="card-header">
                <span>库存金额趋势</span>
              </div>
            </template>
            <div id="amount-trend-chart" style="height: 400px;"></div>
          </el-card>
        </el-col>
      </el-row>
    </div>
    <!-- æ•°æ®è¡¨æ ¼ -->
    <div class="table_list">
      <el-table
        :data="tableData"
        v-loading="loading"
        border
        style="width: 100%"
        :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="供应商名称" prop="supplierName" width="240" show-overflow-tooltip />
        <el-table-column label="产品" prop="productCategory" min-width="100" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" min-width="200" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
        <el-table-column label="入库数量" prop="inboundNum" width="90" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="inboundNum0" width="90" show-overflow-tooltip />
        <el-table-column label="含税单价" prop="taxInclusiveUnitPrice" width="100" show-overflow-tooltip />
        <el-table-column label="含税总价" prop="taxInclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="税率(%)" prop="taxRate" width="80" show-overflow-tooltip />
        <el-table-column label="不含税总价" prop="taxExclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="入库人" prop="createBy" width="100" show-overflow-tooltip />
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper" :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick } from 'vue'
import * as echarts from 'echarts'
import {getStockInChartData, getStockInPage} from "@/api/inventoryManagement/stockIn.js";
// çŠ¶æ€å˜é‡
const loading = ref(false)
const total = ref(0)
const tableData = ref([])
const summaryData = ref({})
const page = reactive({
  current: 1,
  size: 100,
})
// å›¾è¡¨å®žä¾‹
const categoryPieChart = ref(null)
const amountTrendChart = ref(null)
// ç­›é€‰è¡¨å•
const filterForm = reactive({
  dateRange: [],
  supplierName: '',
  productCategory: ''
})
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  loadData()
}
// åˆå§‹åŒ–数据
onMounted(() => {
  loadSummaryData()
  loadData()
})
// åŠ è½½ç»Ÿè®¡æ±‡æ€»æ•°æ®
const loadSummaryData = () => {
  getStockInChartData().then(res => {
    summaryData.value = res.data
  })
}
// åŠ è½½åº“å­˜æ•°æ®
const loadData = () => {
  loading.value = true
  getStockInPage({ ...filterForm, ...page }).then(res => {
    loading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
    console.log('res', res.data.records)
    // æ•°æ®åŠ è½½å®ŒæˆåŽæ¸²æŸ“å›¾è¡¨
    nextTick(() => {
      renderCategoryPieChart()
      renderAmountTrendChart()
    })
  }).catch(() => {
    loading.value = false
  })
}
// æ¸²æŸ“分类占比饼图
const renderCategoryPieChart = () => {
  if (!categoryPieChart.value) {
    categoryPieChart.value = echarts.init(document.getElementById('category-pie-chart'))
  }
  // æ ¹æ® tableData æŒ‰ productCategory åˆ†ç±»å¹¶è®¡ç®— inboundNum0 æ•°é‡æ€»å’Œ
  const categoryMap = tableData.value.reduce((acc, cur) => {
    acc[cur.productCategory] = (acc[cur.productCategory] || 0) + cur.inboundNum0
    return acc
  }, {})
  // å°†åˆ†ç±»ç»“果转换为 ECharts é¥¼å›¾æ‰€éœ€çš„æ•°æ®æ ¼å¼
  const categoryData = Object.entries(categoryMap).map(([name, value]) => ({
    name: name,
    value: value
  }))
  const option = {
    title: {
      text: '库存分类占比',
      left: 'center'
    },
    tooltip: {
      trigger: 'item',
      formatter: '{a} <br/>{b}: {c} ({d}%)'
    },
    legend: {
      orient: 'vertical',
      left: 'left'
    },
    series: [
      {
        name: '库存分类',
        type: 'pie',
        radius: ['40%', '70%'],
        avoidLabelOverlap: false,
        itemStyle: {
          borderRadius: 10,
          borderColor: '#fff',
          borderWidth: 2
        },
        label: {
          show: true,
          formatter: '{b}: {d}%'
        },
        emphasis: {
          label: {
            show: true,
            fontSize: '16',
            fontWeight: 'bold'
          }
        },
        data: categoryData
      }
    ]
  }
  categoryPieChart.value.setOption(option)
}
// æ¸²æŸ“金额趋势折线图
const renderAmountTrendChart = () => {
  if (!amountTrendChart.value) {
    amountTrendChart.value = echarts.init(document.getElementById('amount-trend-chart'))
  }
  // æŒ‰æœˆä»½åˆ†ç»„并计算taxInclusiveTotalPrice总和
  const monthlyAmounts = tableData.value.reduce((acc, cur) => {
    const date = new Date(cur.createTime);
    const month = date.getMonth() + 1;
    // ç¡®ä¿month在1-12范围内
    if (month >= 1 && month <= 12) {
      acc[month] = (acc[month] || 0) + cur.taxInclusiveTotalPrice;
    }
    return acc;
  }, {});
  // ç”Ÿæˆ12个月的数据,缺失月份用0代替
  const amounts = [];
  for (let i = 1; i <= 12; i++) {
    amounts.push(monthlyAmounts[i] || 0);
  }
  const dates = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
  const option = {
    title: {
      text: '库存金额趋势',
      left: 'center'
    },
    tooltip: {
      trigger: 'axis',
      formatter: '{b}: Â¥{c}'
    },
    xAxis: {
      type: 'category',
      data: dates
    },
    yAxis: {
      type: 'value',
      axisLabel: {
        formatter: 'Â¥{value}'
      }
    },
    series: [
      {
        name: '库存金额',
        type: 'line',
        data: amounts,
        smooth: true,
        areaStyle: {}
      }
    ]
  }
  amountTrendChart.value.setOption(option)
}
// æŸ¥è¯¢æ“ä½œ
const handleSearch = () => {
  loadData()
}
// é‡ç½®æ“ä½œ
const handleReset = () => {
  filterForm.dateRange = []
  filterForm.supplierName = ''
  filterForm.productCategory = ''
  loadData()
}
// å¯¼å‡ºæ“ä½œ
const handleExport = () => {
  console.log('导出数据')
}
// çª—口大小改变时,重新调整图表大小
window.addEventListener('resize', () => {
  if (categoryPieChart.value) categoryPieChart.value.resize()
  if (amountTrendChart.value) amountTrendChart.value.resize()
})
</script>
<style scoped>
.inventory-statistics {
  padding: 20px;
}
.filter-form {
  margin-bottom: 20px;
}
.summary-cards {
  margin-bottom: 20px;
}
.summary-card {
  text-align: center;
  height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.summary-item {
  width: 100%;
}
.summary-title {
  font-size: 14px;
  color: #606266;
  margin-bottom: 5px;
}
.summary-value {
  font-size: 24px;
  font-weight: bold;
  color: #303133;
}
.summary-value.warning {
  color: #e6a23c;
}
.summary-value.danger {
  color: #f56c6c;
}
.chart-section {
  margin-bottom: 20px;
}
.chart-card {
  height: 460px;
}
.card-header {
  font-weight: bold;
}
.table_list {
  margin-top: 20px;
}
.pagination {
  text-align: right;
  margin-top: 20px;
}
</style>
src/views/financialManagement/revenueManagement/index.vue
@@ -106,6 +106,7 @@
  listPage,
  {
    incomeMethod: undefined,
    entryDate: undefined,
  },
  [
    {
@@ -226,9 +227,11 @@
const changeDaterange = (value) => {
  if (value) {
    filters.entryDate = value;
    filters.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    filters.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
  } else {
    filters.entryDate = null;
    filters.entryDateStart = undefined;
    filters.entryDateEnd = undefined;
  }
@@ -256,12 +259,6 @@
};
onMounted(() => {
  filters.entryDate = [
    dayjs().format("YYYY-MM-DD"),
    dayjs().add(1, "day").format("YYYY-MM-DD"),
  ]
  filters.entryDateStart = dayjs().format("YYYY-MM-DD")
  filters.entryDateEnd = dayjs().add(1, "day").format("YYYY-MM-DD")
  getTableData();
});
</script>