gaoluyang
2026-08-06 377c0a70c094d024aeb0bb94f4b791c6d5530c1e
src/views/financialManagement/generalLedger/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,499 @@
<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="资产类" />
          <el-option label="负债类"
                     value="负债类" />
          <el-option label="权益类"
                     value="权益类" />
          <el-option label="成本类"
                     value="成本类" />
          <el-option label="损益类"
                     value="损益类" />
        </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>
      <el-table ref="tableRef"
                v-loading="loading"
                :data="dataList"
                row-key="id"
                :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
                height="calc(100vh - 280px)"
                border
                stripe
                highlight-current-row
                class="subject-table">
        <el-table-column label="科目编码" prop="subjectCode" width="140">
          <template #default="scope">
            <span class="subject-code">{{ scope.row.subjectCode }}</span>
          </template>
        </el-table-column>
        <el-table-column label="科目名称" prop="subjectName" min-width="180">
          <template #default="scope">
            <span class="subject-name" :class="{ 'is-parent': scope.row.children?.length > 0 }">
              {{ scope.row.subjectName }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="科目类型" prop="subjectType" width="100" align="center">
          <template #default="scope">
            <el-tag size="small" :type="getSubjectTypeType(scope.row.subjectType)">
              {{ scope.row.subjectType }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="余额方向" prop="balanceDirection" width="100" align="center">
          <template #default="scope">
            <el-tag size="small" :type="scope.row.balanceDirection === '借方' ? 'primary' : 'danger'">
              {{ scope.row.balanceDirection }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="状态" prop="status" width="80" align="center">
          <template #default="scope">
            <el-tag size="small" :type="scope.row.status === 0 || scope.row.status === '0' ? 'success' : 'info'">
              {{ scope.row.status === 0 || scope.row.status === '0' ? '启用' : '禁用' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="备注" prop="remark" show-overflow-tooltip min-width="150" />
        <el-table-column label="操作" align="center" fixed="right" width="240">
          <template #default="scope">
            <el-button link type="primary" icon="Plus" @click="addChild(scope.row)">新增</el-button>
            <el-button link type="primary" icon="Edit" @click="edit(scope.row)">编辑</el-button>
            <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['financialManagement:generalLedger:remove']">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <FormDialog :title="dialogTitle"
                v-model="dialogVisible"
                width="600px"
                @confirm="submitForm"
                @cancel="dialogVisible = false">
      <el-form :model="form"
               :rules="rules"
               ref="formRef"
               label-width="100px">
        <el-form-item label="父级科目">
          <el-input :model-value="parentSubjectLabel"
                    disabled />
        </el-form-item>
        <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="资产类" />
            <el-option label="负债类"
                       value="负债类" />
            <el-option label="权益类"
                       value="权益类" />
            <el-option label="成本类"
                       value="成本类" />
            <el-option label="损益类"
                       value="损益类" />
          </el-select>
        </el-form-item>
        <el-form-item label="余额方向"
                      prop="balanceDirection">
          <el-radio-group v-model="form.balanceDirection">
            <el-radio label="借方">借方</el-radio>
            <el-radio label="贷方">贷方</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="状态"
                      prop="status">
          <el-radio-group v-model="form.status">
            <el-radio :label="0">启用</el-radio>
            <el-radio :label="1">禁用</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 type="primary"
                   @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </FormDialog>
  </div>
</template>
<script setup>
  import { ref, reactive, onMounted, getCurrentInstance, nextTick } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import FormDialog from "@/components/Dialog/FormDialog.vue";
  import {
    listAccountSubject,
    addAccountSubject,
    updateAccountSubject,
    delAccountSubject,
    exportAccountSubject,
  } from "@/api/financialManagement/accountSubject";
  defineOptions({
    name: "总帐科目",
  });
  const { proxy } = getCurrentInstance();
  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" },
    {
      label: "余额方向",
      prop: "balanceDirection",
      dataType: "tag",
      formatData: value => {
        if (value === "借方") {
          return "借方";
        }
        return "贷方";
      },
      formatType: value => {
        if (value === "借方") {
          return "primary";
        }
        return "danger";
      },
    },
    {
      label: "状态",
      prop: "status",
      dataType: "tag",
      formatData: value => {
        if (value === 0 || value === "0") {
          return "启用";
        }
        return "禁用";
      },
      formatType: value => {
        if (value === 0 || value === "0") {
          return "success";
        }
        return "info";
      },
    },
    { label: "备注", prop: "remark", showOverflowTooltip: true },
    {
      dataType: "action",
      label: "操作",
      align: "center",
      fixed: "right",
      width: "220",
      operation: [
        {
          name: "新增",
          type: "primary",
          clickFun: row => {
            addChild(row);
          },
        },
        {
          name: "编辑",
          type: "primary",
          clickFun: row => {
            edit(row);
          },
        },
        {
          name: "删除",
          type: "danger",
          clickFun: row => {
            handleDelete(row);
          },
          hasPermi: ['financialManagement:generalLedger:remove']
        },
      ],
    },
  ];
  const dataList = ref([]);
  const dialogVisible = ref(false);
  const dialogTitle = ref("");
  const parentSubjectLabel = ref("顶级科目");
  const formRef = ref(null);
  const tableRef = ref(null);
  const isEdit = ref(false);
  const loading = ref(false);
  const form = reactive({
    id: undefined,
    parentId: null,
    subjectCode: "",
    subjectName: "",
    subjectType: "",
    balanceDirection: "借方",
    status: 0,
    remark: "",
  });
  const rules = {
    subjectCode: [{ required: true, message: "请输入科目编码", trigger: "blur" }],
    subjectName: [{ required: true, message: "请输入科目名称", trigger: "blur" }],
    subjectType: [
      { required: true, message: "请选择科目类型", trigger: "change" },
    ],
  };
  const getSubjectTypeType = type => {
    const map = {
      èµ„产类: "success",
      è´Ÿå€ºç±»: "danger",
      æƒç›Šç±»: "warning",
      æˆæœ¬ç±»: "info",
      æŸç›Šç±»: "primary",
    };
    return map[type] || "";
  };
  const getTableData = () => {
    loading.value = true;
    const query = {
      current: pagination.currentPage,
      size: pagination.pageSize,
      ...filters,
    };
    listAccountSubject(query).then(response => {
      dataList.value = response.data.records || [];
      loading.value = false;
    }).catch(() => {
      loading.value = false;
    });
  };
  const resetFilters = () => {
    filters.subjectCode = "";
    filters.subjectName = "";
    filters.subjectType = "";
    pagination.currentPage = 1;
    getTableData();
  };
  const changePage = obj => {
    pagination.currentPage = obj.page;
    pagination.pageSize = obj.limit;
    getTableData();
  };
  const buildParentSubjectLabel = parentRow => {
    if (!parentRow) {
      return "顶级科目";
    }
    const code = parentRow.subjectCode || "";
    const name = parentRow.subjectName || "";
    return `${code} ${name}`.trim();
  };
  const resetForm = ({ parentId = null, parentRow = null } = {}) => {
    Object.assign(form, {
      id: undefined,
      parentId,
      subjectCode: "",
      subjectName: "",
      subjectType: "",
      balanceDirection: "借方",
      status: 0,
      remark: "",
    });
    parentSubjectLabel.value = buildParentSubjectLabel(parentRow);
  };
  const add = () => {
    isEdit.value = false;
    dialogTitle.value = "新增科目";
    resetForm({ parentId: null, parentRow: null });
    dialogVisible.value = true;
  };
  const addChild = row => {
    isEdit.value = false;
    dialogTitle.value = "新增子科目";
    resetForm({ parentId: row.id, parentRow: row });
    form.subjectType = row.subjectType || "";
    form.balanceDirection = row.balanceDirection || "借方";
    dialogVisible.value = true;
  };
  const findSubjectById = (nodes, id) => {
    for (const item of nodes || []) {
      if (item.id === id) {
        return item;
      }
      if (item.children && item.children.length > 0) {
        const found = findSubjectById(item.children, id);
        if (found) {
          return found;
        }
      }
    }
    return null;
  };
  const edit = row => {
    isEdit.value = true;
    dialogTitle.value = "编辑科目";
    Object.assign(form, row);
    form.parentId = row.parentId ?? null;
    const parentRow =
      row.parentId === null || row.parentId === undefined
        ? null
        : findSubjectById(dataList.value, row.parentId);
    parentSubjectLabel.value = parentRow
      ? buildParentSubjectLabel(parentRow)
      : row.parentId
      ? `上级ID: ${row.parentId}`
      : buildParentSubjectLabel(null);
    dialogVisible.value = true;
  };
  const submitForm = () => {
    formRef.value.validate(valid => {
      if (valid) {
        if (isEdit.value) {
          updateAccountSubject(form).then(() => {
            ElMessage.success("编辑成功");
            dialogVisible.value = false;
            getTableData();
          });
        } else {
          addAccountSubject(form).then(() => {
            ElMessage.success("新增成功");
            dialogVisible.value = false;
            getTableData();
          });
        }
      }
    });
  };
  const handleDelete = row => {
    const ids = row.id;
    ElMessageBox.confirm("确认删除该科目吗?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        return delAccountSubject(ids);
      })
      .then(() => {
        ElMessage.success("删除成功");
        getTableData();
      });
  };
  const handleOut = () => {
    proxy.download(
      "accountSubject/export",
      {
        ...filters,
      },
      `account_subject_${new Date().getTime()}.xlsx`
    );
  };
  onMounted(() => {
    getTableData();
  });
</script>
<style lang="scss" scoped>
  .actions {
    display: flex;
    justify-content: space-between;
    margin-bottom: 15px;
  }
  .subject-table {
    border-radius: 8px;
    overflow: hidden;
    :deep(.el-table__row) {
      transition: background-color 0.3s;
    }
    :deep(.el-table__row:hover) {
      background-color: #f5f7fa;
    }
    .subject-code {
      color: #606266;
    }
    .subject-name {
      font-weight: 500;
      &.is-parent {
        color: #409eff;
      }
    }
  }
</style>