gaoluyang
2026-04-20 fca0461c6014fc7dd1b9848017fd3bbae3f8d7de
进销存升级
1.财务模块前端页面(routerjs文件后面要回退)
已添加17个文件
已修改1个文件
5897 ■■■■■ 文件已修改
src/router/index.js 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/assets/fixedAssets.vue 461 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/assets/intangibleAssets.vue 457 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/generalLedger/index.vue 290 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/input-invoice.vue 404 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/payment.vue 376 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/paymentApply.vue 359 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/purchaseIn.vue 331 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/reconciliation.vue 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/invoiceApply.vue 358 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/outputInvoice.vue 368 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/receipt.vue 355 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/reconciliation.vue 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/salesOut.vue 271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/salesReturn.vue 305 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/voucher/detailLedger.vue 289 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/voucher/generalLedger.vue 230 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/voucher/index.vue 418 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js
@@ -119,6 +119,119 @@
      },
    ],
  },
  // è´¢åŠ¡ç®¡ç†æ¨¡å—è·¯ç”±
  {
    path: "/financial",
    component: Layout,
    hidden: false,
    redirect: "/financial/general-ledger",
    alwaysShow: true,
    meta: { title: "财务管理", icon: "money" },
    children: [
      {
        path: "general-ledger",
        component: () => import("@/views/financialManagement/generalLedger/index.vue"),
        name: "GeneralLedger",
        meta: { title: "总帐科目" }
      },
      {
        path: "sales-out",
        component: () => import("@/views/financialManagement/receivable/salesOut.vue"),
        name: "SalesOut",
        meta: { title: "销售出库" }
      },
      {
        path: "sales-return",
        component: () => import("@/views/financialManagement/receivable/salesReturn.vue"),
        name: "SalesReturn",
        meta: { title: "销售退货" }
      },
      {
        path: "receivable-reconciliation",
        component: () => import("@/views/financialManagement/receivable/reconciliation.vue"),
        name: "ReceivableReconciliation",
        meta: { title: "应收对账" }
      },
      {
        path: "invoice-apply",
        component: () => import("@/views/financialManagement/receivable/invoiceApply.vue"),
        name: "InvoiceApply",
        meta: { title: "开票申请" }
      },
      {
        path: "output-invoice",
        component: () => import("@/views/financialManagement/receivable/outputInvoice.vue"),
        name: "OutputInvoice",
        meta: { title: "销项发票" }
      },
      {
        path: "receipt",
        component: () => import("@/views/financialManagement/receivable/receipt.vue"),
        name: "Receipt",
        meta: { title: "收款单" }
      },
      {
        path: "purchase-in",
        component: () => import("@/views/financialManagement/payable/purchaseIn.vue"),
        name: "PurchaseIn",
        meta: { title: "采购入库" }
      },
      {
        path: "payable-reconciliation",
        component: () => import("@/views/financialManagement/payable/reconciliation.vue"),
        name: "PayableReconciliation",
        meta: { title: "应付对账" }
      },
      {
        path: "input-invoice",
        component: () => import("@/views/financialManagement/payable/input-invoice.vue"),
        name: "InputInvoice",
        meta: { title: "进项发票" }
      },
      {
        path: "payment-apply",
        component: () => import("@/views/financialManagement/payable/paymentApply.vue"),
        name: "PaymentApply",
        meta: { title: "付款申请" }
      },
      {
        path: "payment",
        component: () => import("@/views/financialManagement/payable/payment.vue"),
        name: "Payment",
        meta: { title: "付款单" }
      },
      {
        path: "fixed-assets",
        component: () => import("@/views/financialManagement/assets/fixedAssets.vue"),
        name: "FixedAssets",
        meta: { title: "固定资产" }
      },
      {
        path: "intangible-assets",
        component: () => import("@/views/financialManagement/assets/intangibleAssets.vue"),
        name: "IntangibleAssets",
        meta: { title: "无形资产" }
      },
      {
        path: "voucher",
        component: () => import("@/views/financialManagement/voucher/index.vue"),
        name: "Voucher",
        meta: { title: "凭证" }
      },
      {
        path: "voucher-general-ledger",
        component: () => import("@/views/financialManagement/voucher/generalLedger.vue"),
        name: "VoucherGeneralLedger",
        meta: { title: "科目总帐" }
      },
      {
        path: "voucher-detail-ledger",
        component: () => import("@/views/financialManagement/voucher/detailLedger.vue"),
        name: "VoucherDetailLedger",
        meta: { title: "科目明细帐" }
      }
    ]
  }
];
// åŠ¨æ€è·¯ç”±ï¼ŒåŸºäºŽç”¨æˆ·æƒé™åŠ¨æ€åŽ»åŠ è½½
src/views/financialManagement/assets/fixedAssets.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,461 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="资产编号:">
        <el-input v-model="filters.assetCode" placeholder="请输入资产编号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="资产名称:">
        <el-input v-model="filters.assetName" placeholder="请输入资产名称" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="资产类别:">
        <el-select v-model="filters.category" placeholder="请选择类别" clearable style="width: 150px;">
          <el-option label="房屋建筑" value="building" />
          <el-option label="机器设备" value="machine" />
          <el-option label="运输工具" value="vehicle" />
          <el-option label="电子设备" value="electronic" />
          <el-option label="办公家具" value="furniture" />
        </el-select>
      </el-form-item>
      <el-form-item label="状态:">
        <el-select v-model="filters.status" placeholder="请选择状态" clearable style="width: 150px;">
          <el-option label="在用" value="in_use" />
          <el-option label="闲置" value="idle" />
          <el-option label="报废" value="scrapped" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div>
          <el-statistic title="资产原值合计" :value="totalOriginalValue" precision="2" prefix="Â¥" />
          <el-statistic title="累计折旧合计" :value="totalDepreciation" precision="2" prefix="Â¥" style="margin-left: 30px;" />
          <el-statistic title="净值合计" :value="totalNetValue" precision="2" prefix="Â¥" style="margin-left: 30px;" />
        </div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增资产</el-button>
          <el-button type="warning" @click="handleDepreciation" icon="Money">折旧计提</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #originalValue="{ row }">
          <span class="text-primary">Â¥{{ formatMoney(row.originalValue) }}</span>
        </template>
        <template #accumulatedDepreciation="{ row }">
          <span class="text-warning">Â¥{{ formatMoney(row.accumulatedDepreciation) }}</span>
        </template>
        <template #netValue="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.netValue) }}</span>
        </template>
        <template #category="{ row }">
          <el-tag>{{ getCategoryLabel(row.category) }}</el-tag>
        </template>
        <template #status="{ row }">
          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)">编辑</el-button>
          <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="资产编号" prop="assetCode">
              <el-input v-model="form.assetCode" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="资产名称" prop="assetName">
              <el-input v-model="form.assetName" placeholder="请输入资产名称" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="资产类别" prop="category">
              <el-select v-model="form.category" placeholder="请选择资产类别" style="width: 100%;">
                <el-option label="房屋建筑" value="building" />
                <el-option label="机器设备" value="machine" />
                <el-option label="运输工具" value="vehicle" />
                <el-option label="电子设备" value="electronic" />
                <el-option label="办公家具" value="furniture" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="规格型号" prop="specification">
              <el-input v-model="form.specification" placeholder="请输入规格型号" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="购置日期" prop="purchaseDate">
              <el-date-picker v-model="form.purchaseDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="资产原值" prop="originalValue">
              <el-input-number v-model="form.originalValue" :min="0" :precision="2" style="width: 100%;" @change="calculateNetValue" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="使用年限" prop="usefulLife">
              <el-input-number v-model="form.usefulLife" :min="1" :max="50" style="width: 100%;" />
              <span style="margin-left: 10px;">å¹´</span>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="残值率" prop="residualRate">
              <el-input-number v-model="form.residualRate" :min="0" :max="10" :precision="2" style="width: 100%;" />
              <span style="margin-left: 10px;">%</span>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="累计折旧">
              <el-input v-model="form.accumulatedDepreciation" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="资产净值">
              <el-input v-model="form.netValue" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="存放地点" prop="location">
              <el-input v-model="form.location" placeholder="请输入存放地点" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="使用部门" prop="department">
              <el-input v-model="form.department" placeholder="请输入使用部门" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="保管人" prop="keeper">
              <el-input v-model="form.keeper" placeholder="请输入保管人" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="状态" prop="status">
              <el-select v-model="form.status" placeholder="请选择状态" style="width: 100%;">
                <el-option label="在用" value="in_use" />
                <el-option label="闲置" value="idle" />
                <el-option label="报废" value="scrapped" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "固定资产",
});
const filters = reactive({
  assetCode: "",
  assetName: "",
  category: "",
  status: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "资产编号", prop: "assetCode", width: "130" },
  { label: "资产名称", prop: "assetName", width: "150" },
  { label: "资产类别", prop: "category", slot: "category" },
  { label: "规格型号", prop: "specification", width: "120" },
  { label: "资产原值", prop: "originalValue", slot: "originalValue" },
  { label: "累计折旧", prop: "accumulatedDepreciation", slot: "accumulatedDepreciation" },
  { label: "资产净值", prop: "netValue", slot: "netValue" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "操作", prop: "operation", slot: "operation", width: "180", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const form = reactive({
  assetCode: "",
  assetName: "",
  category: "",
  specification: "",
  purchaseDate: "",
  originalValue: 0,
  usefulLife: 5,
  residualRate: 5,
  accumulatedDepreciation: 0,
  netValue: 0,
  location: "",
  department: "",
  keeper: "",
  status: "in_use",
  remark: "",
});
const rules = {
  assetName: [{ required: true, message: "请输入资产名称", trigger: "blur" }],
  category: [{ required: true, message: "请选择资产类别", trigger: "change" }],
  purchaseDate: [{ required: true, message: "请选择购置日期", trigger: "change" }],
  originalValue: [{ required: true, message: "请输入资产原值", trigger: "blur" }],
  usefulLife: [{ required: true, message: "请输入使用年限", trigger: "blur" }],
};
const mockData = [
  { id: 1, assetCode: "GD2024001", assetName: "办公电脑", category: "electronic", specification: "联想ThinkPad X1", purchaseDate: "2023-01-15", originalValue: 8000, usefulLife: 5, residualRate: 5, accumulatedDepreciation: 1520, netValue: 6480, location: "办公室", department: "财务部", keeper: "张三", status: "in_use", remark: "" },
  { id: 2, assetCode: "GD2024002", assetName: "打印机", category: "electronic", specification: "惠普M479fdw", purchaseDate: "2023-03-20", originalValue: 3500, usefulLife: 5, residualRate: 5, accumulatedDepreciation: 532, netValue: 2968, location: "文印室", department: "行政部", keeper: "李四", status: "in_use", remark: "" },
  { id: 3, assetCode: "GD2024003", assetName: "办公桌椅", category: "furniture", specification: "实木办公桌", purchaseDate: "2023-06-10", originalValue: 2500, usefulLife: 10, residualRate: 5, accumulatedDepreciation: 118.75, netValue: 2381.25, location: "办公室", department: "销售部", keeper: "王五", status: "in_use", remark: "" },
  { id: 4, assetCode: "GD2024004", assetName: "商务车", category: "vehicle", specification: "别克GL8", purchaseDate: "2022-08-01", originalValue: 280000, usefulLife: 10, residualRate: 5, accumulatedDepreciation: 53200, netValue: 226800, location: "停车场", department: "行政部", keeper: "赵六", status: "in_use", remark: "" },
];
const totalOriginalValue = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.originalValue), 0);
});
const totalDepreciation = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.accumulatedDepreciation), 0);
});
const totalNetValue = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.netValue), 0);
});
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getCategoryLabel = (category) => {
  const map = {
    building: "房屋建筑",
    machine: "机器设备",
    vehicle: "运输工具",
    electronic: "电子设备",
    furniture: "办公家具",
  };
  return map[category] || category;
};
const getStatusLabel = (status) => {
  const map = { in_use: "在用", idle: "闲置", scrapped: "报废" };
  return map[status] || status;
};
const getStatusType = (status) => {
  const map = { in_use: "success", idle: "warning", scrapped: "info" };
  return map[status] || "";
};
const calculateNetValue = () => {
  form.netValue = Number((form.originalValue - form.accumulatedDepreciation).toFixed(2));
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.assetCode) {
    result = result.filter(item => item.assetCode.includes(filters.assetCode));
  }
  if (filters.assetName) {
    result = result.filter(item => item.assetName.includes(filters.assetName));
  }
  if (filters.category) {
    result = result.filter(item => item.category === filters.category);
  }
  if (filters.status) {
    result = result.filter(item => item.status === filters.status);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.assetCode = "";
  filters.assetName = "";
  filters.category = "";
  filters.status = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增固定资产";
  Object.assign(form, {
    assetCode: "GD" + Date.now().toString().slice(-8),
    assetName: "",
    category: "",
    specification: "",
    purchaseDate: new Date().toISOString().split('T')[0],
    originalValue: 0,
    usefulLife: 5,
    residualRate: 5,
    accumulatedDepreciation: 0,
    netValue: 0,
    location: "",
    department: "",
    keeper: "",
    status: "in_use",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑固定资产";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看资产: ${row.assetName}`);
};
const handleDelete = (row) => {
  ElMessageBox.confirm("确认删除该固定资产吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData.splice(index, 1);
    }
    ElMessage.success("删除成功");
    getTableData();
  });
};
const handleDepreciation = () => {
  ElMessageBox.confirm("确认进行本月折旧计提吗?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "info",
  }).then(() => {
    mockData.forEach(item => {
      if (item.status === "in_use") {
        const monthlyDepreciation = (item.originalValue * (1 - item.residualRate / 100)) / (item.usefulLife * 12);
        item.accumulatedDepreciation = Number((item.accumulatedDepreciation + monthlyDepreciation).toFixed(2));
        item.netValue = Number((item.originalValue - item.accumulatedDepreciation).toFixed(2));
      }
    });
    ElMessage.success("折旧计提完成");
    getTableData();
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      calculateNetValue();
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
  > div:first-child {
    display: flex;
    align-items: center;
  }
}
.text-primary {
  color: #409eff;
  font-weight: bold;
}
.text-warning {
  color: #e6a23c;
  font-weight: bold;
}
.text-success {
  color: #67c23a;
  font-weight: bold;
}
</style>
src/views/financialManagement/assets/intangibleAssets.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,457 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="资产编号:">
        <el-input v-model="filters.assetCode" placeholder="请输入资产编号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="资产名称:">
        <el-input v-model="filters.assetName" placeholder="请输入资产名称" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="资产类别:">
        <el-select v-model="filters.category" placeholder="请选择类别" clearable style="width: 150px;">
          <el-option label="专利权" value="patent" />
          <el-option label="商标权" value="trademark" />
          <el-option label="著作权" value="copyright" />
          <el-option label="软件" value="software" />
          <el-option label="土地使用权" value="land" />
          <el-option label="其他" value="other" />
        </el-select>
      </el-form-item>
      <el-form-item label="状态:">
        <el-select v-model="filters.status" placeholder="请选择状态" clearable style="width: 150px;">
          <el-option label="在用" value="in_use" />
          <el-option label="闲置" value="idle" />
          <el-option label="已摊销完毕" value="amortized" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div>
          <el-statistic title="资产原值合计" :value="totalOriginalValue" precision="2" prefix="Â¥" />
          <el-statistic title="累计摊销合计" :value="totalAmortization" precision="2" prefix="Â¥" style="margin-left: 30px;" />
          <el-statistic title="净值合计" :value="totalNetValue" precision="2" prefix="Â¥" style="margin-left: 30px;" />
        </div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增资产</el-button>
          <el-button type="warning" @click="handleAmortization" icon="Money">摊销计提</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #originalValue="{ row }">
          <span class="text-primary">Â¥{{ formatMoney(row.originalValue) }}</span>
        </template>
        <template #accumulatedAmortization="{ row }">
          <span class="text-warning">Â¥{{ formatMoney(row.accumulatedAmortization) }}</span>
        </template>
        <template #netValue="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.netValue) }}</span>
        </template>
        <template #category="{ row }">
          <el-tag>{{ getCategoryLabel(row.category) }}</el-tag>
        </template>
        <template #status="{ row }">
          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)">编辑</el-button>
          <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="资产编号" prop="assetCode">
              <el-input v-model="form.assetCode" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="资产名称" prop="assetName">
              <el-input v-model="form.assetName" placeholder="请输入资产名称" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="资产类别" prop="category">
              <el-select v-model="form.category" placeholder="请选择资产类别" style="width: 100%;">
                <el-option label="专利权" value="patent" />
                <el-option label="商标权" value="trademark" />
                <el-option label="著作权" value="copyright" />
                <el-option label="软件" value="software" />
                <el-option label="土地使用权" value="land" />
                <el-option label="其他" value="other" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="证书编号" prop="certificateNo">
              <el-input v-model="form.certificateNo" placeholder="请输入证书编号" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="取得日期" prop="acquisitionDate">
              <el-date-picker v-model="form.acquisitionDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="资产原值" prop="originalValue">
              <el-input-number v-model="form.originalValue" :min="0" :precision="2" style="width: 100%;" @change="calculateNetValue" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="摊销年限" prop="amortizationPeriod">
              <el-input-number v-model="form.amortizationPeriod" :min="1" :max="50" style="width: 100%;" />
              <span style="margin-left: 10px;">å¹´</span>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="残值率" prop="residualRate">
              <el-input-number v-model="form.residualRate" :min="0" :max="10" :precision="2" style="width: 100%;" />
              <span style="margin-left: 10px;">%</span>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="累计摊销">
              <el-input v-model="form.accumulatedAmortization" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="资产净值">
              <el-input v-model="form.netValue" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="有效期至" prop="validityDate">
              <el-date-picker v-model="form.validityDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="状态" prop="status">
              <el-select v-model="form.status" placeholder="请选择状态" style="width: 100%;">
                <el-option label="在用" value="in_use" />
                <el-option label="闲置" value="idle" />
                <el-option label="已摊销完毕" value="amortized" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="资产描述" prop="description">
          <el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入资产描述" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "无形资产",
});
const filters = reactive({
  assetCode: "",
  assetName: "",
  category: "",
  status: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "资产编号", prop: "assetCode", width: "130" },
  { label: "资产名称", prop: "assetName", width: "150" },
  { label: "资产类别", prop: "category", slot: "category" },
  { label: "证书编号", prop: "certificateNo", width: "150" },
  { label: "资产原值", prop: "originalValue", slot: "originalValue" },
  { label: "累计摊销", prop: "accumulatedAmortization", slot: "accumulatedAmortization" },
  { label: "资产净值", prop: "netValue", slot: "netValue" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "操作", prop: "operation", slot: "operation", width: "180", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const form = reactive({
  assetCode: "",
  assetName: "",
  category: "",
  certificateNo: "",
  acquisitionDate: "",
  originalValue: 0,
  amortizationPeriod: 10,
  residualRate: 0,
  accumulatedAmortization: 0,
  netValue: 0,
  validityDate: "",
  status: "in_use",
  description: "",
  remark: "",
});
const rules = {
  assetName: [{ required: true, message: "请输入资产名称", trigger: "blur" }],
  category: [{ required: true, message: "请选择资产类别", trigger: "change" }],
  acquisitionDate: [{ required: true, message: "请选择取得日期", trigger: "change" }],
  originalValue: [{ required: true, message: "请输入资产原值", trigger: "blur" }],
  amortizationPeriod: [{ required: true, message: "请输入摊销年限", trigger: "blur" }],
};
const mockData = [
  { id: 1, assetCode: "WX2024001", assetName: "ERP软件许可", category: "software", certificateNo: "SW-2023-001", acquisitionDate: "2023-01-01", originalValue: 50000, amortizationPeriod: 10, residualRate: 0, accumulatedAmortization: 5000, netValue: 45000, validityDate: "2033-01-01", status: "in_use", description: "企业资源计划管理系统", remark: "" },
  { id: 2, assetCode: "WX2024002", assetName: "发明专利", category: "patent", certificateNo: "ZL202210123456.7", acquisitionDate: "2022-06-15", originalValue: 100000, amortizationPeriod: 20, residualRate: 0, accumulatedAmortization: 3750, netValue: 96250, validityDate: "2042-06-15", status: "in_use", description: "一种新型生产工艺", remark: "" },
  { id: 3, assetCode: "WX2024003", assetName: "商标权", category: "trademark", certificateNo: "TM-2023-008", acquisitionDate: "2023-03-10", originalValue: 20000, amortizationPeriod: 10, residualRate: 0, accumulatedAmortization: 1500, netValue: 18500, validityDate: "2033-03-10", status: "in_use", description: "公司品牌商标", remark: "" },
  { id: 4, assetCode: "WX2024004", assetName: "土地使用权", category: "land", certificateNo: "土国用(2023)第001号", acquisitionDate: "2023-07-01", originalValue: 500000, amortizationPeriod: 50, residualRate: 0, accumulatedAmortization: 5000, netValue: 495000, validityDate: "2073-07-01", status: "in_use", description: "工业用地使用权", remark: "" },
];
const totalOriginalValue = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.originalValue), 0);
});
const totalAmortization = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.accumulatedAmortization), 0);
});
const totalNetValue = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.netValue), 0);
});
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getCategoryLabel = (category) => {
  const map = {
    patent: "专利权",
    trademark: "商标权",
    copyright: "著作权",
    software: "软件",
    land: "土地使用权",
    other: "其他",
  };
  return map[category] || category;
};
const getStatusLabel = (status) => {
  const map = { in_use: "在用", idle: "闲置", amortized: "已摊销完毕" };
  return map[status] || status;
};
const getStatusType = (status) => {
  const map = { in_use: "success", idle: "warning", amortized: "info" };
  return map[status] || "";
};
const calculateNetValue = () => {
  form.netValue = Number((form.originalValue - form.accumulatedAmortization).toFixed(2));
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.assetCode) {
    result = result.filter(item => item.assetCode.includes(filters.assetCode));
  }
  if (filters.assetName) {
    result = result.filter(item => item.assetName.includes(filters.assetName));
  }
  if (filters.category) {
    result = result.filter(item => item.category === filters.category);
  }
  if (filters.status) {
    result = result.filter(item => item.status === filters.status);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.assetCode = "";
  filters.assetName = "";
  filters.category = "";
  filters.status = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增无形资产";
  Object.assign(form, {
    assetCode: "WX" + Date.now().toString().slice(-8),
    assetName: "",
    category: "",
    certificateNo: "",
    acquisitionDate: new Date().toISOString().split('T')[0],
    originalValue: 0,
    amortizationPeriod: 10,
    residualRate: 0,
    accumulatedAmortization: 0,
    netValue: 0,
    validityDate: "",
    status: "in_use",
    description: "",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑无形资产";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看资产: ${row.assetName}`);
};
const handleDelete = (row) => {
  ElMessageBox.confirm("确认删除该无形资产吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData.splice(index, 1);
    }
    ElMessage.success("删除成功");
    getTableData();
  });
};
const handleAmortization = () => {
  ElMessageBox.confirm("确认进行本月摊销计提吗?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "info",
  }).then(() => {
    mockData.forEach(item => {
      if (item.status === "in_use") {
        const monthlyAmortization = (item.originalValue * (1 - item.residualRate / 100)) / (item.amortizationPeriod * 12);
        item.accumulatedAmortization = Number((item.accumulatedAmortization + monthlyAmortization).toFixed(2));
        item.netValue = Number((item.originalValue - item.accumulatedAmortization).toFixed(2));
        if (item.netValue <= 0) {
          item.status = "amortized";
          item.netValue = 0;
        }
      }
    });
    ElMessage.success("摊销计提完成");
    getTableData();
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      calculateNetValue();
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
  > div:first-child {
    display: flex;
    align-items: center;
  }
}
.text-primary {
  color: #409eff;
  font-weight: bold;
}
.text-warning {
  color: #e6a23c;
  font-weight: bold;
}
.text-success {
  color: #67c23a;
  font-weight: bold;
}
</style>
src/views/financialManagement/generalLedger/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,290 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="科目编码:">
        <el-input v-model="filters.subjectCode" placeholder="请输入科目编码" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="科目名称:">
        <el-input v-model="filters.subjectName" placeholder="请输入科目名称" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="科目类型:">
        <el-select v-model="filters.subjectType" placeholder="请选择" clearable style="width: 200px;">
          <el-option label="资产类" value="asset" />
          <el-option label="负债类" value="liability" />
          <el-option label="权益类" value="equity" />
          <el-option label="成本类" value="cost" />
          <el-option label="损益类" value="profit_loss" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #subjectType="{ row }">
          <el-tag :type="getSubjectTypeType(row.subjectType)">{{ getSubjectTypeLabel(row.subjectType) }}</el-tag>
        </template>
        <template #balanceDirection="{ row }">
          <el-tag :type="row.balanceDirection === 'debit' ? 'success' : 'danger'">
            {{ row.balanceDirection === 'debit' ? '借方' : '贷方' }}
          </el-tag>
        </template>
        <template #status="{ row }">
          <el-tag :type="row.status === 'active' ? 'success' : 'info'">
            {{ row.status === 'active' ? '启用' : '禁用' }}
          </el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="edit(row)">编辑</el-button>
          <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="600px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <el-form-item label="科目编码" prop="subjectCode">
          <el-input v-model="form.subjectCode" placeholder="请输入科目编码" />
        </el-form-item>
        <el-form-item label="科目名称" prop="subjectName">
          <el-input v-model="form.subjectName" placeholder="请输入科目名称" />
        </el-form-item>
        <el-form-item label="科目类型" prop="subjectType">
          <el-select v-model="form.subjectType" placeholder="请选择科目类型" style="width: 100%;">
            <el-option label="资产类" value="asset" />
            <el-option label="负债类" value="liability" />
            <el-option label="权益类" value="equity" />
            <el-option label="成本类" value="cost" />
            <el-option label="损益类" value="profit_loss" />
          </el-select>
        </el-form-item>
        <el-form-item label="余额方向" prop="balanceDirection">
          <el-radio-group v-model="form.balanceDirection">
            <el-radio label="debit">借方</el-radio>
            <el-radio label="credit">贷方</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-radio-group v-model="form.status">
            <el-radio label="active">启用</el-radio>
            <el-radio label="inactive">禁用</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "总帐科目",
});
const filters = reactive({
  subjectCode: "",
  subjectName: "",
  subjectType: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "科目编码", prop: "subjectCode", width: "120" },
  { label: "科目名称", prop: "subjectName", width: "150" },
  { label: "科目类型", prop: "subjectType", slot: "subjectType" },
  { label: "余额方向", prop: "balanceDirection", slot: "balanceDirection" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "备注", prop: "remark", showOverflowTooltip: true },
  { label: "操作", prop: "operation", slot: "operation", width: "150", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const form = reactive({
  subjectCode: "",
  subjectName: "",
  subjectType: "",
  balanceDirection: "debit",
  status: "active",
  remark: "",
});
const rules = {
  subjectCode: [{ required: true, message: "请输入科目编码", trigger: "blur" }],
  subjectName: [{ required: true, message: "请输入科目名称", trigger: "blur" }],
  subjectType: [{ required: true, message: "请选择科目类型", trigger: "change" }],
};
const mockData = [
  { id: 1, subjectCode: "1001", subjectName: "库存现金", subjectType: "asset", balanceDirection: "debit", status: "active", remark: "" },
  { id: 2, subjectCode: "1002", subjectName: "银行存款", subjectType: "asset", balanceDirection: "debit", status: "active", remark: "" },
  { id: 3, subjectCode: "1122", subjectName: "应收账款", subjectType: "asset", balanceDirection: "debit", status: "active", remark: "" },
  { id: 4, subjectCode: "2202", subjectName: "应付账款", subjectType: "liability", balanceDirection: "credit", status: "active", remark: "" },
  { id: 5, subjectCode: "4001", subjectName: "实收资本", subjectType: "equity", balanceDirection: "credit", status: "active", remark: "" },
  { id: 6, subjectCode: "5001", subjectName: "生产成本", subjectType: "cost", balanceDirection: "debit", status: "active", remark: "" },
  { id: 7, subjectCode: "6001", subjectName: "主营业务收入", subjectType: "profit_loss", balanceDirection: "credit", status: "active", remark: "" },
  { id: 8, subjectCode: "6401", subjectName: "主营业务成本", subjectType: "profit_loss", balanceDirection: "debit", status: "active", remark: "" },
];
const getSubjectTypeLabel = (type) => {
  const map = {
    asset: "资产类",
    liability: "负债类",
    equity: "权益类",
    cost: "成本类",
    profit_loss: "损益类",
  };
  return map[type] || type;
};
const getSubjectTypeType = (type) => {
  const map = {
    asset: "success",
    liability: "danger",
    equity: "warning",
    cost: "info",
    profit_loss: "primary",
  };
  return map[type] || "";
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.subjectCode) {
    result = result.filter(item => item.subjectCode.includes(filters.subjectCode));
  }
  if (filters.subjectName) {
    result = result.filter(item => item.subjectName.includes(filters.subjectName));
  }
  if (filters.subjectType) {
    result = result.filter(item => item.subjectType === filters.subjectType);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.subjectCode = "";
  filters.subjectName = "";
  filters.subjectType = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增科目";
  Object.assign(form, {
    subjectCode: "",
    subjectName: "",
    subjectType: "",
    balanceDirection: "debit",
    status: "active",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑科目";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
const handleDelete = (row) => {
  ElMessageBox.confirm("确认删除该科目吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData.splice(index, 1);
    }
    ElMessage.success("删除成功");
    getTableData();
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
</style>
src/views/financialManagement/payable/input-invoice.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,404 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="发票代码:">
        <el-input v-model="filters.invoiceCode" placeholder="请输入发票代码" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="发票号码:">
        <el-input v-model="filters.invoiceNo" placeholder="请输入发票号码" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="供应商:">
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable style="width: 200px;">
          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="认证状态:">
        <el-select v-model="filters.certifyStatus" placeholder="请选择认证状态" clearable style="width: 150px;">
          <el-option label="未认证" value="uncertified" />
          <el-option label="已认证" value="certified" />
          <el-option label="认证失败" value="failed" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div>
          <el-button type="success" @click="handleBatchCertify" icon="Check" :disabled="selectedRows.length === 0">批量认证</el-button>
        </div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">录入发票</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        isSelection
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @selection-change="handleSelectionChange"
        @pagination="changePage"
      >
        <template #amount="{ row }">
          <span class="text-primary">Â¥{{ formatMoney(row.amount) }}</span>
        </template>
        <template #taxAmount="{ row }">
          <span class="text-danger">Â¥{{ formatMoney(row.taxAmount) }}</span>
        </template>
        <template #totalAmount="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.totalAmount) }}</span>
        </template>
        <template #certifyStatus="{ row }">
          <el-tag :type="getCertifyStatusType(row.certifyStatus)">{{ getCertifyStatusLabel(row.certifyStatus) }}</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)">编辑</el-button>
          <el-button type="success" link @click="handleCertify(row)" v-if="row.certifyStatus === 'uncertified'">认证</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="发票代码" prop="invoiceCode">
              <el-input v-model="form.invoiceCode" placeholder="请输入发票代码" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="发票号码" prop="invoiceNo">
              <el-input v-model="form.invoiceNo" placeholder="请输入发票号码" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="供应商" prop="supplierId">
              <el-select v-model="form.supplierId" placeholder="请选择供应商" style="width: 100%;">
                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="开票日期" prop="invoiceDate">
              <el-date-picker v-model="form.invoiceDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="金额(不含税)" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" @change="calculateTax" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="税率" prop="taxRate">
              <el-select v-model="form.taxRate" placeholder="请选择税率" style="width: 100%;" @change="calculateTax">
                <el-option label="0%" :value="0" />
                <el-option label="3%" :value="3" />
                <el-option label="6%" :value="6" />
                <el-option label="9%" :value="9" />
                <el-option label="13%" :value="13" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="税额">
              <el-input v-model="form.taxAmount" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="认证状态" prop="certifyStatus">
              <el-select v-model="form.certifyStatus" placeholder="请选择认证状态" style="width: 100%;" disabled>
                <el-option label="未认证" value="uncertified" />
                <el-option label="已认证" value="certified" />
                <el-option label="认证失败" value="failed" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="认证日期" prop="certifyDate">
              <el-date-picker v-model="form.certifyDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="发票内容" prop="content">
          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="请输入发票内容" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "进项发票",
});
const filters = reactive({
  invoiceCode: "",
  invoiceNo: "",
  supplierId: "",
  certifyStatus: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "发票代码", prop: "invoiceCode", width: "130" },
  { label: "发票号码", prop: "invoiceNo", width: "120" },
  { label: "供应商", prop: "supplierName", width: "180" },
  { label: "开票日期", prop: "invoiceDate", width: "120" },
  { label: "金额", prop: "amount", slot: "amount" },
  { label: "税额", prop: "taxAmount", slot: "taxAmount" },
  { label: "价税合计", prop: "totalAmount", slot: "totalAmount" },
  { label: "认证状态", prop: "certifyStatus", slot: "certifyStatus" },
  { label: "操作", prop: "operation", slot: "operation", width: "180", fixed: "right" },
];
const dataList = ref([]);
const selectedRows = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const supplierList = [
  { id: 1, name: "北京原材料供应商" },
  { id: 2, name: "上海电子元器件公司" },
  { id: 3, name: "广州包装材料厂" },
  { id: 4, name: "深圳五金配件公司" },
];
const form = reactive({
  invoiceCode: "",
  invoiceNo: "",
  supplierId: "",
  invoiceDate: "",
  amount: 0,
  taxRate: 13,
  taxAmount: 0,
  totalAmount: 0,
  certifyStatus: "uncertified",
  certifyDate: "",
  content: "",
  remark: "",
});
const rules = {
  invoiceCode: [{ required: true, message: "请输入发票代码", trigger: "blur" }],
  invoiceNo: [{ required: true, message: "请输入发票号码", trigger: "blur" }],
  supplierId: [{ required: true, message: "请选择供应商", trigger: "change" }],
  invoiceDate: [{ required: true, message: "请选择开票日期", trigger: "change" }],
  amount: [{ required: true, message: "请输入金额", trigger: "blur" }],
  taxRate: [{ required: true, message: "请选择税率", trigger: "change" }],
};
const mockData = [
  { id: 1, invoiceCode: "0440021001", invoiceNo: "12345678", supplierId: 1, supplierName: "北京原材料供应商", invoiceDate: "2024-01-08", amount: 8000, taxRate: 13, taxAmount: 1040, totalAmount: 9040, certifyStatus: "certified", certifyDate: "2024-01-15", content: "原材料采购", remark: "" },
  { id: 2, invoiceCode: "0440021002", invoiceNo: "87654321", supplierId: 2, supplierName: "上海电子元器件公司", invoiceDate: "2024-01-10", amount: 12000, taxRate: 13, taxAmount: 1560, totalAmount: 13560, certifyStatus: "uncertified", certifyDate: "", content: "电子元器件", remark: "" },
  { id: 3, invoiceCode: "0440021003", invoiceNo: "11112222", supplierId: 3, supplierName: "广州包装材料厂", invoiceDate: "2024-01-12", amount: 3500, taxRate: 13, taxAmount: 455, totalAmount: 3955, certifyStatus: "certified", certifyDate: "2024-01-18", content: "包装材料", remark: "" },
];
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const calculateTax = () => {
  form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(2));
  form.totalAmount = Number((form.amount + form.taxAmount).toFixed(2));
};
const getCertifyStatusLabel = (status) => {
  const map = { uncertified: "未认证", certified: "已认证", failed: "认证失败" };
  return map[status] || status;
};
const getCertifyStatusType = (status) => {
  const map = { uncertified: "info", certified: "success", failed: "danger" };
  return map[status] || "";
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.invoiceCode) {
    result = result.filter(item => item.invoiceCode.includes(filters.invoiceCode));
  }
  if (filters.invoiceNo) {
    result = result.filter(item => item.invoiceNo.includes(filters.invoiceNo));
  }
  if (filters.supplierId) {
    result = result.filter(item => item.supplierId === filters.supplierId);
  }
  if (filters.certifyStatus) {
    result = result.filter(item => item.certifyStatus === filters.certifyStatus);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.invoiceCode = "";
  filters.invoiceNo = "";
  filters.supplierId = "";
  filters.certifyStatus = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "录入发票";
  Object.assign(form, {
    invoiceCode: "",
    invoiceNo: "",
    supplierId: "",
    invoiceDate: new Date().toISOString().split('T')[0],
    amount: 0,
    taxRate: 13,
    taxAmount: 0,
    totalAmount: 0,
    certifyStatus: "uncertified",
    certifyDate: "",
    content: "",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑发票";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看发票: ${row.invoiceCode}-${row.invoiceNo}`);
};
const handleCertify = (row) => {
  ElMessageBox.confirm("确认认证该发票吗?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "info",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].certifyStatus = "certified";
      mockData[index].certifyDate = new Date().toISOString().split('T')[0];
    }
    ElMessage.success("认证成功");
    getTableData();
  });
};
const handleBatchCertify = () => {
  ElMessageBox.confirm(`确认批量认证选中的 ${selectedRows.value.length} å¼ å‘票吗?`, "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "info",
  }).then(() => {
    selectedRows.value.forEach(row => {
      const index = mockData.findIndex(item => item.id === row.id);
      if (index !== -1 && mockData[index].certifyStatus === "uncertified") {
        mockData[index].certifyStatus = "certified";
        mockData[index].certifyDate = new Date().toISOString().split('T')[0];
      }
    });
    ElMessage.success("批量认证成功");
    getTableData();
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const supplier = supplierList.find(item => item.id === form.supplierId);
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, supplierName: supplier?.name });
        ElMessage.success("录入成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
.text-primary {
  color: #409eff;
  font-weight: bold;
}
.text-danger {
  color: #f56c6c;
  font-weight: bold;
}
.text-success {
  color: #67c23a;
  font-weight: bold;
}
</style>
src/views/financialManagement/payable/payment.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,376 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="付款单号:">
        <el-input v-model="filters.paymentCode" placeholder="请输入付款单号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="供应商:">
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable style="width: 200px;">
          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="付款方式:">
        <el-select v-model="filters.paymentMethod" placeholder="请选择付款方式" clearable style="width: 150px;">
          <el-option label="银行转账" value="bank_transfer" />
          <el-option label="现金" value="cash" />
          <el-option label="支票" value="check" />
          <el-option label="汇票" value="draft" />
        </el-select>
      </el-form-item>
      <el-form-item label="状态:">
        <el-select v-model="filters.status" placeholder="请选择状态" clearable style="width: 150px;">
          <el-option label="待付款" value="pending" />
          <el-option label="已完成" value="completed" />
          <el-option label="已取消" value="cancelled" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div>
          <el-statistic title="本期付款合计" :value="totalPaymentAmount" precision="2" prefix="Â¥" />
        </div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增付款</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #amount="{ row }">
          <span class="text-danger">Â¥{{ formatMoney(row.amount) }}</span>
        </template>
        <template #paymentMethod="{ row }">
          <el-tag>{{ getPaymentMethodLabel(row.paymentMethod) }}</el-tag>
        </template>
        <template #status="{ row }">
          <el-tag :type="row.status === 'completed' ? 'success' : row.status === 'pending' ? 'warning' : 'info'">
            {{ row.status === 'completed' ? '已完成' : row.status === 'pending' ? '待付款' : '已取消' }}
          </el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">编辑</el-button>
          <el-button type="success" link @click="handleComplete(row)" v-if="row.status === 'pending'">完成</el-button>
          <el-button type="danger" link @click="handleCancel(row)" v-if="row.status === 'pending'">取消</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款单号" prop="paymentCode">
              <el-input v-model="form.paymentCode" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="关联申请单" prop="applyCode">
              <el-select v-model="form.applyCode" placeholder="请选择关联申请单" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in applyList" :key="item.applyCode" :label="item.applyCode" :value="item.applyCode" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="供应商" prop="supplierId">
              <el-select v-model="form.supplierId" placeholder="请选择供应商" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款日期" prop="paymentDate">
              <el-date-picker v-model="form.paymentDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款金额" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款方式" prop="paymentMethod">
              <el-select v-model="form.paymentMethod" placeholder="请选择付款方式" style="width: 100%;">
                <el-option label="银行转账" value="bank_transfer" />
                <el-option label="现金" value="cash" />
                <el-option label="支票" value="check" />
                <el-option label="汇票" value="draft" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="银行账号" prop="bankAccount" v-if="form.paymentMethod === 'bank_transfer'">
              <el-input v-model="form.bankAccount" placeholder="请输入银行账号" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="开户行" prop="bankName" v-if="form.paymentMethod === 'bank_transfer'">
              <el-input v-model="form.bankName" placeholder="请输入开户行" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "付款单",
});
const filters = reactive({
  paymentCode: "",
  supplierId: "",
  paymentMethod: "",
  status: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "付款单号", prop: "paymentCode", width: "150" },
  { label: "关联申请单", prop: "applyCode", width: "150" },
  { label: "供应商", prop: "supplierName", width: "180" },
  { label: "付款日期", prop: "paymentDate", width: "120" },
  { label: "付款金额", prop: "amount", slot: "amount" },
  { label: "付款方式", prop: "paymentMethod", slot: "paymentMethod" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "备注", prop: "remark", showOverflowTooltip: true },
  { label: "操作", prop: "operation", slot: "operation", width: "220", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const supplierList = [
  { id: 1, name: "北京原材料供应商" },
  { id: 2, name: "上海电子元器件公司" },
  { id: 3, name: "广州包装材料厂" },
  { id: 4, name: "深圳五金配件公司" },
];
const applyList = [
  { applyCode: "FK2024001", supplierId: 1, amount: 5000 },
  { applyCode: "FK2024002", supplierId: 2, amount: 8000 },
  { applyCode: "FK2024003", supplierId: 3, amount: 3000 },
];
const form = reactive({
  paymentCode: "",
  applyCode: "",
  supplierId: "",
  paymentDate: "",
  amount: 0,
  paymentMethod: "bank_transfer",
  bankAccount: "",
  bankName: "",
  remark: "",
});
const rules = {
  applyCode: [{ required: true, message: "请选择关联申请单", trigger: "change" }],
  supplierId: [{ required: true, message: "请选择供应商", trigger: "change" }],
  paymentDate: [{ required: true, message: "请选择付款日期", trigger: "change" }],
  amount: [{ required: true, message: "请输入付款金额", trigger: "blur" }],
  paymentMethod: [{ required: true, message: "请选择付款方式", trigger: "change" }],
};
const mockData = [
  { id: 1, paymentCode: "FKD2024001", applyCode: "FK2024001", supplierId: 1, supplierName: "北京原材料供应商", paymentDate: "2024-01-15", amount: 5000, paymentMethod: "bank_transfer", status: "completed", bankAccount: "6222021234567890123", bankName: "工商银行", remark: "" },
  { id: 2, paymentCode: "FKD2024002", applyCode: "FK2024002", supplierId: 2, supplierName: "上海电子元器件公司", paymentDate: "2024-01-18", amount: 8000, paymentMethod: "bank_transfer", status: "pending", bankAccount: "6222029876543210987", bankName: "建设银行", remark: "" },
  { id: 3, paymentCode: "FKD2024003", applyCode: "FK2024003", supplierId: 3, supplierName: "广州包装材料厂", paymentDate: "2024-01-20", amount: 3000, paymentMethod: "cash", status: "completed", remark: "" },
];
const totalPaymentAmount = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.amount), 0);
});
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getPaymentMethodLabel = (method) => {
  const map = {
    bank_transfer: "银行转账",
    cash: "现金",
    check: "支票",
    draft: "汇票",
  };
  return map[method] || method;
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.paymentCode) {
    result = result.filter(item => item.paymentCode.includes(filters.paymentCode));
  }
  if (filters.supplierId) {
    result = result.filter(item => item.supplierId === filters.supplierId);
  }
  if (filters.paymentMethod) {
    result = result.filter(item => item.paymentMethod === filters.paymentMethod);
  }
  if (filters.status) {
    result = result.filter(item => item.status === filters.status);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.paymentCode = "";
  filters.supplierId = "";
  filters.paymentMethod = "";
  filters.status = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增付款";
  Object.assign(form, {
    paymentCode: "FKD" + Date.now().toString().slice(-8),
    applyCode: "",
    supplierId: "",
    paymentDate: new Date().toISOString().split('T')[0],
    amount: 0,
    paymentMethod: "bank_transfer",
    bankAccount: "",
    bankName: "",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑付款";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看付款单: ${row.paymentCode}`);
};
const handleComplete = (row) => {
  ElMessageBox.confirm("确认该付款单已完成吗?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "info",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "completed";
    }
    ElMessage.success("付款完成");
    getTableData();
  });
};
const handleCancel = (row) => {
  ElMessageBox.confirm("确认取消该付款单吗?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "cancelled";
    }
    ElMessage.success("已取消");
    getTableData();
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const supplier = supplierList.find(item => item.id === form.supplierId);
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
}
.text-danger {
  color: #f56c6c;
  font-weight: bold;
}
</style>
src/views/financialManagement/payable/paymentApply.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,359 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="申请单号:">
        <el-input v-model="filters.applyCode" placeholder="请输入申请单号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="供应商:">
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable style="width: 200px;">
          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="状态:">
        <el-select v-model="filters.status" placeholder="请选择状态" clearable style="width: 150px;">
          <el-option label="待审批" value="pending" />
          <el-option label="已审批" value="approved" />
          <el-option label="已驳回" value="rejected" />
          <el-option label="已付款" value="paid" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增申请</el-button>
          <el-button @click="handleBatchApply" icon="Document" :disabled="selectedRows.length === 0">批量申请</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        isSelection
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @selection-change="handleSelectionChange"
        @pagination="changePage"
      >
        <template #amount="{ row }">
          <span class="text-danger">Â¥{{ formatMoney(row.amount) }}</span>
        </template>
        <template #paymentMethod="{ row }">
          <el-tag>{{ getPaymentMethodLabel(row.paymentMethod) }}</el-tag>
        </template>
        <template #status="{ row }">
          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">编辑</el-button>
          <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">审批</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="申请单号" prop="applyCode">
              <el-input v-model="form.applyCode" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="供应商" prop="supplierId">
              <el-select v-model="form.supplierId" placeholder="请选择供应商" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款金额" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款方式" prop="paymentMethod">
              <el-select v-model="form.paymentMethod" placeholder="请选择付款方式" style="width: 100%;">
                <el-option label="银行转账" value="bank_transfer" />
                <el-option label="现金" value="cash" />
                <el-option label="支票" value="check" />
                <el-option label="汇票" value="draft" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="申请日期" prop="applyDate">
              <el-date-picker v-model="form.applyDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="期望付款日期" prop="expectedDate">
              <el-date-picker v-model="form.expectedDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="关联入库单" prop="relatedDocs">
          <el-select v-model="form.relatedDocs" multiple placeholder="请选择关联入库单" style="width: 100%;">
            <el-option v-for="item in inList" :key="item.inCode" :label="item.inCode" :value="item.inCode" />
          </el-select>
        </el-form-item>
        <el-form-item label="付款事由" prop="reason">
          <el-input v-model="form.reason" type="textarea" :rows="3" placeholder="请输入付款事由" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "付款申请",
});
const filters = reactive({
  applyCode: "",
  supplierId: "",
  status: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "申请单号", prop: "applyCode", width: "150" },
  { label: "供应商", prop: "supplierName", width: "180" },
  { label: "付款金额", prop: "amount", slot: "amount" },
  { label: "付款方式", prop: "paymentMethod", slot: "paymentMethod" },
  { label: "申请日期", prop: "applyDate", width: "120" },
  { label: "期望付款日", prop: "expectedDate", width: "120" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "操作", prop: "operation", slot: "operation", width: "200", fixed: "right" },
];
const dataList = ref([]);
const selectedRows = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const supplierList = [
  { id: 1, name: "北京原材料供应商" },
  { id: 2, name: "上海电子元器件公司" },
  { id: 3, name: "广州包装材料厂" },
  { id: 4, name: "深圳五金配件公司" },
];
const inList = [
  { inCode: "RK2024001", supplierId: 1 },
  { inCode: "RK2024002", supplierId: 2 },
  { inCode: "RK2024003", supplierId: 3 },
];
const form = reactive({
  applyCode: "",
  supplierId: "",
  amount: 0,
  paymentMethod: "bank_transfer",
  applyDate: "",
  expectedDate: "",
  relatedDocs: [],
  reason: "",
  remark: "",
});
const rules = {
  supplierId: [{ required: true, message: "请选择供应商", trigger: "change" }],
  amount: [{ required: true, message: "请输入付款金额", trigger: "blur" }],
  paymentMethod: [{ required: true, message: "请选择付款方式", trigger: "change" }],
  applyDate: [{ required: true, message: "请选择申请日期", trigger: "change" }],
  expectedDate: [{ required: true, message: "请选择期望付款日期", trigger: "change" }],
};
const mockData = [
  { id: 1, applyCode: "FK2024001", supplierId: 1, supplierName: "北京原材料供应商", amount: 5000, paymentMethod: "bank_transfer", applyDate: "2024-01-12", expectedDate: "2024-01-15", status: "pending", relatedDocs: ["RK2024001"], reason: "支付原材料货款", remark: "" },
  { id: 2, applyCode: "FK2024002", supplierId: 2, supplierName: "上海电子元器件公司", amount: 8000, paymentMethod: "bank_transfer", applyDate: "2024-01-14", expectedDate: "2024-01-18", status: "approved", relatedDocs: ["RK2024002"], reason: "支付电子元器件货款", remark: "" },
  { id: 3, applyCode: "FK2024003", supplierId: 3, supplierName: "广州包装材料厂", amount: 3000, paymentMethod: "cash", applyDate: "2024-01-16", expectedDate: "2024-01-20", status: "paid", relatedDocs: ["RK2024003"], reason: "支付包装材料货款", remark: "" },
];
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getPaymentMethodLabel = (method) => {
  const map = {
    bank_transfer: "银行转账",
    cash: "现金",
    check: "支票",
    draft: "汇票",
  };
  return map[method] || method;
};
const getStatusLabel = (status) => {
  const map = { pending: "待审批", approved: "已审批", rejected: "已驳回", paid: "已付款" };
  return map[status] || status;
};
const getStatusType = (status) => {
  const map = { pending: "warning", approved: "success", rejected: "danger", paid: "primary" };
  return map[status] || "";
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.applyCode) {
    result = result.filter(item => item.applyCode.includes(filters.applyCode));
  }
  if (filters.supplierId) {
    result = result.filter(item => item.supplierId === filters.supplierId);
  }
  if (filters.status) {
    result = result.filter(item => item.status === filters.status);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.applyCode = "";
  filters.supplierId = "";
  filters.status = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增付款申请";
  Object.assign(form, {
    applyCode: "FK" + Date.now().toString().slice(-8),
    supplierId: "",
    amount: 0,
    paymentMethod: "bank_transfer",
    applyDate: new Date().toISOString().split('T')[0],
    expectedDate: "",
    relatedDocs: [],
    reason: "",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑付款申请";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看申请单: ${row.applyCode}`);
};
const handleAudit = (row) => {
  ElMessageBox.confirm("确认审批通过该付款申请吗?", "提示", {
    confirmButtonText: "通过",
    cancelButtonText: "驳回",
    distinguishCancelAndClose: true,
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "approved";
    }
    ElMessage.success("审批通过");
    getTableData();
  }).catch((action) => {
    if (action === "cancel") {
      const index = mockData.findIndex(item => item.id === row.id);
      if (index !== -1) {
        mockData[index].status = "rejected";
      }
      ElMessage.warning("已驳回");
      getTableData();
    }
  });
};
const handleBatchApply = () => {
  ElMessage.success(`批量申请 ${selectedRows.value.length} æ¡è®°å½•`);
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const supplier = supplierList.find(item => item.id === form.supplierId);
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
.text-danger {
  color: #f56c6c;
  font-weight: bold;
}
</style>
src/views/financialManagement/payable/purchaseIn.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,331 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="入库单号:">
        <el-input v-model="filters.inCode" placeholder="请输入入库单号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="供应商:">
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable style="width: 200px;">
          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="入库日期:">
        <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" clearable />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增入库</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #amount="{ row }">
          <span class="text-primary">Â¥{{ formatMoney(row.amount) }}</span>
        </template>
        <template #status="{ row }">
          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">编辑</el-button>
          <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">删除</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="入库单号" prop="inCode">
              <el-input v-model="form.inCode" placeholder="请输入入库单号" :disabled="isEdit" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="供应商" prop="supplierId">
              <el-select v-model="form.supplierId" placeholder="请选择供应商" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="入库日期" prop="inDate">
              <el-date-picker v-model="form.inDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="入库金额" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="入库明细" prop="details">
          <el-table :data="form.details" border style="width: 100%">
            <el-table-column prop="materialName" label="物料名称" width="150">
              <template #default="{ $index }">
                <el-input v-model="form.details[$index].materialName" placeholder="物料名称" />
              </template>
            </el-table-column>
            <el-table-column prop="spec" label="规格" width="120">
              <template #default="{ $index }">
                <el-input v-model="form.details[$index].spec" placeholder="规格" />
              </template>
            </el-table-column>
            <el-table-column prop="quantity" label="数量" width="100">
              <template #default="{ $index }">
                <el-input-number v-model="form.details[$index].quantity" :min="0" style="width: 100%;" />
              </template>
            </el-table-column>
            <el-table-column prop="unitPrice" label="单价" width="120">
              <template #default="{ $index }">
                <el-input-number v-model="form.details[$index].unitPrice" :min="0" :precision="2" style="width: 100%;" />
              </template>
            </el-table-column>
            <el-table-column prop="total" label="金额" width="120">
              <template #default="{ row }">
                <span>Â¥{{ formatMoney(row.quantity * row.unitPrice) }}</span>
              </template>
            </el-table-column>
            <el-table-column label="操作" width="80">
              <template #default="{ $index }">
                <el-button type="danger" link @click="removeDetail($index)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
          <el-button type="primary" link @click="addDetail" style="margin-top: 10px;">+ æ·»åŠ æ˜Žç»†</el-button>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "采购入库",
});
const filters = reactive({
  inCode: "",
  supplierId: "",
  dateRange: [],
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "入库单号", prop: "inCode", width: "150" },
  { label: "供应商", prop: "supplierName", width: "180" },
  { label: "入库日期", prop: "inDate", width: "120" },
  { label: "入库金额", prop: "amount", slot: "amount" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "备注", prop: "remark", showOverflowTooltip: true },
  { label: "操作", prop: "operation", slot: "operation", width: "200", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const supplierList = [
  { id: 1, name: "北京原材料供应商" },
  { id: 2, name: "上海电子元器件公司" },
  { id: 3, name: "广州包装材料厂" },
  { id: 4, name: "深圳五金配件公司" },
];
const form = reactive({
  inCode: "",
  supplierId: "",
  inDate: "",
  amount: 0,
  details: [],
  remark: "",
});
const rules = {
  inCode: [{ required: true, message: "请输入入库单号", trigger: "blur" }],
  supplierId: [{ required: true, message: "请选择供应商", trigger: "change" }],
  inDate: [{ required: true, message: "请选择入库日期", trigger: "change" }],
  amount: [{ required: true, message: "请输入入库金额", trigger: "blur" }],
};
const mockData = [
  { id: 1, inCode: "RK2024001", supplierId: 1, supplierName: "北京原材料供应商", inDate: "2024-01-10", amount: 8000, status: "approved", details: [{ materialName: "钢材", spec: "Q235", quantity: 10, unitPrice: 500 }], remark: "" },
  { id: 2, inCode: "RK2024002", supplierId: 2, supplierName: "上海电子元器件公司", inDate: "2024-01-12", amount: 12000, status: "pending", details: [{ materialName: "芯片", spec: "STM32", quantity: 100, unitPrice: 80 }], remark: "" },
  { id: 3, inCode: "RK2024003", supplierId: 3, supplierName: "广州包装材料厂", inDate: "2024-01-15", amount: 3500, status: "approved", details: [{ materialName: "纸箱", spec: "50*40*30", quantity: 500, unitPrice: 5 }], remark: "" },
];
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getStatusLabel = (status) => {
  const map = { pending: "待审核", approved: "已审核", rejected: "已驳回" };
  return map[status] || status;
};
const getStatusType = (status) => {
  const map = { pending: "warning", approved: "success", rejected: "danger" };
  return map[status] || "";
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.inCode) {
    result = result.filter(item => item.inCode.includes(filters.inCode));
  }
  if (filters.supplierId) {
    result = result.filter(item => item.supplierId === filters.supplierId);
  }
  if (filters.dateRange && filters.dateRange.length === 2) {
    result = result.filter(item => item.inDate >= filters.dateRange[0] && item.inDate <= filters.dateRange[1]);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.inCode = "";
  filters.supplierId = "";
  filters.dateRange = [];
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const addDetail = () => {
  form.details.push({ materialName: "", spec: "", quantity: 0, unitPrice: 0 });
};
const removeDetail = (index) => {
  form.details.splice(index, 1);
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增入库";
  Object.assign(form, {
    inCode: "RK" + Date.now().toString().slice(-8),
    supplierId: "",
    inDate: new Date().toISOString().split('T')[0],
    amount: 0,
    details: [{ materialName: "", spec: "", quantity: 0, unitPrice: 0 }],
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑入库";
  Object.assign(form, row);
  if (!form.details || form.details.length === 0) {
    form.details = [{ materialName: "", spec: "", quantity: 0, unitPrice: 0 }];
  }
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看入库单: ${row.inCode}`);
};
const handleDelete = (row) => {
  ElMessageBox.confirm("确认删除该入库单吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData.splice(index, 1);
    }
    ElMessage.success("删除成功");
    getTableData();
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const supplier = supplierList.find(item => item.id === form.supplierId);
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
.text-primary {
  color: #409eff;
  font-weight: bold;
}
</style>
src/views/financialManagement/payable/reconciliation.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,254 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="供应商:">
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable style="width: 200px;">
          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="对账期间:">
        <el-date-picker v-model="filters.startMonth" type="month" placeholder="开始月份" value-format="YYYY-MM" style="width: 140px;" />
        <span style="margin: 0 10px;">至</span>
        <el-date-picker v-model="filters.endMonth" type="month" placeholder="结束月份" value-format="YYYY-MM" style="width: 140px;" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div>
          <el-button type="primary" @click="generateStatement" icon="Document">生成对账单</el-button>
        </div>
        <div>
          <el-button @click="handleOut" icon="Download">导出对账单</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #beginBalance="{ row }">
          <span :class="row.beginBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.beginBalance) }}</span>
        </template>
        <template #currentPayable="{ row }">
          <span class="text-danger">Â¥{{ formatMoney(row.currentPayable) }}</span>
        </template>
        <template #currentPayment="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.currentPayment) }}</span>
        </template>
        <template #endBalance="{ row }">
          <span :class="row.endBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.endBalance) }}</span>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="viewDetail(row)">查看明细</el-button>
          <el-button type="primary" link @click="printStatement(row)">打印</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog title="对账明细" v-model="detailDialogVisible" width="900px" append-to-body>
      <div class="statement-header">
        <h3>{{ currentSupplier }} åº”付对账单</h3>
        <p>对账期间: {{ currentPeriod }}</p>
      </div>
      <el-table :data="detailData" border style="width: 100%">
        <el-table-column prop="date" label="日期" width="120" />
        <el-table-column prop="type" label="类型" width="100">
          <template #default="{ row }">
            <el-tag :type="row.type === '入库' ? 'success' : row.type === '退货' ? 'danger' : 'primary'">{{ row.type }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="code" label="单据编号" width="150" />
        <el-table-column prop="debit" label="借方(付款)" width="120">
          <template #default="{ row }">
            <span v-if="row.debit > 0" class="text-success">Â¥{{ formatMoney(row.debit) }}</span>
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column prop="credit" label="贷方(应付)" width="120">
          <template #default="{ row }">
            <span v-if="row.credit > 0" class="text-danger">Â¥{{ formatMoney(row.credit) }}</span>
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column prop="balance" label="余额" width="120">
          <template #default="{ row }">
            <span :class="row.balance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.balance) }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="remark" label="备注" show-overflow-tooltip />
      </el-table>
      <template #footer>
        <el-button @click="detailDialogVisible = false">关闭</el-button>
        <el-button type="primary" @click="printDetail">打印</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage } from "element-plus";
defineOptions({
  name: "应付对账",
});
const filters = reactive({
  supplierId: "",
  startMonth: "",
  endMonth: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "对账单号", prop: "statementCode", width: "150" },
  { label: "供应商", prop: "supplierName", width: "180" },
  { label: "对账期间", prop: "period", width: "150" },
  { label: "期初余额", prop: "beginBalance", slot: "beginBalance" },
  { label: "本期应付", prop: "currentPayable", slot: "currentPayable" },
  { label: "本期付款", prop: "currentPayment", slot: "currentPayment" },
  { label: "期末余额", prop: "endBalance", slot: "endBalance" },
  { label: "操作", prop: "operation", slot: "operation", width: "150", fixed: "right" },
];
const dataList = ref([]);
const detailDialogVisible = ref(false);
const currentSupplier = ref("");
const currentPeriod = ref("");
const detailData = ref([]);
const supplierList = [
  { id: 1, name: "北京原材料供应商" },
  { id: 2, name: "上海电子元器件公司" },
  { id: 3, name: "广州包装材料厂" },
  { id: 4, name: "深圳五金配件公司" },
];
const mockData = [
  { id: 1, statementCode: "DZ202401001", supplierId: 1, supplierName: "北京原材料供应商", period: "2024-01", beginBalance: 20000, currentPayable: 15000, currentPayment: 10000, endBalance: 25000 },
  { id: 2, statementCode: "DZ202401002", supplierId: 2, supplierName: "上海电子元器件公司", period: "2024-01", beginBalance: 10000, currentPayable: 20000, currentPayment: 15000, endBalance: 15000 },
  { id: 3, statementCode: "DZ202402001", supplierId: 1, supplierName: "北京原材料供应商", period: "2024-02", beginBalance: 25000, currentPayable: 18000, currentPayment: 20000, endBalance: 23000 },
];
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.supplierId) {
    result = result.filter(item => item.supplierId === filters.supplierId);
  }
  if (filters.startMonth && filters.endMonth) {
    result = result.filter(item => item.period >= filters.startMonth && item.period <= filters.endMonth);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.supplierId = "";
  filters.startMonth = "";
  filters.endMonth = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const generateStatement = () => {
  ElMessage.success("对账单生成成功");
  const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
  const supplier = supplierList[Math.floor(Math.random() * supplierList.length)];
  mockData.unshift({
    id: newId,
    statementCode: "DZ" + Date.now(),
    supplierId: supplier.id,
    supplierName: supplier.name,
    period: "2024-03",
    beginBalance: Math.floor(Math.random() * 20000),
    currentPayable: Math.floor(Math.random() * 25000),
    currentPayment: Math.floor(Math.random() * 20000),
    endBalance: Math.floor(Math.random() * 25000),
  });
  getTableData();
};
const viewDetail = (row) => {
  currentSupplier.value = row.supplierName;
  currentPeriod.value = row.period;
  detailData.value = [
    { date: row.period + "-01", type: "期初", code: "-", debit: 0, credit: 0, balance: row.beginBalance, remark: "期初余额" },
    { date: row.period + "-05", type: "入库", code: "RK2024001", debit: 0, credit: 8000, balance: row.beginBalance + 8000, remark: "" },
    { date: row.period + "-10", type: "付款", code: "FK2024001", debit: 5000, credit: 0, balance: row.beginBalance + 3000, remark: "" },
    { date: row.period + "-15", type: "入库", code: "RK2024002", credit: 12000, balance: row.beginBalance + 15000, remark: "" },
    { date: row.period + "-20", type: "退货", code: "TH2024001", debit: 2000, credit: 0, balance: row.beginBalance + 13000, remark: "" },
    { date: row.period + "-25", type: "付款", code: "FK2024002", debit: row.currentPayment - 5000, balance: row.endBalance, remark: "" },
  ];
  detailDialogVisible.value = true;
};
const printStatement = (row) => {
  ElMessage.info(`打印对账单: ${row.statementCode}`);
};
const printDetail = () => {
  ElMessage.info("打印明细");
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
.text-success {
  color: #67c23a;
}
.text-danger {
  color: #f56c6c;
}
.statement-header {
  text-align: center;
  margin-bottom: 20px;
  h3 {
    margin: 0 0 10px 0;
  }
  p {
    color: #909399;
    margin: 0;
  }
}
</style>
src/views/financialManagement/receivable/invoiceApply.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,358 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="申请单号:">
        <el-input v-model="filters.applyCode" placeholder="请输入申请单号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="客户:">
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="状态:">
        <el-select v-model="filters.status" placeholder="请选择状态" clearable style="width: 150px;">
          <el-option label="待审核" value="pending" />
          <el-option label="已审核" value="approved" />
          <el-option label="已驳回" value="rejected" />
          <el-option label="已开票" value="invoiced" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增申请</el-button>
          <el-button @click="handleBatchApply" icon="Document" :disabled="selectedRows.length === 0">批量申请</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        isSelection
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @selection-change="handleSelectionChange"
        @pagination="changePage"
      >
        <template #amount="{ row }">
          <span class="text-primary">Â¥{{ formatMoney(row.amount) }}</span>
        </template>
        <template #taxRate="{ row }">
          <span>{{ row.taxRate }}%</span>
        </template>
        <template #status="{ row }">
          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">编辑</el-button>
          <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">审核</el-button>
          <el-button type="warning" link @click="handleInvoice(row)" v-if="row.status === 'approved'">开票</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="申请单号" prop="applyCode">
              <el-input v-model="form.applyCode" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择客户" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="开票金额" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="税率" prop="taxRate">
              <el-select v-model="form.taxRate" placeholder="请选择税率" style="width: 100%;">
                <el-option label="0%" :value="0" />
                <el-option label="3%" :value="3" />
                <el-option label="6%" :value="6" />
                <el-option label="9%" :value="9" />
                <el-option label="13%" :value="13" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="发票类型" prop="invoiceType">
              <el-select v-model="form.invoiceType" placeholder="请选择发票类型" style="width: 100%;">
                <el-option label="增值税专用发票" value="special" />
                <el-option label="增值税普通发票" value="normal" />
                <el-option label="电子发票" value="electronic" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="申请日期" prop="applyDate">
              <el-date-picker v-model="form.applyDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="发票内容" prop="content">
          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="请输入发票内容" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "开票申请",
});
const filters = reactive({
  applyCode: "",
  customerId: "",
  status: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "申请单号", prop: "applyCode", width: "150" },
  { label: "客户名称", prop: "customerName", width: "180" },
  { label: "开票金额", prop: "amount", slot: "amount" },
  { label: "税率", prop: "taxRate", slot: "taxRate" },
  { label: "发票类型", prop: "invoiceTypeLabel", width: "130" },
  { label: "申请日期", prop: "applyDate", width: "120" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "操作", prop: "operation", slot: "operation", width: "200", fixed: "right" },
];
const dataList = ref([]);
const selectedRows = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
  { id: 3, name: "广州实业有限公司" },
  { id: 4, name: "深圳电子公司" },
];
const form = reactive({
  applyCode: "",
  customerId: "",
  amount: 0,
  taxRate: 13,
  invoiceType: "special",
  applyDate: "",
  content: "",
  remark: "",
});
const rules = {
  customerId: [{ required: true, message: "请选择客户", trigger: "change" }],
  amount: [{ required: true, message: "请输入开票金额", trigger: "blur" }],
  taxRate: [{ required: true, message: "请选择税率", trigger: "change" }],
  invoiceType: [{ required: true, message: "请选择发票类型", trigger: "change" }],
  applyDate: [{ required: true, message: "请选择申请日期", trigger: "change" }],
};
const mockData = [
  { id: 1, applyCode: "KP2024001", customerId: 1, customerName: "北京科技有限公司", amount: 5000, taxRate: 13, invoiceType: "special", invoiceTypeLabel: "增值税专用发票", applyDate: "2024-01-15", status: "pending", content: "软件服务费", remark: "" },
  { id: 2, applyCode: "KP2024002", customerId: 2, customerName: "上海贸易公司", amount: 8000, taxRate: 13, invoiceType: "normal", invoiceTypeLabel: "增值税普通发票", applyDate: "2024-01-16", status: "approved", content: "商品销售", remark: "" },
  { id: 3, applyCode: "KP2024003", customerId: 3, customerName: "广州实业有限公司", amount: 12000, taxRate: 6, invoiceType: "electronic", invoiceTypeLabel: "电子发票", applyDate: "2024-01-18", status: "invoiced", content: "技术服务费", remark: "" },
];
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getStatusLabel = (status) => {
  const map = { pending: "待审核", approved: "已审核", rejected: "已驳回", invoiced: "已开票" };
  return map[status] || status;
};
const getStatusType = (status) => {
  const map = { pending: "warning", approved: "success", rejected: "danger", invoiced: "primary" };
  return map[status] || "";
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.applyCode) {
    result = result.filter(item => item.applyCode.includes(filters.applyCode));
  }
  if (filters.customerId) {
    result = result.filter(item => item.customerId === filters.customerId);
  }
  if (filters.status) {
    result = result.filter(item => item.status === filters.status);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.applyCode = "";
  filters.customerId = "";
  filters.status = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增开票申请";
  Object.assign(form, {
    applyCode: "KP" + Date.now().toString().slice(-8),
    customerId: "",
    amount: 0,
    taxRate: 13,
    invoiceType: "special",
    applyDate: new Date().toISOString().split('T')[0],
    content: "",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑开票申请";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看申请单: ${row.applyCode}`);
};
const handleAudit = (row) => {
  ElMessageBox.confirm("确认审核通过该开票申请吗?", "提示", {
    confirmButtonText: "通过",
    cancelButtonText: "驳回",
    distinguishCancelAndClose: true,
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "approved";
    }
    ElMessage.success("审核通过");
    getTableData();
  }).catch((action) => {
    if (action === "cancel") {
      const index = mockData.findIndex(item => item.id === row.id);
      if (index !== -1) {
        mockData[index].status = "rejected";
      }
      ElMessage.warning("已驳回");
      getTableData();
    }
  });
};
const handleInvoice = (row) => {
  ElMessageBox.confirm("确认已开具发票?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "info",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "invoiced";
    }
    ElMessage.success("开票完成");
    getTableData();
  });
};
const handleBatchApply = () => {
  ElMessage.success(`批量申请 ${selectedRows.value.length} æ¡è®°å½•`);
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const customer = customerList.find(item => item.id === form.customerId);
      const invoiceTypeMap = { special: "增值税专用发票", normal: "增值税普通发票", electronic: "电子发票" };
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType], status: "pending" });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
.text-primary {
  color: #409eff;
  font-weight: bold;
}
</style>
src/views/financialManagement/receivable/outputInvoice.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,368 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="发票代码:">
        <el-input v-model="filters.invoiceCode" placeholder="请输入发票代码" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="发票号码:">
        <el-input v-model="filters.invoiceNo" placeholder="请输入发票号码" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="客户:">
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">录入发票</el-button>
          <el-button @click="handleImport" icon="Upload">导入</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #amount="{ row }">
          <span class="text-primary">Â¥{{ formatMoney(row.amount) }}</span>
        </template>
        <template #taxAmount="{ row }">
          <span class="text-danger">Â¥{{ formatMoney(row.taxAmount) }}</span>
        </template>
        <template #totalAmount="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.totalAmount) }}</span>
        </template>
        <template #invoiceType="{ row }">
          <el-tag :type="row.invoiceType === 'special' ? 'danger' : 'primary'">{{ row.invoiceTypeLabel }}</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)">编辑</el-button>
          <el-button type="danger" link @click="handleDelete(row)">作废</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="发票代码" prop="invoiceCode">
              <el-input v-model="form.invoiceCode" placeholder="请输入发票代码" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="发票号码" prop="invoiceNo">
              <el-input v-model="form.invoiceNo" placeholder="请输入发票号码" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="客户" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择客户" style="width: 100%;">
                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="开票日期" prop="invoiceDate">
              <el-date-picker v-model="form.invoiceDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="发票类型" prop="invoiceType">
              <el-select v-model="form.invoiceType" placeholder="请选择发票类型" style="width: 100%;" @change="handleInvoiceTypeChange">
                <el-option label="增值税专用发票" value="special" />
                <el-option label="增值税普通发票" value="normal" />
                <el-option label="电子发票" value="electronic" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="税率" prop="taxRate">
              <el-select v-model="form.taxRate" placeholder="请选择税率" style="width: 100%;" @change="calculateTax">
                <el-option label="0%" :value="0" />
                <el-option label="3%" :value="3" />
                <el-option label="6%" :value="6" />
                <el-option label="9%" :value="9" />
                <el-option label="13%" :value="13" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="金额(不含税)" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" @change="calculateTax" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="税额">
              <el-input v-model="form.taxAmount" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="价税合计">
              <el-input v-model="form.totalAmount" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="发票内容" prop="content">
          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="请输入发票内容" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "销项发票",
});
const filters = reactive({
  invoiceCode: "",
  invoiceNo: "",
  customerId: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "发票代码", prop: "invoiceCode", width: "130" },
  { label: "发票号码", prop: "invoiceNo", width: "120" },
  { label: "客户名称", prop: "customerName", width: "180" },
  { label: "开票日期", prop: "invoiceDate", width: "120" },
  { label: "金额", prop: "amount", slot: "amount" },
  { label: "税额", prop: "taxAmount", slot: "taxAmount" },
  { label: "价税合计", prop: "totalAmount", slot: "totalAmount" },
  { label: "发票类型", prop: "invoiceType", slot: "invoiceType" },
  { label: "操作", prop: "operation", slot: "operation", width: "180", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
  { id: 3, name: "广州实业有限公司" },
  { id: 4, name: "深圳电子公司" },
];
const form = reactive({
  invoiceCode: "",
  invoiceNo: "",
  customerId: "",
  invoiceDate: "",
  invoiceType: "special",
  taxRate: 13,
  amount: 0,
  taxAmount: 0,
  totalAmount: 0,
  content: "",
  remark: "",
});
const rules = {
  invoiceCode: [{ required: true, message: "请输入发票代码", trigger: "blur" }],
  invoiceNo: [{ required: true, message: "请输入发票号码", trigger: "blur" }],
  customerId: [{ required: true, message: "请选择客户", trigger: "change" }],
  invoiceDate: [{ required: true, message: "请选择开票日期", trigger: "change" }],
  invoiceType: [{ required: true, message: "请选择发票类型", trigger: "change" }],
  taxRate: [{ required: true, message: "请选择税率", trigger: "change" }],
  amount: [{ required: true, message: "请输入金额", trigger: "blur" }],
};
const mockData = [
  { id: 1, invoiceCode: "0440021001", invoiceNo: "12345678", customerId: 1, customerName: "北京科技有限公司", invoiceDate: "2024-01-15", amount: 5000, taxRate: 13, taxAmount: 650, totalAmount: 5650, invoiceType: "special", invoiceTypeLabel: "增值税专用发票", content: "软件服务费", remark: "" },
  { id: 2, invoiceCode: "0440021002", invoiceNo: "87654321", customerId: 2, customerName: "上海贸易公司", invoiceDate: "2024-01-16", amount: 8000, taxRate: 13, taxAmount: 1040, totalAmount: 9040, invoiceType: "normal", invoiceTypeLabel: "增值税普通发票", content: "商品销售", remark: "" },
  { id: 3, invoiceCode: "0440021003", invoiceNo: "11112222", customerId: 3, customerName: "广州实业有限公司", invoiceDate: "2024-01-18", amount: 12000, taxRate: 6, taxAmount: 720, totalAmount: 12720, invoiceType: "electronic", invoiceTypeLabel: "电子发票", content: "技术服务费", remark: "" },
];
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const calculateTax = () => {
  form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(2));
  form.totalAmount = Number((form.amount + form.taxAmount).toFixed(2));
};
const handleInvoiceTypeChange = () => {
  if (form.invoiceType === "special") {
    form.taxRate = 13;
  } else {
    form.taxRate = 13;
  }
  calculateTax();
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.invoiceCode) {
    result = result.filter(item => item.invoiceCode.includes(filters.invoiceCode));
  }
  if (filters.invoiceNo) {
    result = result.filter(item => item.invoiceNo.includes(filters.invoiceNo));
  }
  if (filters.customerId) {
    result = result.filter(item => item.customerId === filters.customerId);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.invoiceCode = "";
  filters.invoiceNo = "";
  filters.customerId = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "录入发票";
  Object.assign(form, {
    invoiceCode: "",
    invoiceNo: "",
    customerId: "",
    invoiceDate: new Date().toISOString().split('T')[0],
    invoiceType: "special",
    taxRate: 13,
    amount: 0,
    taxAmount: 0,
    totalAmount: 0,
    content: "",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑发票";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看发票: ${row.invoiceCode}-${row.invoiceNo}`);
};
const handleDelete = (row) => {
  ElMessageBox.confirm("确认作废该发票吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData.splice(index, 1);
    }
    ElMessage.success("作废成功");
    getTableData();
  });
};
const handleImport = () => {
  ElMessage.info("导入功能");
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const customer = customerList.find(item => item.id === form.customerId);
      const invoiceTypeMap = { special: "增值税专用发票", normal: "增值税普通发票", electronic: "电子发票" };
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] });
        ElMessage.success("录入成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
.text-primary {
  color: #409eff;
  font-weight: bold;
}
.text-danger {
  color: #f56c6c;
  font-weight: bold;
}
.text-success {
  color: #67c23a;
  font-weight: bold;
}
</style>
src/views/financialManagement/receivable/receipt.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,355 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="收款单号:">
        <el-input v-model="filters.receiptCode" placeholder="请输入收款单号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="客户:">
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="收款方式:">
        <el-select v-model="filters.receiptMethod" placeholder="请选择收款方式" clearable style="width: 150px;">
          <el-option label="银行转账" value="bank_transfer" />
          <el-option label="现金" value="cash" />
          <el-option label="支票" value="check" />
          <el-option label="汇票" value="draft" />
          <el-option label="支付宝" value="alipay" />
          <el-option label="微信" value="wechat" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div>
          <el-statistic title="本期收款合计" :value="totalReceiptAmount" precision="2" prefix="Â¥" />
        </div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增收款</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #amount="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.amount) }}</span>
        </template>
        <template #receiptMethod="{ row }">
          <el-tag>{{ getReceiptMethodLabel(row.receiptMethod) }}</el-tag>
        </template>
        <template #status="{ row }">
          <el-tag :type="row.status === 'confirmed' ? 'success' : 'warning'">{{ row.status === 'confirmed' ? '已确认' : '待确认' }}</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">编辑</el-button>
          <el-button type="success" link @click="handleConfirm(row)" v-if="row.status === 'pending'">确认</el-button>
          <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">删除</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="收款单号" prop="receiptCode">
              <el-input v-model="form.receiptCode" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择客户" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="收款日期" prop="receiptDate">
              <el-date-picker v-model="form.receiptDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="收款金额" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="收款方式" prop="receiptMethod">
              <el-select v-model="form.receiptMethod" placeholder="请选择收款方式" style="width: 100%;">
                <el-option label="银行转账" value="bank_transfer" />
                <el-option label="现金" value="cash" />
                <el-option label="支票" value="check" />
                <el-option label="汇票" value="draft" />
                <el-option label="支付宝" value="alipay" />
                <el-option label="微信" value="wechat" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="银行账号" prop="bankAccount" v-if="form.receiptMethod === 'bank_transfer'">
              <el-input v-model="form.bankAccount" placeholder="请输入银行账号" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="关联单据" prop="relatedDocs">
          <el-select v-model="form.relatedDocs" multiple placeholder="请选择关联单据" style="width: 100%;">
            <el-option v-for="item in outList" :key="item.outCode" :label="item.outCode" :value="item.outCode" />
          </el-select>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "收款单",
});
const filters = reactive({
  receiptCode: "",
  customerId: "",
  receiptMethod: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "收款单号", prop: "receiptCode", width: "150" },
  { label: "客户名称", prop: "customerName", width: "180" },
  { label: "收款日期", prop: "receiptDate", width: "120" },
  { label: "收款金额", prop: "amount", slot: "amount" },
  { label: "收款方式", prop: "receiptMethod", slot: "receiptMethod" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "备注", prop: "remark", showOverflowTooltip: true },
  { label: "操作", prop: "operation", slot: "operation", width: "220", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
  { id: 3, name: "广州实业有限公司" },
  { id: 4, name: "深圳电子公司" },
];
const outList = [
  { outCode: "CK2024001", customerId: 1 },
  { outCode: "CK2024002", customerId: 2 },
  { outCode: "CK2024003", customerId: 3 },
];
const form = reactive({
  receiptCode: "",
  customerId: "",
  receiptDate: "",
  amount: 0,
  receiptMethod: "bank_transfer",
  bankAccount: "",
  relatedDocs: [],
  remark: "",
});
const rules = {
  customerId: [{ required: true, message: "请选择客户", trigger: "change" }],
  receiptDate: [{ required: true, message: "请选择收款日期", trigger: "change" }],
  amount: [{ required: true, message: "请输入收款金额", trigger: "blur" }],
  receiptMethod: [{ required: true, message: "请选择收款方式", trigger: "change" }],
};
const mockData = [
  { id: 1, receiptCode: "SK2024001", customerId: 1, customerName: "北京科技有限公司", receiptDate: "2024-01-16", amount: 3000, receiptMethod: "bank_transfer", status: "confirmed", relatedDocs: ["CK2024001"], remark: "" },
  { id: 2, receiptCode: "SK2024002", customerId: 2, customerName: "上海贸易公司", receiptDate: "2024-01-18", amount: 5000, receiptMethod: "cash", status: "pending", relatedDocs: ["CK2024002"], remark: "" },
  { id: 3, receiptCode: "SK2024003", customerId: 3, customerName: "广州实业有限公司", receiptDate: "2024-01-20", amount: 8000, receiptMethod: "alipay", status: "confirmed", relatedDocs: ["CK2024003"], remark: "" },
];
const totalReceiptAmount = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.amount), 0);
});
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getReceiptMethodLabel = (method) => {
  const map = {
    bank_transfer: "银行转账",
    cash: "现金",
    check: "支票",
    draft: "汇票",
    alipay: "支付宝",
    wechat: "微信",
  };
  return map[method] || method;
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.receiptCode) {
    result = result.filter(item => item.receiptCode.includes(filters.receiptCode));
  }
  if (filters.customerId) {
    result = result.filter(item => item.customerId === filters.customerId);
  }
  if (filters.receiptMethod) {
    result = result.filter(item => item.receiptMethod === filters.receiptMethod);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.receiptCode = "";
  filters.customerId = "";
  filters.receiptMethod = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增收款";
  Object.assign(form, {
    receiptCode: "SK" + Date.now().toString().slice(-8),
    customerId: "",
    receiptDate: new Date().toISOString().split('T')[0],
    amount: 0,
    receiptMethod: "bank_transfer",
    bankAccount: "",
    relatedDocs: [],
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑收款";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看收款单: ${row.receiptCode}`);
};
const handleConfirm = (row) => {
  ElMessageBox.confirm("确认该收款单吗?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "info",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "confirmed";
    }
    ElMessage.success("确认成功");
    getTableData();
  });
};
const handleDelete = (row) => {
  ElMessageBox.confirm("确认删除该收款单吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData.splice(index, 1);
    }
    ElMessage.success("删除成功");
    getTableData();
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const customer = customerList.find(item => item.id === form.customerId);
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
}
.text-success {
  color: #67c23a;
  font-weight: bold;
}
</style>
src/views/financialManagement/receivable/reconciliation.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,258 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="客户:">
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="对账期间:">
        <el-date-picker v-model="filters.startMonth" type="month" placeholder="开始月份" value-format="YYYY-MM" style="width: 140px;" />
        <span style="margin: 0 10px;">至</span>
        <el-date-picker v-model="filters.endMonth" type="month" placeholder="结束月份" value-format="YYYY-MM" style="width: 140px;" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div>
          <el-button type="primary" @click="generateStatement" icon="Document">生成对账单</el-button>
        </div>
        <div>
          <el-button @click="handleOut" icon="Download">导出对账单</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #beginBalance="{ row }">
          <span :class="row.beginBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.beginBalance) }}</span>
        </template>
        <template #currentReceivable="{ row }">
          <span class="text-primary">Â¥{{ formatMoney(row.currentReceivable) }}</span>
        </template>
        <template #currentReceipt="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.currentReceipt) }}</span>
        </template>
        <template #endBalance="{ row }">
          <span :class="row.endBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.endBalance) }}</span>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="viewDetail(row)">查看明细</el-button>
          <el-button type="primary" link @click="printStatement(row)">打印</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog title="对账明细" v-model="detailDialogVisible" width="900px" append-to-body>
      <div class="statement-header">
        <h3>{{ currentCustomer }} åº”收对账单</h3>
        <p>对账期间: {{ currentPeriod }}</p>
      </div>
      <el-table :data="detailData" border style="width: 100%">
        <el-table-column prop="date" label="日期" width="120" />
        <el-table-column prop="type" label="类型" width="100">
          <template #default="{ row }">
            <el-tag :type="row.type === '出库' ? 'success' : row.type === '退货' ? 'danger' : 'primary'">{{ row.type }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="code" label="单据编号" width="150" />
        <el-table-column prop="debit" label="借方(应收)" width="120">
          <template #default="{ row }">
            <span v-if="row.debit > 0" class="text-danger">Â¥{{ formatMoney(row.debit) }}</span>
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column prop="credit" label="贷方(收款)" width="120">
          <template #default="{ row }">
            <span v-if="row.credit > 0" class="text-success">Â¥{{ formatMoney(row.credit) }}</span>
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column prop="balance" label="余额" width="120">
          <template #default="{ row }">
            <span :class="row.balance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.balance) }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="remark" label="备注" show-overflow-tooltip />
      </el-table>
      <template #footer>
        <el-button @click="detailDialogVisible = false">关闭</el-button>
        <el-button type="primary" @click="printDetail">打印</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage } from "element-plus";
defineOptions({
  name: "应收对账",
});
const filters = reactive({
  customerId: "",
  startMonth: "",
  endMonth: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "对账单号", prop: "statementCode", width: "150" },
  { label: "客户名称", prop: "customerName", width: "180" },
  { label: "对账期间", prop: "period", width: "150" },
  { label: "期初余额", prop: "beginBalance", slot: "beginBalance" },
  { label: "本期应收", prop: "currentReceivable", slot: "currentReceivable" },
  { label: "本期收款", prop: "currentReceipt", slot: "currentReceipt" },
  { label: "期末余额", prop: "endBalance", slot: "endBalance" },
  { label: "操作", prop: "operation", slot: "operation", width: "150", fixed: "right" },
];
const dataList = ref([]);
const detailDialogVisible = ref(false);
const currentCustomer = ref("");
const currentPeriod = ref("");
const detailData = ref([]);
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
  { id: 3, name: "广州实业有限公司" },
  { id: 4, name: "深圳电子公司" },
];
const mockData = [
  { id: 1, statementCode: "DZ202401001", customerId: 1, customerName: "北京科技有限公司", period: "2024-01", beginBalance: 10000, currentReceivable: 15000, currentReceipt: 8000, endBalance: 17000 },
  { id: 2, statementCode: "DZ202401002", customerId: 2, customerName: "上海贸易公司", period: "2024-01", beginBalance: 5000, currentReceivable: 12000, currentReceipt: 10000, endBalance: 7000 },
  { id: 3, statementCode: "DZ202402001", customerId: 1, customerName: "北京科技有限公司", period: "2024-02", beginBalance: 17000, currentReceivable: 20000, currentReceipt: 15000, endBalance: 22000 },
];
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.customerId) {
    result = result.filter(item => item.customerId === filters.customerId);
  }
  if (filters.startMonth && filters.endMonth) {
    result = result.filter(item => item.period >= filters.startMonth && item.period <= filters.endMonth);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.customerId = "";
  filters.startMonth = "";
  filters.endMonth = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const generateStatement = () => {
  ElMessage.success("对账单生成成功");
  const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
  const customer = customerList[Math.floor(Math.random() * customerList.length)];
  mockData.unshift({
    id: newId,
    statementCode: "DZ" + Date.now(),
    customerId: customer.id,
    customerName: customer.name,
    period: "2024-03",
    beginBalance: Math.floor(Math.random() * 10000),
    currentReceivable: Math.floor(Math.random() * 20000),
    currentReceipt: Math.floor(Math.random() * 15000),
    endBalance: Math.floor(Math.random() * 20000),
  });
  getTableData();
};
const viewDetail = (row) => {
  currentCustomer.value = row.customerName;
  currentPeriod.value = row.period;
  detailData.value = [
    { date: row.period + "-01", type: "期初", code: "-", debit: 0, credit: 0, balance: row.beginBalance, remark: "期初余额" },
    { date: row.period + "-05", type: "出库", code: "CK2024001", debit: 5000, credit: 0, balance: row.beginBalance + 5000, remark: "" },
    { date: row.period + "-10", type: "收款", code: "SK2024001", debit: 0, credit: 3000, balance: row.beginBalance + 2000, remark: "" },
    { date: row.period + "-15", type: "出库", code: "CK2024002", debit: 8000, credit: 0, balance: row.beginBalance + 10000, remark: "" },
    { date: row.period + "-20", type: "退货", code: "TH2024001", debit: 0, credit: 2000, balance: row.beginBalance + 8000, remark: "" },
    { date: row.period + "-25", type: "收款", code: "SK2024002", credit: row.currentReceipt - 3000, balance: row.endBalance, remark: "" },
  ];
  detailDialogVisible.value = true;
};
const printStatement = (row) => {
  ElMessage.info(`打印对账单: ${row.statementCode}`);
};
const printDetail = () => {
  ElMessage.info("打印明细");
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
.text-success {
  color: #67c23a;
}
.text-danger {
  color: #f56c6c;
}
.text-primary {
  color: #409eff;
}
.statement-header {
  text-align: center;
  margin-bottom: 20px;
  h3 {
    margin: 0 0 10px 0;
  }
  p {
    color: #909399;
    margin: 0;
  }
}
</style>
src/views/financialManagement/receivable/salesOut.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,271 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="出库单号:">
        <el-input v-model="filters.outCode" placeholder="请输入出库单号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="客户:">
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="出库日期:">
        <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" clearable />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增出库</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #status="{ row }">
          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">编辑</el-button>
          <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">删除</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="出库单号" prop="outCode">
              <el-input v-model="form.outCode" placeholder="请输入出库单号" :disabled="isEdit" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择客户" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="出库日期" prop="outDate">
              <el-date-picker v-model="form.outDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="金额" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "销售出库",
});
const filters = reactive({
  outCode: "",
  customerId: "",
  dateRange: [],
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "出库单号", prop: "outCode", width: "150" },
  { label: "客户名称", prop: "customerName", width: "180" },
  { label: "出库日期", prop: "outDate", width: "120" },
  { label: "金额", prop: "amount", width: "120" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "备注", prop: "remark", showOverflowTooltip: true },
  { label: "操作", prop: "operation", slot: "operation", width: "200", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
  { id: 3, name: "广州实业有限公司" },
  { id: 4, name: "深圳电子公司" },
];
const form = reactive({
  outCode: "",
  customerId: "",
  outDate: "",
  amount: 0,
  remark: "",
});
const rules = {
  outCode: [{ required: true, message: "请输入出库单号", trigger: "blur" }],
  customerId: [{ required: true, message: "请选择客户", trigger: "change" }],
  outDate: [{ required: true, message: "请选择出库日期", trigger: "change" }],
  amount: [{ required: true, message: "请输入金额", trigger: "blur" }],
};
const mockData = [
  { id: 1, outCode: "CK2024001", customerId: 1, customerName: "北京科技有限公司", outDate: "2024-01-15", amount: 5000, status: "approved", remark: "" },
  { id: 2, outCode: "CK2024002", customerId: 2, customerName: "上海贸易公司", outDate: "2024-01-16", amount: 8000, status: "pending", remark: "" },
  { id: 3, outCode: "CK2024003", customerId: 3, customerName: "广州实业有限公司", outDate: "2024-01-18", amount: 12000, status: "approved", remark: "" },
  { id: 4, outCode: "CK2024004", customerId: 4, customerName: "深圳电子公司", outDate: "2024-01-20", amount: 3500, status: "pending", remark: "" },
];
const getStatusLabel = (status) => {
  const map = { pending: "待审核", approved: "已审核", rejected: "已驳回" };
  return map[status] || status;
};
const getStatusType = (status) => {
  const map = { pending: "warning", approved: "success", rejected: "danger" };
  return map[status] || "";
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.outCode) {
    result = result.filter(item => item.outCode.includes(filters.outCode));
  }
  if (filters.customerId) {
    result = result.filter(item => item.customerId === filters.customerId);
  }
  if (filters.dateRange && filters.dateRange.length === 2) {
    result = result.filter(item => item.outDate >= filters.dateRange[0] && item.outDate <= filters.dateRange[1]);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.outCode = "";
  filters.customerId = "";
  filters.dateRange = [];
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增出库";
  Object.assign(form, {
    outCode: "CK" + Date.now(),
    customerId: "",
    outDate: "",
    amount: 0,
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑出库";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看出库单: ${row.outCode}`);
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const customer = customerList.find(item => item.id === form.customerId);
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
const handleDelete = (row) => {
  ElMessageBox.confirm("确认删除该出库单吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData.splice(index, 1);
    }
    ElMessage.success("删除成功");
    getTableData();
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
</style>
src/views/financialManagement/receivable/salesReturn.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,305 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="退货单号:">
        <el-input v-model="filters.returnCode" placeholder="请输入退货单号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="客户:">
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="退货日期:">
        <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" clearable />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增退货</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #status="{ row }">
          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">编辑</el-button>
          <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">审核</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="退货单号" prop="returnCode">
              <el-input v-model="form.returnCode" placeholder="请输入退货单号" :disabled="isEdit" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="关联出库单" prop="outCode">
              <el-select v-model="form.outCode" placeholder="请选择出库单" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in outList" :key="item.outCode" :label="item.outCode" :value="item.outCode" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="客户" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择客户" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="退货日期" prop="returnDate">
              <el-date-picker v-model="form.returnDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="退货金额" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="退货原因" prop="reason">
              <el-input v-model="form.reason" placeholder="请输入退货原因" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "销售退货",
});
const filters = reactive({
  returnCode: "",
  customerId: "",
  dateRange: [],
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "退货单号", prop: "returnCode", width: "150" },
  { label: "客户名称", prop: "customerName", width: "180" },
  { label: "关联出库单", prop: "outCode", width: "150" },
  { label: "退货日期", prop: "returnDate", width: "120" },
  { label: "退货金额", prop: "amount", width: "120" },
  { label: "退货原因", prop: "reason", width: "150", showOverflowTooltip: true },
  { label: "状态", prop: "status", slot: "status" },
  { label: "操作", prop: "operation", slot: "operation", width: "200", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
  { id: 3, name: "广州实业有限公司" },
  { id: 4, name: "深圳电子公司" },
];
const outList = [
  { outCode: "CK2024001", customerId: 1 },
  { outCode: "CK2024002", customerId: 2 },
  { outCode: "CK2024003", customerId: 3 },
];
const form = reactive({
  returnCode: "",
  outCode: "",
  customerId: "",
  returnDate: "",
  amount: 0,
  reason: "",
  remark: "",
});
const rules = {
  returnCode: [{ required: true, message: "请输入退货单号", trigger: "blur" }],
  outCode: [{ required: true, message: "请选择关联出库单", trigger: "change" }],
  customerId: [{ required: true, message: "请选择客户", trigger: "change" }],
  returnDate: [{ required: true, message: "请选择退货日期", trigger: "change" }],
  amount: [{ required: true, message: "请输入退货金额", trigger: "blur" }],
};
const mockData = [
  { id: 1, returnCode: "TH2024001", outCode: "CK2024001", customerId: 1, customerName: "北京科技有限公司", returnDate: "2024-01-20", amount: 1000, reason: "质量问题", status: "approved", remark: "" },
  { id: 2, returnCode: "TH2024002", outCode: "CK2024002", customerId: 2, customerName: "上海贸易公司", returnDate: "2024-01-22", amount: 500, reason: "规格不符", status: "pending", remark: "" },
];
const getStatusLabel = (status) => {
  const map = { pending: "待审核", approved: "已审核", rejected: "已驳回" };
  return map[status] || status;
};
const getStatusType = (status) => {
  const map = { pending: "warning", approved: "success", rejected: "danger" };
  return map[status] || "";
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.returnCode) {
    result = result.filter(item => item.returnCode.includes(filters.returnCode));
  }
  if (filters.customerId) {
    result = result.filter(item => item.customerId === filters.customerId);
  }
  if (filters.dateRange && filters.dateRange.length === 2) {
    result = result.filter(item => item.returnDate >= filters.dateRange[0] && item.returnDate <= filters.dateRange[1]);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.returnCode = "";
  filters.customerId = "";
  filters.dateRange = [];
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增退货";
  Object.assign(form, {
    returnCode: "TH" + Date.now(),
    outCode: "",
    customerId: "",
    returnDate: "",
    amount: 0,
    reason: "",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑退货";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看退货单: ${row.returnCode}`);
};
const handleAudit = (row) => {
  ElMessageBox.confirm("确认审核通过该退货单吗?", "提示", {
    confirmButtonText: "通过",
    cancelButtonText: "驳回",
    distinguishCancelAndClose: true,
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "approved";
    }
    ElMessage.success("审核通过");
    getTableData();
  }).catch((action) => {
    if (action === "cancel") {
      const index = mockData.findIndex(item => item.id === row.id);
      if (index !== -1) {
        mockData[index].status = "rejected";
      }
      ElMessage.warning("已驳回");
      getTableData();
    }
  });
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const customer = customerList.find(item => item.id === form.customerId);
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
</style>
src/views/financialManagement/voucher/detailLedger.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,289 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="会计科目:">
        <el-cascader v-model="filters.subject" :options="subjectOptions" :props="{ label: 'name', value: 'code' }" placeholder="请选择会计科目" clearable style="width: 250px;" filterable />
      </el-form-item>
      <el-form-item label="辅助核算:">
        <el-select v-model="filters.auxiliary" placeholder="请选择辅助核算" clearable style="width: 180px;">
          <el-option label="客户" value="customer" />
          <el-option label="供应商" value="supplier" />
          <el-option label="部门" value="department" />
          <el-option label="员工" value="employee" />
          <el-option label="项目" value="project" />
        </el-select>
      </el-form-item>
      <el-form-item label="核算对象:">
        <el-select v-model="filters.auxiliaryItem" placeholder="请选择核算对象" clearable style="width: 200px;" :disabled="!filters.auxiliary">
          <el-option v-for="item in auxiliaryItems" :key="item.value" :label="item.label" :value="item.value" />
        </el-select>
      </el-form-item>
      <el-form-item label="期间:">
        <el-date-picker v-model="filters.startMonth" type="month" placeholder="开始月份" value-format="YYYY-MM" style="width: 140px;" />
        <span style="margin: 0 10px;">至</span>
        <el-date-picker v-model="filters.endMonth" type="month" placeholder="结束月份" value-format="YYYY-MM" style="width: 140px;" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">查询</el-button>
        <el-button @click="resetFilters">重置</el-button>
        <el-button @click="handlePrint" icon="Printer">打印</el-button>
        <el-button @click="handleOut" icon="Download">导出</el-button>
      </el-form-item>
    </el-form>
    <div class="ledger-header" v-if="currentSubject">
      <h2>科目明细账</h2>
      <p>科目: {{ currentSubject.code }} {{ currentSubject.name }}</p>
      <p v-if="filters.auxiliary && filters.auxiliaryItem">辅助核算: {{ getAuxiliaryLabel() }}</p>
      <p>期间: {{ filters.startMonth }} è‡³ {{ filters.endMonth }}</p>
    </div>
    <div class="table_list">
      <el-table :data="dataList" border style="width: 100%" show-summary :summary-method="getSummaries">
        <el-table-column prop="date" label="日期" width="120" />
        <el-table-column prop="voucherNo" label="凭证字号" width="120" />
        <el-table-column prop="summary" label="摘要" min-width="200" show-overflow-tooltip />
        <el-table-column label="借方" width="150">
          <template #default="{ row }">
            <span v-if="row.debit > 0" class="text-danger">Â¥{{ formatMoney(row.debit) }}</span>
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column label="贷方" width="150">
          <template #default="{ row }">
            <span v-if="row.credit > 0" class="text-success">Â¥{{ formatMoney(row.credit) }}</span>
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column label="方向" width="80">
          <template #default="{ row }">
            <el-tag :type="row.direction === '借' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="余额" width="150">
          <template #default="{ row }">
            <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">Â¥{{ formatMoney(Math.abs(row.balance)) }}</span>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <el-empty v-if="!currentSubject" description="请选择会计科目查询" style="margin-top: 50px;" />
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed, watch } from "vue";
import { ElMessage } from "element-plus";
defineOptions({
  name: "科目明细账",
});
const filters = reactive({
  subject: [],
  auxiliary: "",
  auxiliaryItem: "",
  startMonth: "2024-01",
  endMonth: "2024-03",
});
const dataList = ref([]);
const subjectOptions = [
  {
    code: "1122",
    name: "应收账款",
    children: [
      { code: "112201", name: "北京科技有限公司" },
      { code: "112202", name: "上海贸易公司" },
      { code: "112203", name: "广州实业有限公司" },
    ],
  },
  {
    code: "2202",
    name: "应付账款",
    children: [
      { code: "220201", name: "北京原材料供应商" },
      { code: "220202", name: "上海电子元器件公司" },
      { code: "220203", name: "广州包装材料厂" },
    ],
  },
  {
    code: "6602",
    name: "管理费用",
    children: [
      { code: "660201", name: "办公费" },
      { code: "660202", name: "差旅费" },
      { code: "660203", name: "业务招待费" },
    ],
  },
];
const auxiliaryItems = computed(() => {
  const map = {
    customer: [
      { value: "1", label: "北京科技有限公司" },
      { value: "2", label: "上海贸易公司" },
      { value: "3", label: "广州实业有限公司" },
    ],
    supplier: [
      { value: "1", label: "北京原材料供应商" },
      { value: "2", label: "上海电子元器件公司" },
      { value: "3", label: "广州包装材料厂" },
    ],
    department: [
      { value: "1", label: "财务部" },
      { value: "2", label: "销售部" },
      { value: "3", label: "采购部" },
    ],
    employee: [
      { value: "1", label: "张三" },
      { value: "2", label: "李四" },
      { value: "3", label: "王五" },
    ],
    project: [
      { value: "1", label: "项目A" },
      { value: "2", label: "项目B" },
      { value: "3", label: "项目C" },
    ],
  };
  return map[filters.auxiliary] || [];
});
watch(() => filters.auxiliary, () => {
  filters.auxiliaryItem = "";
});
const currentSubject = computed(() => {
  if (!filters.subject || filters.subject.length === 0) return null;
  const code = filters.subject[filters.subject.length - 1];
  return findSubject(subjectOptions, code);
});
const findSubject = (options, code) => {
  for (const item of options) {
    if (item.code === code) return item;
    if (item.children && item.children.length > 0) {
      const found = findSubject(item.children, code);
      if (found) return found;
    }
  }
  return null;
};
const getAuxiliaryLabel = () => {
  const item = auxiliaryItems.value.find(i => i.value === filters.auxiliaryItem);
  return item ? item.label : "";
};
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const mockData = [
  { date: "2024-01-01", voucherNo: "-", summary: "期初余额", debit: 0, credit: 0, direction: "借", balance: 10000 },
  { date: "2024-01-05", voucherNo: "è®°-0001", summary: "销售出库", debit: 5000, credit: 0, direction: "借", balance: 15000 },
  { date: "2024-01-10", voucherNo: "è®°-0002", summary: "收到货款", debit: 0, credit: 3000, direction: "借", balance: 12000 },
  { date: "2024-01-15", voucherNo: "è®°-0003", summary: "销售出库", debit: 8000, credit: 0, direction: "借", balance: 20000 },
  { date: "2024-01-20", voucherNo: "è®°-0004", summary: "销售退货", debit: 0, credit: 2000, direction: "借", balance: 18000 },
  { date: "2024-01-25", voucherNo: "è®°-0005", summary: "收到货款", debit: 0, credit: 5000, direction: "借", balance: 13000 },
  { date: "2024-01-31", voucherNo: "-", summary: "本月合计", debit: 13000, credit: 10000, direction: "借", balance: 13000 },
  { date: "2024-02-01", voucherNo: "-", summary: "期初余额", debit: 0, credit: 0, direction: "借", balance: 13000 },
  { date: "2024-02-10", voucherNo: "è®°-0006", summary: "销售出库", debit: 6000, credit: 0, direction: "借", balance: 19000 },
  { date: "2024-02-15", voucherNo: "è®°-0007", summary: "收到货款", debit: 0, credit: 4000, direction: "借", balance: 15000 },
  { date: "2024-02-28", voucherNo: "-", summary: "本月合计", debit: 6000, credit: 4000, direction: "借", balance: 15000 },
  { date: "2024-03-01", voucherNo: "-", summary: "期初余额", debit: 0, credit: 0, direction: "借", balance: 15000 },
  { date: "2024-03-05", voucherNo: "è®°-0008", summary: "销售出库", debit: 7000, credit: 0, direction: "借", balance: 22000 },
  { date: "2024-03-10", voucherNo: "è®°-0009", summary: "收到货款", debit: 0, credit: 6000, direction: "借", balance: 16000 },
  { date: "2024-03-31", voucherNo: "-", summary: "本月合计", debit: 7000, credit: 6000, direction: "借", balance: 16000 },
  { date: "2024-03-31", voucherNo: "-", summary: "本年累计", debit: 26000, credit: 20000, direction: "借", balance: 16000 },
];
const getTableData = () => {
  if (!currentSubject.value) {
    dataList.value = [];
    return;
  }
  dataList.value = [...mockData];
};
const resetFilters = () => {
  filters.subject = [];
  filters.auxiliary = "";
  filters.auxiliaryItem = "";
  filters.startMonth = "2024-01";
  filters.endMonth = "2024-03";
  dataList.value = [];
};
const getSummaries = (param) => {
  const { columns, data } = param;
  const sums = [];
  columns.forEach((column, index) => {
    if (index === 0) {
      sums[index] = "合计";
      return;
    }
    if (column.property === "debit") {
      const values = data.map(item => Number(item.debit));
      const sum = values.reduce((prev, curr) => prev + curr, 0);
      sums[index] = "Â¥" + formatMoney(sum);
    } else if (column.property === "credit") {
      const values = data.map(item => Number(item.credit));
      const sum = values.reduce((prev, curr) => prev + curr, 0);
      sums[index] = "Â¥" + formatMoney(sum);
    } else {
      sums[index] = "";
    }
  });
  return sums;
};
const handlePrint = () => {
  ElMessage.info("打印功能");
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
onMounted(() => {
  // é»˜è®¤ä¸åŠ è½½æ•°æ®ï¼Œéœ€è¦é€‰æ‹©ç§‘ç›®
});
</script>
<style lang="scss" scoped>
.ledger-header {
  text-align: center;
  margin-bottom: 20px;
  h2 {
    margin: 0 0 10px 0;
  }
  p {
    color: #606266;
    margin: 5px 0;
  }
}
.text-primary {
  color: #409eff;
  font-weight: bold;
}
.text-success {
  color: #67c23a;
  font-weight: bold;
}
.text-danger {
  color: #f56c6c;
  font-weight: bold;
}
.text-warning {
  color: #e6a23c;
  font-weight: bold;
}
</style>
src/views/financialManagement/voucher/generalLedger.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,230 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="会计科目:">
        <el-cascader v-model="filters.subject" :options="subjectOptions" :props="{ label: 'name', value: 'code' }" placeholder="请选择会计科目" clearable style="width: 250px;" filterable />
      </el-form-item>
      <el-form-item label="期间:">
        <el-date-picker v-model="filters.startMonth" type="month" placeholder="开始月份" value-format="YYYY-MM" style="width: 140px;" />
        <span style="margin: 0 10px;">至</span>
        <el-date-picker v-model="filters.endMonth" type="month" placeholder="结束月份" value-format="YYYY-MM" style="width: 140px;" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">查询</el-button>
        <el-button @click="resetFilters">重置</el-button>
        <el-button @click="handlePrint" icon="Printer">打印</el-button>
        <el-button @click="handleOut" icon="Download">导出</el-button>
      </el-form-item>
    </el-form>
    <div class="ledger-header" v-if="currentSubject">
      <h2>科目总账</h2>
      <p>科目: {{ currentSubject.code }} {{ currentSubject.name }}</p>
      <p>期间: {{ filters.startMonth }} è‡³ {{ filters.endMonth }}</p>
    </div>
    <div class="table_list">
      <el-table :data="dataList" border style="width: 100%" show-summary :summary-method="getSummaries">
        <el-table-column prop="date" label="日期" width="120" />
        <el-table-column prop="voucherNo" label="凭证字号" width="120" />
        <el-table-column prop="summary" label="摘要" min-width="200" show-overflow-tooltip />
        <el-table-column label="借方" width="150">
          <template #default="{ row }">
            <span v-if="row.debit > 0" class="text-danger">Â¥{{ formatMoney(row.debit) }}</span>
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column label="贷方" width="150">
          <template #default="{ row }">
            <span v-if="row.credit > 0" class="text-success">Â¥{{ formatMoney(row.credit) }}</span>
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column label="方向" width="80">
          <template #default="{ row }">
            <el-tag :type="row.direction === '借' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="余额" width="150">
          <template #default="{ row }">
            <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">Â¥{{ formatMoney(Math.abs(row.balance)) }}</span>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <el-empty v-if="!currentSubject" description="请选择会计科目查询" style="margin-top: 50px;" />
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage } from "element-plus";
defineOptions({
  name: "科目总账",
});
const filters = reactive({
  subject: [],
  startMonth: "2024-01",
  endMonth: "2024-03",
});
const dataList = ref([]);
const subjectOptions = [
  {
    code: "1001",
    name: "库存现金",
    children: [],
  },
  {
    code: "1002",
    name: "银行存款",
    children: [
      { code: "100201", name: "工商银行" },
      { code: "100202", name: "建设银行" },
    ],
  },
  {
    code: "1122",
    name: "应收账款",
    children: [],
  },
  {
    code: "2202",
    name: "应付账款",
    children: [],
  },
  {
    code: "6001",
    name: "主营业务收入",
    children: [],
  },
];
const currentSubject = computed(() => {
  if (!filters.subject || filters.subject.length === 0) return null;
  const code = filters.subject[filters.subject.length - 1];
  return findSubject(subjectOptions, code);
});
const findSubject = (options, code) => {
  for (const item of options) {
    if (item.code === code) return item;
    if (item.children && item.children.length > 0) {
      const found = findSubject(item.children, code);
      if (found) return found;
    }
  }
  return null;
};
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const mockData = [
  { date: "2024-01-01", voucherNo: "-", summary: "期初余额", debit: 0, credit: 0, direction: "借", balance: 100000 },
  { date: "2024-01-05", voucherNo: "è®°-0001", summary: "销售收入", debit: 5650, credit: 0, direction: "借", balance: 105650 },
  { date: "2024-01-10", voucherNo: "è®°-0002", summary: "采购支出", debit: 0, credit: 8000, direction: "借", balance: 97650 },
  { date: "2024-01-15", voucherNo: "è®°-0003", summary: "收到货款", debit: 10000, credit: 0, direction: "借", balance: 107650 },
  { date: "2024-01-20", voucherNo: "è®°-0004", summary: "支付费用", debit: 0, credit: 5000, direction: "借", balance: 102650 },
  { date: "2024-01-31", voucherNo: "-", summary: "本月合计", debit: 15650, credit: 13000, direction: "借", balance: 102650 },
  { date: "2024-02-01", voucherNo: "-", summary: "期初余额", debit: 0, credit: 0, direction: "借", balance: 102650 },
  { date: "2024-02-10", voucherNo: "è®°-0005", summary: "销售收入", debit: 8000, credit: 0, direction: "借", balance: 110650 },
  { date: "2024-02-15", voucherNo: "è®°-0006", summary: "采购支出", debit: 0, credit: 12000, direction: "借", balance: 98650 },
  { date: "2024-02-28", voucherNo: "-", summary: "本月合计", debit: 8000, credit: 12000, direction: "借", balance: 98650 },
  { date: "2024-03-01", voucherNo: "-", summary: "期初余额", debit: 0, credit: 0, direction: "借", balance: 98650 },
  { date: "2024-03-05", voucherNo: "è®°-0007", summary: "销售收入", debit: 12000, credit: 0, direction: "借", balance: 110650 },
  { date: "2024-03-10", voucherNo: "è®°-0008", summary: "支付工资", debit: 0, credit: 15000, direction: "借", balance: 95650 },
  { date: "2024-03-31", voucherNo: "-", summary: "本月合计", debit: 12000, credit: 15000, direction: "借", balance: 95650 },
  { date: "2024-03-31", voucherNo: "-", summary: "本年累计", debit: 35650, credit: 40000, direction: "借", balance: 95650 },
];
const getTableData = () => {
  if (!currentSubject.value) {
    dataList.value = [];
    return;
  }
  dataList.value = [...mockData];
};
const resetFilters = () => {
  filters.subject = [];
  filters.startMonth = "2024-01";
  filters.endMonth = "2024-03";
  dataList.value = [];
};
const getSummaries = (param) => {
  const { columns, data } = param;
  const sums = [];
  columns.forEach((column, index) => {
    if (index === 0) {
      sums[index] = "合计";
      return;
    }
    if (column.property === "debit") {
      const values = data.map(item => Number(item.debit));
      const sum = values.reduce((prev, curr) => prev + curr, 0);
      sums[index] = "Â¥" + formatMoney(sum);
    } else if (column.property === "credit") {
      const values = data.map(item => Number(item.credit));
      const sum = values.reduce((prev, curr) => prev + curr, 0);
      sums[index] = "Â¥" + formatMoney(sum);
    } else {
      sums[index] = "";
    }
  });
  return sums;
};
const handlePrint = () => {
  ElMessage.info("打印功能");
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
onMounted(() => {
  // é»˜è®¤ä¸åŠ è½½æ•°æ®ï¼Œéœ€è¦é€‰æ‹©ç§‘ç›®
});
</script>
<style lang="scss" scoped>
.ledger-header {
  text-align: center;
  margin-bottom: 20px;
  h2 {
    margin: 0 0 10px 0;
  }
  p {
    color: #606266;
    margin: 5px 0;
  }
}
.text-primary {
  color: #409eff;
  font-weight: bold;
}
.text-success {
  color: #67c23a;
  font-weight: bold;
}
.text-danger {
  color: #f56c6c;
  font-weight: bold;
}
.text-warning {
  color: #e6a23c;
  font-weight: bold;
}
</style>
src/views/financialManagement/voucher/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,418 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="凭证字号:">
        <el-input v-model="filters.voucherNo" placeholder="请输入凭证字号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="凭证日期:">
        <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" clearable />
      </el-form-item>
      <el-form-item label="制单人:">
        <el-select v-model="filters.creator" placeholder="请选择制单人" clearable style="width: 150px;">
          <el-option label="张三" value="张三" />
          <el-option label="李四" value="李四" />
          <el-option label="王五" value="王五" />
        </el-select>
      </el-form-item>
      <el-form-item label="状态:">
        <el-select v-model="filters.status" placeholder="请选择状态" clearable style="width: 150px;">
          <el-option label="未过账" value="unposted" />
          <el-option label="已过账" value="posted" />
          <el-option label="已作废" value="cancelled" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div>
          <el-statistic title="借方合计" :value="totalDebit" precision="2" prefix="Â¥" />
          <el-statistic title="贷方合计" :value="totalCredit" precision="2" prefix="Â¥" style="margin-left: 30px;" />
        </div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增凭证</el-button>
          <el-button @click="handleImport" icon="Upload">导入</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
        <template #debit="{ row }">
          <span class="text-danger" v-if="row.debit > 0">Â¥{{ formatMoney(row.debit) }}</span>
          <span v-else>-</span>
        </template>
        <template #credit="{ row }">
          <span class="text-success" v-if="row.credit > 0">Â¥{{ formatMoney(row.credit) }}</span>
          <span v-else>-</span>
        </template>
        <template #status="{ row }">
          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'unposted'">编辑</el-button>
          <el-button type="success" link @click="handlePost(row)" v-if="row.status === 'unposted'">过账</el-button>
          <el-button type="danger" link @click="handleCancel(row)" v-if="row.status === 'unposted'">作废</el-button>
        </template>
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="900px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="凭证字号" prop="voucherNo">
              <el-input v-model="form.voucherNo" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="凭证日期" prop="voucherDate">
              <el-date-picker v-model="form.voucherDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="附件张数" prop="attachmentCount">
              <el-input-number v-model="form.attachmentCount" :min="0" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="凭证分录" prop="entries">
          <el-table :data="form.entries" border style="width: 100%">
            <el-table-column type="index" label="序号" width="60" />
            <el-table-column prop="subjectCode" label="科目编码" width="120">
              <template #default="{ $index }">
                <el-select v-model="form.entries[$index].subjectCode" placeholder="选择科目" filterable style="width: 100%;" @change="(val) => handleSubjectChange(val, $index)">
                  <el-option v-for="item in subjectList" :key="item.code" :label="item.code" :value="item.code" />
                </el-select>
              </template>
            </el-table-column>
            <el-table-column prop="subjectName" label="科目名称" width="150">
              <template #default="{ $index }">
                <el-input v-model="form.entries[$index].subjectName" disabled />
              </template>
            </el-table-column>
            <el-table-column prop="summary" label="摘要">
              <template #default="{ $index }">
                <el-input v-model="form.entries[$index].summary" placeholder="请输入摘要" />
              </template>
            </el-table-column>
            <el-table-column prop="debit" label="借方金额" width="130">
              <template #default="{ $index }">
                <el-input-number v-model="form.entries[$index].debit" :min="0" :precision="2" style="width: 100%;" @change="calculateTotal" />
              </template>
            </el-table-column>
            <el-table-column prop="credit" label="贷方金额" width="130">
              <template #default="{ $index }">
                <el-input-number v-model="form.entries[$index].credit" :min="0" :precision="2" style="width: 100%;" @change="calculateTotal" />
              </template>
            </el-table-column>
            <el-table-column label="操作" width="80">
              <template #default="{ $index }">
                <el-button type="danger" link @click="removeEntry($index)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
          <div style="display: flex; justify-content: space-between; margin-top: 10px;">
            <el-button type="primary" link @click="addEntry">+ æ·»åŠ åˆ†å½•</el-button>
            <div>
              <span style="margin-right: 20px;">合计: å€Ÿæ–¹ <span :class="totalDebitEntry === totalCreditEntry ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(totalDebitEntry) }}</span></span>
              <span>贷方 <span :class="totalDebitEntry === totalCreditEntry ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(totalCreditEntry) }}</span></span>
            </div>
          </div>
        </el-form-item>
        <el-form-item label="制单人" prop="creator">
          <el-input v-model="form.creator" disabled />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm" :disabled="totalDebitEntry !== totalCreditEntry">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "凭证管理",
});
const filters = reactive({
  voucherNo: "",
  dateRange: [],
  creator: "",
  status: "",
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "凭证字号", prop: "voucherNo", width: "120" },
  { label: "凭证日期", prop: "voucherDate", width: "120" },
  { label: "摘要", prop: "summary", showOverflowTooltip: true },
  { label: "借方金额", prop: "debit", slot: "debit" },
  { label: "贷方金额", prop: "credit", slot: "credit" },
  { label: "制单人", prop: "creator", width: "100" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "操作", prop: "operation", slot: "operation", width: "220", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const subjectList = [
  { code: "1001", name: "库存现金" },
  { code: "1002", name: "银行存款" },
  { code: "1122", name: "应收账款" },
  { code: "2202", name: "应付账款" },
  { code: "5001", name: "生产成本" },
  { code: "6001", name: "主营业务收入" },
  { code: "6401", name: "主营业务成本" },
];
const form = reactive({
  voucherNo: "",
  voucherDate: "",
  attachmentCount: 0,
  entries: [],
  creator: "张三",
  remark: "",
});
const rules = {
  voucherDate: [{ required: true, message: "请选择凭证日期", trigger: "change" }],
};
const mockData = [
  { id: 1, voucherNo: "è®°-0001", voucherDate: "2024-01-15", summary: "销售收入", debit: 5650, credit: 5650, creator: "张三", status: "posted", entries: [{ subjectCode: "1002", subjectName: "银行存款", summary: "销售收入", debit: 5650, credit: 0 }, { subjectCode: "6001", subjectName: "主营业务收入", summary: "销售收入", debit: 0, credit: 5000 }, { subjectCode: "2221", subjectName: "应交税费", summary: "销项税额", debit: 0, credit: 650 }] },
  { id: 2, voucherNo: "è®°-0002", voucherDate: "2024-01-16", summary: "采购原材料", debit: 9040, credit: 9040, creator: "李四", status: "unposted", entries: [{ subjectCode: "5001", subjectName: "生产成本", summary: "采购原材料", debit: 8000, credit: 0 }, { subjectCode: "2221", subjectName: "应交税费", summary: "进项税额", debit: 1040, credit: 0 }, { subjectCode: "2202", subjectName: "应付账款", summary: "采购原材料", debit: 0, credit: 9040 }] },
  { id: 3, voucherNo: "è®°-0003", voucherDate: "2024-01-18", summary: "支付货款", debit: 5000, credit: 5000, creator: "张三", status: "posted", entries: [{ subjectCode: "2202", subjectName: "应付账款", summary: "支付货款", debit: 5000, credit: 0 }, { subjectCode: "1002", subjectName: "银行存款", summary: "支付货款", debit: 0, credit: 5000 }] },
];
const totalDebit = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.debit), 0);
});
const totalCredit = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.credit), 0);
});
const totalDebitEntry = computed(() => {
  return form.entries.reduce((sum, item) => sum + Number(item.debit || 0), 0);
});
const totalCreditEntry = computed(() => {
  return form.entries.reduce((sum, item) => sum + Number(item.credit || 0), 0);
});
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getStatusLabel = (status) => {
  const map = { unposted: "未过账", posted: "已过账", cancelled: "已作废" };
  return map[status] || status;
};
const getStatusType = (status) => {
  const map = { unposted: "warning", posted: "success", cancelled: "info" };
  return map[status] || "";
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.voucherNo) {
    result = result.filter(item => item.voucherNo.includes(filters.voucherNo));
  }
  if (filters.dateRange && filters.dateRange.length === 2) {
    result = result.filter(item => item.voucherDate >= filters.dateRange[0] && item.voucherDate <= filters.dateRange[1]);
  }
  if (filters.creator) {
    result = result.filter(item => item.creator === filters.creator);
  }
  if (filters.status) {
    result = result.filter(item => item.status === filters.status);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.voucherNo = "";
  filters.dateRange = [];
  filters.creator = "";
  filters.status = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const addEntry = () => {
  form.entries.push({ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 });
};
const removeEntry = (index) => {
  form.entries.splice(index, 1);
  calculateTotal();
};
const handleSubjectChange = (val, index) => {
  const subject = subjectList.find(item => item.code === val);
  if (subject) {
    form.entries[index].subjectName = subject.name;
  }
};
const calculateTotal = () => {
  // è‡ªåŠ¨è®¡ç®—ï¼Œç”±computed属性处理
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增凭证";
  Object.assign(form, {
    voucherNo: "è®°-" + String(mockData.length + 1).padStart(4, "0"),
    voucherDate: new Date().toISOString().split('T')[0],
    attachmentCount: 0,
    entries: [{ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 }],
    creator: "张三",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑凭证";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看凭证: ${row.voucherNo}`);
};
const handlePost = (row) => {
  ElMessageBox.confirm("确认过账该凭证吗?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "info",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "posted";
    }
    ElMessage.success("过账成功");
    getTableData();
  });
};
const handleCancel = (row) => {
  ElMessageBox.confirm("确认作废该凭证吗?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "cancelled";
    }
    ElMessage.success("作废成功");
    getTableData();
  });
};
const handleImport = () => {
  ElMessage.info("导入功能");
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      if (totalDebitEntry.value !== totalCreditEntry.value) {
        ElMessage.error("借贷不平衡,请检查分录");
        return;
      }
      const summary = form.entries.find(e => e.debit > 0)?.summary || "";
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, summary, debit: totalDebitEntry.value, credit: totalCreditEntry.value };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, summary, debit: totalDebitEntry.value, credit: totalCreditEntry.value, status: "unposted" });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
onMounted(() => {
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
  > div:first-child {
    display: flex;
    align-items: center;
  }
}
.text-success {
  color: #67c23a;
  font-weight: bold;
}
.text-danger {
  color: #f56c6c;
  font-weight: bold;
}
</style>