sen
2026-03-23 075d8b76626b2c830cc3bef11fb32e89d6067a3e
ui/admin-ui3/src/views/cwgl/voucherSubjectSetting/index.vue
@@ -10,30 +10,39 @@
      @search-reset="searchReset" @selection-change="selectionChange" @current-change="currentChange"
      @size-change="sizeChange" @on-load="onLoad">
      <template #menu-left>
        <el-button type="success" icon="Edit" :disabled="pageF.single" v-hasPermi="['cwgl:voucherSubjectSetting:edit']"
        <!-- <el-button type="success" icon="Edit" :disabled="pageF.single" v-hasPermi="['cwgl:voucherSubjectSetting:edit']"
          @click="handleUpdate">修改
        </el-button>
        <el-button type="danger" icon="Delete" :disabled="pageF.multiple" @click="handleDelete"
          v-hasPermi="['cwgl:voucherSubjectSetting:remove']">删除
        </el-button>
        </el-button> -->
        <el-button type="warning" plain icon="Download" @click="handleExport"
          v-hasPermi="['cwgl:voucherSubjectSetting:export']">导出
        </el-button>
      </template>
      <template #menu="{ row, index, size }">
        <el-button type="primary" text icon="Plus" @click="handleRowAdd(row)">新增子项</el-button>
        <el-button type="primary" text icon="Plus" v-hasPermi="['cwgl:voucherSubjectSetting:addz']"
          @click="handleRowAdd(row)">新增子项</el-button>
        <el-button type="text" icon="View" @click="handleFlow(row)"
          v-hasPermi="['cwgl:voucherSubjectSetting:flow']">日志</el-button>
      </template>
    </avue-crud>
  </basicContainer>
  <OperationLogModal ref="logModalRef" />
</template>
<script setup name="voucherSubjectSetting" lang="ts">
import { listVoucherSubjectSettingLog, } from "@/api/cwgl/voucherSubjectSettingLog";
import { VoucherSubjectSettingI, addVoucherSubjectSetting, delVoucherSubjectSetting, exportVoucherSubjectSetting, getVoucherSubjectSetting, listVoucherSubjectSetting, updateVoucherSubjectSetting } from "@/api/cwgl/voucherSubjectSetting";
import useCurrentInstance from "@/utils/useCurrentInstance";
import { computed, reactive, ref, toRefs } from "vue";
import { PagesInterface, PageQueryInterface } from "@/utils/globalInterface";
import { usePagePlus } from "@/hooks/usePagePlus";
import { hasPermission } from "@/utils/permissionUtils";
import OperationLogModal from '@/components/OperationLogModal/index.vue';
const { proxy } = useCurrentInstance();
const crudRef = ref();
@@ -66,6 +75,10 @@
const option = ref({
  pageKey: 'VoucherSubjectSetting',
  rowKey: 'id',
  tree: true,           // 开启树形表格模式
  childrenHandler: 'children', // 指定子节点字段名,默认为 children
  expandAll: true,     // 是否默认展开所有行
  viewBtn: false,
  searchSpan: 5,
  labelWidth: 150,
  searchLabelWidth: 120,
@@ -92,10 +105,10 @@
    //     }
    //   ],
    // },
    accountSet: {
    type: {
      label: '账套',
      search: true,
      minWidth: 120,
      minWidth: 220,
      addDisabled: true,
      editDisabled: true,
      type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/sys_accounting_type',
@@ -143,8 +156,7 @@
    parentSubjectCode: {
      label: '上级科目代码',
      minWidth: 150,
      search: true,
      hide: true,
      // rules: [
      //   {
      //     required: true,
@@ -169,16 +181,21 @@
    //   //   }
    //   // ],
    // },
    // 在 option.value.column 中找到 subjectName
    // 找到 option 里的 subjectName 列
    subjectName: {
      label: '科目名称',
      minWidth: 150,
      prop: 'subjectName', // 保持原始 prop,这样弹窗里的输入框才是干净的名称
      minWidth: 250,       // 拼接后会很长,建议调大宽度
      search: true,
      overHidden: true,
      // --- 使用 formatter 来控制表格界面的显示 ---
      formatter: (row: any) => {
        // 优先显示我们递归生成的拼接字段,如果没有(比如刚新增还没刷新的数据),显示原始名称
        return row.subjectNameWithParent || row.subjectName;
      },
      rules: [
        {
          required: true,
          message: "科目名称不能为空", trigger: "blur"
        }
        { required: true, message: "科目名称不能为空", trigger: "blur" }
      ],
    },
    subjectCode: {
@@ -224,7 +241,7 @@
      editDisplay: false,
    },
    accountingItemsContains: {
      label: '核算项目1',
      label: '核算项目',
      minWidth: 150,
      search: true,
      type: 'select', // 确保类型为 select
@@ -241,7 +258,7 @@
      }
    },
    accountingItems: {
      label: '核算项目2',
      label: '核算项目',
      minWidth: 150,
      type: 'select',
      multiple: true,
@@ -391,8 +408,13 @@
    // 【关键修复点】:拦截详情接口,在数据进入 form 之前完成格式化
    getDetailApi: (id: any) => {
      return getVoucherSubjectSetting(id).then((res: any) => {
        console.log(res);
        if (res.code === 200 && res.data) {
          // 1. 处理 accountingItems 数字转数组
          // 1. 如果 accountSet 是数字,转为字符串
          res.data.accountSet = res.data.accountSet ? String(res.data.accountSet) : "0";
          if (typeof res.data.accountingItems === 'number') {
            res.data.accountingItems = decomposeAccountingItems(res.data.accountingItems);
          } else if (!res.data.accountingItems) {
@@ -413,6 +435,47 @@
    deleteApi: delVoucherSubjectSetting,
    addApi: addVoucherSubjectSetting,
    updateApi: updateVoucherSubjectSetting,
    getListFunc: (res: any) => {
      // 1. 转换树结构
      const tree = proxy.handleTree(res.rows, "id");
      // 2. 递归拼接名称函数 (核心修改)
      const recursionFormatName = (list: any[], pName = '') => {
        list.forEach(item => {
          // 这里的逻辑:如果有父级名称,拼在一起;否则只显示自己
          item.subjectNameWithParent = pName ? `${pName} / ${item.subjectName}` : item.subjectName;
          // 如果有子节点,继续递归,把当前的拼接结果传给子节点作为 pName
          if (item.children && item.children.length > 0) {
            recursionFormatName(item.children, item.subjectNameWithParent);
          }
        });
      };
      // 执行递归
      recursionFormatName(tree);
      // 3. 赋值给表格数据
      tableData.value = tree;
      // 4. 递归展开所有层级 (你原有的逻辑)
      nextTick(() => {
        if (crudRef.value) {
          const tableMethods = crudRef.value.$refs.table;
          if (tableMethods && tableData.value) {
            const expandAllNodes = (list: any[]) => {
              list.forEach(row => {
                if (row.children && row.children.length > 0) {
                  tableMethods.toggleRowExpansion(row, true);
                  expandAllNodes(row.children);
                }
              });
            };
            expandAllNodes(tableData.value);
          }
        }
      });
    },
    handleUpdateFunc: () => {
      isFromRow.value = true; // 修改操作也视为禁用上级科目
      crudRef.value.rowEdit(selectionList.value[0]);
@@ -549,11 +612,23 @@
    rowSaveBegin: (row: any, done: any, loading: any) => {
      processAccountingItems(row);
      delete row.accountingItemsContains;
      // 如果 row 对象里带了 id,新增接口可能会报错或变成修改,如果是顶部新增,确保没有 id
      // --- 核心逻辑开始 ---
      if (!isFromRow.value) {
        // 1. 确保没有 ID(防止误操作)
        delete row.id;
        // 2. 如果是顶级科目(parentId 为 0 或 无)
        if (row.parentId === 0 || !row.parentId) {
          // 将上级科目代码设置为当前填写的科目代码
          row.parentSubjectCode = row.subjectCode;
        }
      } else {
        // 如果是行内新增,parentId 已经是父节点的 ID
      }
      done(row); // 必须调用 done 并传入处理后的 row 才会继续请求接口
      // --- 核心逻辑结束 ---
      done(row);
    },
    // 修改保存前的逻辑
@@ -561,7 +636,7 @@
      processAccountingItems(row);
      delete row.accountingItemsContains;
      if (row.parentSubjectCode == "顶级科目代码") {
        row.parentSubjectCode = row.subjectCode;
        row.parentSubjectCode = row.subjectCode;
      }
      done(row); // 同理
    },
@@ -593,17 +668,14 @@
};
const handleTabClick = (tab: any) => {
  const selectedTabName = tab.props.name;
  console.log("Tab点击的值:", selectedTabName);
  activeAccountSet.value = selectedTabName;
  queryParams.value.type = selectedTabName;
  const nextParams = {
    ...queryParams.value,
    type: selectedTabName
  };
  // 4. 重置分页并加载
  // 先清空当前数据,防止旧数据的树结构影响新数据的渲染
  tableData.value = [];
  queryParams.value.type = selectedTabName;
  page.value.currentPage = 1;
  onLoad(page.value, nextParams);
  onLoad(page.value, { type: selectedTabName });
}
/**
@@ -621,22 +693,37 @@
  return result;
}
/**
 * 处理行内“新增”点击
 * 处理行内“新增子项”点击
 */
const handleRowAdd = (row: any) => {
  isFromRow.value = true;
  getVoucherSubjectSetting(row.id).then((res: any) => {
  // 1. 先触发新增动作,这会弹出窗口并初始化 form
  crudRef.value.rowAdd();
  // 2. 在 DOM 更新后的下一次循环中强制赋值
  nextTick(() => {
    // 确保赋值给 data 响应式对象中的 form
    form.value.parentId = row.id;
    form.value.parentSubjectCode = row.subjectCode;
    form.value.accountSet = row.accountSet;
    form.value.type = row.accountSet;
    form.value.enabled = "1";
    form.value.accountingItems = [];
  });
};
const logModalRef = ref(null);
const handleFlow = (row: any) => {
  // 这里可以从 row 中直接获取日志,或者调用后端接口查询
  // 示例模拟数据
  listVoucherSubjectSettingLog({ subjectId: row.id }).then((res) => {
    if (res.code == 200) {
      form.value = res.data;
      form.value.parentId = form.value.id;
      const num = form.value.accountingItems;
      form.value.accountingItems = decomposeAccountingItems(num);
      // 触发 Avue 的内置新增弹窗
      crudRef.value.rowAdd();
      logModalRef.value.open(res.rows, 'payable');
    }
  });
};
}
</script>