wujianwei
2026-01-15 242d318e85df66b916f554d64a026cfe0cb58e19
Merge remote-tracking branch 'origin/cwxt_master' into cwxt_master
2个文件已添加
4个文件已修改
1123 ■■■■■ 已修改文件
ui/admin-ui3/src/api/cwgl/fundFlow.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/components/AccountsPayableManagementDialog/index.vue 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/components/ClaimBillDialog/index.vue 543 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/components/receivableBillManagementDialog/index.vue 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/views/cwgl/fundFlow/index.vue 252 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/views/cwgl/fundFlowClaimDetail/index.vue 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/api/cwgl/fundFlow.ts
@@ -56,6 +56,15 @@
    })
}
/**
 * 确认资金流水
 */
export const confirmFundFlow:requestType = (id) => {
    return request({
        url: '/cwgl/fundFlow/confirm/' + id,
        method: 'put'
    })
}
/**
 * 导出资金流水
@@ -65,3 +74,11 @@
        download('/cwgl/fundFlow/export',query);
    })
}
/* 账单认领 */
export const addFundFlowClaimDetailClaim:requestType = (data,id) => {
    return request({
        url: '/cwgl/fundFlowClaimDetail/claim/'+id,
        method: 'post',
        data
    })
}
ui/admin-ui3/src/components/AccountsPayableManagementDialog/index.vue
New file
@@ -0,0 +1,222 @@
<template>
  <el-dialog v-model="dialogVisible" title="请选择 关联应付账单" width="85%" destroy-on-close @close="handleClose"
    append-to-body>
    <div class="search-bar">
      <el-form inline :model="queryParams" class="search-form" size="default">
        <el-form-item label="系统编号:">
          <el-input v-model="queryParams.systemNo" placeholder="请输入系统编号" style="width: 180px" clearable />
        </el-form-item>
        <el-form-item label="账单名称:">
          <el-input v-model="queryParams.billName" placeholder="请输入账单名称" style="width: 180px" clearable />
        </el-form-item>
        <el-form-item label="供应商名称:">
          <el-input v-model="queryParams.supplierName" placeholder="请输入供应商名称" style="width: 180px" clearable />
        </el-form-item>
        <!-- <el-form-item label="状态:">
          <el-select v-model="queryParams.status" style="width: 150px;" placeholder="请选择状态" clearable>
            <el-option v-for="dict in sys_bill_status" :key="dict.value" :label="dict.label" :value="dict.value" />
          </el-select>
        </el-form-item> -->
        <el-form-item>
          <el-button type="primary" icon="Search" @click="handleSearch">搜索</el-button>
          <el-button plain icon="RefreshLeft" @click="handleReset">清空</el-button>
        </el-form-item>
      </el-form>
    </div>
    <el-table ref="customerTableRef" :data="customerList" border size="small" style="width: 100%"
      :highlight-current-row="true" row-key="id" @current-change="handleRowSelect" class="customer-table">
      <el-table-column prop="systemNo" label="系统编号" min-width="150" show-overflow-tooltip />
      <el-table-column prop="billName" label="账单名称" min-width="150" show-overflow-tooltip />
      <el-table-column prop="supplierName" label="供应商名称" min-width="150" show-overflow-tooltip />
      <el-table-column prop="isInternalSettlement" label="是否内部结算" min-width="120">
        <template #default="scope">
          {{ dictFormat(sys_whether_type, scope.row.isInternalSettlement) }}
        </template>
      </el-table-column>
      <el-table-column prop="internalSettlementUnit" label="内部结算单位" min-width="120" show-overflow-tooltip />
      <el-table-column prop="documentCount" label="单据数量" min-width="100" />
      <el-table-column prop="totalAmount" label="应结算金额" min-width="120" />
      <el-table-column prop="currency" label="币制" min-width="100">
        <template #default="scope">
          {{ dictFormat(sys_currency, scope.row.currency) }}
        </template>
      </el-table-column>
      <el-table-column prop="discountAmount" label="减免金额" min-width="100" />
      <el-table-column prop="paidAmount" label="已付金额" min-width="100" />
      <el-table-column prop="pendingAmount" label="待付金额" min-width="100" />
      <el-table-column prop="status" label="状态" width="100">
        <template #default="scope">
          {{ dictFormat(sys_bill_status, scope.row.status) }}
        </template>
      </el-table-column>
    </el-table>
    <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
      v-model:limit="queryParams.pageSize" @pagination="getList" />
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="handleClose">取消</el-button>
        <el-button type="primary" @click="handleConfirm">确定选择</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, watch, nextTick } from 'vue';
import type { Table } from 'element-plus';
import { ElMessage } from 'element-plus';
import useCurrentInstance from "@/utils/useCurrentInstance";
import { listPayableBillManagement } from "@/api/cwgl/payableBillManagement";
interface Customer {
  id: string | number;
  systemNo: string;
  billName: string;
  supplierName: string;
  isInternalSettlement: string;
  internalSettlementUnit: string;
  documentCount: number;
  totalAmount: number;
  currency: string;
  discountAmount: number;
  paidAmount: number;
  pendingAmount: number;
  status: string | number;
}
const props = defineProps({
  visible: { type: Boolean, default: false },
  defaultSelectedId: { type: [String, Number], default: '' },
  // 新增:接收默认状态
  defaultStatus: { type: [String, Number], default: '' }
});
const emit = defineEmits(['confirm', 'close', 'update:visible']);
const { proxy } = useCurrentInstance();
// 获取所需的字典
const { sys_bill_status, sys_currency, sys_whether_type } = proxy.useDict(
  'sys_bill_status',
  'sys_currency',
  'sys_whether_type'
);
const dictFormat = (dict: any, value: any) => {
  return proxy.selectDictLabel(dict, value);
};
const dialogVisible = ref(false);
const customerList = ref<Customer[]>([]);
const total = ref(0);
const selectedRow = ref<Customer | null>(null);
const customerTableRef = ref<InstanceType<typeof Table>>();
const queryParams = reactive({
  systemNo: '',
  billName: '',
  supplierName: '',
  status: '',
  pageNum: 1,
  pageSize: 10
});
const getDataList = async () => {
  try {
    const res = await listPayableBillManagement(queryParams);
    if (res.code === 200) {
      customerList.value = res.rows;
      total.value = res.total;
      return res.rows;
    }
  } catch (err) {
    console.error('加载列表失败:', err);
  }
  return [];
};
const autoSelectRow = (list: Customer[]) => {
  if (!props.defaultSelectedId || list.length === 0) return;
  nextTick(() => {
    const target = list.find(item => String(item.id) === String(props.defaultSelectedId));
    if (target && customerTableRef.value) {
      customerTableRef.value.setCurrentRow(target);
      selectedRow.value = target;
    }
  });
};
const handleSearch = () => {
  queryParams.pageNum = 1;
  getList();
};
const handleReset = () => {
  // 重置其他搜索字段
  queryParams.systemNo = '';
  queryParams.billName = '';
  queryParams.customerName = '';
  // 核心处理:如果有默认状态值就恢复默认值,没有才设为空
  if (props.defaultStatus !== undefined && props.defaultStatus !== null && props.defaultStatus !== '') {
    queryParams.status = props.defaultStatus;
  } else {
    queryParams.status = '';
  }
  handleSearch();
};
const getList = () => {
  getDataList().then((list) => {
    autoSelectRow(list);
  });
};
const handleRowSelect = (val: Customer | null) => {
  selectedRow.value = val;
};
const handleConfirm = () => {
  if (!selectedRow.value) {
    ElMessage.warning('请先点击表格选择一行数据');
    return;
  }
  emit('confirm', selectedRow.value);
  handleClose();
};
const handleClose = () => {
  emit('update:visible', false);
  emit('close');
};
watch(() => props.visible, (newVal) => {
  dialogVisible.value = newVal;
  if (newVal) {
    // 关键逻辑:如果 props 传了默认状态就用它,否则设置为空字符串
    // 使用 queryParams.status = props.defaultStatus || '';
    // 但考虑到 '0' 可能是有效值,建议判断是否为 undefined 或 null
    if (props.defaultStatus !== undefined && props.defaultStatus !== null) {
        queryParams.status = props.defaultStatus;
    } else {
        queryParams.status = '';
    }
    // 重置页码为第一页并加载数据
    queryParams.pageNum = 1;
    getList();
  }
});
</script>
ui/admin-ui3/src/components/ClaimBillDialog/index.vue
New file
@@ -0,0 +1,543 @@
<template>
  <el-dialog v-model="visible" title="账单认领" width="1150px" destroy-on-close :close-on-click-modal="false">
    <div style="text-align: right;margin-bottom: 10px;">
      <el-button type="warning" v-if="isViewMode" plain icon="Download" @click="handleExport"
        v-hasPermi="['cwgl:fundFlowClaimDetail:export']">导出
      </el-button>
      <!-- <el-button type="primary" @click="handleFinalSubmit">确 定</el-button> -->
    </div>
    <div class="claim-wrapper">
      <!-- <div class="section-header">流水详细信息</div> -->
      <el-descriptions :column="3" border class="mb-20">
        <el-descriptions-item label="id">{{ detail.id }}</el-descriptions-item>
        <el-descriptions-item label="银行流水号">{{ detail.bankFlowNo }}</el-descriptions-item>
        <el-descriptions-item label="单位">{{ detail.company }}</el-descriptions-item>
        <el-descriptions-item label="本方账号">{{ detail.ourAccount }}</el-descriptions-item>
        <el-descriptions-item label="本方账户开户行">{{ detail.ourBankName }}</el-descriptions-item>
        <el-descriptions-item label="收支标识">
          {{ dictFormat(sys_income_expenses, detail.incomeExpenseFlag) }}
        </el-descriptions-item>
        <el-descriptions-item label="交易金额">
          <span class="amount-text">{{ detail.transactionAmount }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="交易币种">{{ detail.currency }}</el-descriptions-item>
        <el-descriptions-item label="对方账号">{{ detail.counterpartyAccount }}</el-descriptions-item>
        <el-descriptions-item label="对方户名">{{ detail.counterpartyName }}</el-descriptions-item>
        <el-descriptions-item label="交易日期">{{ detail.transactionDate }}</el-descriptions-item>
        <el-descriptions-item label="用途">{{ detail.purpose }}</el-descriptions-item>
        <el-descriptions-item label="摘要" :span="1">{{ detail.summary }}</el-descriptions-item>
        <el-descriptions-item label="附言" :span="1">{{ detail.remarks }}</el-descriptions-item>
        <el-descriptions-item label="已认领金额">{{ detail.claimedAmount || 0 }}</el-descriptions-item>
        <el-descriptions-item label="待认领金额">
          <span class="text-danger font-bold">{{ remainingAmountDr }}</span>
        </el-descriptions-item v-if="isViewMode">
          <el-descriptions-item label="关联账单类型">
          <span  v-if="detail.incomeExpenseFlag == 0">
            供应商
          </span>
          <span  v-if="detail.incomeExpenseFlag == 1">
            客户
          </span>
        </el-descriptions-item>
        <el-descriptions-item label="" :span="1"></el-descriptions-item>
      </el-descriptions>
      <div class="section-header">{{ isViewMode ? '账单认领明细' : '账单认领' }}</div>
      <div v-if="!isViewMode" class="type-selection-bar mb-20">
        <span class="required-label">关联账单类型</span>
        <el-radio-group v-model="detail.incomeExpenseFlag" disabled @change="handleTypeChange">
          <el-radio :label="0">应收账单</el-radio>
          <el-radio :label="1">应付账单</el-radio>
        </el-radio-group>
      </div>
      <div v-if="!isViewMode" class="mb-10">
        <el-button type="primary" icon="Plus" @click="handleAddRow">新增</el-button>
      </div>
      <el-table :data="detail.claimDetails" border stripe>
        <el-table-column label="账单编号" min-width="220">
          <template #default="{ row, $index }">
            <el-input v-model="row.billNo" @click="openReceivableDialog($index)" :disabled="!row.$edit" readonly
              placeholder="点击选择账单">
              <template v-if="row.$edit" #append>
                <el-button icon="Search" @click="openReceivableDialog($index)" :disabled="!row.$edit" />
              </template>
            </el-input>
          </template>
        </el-table-column>
        <el-table-column label="关联企业类型" width="120">
          <template #default="{ row }">
            <span>{{ row.relatedCompanyType }}</span>
          </template>
        </el-table-column>
        <el-table-column label="关联企业名称" width="150">
          <template #default="{ row }">
            <span>{{ row.relatedCompanyName }}</span>
          </template>
        </el-table-column>
        <el-table-column label="账单金额" width="120">
          <template #default="{ row }">
            <span>{{ row.billAmount || 0 }}</span>
          </template>
        </el-table-column>
        <el-table-column label="账单待结算金额" width="130">
          <template #default="{ row }">
            <span>{{ row.billPendingAmount || 0 }}</span>
          </template>
        </el-table-column>
        <el-table-column label="认领金额" width="150">
          <template #default="{ row }">
            <el-input-number v-model="row.claimAmount" :precision="2" :controls="false" :disabled="!row.$edit" :min="0"
              style="width: 100%" placeholder="输入金额" @change="(val) => handleAmountChange(val, row)" />
          </template>
        </el-table-column>
        <el-table-column label="认领日期" width="200"> <template #default="{ row }">
            <el-date-picker v-model="row.claimDate" type="datetime" value-format="YYYY-MM-DD HH:mm:ss"
              :disabled="!row.$edit" style="width: 100%" placeholder="选择日期时间" />
          </template>
        </el-table-column>
        <el-table-column label="备注">
          <template #default="{ row }">
            <el-input v-model="row.remark" :disabled="!row.$edit" placeholder="备注信息" />
          </template>
        </el-table-column>
        <el-table-column label="操作" v-if="!isViewMode" width="120" fixed="right" align="center">
          <template #default="{ row, $index }">
            <template v-if="row.$edit">
              <el-button type="primary" link @click="handleSaveRow(row)">确定</el-button>
            </template>
            <template v-else>
              <!-- <el-button type="primary" link @click="row.$edit = true">修改</el-button> -->
              <el-button type="danger" link @click="handleDeleteRow(row)">删除</el-button>
            </template>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 10" :total="total" v-model:page="queryParams.pageNum"
        v-model:limit="queryParams.pageSize" @pagination="getList" />
    </div>
    <template #footer>
      <div style="text-align: center;">
        <el-button @click="handleCancel">关 闭</el-button>
        <!-- <el-button type="primary" @click="handleFinalSubmit">确 定</el-button> -->
      </div>
    </template>
  </el-dialog>
  <receivableBillManagementDialog :default-status="-1" v-model:visible="receivablIshow"
    :default-selected-id="detail.invoiceManageId" @confirm="receivablForm" />
  <AccountsPayableManagementDialog :default-status="-1" v-model:visible="accountsIshow"
    :default-selected-id="detail.invoiceManageId" @confirm="accountsForm" />
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import useCurrentInstance from "@/utils/useCurrentInstance";
import receivableBillManagementDialog from "../receivableBillManagementDialog/index.vue";
import AccountsPayableManagementDialog from "../AccountsPayableManagementDialog/index.vue";
import { addFundFlowClaimDetailClaim } from "@/api/cwgl/fundFlow";
import { delFundFlowClaimDetail, } from "@/api/cwgl/fundFlowClaimDetail";
import { listFundFlowClaimDetail } from "@/api/cwgl/fundFlowClaimDetail";
const { proxy } = useCurrentInstance();
const { sys_income_expenses } = proxy.useDict('sys_income_expenses');
const dictFormat = (dict: any, value: any) => proxy.selectDictLabel(dict, value);
const emit = defineEmits(['submit']);
const visible = ref(false);
const detail = ref<any>({
  claimDetails: []
});
const billType = ref('');
// 1. 【待认领金额计算】: 交易金额 - 已认领金额 )
const remainingAmountDr = computed(() => {
  const total = parseFloat(detail.value.transactionAmount) || 0;
  const alreadyClaimed = parseFloat(detail.value.claimedAmount) || 0;
  const res = total - alreadyClaimed;
  return res.toFixed(2);
});
// 1. 实时计算待认领池子(剩余总额)
const remainingAmount = computed(() => {
  const total = parseFloat(detail.value.transactionAmount) || 0; // 流水总额
  const historicClaimed = parseFloat(detail.value.claimedAmount) || 0; // 历史已认领
  // 累加当前表格中所有行填写的金额
  const currentTableTotal = (detail.value.claimDetails || []).reduce((sum, item) => {
    return sum + (parseFloat(item.claimAmount) || 0);
  }, 0);
  const res = total - historicClaimed - currentTableTotal;
  return res.toFixed(2);
});
// 修正后的函数
const handleAmountChange = (val: number | null, row: any) => {
  if (val === null) return;
  // 1. 获取池子剩余(需要加回当前行金额)
  const currentRemaining = parseFloat(remainingAmount.value) || 0;
  const currentLineAmount = val || 0;
  // 计算此时如果没填这一行,池子有多少钱
  // 注意:这里因为 val 已经改变了,remainingAmount 已经减去了新的 val
  // 所以池子可用总量 = remainingAmount + val
  const totalAvailablePool = currentRemaining + currentLineAmount;
  // 2. 确定两个上限
  const limitByBill = row.billAmount || 0; // 账单上限
  const limitByPool = totalAvailablePool;         // 流水上限
  // 3. 校验并修正
  if (val > limitByBill) {
    ElMessage.warning(`认领金额不能超过账单金额 (${limitByBill})`);
    row.claimAmount = limitByBill;
  } else if (val > limitByPool) {
    ElMessage.warning(`认领金额不能超过流水待认领金额 (${limitByPool.toFixed(2)})`);
    // row.claimAmount = parseFloat(limitByPool.toFixed(2));
  }
};
// 建议保留 getMaxClaimAmount 作为一个保险上限,或者直接删除它
const getMaxClaimAmount = (row: any) => {
  // 为了不干扰输入过程,这里返回一个极大的安全值,或者流水总金额
  return parseFloat(detail.value.transactionAmount) || 99999999;
};
// 重置表单和数据
const resetForm = () => {
  // 1. 清空明细表格
  detail.value = {
    claimDetails: [],
    // 如果有其他需要清空的流水基础信息,也可以在这里初始化
    transactionAmount: 0,
    claimedAmount: 0
  };
  // 2. 清空选择的账单类型
  billType.value = '';
  // 3. 重置索引和弹窗控制变量
  currentRowIndex.value = null;
  receivablIshow.value = false;
  accountsIshow.value = false;
};
// 取消按钮点击事件
const handleCancel = () => {
  visible.value = false;
  resetForm();
};
const isViewMode = ref(false); // 新增:模式控制
// --- 新增:分页与搜索相关的响应式变量 ---
const total = ref(0);
const loading = ref(false);
const queryParams = ref({
  pageNum: 1,
  pageSize: 10,
  fundFlowId: null as any // 关联的流水ID
});
// --- 新增:获取列表数据的方法 ---
const getList = async () => {
  if (!detail.value.id) return;
  loading.value = true;
  try {
    const res = await listFundFlowClaimDetail({
      ...queryParams.value,
      fundFlowId: detail.value.id
    });
    if (res.code === 200) {
      detail.value.claimDetails = res.rows;
     detail.value.claimDetails.forEach((item: any) => {
      if (item.$edit === undefined) {
        item.$edit = false;
      }
    });
      total.value = res.total;
    }
  } catch (error) {
    console.error("获取明细列表失败", error);
  } finally {
    loading.value = false;
  }
};
const handleExport =()=>{
  proxy.download("/cwgl/fundFlowClaimDetail/export",{...queryParams.value})
}
// 打开弹窗
const open = (rowData: any, mode: 'view' | 'edit' = 'edit') => {
  // 1. 先重置一次,防止上次残留
  resetForm();
  isViewMode.value = mode === 'view'; // 设置模式
  // 2. 浅拷贝基础数据
  detail.value = {
    ...rowData,
  };
  // 3. 处理 billType 的自动回显逻辑
  if (detail.value.claimDetails && detail.value.claimDetails.length > 0) {
    // 取出第一条明细的关联企业类型
    const firstCompanyType = detail.value.claimDetails[0].relatedCompanyType;
    if (firstCompanyType === '客户') {
      billType.value = 'RECEIVABLE';
    } else if (firstCompanyType === '供应商') {
      billType.value = 'PAYABLE';
    }
    // 如果有ID,则请求后端明细数据
    // 确保已有的数据行不会变成编辑模式
    getList()
  }
  visible.value = true;
};
// 关联账单类型切换逻辑
const handleTypeChange = (val: string) => {
  if (detail.value.claimDetails && detail.value.claimDetails.length > 0) {
    ElMessageBox.confirm('切换账单类型将清空当前已添加的认领明细,是否继续?', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }).then(() => {
      detail.value.claimDetails = [];
      ElMessage.success('已清空明细');
    }).catch(() => {
      billType.value = val === 'RECEIVABLE' ? 'PAYABLE' : 'RECEIVABLE';
    });
  }
};
// 新增行
// 修改新增行逻辑
const handleAddRow = () => {
  const defaultCompanyType = detail.value.incomeExpenseFlag == '0' ? '客户' : '供应商';
  // 获取当前时间的 YYYY-MM-DD HH:mm:ss 格式
  const now = new Date();
  const formatTime = (date: Date) => {
    const pad = (num: number) => String(num).padStart(2, '0');
    return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +
      `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
  };
  detail.value.claimDetails.push({
    billNo: '',
    claimAmount: 0,
    claimDate: formatTime(now), // 使用带时分秒的默认值
    remark: '',
    relatedCompanyType: defaultCompanyType,
    relatedCompanyName: '',
    billAmount: 0,
    billPendingAmount: 0,
    $edit: true
  });
};
// 保存行
const handleSaveRow = (row: any) => {
  if (!row.billNo) return ElMessage.warning('请选择账单编号');
  // 转换数值,确保计算准确
  const currentClaim = parseFloat(row.claimAmount) || 0;
  if (currentClaim <= 0) return ElMessage.warning('认领金额必须大于0');
  // 1. 获取当前计算属性中的剩余额度(此时已经扣除了所有行的 claimAmount)
  const currentRemaining = parseFloat(remainingAmount.value) || 0;
  // 2. 【关键】计算该行实际可用的“剩余池子上限”
  // 公式:池子真实余量 = 当前显示的余额 + 该行刚才占用的额度
  const poolLimit = currentRemaining + currentClaim;
  // 3. 获取账单本身的上限
  const billLimit = row.billPendingAmount || row.billAmount || 0;
  // --- 开始拦截判定 ---
  // 判定 A:超过账单欠款
  if (currentClaim > billLimit) {
    return ElMessage.error(`保存失败:认领金额不能超过账单待结算金额 (${billLimit})`);
  }
  // 判定 B:超过流水总余量
  // 使用 0.01 容差防止 JS 浮点数计算误差
  if (currentClaim > (poolLimit + 0.01)) {
    return ElMessage.error(`保存失败:认领总额超过流水待认领金额,该行当前最大可填 ${poolLimit.toFixed(2)}`);
  }
  addFundFlowClaimDetailClaim(row, detail.value.id).then((response) => {
    if (response.code == 200) {
      proxy.$modal.msgSuccess("保存成功");
      getList(); // 使用统一的 getList 方法
    }
  })
};
const handleDeleteRow = (row: any) => {
  proxy.$modal.confirm('是否确认删除账单编号为"' + row.billNo + '"?').then(function () {
    return delFundFlowClaimDetail(row.id);
  }).then((res) => {
    if (res.code == 200) {
    proxy.$modal.msgSuccess("删除成功");
      getList(); // 使用统一的 getList 方法
    }
  }).catch(() => { });
};
const receivablIshow = ref(false);
const currentRowIndex = ref<number | null>(null);
const accountsIshow = ref(false);
const openReceivableDialog = (index: number) => {
  currentRowIndex.value = index;
  if (detail.value.incomeExpenseFlag == '0') {
    receivablIshow.value = true;
  } else if (detail.value.incomeExpenseFlag == '1') {
    accountsIshow.value = true;
  }
};
// 回填弹窗选中的数据
const receivablForm = (data: any) => {
  const defaultCompanyType = billType.value === 'RECEIVABLE' ? '客户' : '供应商';
  if (currentRowIndex.value !== null && data) {
    const row = detail.value.claimDetails[currentRowIndex.value];
    row.billNo = data.systemNo
    row.relatedCompanyType = defaultCompanyType;
    row.relatedCompanyName = data.customerName
    row.billAmount = data.totalAmount || 0;
    row.billPendingAmount = data.pendingAmount || 0;
    currentRowIndex.value = null;
    receivablIshow.value = false;
  }
};
const accountsForm = (data: any) => {
  const defaultCompanyType = billType.value === 'RECEIVABLE' ? '客户' : '供应商';
  if (currentRowIndex.value !== null && data) {
    const row = detail.value.claimDetails[currentRowIndex.value];
    row.billNo = data.systemNo
    row.relatedCompanyType = defaultCompanyType;
    row.relatedCompanyName = data.supplierName
    row.billAmount = data.totalAmount || 0;
    row.billPendingAmount = data.pendingAmount || 0;
    currentRowIndex.value = null;
    accountsIshow.value = false;
  }
};
// 确定提交
const handleFinalSubmit = () => {
  if (!billType.value) return ElMessage.warning('请选择关联账单类型');
  if (detail.value.claimDetails.length === 0) return ElMessage.warning('请添加至少一条认领明细');
  const hasEditing = detail.value.claimDetails.some((row: any) => row.$edit);
  if (hasEditing) return ElMessage.warning('请先保存正在编辑的明细行');
  if (parseFloat(remainingAmount.value) < 0) {
    return ElMessage.error('最终认领总额不能超过流水待认领金额');
  }
  emit('submit', detail.value);
  // console.log("提交数据:", detail.value);
  // ElMessage.success('操作成功');
  // visible.value = false;
};
defineExpose({ open, handleCancel });
</script>
<style scoped>
.claim-wrapper {
  padding: 0 10px;
}
.section-header {
  font-size: 16px;
  font-weight: bold;
  margin: 15px 0 10px;
  border-left: 4px solid #409eff;
  padding-left: 10px;
  color: #303133;
}
.type-selection-bar {
  display: flex;
  align-items: center;
  gap: 20px;
  background: #f8f9fb;
  padding: 10px 15px;
  border-radius: 4px;
}
.required-label {
  font-size: 14px;
  font-weight: bold;
  color: #606266;
}
.required-label::before {
  content: '*';
  color: #f56c6c;
  margin-right: 4px;
}
.amount-text {
  color: #409eff;
  font-weight: bold;
}
.text-danger {
  color: #f56c6c;
}
.font-bold {
  font-weight: bold;
}
.mb-20 {
  margin-bottom: 20px;
}
.mb-10 {
  margin-bottom: 10px;
}
:deep(.el-input.is-disabled .el-input__wrapper) {
  background-color: transparent;
  box-shadow: none;
}
:deep(.el-descriptions__body .el-descriptions__table.is-bordered .el-descriptions__cell) {
  width: 16%;
}
</style>
ui/admin-ui3/src/components/receivableBillManagementDialog/index.vue
@@ -15,11 +15,11 @@
          <el-input v-model="queryParams.customerName" placeholder="请输入客户名称" style="width: 180px" clearable />
        </el-form-item>
        <el-form-item label="状态:">
        <!-- <el-form-item label="状态:">
          <el-select v-model="queryParams.status" style="width: 150px;" placeholder="请选择状态" clearable>
            <el-option v-for="dict in sys_bill_status" :key="dict.value" :label="dict.label" :value="dict.value" />
          </el-select>
        </el-form-item>
        </el-form-item> -->
        <el-form-item>
          <el-button type="primary" icon="Search" @click="handleSearch">搜索</el-button>
@@ -79,7 +79,9 @@
const props = defineProps({
  visible: { type: Boolean, default: false },
  defaultSelectedId: { type: [String, Number], default: '' }
  defaultSelectedId: { type: [String, Number], default: '' },
  // 新增:接收默认状态
  defaultStatus: { type: [String, Number], default: '' }
});
const emit = defineEmits(['confirm', 'close', 'update:visible']);
@@ -138,11 +140,18 @@
};
const handleReset = () => {
  // 重置字段必须与 queryParams 定义的一致
  // 重置其他搜索字段
  queryParams.systemNo = '';
  queryParams.billName = '';
  queryParams.customerName = '';
  // 核心处理:如果有默认状态值就恢复默认值,没有才设为空
  if (props.defaultStatus !== undefined && props.defaultStatus !== null && props.defaultStatus !== '') {
    queryParams.status = props.defaultStatus;
  } else {
  queryParams.status = '';
  }
  handleSearch();
};
@@ -173,6 +182,17 @@
watch(() => props.visible, (newVal) => {
  dialogVisible.value = newVal;
  if (newVal) {
    // 关键逻辑:如果 props 传了默认状态就用它,否则设置为空字符串
    // 使用 queryParams.status = props.defaultStatus || '';
    // 但考虑到 '0' 可能是有效值,建议判断是否为 undefined 或 null
    if (props.defaultStatus !== undefined && props.defaultStatus !== null) {
        queryParams.status = props.defaultStatus;
    } else {
        queryParams.status = '';
    }
    // 重置页码为第一页并加载数据
    queryParams.pageNum = 1;
    getList();
  }
});
ui/admin-ui3/src/views/cwgl/fundFlow/index.vue
@@ -1,61 +1,65 @@
<template>
  <basicContainer >
    <avue-crud
        :option="option"
        :table-loading="pageF.loading"
        :data="tableData"
        :page="page"
        :permission="permissionList"
        :before-open="beforeOpen"
        v-model="form"
        ref="crudRef"
        @row-update="rowUpdate"
        @row-save="rowSave"
        @refresh-change="refreshChange"
        @row-del="rowDel"
        @search-change="searchChange"
        @search-reset="searchReset"
        @selection-change="selectionChange"
        @current-change="currentChange"
        @size-change="sizeChange"
        @on-load="onLoad"
    >
    <avue-crud :option="option" :table-loading="pageF.loading" :data="tableData" :page="page"
      :permission="permissionList" :before-open="beforeOpen" v-model="form" ref="crudRef" @row-update="rowUpdate"
      @row-save="rowSave" @refresh-change="refreshChange" @row-del="rowDel" @search-change="searchChange"
      @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:fundFlow:edit']"
        <!-- <el-button type="success" icon="Edit" :disabled="pageF.single" v-hasPermi="['cwgl:fundFlow:edit']"
            @click="handleUpdate">修改
        </el-button>
        <el-button
            type="danger"
            icon="Delete"
            :disabled="pageF.multiple"
            @click="handleDelete"
            v-hasPermi="['cwgl:fundFlow:remove']"
        >删除
        <el-button type="danger" icon="Delete" :disabled="pageF.multiple" @click="handleDelete"
          v-hasPermi="['cwgl:fundFlow:remove']">删除
        </el-button> -->
        <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['cwgl:fundFlow:export']">导出
        </el-button>
        <el-button
            type="warning"
            plain
            icon="Download"
            @click="handleExport"
            v-hasPermi="['cwgl:fundFlow:export']"
        >导出
        </el-button>
      </template>
      <template #menu="{ size, row, index }">
        <el-link v-if="row.status == '0'" class="link-btn" type="primary" icon="Edit" :underline="false" :size="size"
          @click="crudRef.rowEdit(row, index)"> 修改
        </el-link>
        <el-link v-if="row.status == '0'" class="link-btn" type="primary" icon="Position" :underline="false"
          :size="size" @click="makeInvoice(row)" v-hasPermi="['cwgl:fundFlow:confirm']"> 确认
        </el-link>
        <el-link v-if="row.status == '0'" class="link-btn" type="primary" icon="Delete" :underline="false" :size="size"
          @click="crudRef.rowDel(row, index)"> 删除
        </el-link>
        <el-link v-if="['1', '2'].includes(String(row.status))" class="link-btn" type="primary" icon="Position"
          :underline="false" :size="size" @click="handleClaim(row)" v-hasPermi="['cwgl:fundFlow:claim']">
          账单认领
        </el-link>
          <el-link v-if="['1', '2','3'].includes(String(row.status))" class="link-btn" type="primary" icon="Position"
          :underline="false" :size="size" @click="handleDetails(row)" v-hasPermi="['cwgl:fundFlow:edit']">
          认领明细
        </el-link>
          <el-button type="text" icon="View" @click="handleFlow(row)"
          v-hasPermi="['cwgl:fundFlow:flow']">日志</el-button>
        <!-- <el-button type="text" icon="View" @click="handleFlow(row)"
          v-hasPermi="['cwgl:invoiceManage:flow']">日志</el-button> -->
      </template>
    </avue-crud>
  </basicContainer>
  <OperationLogModal ref="logModalRef" />
  <ClaimBillDialog ref="claimDialogRef" @submit="handleClaimSubmit" />
</template>
<script setup name="fundFlow" lang="ts">
  import {FundFlowI,addFundFlow, delFundFlow, exportFundFlow, getFundFlow, listFundFlow, updateFundFlow} from "@/api/cwgl/fundFlow";
import { FundFlowI, addFundFlow, delFundFlow, addFundFlowClaimDetailClaim, exportFundFlow, confirmFundFlow, getFundFlow, listFundFlow, updateFundFlow } from "@/api/cwgl/fundFlow";
  import useCurrentInstance from "@/utils/useCurrentInstance";
  import { listFundFlowLog} from "@/api/cwgl/fundFlowLog";
  import {computed,reactive, ref, toRefs} from "vue";
  import {PagesInterface, PageQueryInterface} from "@/utils/globalInterface";
  import {usePagePlus} from "@/hooks/usePagePlus";
  import {hasPermission} from "@/utils/permissionUtils";
import { listFundFlowClaimDetail } from "@/api/cwgl/fundFlowClaimDetail";
import OperationLogModal from '@/components/OperationLogModal/index.vue';
import ClaimBillDialog from "../../../components/ClaimBillDialog/index.vue";
  const { proxy } = useCurrentInstance();
  const crudRef = ref();
@@ -83,70 +87,132 @@
  const option = ref({
    pageKey: 'FundFlow',
    rowKey: 'id',
  searchSpan: 5,
  labelWidth: 180,
  searchLabelWidth: 150,
  delBtn: false,
  editBtn: false,
    column: {
                                id: {
          label: 'ID',
                            },
    // id: {
    //   label: 'ID',
    // },
                                bankFlowNo: {
          label: '银行流水号',
      minWidth: 120,
      search: true,
                                rules: [
              {
                required: true,
                message: "银行流水号不能为空", trigger: "blur" }
            ],                  },
          message: "银行流水号不能为空", trigger: "blur"
        }
      ],
    },
                                company: {
          label: '单位',
      minWidth: 120,
                            },
                                ourAccount: {
          label: '本方账号',
      search: true,
      minWidth: 120,
                            },
                                ourBankName: {
          label: '本方账户开户行',
      minWidth: 120,
                            },
                                incomeExpenseFlag: {
          label: '收支标识借贷标志 0-收 1-支',
      label: '收支标识',
      type: 'radio', // 设置为单选按钮
      search: true,  // 如果需要在搜索栏也显示
      dicUrl: '/system/dict/data/type/sys_income_expenses',
      rules: [
        {
          required: true,
          message: "请选择收支标识",
          trigger: "blur"
        }
      ],
                            },
                                transactionAmount: {
          label: '交易金额',
                            },
                                currency: {
          label: '交易币种',
      search: true,
      minWidth: 120,
      dataType: 'string',
      type: 'select',
      // addDisplay: false,  // 新增时不显示
      // editDisplay: false, // 修改时不显示
      viewDisplay: false,
      dicUrl: '/system/dict/data/type/sys_currency',
                            },
                                counterpartyAccount: {
      minWidth: 150,
          label: '对方账号',
      search: true,
                            },
                                counterpartyName: {
      minWidth: 150,
          label: '对方户名',
      search: true,
                            },
                                transactionDate: {
      minWidth: 200,
      // search: true,
          label: '交易日期',
      type: 'datetime',          // 类型改为 datetime
      format: 'YYYY-MM-DD HH:mm:ss',      // 界面显示的格式
      valueFormat: 'YYYY-MM-DD HH:mm:ss', // 提交给后台的数据格式
      // search: true,               // 如果开启搜索
      rules: [
        {
          required: true,
          message: "请选择交易日期",
          trigger: "change"
        }
      ],
                            },
                                purpose: {
          label: '用途',
      minWidth: 150,
                            },
                                summary: {
          label: '摘要',
                      type: 'textarea', minRows: 3, maxRows: 5,
      hide: true,
                            },
                                remarks: {
          label: '附言',
      hide: true,
                      type: 'textarea', minRows: 3, maxRows: 5,
                            },
                                createBy: {
          label: '创建者',
    status: {
      minWidth: 120,
      label: '状态',
      fixed: 'right',
      value: '0',
      addDisplay: false,  // 表单不显示
      editDisplay: false,
      viewDisplay: true,
      dicUrl: '/system/dict/data/type/sys_capital_status',
                            },
                                createTime: {
          label: '创建时间',
                            },
                                updateBy: {
          label: '更新者',
                            },
                                updateTime: {
          label: '更新时间',
                            },
                                delFlag: {
          label: '删除标志',
                            },
    // createBy: {
    //   label: '创建者',
    // },
    // createTime: {
    //   label: '创建时间',
    // },
    // updateBy: {
    //   label: '更新者',
    // },
    // updateTime: {
    //   label: '更新时间',
    // },
    // delFlag: {
    //   label: '删除标志',
    // },
          }
  })
@@ -172,4 +238,68 @@
  })
const makeInvoice = (row: any) => {
  proxy.$modal.confirm('是否确认银行流水号为"' + row.bankFlowNo + '"?').then(function () {
    return confirmFundFlow(row.id);
  }).then(() => {
    onLoad(page.value);
    proxy.$modal.msgSuccess("确认成功");
  }).catch(() => { });
}
const claimDialogRef = ref();
// fundFlowId
const handleClaim = (row: any) => {
  getFundFlow(row.id).then((response) => {
    if (response.code == 200) {
       listFundFlowClaimDetail({ fundFlowId: row.id }).then((res) => {
      if (res.code == 200) {
        response.data.claimDetails = res.rows;
        claimDialogRef.value.open(response.data);
        claimDialogRef.value.open(response.data, 'edit');
      }
    })
    }
  })
};
const handleClaimSubmit = (payload) => {
  addFundFlowClaimDetailClaim(payload.claimDetails, payload.id).then((response) => {
    if (response.code == 200) {
      onLoad(page.value);
      proxy.$modal.msgSuccess("认领成功");
      claimDialogRef.value.handleCancel();
    }
  })
  // console.log("最终提交给后端的数据包:", payload);
};
const handleDetails = (row: any) => {
  getFundFlow(row.id).then((response) => {
    if (response.code == 200) {
       listFundFlowClaimDetail({ fundFlowId: row.id }).then((res) => {
      if (res.code == 200) {
        response.data.claimDetails = res.rows;
       claimDialogRef.value.open(response.data, 'view');
      }
    })
    }
  })
};
const logModalRef = ref(null);
const handleFlow = (row: any,) => {
  // 这里可以从 row 中直接获取日志,或者调用后端接口查询
  listFundFlowLog({flowId:row.id}).then((res) => {
    if (res.code == 200) {
     logModalRef.value.open(res.rows,'payable');
    }
  });
}
/* listFundFlowClaimDetail */
</script>
ui/admin-ui3/src/views/cwgl/fundFlowClaimDetail/index.vue
@@ -1,48 +1,19 @@
<template>
  <basicContainer >
    <avue-crud
        :option="option"
        :table-loading="pageF.loading"
        :data="tableData"
        :page="page"
        :permission="permissionList"
        :before-open="beforeOpen"
        v-model="form"
        ref="crudRef"
        @row-update="rowUpdate"
        @row-save="rowSave"
        @refresh-change="refreshChange"
        @row-del="rowDel"
        @search-change="searchChange"
        @search-reset="searchReset"
        @selection-change="selectionChange"
        @current-change="currentChange"
        @size-change="sizeChange"
        @on-load="onLoad"
    >
    <avue-crud :option="option" :table-loading="pageF.loading" :data="tableData" :page="page"
      :permission="permissionList" :before-open="beforeOpen" v-model="form" ref="crudRef" @row-update="rowUpdate"
      @row-save="rowSave" @refresh-change="refreshChange" @row-del="rowDel" @search-change="searchChange"
      @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:fundFlowClaimDetail:edit']"
        <el-button type="success" icon="Edit" :disabled="pageF.single" v-hasPermi="['cwgl:fundFlowClaimDetail:edit']"
            @click="handleUpdate">修改
        </el-button>
        <el-button
            type="danger"
            icon="Delete"
            :disabled="pageF.multiple"
            @click="handleDelete"
            v-hasPermi="['cwgl:fundFlowClaimDetail:remove']"
        >删除
        <el-button type="danger" icon="Delete" :disabled="pageF.multiple" @click="handleDelete"
          v-hasPermi="['cwgl:fundFlowClaimDetail:remove']">删除
        </el-button>
        <el-button
            type="warning"
            plain
            icon="Download"
            @click="handleExport"
            v-hasPermi="['cwgl:fundFlowClaimDetail:export']"
        >导出
        <el-button type="warning" plain icon="Download" @click="handleExport"
          v-hasPermi="['cwgl:fundFlowClaimDetail:export']">导出
        </el-button>
      </template>
    </avue-crud>
@@ -92,15 +63,19 @@
                                rules: [
              {
                required: true,
                message: "资金流水ID不能为空", trigger: "blur" }
            ],                  },
          message: "资金流水ID不能为空", trigger: "blur"
        }
      ],
    },
                                billNo: {
          label: '账单编号',
                                rules: [
              {
                required: true,
                message: "账单编号不能为空", trigger: "blur" }
            ],                  },
          message: "账单编号不能为空", trigger: "blur"
        }
      ],
    },
                                relatedCompanyType: {
          label: '关联企业类型',
                            },