张诺
2026-01-30 e2c871b1be0ff8cfa61e55325095ee1c79932ddd
tms 开发承运商运费结算模块
已添加1个文件
已修改2个文件
472 ■■■■■ 文件已修改
src/api/inventoryManagement/CarrierManagement.js 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/procurementManagement/freightSettlement/index.vue 421 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/CarrierManagement.js
@@ -154,3 +154,52 @@
        method: "delete",
    });
}
// è¿è´¹ç»“算单管理
// åˆ†é¡µæŸ¥è¯¢è¿è´¹ç»“算单列表
// /fakeWarehousing/freightSettlement/list
export const getFreightSettlementPage = (params) => {
    return request({
        url: "/fakeWarehousing/freightSettlement/list",
        method: "get",
        params,
    });
}
// æ ¹æ®ç»“ç®—ID查询
// /fakeWarehousing/freightSettlement/{id}
export const getFreightSettlementDetail = (id) => {
    return request({
        url: `/fakeWarehousing/freightSettlement/${id}`,
        method: "get",
    });
}
// æ–°å¢žè¿è´¹ç»“算单
// /fakeWarehousing/freightSettlement/
export const addFreightSettlement = (data) => {
    return request({
        url: "/fakeWarehousing/freightSettlement",
        method: "post",
        data,
    });
}
// ä¿®æ”¹è¿è´¹ç»“算单
// /fakeWarehousing/freightSettlement/
export const updateFreightSettlement = (data) => {
    return request({
        url: "/fakeWarehousing/freightSettlement",
        method: "put",
        data,
    });
}
// åˆ é™¤è¿è´¹ç»“算单
// /fakeWarehousing/freightSettlement/{ids}
export const deleteFreightSettlement = (ids) => {
    return request({
        url: `/fakeWarehousing/freightSettlement/${ids}`,
        method: "delete",
    });
}
src/views/inventoryManagement/procurementManagement/freightSettlement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,421 @@
<template>
  <div class="app-container">
    <!-- æœç´¢æ  -->
    <div class="search_form" style="display: flex; justify-content: space-between; gap: 12px; align-items: flex-start;">
      <div style="display: flex; align-items: center; flex-wrap: wrap; gap: 10px;">
        <span class="search_title">结算单号:</span>
        <el-input
          v-model="searchForm.settleNo"
          style="width: 240px"
          placeholder="结算单号"
          clearable
          :prefix-icon="Search"
          @keyup.enter="handleQuery"
        />
        <span class="search_title">承运商:</span>
        <el-input
            v-model="searchForm.carrierName"
            style="width: 240px"
            placeholder="承运商"
            clearable
            :prefix-icon="Search"
            @keyup.enter="handleQuery"
        />
        <el-button type="primary" @click="handleQuery">搜索</el-button>
        <el-button @click="resetQuery">重置</el-button>
      </div>
      <div>
        <el-button type="primary" @click="openCreate">创建结算单</el-button>
      </div>
    </div>
    <!-- è¡¨æ ¼ -->
    <el-row :gutter="20">
      <el-col :span="24">
        <div class="table_list">
          <el-table
            border
            v-loading="tableLoading"
            :data="tableData"
            :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
            height="calc(100vh - 18.5em)"
            :highlight-current-row="true"
            style="width: 100%"
            stripe
            tooltip-effect="dark"
            class="lims-table"
          >
            <el-table-column align="center" label="序号" type="index" width="60" />
            <el-table-column label="结算单号" prop="settleNo" width="180" show-overflow-tooltip />
            <el-table-column label="承运商" prop="carrierName" width="200" show-overflow-tooltip />
            <el-table-column label="发票号码" prop="invoiceNo" width="160" show-overflow-tooltip />
            <el-table-column label="发票金额(元)" prop="invoiceAmt" width="140" align="right">
              <template #default="{ row }">{{ toMoney(row.invoiceAmt) }}</template>
            </el-table-column>
            <el-table-column label="开票日期" prop="invoiceDate" width="120" />
            <el-table-column label="更新时间" prop="updateTime" width="170" />
            <el-table-column fixed="right" label="操作" width="200" align="center">
              <template #default="scope">
                <el-button link type="primary" size="small" @click.stop="openView(scope.row)">查看</el-button>
                <el-button link type="primary" size="small" @click.stop="openEdit(scope.row)">编辑</el-button>
                <el-button link type="danger" size="small" @click.stop="handleDelete(scope.row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
          <pagination
            v-show="total > 0"
            @pagination="paginationSearch"
            :total="total"
            :layout="page.layout"
            :page="page.current"
            :limit="page.size"
          />
        </div>
      </el-col>
    </el-row>
    <!-- åˆ›å»º/编辑弹窗 -->
    <el-dialog
      v-model="editVisible"
      :title="editMode === 'create' ? '创建结算单' : '编辑结算单'"
      width="720px"
      :close-on-click-modal="false"
      destroy-on-close
    >
      <el-form ref="editFormRef" :model="editForm" :rules="rules" label-width="120px">
        <el-row :gutter="12">
          <el-col :span="12">
            <el-form-item label="结算单号" prop="settleNo">
              <el-input v-model="editForm.settleNo" placeholder="如 FS-20260130-0001" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="承运商名称" prop="carrierName">
              <el-input v-model="editForm.carrierName" placeholder="请输入承运商名称" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="发票号码" prop="invoiceNo">
              <el-input v-model="editForm.invoiceNo" placeholder="请输入发票号" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="发票金额(元)" prop="invoiceAmt">
              <el-input-number v-model="editForm.invoiceAmt" :min="0" :precision="2" style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="开票日期" prop="invoiceDate">
              <el-date-picker
                v-model="editForm.invoiceDate"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                placeholder="请选择"
                clearable
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <el-button @click="editVisible = false">取消</el-button>
        <el-button type="primary" :loading="saving" @click="submitEdit">确定</el-button>
      </template>
    </el-dialog>
    <!-- æŸ¥çœ‹å¼¹çª— -->
    <el-dialog v-model="viewVisible" title="结算单信息查看" width="720px" destroy-on-close>
      <el-descriptions :column="2" border>
        <el-descriptions-item label="结算单号">{{ viewRow.settleNo }}</el-descriptions-item>
        <el-descriptions-item label="承运商">{{ viewRow.carrierName }}</el-descriptions-item>
        <el-descriptions-item label="发票号码">{{ viewRow.invoiceNo }}</el-descriptions-item>
        <el-descriptions-item label="发票金额(元)">{{ toMoney(viewRow.invoiceAmt) }}</el-descriptions-item>
        <el-descriptions-item label="开票日期">{{ viewRow.invoiceDate }}</el-descriptions-item>
        <el-descriptions-item label="创建时间">{{ viewRow.createTime }}</el-descriptions-item>
        <el-descriptions-item label="更新时间">{{ viewRow.updateTime }}</el-descriptions-item>
      </el-descriptions>
      <template #footer>
        <el-button @click="viewVisible = false">关闭</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { Search } from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox } from "element-plus";
import pagination from "@/components/PIMTable/Pagination.vue";
import dayjs from "dayjs";
import {
  getFreightSettlementPage,
  getFreightSettlementDetail,
  addFreightSettlement,
  updateFreightSettlement,
  deleteFreightSettlement,
} from "@/api/inventoryManagement/CarrierManagement";
const toMoney = (v) => {
  if (v === undefined || v === null || v === "") return "0.00";
  const n = Number(v);
  return Number.isFinite(n) ? n.toFixed(2) : "0.00";
};
// é€‚配后端字段
const normalizeRow = (raw = {}) => {
  return {
    id: raw.id,
    settleNo: raw.settleNo,
    carrierName: raw.carrierName,
    invoiceNo: raw.invoiceNo,
    invoiceAmt: raw.invoiceAmt,
    invoiceDate: raw.invoiceDate,
    createTime: raw.createTime,
    updateTime: raw.updateTime,
  };
};
// ------------------ é¡µé¢çŠ¶æ€ ------------------
const searchForm = reactive({
  keyword: "",
  invoiceDateRange: [],
  invoiceDateStart: undefined,
  invoiceDateEnd: undefined,
});
const page = reactive({
  current: 1,
  size: 10,
  layout: "total, sizes, prev, pager, next, jumper",
});
const total = ref(0);
const tableData = ref([]);
const tableLoading = ref(false);
const buildListParams = () => {
  return {
    current: page.current,
    size: page.size,
    settleNo: searchForm.settleNo || undefined,
    carrierName: searchForm.carrierName || undefined,
  };
};
const changeDateRange = (date) => {
  if (date && date.length === 2) {
    searchForm.invoiceDateStart = date[0];
    searchForm.invoiceDateEnd = date[1];
  } else {
    searchForm.invoiceDateStart = undefined;
    searchForm.invoiceDateEnd = undefined;
  }
  getList();
};
const clearRange = () => {
  searchForm.invoiceDateRange = [];
  getList();
};
const handleQuery = () => {
  page.current = 1;
  getList();
};
const resetQuery = () => {
  searchForm.keyword = "";
  searchForm.invoiceDateRange = [];
  searchForm.invoiceDateStart = undefined;
  searchForm.invoiceDateEnd = undefined;
  page.current = 1;
  getList();
};
const paginationSearch = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = async () => {
  tableLoading.value = true;
  try {
    const res = await getFreightSettlementPage(buildListParams());
    const data = res?.data ?? res;
    const records = data?.records ?? data?.rows ?? data?.list ?? [];
    const t = data?.total ?? data?.count ?? 0;
    tableData.value = (records || []).map(normalizeRow);
    total.value = t;
  } catch (e) {
    ElMessage.error(e?.message || e?.msg || "加载失败");
  } finally {
    tableLoading.value = false;
  }
};
// ------------------ åˆ›å»º/编辑/查看/删除 ------------------
const editVisible = ref(false);
const viewVisible = ref(false);
const editMode = ref("create");
const saving = ref(false);
const editFormRef = ref();
const editForm = reactive({
  id: undefined,
  settleNo: "",
  carrierName: "",
  invoiceNo: "",
  invoiceAmt: 0,
  invoiceDate: "",
});
const rules = {
  settleNo: [{ required: true, message: "请输入结算单号", trigger: "blur" }],
  carrierName: [{ required: true, message: "请输入承运商名称", trigger: "blur" }],
  invoiceNo: [{ required: true, message: "请输入发票号码", trigger: "blur" }],
  invoiceAmt: [{ required: true, message: "请输入发票金额", trigger: "change" },
    {validator: (rule, value, callback) => {if (value <= 0) {callback(new Error("发票金额必须大于0"));} else {callback();}}, trigger: "change",}],
  invoiceDate: [{ required: true, message: "请选择开票日期", trigger: "change" }],
};
const openCreate = () => {
  editMode.value = "create";
  Object.assign(editForm, {
    id: undefined,
    settleNo: `FS-${dayjs().format("YYYYMMDD")}-${String(Math.floor(Math.random() * 9000 + 1000))}`,
    carrierName: "",
    invoiceNo: "",
    invoiceAmt: 0,
    invoiceDate: dayjs().format("YYYY-MM-DD"),
  });
  editVisible.value = true;
  queueMicrotask(() => editFormRef.value?.clearValidate?.());
};
const openEdit = async (row) => {
  editMode.value = "edit";
  Object.assign(editForm, { ...row });
  if (row?.id) {
    try {
      const res = await getFreightSettlementDetail(row.id);
      const detail = res?.data ?? res;
      Object.assign(editForm, normalizeRow(detail));
    } catch {
      // ignore
    }
  }
  editVisible.value = true;
  queueMicrotask(() => editFormRef.value?.clearValidate?.());
};
const viewRow = reactive({});
const openView = async (row) => {
  Object.assign(viewRow, row);
  viewVisible.value = true;
  if (row?.id) {
    try {
      const res = await getFreightSettlementDetail(row.id);
      const detail = res?.data ?? res;
      Object.assign(viewRow, normalizeRow(detail));
    } catch {
      // ignore
    }
  }
};
const submitEdit = async () => {
  saving.value = true;
  try {
    await editFormRef.value?.validate?.();
    const payload = {
      id: editForm.id,
      settleNo: editForm.settleNo,
      carrierName: editForm.carrierName,
      invoiceNo: editForm.invoiceNo,
      invoiceAmt: editForm.invoiceAmt,
      invoiceDate: editForm.invoiceDate,
    };
    if (editMode.value === "create") {
      await addFreightSettlement(payload);
      ElMessage.success("创建成功");
      page.current = 1;
    } else {
      await updateFreightSettlement(payload);
      ElMessage.success("更新成功");
    }
    editVisible.value = false;
    await getList();
  } catch (e) {
    if (e?.message) ElMessage.error(e.message);
  } finally {
    saving.value = false;
  }
};
const handleDelete = async (row) => {
  await ElMessageBox.confirm(`确认删除结算单【${row.settleNo}】?`, "提示", {
    confirmButtonText: "删除",
    cancelButtonText: "取消",
    type: "warning",
  });
  tableLoading.value = true;
  try {
    await deleteFreightSettlement(row.id);
    ElMessage.success("删除成功");
    // åˆ é™¤åŽå¦‚果当前页无数据则回退
    const res = await getFreightSettlementPage(buildListParams());
    const data = res?.data ?? res;
    const records = data?.records ?? data?.rows ?? data?.list ?? [];
    if ((records || []).length === 0 && page.current > 1) {
      page.current -= 1;
    }
    await getList();
  } catch (e) {
    ElMessage.error(e?.message || e?.msg || "删除失败");
  } finally {
    tableLoading.value = false;
  }
};
onMounted(() => {
  getList();
});
</script>
<style scoped lang="scss">
.el-pagination {
  width: 100%;
  height: 55px;
  display: flex;
  justify-content: flex-end;
  float: right;
  flex-direction: row;
  align-items: center;
  background: #fff;
  margin: -20px 0 0 0;
  padding: 0 20px;
}
.pagination-container {
  margin-top: 0;
}
</style>
src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue
@@ -17,7 +17,7 @@
          gap: 10px;
        "
      >
        <span class="search_title">关键字:</span>
        <span class="search_title">关键字查询:</span>
        <el-input
          v-model="searchForm.keyword"
          style="width: 240px"