gaoluyang
昨天 01975cbdf4d8c738d9f92a8a16396d98d25147e8
进销存升级
1.财务报表页面样式修改
已添加10个文件
已修改5个文件
3231 ■■■■■ 文件已修改
src/assets/icons/png/circleBlue@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/icons/png/circleGreen@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/icons/png/circleOrange@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/icons/png/circleRed@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/icons/png/circleYellow@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/icons/png/walletBlue@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/icons/png/walletGreen@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/icons/png/walletOrange@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/icons/png/walletRed@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/icons/png/walletYellow@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index.vue 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/financialStatements/index.vue 747 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/deliveryLedger/index.vue 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/invoiceLedger/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 2132 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/icons/png/circleBlue@2x.png
src/assets/icons/png/circleGreen@2x.png
src/assets/icons/png/circleOrange@2x.png
src/assets/icons/png/circleRed@2x.png
src/assets/icons/png/circleYellow@2x.png
src/assets/icons/png/walletBlue@2x.png
src/assets/icons/png/walletGreen@2x.png
src/assets/icons/png/walletOrange@2x.png
src/assets/icons/png/walletRed@2x.png
src/assets/icons/png/walletYellow@2x.png
src/views/collaborativeApproval/approvalProcess/index.vue
@@ -8,7 +8,7 @@
      <el-tab-pane label="报销管理" name="4"></el-tab-pane>
      <el-tab-pane label="采购审批" name="5"></el-tab-pane>
      <el-tab-pane label="报价审批" name="6"></el-tab-pane>
      <el-tab-pane label="出库审批" name="7"></el-tab-pane>
      <el-tab-pane label="发货审批" name="7"></el-tab-pane>
    </el-tabs>
    
    <div class="search_form">
@@ -35,9 +35,18 @@
        >
      </div>
      <div>
        <el-button type="primary" @click="openForm('add')" v-if="currentApproveType !== 6">新增</el-button>
        <el-button
          type="primary"
          @click="openForm('add')"
          v-if="currentApproveType !== 6 && currentApproveType !== 7"
        >新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
        <el-button
          type="danger"
          plain
          @click="handleDelete"
          v-if="currentApproveType !== 7"
        >删除</el-button>
      </div>
    </div>
    <div class="table_list">
@@ -205,7 +214,12 @@
        clickFun: (row) => {
          openForm("edit", row);
        },
        disabled: (row) => currentApproveType.value === 6 || row.approveStatus == 2 || row.approveStatus == 1 || row.approveStatus == 4
        disabled: (row) =>
          currentApproveType.value === 6 ||
          currentApproveType.value === 7 ||
          row.approveStatus == 2 ||
          row.approveStatus == 1 ||
          row.approveStatus == 4
      },
      {
        name: "审核",
@@ -294,7 +308,7 @@
    4: "报销管理审批表",
    5: "采购申请审批表",
    6: "报价审批表",
    7: "出库审批表",
    7: "发货审批表",
  }
  const fileName = nameMap[type] || nameMap[0]
  proxy.download(url, {}, `${fileName}.xlsx`)
src/views/financialManagement/financialStatements/index.vue
@@ -28,103 +28,135 @@
    
    <main class="container mx-auto px-4 pb-10">
      <!-- 财务指标卡片 -->
      <div class="grid-container">
        <!-- 总收入 -->
        <el-card class="bg1">
          <p>总收入</p>
          <h3>
            ¥{{ pageInfo.totalIncome }}
          </h3>
        </el-card>
        <!-- 收入笔数 -->
        <el-card class="bg2">
          <p>收入笔数</p>
          <h3>
            {{ pageInfo.incomeNumber }}
          </h3>
        </el-card>
      <div class="stats-cards">
        <!-- 总营收 -->
        <div class="stat-card stat-card-blue">
          <div class="stat-icon">
            <img src="@/assets/icons/png/walletBlue@2x.png" alt="总营收" />
          </div>
          <div class="stat-content">
            <div class="stat-label">总营收</div>
            <div class="stat-value">{{ formatMoney(pageInfo.totalIncome || 0) }} 元</div>
          </div>
        </div>
        
        <!-- 总支出 -->
        <el-card class="bg3">
          <p>总支出</p>
          <h3>
            ¥{{ pageInfo.totalExpense }}
          </h3>
        </el-card>
        <div class="stat-card stat-card-orange">
          <div class="stat-icon">
            <img src="@/assets/icons/png/walletOrange@2x.png" alt="总支出" />
          </div>
          <div class="stat-content">
            <div class="stat-label">总支出</div>
            <div class="stat-value">{{ formatMoney(pageInfo.totalExpense || 0) }} 元</div>
          </div>
        </div>
        
        <!-- 支出笔数 -->
        <el-card class="bg4">
          <p>支出笔数</p>
          <h3>
            {{ pageInfo.expenseNumber }}
          </h3>
        </el-card>
        <!-- 总收入笔数 -->
        <div class="stat-card stat-card-green">
          <div class="stat-icon">
            <img src="@/assets/icons/png/walletGreen@2x.png" alt="总收入笔数" />
          </div>
          <div class="stat-content">
            <div class="stat-label">总收入笔数</div>
            <div class="stat-value">{{ pageInfo.incomeNumber || 0 }} 笔</div>
          </div>
        </div>
        <!-- 总支出笔数 -->
        <div class="stat-card stat-card-red">
          <div class="stat-icon">
            <img src="@/assets/icons/png/walletRed@2x.png" alt="总支出笔数" />
          </div>
          <div class="stat-content">
            <div class="stat-label">总支出笔数</div>
            <div class="stat-value">{{ pageInfo.expenseNumber || 0 }} 笔</div>
          </div>
        </div>
        
        <!-- 净收入 -->
        <el-card class="bg5">
          <p>净收入</p>
          <h3>
            ¥{{ pageInfo.netRevenue }}
          </h3>
        <div class="stat-card stat-card-yellow">
          <div class="stat-icon">
            <img src="@/assets/icons/png/walletYellow@2x.png" alt="净收入" />
          </div>
          <div class="stat-content">
            <div class="stat-label">净收入</div>
            <div class="stat-value">{{ formatMoney(pageInfo.netRevenue || 0) }} 元</div>
          </div>
        </div>
      </div>
      <!-- 中间图表区域 -->
      <div class="charts-row">
        <!-- 左侧:收入支出分析 -->
        <el-card class="chart-card">
          <h2 class="section-title">收入支出分析</h2>
          <div class="pie-chart-container">
            <Echarts
              :legend="pieLegendIncomeExpense"
              :chartStyle="chartStylePie"
              :series="pieSeriesIncomeExpense"
              :tooltip="pieTooltipIncomeExpense"
              style="height: 320px; width: 100%;">
            </Echarts>
            <div class="pie-stats">
              <div class="bar-stat-item">
                <span class="bar-stat-label">收入数量</span>
                <span class="bar-stat-value">{{ pageInfo.incomeNumber || 0 }}</span>
              </div>
              <div class="bar-stat-item">
                <span class="bar-stat-label">支出数量</span>
                <span class="bar-stat-value">{{ pageInfo.expenseNumber || 0 }}</span>
              </div>
            </div>
          </div>
        </el-card>
        <!-- 右侧:行项盈利分析 -->
        <el-card class="chart-card">
          <h2 class="section-title">行项盈利分析</h2>
          <div class="bar-chart-header">
            <div class="bar-stat-item">
              <span class="bar-stat-label">当前总个数</span>
              <span class="bar-stat-value">{{ allBarTypes.value?.length || 0 }}</span>
            </div>
            <div class="bar-stat-item">
              <span class="bar-stat-label">支出金额</span>
              <span class="bar-stat-value">{{ formatMoney(pageInfo.totalExpense || 0) }}</span>
            </div>
            <div class="bar-stat-item">
              <span class="bar-stat-label">收入金额</span>
              <span class="bar-stat-value">{{ formatMoney(pageInfo.totalIncome || 0) }}</span>
            </div>
          </div>
          <Echarts
            ref="barChart"
            :chartStyle="chartStyle"
            :grid="barGrid"
            :legend="barLegend"
            :series="barSeries"
            :tooltip="barTooltip"
            :xAxis="barXAxis"
            :yAxis="barYAxis"
            style="height: 300px; width: 100%;">
          </Echarts>
        </el-card>
      </div>
      
      <!-- 收入统计图表 -->
      <div class="grid-layout">
        <el-card style="margin-bottom: 20px;">
          <h2 class="section-title">收入统计(元)</h2>
          <div class="echarts">
            <Echarts :legend="pieLegend0" :chartStyle="chartStylePie"
                                         :series="materialPieSeries0"
                                         :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;">{{ pageInfo.totalIncome }}</span>
                     </div>
                    </Echarts>
            <Echarts ref="chart"
                                 :chartStyle="chartStyle"
                                 :grid="grid"
                                 :legend="lineLegend"
                                 :series="lineSeries0"
                                 :tooltip="tooltip"
                                 :xAxis="xAxis0"
                                 :yAxis="yAxis0"
                                 style="height: 260px;width: 64%;"></Echarts>
          </div>
        </el-card>
        <!-- 支出统计图表 -->
        <el-card>
          <h2 class="section-title">支出统计(元)</h2>
          <div class="echarts">
            <Echarts ref="chart"
                    :legend="pieLegend1"
                    :chartStyle="chartStylePie"
                                         :series="materialPieSeries1"
                                         :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;">{{ pageInfo.totalExpense }}</span>
                     </div></Echarts>
            <Echarts ref="chart"
                                 :chartStyle="chartStyle"
                                 :grid="grid"
                                 :legend="lineLegend"
                                 :series="lineSeries1"
                                 :tooltip="tooltip"
                                 :xAxis="xAxis1"
                                 :yAxis="yAxis1"
                                 style="height: 260px;width: 64%;"></Echarts>
          </div>
        </el-card>
      </div>
      <!-- 底部:营收趋势分析 -->
      <el-card class="trend-chart-card">
        <h2 class="section-title">营收趋势分析</h2>
        <Echarts
          ref="trendChart"
          :chartStyle="chartStyle"
          :grid="grid"
          :legend="trendLegend"
          :series="trendSeries"
          :tooltip="tooltip"
          :xAxis="xAxis0"
          :yAxis="trendYAxis"
          style="height: 350px; width: 100%;">
        </Echarts>
      </el-card>
    </main>
  </div>
</template>
@@ -334,6 +366,262 @@
const pageInfo = ref({
})
// 格式化金额
const formatMoney = (value) => {
  if (!value && value !== 0) return '0';
  return Number(value).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
};
// 收入支出分析饼图
const pieDataIncomeExpense = computed(() => {
  const totalIncome = Number(pageInfo.value.totalIncome) || 0;
  const totalExpense = Number(pageInfo.value.totalExpense) || 0;
  const total = totalIncome + totalExpense;
  if (total === 0) {
    return [
      { name: '收入', value: 0, percent: '0%' },
      { name: '支出', value: 0, percent: '0%' }
    ];
  }
  const incomePercent = ((totalIncome / total) * 100).toFixed(0);
  const expensePercent = ((totalExpense / total) * 100).toFixed(0);
  return [
    { name: '收入', value: totalIncome, percent: `${incomePercent}%` },
    { name: '支出', value: totalExpense, percent: `${expensePercent}%` }
  ];
});
const pieLegendIncomeExpense = computed(() => ({
  show: false
}));
const pieTooltipIncomeExpense = reactive({
  trigger: 'item',
  formatter: function(params) {
    if (!params.data) return params.name;
    return `${params.name}占比 ${params.percent}%`;
  }
});
const pieSeriesIncomeExpense = computed(() => [
  {
    type: 'pie',
    radius: ['0%', '70%'],
    center: ['50%', '50%'],
    avoidLabelOverlap: true,
    itemStyle: {
      borderColor: '#fff',
      borderWidth: 2
    },
    label: {
      show: true,
      position: 'outside',
      formatter: function(params) {
        return `${params.name}占比 ${params.percent}%`;
      },
      fontSize: 14,
      color: '#333'
    },
    labelLine: {
      show: true,
      length: 15,
      length2: 10,
      lineStyle: {
        color: '#333'
      }
    },
    emphasis: {
      label: {
        show: true,
        fontSize: 16,
        fontWeight: 'bold'
      }
    },
    data: pieDataIncomeExpense.value,
    color: ['#1890FF', '#FACC14']
  }
]);
// 行项盈利分析柱状图
const barXAxis = computed(() => {
  return [{
    type: 'category',
    data: (allBarTypes.value && allBarTypes.value.length > 0) ? allBarTypes.value : ['项目1', '项目2', '项目3', '项目4', '项目5', '项目6', '项目7'],
    axisTick: { show: true, alignWithLabel: true },
  }];
});
const barYAxis = [{
  type: 'value',
  name: '单位: 元',
  position: 'left',
  min: 0,
  nameTextStyle: {
    color: '#000',
    fontSize: 14,
  },
}];
const barGrid = {
  left: '3%',
  right: '4%',
  bottom: '3%',
  containLabel: true
};
const barLegend = {
  show: true,
  top: 10,
  right: 10,
};
// 获取所有类型名称
const allBarTypes = computed(() => {
  const incomeTypes = (lineSeries0.value || []).map(item => item.name || item.typeName).filter(Boolean);
  const expenseTypes = (lineSeries1.value || []).map(item => item.name || item.typeName).filter(Boolean);
  return [...new Set([...incomeTypes, ...expenseTypes])];
});
const barSeries = computed(() => {
  if (allBarTypes.value.length === 0) {
    return [
      {
        name: '支出',
        type: 'bar',
        data: [],
        itemStyle: { color: '#1890FF' }
      },
      {
        name: '收入',
        type: 'bar',
        data: [],
        itemStyle: { color: '#13C2C2' }
      }
    ];
  }
  // 计算每个项目的总收入(汇总所有月份)
  const incomeData = allBarTypes.value.map(typeName => {
    const incomeItem = (lineSeries0.value || []).find(item => (item.name || item.typeName) === typeName);
    if (incomeItem && incomeItem.data && Array.isArray(incomeItem.data)) {
      return incomeItem.data.reduce((sum, val) => sum + (Number(val) || 0), 0);
    }
    return 0;
  });
  // 计算每个项目的总支出(汇总所有月份)
  const expenseData = allBarTypes.value.map(typeName => {
    const expenseItem = (lineSeries1.value || []).find(item => (item.name || item.typeName) === typeName);
    if (expenseItem && expenseItem.data && Array.isArray(expenseItem.data)) {
      return expenseItem.data.reduce((sum, val) => sum + (Number(val) || 0), 0);
    }
    return 0;
  });
  return [
    {
      name: '支出',
      type: 'bar',
      data: expenseData,
      itemStyle: { color: '#1890FF' }
    },
    {
      name: '收入',
      type: 'bar',
      data: incomeData,
      itemStyle: { color: '#13C2C2' }
    }
  ];
});
const barTooltip = reactive({
  trigger: 'axis',
  axisPointer: {
    type: 'shadow'
  },
  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>`;
        const value = typeof p.value === 'number' ? p.value.toFixed(2) : p.value;
        return `${colorDot}${p.seriesName} ${value}`;
      })
      .join('<br/>');
    return `<div>${axisLabel}</div><div>${rows}</div>`;
  }
});
// 营收趋势分析
const trendLegend = {
  show: true,
  top: 10,
  right: 10,
};
const trendYAxis = [{
  type: 'value',
  name: '单位: 元',
  position: 'left',
  min: 0,
  nameTextStyle: {
    color: '#000',
    fontSize: 14,
  },
}];
const trendSeries = computed(() => {
  // 汇总所有支出类型的数据
  let expenseTrend = [];
  if (lineSeries1.value.length > 0) {
    const monthCount = Math.max(...lineSeries1.value.map(item => item.data?.length || 0));
    expenseTrend = Array(monthCount).fill(0);
    lineSeries1.value.forEach(item => {
      if (item.data && Array.isArray(item.data)) {
        item.data.forEach((val, index) => {
          if (index < monthCount) {
            expenseTrend[index] += Number(val) || 0;
          }
        });
      }
    });
  }
  // 汇总所有收入类型的数据
  let incomeTrend = [];
  if (lineSeries0.value.length > 0) {
    const monthCount = Math.max(...lineSeries0.value.map(item => item.data?.length || 0));
    incomeTrend = Array(monthCount).fill(0);
    lineSeries0.value.forEach(item => {
      if (item.data && Array.isArray(item.data)) {
        item.data.forEach((val, index) => {
          if (index < monthCount) {
            incomeTrend[index] += Number(val) || 0;
          }
        });
      }
    });
  }
  return [
    {
      name: '支出',
      type: 'line',
      data: expenseTrend,
      itemStyle: { color: '#1890FF' },
      smooth: true
    },
    {
      name: '收入',
      type: 'line',
      data: incomeTrend,
      itemStyle: { color: '#13C2C2' },
      smooth: true
    }
  ];
});
// 获取最近六个月的范围
const getLastSixMonths = () => {
  const endMonth = dayjs().format('YYYY-MM');
@@ -487,111 +775,220 @@
: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 容器基础样式 */
/* 统计卡片样式 */
.stats-cards {
  display: grid;
  gap: 1rem; /* gap-4 对应 1rem (16px) */
  margin-bottom: 2rem; /* mb-8 对应 2rem (32px) */
  grid-template-columns: repeat(5, 1fr);
  gap: 20px;
  margin-bottom: 20px;
}
.stat-card {
  background: #fff;
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  padding: 20px;
  display: flex;
  align-items: center;
  gap: 15px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  transition: all 0.3s;
  
  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;
  &:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    transform: translateY(-2px);
  }
  
}
/* 移动端默认样式 (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));
  .stat-icon {
    width: 48px;
    height: 48px;
    flex-shrink: 0;
    img {
      width: 100%;
      height: 100%;
      object-fit: contain;
    }
  }
  .stat-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .stat-label {
    font-size: 14px;
    color: #666;
    line-height: 1.2;
  }
  .stat-value {
    font-size: 24px;
    font-weight: 600;
    color: #333;
    line-height: 1.2;
  }
  .stat-trend {
    font-size: 12px;
    line-height: 1.2;
    &.trend-up {
      color: #f56c6c;
    }
    &.trend-down {
      color: #67c23a;
    }
  }
}
/* 大屏幕及以上 (lg:grid-cols-5) */
@media (min-width: 1024px) {
  .grid-container {
    grid-template-columns: repeat(5, minmax(0, 1fr));
/* 图表行布局 */
.charts-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-bottom: 20px;
}
.chart-card {
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  :deep(.el-card__body) {
    padding: 20px !important;
  }
}
/* 卡片悬停效果增强 */
.el-card:hover {
  transform: translateY(-2px);
.trend-chart-card {
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  :deep(.el-card__body) {
    padding: 20px !important;
  }
}
.echarts{
/* 饼图容器 */
.pie-chart-container {
  position: relative;
  .pie-stats {
    display: flex;
    justify-content: space-between;
    gap: 20px;
    margin-top: 20px;
    .bar-stat-item {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 8px;
      padding: 15px;
      background: #f5f7fa;
      border-radius: 6px;
      flex: 1;
      .bar-stat-label {
        font-size: 14px;
        color: #666;
      }
      .bar-stat-value {
        font-size: 18px;
        font-weight: 600;
        color: #333;
      }
    }
  }
}
/* 柱状图头部统计 */
.bar-chart-header {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 20px;
  .bar-stat-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 15px;
    background: #f5f7fa;
    border-radius: 6px;
    flex: 1;
    .bar-stat-label {
      font-size: 14px;
      color: #666;
    }
    .bar-stat-value {
      font-size: 18px;
      font-weight: 600;
      color: #333;
    }
  }
}
/* 图表容器样式 */
.el-chart {
  width: 100%;
  height: 100%;
}
/* 标题样式 */
.section-title {
    position: relative;
    font-size: 18px;
    color: #333;
    padding-left: 10px;
    margin-bottom: 10px;
    font-weight: 700;
  position: relative;
  font-size: 18px;
  color: #333;
  padding-left: 12px;
  margin-bottom: 20px;
  font-weight: 700;
  &::before {
    position: absolute;
    left: 0;
    top: 2px;
    content: '';
    width: 4px;
    height: 18px;
    background-color: #002FA7;
    border-radius: 2px;
  }
}
.section-title::before {
    position: absolute;
    left: 0;
    top: 0px;
    content: '';
    width: 4px;
    height: 18px;
    background-color: #002FA7;
    border-radius: 2px;
/* 响应式设计 */
@media (max-width: 1400px) {
  .stats-cards {
    grid-template-columns: repeat(3, 1fr);
  }
}
.chart-num{
  position: absolute;
  z-index: 3;
  top: 92px;
  left: 92px;
  display: flex;
  flex-direction: column;
  justify-content: center;
@media (max-width: 1024px) {
  .stats-cards {
    grid-template-columns: repeat(2, 1fr);
  }
  .charts-row {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 640px) {
  .stats-cards {
    grid-template-columns: 1fr;
  }
}
</style>
src/views/salesManagement/deliveryLedger/index.vue
@@ -3,11 +3,15 @@
    <div class="search_form">
      <el-form :model="searchForm" :inline="true">
        <el-form-item label="销售订单号:">
          <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search"
          <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search" style="width: 200px"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="车牌号:">
          <el-input v-model="searchForm.shippingCarNumber" placeholder="请输入" clearable prefix-icon="Search"
          <el-input v-model="searchForm.shippingCarNumber" placeholder="请输入" clearable prefix-icon="Search" style="width: 200px"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="快递单号:">
          <el-input v-model="searchForm.expressNumber" placeholder="请输入" clearable prefix-icon="Search" style="width: 200px"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item>
@@ -24,13 +28,15 @@
        </div>
      </div>
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :row-key="(row) => row.id" style="width: 100%" height="calc(100vh - 18.5em)">
        :row-key="(row) => row.id" style="width: 100%" height="calc(100vh - 21.5em)">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="销售订单" prop="salesContractNo" show-overflow-tooltip />
        <el-table-column label="客户名称" prop="customerName" show-overflow-tooltip />
        <el-table-column label="发货时间" prop="shippingDate" show-overflow-tooltip />
        <el-table-column label="发货车牌号" prop="shippingCarNumber" show-overflow-tooltip />
        <el-table-column label="快递公司" prop="expressCompany" show-overflow-tooltip />
        <el-table-column label="快递单号" prop="expressNumber" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" width="150" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">编辑</el-button>
@@ -41,61 +47,123 @@
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
    <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增发货台账' : '编辑发货台账'" :width="'50%'"
      :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia">
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增发货台账' : '编辑发货台账'" width="40%"
      @close="closeDia">
      <el-form :model="form" label-width="120px" label-position="top" :rules="rules" ref="formRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="销售订单:" prop="salesContractNo">
              <el-select v-model="form.salesContractNo" placeholder="请选择" clearable filterable
                @change="handleSalesOrderChange" style="width: 100%" :disabled="operationType === 'edit'">
                <el-option v-for="item in salesOrderOptions" :key="item.salesContractNo"
                  :label="item.salesContractNo" :value="item.salesContractNo">
                  {{ item.salesContractNo + ' - ' + item.customerName }}
                </el-option>
            <el-form-item label="发货类型:" prop="type">
              <el-select
                v-model="form.type"
                placeholder="请选择发货类型"
                style="width: 100%"
                @change="handleShippingTypeChange"
              >
                <el-option label="货车" value="货车" />
                <el-option label="快递" value="快递" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="客户名称:" prop="customerName">
              <el-input v-model="form.customerName" placeholder="请输入" clearable :disabled="operationType === 'edit'" />
            <el-form-item label="发货日期:" prop="shippingDate">
              <el-date-picker
                style="width: 100%"
                v-model="form.shippingDate"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                placeholder="请选择发货日期"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="发货时间:" prop="shippingDate">
              <el-date-picker style="width: 100%" v-model="form.shippingDate" value-format="YYYY-MM-DD"
                format="YYYY-MM-DD" type="date" placeholder="请选择" clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
          <el-col :span="24" v-if="form.type === '货车'">
            <el-form-item label="发货车牌号:" prop="shippingCarNumber">
              <el-input v-model="form.shippingCarNumber" placeholder="请输入" clearable />
              <el-input
                v-model="form.shippingCarNumber"
                placeholder="请输入发货车牌号"
                clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="24" v-else>
            <el-form-item label="快递公司:" prop="expressCompany">
              <el-input
                v-model="form.expressCompany"
                placeholder="请输入快递公司"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30" v-if="form.type === '快递'">
          <el-col :span="24">
            <el-form-item label="快递单号:" prop="expressNumber">
              <el-input
                v-model="form.expressNumber"
                placeholder="请输入快递单号"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="发货图片:">
              <el-upload
                v-model:file-list="deliveryFileList"
                :action="upload.url"
                multiple
                ref="deliveryFileUpload"
                auto-upload
                :headers="upload.headers"
                :data="{ type: 9 }"
                :before-upload="handleDeliveryBeforeUpload"
                :on-error="handleDeliveryUploadError"
                :on-success="handleDeliveryUploadSuccess"
                :on-remove="handleDeliveryRemove"
                list-type="picture-card"
                :limit="9"
                accept="image/png,image/jpeg,image/jpg"
              >
                <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
                <template #tip>
                  <div class="el-upload__tip">
                    支持 jpg、jpeg、png 格式,最多上传 9 张,单张大小不超过 10MB
                  </div>
                </template>
              </el-upload>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </FormDialog>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import pagination from "@/components/PIMTable/Pagination.vue";
import FormDialog from '@/components/Dialog/FormDialog.vue';
import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
import { ElMessageBox } from "element-plus";
import { Plus } from "@element-plus/icons-vue";
import { getToken } from "@/utils/auth";
import { getCurrentDate } from "@/utils/index.js";
import {
  deliveryLedgerListPage,
  addOrUpdateDeliveryLedger,
  delDeliveryLedger,
} from "@/api/salesManagement/deliveryLedger.js";
import { delLedgerFile } from "@/api/salesManagement/salesLedger.js";
 
const { proxy } = getCurrentInstance();
@@ -108,6 +176,16 @@
  size: 100,
});
const total = ref(0);
const deliveryFileList = ref([]);
const javaApi = proxy.javaApi;
// 上传配置
const upload = reactive({
  // 上传的地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // 设置上传的请求头部
  headers: { Authorization: "Bearer " + getToken() },
});
// 用户信息表单弹框数据
const operationType = ref("");
@@ -116,19 +194,31 @@
  searchForm: {
    salesContractNo: "", // 销售订单号
    shippingCarNumber: "", // 车牌号
    expressNumber: "", // 快递单号
  },
  form: {
    id: null,
    salesContractNo: "",
    customerName: "",
    type: "货车", // 货车, 快递
    shippingDate: "",
    shippingCarNumber: "",
    expressCompany: "",
    expressNumber: "", // 快递单号
  },
  rules: {
    salesContractNo: [{ required: true, message: "请选择销售订单", trigger: "change" }],
    customerName: [{ required: true, message: "请输入客户名称", trigger: "blur" }],
    type: [
      { required: true, message: "请选择发货类型", trigger: "change" }
    ],
    shippingDate: [{ required: true, message: "请选择发货时间", trigger: "change" }],
    shippingCarNumber: [{ required: true, message: "请输入发货车牌号", trigger: "blur" }],
    shippingCarNumber: [
      { validator: (_, value, callback) => validateShippingCarNumber(value, callback), trigger: "blur" }
    ],
    expressCompany: [
      { validator: (_, value, callback) => validateExpressCompany(value, callback), trigger: "blur" }
    ],
  },
});
const { form, rules } = toRefs(data);
@@ -177,22 +267,85 @@
// 打开弹框
const openForm = async (type, row) => {
  operationType.value = type;
  const baseUrl = import.meta.env.VITE_APP_BASE_API;
  if (type === 'edit' && row) {
    form.value = {
      id: row.id ?? null,
      salesContractNo: row.salesContractNo ?? "",
      customerName: row.customerName ?? "",
      type: row.type || "货车",
      shippingDate: row.shippingDate || getCurrentDate(),
      shippingCarNumber: row.shippingCarNumber ?? "",
      expressCompany: row.expressCompany ?? "",
      expressNumber: row.expressNumber ?? "",
    };
    // 如果有图片,将 commonFileList 转换为文件列表格式
    if (row.commonFileList && Array.isArray(row.commonFileList) && row.commonFileList.length > 0) {
      deliveryFileList.value = row.commonFileList.map((file, index) => {
        // 处理 URL:将 Windows 路径转换为可访问的 URL
        let fileUrl = file.url || '';
        console.log('原始 URL:', fileUrl);
        // 如果 URL 是 Windows 路径格式(包含反斜杠),需要转换
        if (fileUrl && fileUrl.indexOf('\\') > -1) {
          // 查找 uploads 关键字的位置,从那里开始提取相对路径
          const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads');
          if (uploadsIndex > -1) {
            // 从 uploads 开始提取路径,并将反斜杠替换为正斜杠
            const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/');
            fileUrl = '/' + relativePath;
            console.log('转换后的相对路径:', fileUrl);
          } else {
            // 如果没有找到 uploads,提取最后一个目录和文件名
            const parts = fileUrl.split('\\');
            const fileName = parts[parts.length - 1];
            fileUrl = '/uploads/' + fileName;
            console.log('未找到 uploads,使用文件名:', fileUrl);
          }
        }
        // 确保所有非 http 开头的 URL 都拼接 baseUrl
        if (fileUrl && !fileUrl.startsWith('http')) {
          // 确保路径以 / 开头
          if (!fileUrl.startsWith('/')) {
            fileUrl = '/' + fileUrl;
          }
          // 拼接 baseUrl
          fileUrl = javaApi + fileUrl;
          console.log('最终拼接的 URL:', fileUrl);
        }
        return {
          uid: file.id || Date.now() + index,
          name: file.name || `image_${index + 1}.jpg`,
          url: fileUrl,
          status: 'success',
          response: {
            code: 200,
            data: {
              tempId: file.id,
              url: fileUrl
            }
          },
          tempId: file.id // 保存文件ID,用于提交时使用
        };
      });
    } else {
      deliveryFileList.value = [];
    }
  } else {
    form.value = {
      id: null,
      salesContractNo: "",
      customerName: "",
      type: "货车",
      shippingDate: getCurrentDate(),
      shippingCarNumber: "",
      expressCompany: "",
      expressNumber: "",
    };
    deliveryFileList.value = [];
  }
  
  dialogFormVisible.value = true;
@@ -202,10 +355,18 @@
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      let tempFileIds = [];
      if (deliveryFileList.value !== null && deliveryFileList.value.length > 0) {
        tempFileIds = deliveryFileList.value.map((item) => item.tempId);
      }
      const payload = {
        id: form.value.id,
        type: form.value.type,
        shippingDate: form.value.shippingDate,
        shippingCarNumber: form.value.shippingCarNumber,
        shippingCarNumber: form.value.type === "货车" ? form.value.shippingCarNumber : "",
        expressCompany: form.value.type === "快递" ? form.value.expressCompany : "",
        expressNumber: form.value.type === "快递" ? form.value.expressNumber : "",
        tempFileIds: tempFileIds,
      };
      addOrUpdateDeliveryLedger(payload).then((res) => {
        proxy.$modal.msgSuccess("操作成功");
@@ -219,6 +380,7 @@
// 关闭弹框
const closeDia = () => {
  proxy.resetForm("formRef");
  deliveryFileList.value = []; // 清空文件列表
  dialogFormVisible.value = false;
};
@@ -280,6 +442,89 @@
    });
};
// 发货类型校验:货车时要求车牌,快递时要求快递公司
const validateShippingCarNumber = (value, callback) => {
  if (form.value.type === "货车") {
    if (!value) return callback(new Error("请输入发货车牌号"));
  }
  callback();
};
const validateExpressCompany = (value, callback) => {
  if (form.value.type === "快递") {
    if (!value) return callback(new Error("请输入快递公司"));
  }
  callback();
};
// 发货图片上传前校检
function handleDeliveryBeforeUpload(file) {
  // 校检文件类型
  const isImage = file.type === 'image/png' || file.type === 'image/jpeg' || file.type === 'image/jpg';
  if (!isImage) {
    proxy.$modal.msgError("只能上传 jpg、jpeg、png 格式的图片!");
    return false;
  }
  // 校检文件大小
  const isLt10M = file.size / 1024 / 1024 < 10;
  if (!isLt10M) {
    proxy.$modal.msgError("上传图片大小不能超过 10MB!");
    return false;
  }
  proxy.$modal.loading("正在上传图片,请稍候...");
  return true;
}
// 发货图片上传失败
function handleDeliveryUploadError(err) {
  proxy.$modal.msgError("上传图片失败");
  proxy.$modal.closeLoading();
}
// 发货图片上传成功回调
function handleDeliveryUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
  if (res.code === 200) {
    file.tempId = res.data.tempId;
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.deliveryFileUpload.handleRemove(file);
  }
}
// 移除发货图片
function handleDeliveryRemove(file) {
  console.log('file--', file)
  // 如果是编辑模式且文件有 id,需要调用接口删除
  if (operationType.value === "edit") {
    let ids = [];
    ids.push(file.uid);
    delLedgerFile(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
      // 从文件列表中移除
      const index = deliveryFileList.value.findIndex(item => item.uid === file.uid);
      if (index > -1) {
        deliveryFileList.value.splice(index, 1);
      }
    }).catch(() => {
      proxy.$modal.msgError("删除失败");
    });
  } else {
    // 新增模式或没有 id 的文件,直接从列表中移除
    const index = deliveryFileList.value.findIndex(item => item.uid === file.uid);
    if (index > -1) {
      deliveryFileList.value.splice(index, 1);
    }
  }
}
// 发货类型切换时清空对应字段
const handleShippingTypeChange = (val) => {
  if (val === "货车") {
    form.value.expressCompany = "";
    form.value.expressNumber = "";
  } else {
    form.value.shippingCarNumber = "";
  }
};
onMounted(() => {
  getList();
});
@@ -295,5 +540,12 @@
  justify-content: space-between;
  margin-bottom: 10px;
}
// 隐藏图片上传组件的预览按钮(放大镜)
:deep(.el-upload-list--picture-card .el-upload-list__item-actions) {
  .el-upload-list__item-preview {
    display: none;
  }
}
</style>
src/views/salesManagement/invoiceLedger/index.vue
@@ -32,7 +32,6 @@
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="销售合同号" prop="salesContractNo" show-overflow-tooltip width="180" />
        <el-table-column label="客户名称" prop="customerName" show-overflow-tooltip width="240" />
<!--        <el-table-column label="项目" prop="projectName" width="320" />-->
        <el-table-column label="产品大类" prop="productCategory" width="200" />
        <el-table-column label="规格型号" prop="specificationModel" width="160" show-overflow-tooltip />
        <el-table-column label="发票号" prop="invoiceNo" width="200" show-overflow-tooltip />
@@ -42,17 +41,6 @@
        <el-table-column label="录入人" prop="invoicePerson" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="createTime" show-overflow-tooltip :formatter="formatDate" width="180" />
        <el-table-column label="开票日期" prop="invoiceDate" show-overflow-tooltip width="120" />
        <!-- <el-table-column label="发票" prop="invoiceFileName" width="120" align="center" show-overflow-tooltip fixed="right">
          <template #default="scope">
            <el-button v-if="scope.row.invoiceFileName" text bg type="primary"
              @click="handleFile(scope.row.commonFiles)">
              查看附件
            </el-button>
            <el-button v-else link type="primary" @click="handleDownload(scope.row)">
              上传
            </el-button>
          </template>
        </el-table-column> -->
        <el-table-column fixed="right" label="操作" width="150" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm(scope.row)">编辑</el-button>
@@ -439,12 +427,6 @@
}
onMounted(() => {
  // 设置开票日期范围默认值为当天
  const today = dayjs().format('YYYY-MM-DD');
  searchForm.invoiceDate = [today, today];
  // 设置范围日期字段的起始和结束时间
  searchForm.invoiceDateStart = today;
  searchForm.invoiceDateEnd = today;
  getList();
});
</script>
src/views/salesManagement/salesLedger/index.vue
@@ -1,273 +1,318 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm" :inline="true">
        <el-form-item label="客户名称:">
          <el-input v-model="searchForm.customerName" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="客户合同号:">
          <el-input v-model="searchForm.customerContractNo" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="销售合同号:">
          <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="项目名称:">
          <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="录入日期:">
          <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
            placeholder="请选择" clearable @change="changeDaterange" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery"> 搜索 </el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="table_list">
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="openForm('add')">
            新增台账
          </el-button>
          <el-button @click="handleOut">导出</el-button>
          <el-button type="danger" plain @click="handleDelete">删除</el-button>
          <el-button type="primary" plain @click="handlePrint">打印</el-button>
        </div>
      </div>
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column type="expand">
          <template #default="props">
            <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable">
              <el-table-column align="center" label="序号" type="index" width="60" />
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="单位" prop="unit" />
              <el-table-column label="产品状态" width="100px" align="center">
                <template #default="scope">
                  <el-tag v-if="scope.row.approveStatus === 0" type="info">未出库</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 1" type="success">已出库</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 2" type="warning">审核中</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 3" type="success">审核成功</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 4" type="danger">审核失败</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="发货车牌" minWidth="100px" align="center">
                <template #default="scope">
                  <div>
                    <el-tag type="success" v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</el-tag>
                    <el-tag v-else type="info">未发货</el-tag>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="发货日期" minWidth="100px" align="center">
                <template #default="scope">
                  <div>
                    <div v-if="scope.row.shippingDate">{{ scope.row.shippingDate }}</div>
                    <el-tag v-else type="info">未发货</el-tag>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
              <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
              <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
            <!--操作-->
              <el-table-column Width="60px" label="操作" align="center">
                <template #default="scope">
                  <el-button :disabled="scope.row.approveStatus!==2 || scope.row.approveStatus!==5" link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>
                </template>
              </el-table-column>
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户合同号" prop="customerContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户名称" prop="customerName" width="300" show-overflow-tooltip />
        <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip />
        <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip />
        <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip />
        <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip
          :formatter="formattedNumber" />
        <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip />
        <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="100" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">编辑</el-button>
<!--            <el-button link type="primary" size="small" @click="openForm('view', scope.row)">详情</el-button>-->
            <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">附件</el-button>
<!--            <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>-->
          </template>
        </el-table-column>
      </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>
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'" width="70%"
      @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="销售合同号:" prop="salesContractNo">
              <el-input v-model="form.salesContractNo" placeholder="自动生成" clearable disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="业务员:" prop="salesman">
              <el-select v-model="form.salesman" placeholder="请选择" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                  :value="item.nickName" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="客户合同号:" prop="customerContractNo">
              <el-input v-model="form.customerContractNo" placeholder="请输入" clearable :disabled="operationType === 'view'"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户名称:" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
                  {{
                    item.customerName + "——" + item.taxpayerIdentificationNumber
                  }}
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="项目名称:" prop="projectName">
              <el-input v-model="form.projectName" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
    <div class="app-container">
        <div class="search_form">
            <el-form :model="searchForm" :inline="true">
                <el-form-item label="客户名称:">
                    <el-input v-model="searchForm.customerName" placeholder="请输入" clearable prefix-icon="Search"
                                        @change="handleQuery" />
                </el-form-item>
                <el-form-item label="销售合同号:">
                    <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search"
                                        @change="handleQuery" />
                </el-form-item>
                <el-form-item label="录入日期:">
                    <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                                                    placeholder="请选择" clearable @change="changeDaterange" />
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="handleQuery"> 搜索 </el-button>
                </el-form-item>
            </el-form>
        </div>
        <div class="table_list">
            <div class="actions">
                <div></div>
                <div>
                    <el-button type="primary" @click="openForm('add')">
                        新增台账
                    </el-button>
                    <el-button @click="handleImport">导入</el-button>
                    <el-button @click="handleOut">导出</el-button>
                    <el-button type="danger" plain @click="handleDelete">删除</el-button>
                    <el-button type="primary" plain @click="handlePrint">打印</el-button>
                </div>
            </div>
            <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
                                :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%"
                                :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 21em)">
                <el-table-column align="center" type="selection" width="55" />
                <el-table-column type="expand">
                    <template #default="props">
                        <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable">
                            <el-table-column align="center" label="序号" type="index" width="60" />
                            <el-table-column label="产品大类" prop="productCategory" />
                            <el-table-column label="规格型号" prop="specificationModel" />
                            <el-table-column label="单位" prop="unit" />
                            <el-table-column label="数量" prop="quantity" />
                            <el-table-column label="税率(%)" prop="taxRate" />
                            <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
                            <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
                            <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
                            <el-table-column label="产品状态"
                                                             width="100px"
                                                             align="center">
                                <template #default="scope">
                                    <el-tag v-if="scope.row.approveStatus === 1"
                                                    type="success">充足</el-tag>
                                    <el-tag v-else
                                                    type="danger">不足</el-tag>
                                </template>
                            </el-table-column>
                            <el-table-column label="发货状态" prop="shippingStatus" width="140" align="center" show-overflow-tooltip />
                            <el-table-column label="发货日期"
                                                             minWidth="100px"
                                                             align="center">
                                <template #default="scope">
                                    <div>
                                        <div v-if="scope.row.shippingDate">{{ scope.row.shippingDate }}</div>
                                        <el-tag v-else
                                                        type="info">-</el-tag>
                                    </div>
                                </template>
                            </el-table-column>
                            <el-table-column Width="60px"
                                                             label="操作"
                                                             align="center">
                                <template #default="scope">
                                    <el-button
                                                         link
                                                         type="primary"
                                                         size="small"
                                                         @click="openDeliveryForm(scope.row)">发货</el-button>
                                </template>
                            </el-table-column>
                        </el-table>
                    </template>
                </el-table-column>
                <el-table-column align="center" label="序号" type="index" width="60" />
                <el-table-column label="销售合同号" prop="salesContractNo" show-overflow-tooltip />
                <el-table-column label="客户名称" prop="customerName" show-overflow-tooltip />
                <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip />
                <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip
                                                 :formatter="formattedNumber" />
                <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip />
                <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip />
                <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip />
                <el-table-column fixed="right" label="操作" width="120" align="center">
                    <template #default="scope">
                        <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">编辑</el-button>
                        <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">附件</el-button>
                    </template>
                </el-table-column>
            </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>
        <FormDialog
            v-model="dialogFormVisible"
            :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'"
            :width="'70%'"
            :operation-type="operationType"
            @close="closeDia"
            @confirm="submitForm"
            @cancel="closeDia">
            <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
                <el-row v-if="operationType !== 'view'">
                    <el-col :span="24" style="display:flex; justify-content:flex-end; gap:10px; margin-bottom: 6px;">
                        <el-button type="primary" plain @click="openQuotationDialog">从审批通过的报价单导入</el-button>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="销售合同号:" prop="salesContractNo">
                            <el-input v-model="form.salesContractNo" placeholder="自动生成" clearable disabled />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="业务员:" prop="salesman">
                            <el-select v-model="form.salesman"
                                                 filterable
                                                 :reserve-keyword="false" placeholder="请选择" clearable :disabled="operationType === 'view'">
                                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                                                     :value="item.nickName" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="客户名称:" prop="customerId">
                            <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'" filterable>
                                <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
                                    {{
                                        item.customerName + "——" + item.taxpayerIdentificationNumber
                                    }}
                                </el-option>
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="签订日期:" prop="executionDate">
                            <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD"
                                                            format="YYYY-MM-DD" type="date" placeholder="请选择" clearable :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="录入人:" prop="entryPerson">
                            <el-select v-model="form.entryPerson" placeholder="请选择" clearable @change="changs" disabled>
                            <el-select v-model="form.entryPerson"
                                                 filterable
                                                 default-first-option
                                                 :reserve-keyword="false" placeholder="请选择" clearable @change="changs">
                                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
                            </el-select>
                        </el-form-item>
                    </el-col>
          <el-col :span="12">
            <el-form-item label="录入日期:" prop="entryDate">
              <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                type="date" placeholder="请选择" clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-form-item label="产品信息:" prop="entryDate">
            <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">添加</el-button>
            <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >删除</el-button>
          </el-form-item>
        </el-row>
        <el-table :data="productData" border @selection-change="productSelected" show-summary
          :summary-method="summarizeMainTable">
          <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" />
          <el-table-column align="center" label="序号" type="index" width="60" />
          <el-table-column label="产品大类" prop="productCategory" />
          <el-table-column label="规格型号" prop="specificationModel" />
          <el-table-column label="单位" prop="unit" />
          <el-table-column label="数量" prop="quantity" />
          <el-table-column label="税率(%)" prop="taxRate" />
          <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
          <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
          <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
          <el-table-column fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'">
            <template #default="scope">
              <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
            </template>
          </el-table-column>
        </el-table>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="备注·:" prop="remark">
              <el-input v-model="form.remark" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="附件材料:" prop="remark">
              <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                :on-success="handleUploadSuccess" :on-remove="handleRemove">
                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                <template #tip v-if="operationType !== 'view'">
                  <div class="el-upload__tip">
                    文件格式支持
                    doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
                  </div>
                </template>
              </el-upload>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <el-dialog v-model="productFormVisible" :title="productOperationType === 'add' ? '新增产品' : '编辑产品'" width="40%" @close="closeProductDia">
      <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品大类:" prop="productCategory">
              <!-- <el-select v-model="productForm.productCategory" placeholder="请选择" clearable>
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
              </el-select> -->
              <el-tree-select v-model="productForm.productCategory" placeholder="请选择" clearable check-strictly
                @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel">
                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="单位:" prop="unit">
              <el-input v-model="productForm.unit" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="录入日期:" prop="entryDate">
                            <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                                                            type="date" placeholder="请选择" clearable />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-form-item label="产品信息:" prop="entryDate">
                        <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">添加</el-button>
                        <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >删除</el-button>
                    </el-form-item>
                </el-row>
                <el-table :data="productData" border @selection-change="productSelected" show-summary
                                    :summary-method="summarizeMainTable">
                    <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" />
                    <el-table-column align="center" label="序号" type="index" width="60" />
                    <el-table-column label="产品大类" prop="productCategory" />
                    <el-table-column label="规格型号" prop="specificationModel" />
                    <el-table-column label="单位" prop="unit" />
                    <el-table-column label="数量" prop="quantity" />
                    <el-table-column label="税率(%)" prop="taxRate" />
                    <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
                    <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
                    <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
                    <el-table-column fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'">
                        <template #default="scope">
                            <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
                        </template>
                    </el-table-column>
                </el-table>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="备注·:" prop="remark">
                            <el-input v-model="form.remark" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="附件材料:" prop="remark">
                            <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                                                 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                                                 :on-success="handleUploadSuccess" :on-remove="handleRemove">
                                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                                <template #tip v-if="operationType !== 'view'">
                                    <div class="el-upload__tip">
                                        文件格式支持
                                        doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
                                    </div>
                                </template>
                            </el-upload>
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
        </FormDialog>
        <!-- 从报价单导入(仅审批通过) -->
        <el-dialog
            v-model="quotationDialogVisible"
            title="选择审批通过的销售报价单"
            width="80%"
            :close-on-click-modal="false"
        >
            <div style="margin-bottom: 12px; display:flex; gap: 12px; align-items:center;">
                <el-input
                    v-model="quotationSearchForm.quotationNo"
                    placeholder="请输入报价单号"
                    clearable
                    style="max-width: 260px;"
                    @change="fetchQuotationList"
                />
                <el-input
                    v-model="quotationSearchForm.customer"
                    placeholder="请输入客户名称"
                    clearable
                    style="max-width: 260px;"
                    @change="fetchQuotationList"
                />
                <el-button type="primary" @click="fetchQuotationList">搜索</el-button>
                <el-button @click="resetQuotationSearch">重置</el-button>
            </div>
            <el-table
                :data="quotationList"
                border
                stripe
                v-loading="quotationLoading"
                height="420px"
            >
                <el-table-column align="center" label="序号" type="index" width="60" />
                <el-table-column prop="quotationNo" label="报价单号" width="180" show-overflow-tooltip />
                <el-table-column prop="customer" label="客户名称" min-width="220" show-overflow-tooltip />
                <el-table-column prop="salesperson" label="业务员" width="120" show-overflow-tooltip />
                <el-table-column prop="quotationDate" label="报价日期" width="140" />
                <el-table-column prop="status" label="审批状态" width="120" align="center" />
                <el-table-column prop="totalAmount" label="报价金额(元)" width="160" align="right">
                    <template #default="scope">
                        {{ Number(scope.row.totalAmount ?? 0).toFixed(2) }}
                    </template>
                </el-table-column>
                <el-table-column fixed="right" label="操作" width="120" align="center">
                    <template #default="scope">
                        <el-button type="primary" link @click="applyQuotation(scope.row)">选择</el-button>
                    </template>
                </el-table-column>
            </el-table>
            <template #footer>
                <el-button @click="quotationDialogVisible = false">关闭</el-button>
            </template>
        </el-dialog>
        <FormDialog
            v-model="productFormVisible"
            :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
            :width="'40%'"
            :operation-type="productOperationType"
            @close="closeProductDia"
            @confirm="submitProduct"
            @cancel="closeProductDia">
            <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="产品大类:" prop="productCategory">
                            <!-- <el-select v-model="productForm.productCategory" placeholder="请选择" clearable>
                                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
                            </el-select> -->
                            <el-tree-select v-model="productForm.productCategory" placeholder="请选择" clearable check-strictly
                                                            @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="规格型号:" prop="productModelId">
                            <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel" filterable>
                                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="单位:" prop="unit">
                            <el-input v-model="productForm.unit" placeholder="请输入" clearable />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="税率(%):" prop="taxRate">
                            <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate">
@@ -277,15 +322,15 @@
                            </el-select>
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
              <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%"
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
                            <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%"
                                                             :precision="2"
                                                             placeholder="请输入" clearable @change="calculateFromUnitPrice" />
            </el-form-item>
          </el-col>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="数量:" prop="quantity">
                            <el-input-number  :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请输入" clearable
@@ -293,37 +338,31 @@
                                                                @change="calculateFromQuantity" style="width: 100%" />
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
              <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice">
              <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="发票类型:" prop="invoiceType">
              <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable>
                <el-option label="增普票" value="增普票" />
                <el-option label="增专票" value="增专票" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitProduct">确认</el-button>
          <el-button @click="closeProductDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
                            <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice">
                            <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="发票类型:" prop="invoiceType">
                            <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable>
                                <el-option label="增普票" value="增普票" />
                                <el-option label="增专票" value="增专票" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
        </FormDialog>
        <!-- 打印预览弹窗 -->
        <el-dialog
            v-model="printPreviewVisible"
@@ -358,12 +397,15 @@
                                        <span class="value">{{ formatDate(item.createTime) }}</span>
                                    </div>
                                    <div>
                                        <span class="label">客户名称:</span>
                                        <span class="value">{{ item.customerName || '张爱有' }}</span>
                                        <span class="label">发货车牌号:</span>
                                        <span class="value">{{ item.shippingCarNumber }}</span>
                                    </div>
                                </div>
                                <div class="info-row">
                                    <div>
                                        <span class="label">客户名称:</span>
                                        <span class="value">{{ item.customerName || '张爱有' }}</span>
                                    </div>
                                    <span class="label">单号:</span>
                                    <span class="value">{{ item.salesContractNo }}</span>
                                </div>
@@ -442,42 +484,66 @@
        <el-dialog
            v-model="deliveryFormVisible"
            title="发货信息"
            width="40%"
        width="40%"
            @close="closeDeliveryDia"
        >
            <el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef">
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="发货日期:" prop="shippingDate">
                            <el-date-picker
                        <el-form-item label="发货类型:" prop="type">
                            <el-select
                                v-model="deliveryForm.type"
                                placeholder="请选择发货类型"
                                style="width: 100%"
                                v-model="deliveryForm.shippingDate"
                                value-format="YYYY-MM-DD"
                                format="YYYY-MM-DD"
                                type="date"
                                placeholder="请选择发货日期"
                                clearable
                            />
                            >
                                <el-option label="货车" value="货车" />
                                <el-option label="快递" value="快递" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="发货车牌号:" prop="shippingCarNumber">
                            <el-input
                                v-model="deliveryForm.shippingCarNumber"
                                placeholder="请输入发货车牌号"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
        <el-row :gutter="30">
        <!-- 审批人选择(仿协同审批里的审批人节点选择) -->
        <el-row>
          <el-col :span="24">
            <el-form-item label="审批人:" prop="approverId">
              <el-select v-model="deliveryForm.approverId" placeholder="请选择审批人" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
              </el-select>
            <el-form-item>
              <template #label>
                <span>审批人选择:</span>
                <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">新增节点</el-button>
              </template>
              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
                <div
                  v-for="(node, index) in approverNodes"
                  :key="node.id"
                  style="margin-right: 20px; text-align: center; margin-bottom: 10px;"
                >
                  <div>
                    <span>审批人</span>
                    →
                  </div>
                  <el-select
                    v-model="node.userId"
                    placeholder="选择人员"
                    filterable
                    style="width: 140px; margin-bottom: 8px;"
                  >
                    <el-option
                      v-for="user in userList"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                    />
                  </el-select>
                  <div>
                    <el-button
                      type="danger"
                      size="small"
                      @click="removeApproverNode(index)"
                      v-if="approverNodes.length > 1"
                    >删除</el-button>
                  </div>
                </div>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
@@ -489,45 +555,7 @@
                </div>
            </template>
        </el-dialog>
    <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" />
    <!-- 导入对话框 -->
    <el-dialog
      :title="importUpload.title"
      v-model="importUpload.open"
      width="400px"
      append-to-body
    >
      <el-upload
        ref="importUploadRef"
        :limit="1"
        accept=".xlsx, .xls"
        :headers="importUpload.headers"
        :action="importUpload.url"
        :disabled="importUpload.isUploading"
        :before-upload="importUpload.beforeUpload"
        :on-progress="importUpload.onProgress"
        :on-success="importUpload.onSuccess"
        :on-error="importUpload.onError"
        :on-change="importUpload.onChange"
        :auto-upload="false"
        drag
      >
        <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        <template #tip>
          <div class="el-upload__tip text-center">
            <span>仅允许导入xls、xlsx格式文件。</span>
          </div>
        </template>
      </el-upload>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitImportFile" :loading="importUpload.isUploading">确 定</el-button>
          <el-button @click="importUpload.open = false">取 消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
    </div>
</template>
<script setup>
@@ -536,24 +564,27 @@
import {onMounted, ref, getCurrentInstance} from "vue";
import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
import { ElMessageBox, ElMessage } from "element-plus";
import { UploadFilled } from "@element-plus/icons-vue";
import { UploadFilled, Download } from "@element-plus/icons-vue";
import useUserStore from "@/store/modules/user";
import { userListNoPage } from "@/api/system/user.js";
import FileList from "./fileList.vue";
import FileListDialog from '@/components/Dialog/FileListDialog.vue';
import FormDialog from '@/components/Dialog/FormDialog.vue';
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
import {
  ledgerListPage,
  productList,
  customerList,
  addOrUpdateSalesLedger,
  getSalesLedgerWithProducts,
  delLedger,
  addOrUpdateSalesLedgerProduct,
  delProduct,
  delLedgerFile, getProductInventory,
    ledgerListPage,
    productList,
    customerList,
    addOrUpdateSalesLedger,
    getSalesLedgerWithProducts,
    delLedger,
    addOrUpdateSalesLedgerProduct,
    delProduct,
    delLedgerFile, getProductInventory,
} from "@/api/salesManagement/salesLedger.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
@@ -567,8 +598,8 @@
const modelOptions = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
    current: 1,
    size: 100,
});
const total = ref(0);
const fileList = ref([]);
@@ -577,39 +608,30 @@
const operationType = ref("");
const dialogFormVisible = ref(false);
const data = reactive({
  searchForm: {
    customerName: "", // 客户名称
    customerContractNo: "", // 客户合同编号
    salesContractNo: "", // 销售合同编号
    projectName: "", // 项目名称
    entryDate: null, // 录入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
  form: {
    salesContractNo: "",
    salesman: "",
    customerContractNo: "",
    customerId: "",
    projectName: "",
    entryPerson: "",
    entryDate: "",
    maintenanceTime: "",
    productData: [],
    executionDate: "",
    paymentMethod: "",
  },
  rules: {
    salesman: [{ required: true, message: "请选择", trigger: "change" }],
    customerContractNo: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    customerId: [{ required: true, message: "请选择", trigger: "change" }],
    projectName: [{ required: true, message: "请输入", trigger: "blur" }],
    entryPerson: [{ required: true, message: "请选择", trigger: "change" }],
    entryDate: [{ required: true, message: "请选择", trigger: "change" }],
    executionDate: [{ required: true, message: "请选择", trigger: "change" }],
  },
    searchForm: {
        customerName: "", // 客户名称
        salesContractNo: "", // 销售合同编号
        entryDate: null, // 录入日期
        entryDateStart: undefined,
        entryDateEnd: undefined,
    },
    form: {
        salesContractNo: "",
        salesman: "",
        customerId: "",
        entryPerson: "",
        entryDate: "",
        maintenanceTime: "",
        productData: [],
        executionDate: "",
    },
    rules: {
        salesman: [{ required: true, message: "请选择", trigger: "change" }],
        customerId: [{ required: true, message: "请选择", trigger: "change" }],
        entryPerson: [{ required: true, message: "请选择", trigger: "change" }],
        entryDate: [{ required: true, message: "请选择", trigger: "change" }],
        executionDate: [{ required: true, message: "请选择", trigger: "change" }],
    },
});
const { form, rules } = toRefs(data);
const { form: searchForm } = useFormData(data.searchForm);
@@ -618,514 +640,645 @@
const productOperationType = ref("");
const currentId = ref("");
const productFormData = reactive({
  productForm: {
    productCategory: "",
    specificationModel: "",
    unit: "",
    quantity: "",
    taxInclusiveUnitPrice: "",
    taxRate: "",
    taxInclusiveTotalPrice: "",
    taxExclusiveTotalPrice: "",
    invoiceType: "",
  },
  productRules: {
    productCategory: [{ required: true, message: "请选择", trigger: "change" }],
    productForm: {
        productCategory: "",
        specificationModel: "",
        unit: "",
        quantity: "",
        taxInclusiveUnitPrice: "",
        taxRate: "",
        taxInclusiveTotalPrice: "",
        taxExclusiveTotalPrice: "",
        invoiceType: "",
    },
    productRules: {
        productCategory: [{ required: true, message: "请选择", trigger: "change" }],
        productModelId: [{ required: true, message: "请选择", trigger: "change" }],
    specificationModel: [
      { required: true, message: "请选择", trigger: "change" },
    ],
    unit: [{ required: true, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    taxInclusiveUnitPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    taxRate: [{ required: true, message: "请选择", trigger: "change" }],
    taxInclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    taxExclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
  },
        specificationModel: [
            { required: true, message: "请选择", trigger: "change" },
        ],
        unit: [{ required: true, message: "请输入", trigger: "blur" }],
        quantity: [{ required: true, message: "请输入", trigger: "blur" }],
        taxInclusiveUnitPrice: [
            { required: true, message: "请输入", trigger: "blur" },
        ],
        taxRate: [{ required: true, message: "请选择", trigger: "change" }],
        taxInclusiveTotalPrice: [
            { required: true, message: "请输入", trigger: "blur" },
        ],
        taxExclusiveTotalPrice: [
            { required: true, message: "请输入", trigger: "blur" },
        ],
        invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
    },
});
const { productForm, productRules } = toRefs(productFormData);
// 防止循环计算的标志
const isCalculating = ref(false);
const upload = reactive({
  // 上传的地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // 设置上传的请求头部
  headers: { Authorization: "Bearer " + getToken() },
    // 上传的地址
    url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
    // 设置上传的请求头部
    headers: { Authorization: "Bearer " + getToken() },
});
// 打印相关
const printPreviewVisible = ref(false);
const printData = ref([]);
// 报价单导入相关
const quotationDialogVisible = ref(false);
const quotationLoading = ref(false);
const quotationList = ref([]);
const quotationSearchForm = reactive({
    quotationNo: "",
    customer: "",
});
const selectedQuotation = ref(null);
// 发货相关
const deliveryFormVisible = ref(false);
const currentDeliveryRow = ref(null);
const deliveryFormData = reactive({
  deliveryForm: {
    shippingDate: "",
    shippingCarNumber: "",
    type: "货车", // 货车, 快递
  },
  deliveryRules: {
    shippingDate: [
      { required: true, message: "请选择发货日期", trigger: "change" }
    ],
    shippingCarNumber: [
      { required: true, message: "请输入发货车牌号", trigger: "blur" }
    ],
    approverId:[
      {
        required: true,message: "",
      }
    type: [
      { required: true, message: "请选择发货类型", trigger: "change" }
    ]
  },
});
const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
// 发货审批人节点(仿协同审批 infoFormDia.vue)
const approverNodes = ref([{ id: 1, userId: null }]);
let nextApproverId = 2;
const addApproverNode = () => {
  approverNodes.value.push({ id: nextApproverId++, userId: null });
};
const removeApproverNode = (index) => {
  approverNodes.value.splice(index, 1);
};
// 导入相关
const importUploadRef = ref(null);
const importUpload = reactive({
  title: "导入销售台账",
  open: false,
  url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
  headers: { Authorization: "Bearer " + getToken() },
  isUploading: false,
  beforeUpload: (file) => {
    const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
    const isLt10M = file.size / 1024 / 1024 < 10;
    if (!isExcel) {
      proxy.$modal.msgError("上传文件只能是 xlsx/xls 格式!");
      return false;
    }
    if (!isLt10M) {
      proxy.$modal.msgError("上传文件大小不能超过 10MB!");
      return false;
    }
    return true;
  },
  onChange: (file, fileList) => {
    console.log('文件状态改变', file, fileList);
  },
  onProgress: (event, file, fileList) => {
    console.log('上传中...', event.percent);
  },
  onSuccess: (response, file, fileList) => {
    console.log('上传成功', response, file, fileList);
    importUpload.isUploading = false;
    if (response.code === 200) {
      proxy.$modal.msgSuccess("导入成功");
      importUpload.open = false;
      if (importUploadRef.value) {
        importUploadRef.value.clearFiles();
      }
      getList();
    } else {
      proxy.$modal.msgError(response.msg || "导入失败");
    }
  },
  onError: (error, file, fileList) => {
    console.error('上传失败', error, file, fileList);
    importUpload.isUploading = false;
    proxy.$modal.msgError("导入失败,请重试");
  },
    title: "导入销售台账",
    open: false,
    url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
    headers: { Authorization: "Bearer " + getToken() },
    isUploading: false,
    beforeUpload: (file) => {
        const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
        const isLt10M = file.size / 1024 / 1024 < 10;
        if (!isExcel) {
            proxy.$modal.msgError("上传文件只能是 xlsx/xls 格式!");
            return false;
        }
        if (!isLt10M) {
            proxy.$modal.msgError("上传文件大小不能超过 10MB!");
            return false;
        }
        return true;
    },
    onChange: (file, fileList) => {
        console.log('文件状态改变', file, fileList);
    },
    onProgress: (event, file, fileList) => {
        console.log('上传中...', event.percent);
    },
    onSuccess: (response, file, fileList) => {
        console.log('上传成功', response, file, fileList);
        importUpload.isUploading = false;
        if (response.code === 200) {
            proxy.$modal.msgSuccess("导入成功");
            importUpload.open = false;
            if (importUploadRef.value) {
                importUploadRef.value.clearFiles();
            }
            getList();
        } else {
            proxy.$modal.msgError(response.msg || "导入失败");
        }
    },
    onError: (error, file, fileList) => {
        console.error('上传失败', error, file, fileList);
        importUpload.isUploading = false;
        proxy.$modal.msgError("导入失败,请重试");
    },
});
const changeDaterange = (value) => {
  if (value) {
    searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
  } else {
    searchForm.entryDateStart = undefined;
    searchForm.entryDateEnd = undefined;
  }
  handleQuery();
    if (value) {
        searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
        searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
    } else {
        searchForm.entryDateStart = undefined;
        searchForm.entryDateEnd = undefined;
    }
    handleQuery();
};
// 查询列表
/** 搜索按钮操作 */
const handleQuery = () => {
  page.current = 1;
    // 只有在点击搜索按钮时才重置页码到第一页
    // 避免表单字段change事件干扰分页
    if (arguments.length === 0) {
        page.current = 1;
    }
    expandedRowKeys.value = [];
  getList();
    getList();
};
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const getList =async () => {
  let userLists = await userListNoPage();
  userList.value = userLists.data;
  tableLoading.value = true;
  const { entryDate, ...rest } = searchForm;
  ledgerListPage({ ...rest, ...page })
    .then((res) => {
      tableLoading.value = false;
      tableData.value = res.records;
      tableData.value.map((item) => {
        item.children = [];
      });
      total.value = res.total;
    })
    .catch(() => {
      tableLoading.value = false;
    });
const getList = () => {
    tableLoading.value = true;
    const { entryDate, ...rest } = searchForm;
    // 将范围日期字段传递给后端
    const params = { ...rest, ...page };
    // 移除录入日期的默认值设置,只保留范围日期字段
    delete params.entryDate;
    ledgerListPage(params)
        .then((res) => {
            tableLoading.value = false;
            tableData.value = res.records;
            tableData.value.map((item) => {
                item.children = [];
            });
            total.value = res.total;
        })
        .catch(() => {
            tableLoading.value = false;
        });
};
// 获取产品大类tree数据
const getProductOptions = () => {
  productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
  });
    // 返回 Promise,便于在编辑产品时等待加载完成
    return productTreeList().then((res) => {
        productOptions.value = convertIdToValue(res);
        return productOptions.value;
    });
};
const formattedNumber = (row, column, cellValue) => {
  return parseFloat(cellValue).toFixed(2);
    return parseFloat(cellValue).toFixed(2);
};
// 获取tree子数据
const getModels = (value) => {
  productForm.value.productCategory = findNodeById(productOptions.value, value);
  modelList({ id: value }).then((res) => {
    modelOptions.value = res;
  });
    productForm.value.productCategory = findNodeById(productOptions.value, value);
    modelList({ id: value }).then((res) => {
        modelOptions.value = res;
    });
};
const getProductModel = (value) => {
  const index = modelOptions.value.findIndex((item) => item.id === value);
  if (index !== -1) {
    productForm.value.specificationModel = modelOptions.value[index].model;
    productForm.value.unit = modelOptions.value[index].unit;
  } else {
    productForm.value.specificationModel = null;
    productForm.value.unit = null;
  }
    const index = modelOptions.value.findIndex((item) => item.id === value);
    if (index !== -1) {
        productForm.value.specificationModel = modelOptions.value[index].model;
        productForm.value.unit = modelOptions.value[index].unit;
    } else {
        productForm.value.specificationModel = null;
        productForm.value.unit = null;
    }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // 找到节点,返回该节点
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode; // 在子节点中找到,返回该节点
      }
    }
  }
  return null; // 没有找到节点,返回null
    for (let i = 0; i < nodes.length; i++) {
        if (nodes[i].value === productId) {
            return nodes[i].label; // 找到节点,返回该节点
        }
        if (nodes[i].children && nodes[i].children.length > 0) {
            const foundNode = findNodeById(nodes[i].children, productId);
            if (foundNode) {
                return foundNode; // 在子节点中找到,返回该节点
            }
        }
    }
    return null; // 没有找到节点,返回null
};
function convertIdToValue(data) {
  if (!data || !Array.isArray(data)) return [];
  return data.map((item) => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
      value: id, // 将 id 改为 value
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
    return data.map((item) => {
        const { id, children, ...rest } = item;
        const newItem = {
            ...rest,
            value: id, // 将 id 改为 value
        };
        if (children && children.length > 0) {
            newItem.children = convertIdToValue(children);
        }
        return newItem;
    });
}
// 根据名称反查产品大类 id,便于仅存名称时的反显
function findNodeIdByLabel(nodes, label) {
    if (!label) return null;
    for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i];
        if (node.label === label) return node.value;
        if (node.children && node.children.length > 0) {
            const found = findNodeIdByLabel(node.children, label);
            if (found !== null && found !== undefined) return found;
        }
    }
    return null;
}
// 表格选择数据
const handleSelectionChange = (selection) => {
  // 过滤掉子数据
  selectedRows.value = selection.filter((item) => item.children !== undefined);
  console.log("selection", selectedRows.value);
    // 过滤掉子数据
    selectedRows.value = selection.filter((item) => item.children !== undefined);
    console.log("selection", selectedRows.value);
};
const productSelected = (selectedRows) => {
  productSelectedRows.value = selectedRows;
    productSelectedRows.value = selectedRows;
};
const expandedRowKeys = ref([]);
// 展开行(始终只展开一行)
const expandChange = (row) => {
  const rowKey = row.id;
  const isExpanded = expandedRowKeys.value.includes(rowKey);
  if (isExpanded) {
    // 当前行已展开 -> 收起
    expandedRowKeys.value = [];
    return;
  }
  // 展开当前行前,先收起其它行
  expandedRowKeys.value = [];
  try {
    productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
      const index = tableData.value.findIndex((item) => item.id === row.id);
      if (index > -1) {
        tableData.value[index].children = res.data;
      }
      // 只保留当前这一行处于展开状态
      expandedRowKeys.value = [rowKey];
    });
  } catch (error) {
    console.log(error);
  }
// 展开行
const expandChange = (row, expandedRows) => {
    if (expandedRows.length > 0) {
        expandedRowKeys.value = [];
        try {
            productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
                const index = tableData.value.findIndex((item) => item.id === row.id);
                if (index > -1) {
                    tableData.value[index].children = res.data;
                }
                expandedRowKeys.value.push(row.id);
            });
        } catch (error) {
            console.log(error);
        }
    } else {
        expandedRowKeys.value = [];
    }
};
// 主表合计方法
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(param, [
    "contractAmount",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
  ]);
    return proxy.summarizeTable(param, [
        "contractAmount",
        "taxInclusiveTotalPrice",
        "taxExclusiveTotalPrice",
    ]);
};
// 子表合计方法
const summarizeChildrenTable = (param) => {
  return proxy.summarizeTable(param, [
    "taxInclusiveUnitPrice",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
  ]);
    return proxy.summarizeTable(param, [
        "taxInclusiveUnitPrice",
        "taxInclusiveTotalPrice",
        "taxExclusiveTotalPrice",
    ]);
};
// 打开弹框
const openForm = async (type, row) => {
  operationType.value = type;
  form.value = {};
  productData.value = [];
  customerList().then((res) => {
    customerOption.value = res;
  });
  form.value.entryPerson = userStore.id;
  if (type !== "add") {
    currentId.value = row.id;
    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
      form.value = { ...res };
      form.value.entryPerson = Number(res.entryPerson);
      productData.value = form.value.productData;
      fileList.value = form.value.salesLedgerFiles;
    });
  }
  // let userAll = await userStore.getInfo()
  // userList.value.forEach(element => {
  //   if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
  //     form.value.entryPerson = userAll.user.userId // 设置默认业务员为当前用户
  //   }
  // });
  form.value.entryDate = getCurrentDate(); // 设置默认录入日期为当前日期
  dialogFormVisible.value = true;
    operationType.value = type;
    form.value = {};
    productData.value = [];
    selectedQuotation.value = null;
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    customerList().then((res) => {
        customerOption.value = res;
    });
    form.value.entryPerson = userStore.id;
    if (type === "add") {
        // 新增时设置录入日期为当天
        form.value.entryDate = getCurrentDate();
        // 签订日期默认为当天
        form.value.executionDate = getCurrentDate();
    } else {
        currentId.value = row.id;
        getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
            form.value = { ...res };
            form.value.entryPerson = Number(res.entryPerson);
            productData.value = form.value.productData;
            fileList.value = form.value.salesLedgerFiles;
        });
    }
    // let userAll = await userStore.getInfo()
    // userList.value.forEach(element => {
    //   if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
    //     form.value.entryPerson = userAll.user.userId // 设置默认业务员为当前用户
    //   }
    // });
    form.value.entryDate = getCurrentDate(); // 设置默认录入日期为当前日期
    dialogFormVisible.value = true;
};
// 打开报价单选择弹窗(仅审批通过)
const openQuotationDialog = async () => {
    if (operationType.value === "view") return;
    quotationDialogVisible.value = true;
    // 先确保客户列表已加载,便于后续回填 customerId
    if (!customerOption.value || customerOption.value.length === 0) {
        try {
            const res = await customerList();
            customerOption.value = res;
        } catch (e) {
            // ignore,允许用户后续手动选择客户
        }
    }
    await fetchQuotationList();
};
const fetchQuotationList = async () => {
    quotationLoading.value = true;
    try {
        const params = {
            // 兼容后端分页字段:这里沿用报价页面已有可用的字段命名
            currentPage: 1,
            pageSize: 100,
            ...quotationSearchForm,
            status: "通过",
        };
        const res = await getQuotationList(params);
        quotationList.value = res?.data?.records || [];
    } finally {
        quotationLoading.value = false;
    }
};
const resetQuotationSearch = async () => {
    quotationSearchForm.quotationNo = "";
    quotationSearchForm.customer = "";
    await fetchQuotationList();
};
// 选中报价单后回填到台账表单
const applyQuotation = (row) => {
    if (!row) return;
    selectedQuotation.value = row;
    // 业务员
    form.value.salesman = row.salesperson || "";
    // 客户名称 -> customerId
    const customer = (customerOption.value || []).find((c) => c.customerName === row.customer);
    if (customer?.id) {
        form.value.customerId = customer.id;
    } else {
        // 如果找不到,保留原值(允许用户手动选择/不打断已有输入)
        form.value.customerId = form.value.customerId || "";
    }
    // 产品信息映射:报价 products -> 台账 productData
    const products = Array.isArray(row.products) ? row.products : [];
    productData.value = products.map((p) => {
        const quantity = Number(p.quantity ?? 0) || 0;
        const unitPrice = Number(p.unitPrice ?? 0) || 0;
        const taxRate = "13"; // 默认 13%,便于直接提交(如需可在产品中自行修改)
        const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
        const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(taxInclusiveTotalPrice, taxRate);
        return {
            // 台账字段
            productCategory: p.product || p.productName || "",
            specificationModel: p.specification || "",
            unit: p.unit || "",
            quantity: quantity,
            taxRate: taxRate,
            taxInclusiveUnitPrice: unitPrice.toFixed(2),
            taxInclusiveTotalPrice: taxInclusiveTotalPrice,
            taxExclusiveTotalPrice: taxExclusiveTotalPrice,
            invoiceType: "增普票",
        };
    });
    quotationDialogVisible.value = false;
};
function changs(val) {
  console.log(val);
    console.log(val);
}
// 上传前校检
function handleBeforeUpload(file) {
  // 校检文件大小
  // if (file.size > 1024 * 1024 * 10) {
  //   proxy.$modal.msgError("上传文件大小不能超过10MB!");
  //   return false;
  // }
  proxy.$modal.loading("正在上传文件,请稍候...");
  return true;
    // 校检文件大小
    // if (file.size > 1024 * 1024 * 10) {
    //   proxy.$modal.msgError("上传文件大小不能超过10MB!");
    //   return false;
    // }
    proxy.$modal.loading("正在上传文件,请稍候...");
    return true;
}
// 上传失败
function handleUploadError(err) {
  proxy.$modal.msgError("上传文件失败");
  proxy.$modal.closeLoading();
    proxy.$modal.msgError("上传文件失败");
    proxy.$modal.closeLoading();
}
// 上传成功回调
function handleUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
  if (res.code === 200) {
    file.tempId = res.data.tempId;
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.fileUpload.handleRemove(file);
  }
    proxy.$modal.closeLoading();
    if (res.code === 200) {
        file.tempId = res.data.tempId;
        proxy.$modal.msgSuccess("上传成功");
    } else {
        proxy.$modal.msgError(res.msg);
        proxy.$refs.fileUpload.handleRemove(file);
    }
}
// 移除文件
function handleRemove(file) {
  if (operationType.value === "edit") {
    let ids = [];
    ids.push(file.id);
    delLedgerFile(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
    });
  }
    if (operationType.value === "edit") {
        let ids = [];
        ids.push(file.id);
        delLedgerFile(ids).then((res) => {
            proxy.$modal.msgSuccess("删除成功");
        });
    }
}
// 提交表单
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
    proxy.$refs["formRef"].validate((valid) => {
        if (valid) {
            console.log('productData.value--', productData.value)
      if (productData.value !== null && productData.value.length > 0) {
        form.value.productData = proxy.HaveJson(productData.value);
      } else {
        proxy.$modal.msgWarning("请添加产品信息");
        return;
      }
      let tempFileIds = [];
      if (fileList.value !== null && fileList.value.length > 0) {
        tempFileIds = fileList.value.map((item) => item.tempId);
      }
      form.value.tempFileIds = tempFileIds;
      form.value.type = 1;
      addOrUpdateSalesLedger(form.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
        getList();
      });
    }
  });
            if (productData.value !== null && productData.value.length > 0) {
                form.value.productData = proxy.HaveJson(productData.value);
            } else {
                proxy.$modal.msgWarning("请添加产品信息");
                return;
            }
            let tempFileIds = [];
            if (fileList.value !== null && fileList.value.length > 0) {
                tempFileIds = fileList.value.map((item) => item.tempId);
            }
            form.value.tempFileIds = tempFileIds;
            form.value.type = 1;
            addOrUpdateSalesLedger(form.value).then((res) => {
                proxy.$modal.msgSuccess("提交成功");
                closeDia();
                getList();
            });
        }
    });
};
// 关闭弹框
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
};
const productIndex = ref(0);
// 打开产品弹框
const openProductForm =async (type, row,index) => {
  productOperationType.value = type;
  productForm.value = {};
  proxy.resetForm("productFormRef");
  // 新增、编辑都需先加载产品树,否则 el-tree-select 无数据
  try {
    await getProductOptions();
  } catch (e) {
    console.error("加载产品树失败", e);
  }
  if (type === "edit") {
    productForm.value = { ...row };
    productIndex.value = index;
  }
  productFormVisible.value = true;
  getProductOptions();
const openProductForm = async (type, row, index) => {
    productOperationType.value = type;
    productForm.value = {};
    proxy.resetForm("productFormRef");
    if (type === "edit") {
        productForm.value = { ...row };
        productIndex.value = index;
        // 编辑时根据产品大类名称反查 tree 节点 id,并加载规格型号列表
        try {
            const options = productOptions.value && productOptions.value.length > 0
                ? productOptions.value
                : await getProductOptions();
            const categoryId = findNodeIdByLabel(options, productForm.value.productCategory);
            if (categoryId) {
                const models = await modelList({ id: categoryId });
                modelOptions.value = models || [];
                // 根据当前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
                const currentModel = (modelOptions.value || []).find(
                    (m) => m.model === productForm.value.specificationModel
                );
                if (currentModel) {
                    productForm.value.productModelId = currentModel.id;
                }
            }
        } catch (e) {
            // 加载失败时保持可编辑,不中断弹窗
            console.error("加载产品规格型号失败", e);
        }
    }
    productFormVisible.value = true;
};
// 提交产品表单
const submitProduct = () => {
  proxy.$refs["productFormRef"].validate((valid) => {
    if (valid) {
      if (operationType.value === "edit") {
        submitProductEdit();
      } else {
        if(productOperationType.value === "add"){
          productData.value.push({ ...productForm.value });
        }else{
          productData.value[productIndex.value] = { ...productForm.value }
        }
        closeProductDia();
      }
    }
  });
    proxy.$refs["productFormRef"].validate((valid) => {
        if (valid) {
            if (operationType.value === "edit") {
                submitProductEdit();
            } else {
                if(productOperationType.value === "add"){
                    productData.value.push({ ...productForm.value });
                }else{
                    productData.value[productIndex.value] = { ...productForm.value }
                }
                closeProductDia();
            }
        }
    });
};
const submitProductEdit = () => {
  productForm.value.salesLedgerId = currentId.value;
  productForm.value.type = 1
  addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
    proxy.$modal.msgSuccess("提交成功");
    closeProductDia();
    getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => {
      productData.value = res.productData;
    });
  });
    productForm.value.salesLedgerId = currentId.value;
    productForm.value.type = 1
    addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeProductDia();
        getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => {
            productData.value = res.productData;
        });
    });
};
// 删除产品
const deleteProduct = () => {
  if (productSelectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  if (operationType.value === "add") {
    productSelectedRows.value.forEach((selectedRow) => {
      const index = productData.value.findIndex(
        (product) => product.id === selectedRow.id
      );
      if (index !== -1) {
        productData.value.splice(index, 1);
      }
    });
  } else {
    let ids = [];
    if (productSelectedRows.value.length > 0) {
      ids = productSelectedRows.value.map((item) => item.id);
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        delProduct(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          closeProductDia();
          getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
            (res) => {
              productData.value = res.productData;
            }
          );
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  }
    if (productSelectedRows.value.length === 0) {
        proxy.$modal.msgWarning("请选择数据");
        return;
    }
    if (operationType.value === "add") {
        productSelectedRows.value.forEach((selectedRow) => {
            const index = productData.value.findIndex(
                (product) => product.id === selectedRow.id
            );
            if (index !== -1) {
                productData.value.splice(index, 1);
            }
        });
    } else {
        let ids = [];
        if (productSelectedRows.value.length > 0) {
            ids = productSelectedRows.value.map((item) => item.id);
        }
        ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
            confirmButtonText: "确认",
            cancelButtonText: "取消",
            type: "warning",
        })
            .then(() => {
                delProduct(ids).then((res) => {
                    proxy.$modal.msgSuccess("删除成功");
                    closeProductDia();
                    getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
                        (res) => {
                            productData.value = res.productData;
                        }
                    );
                });
            })
            .catch(() => {
                proxy.$modal.msg("已取消");
            });
    }
};
// 关闭产品弹框
const closeProductDia = () => {
  proxy.resetForm("productFormRef");
  productFormVisible.value = false;
    proxy.resetForm("productFormRef");
    productFormVisible.value = false;
};
// 导入
const handleImport = () => {
  importUpload.title = "导入销售台账";
  importUpload.open = true;
  if (importUploadRef.value) {
    importUploadRef.value.clearFiles();
  }
    importUpload.title = "导入销售台账";
    importUpload.open = true;
    if (importUploadRef.value) {
        importUploadRef.value.clearFiles();
    }
};
// 下载导入模板
const downloadTemplate = () => {
    proxy.download("/sales/ledger/exportTemplate", {}, "销售台账导入模板.xlsx");
};
// 提交导入文件
const submitImportFile = () => {
  importUpload.isUploading = true;
  proxy.$refs["importUploadRef"].submit();
    importUpload.isUploading = true;
    proxy.$refs["importUploadRef"].submit();
};
// 导出
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/sales/ledger/export", {}, "销售台账.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            proxy.download("/sales/ledger/export", {}, "销售台账.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
// 删除
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      delLedger(ids).then((res) => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
    let ids = [];
    if (selectedRows.value.length > 0) {
        ids = selectedRows.value.map((item) => item.id);
    } else {
        proxy.$modal.msgWarning("请选择数据");
        return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            delLedger(ids).then((res) => {
                proxy.$modal.msgSuccess("删除成功");
                getList();
            });
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
// 打印功能
@@ -1359,8 +1512,8 @@
                </tr>
              </thead>
              <tbody>
                ${item.products && item.products.length > 0 ?
                  item.products.map(product => `
                ${item.products && item.products.length > 0 ?
            item.products.map(product => `
                    <tr>
                      <td>${product.productCategory || ''}</td>
                      <td>${product.specificationModel || ''}</td>
@@ -1369,9 +1522,9 @@
                      <td>${product.quantity || '0'}</td>
                      <td>${product.taxInclusiveTotalPrice || '0'}</td>
                    </tr>
                  `).join('') :
                  '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>'
                }
                  `).join('') :
            '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>'
        }
              </tbody>
              <tfoot>
                <tr>
@@ -1454,100 +1607,91 @@
    const seconds = String(date.getSeconds()).padStart(2, "0");
    return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
};
// 获取当前日期并格式化为 YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // 月份从0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
// 计算产品总数量
const getTotalQuantity = (products) => {
  if (!products || products.length === 0) return '0';
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.quantity) || 0);
  }, 0);
  return total.toFixed(2);
    if (!products || products.length === 0) return '0';
    const total = products.reduce((sum, product) => {
        return sum + (parseFloat(product.quantity) || 0);
    }, 0);
    return total.toFixed(2);
};
// 计算产品总金额
const getTotalAmount = (products) => {
  if (!products || products.length === 0) return '0';
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
  }, 0);
  return total.toFixed(2);
    if (!products || products.length === 0) return '0';
    const total = products.reduce((sum, product) => {
        return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
    }, 0);
    return total.toFixed(2);
};
// 用于打印的计算函数
const getTotalQuantityForPrint = (products) => {
  if (!products || products.length === 0) return '0';
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.quantity) || 0);
  }, 0);
  return total.toFixed(2);
    if (!products || products.length === 0) return '0';
    const total = products.reduce((sum, product) => {
        return sum + (parseFloat(product.quantity) || 0);
    }, 0);
    return total.toFixed(2);
};
const getTotalAmountForPrint = (products) => {
  if (!products || products.length === 0) return '0';
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
  }, 0);
  return total.toFixed(2);
    if (!products || products.length === 0) return '0';
    const total = products.reduce((sum, product) => {
        return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
    }, 0);
    return total.toFixed(2);
};
const mathNum = () => {
  console.log("productForm.value", productForm.value);
  if (!productForm.value.taxInclusiveUnitPrice) {
    return;
  }
  if (!productForm.value.quantity) {
    return;
  }
  // 含税总价计算
  productForm.value.taxInclusiveTotalPrice =
    proxy.calculateTaxIncludeTotalPrice(
      productForm.value.taxInclusiveUnitPrice,
      productForm.value.quantity
    );
  if (productForm.value.taxRate) {
    // 不含税总价计算
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
        productForm.value.taxInclusiveTotalPrice,
        productForm.value.taxRate
      );
  }
    console.log("productForm.value", productForm.value);
    if (!productForm.value.taxInclusiveUnitPrice) {
        return;
    }
    if (!productForm.value.quantity) {
        return;
    }
    // 含税总价计算
    productForm.value.taxInclusiveTotalPrice =
        proxy.calculateTaxIncludeTotalPrice(
            productForm.value.taxInclusiveUnitPrice,
            productForm.value.quantity
        );
    if (productForm.value.taxRate) {
        // 不含税总价计算
        productForm.value.taxExclusiveTotalPrice =
            proxy.calculateTaxExclusiveTotalPrice(
                productForm.value.taxInclusiveTotalPrice,
                productForm.value.taxRate
            );
    }
};
// 根据含税总价计算含税单价和数量
const calculateFromTotalPrice = () => {
  if (isCalculating.value) return;
  const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
  const quantity = parseFloat(productForm.value.quantity);
  if (!totalPrice || !quantity || quantity <= 0) {
    return;
  }
  isCalculating.value = true;
  // 计算含税单价 = 含税总价 / 数量
  productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
  // 如果有税率,计算不含税总价
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
        totalPrice,
        productForm.value.taxRate
      );
  }
  isCalculating.value = false;
    if (isCalculating.value) return;
    const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
    const quantity = parseFloat(productForm.value.quantity);
    if (!totalPrice || !quantity || quantity <= 0) {
        return;
    }
    isCalculating.value = true;
    // 计算含税单价 = 含税总价 / 数量
    productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
    // 如果有税率,计算不含税总价
    if (productForm.value.taxRate) {
        productForm.value.taxExclusiveTotalPrice =
            proxy.calculateTaxExclusiveTotalPrice(
                totalPrice,
                productForm.value.taxRate
            );
    }
    isCalculating.value = false;
};
// 根据不含税总价计算含税单价和数量
@@ -1556,27 +1700,27 @@
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice);
  const quantity = parseFloat(productForm.value.quantity);
  const taxRate = parseFloat(productForm.value.taxRate);
  if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
    return;
  }
  isCalculating.value = true;
  // 先计算含税总价 = 不含税总价 / (1 - 税率/100)
  const taxRateDecimal = taxRate / 100;
  const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
  productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
  // 计算含税单价 = 含税总价 / 数量
  productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2);
  isCalculating.value = false;
    if (isCalculating.value) return;
    const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice);
    const quantity = parseFloat(productForm.value.quantity);
    const taxRate = parseFloat(productForm.value.taxRate);
    if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
        return;
    }
    isCalculating.value = true;
    // 先计算含税总价 = 不含税总价 / (1 - 税率/100)
    const taxRateDecimal = taxRate / 100;
    const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
    productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
    // 计算含税单价 = 含税总价 / 数量
    productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2);
    isCalculating.value = false;
};
// 根据数量变化计算总价
@@ -1585,30 +1729,30 @@
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  const quantity = parseFloat(productForm.value.quantity);
  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
  if (!quantity || quantity <= 0 || !unitPrice) {
    return;
  }
  isCalculating.value = true;
  // 计算含税总价
  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
  // 如果有税率,计算不含税总价
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
        productForm.value.taxInclusiveTotalPrice,
        productForm.value.taxRate
      );
  }
  isCalculating.value = false;
    if (isCalculating.value) return;
    const quantity = parseFloat(productForm.value.quantity);
    const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
    if (!quantity || quantity <= 0 || !unitPrice) {
        return;
    }
    isCalculating.value = true;
    // 计算含税总价
    productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    // 如果有税率,计算不含税总价
    if (productForm.value.taxRate) {
        productForm.value.taxExclusiveTotalPrice =
            proxy.calculateTaxExclusiveTotalPrice(
                productForm.value.taxInclusiveTotalPrice,
                productForm.value.taxRate
            );
    }
    isCalculating.value = false;
};
// 根据含税单价变化计算总价
@@ -1617,30 +1761,30 @@
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  const quantity = parseFloat(productForm.value.quantity);
  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
  if (!quantity || quantity <= 0 || !unitPrice) {
    return;
  }
  isCalculating.value = true;
  // 计算含税总价
  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
  // 如果有税率,计算不含税总价
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
        productForm.value.taxInclusiveTotalPrice,
        productForm.value.taxRate
      );
  }
  isCalculating.value = false;
    if (isCalculating.value) return;
    const quantity = parseFloat(productForm.value.quantity);
    const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
    if (!quantity || quantity <= 0 || !unitPrice) {
        return;
    }
    isCalculating.value = true;
    // 计算含税总价
    productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    // 如果有税率,计算不含税总价
    if (productForm.value.taxRate) {
        productForm.value.taxExclusiveTotalPrice =
            proxy.calculateTaxExclusiveTotalPrice(
                productForm.value.taxInclusiveTotalPrice,
                productForm.value.taxRate
            );
    }
    isCalculating.value = false;
};
// 根据税率变化计算不含税总价
@@ -1649,25 +1793,25 @@
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
  const taxRate = parseFloat(productForm.value.taxRate);
  if (!inclusiveTotalPrice || !taxRate) {
    return;
  }
  isCalculating.value = true;
  // 计算不含税总价
  productForm.value.taxExclusiveTotalPrice =
    proxy.calculateTaxExclusiveTotalPrice(
      inclusiveTotalPrice,
      taxRate
    );
  isCalculating.value = false;
    if (isCalculating.value) return;
    const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
    const taxRate = parseFloat(productForm.value.taxRate);
    if (!inclusiveTotalPrice || !taxRate) {
        return;
    }
    isCalculating.value = true;
    // 计算不含税总价
    productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
            inclusiveTotalPrice,
            taxRate
        );
    isCalculating.value = false;
};
/**
 * 下载文件
@@ -1675,42 +1819,50 @@
 * @param row 下载文件的相关信息对象
 */
const fileListRef = ref(null)
const fileListDialogVisible = ref(false)
const downLoadFile = (row) => {
  getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
    fileListRef.value.open(res.salesLedgerFiles)
  });
    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
        if (fileListRef.value) {
            fileListRef.value.open(res.salesLedgerFiles)
            fileListDialogVisible.value = true
        }
    });
}
// 打开发货弹框
const openDeliveryForm = (row) => {
  currentDeliveryRow.value = row;
    currentDeliveryRow.value = row;
  deliveryForm.value = {
    shippingDate: "", // 移除默认值设置
    shippingCarNumber: "",
    type: "货车",
  };
  deliveryFormVisible.value = true;
  // 重置审批人节点(默认一个空节点)
  approverNodes.value = [{ id: 1, userId: null }];
  nextApproverId = 2;
    deliveryFormVisible.value = true;
};
// 提交发货表单
const submitDelivery = () => {
  proxy.$refs["deliveryFormRef"].validate((valid) => {
    if (valid) {
      // 审批人必填校验(所有节点都要选人)
      const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
      if (hasEmptyApprover) {
        proxy.$modal.msgError("请为所有审批节点选择审批人!");
        return;
      }
      const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
      addShippingInfo({
        approverId:deliveryForm.value.approverId,
        salesLedgerId: currentDeliveryRow.value.salesLedgerId,
        salesLedgerProductId: currentDeliveryRow.value.id,
        shippingDate: deliveryForm.value.shippingDate,
        shippingCarNumber: deliveryForm.value.shippingCarNumber,
        type: deliveryForm.value.type,
                approveUserIds,
      })
        .then(() => {
          proxy.$modal.msgSuccess("发货成功");
          closeDeliveryDia();
          getList();
          expandedRowKeys.value = [];
        })
        .catch(() => {
          proxy.$modal.msgError("发货失败,请重试");
        });
    }
  });
};
@@ -1721,25 +1873,33 @@
  deliveryFormVisible.value = false;
  currentDeliveryRow.value = null;
};
const currentFactoryName = ref("");
const getCurrentFactoryName = async () => {
    let res = await userStore.getInfo();
    currentFactoryName.value = res.user.currentFactoryName;
};
onMounted(() => {
    getList();
    userListNoPage().then(res => {
        userList.value = res.data;
    })
    getCurrentFactoryName();
});
</script>
<style scoped lang="scss">
.ml-10 {
  margin-left: 10px;
    margin-left: 10px;
}
.table_list {
  margin-top: unset;
    margin-top: unset;
}
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
    display: flex;
    justify-content: space-between;
    margin-bottom: 10px;
}
.print-preview-dialog {
    .el-dialog__body {