feat: 库存管理三表单与详情页签分组、采购台账分栏、油品联动与电话校验、冻结解冻修复
已添加6个文件
已修改6个文件
3025 ■■■■■ 文件已修改
src/api/basicData/oilDepot.js 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/basicData/tankInfo.js 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockInventory.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/oilDepot/index.vue 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/tankInfo/index.vue 324 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/FrozenAndThaw.vue 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/New.vue 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/OilStockInDialog.vue 791 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue 759 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/Record.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/basicData/oilDepot.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
import request from '@/utils/request'
// åˆ†é¡µæŸ¥è¯¢æ²¹åº“信息列表
export function listOilDepot(query) {
  return request({
    url: '/basic/oilDepot/list',
    method: 'get',
    params: query
  })
}
// æŸ¥è¯¢å…¨éƒ¨æ²¹åº“信息(供下拉选择)
export function allOilDepot() {
  return request({
    url: '/basic/oilDepot/all',
    method: 'get'
  })
}
// æŸ¥è¯¢æ²¹åº“信息详情
export function getOilDepot(id) {
  return request({
    url: '/basic/oilDepot/' + id,
    method: 'get'
  })
}
// æ–°å¢žæ²¹åº“信息
export function addOilDepot(data) {
  return request({
    url: '/basic/oilDepot/add',
    method: 'post',
    data: data
  })
}
// ä¿®æ”¹æ²¹åº“信息
export function updateOilDepot(data) {
  return request({
    url: '/basic/oilDepot/update',
    method: 'post',
    data: data
  })
}
// åˆ é™¤æ²¹åº“信息
export function delOilDepot(ids) {
  return request({
    url: '/basic/oilDepot/del',
    method: 'delete',
    data: ids
  })
}
src/api/basicData/tankInfo.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
import request from '@/utils/request'
// åˆ†é¡µæŸ¥è¯¢å‚¨ç½ä¿¡æ¯åˆ—表
export function listTankInfo(query) {
  return request({
    url: '/basic/tankInfo/list',
    method: 'get',
    params: query
  })
}
// æŸ¥è¯¢å…¨éƒ¨å‚¨ç½ä¿¡æ¯ï¼ˆä¾›ä¸‹æ‹‰é€‰æ‹©ï¼‰
export function allTankInfo() {
  return request({
    url: '/basic/tankInfo/all',
    method: 'get'
  })
}
// æŸ¥è¯¢å‚¨ç½ä¿¡æ¯è¯¦æƒ…
export function getTankInfo(id) {
  return request({
    url: '/basic/tankInfo/' + id,
    method: 'get'
  })
}
// æ–°å¢žå‚¨ç½ä¿¡æ¯
export function addTankInfo(data) {
  return request({
    url: '/basic/tankInfo/add',
    method: 'post',
    data: data
  })
}
// ä¿®æ”¹å‚¨ç½ä¿¡æ¯
export function updateTankInfo(data) {
  return request({
    url: '/basic/tankInfo/update',
    method: 'post',
    data: data
  })
}
// åˆ é™¤å‚¨ç½ä¿¡æ¯
export function delTankInfo(ids) {
  return request({
    url: '/basic/tankInfo/del',
    method: 'delete',
    data: ids
  })
}
src/api/inventoryManagement/stockInventory.js
@@ -61,6 +61,15 @@
    });
};
// æ–°å¢žæ²¹å“å‡ºåº“记录(油品出库专用表单,仅创建记录,不调整库存)
export const addOilOutRecordOnly = (params) => {
    return request({
        url: "/stockInventory/addOilOutRecordOnly",
        method: "post",
        data: params,
    });
};
export const getStockInventoryReportList = (params) => {
    return request({
        url: "/stockInventory/stockInventoryPage",
src/views/basicData/oilDepot/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,297 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div>
        <el-form :model="searchForm"
                 :inline="true">
          <el-form-item label="油库名称:">
            <el-input v-model="searchForm.depotName"
                      placeholder="请输入"
                      clearable
                      prefix-icon="Search"
                      @change="handleQuery" />
          </el-form-item>
          <el-form-item label="所属公司:">
            <el-input v-model="searchForm.company"
                      placeholder="请输入"
                      clearable
                      prefix-icon="Search"
                      @change="handleQuery" />
          </el-form-item>
          <el-form-item>
            <el-button type="primary"
                       @click="handleQuery"> æœç´¢
            </el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
    <div class="table_list">
      <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
        <el-button type="primary"
                   @click="openForm('add')">新增油库</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除
        </el-button>
      </div>
      <el-table :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange">
        <el-table-column align="center"
                         type="selection"
                         width="55" />
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column label="油库名称"
                         prop="depotName"
                         show-overflow-tooltip />
        <el-table-column label="负责人"
                         prop="chargePerson"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="联系电话"
                         prop="phone"
                         width="140" />
        <el-table-column label="启用日期"
                         prop="enableDate"
                         width="120" />
        <el-table-column label="所属公司"
                         prop="company"
                         show-overflow-tooltip />
        <el-table-column label="创建时间"
                         prop="createTime"
                         width="180">
          <template #default="scope">
            {{ scope.row.createTime ? scope.row.createTime.replace('T', ' ').slice(0, 19) : '--' }}
          </template>
        </el-table-column>
        <el-table-column fixed="right"
                         label="操作"
                         width="120"
                         align="center">
          <template #default="scope">
            <el-button link
                       type="primary"
                       @click="openForm('edit', scope.row)">编辑
            </el-button>
            <el-button link
                       type="danger"
                       @click="handleDelete([scope.row])">删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0"
                  :total="total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="paginationChange" />
    </div>
    <FormDialog v-model="dialogFormVisible"
                :title="operationType === 'add' ? '新增油库' : '编辑油库'"
                :width="'40%'"
                :operation-type="operationType"
                @close="closeDia"
                @confirm="submitForm"
                @cancel="closeDia">
      <el-form :model="form"
               label-width="120px"
               label-position="top"
               :rules="rules"
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="油库名称:"
                          prop="depotName">
              <el-input v-model="form.depotName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="负责人:"
                          prop="chargePerson">
              <el-input v-model="form.chargePerson"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="联系电话:"
                          prop="phone">
              <el-input v-model="form.phone"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="启用日期:"
                          prop="enableDate">
              <el-date-picker style="width: 100%"
                              v-model="form.enableDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="所属公司:"
                          prop="company">
              <el-input v-model="form.company"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </FormDialog>
  </div>
</template>
<script setup>
  import FormDialog from "@/components/Dialog/FormDialog.vue";
  import pagination from "@/components/PIMTable/Pagination.vue";
  import { ref, reactive, toRefs, getCurrentInstance } from "vue";
  import { Search } from "@element-plus/icons-vue";
  import { ElMessageBox } from "element-plus";
  import {
    listOilDepot,
    addOilDepot,
    updateOilDepot,
    delOilDepot,
  } from "@/api/basicData/oilDepot.js";
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
  const selectedRows = ref([]);
  const tableLoading = ref(false);
  const dialogFormVisible = ref(false);
  const operationType = ref("");
  const page = reactive({
    current: 1,
    size: 10,
  });
  const total = ref(0);
  const data = reactive({
    searchForm: {
      depotName: "",
      company: "",
    },
    form: {
      id: null,
      depotName: "",
      chargePerson: "",
      phone: "",
      enableDate: "",
      company: "",
    },
    rules: {
      depotName: [{ required: true, message: "请输入油库名称", trigger: "blur" }],
      company: [{ required: true, message: "请输入所属公司", trigger: "blur" }],
    },
  });
  const { searchForm, form, rules } = toRefs(data);
  const getList = () => {
    tableLoading.value = true;
    listOilDepot({ ...searchForm.value, ...page })
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records || [];
        total.value = res.data.total || 0;
      })
      .catch(() => {
        tableLoading.value = false;
      });
  };
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  const openForm = (type, row) => {
    operationType.value = type;
    form.value = {};
    proxy.resetForm("formRef");
    dialogFormVisible.value = true;
    if (type === "edit" && row) {
      const { id, depotName, chargePerson, phone, enableDate, company } = row;
      form.value = { id, depotName, chargePerson, phone, enableDate, company };
    }
  };
  const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        const api = form.value.id ? updateOilDepot : addOilDepot;
        api(form.value).then(() => {
          proxy.$modal.msgSuccess("操作成功");
          closeDia();
          getList();
        });
      }
    });
  };
  const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
  };
  const handleDelete = rows => {
    let ids = [];
    if (rows) {
      ids = rows.map(item => item.id);
    } else if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        delOilDepot(ids).then(() => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  getList();
</script>
<style scoped lang="scss">
  .search_form {
    margin-bottom: 16px;
  }
</style>
src/views/basicData/tankInfo/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,324 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div>
        <el-form :model="searchForm"
                 :inline="true">
          <el-form-item label="储罐号:">
            <el-input v-model="searchForm.tankNo"
                      placeholder="请输入"
                      clearable
                      prefix-icon="Search"
                      @change="handleQuery" />
          </el-form-item>
          <el-form-item label="大类:">
            <el-input v-model="searchForm.category"
                      placeholder="请输入"
                      clearable
                      prefix-icon="Search"
                      @change="handleQuery" />
          </el-form-item>
          <el-form-item>
            <el-button type="primary"
                       @click="handleQuery"> æœç´¢
            </el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
    <div class="table_list">
      <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
        <el-button type="primary"
                   @click="openForm('add')">新增储罐</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete()">删除
        </el-button>
      </div>
      <el-table :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange">
        <el-table-column align="center"
                         type="selection"
                         width="55" />
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column label="储罐号"
                         prop="tankNo"
                         min-width="140"
                         show-overflow-tooltip />
        <el-table-column label="大类"
                         prop="category"
                         min-width="120"
                         show-overflow-tooltip />
        <el-table-column label="油品"
                         prop="oilProduct"
                         min-width="120"
                         show-overflow-tooltip />
        <el-table-column label="罐容"
                         prop="capacity"
                         min-width="120" />
        <el-table-column label="库存余量(m³)"
                         prop="remaining"
                         min-width="140" />
        <el-table-column label="租客信息"
                         prop="tenantInfo"
                         min-width="160"
                         show-overflow-tooltip />
        <el-table-column label="创建时间"
                         prop="createTime"
                         min-width="180">
          <template #default="scope">
            {{ scope.row.createTime ? scope.row.createTime.replace('T', ' ').slice(0, 19) : '--' }}
          </template>
        </el-table-column>
        <el-table-column fixed="right"
                         label="操作"
                         width="120"
                         align="center">
          <template #default="scope">
            <el-button link
                       type="primary"
                       @click="openForm('edit', scope.row)">编辑
            </el-button>
            <el-button link
                       type="danger"
                       @click="handleDelete([scope.row])">删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0"
                  :total="total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="paginationChange" />
    </div>
    <FormDialog v-model="dialogFormVisible"
                :title="operationType === 'add' ? '新增储罐' : '编辑储罐'"
                :width="'40%'"
                :operation-type="operationType"
                @close="closeDia"
                @confirm="submitForm"
                @cancel="closeDia">
      <el-form :model="form"
               label-width="120px"
               label-position="top"
               :rules="rules"
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="储罐号:"
                          prop="tankNo">
              <el-input v-model="form.tankNo"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="大类:"
                          prop="category">
              <el-input v-model="form.category"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="油品:"
                          prop="oilProduct">
              <el-select v-model="form.oilProduct"
                         placeholder="请选择"
                         clearable
                         style="width: 100%">
                <el-option label="æ±½#92"
                           value="æ±½#92" />
                <el-option label="æ±½#95"
                           value="æ±½#95" />
                <el-option label="柴#0"
                           value="柴#0" />
                <el-option label="柴#-35"
                           value="柴#-35" />
                <el-option label="原油"
                           value="原油" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="罐容:"
                          prop="capacity">
              <el-input-number v-model="form.capacity"
                               :precision="2"
                               :step="1"
                               :min="0"
                               style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="库存余量(m³):"
                          prop="remaining">
              <el-input-number v-model="form.remaining"
                               :precision="2"
                               :step="1"
                               :min="0"
                               style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="租客信息:"
                          prop="tenantInfo">
              <el-input v-model="form.tenantInfo"
                        placeholder="请输入租客信息"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </FormDialog>
  </div>
</template>
<script setup>
  import FormDialog from "@/components/Dialog/FormDialog.vue";
  import pagination from "@/components/PIMTable/Pagination.vue";
  import { ref, reactive, toRefs, getCurrentInstance } from "vue";
  import { Search } from "@element-plus/icons-vue";
  import { ElMessageBox } from "element-plus";
  import {
    listTankInfo,
    addTankInfo,
    updateTankInfo,
    delTankInfo,
  } from "@/api/basicData/tankInfo.js";
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
  const selectedRows = ref([]);
  const tableLoading = ref(false);
  const dialogFormVisible = ref(false);
  const operationType = ref("");
  const page = reactive({
    current: 1,
    size: 10,
  });
  const total = ref(0);
  const data = reactive({
    searchForm: {
      tankNo: "",
      category: "",
    },
    form: {
      id: null,
      tankNo: "",
      category: "",
      oilProduct: "",
      capacity: null,
      remaining: null,
      tenantInfo: "",
    },
    rules: {
      tankNo: [{ required: true, message: "请输入储罐号", trigger: "blur" }],
      category: [{ required: true, message: "请输入大类", trigger: "blur" }],
    },
  });
  const { searchForm, form, rules } = toRefs(data);
  const getList = () => {
    tableLoading.value = true;
    listTankInfo({ ...searchForm.value, ...page })
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records || [];
        total.value = res.data.total || 0;
      })
      .catch(() => {
        tableLoading.value = false;
      });
  };
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  const openForm = (type, row) => {
    operationType.value = type;
    form.value = {};
    proxy.resetForm("formRef");
    dialogFormVisible.value = true;
    if (type === "edit" && row) {
      const { id, tankNo, category, oilProduct, capacity, remaining, tenantInfo } = row;
      form.value = { id, tankNo, category, oilProduct, capacity, remaining, tenantInfo };
    }
  };
  const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        const api = form.value.id ? updateTankInfo : addTankInfo;
        api(form.value).then(() => {
          proxy.$modal.msgSuccess("操作成功");
          closeDia();
          getList();
        });
      }
    });
  };
  const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
  };
  const handleDelete = rows => {
    let ids = [];
    if (rows) {
      ids = rows.map(item => item.id);
    } else if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        delTankInfo(ids).then(() => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  getList();
</script>
<style scoped lang="scss">
  .search_form {
    margin-bottom: 16px;
  }
</style>
src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue
@@ -9,6 +9,10 @@
  >
    <div class="detail-content">
      <div class="detail-table-wrapper">
        <el-tabs v-model="detailTab"
                 class="detail-tabs">
          <el-tab-pane label="库存信息"
                       name="main">
        <el-table
          :data="tableData"
          border
@@ -94,6 +98,160 @@
            </template>
          </el-table-column>
        </el-table>
          </el-tab-pane>
          <el-tab-pane label="入库信息"
                       name="in">
            <el-table
              :data="tableData"
              border
              v-loading="tableLoading"
              style="width: 100%"
              height="100%"
            >
          <el-table-column label="入库信息"
                           align="center"
                           :header-cell-style="groupHeaderStyle">
            <el-table-column label="油品" prop="oilProduct" min-width="90" show-overflow-tooltip />
            <el-table-column label="入库名称" prop="oilDepotName" min-width="130" show-overflow-tooltip />
            <el-table-column label="入库类别" prop="inboundCategory" min-width="100" show-overflow-tooltip />
            <el-table-column label="储罐号" prop="tankNo" min-width="100" show-overflow-tooltip />
            <el-table-column label="入库时间" prop="inboundTime" min-width="160" show-overflow-tooltip />
            <el-table-column label="原出厂数量(吨)" prop="originalQuantity" min-width="120" show-overflow-tooltip />
            <el-table-column label="客户名称" prop="customerName" min-width="120" show-overflow-tooltip />
            <el-table-column label="客户对接人" prop="contactPerson" min-width="100" show-overflow-tooltip />
            <el-table-column label="对接人电话" prop="contactPhone" min-width="120" show-overflow-tooltip />
          </el-table-column>
          <el-table-column label="物流信息"
                           align="center"
                           :header-cell-style="groupHeaderStyle">
            <el-table-column label="司机姓名" prop="driverName" min-width="100" show-overflow-tooltip />
            <el-table-column label="司机电话" prop="driverPhone" min-width="120" show-overflow-tooltip />
            <el-table-column label="押运员姓名" prop="escortName" min-width="100" show-overflow-tooltip />
            <el-table-column label="押运员电话" prop="escortPhone" min-width="120" show-overflow-tooltip />
            <el-table-column label="车牌号" prop="truckPlateNo" min-width="110" show-overflow-tooltip />
            <el-table-column label="车挂牌号" prop="trailerPlateNo" min-width="110" show-overflow-tooltip />
          </el-table-column>
          <el-table-column label="卸油操作信息"
                           align="center"
                           :header-cell-style="groupHeaderStyle">
            <el-table-column label="车体/铅封检验" prop="tankInspectionStatus" min-width="120" show-overflow-tooltip />
            <el-table-column label="卸车鹤位" prop="unloadingCranePosition" min-width="100" show-overflow-tooltip />
            <el-table-column label="密度" prop="density" min-width="80" show-overflow-tooltip />
            <el-table-column label="卸油拍照" prop="unloadPhotos" align="center" min-width="90">
              <template #default="scope">
                <el-image
                  v-if="toPhotoArray(scope.row.unloadPhotos).length"
                  :src="getPhotoUrl(toPhotoArray(scope.row.unloadPhotos)[0])"
                  :preview-src-list="getPhotoUrls(scope.row.unloadPhotos)"
                  preview-teleported
                  fit="cover"
                  style="width: 40px; height: 40px; border-radius: 4px"
                />
                <span v-else>--</span>
              </template>
            </el-table-column>
          </el-table-column>
          <el-table-column label="过磅数据"
                           align="center"
                           :header-cell-style="groupHeaderStyle">
            <el-table-column label="毛重(吨)" prop="grossWeight" min-width="90" show-overflow-tooltip />
            <el-table-column label="皮重(吨)" prop="tareWeight" min-width="90" show-overflow-tooltip />
            <el-table-column label="净重(吨)" prop="netWeight" min-width="90" show-overflow-tooltip />
            <el-table-column label="亏量(吨)" prop="lossQuantity" min-width="90" show-overflow-tooltip />
            <el-table-column label="实际入库体积(升)" prop="actualVolume" min-width="130" show-overflow-tooltip />
            <el-table-column label="磅单" prop="poundPhotos" align="center" min-width="90">
              <template #default="scope">
                <el-image
                  v-if="toPhotoArray(scope.row.poundPhotos).length"
                  :src="getPhotoUrl(toPhotoArray(scope.row.poundPhotos)[0])"
                  :preview-src-list="getPhotoUrls(scope.row.poundPhotos)"
                  preview-teleported
                  fit="cover"
                  style="width: 40px; height: 40px; border-radius: 4px"
                />
                <span v-else>--</span>
              </template>
            </el-table-column>
          </el-table-column>
            </el-table>
          </el-tab-pane>
          <el-tab-pane label="出库信息"
                       name="out">
            <el-table
              :data="tableData"
              border
              v-loading="tableLoading"
              style="width: 100%"
              height="100%"
            >
          <el-table-column label="出库信息"
                           align="center"
                           :header-cell-style="groupHeaderStyle">
            <el-table-column label="出库类别" prop="outCategory" min-width="90" show-overflow-tooltip />
            <el-table-column label="储库名称" prop="outOilDepotName" min-width="130" show-overflow-tooltip />
            <el-table-column label="储罐号" prop="outTankNo" min-width="100" show-overflow-tooltip />
            <el-table-column label="出库日期" prop="outDate" min-width="110" show-overflow-tooltip />
            <el-table-column label="油品" prop="outOilProduct" min-width="90" show-overflow-tooltip />
            <el-table-column label="出库吨位(吨)" prop="outTonnage" min-width="110" show-overflow-tooltip />
            <el-table-column label="客户名称" prop="outCustomerName" min-width="120" show-overflow-tooltip />
            <el-table-column label="客户对接人" prop="outContactPerson" min-width="100" show-overflow-tooltip />
            <el-table-column label="对接人电话" prop="outContactPhone" min-width="120" show-overflow-tooltip />
          </el-table-column>
          <el-table-column label="装油操作信息"
                           align="center"
                           :header-cell-style="groupHeaderStyle">
            <el-table-column label="装油检验" prop="loadingInspectionStatus" min-width="90" show-overflow-tooltip />
            <el-table-column label="装车鹤位" prop="loadingCranePosition" min-width="100" show-overflow-tooltip />
            <el-table-column label="密度" prop="outDensity" min-width="80" show-overflow-tooltip />
            <el-table-column label="装油拍照" prop="loadingPhotos" align="center" min-width="90">
              <template #default="scope">
                <el-image
                  v-if="toPhotoArray(scope.row.loadingPhotos).length"
                  :src="getPhotoUrl(toPhotoArray(scope.row.loadingPhotos)[0])"
                  :preview-src-list="getPhotoUrls(scope.row.loadingPhotos)"
                  preview-teleported
                  fit="cover"
                  style="width: 40px; height: 40px; border-radius: 4px"
                />
                <span v-else>--</span>
              </template>
            </el-table-column>
          </el-table-column>
          <el-table-column label="出库过磅数据"
                           align="center"
                           :header-cell-style="groupHeaderStyle">
            <el-table-column label="毛重(吨)" prop="outGrossWeight" min-width="90" show-overflow-tooltip />
            <el-table-column label="皮重(吨)" prop="outTareWeight" min-width="90" show-overflow-tooltip />
            <el-table-column label="净重(吨)" prop="outNetWeight" min-width="90" show-overflow-tooltip />
            <el-table-column label="亏量(吨)" prop="outLossQuantity" min-width="90" show-overflow-tooltip />
            <el-table-column label="实际入库体积(升)" prop="outActualVolume" min-width="130" show-overflow-tooltip />
            <el-table-column label="磅单" prop="outPoundPhotos" align="center" min-width="90">
              <template #default="scope">
                <el-image
                  v-if="toPhotoArray(scope.row.outPoundPhotos).length"
                  :src="getPhotoUrl(toPhotoArray(scope.row.outPoundPhotos)[0])"
                  :preview-src-list="getPhotoUrls(scope.row.outPoundPhotos)"
                  preview-teleported
                  fit="cover"
                  style="width: 40px; height: 40px; border-radius: 4px"
                />
                <span v-else>--</span>
              </template>
            </el-table-column>
          </el-table-column>
          <el-table-column label="出库物流信息"
                           align="center"
                           :header-cell-style="groupHeaderStyle">
            <el-table-column label="司机姓名" prop="outDriverName" min-width="100" show-overflow-tooltip />
            <el-table-column label="司机电话" prop="outDriverPhone" min-width="120" show-overflow-tooltip />
            <el-table-column label="押运员姓名" prop="outEscortName" min-width="100" show-overflow-tooltip />
            <el-table-column label="押运员电话" prop="outEscortPhone" min-width="120" show-overflow-tooltip />
            <el-table-column label="车牌号" prop="outTruckPlateNo" min-width="110" show-overflow-tooltip />
            <el-table-column label="车挂牌号" prop="outTrailerPlateNo" min-width="110" show-overflow-tooltip />
          </el-table-column>
            </el-table>
          </el-tab-pane>
        </el-tabs>
      </div>
      <pagination
        v-show="total > 0"
@@ -137,6 +295,7 @@
const tableData = ref([]);
const tableLoading = ref(false);
const total = ref(0);
const detailTab = ref("main");
const page = reactive({
  current: 1,
  size: 20,
@@ -163,6 +322,41 @@
    .finally(() => {
      tableLoading.value = false;
    });
};
// å›¾ç‰‡åˆ—:后端返回为 JSON å­—符串,兼容数组/字符串两种形态
const toPhotoArray = (value) => {
  if (!value) {
    return [];
  }
  if (Array.isArray(value)) {
    return value;
  }
  if (typeof value === "string") {
    try {
      const parsed = JSON.parse(value);
      return Array.isArray(parsed) ? parsed : [];
    } catch (e) {
      return [];
    }
  }
  return [];
};
// å›¾ç‰‡åˆ—:取单张地址(支持字符串或 {url} å¯¹è±¡ï¼‰
const getPhotoUrl = (photo) =>
  typeof photo === "string" ? photo : photo?.url || photo?.previewURL || "";
// å›¾ç‰‡åˆ—:取全部地址用于预览
const getPhotoUrls = (photos) =>
  toPhotoArray(photos).map(getPhotoUrl).filter(Boolean);
// åˆ†ç»„父表头样式:区分各分类
const groupHeaderStyle = {
  background: "#e8f3ff",
  color: "#1d6fd8",
  fontWeight: "700",
  fontSize: "13px",
};
const paginationChange = (obj) => {
@@ -217,6 +411,21 @@
  min-height: 0;
}
.detail-tabs {
  height: 100%;
  display: flex;
  flex-direction: column;
}
:deep(.detail-tabs .el-tabs__content) {
  flex: 1;
  min-height: 0;
}
:deep(.detail-tabs .el-tab-pane) {
  height: 100%;
}
:deep(.batch-no-qty-detail-dialog .el-dialog) {
  max-width: calc(100vw - 48px);
}
src/views/inventoryManagement/stockManagement/FrozenAndThaw.vue
@@ -28,7 +28,7 @@
            :label="operationType === 'frozen' ? '冻结数量:' : '解冻数量:'"
            prop="lockedQuantity"
        >
          <el-input-number v-model="formState.lockedQuantity" :step="1" :min="maxCount > 0 ? 1 : 0" precision="0" style="width: 100%" :max="maxCount" :disabled="maxCount < 1" />
          <el-input-number v-model="formState.lockedQuantity" :step="0.01" :min="0" precision="2" style="width: 100%" :max="maxCount" :disabled="maxCount < 1" />
        </el-form-item>
      </el-form>
@@ -43,7 +43,7 @@
</template>
<script setup>
import {ref, computed, getCurrentInstance, onMounted} from "vue";
import {ref, computed, watch, getCurrentInstance, onMounted} from "vue";
import {frozenStockInventory, thawStockInventory} from "@/api/inventoryManagement/stockInventory.js";
import {frozenStockUninventory, thawStockUninventory} from "@/api/inventoryManagement/stockUninventory.js";
@@ -73,15 +73,21 @@
  lockedQuantity: undefined,
});
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
// å¼¹çª—开关用本地状态管理:父级打开时同步为 true,本地关闭后通知父级
const isShow = ref(false);
watch(
  () => props.visible,
  val => {
    isShow.value = val;
  },
  { immediate: true }
);
const requestClose = () => {
  isShow.value = false;
  emit('update:visible', false);
};
let { proxy } = getCurrentInstance()
@@ -91,7 +97,7 @@
    lockedQuantity: undefined,
    type: undefined,
  };
  isShow.value = false;
  requestClose();
};
const maxCount = computed(() => {
@@ -118,67 +124,48 @@
}
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
  // ä¸ä¾èµ–表单引用,直接手动校验
  if (!formState.value.type) {
    proxy.$modal.msgError("请选择库存类型");
    return;
  }
  const qty = Number(formState.value.lockedQuantity);
  if (!formState.value.lockedQuantity || qty <= 0) {
    proxy.$modal.msgError(
      `请输入${props.operationType === 'frozen' ? '冻结' : '解冻'}数量`
    );
    return;
  }
  if (qty > maxCount.value) {
    proxy.$modal.msgError(`数量不能超过${maxCount.value}`);
    return;
  }
      const data = Object.assign({}, formState.value);
  const doSubmit = (api, id) => {
    data.id = id;
    api(data).then(res => {
      if (res.code === 200) {
        requestClose();
        emit('completed');
        proxy.$modal.msgSuccess("提交成功");
      } else {
        proxy.$modal.msgError(res.msg);
      }
    });
  };
      if (formState.value.type === 'qualified') {
        data.id = props.record.qualifiedId;
        // å†»ç»“
        if (props.operationType === 'frozen') {
          frozenStockInventory(data).then(res => {
            if (res.code === 200) {
              // å…³é—­æ¨¡æ€æ¡†
              isShow.value = false;
              // å‘ŠçŸ¥çˆ¶ç»„件已完成
              emit('completed');
              proxy.$modal.msgSuccess("提交成功");
    doSubmit(
      props.operationType === 'frozen' ? frozenStockInventory : thawStockInventory,
      props.record.qualifiedId
    );
            } else {
              proxy.$modal.msgError(res.msg);
    doSubmit(
      props.operationType === 'frozen' ? frozenStockUninventory : thawStockUninventory,
      props.record.unQualifiedId
    );
            }
          })
        } else {
          thawStockInventory(data).then(res => {
            if (res.code === 200) {
              // å…³é—­æ¨¡æ€æ¡†
              isShow.value = false;
              // å‘ŠçŸ¥çˆ¶ç»„件已完成
              emit('completed');
              proxy.$modal.msgSuccess("提交成功");
            } else {
              proxy.$modal.msgError(res.msg);
            }
          })
        }
      } else {
        data.id = props.record.unQualifiedId;
        if (props.operationType === 'frozen') {
          frozenStockUninventory(data).then(res => {
            if (res.code === 200) {
              // å…³é—­æ¨¡æ€æ¡†
              isShow.value = false;
              // å‘ŠçŸ¥çˆ¶ç»„件已完成
              emit('completed');
              proxy.$modal.msgSuccess("提交成功");
            } else {
              proxy.$modal.msgError(res.msg);
            }
          })
        } else {
          thawStockUninventory(data).then(res => {
            if (res.code === 200) {
              // å…³é—­æ¨¡æ€æ¡†
              isShow.value = false;
              // å‘ŠçŸ¥çˆ¶ç»„件已完成
              emit('completed');
              proxy.$modal.msgSuccess("提交成功");
            } else {
              proxy.$modal.msgError(res.msg);
            }
          })
        }
      }
    }
  })
};
onMounted(() => {
src/views/inventoryManagement/stockManagement/New.vue
@@ -2,12 +2,14 @@
  <div>
    <el-dialog v-model="isShow"
               title="新增库存"
               width="800"
               width="60%"
               @close="closeModal">
      <el-form label-width="140px"
               :model="formState"
               label-position="top"
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="24">
        <el-form-item label="产品名称"
                      prop="productModelId"
                      :rules="[
@@ -22,16 +24,26 @@
            {{ formState.productName ? formState.productName : '选择产品' }}
          </el-button>
        </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
        <el-form-item label="规格"
                      prop="productModelName">
          <el-input v-model="formState.productModelName"
                    disabled />
        </el-form-item>
          </el-col>
          <el-col :span="12">
        <el-form-item label="单位"
                      prop="unit">
          <el-input v-model="formState.unit"
                    disabled />
        </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
        <el-form-item label="库存类型"
                      prop="type"
                      :rules="[
@@ -42,13 +54,16 @@
              }
            ]">
          <el-select v-model="formState.type"
                     placeholder="请选择库存类型">
                         placeholder="请选择库存类型"
                         style="width: 100%">
            <el-option label="合格库存"
                       value="qualified" />
            <el-option label="不合格库存"
                       value="unqualified" />
          </el-select>
        </el-form-item>
          </el-col>
          <el-col :span="12">
        <el-form-item label="库存数量"
                      prop="qualitity">
          <el-input-number v-model="formState.qualitity"
@@ -57,13 +72,12 @@
                           :precision="2"
                           style="width: 100%" />
        </el-form-item>
        <el-form-item label="批号"
                      prop="batchNo">
          <el-input v-model="formState.batchNo"
                    placeholder="请输入批号" />
        </el-form-item>
        <el-form-item v-if="formState.type === 'qualified'"
                      label="库存预警数量"
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col v-if="formState.type === 'qualified'"
                  :span="12">
            <el-form-item label="库存预警数量"
                      prop="warnNum">
          <el-input-number v-model="formState.warnNum"
                           :step="0.01"
@@ -72,11 +86,26 @@
                           :precision="2"
                           style="width: 100%" />
        </el-form-item>
          </el-col>
          <el-col :span="formState.type === 'qualified' ? 12 : 24">
            <el-form-item label="批号"
                          prop="batchNo">
              <el-input v-model="formState.batchNo"
                        placeholder="请输入批号"
                        style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
        <el-form-item label="备注"
                      prop="remark">
          <el-input v-model="formState.remark"
                    type="textarea" />
                        type="textarea"
                        style="width: 100%" />
        </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <!-- äº§å“é€‰æ‹©å¼¹çª— -->
      <ProductSelectDialog v-model="showProductSelectDialog"
@@ -114,8 +143,7 @@
  const emit = defineEmits(["update:visible", "completed"]);
  // å“åº”式数据(替代选项式的 data)
  const formState = ref({
  const buildEmptyForm = () => ({
    productId: undefined,
    productModelId: undefined,
    productName: "",
@@ -127,6 +155,8 @@
    warnNum: 0,
    remark: "",
  });
  const formState = ref(buildEmptyForm());
  const isShow = computed({
    get() {
@@ -152,19 +182,8 @@
  let { proxy } = getCurrentInstance();
  const closeModal = () => {
    // é‡ç½®è¡¨å•数据
    formState.value = {
      productId: undefined,
      productModelId: undefined,
      productName: "",
      productModelName: "",
      unit: "",
      type: undefined,
      qualitity: 0,
      batchNo: null,
      warnNum: 0,
      remark: "",
    };
    proxy.resetForm("formRef");
    formState.value = buildEmptyForm();
    isShow.value = false;
  };
@@ -186,29 +205,21 @@
  const handleSubmit = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        // éªŒè¯æ˜¯å¦é€‰æ‹©äº†äº§å“å’Œè§„æ ¼
        // éªŒè¯æ˜¯å¦é€‰æ‹©äº†äº§å“
        if (!formState.value.productModelId) {
          proxy.$modal.msgError("请选择产品");
          return;
        }
        if (!formState.value.productModelId) {
          proxy.$modal.msgError("请选择规格");
          return;
        }
        if (formState.value.type === "qualified") {
          addStockInRecordOnly(formState.value).then(res => {
            // å…³é—­æ¨¡æ€æ¡†
            isShow.value = false;
            // å‘ŠçŸ¥çˆ¶ç»„件已完成
            emit("completed");
            proxy.$modal.msgSuccess("提交成功");
          });
        } else {
          formState.value.warnNum = 0;
          createStockUnInventory(formState.value).then(res => {
            // å…³é—­æ¨¡æ€æ¡†
            isShow.value = false;
            // å‘ŠçŸ¥çˆ¶ç»„件已完成
            emit("completed");
            proxy.$modal.msgSuccess("提交成功");
          });
src/views/inventoryManagement/stockManagement/OilStockInDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,791 @@
<template>
  <div>
    <el-dialog v-model="isShow"
               title="新增油品入库"
               width="60%"
               @close="closeModal">
      <el-form label-width="140px"
               :model="formState"
               label-position="top"
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品名称"
                          prop="productModelId"
                          :rules="[
                {
                required: true,
                message: '请选择产品',
                trigger: 'change',
              }
            ]">
              <el-button type="primary"
                         @click="showProductSelectDialog = true">
                {{ formState.productName ? formState.productName : '选择产品' }}
              </el-button>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="规格"
                          prop="productModelName">
              <el-input v-model="formState.productModelName"
                        disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="单位"
                          prop="unit">
              <el-input v-model="formState.unit"
                        disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <div class="form-section-title">入库信息</div>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="入库名称"
                          prop="oilDepotId"
                          :rules="[
                {
                required: true,
                message: '请选择入库名称',
                trigger: 'change',
              }
            ]">
              <el-select v-model="formState.oilDepotId"
                         placeholder="请选择"
                         clearable
                         popper-class="oil-depot-select-popper"
                         style="width: 100%">
                <el-option v-for="item in oilDepotList"
                           :key="item.id"
                           :label="item.depotName"
                           :value="item.id">
                  <div class="oil-depot-option">
                    <div class="oil-depot-option-title">{{ item.depotName }}</div>
                    <div class="oil-depot-option-line">负责人:{{ item.chargePerson }}</div>
                    <div class="oil-depot-option-line">联系电话:{{ item.phone }}</div>
                    <div class="oil-depot-option-line">所属公司:{{ item.company }}</div>
                    <div class="oil-depot-option-line">启用日期:{{ item.enableDate }}</div>
                  </div>
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="入库类别"
                          prop="inboundCategory"
                          :rules="[
                {
                required: true,
                message: '请选择入库类别',
                trigger: 'change',
              }
            ]">
              <el-select v-model="formState.inboundCategory"
                         placeholder="请选择"
                         clearable
                         style="width: 100%">
                <el-option label="自购入库"
                           value="自购入库" />
                <el-option label="代储入库"
                           value="代储入库" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="储罐号"
                          prop="tankId"
                          :rules="[
                {
                required: true,
                message: '请选择储罐号',
                trigger: 'change',
              }
            ]">
              <el-select v-model="formState.tankId"
                         placeholder="请选择"
                         clearable
                         popper-class="tank-select-popper"
                         style="width: 100%">
                <el-option v-for="item in tankList"
                           :key="item.id"
                           :label="item.tankNo"
                           :value="item.id">
                  <div class="tank-option">
                    <div class="tank-option-title">{{ item.tankNo }}</div>
                    <div class="tank-option-line">大类:{{ item.category }}</div>
                    <div class="tank-option-line">油品:{{ item.oilProduct }}</div>
                    <div class="tank-option-line">罐容:{{ item.capacity }}</div>
                    <div class="tank-option-line">库存余量(m³):{{ item.remaining }}</div>
                    <div class="tank-option-line">租客信息:{{ item.tenantInfo || '--' }}</div>
                  </div>
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="入库时间"
                          prop="inboundTime"
                          :rules="[
                {
                required: true,
                message: '请选择入库时间',
                trigger: 'change',
              }
            ]">
              <el-date-picker v-model="formState.inboundTime"
                              type="datetime"
                              value-format="YYYY-MM-DD HH:mm:ss"
                              format="YYYY-MM-DD HH:mm:ss"
                              placeholder="请选择"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="油品"
                          prop="oilProduct"
                          :rules="isOilProduct ? [
                {
                required: true,
                message: '请选择油品',
                trigger: 'change',
              }
            ] : []">
              <el-input v-if="isOilProduct"
                        :model-value="formState.oilProduct"
                        readonly
                        placeholder="--" />
              <el-select v-else
                         v-model="formState.oilProduct"
                         placeholder="请选择"
                         disabled
                         style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="原出厂数量(吨)"
                          prop="originalQuantity"
                          :rules="[
                {
                required: true,
                message: '请输入原出厂数量',
                trigger: 'change',
              }
            ]">
              <el-input-number v-model="formState.originalQuantity"
                               :precision="2"
                               :step="1"
                               :min="0"
                               style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="8">
            <el-form-item label="客户名称"
                          prop="customerId"
                          :rules="[
                {
                required: true,
                message: '请选择客户名称',
                trigger: 'change',
              }
            ]">
              <el-select v-model="formState.customerId"
                         placeholder="请选择"
                         clearable
                         filterable
                         popper-class="customer-select-popper"
                         style="width: 100%"
                         @change="customerChange">
                <el-option v-for="item in customerList"
                           :key="item.id"
                           :label="item.customerName"
                           :value="item.id">
                  <div class="customer-option">
                    <div class="customer-option-title">{{ item.customerName }}</div>
                    <div class="customer-option-line">开户行名称:{{ item.basicBankAccount }}</div>
                    <div class="customer-option-line">银行账户:{{ item.bankAccount }}</div>
                    <div class="customer-option-line">地址:{{ item.addressPhone }}</div>
                  </div>
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="客户对接人"
                          prop="contactPerson"
                          :rules="[
                {
                required: true,
                message: '请输入客户对接人',
                trigger: 'blur',
              }
            ]">
              <el-input v-model="formState.contactPerson"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="对接人电话"
                          prop="contactPhone"
                          :rules="[
                {
                required: true,
                message: '请输入对接人电话',
                trigger: 'blur',
              },
                {
                pattern: /^1[3-9]\d{9}$/,
                message: '请输入有效的11位手机号码',
                trigger: 'blur',
              }
            ]">
              <el-input v-model="formState.contactPhone"
                        placeholder="请输入11位数字"
                        maxlength="11"
                        @input="phoneInput('contactPhone')"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <div class="form-section-title">物流信息</div>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="司机姓名"
                          prop="driverName"
                          :rules="[{ required: true, message: '请输入司机姓名', trigger: 'blur' }]">
              <el-input v-model="formState.driverName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="司机电话"
                          prop="driverPhone"
                          :rules="[
                { required: true, message: '请输入司机电话', trigger: 'blur' },
                { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的11位手机号码', trigger: 'blur' }
              ]">
              <el-input v-model="formState.driverPhone"
                        placeholder="请输入11位数字"
                        maxlength="11"
                        @input="phoneInput('driverPhone')"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="押运员姓名"
                          prop="escortName"
                          :rules="[{ required: true, message: '请输入押运员姓名', trigger: 'blur' }]">
              <el-input v-model="formState.escortName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="押运员电话"
                          prop="escortPhone"
                          :rules="[
                { required: true, message: '请输入押运员电话', trigger: 'blur' },
                { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的11位手机号码', trigger: 'blur' }
              ]">
              <el-input v-model="formState.escortPhone"
                        placeholder="请输入11位数字"
                        maxlength="11"
                        @input="phoneInput('escortPhone')"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="车牌号"
                          prop="truckPlateNo"
                          :rules="[{ required: true, message: '请输入车牌号', trigger: 'blur' }]">
              <el-input v-model="formState.truckPlateNo"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="车挂牌号"
                          prop="trailerPlateNo"
                          :rules="[{ required: true, message: '请输入车挂牌号', trigger: 'blur' }]">
              <el-input v-model="formState.trailerPlateNo"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <div class="form-section-title">卸油操作信息</div>
        <el-row :gutter="30">
          <el-col :span="8">
            <el-form-item label="车体/铅封检验是否正常"
                          prop="tankInspectionStatus"
                          :rules="[{ required: true, message: '请选择车体/铅封检验是否正常', trigger: 'change' }]">
              <el-radio-group v-model="formState.tankInspectionStatus">
                <el-radio value="正常">正常</el-radio>
                <el-radio value="异常">异常</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="卸车鹤位"
                          prop="unloadingCranePosition"
                          :rules="[{ required: true, message: '请选择卸车鹤位', trigger: 'change' }]">
              <el-select v-model="formState.unloadingCranePosition"
                         placeholder="请选择"
                         clearable
                         style="width: 100%">
                <el-option label="1#撬装"
                           value="1#撬装" />
                <el-option label="2#撬装"
                           value="2#撬装" />
                <el-option label="临时泵"
                           value="临时泵" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="密度"
                          prop="density"
                          :rules="[
                { required: true, message: '请输入密度', trigger: 'blur' },
                { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入数字,最多保留两位小数', trigger: 'blur' },
              ]">
              <el-input v-model="formState.density"
                        placeholder="请输入数字,最多保留两位小数"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="卸油关键环节与车辆关键位置拍照"
                          prop="unloadPhotos"
                          :rules="[{ required: true, message: '请上传卸油关键环节与车辆关键位置拍照', trigger: 'change' }]">
              <ImageUpload v-model:file-list="formState.unloadPhotos"
                           :limit="10" />
            </el-form-item>
          </el-col>
        </el-row>
        <div class="form-section-title">过磅数据</div>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="毛重(吨)"
                          prop="grossWeight"
                          :rules="[
                { required: true, message: '请输入毛重', trigger: 'blur' },
                { pattern: /^\d+$/, message: '只能输入整数数字', trigger: 'blur' },
              ]">
              <el-input v-model="formState.grossWeight"
                        placeholder="请输入整数数字"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="皮重(吨)"
                          prop="tareWeight"
                          :rules="[
                { required: true, message: '请输入皮重', trigger: 'blur' },
                { pattern: /^\d+$/, message: '只能输入整数数字', trigger: 'blur' },
              ]">
              <el-input v-model="formState.tareWeight"
                        placeholder="请输入整数数字"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="8">
            <el-form-item label="净重(吨)">
              <el-input :model-value="netWeight === null ? '--' : netWeight"
                        readonly />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="亏量(吨)">
              <el-input :model-value="lossQuantity === null ? '--' : lossQuantity"
                        readonly />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="实际入库体积(升)">
              <el-input :model-value="actualVolume"
                        readonly />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="磅单上传"
                          prop="poundPhotos"
                          :rules="[{ required: true, message: '请上传磅单', trigger: 'change' }]">
              <ImageUpload v-model:file-list="formState.poundPhotos"
                           :limit="10" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <!-- äº§å“é€‰æ‹©å¼¹çª— -->
      <ProductSelectDialog v-model="showProductSelectDialog"
                           @confirm="handleProductSelect"
                           single />
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
  import { ref, computed, getCurrentInstance, onMounted } from "vue";
  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
  import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
  import { addStockInRecordOnly } from "@/api/inventoryManagement/stockInventory.js";
  import { allOilDepot } from "@/api/basicData/oilDepot.js";
  import { allTankInfo } from "@/api/basicData/tankInfo.js";
  import { listCustomer } from "@/api/basicData/customer.js";
  import { productTreeList } from "@/api/basicData/product.js";
  const props = defineProps({
    visible: {
      type: Boolean,
      required: true,
    },
  });
  const emit = defineEmits(["update:visible", "completed"]);
  const buildEmptyForm = () => ({
    productId: undefined,
    productModelId: undefined,
    productName: "",
    productModelName: "",
    unit: "",
    oilDepotId: undefined,
    inboundCategory: undefined,
    tankId: undefined,
    inboundTime: "",
    oilProduct: undefined,
    originalQuantity: undefined,
    customerId: undefined,
    contactPerson: "",
    contactPhone: "",
    driverName: "",
    driverPhone: "",
    escortName: "",
    escortPhone: "",
    truckPlateNo: "",
    trailerPlateNo: "",
    tankInspectionStatus: undefined,
    unloadingCranePosition: undefined,
    density: undefined,
    unloadPhotos: [],
    grossWeight: undefined,
    tareWeight: undefined,
    poundPhotos: [],
  });
  const formState = ref(buildEmptyForm());
  const oilDepotList = ref([]);
  const tankList = ref([]);
  const customerList = ref([]);
  const oilProductOptions = ref([]);
  const isShow = computed({
    get() {
      return props.visible;
    },
    set(val) {
      emit("update:visible", val);
    },
  });
  // åœ¨äº§å“æ ‘中查找"油品"分类节点
  const findOilNode = nodes => {
    for (const node of nodes || []) {
      if (node.productName === "油品") {
        return node;
      }
      if (node.children && node.children.length) {
        const found = findOilNode(node.children);
        if (found) {
          return found;
        }
      }
    }
    return null;
  };
  // æ‰€é€‰å•†å“åç§°å‘½ä¸­çš„æ²¹å“é€‰é¡¹ï¼ˆæ²¹å“èŠ‚ç‚¹å­é¡¹ï¼Œæ¥è‡ªäº§å“ç»´æŠ¤æ¨¡å—ï¼‰
  const matchedOilOption = computed(() =>
    oilProductOptions.value.find(
      item => item.productName === formState.value.productName
    )
  );
  // åªæœ‰é€‰ä¸­æ²¹å“å­—段选项里有的具体油品才启用油品字段;"油品"大类本身不启用
  const isOilProduct = computed(() => !!matchedOilOption.value);
  // å‡€é‡(吨) = æ¯›é‡ - çš®é‡
  const netWeight = computed(() => {
    const g = formState.value.grossWeight;
    const t = formState.value.tareWeight;
    if ([g, t].some(v => v === null || v === undefined || v === "")) {
      return null;
    }
    return Number(g) - Number(t);
  });
  // äºé‡(吨) = å‡€é‡çš„è´Ÿæ•°
  const lossQuantity = computed(() => {
    if (netWeight.value === null) {
      return null;
    }
    return -netWeight.value;
  });
  // å®žé™…入库体积(升) = å®žé™…净重(kg) Ã· å¯†åº¦(kg/L),净重吨 Ã— 1000 è½¬ kg
  const actualVolume = computed(() => {
    const d = formState.value.density;
    if (netWeight.value === null || d === null || d === undefined || d === "" || Number(d) <= 0) {
      return 0;
    }
    return Math.round(((netWeight.value * 1000) / Number(d)) * 100) / 100;
  });
  const showProductSelectDialog = ref(false);
  let { proxy } = getCurrentInstance();
  const closeModal = () => {
    proxy.resetForm("formRef");
    formState.value = buildEmptyForm();
    isShow.value = false;
  };
  // ç”µè¯è¾“入限制:只允许数字,最多11位
  const phoneInput = field => {
    formState.value[field] = String(formState.value[field] ?? "")
      .replace(/\D/g, "")
      .slice(0, 11);
  };
  // å®¢æˆ·é€‰æ‹©æ”¹å˜ï¼šç»‘定客户信息表,自动带出对接人与对接电话
  const customerChange = id => {
    const customer = customerList.value.find(item => item.id === id);
    if (customer) {
      formState.value.contactPerson = customer.contactPerson || "";
      formState.value.contactPhone = customer.contactPhone || "";
    } else {
      formState.value.contactPerson = "";
      formState.value.contactPhone = "";
    }
  };
  // äº§å“é€‰æ‹©å¤„理
  const handleProductSelect = async products => {
    if (products && products.length > 0) {
      const product = products[0];
      formState.value.productId = product.productId;
      formState.value.productName = product.productName;
      formState.value.productModelName = product.model;
      formState.value.productModelId = product.id;
      formState.value.unit = product.unit;
      // æ²¹å“é€‰é¡¹ä¸Žå•†å“åç§°åŒæ­¥ï¼šå‘½ä¸­æ²¹å“é€‰é¡¹åˆ™è‡ªåŠ¨å¸¦å‡ºè¯¥æ²¹å“ï¼Œæœªå‘½ä¸­åˆ™æ¸…ç©ºå¹¶ç¦ç”¨
      formState.value.oilProduct = matchedOilOption.value
        ? product.productName
        : undefined;
      showProductSelectDialog.value = false;
      // è§¦å‘表单验证更新
      proxy.$refs["formRef"]?.validateField("productModelId");
    }
  };
  const handleSubmit = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        if (!formState.value.productModelId) {
          proxy.$modal.msgError("请选择产品");
          return;
        }
        // è®¡ç®—结果随表单提交:油品入库固定为合格库存,库存数量取净重
        formState.value.type = "qualified";
        formState.value.warnNum = 0;
        formState.value.qualitity = netWeight.value;
        formState.value.netWeight = netWeight.value;
        formState.value.lossQuantity = lossQuantity.value;
        formState.value.actualVolume = actualVolume.value;
        addStockInRecordOnly(formState.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeModal();
          emit("completed");
        });
      }
    });
  };
  onMounted(() => {
    allOilDepot().then(res => {
      oilDepotList.value = res?.data || [];
    });
    allTankInfo().then(res => {
      tankList.value = res?.data || [];
    });
    listCustomer({ current: 1, size: 1000, type: 0 }).then(res => {
      customerList.value = res?.data?.records || [];
    });
    productTreeList().then(res => {
      const oilNode = findOilNode(res);
      oilProductOptions.value = oilNode?.children || [];
    });
  });
</script>
<style scoped lang="scss">
  // è¡¨å•分栏标题:蓝色竖条 + æµ…蓝底色,突出分区
  .form-section-title {
    margin: 18px 0 14px;
    padding: 8px 14px;
    font-size: 15px;
    font-weight: 600;
    color: #1d6fd8;
    background: linear-gradient(90deg, rgba(64, 158, 255, 0.14), rgba(64, 158, 255, 0.02));
    border-left: 4px solid #409eff;
    border-radius: 3px;
  }
  // å…¥åº“名称下拉选项:小卡片样式
  .oil-depot-option {
    padding: 8px 10px;
    .oil-depot-option-title {
      font-size: 14px;
      font-weight: 600;
      color: #303133;
      margin-bottom: 4px;
    }
    .oil-depot-option-line {
      font-size: 12px;
      color: #909399;
      line-height: 1.8;
    }
  }
  // å‚¨ç½å·ä¸‹æ‹‰é€‰é¡¹ï¼šå°å¡ç‰‡æ ·å¼
  .tank-option {
    padding: 8px 10px;
    .tank-option-title {
      font-size: 14px;
      font-weight: 600;
      color: #303133;
      margin-bottom: 4px;
    }
    .tank-option-line {
      font-size: 12px;
      color: #909399;
      line-height: 1.8;
    }
  }
  // å®¢æˆ·åç§°ä¸‹æ‹‰é€‰é¡¹ï¼šå°å¡ç‰‡æ ·å¼
  .customer-option {
    padding: 8px 10px;
    .customer-option-title {
      font-size: 14px;
      font-weight: 600;
      color: #303133;
      margin-bottom: 4px;
    }
    .customer-option-line {
      font-size: 12px;
      color: #909399;
      line-height: 1.8;
    }
  }
</style>
<style lang="scss">
  // ä¸‹æ‹‰é¢æ¿æ¸²æŸ“在 body ä¸‹ï¼Œéœ€å…¨å±€æ ·å¼
  .oil-depot-select-popper {
    .el-select-dropdown__item {
      height: auto;
      line-height: normal;
      padding: 4px 0;
    }
    .oil-depot-option {
      border: 1px solid #e4e7ed;
      border-radius: 6px;
      background-color: #fafbfc;
      &:hover {
        border-color: #409eff;
        background-color: #f0f7ff;
      }
    }
  }
  // å‚¨ç½å·ä¸‹æ‹‰é¢æ¿ï¼ˆbody ä¸‹ï¼Œéœ€å…¨å±€æ ·å¼ï¼‰
  .tank-select-popper {
    .el-select-dropdown__item {
      height: auto;
      line-height: normal;
      padding: 4px 0;
    }
    .tank-option {
      border: 1px solid #e4e7ed;
      border-radius: 6px;
      background-color: #fafbfc;
      &:hover {
        border-color: #409eff;
        background-color: #f0f7ff;
      }
    }
  }
  // å®¢æˆ·åç§°ä¸‹æ‹‰é¢æ¿ï¼ˆbody ä¸‹ï¼Œéœ€å…¨å±€æ ·å¼ï¼‰
  .customer-select-popper {
    .el-select-dropdown__item {
      height: auto;
      line-height: normal;
      padding: 4px 0;
    }
    .customer-option {
      border: 1px solid #e4e7ed;
      border-radius: 6px;
      background-color: #fafbfc;
      &:hover {
        border-color: #409eff;
        background-color: #f0f7ff;
      }
    }
  }
</style>
src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,759 @@
<template>
  <div>
    <el-dialog v-model="isShow"
               title="新增油品出库"
               width="60%"
               @close="closeModal">
      <el-form label-width="140px"
               :model="formState"
               label-position="top"
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品名称"
                          prop="productModelId"
                          :rules="[
                {
                required: true,
                message: '请选择产品',
                trigger: 'change',
              }
            ]">
              <el-button type="primary"
                         @click="showProductSelectDialog = true">
                {{ formState.productName ? formState.productName : '选择产品' }}
              </el-button>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="规格"
                          prop="model">
              <el-input v-model="formState.model"
                        disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="单位"
                          prop="unit">
              <el-input v-model="formState.unit"
                        disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <div class="form-section-title">出库信息</div>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="出库类别"
                          prop="outCategory"
                          :rules="[{ required: true, message: '请选择出库类别', trigger: 'change' }]">
              <el-select v-model="formState.outCategory"
                         placeholder="请选择"
                         clearable
                         style="width: 100%">
                <el-option label="客存"
                           value="客存" />
                <el-option label="代储"
                           value="代储" />
                <el-option label="销售"
                           value="销售" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="储库名称"
                          prop="oilDepotId"
                          :rules="[{ required: true, message: '请选择储库名称', trigger: 'change' }]">
              <el-select v-model="formState.oilDepotId"
                         placeholder="请选择"
                         clearable
                         popper-class="oil-depot-select-popper"
                         style="width: 100%"
                         @change="depotChange">
                <el-option v-for="item in oilDepotList"
                           :key="item.id"
                           :label="`${item.depotName}-${item.company || ''}`"
                           :value="item.id">
                  <div class="oil-depot-option">
                    <div class="oil-depot-option-title">{{ item.depotName }}-{{ item.company }}</div>
                    <div class="oil-depot-option-line">负责人:{{ item.chargePerson }}</div>
                    <div class="oil-depot-option-line">联系电话:{{ item.phone }}</div>
                    <div class="oil-depot-option-line">启用日期:{{ item.enableDate }}</div>
                  </div>
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="储罐号"
                          prop="tankId"
                          :rules="[{ required: true, message: '请选择储罐号', trigger: 'change' }]">
              <el-select v-model="formState.tankId"
                         placeholder="请选择"
                         clearable
                         popper-class="tank-select-popper"
                         style="width: 100%"
                         @change="tankChange">
                <el-option v-for="item in tankList"
                           :key="item.id"
                           :label="item.tankNo"
                           :value="item.id">
                  <div class="tank-option">
                    <div class="tank-option-title">{{ item.tankNo }}</div>
                    <div class="tank-option-line">大类:{{ item.category }}</div>
                    <div class="tank-option-line">油品:{{ item.oilProduct }}</div>
                    <div class="tank-option-line">罐容:{{ item.capacity }}</div>
                    <div class="tank-option-line">库存余量(m³):{{ item.remaining }}</div>
                    <div class="tank-option-line">租客信息:{{ item.tenantInfo || '--' }}</div>
                  </div>
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="出库日期"
                          prop="outDate"
                          :rules="[{ required: true, message: '请选择出库日期', trigger: 'change' }]">
              <el-date-picker v-model="formState.outDate"
                              type="date"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              placeholder="请选择"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="油品"
                          prop="oilProduct"
                          :rules="isOilProduct ? [
                {
                required: true,
                message: '请选择油品',
                trigger: 'change',
              }
            ] : []">
              <el-input v-if="isOilProduct"
                        :model-value="formState.oilProduct"
                        readonly
                        placeholder="--" />
              <el-select v-else
                         v-model="formState.oilProduct"
                         placeholder="请选择"
                         disabled
                         style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="出库吨位(吨)">
              <el-input :model-value="outTonnage === null ? '--' : outTonnage"
                        readonly />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="8">
            <el-form-item label="客户名称"
                          prop="customerId"
                          :rules="[{ required: true, message: '请选择客户名称', trigger: 'change' }]">
              <el-select v-model="formState.customerId"
                         placeholder="请选择"
                         clearable
                         filterable
                         popper-class="customer-select-popper"
                         style="width: 100%"
                         @change="customerChange">
                <el-option v-for="item in customerList"
                           :key="item.id"
                           :label="item.customerName"
                           :value="item.id">
                  <div class="customer-option">
                    <div class="customer-option-title">{{ item.customerName }}</div>
                    <div class="customer-option-line">银行基本户:{{ item.basicBankAccount }}</div>
                    <div class="customer-option-line">银行账号:{{ item.bankAccount }}</div>
                    <div class="customer-option-line">地址及联系方式:{{ item.addressPhone }}</div>
                  </div>
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="客户对接人"
                          prop="contactPerson"
                          :rules="[{ required: true, message: '请输入客户对接人', trigger: 'blur' }]">
              <el-input v-model="formState.contactPerson"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="对接人电话"
                          prop="contactPhone"
                          :rules="[
                { required: true, message: '请输入对接人电话', trigger: 'blur' },
                { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的11位手机号码', trigger: 'blur' }
              ]">
              <el-input v-model="formState.contactPhone"
                        placeholder="请输入11位数字"
                        maxlength="11"
                        @input="phoneInput('contactPhone')"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <div class="form-section-title">装油操作信息</div>
        <el-row :gutter="30">
          <el-col :span="8">
            <el-form-item label="车体/铅封检验是否正常"
                          prop="loadingInspectionStatus"
                          :rules="[{ required: true, message: '请选择车体/铅封检验是否正常', trigger: 'change' }]">
              <el-radio-group v-model="formState.loadingInspectionStatus">
                <el-radio value="正常">正常</el-radio>
                <el-radio value="异常">异常</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="装车鹤位"
                          prop="loadingCranePosition"
                          :rules="[{ required: true, message: '请选择装车鹤位', trigger: 'change' }]">
              <el-select v-model="formState.loadingCranePosition"
                         placeholder="请选择"
                         clearable
                         style="width: 100%">
                <el-option label="1#撬装"
                           value="1#撬装" />
                <el-option label="2#撬装"
                           value="2#撬装" />
                <el-option label="临时泵"
                           value="临时泵" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="密度"
                          prop="density"
                          :rules="[
                { required: true, message: '请输入密度', trigger: 'blur' },
                { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入数字,最多保留两位小数', trigger: 'blur' },
              ]">
              <el-input v-model="formState.density"
                        placeholder="请输入数字,最多保留两位小数"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="装油关键环节与车辆关键位置拍照"
                          prop="loadingPhotos"
                          :rules="[{ required: true, message: '请上传装油关键环节与车辆关键位置拍照', trigger: 'change' }]">
              <ImageUpload v-model:file-list="formState.loadingPhotos"
                           :limit="10" />
            </el-form-item>
          </el-col>
        </el-row>
        <div class="form-section-title">过磅数据</div>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="毛重(吨)"
                          prop="grossWeight"
                          :rules="[
                { required: true, message: '请输入毛重', trigger: 'blur' },
                { pattern: /^\d+$/, message: '只能输入整数数字', trigger: 'blur' },
              ]">
              <el-input v-model="formState.grossWeight"
                        placeholder="请输入整数数字"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="皮重(吨)"
                          prop="tareWeight"
                          :rules="[
                { required: true, message: '请输入皮重', trigger: 'blur' },
                { pattern: /^\d+$/, message: '只能输入整数数字', trigger: 'blur' },
              ]">
              <el-input v-model="formState.tareWeight"
                        placeholder="请输入整数数字"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="8">
            <el-form-item label="净重(吨)">
              <el-input :model-value="netWeight === null ? '--' : netWeight"
                        readonly />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="亏量(吨)">
              <el-input :model-value="lossQuantity === null ? '--' : lossQuantity"
                        readonly />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="实际入库体积(升)">
              <el-input :model-value="actualVolume"
                        readonly />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="磅单上传"
                          prop="poundPhotos"
                          :rules="[{ required: true, message: '请上传磅单', trigger: 'change' }]">
              <ImageUpload v-model:file-list="formState.poundPhotos"
                           :limit="10" />
            </el-form-item>
          </el-col>
        </el-row>
        <div class="form-section-title">物流信息</div>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="司机姓名"
                          prop="driverName"
                          :rules="[{ required: true, message: '请输入司机姓名', trigger: 'blur' }]">
              <el-input v-model="formState.driverName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="司机电话"
                          prop="driverPhone"
                          :rules="[
                { required: true, message: '请输入司机电话', trigger: 'blur' },
                { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的11位手机号码', trigger: 'blur' }
              ]">
              <el-input v-model="formState.driverPhone"
                        placeholder="请输入11位数字"
                        maxlength="11"
                        @input="phoneInput('driverPhone')"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="押运员姓名"
                          prop="escortName"
                          :rules="[{ required: true, message: '请输入押运员姓名', trigger: 'blur' }]">
              <el-input v-model="formState.escortName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="押运员电话"
                          prop="escortPhone"
                          :rules="[
                { required: true, message: '请输入押运员电话', trigger: 'blur' },
                { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的11位手机号码', trigger: 'blur' }
              ]">
              <el-input v-model="formState.escortPhone"
                        placeholder="请输入11位数字"
                        maxlength="11"
                        @input="phoneInput('escortPhone')"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="车牌号"
                          prop="truckPlateNo"
                          :rules="[{ required: true, message: '请输入车牌号', trigger: 'blur' }]">
              <el-input v-model="formState.truckPlateNo"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="车挂牌号"
                          prop="trailerPlateNo"
                          :rules="[{ required: true, message: '请输入车挂牌号', trigger: 'blur' }]">
              <el-input v-model="formState.trailerPlateNo"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- äº§å“é€‰æ‹©å¼¹çª— -->
    <ProductSelectDialog v-model="showProductSelectDialog"
                         @confirm="handleProductSelect"
                         single />
  </div>
</template>
<script setup>
  import { ref, computed, getCurrentInstance, onMounted } from "vue";
  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
  import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
  import { addOilOutRecordOnly } from "@/api/inventoryManagement/stockInventory.js";
  import { allOilDepot } from "@/api/basicData/oilDepot.js";
  import { allTankInfo } from "@/api/basicData/tankInfo.js";
  import { listCustomer } from "@/api/basicData/customer.js";
  import { productTreeList } from "@/api/basicData/product.js";
  const props = defineProps({
    visible: {
      type: Boolean,
      required: true,
    },
  });
  const emit = defineEmits(["update:visible", "completed"]);
  const buildEmptyForm = () => ({
    productId: undefined,
    productModelId: undefined,
    productName: "",
    model: "",
    unit: "",
    outCategory: undefined,
    oilDepotId: undefined,
    oilDepotName: "",
    tankId: undefined,
    tankNo: "",
    outDate: "",
    oilProduct: undefined,
    customerId: undefined,
    customerName: "",
    contactPerson: "",
    contactPhone: "",
    loadingInspectionStatus: undefined,
    loadingCranePosition: undefined,
    loadingPhotos: [],
    density: undefined,
    grossWeight: undefined,
    tareWeight: undefined,
    poundPhotos: [],
    driverName: "",
    driverPhone: "",
    escortName: "",
    escortPhone: "",
    truckPlateNo: "",
    trailerPlateNo: "",
  });
  const formState = ref(buildEmptyForm());
  const oilDepotList = ref([]);
  const tankList = ref([]);
  const customerList = ref([]);
  const oilProductOptions = ref([]);
  const showProductSelectDialog = ref(false);
  // åœ¨äº§å“æ ‘中查找"油品"分类节点
  const findOilNode = nodes => {
    for (const node of nodes || []) {
      if (node.productName === "油品") {
        return node;
      }
      if (node.children && node.children.length) {
        const found = findOilNode(node.children);
        if (found) {
          return found;
        }
      }
    }
    return null;
  };
  // æ‰€é€‰å•†å“åç§°å‘½ä¸­çš„æ²¹å“é€‰é¡¹ï¼ˆæ²¹å“èŠ‚ç‚¹å­é¡¹ï¼Œæ¥è‡ªäº§å“ç»´æŠ¤æ¨¡å—ï¼‰
  const matchedOilOption = computed(() =>
    oilProductOptions.value.find(
      item => item.productName === formState.value.productName
    )
  );
  // åªæœ‰é€‰ä¸­æ²¹å“å­—段选项里有的具体油品才启用油品字段;"油品"大类本身不启用
  const isOilProduct = computed(() => !!matchedOilOption.value);
  const isShow = computed({
    get() {
      return props.visible;
    },
    set(val) {
      emit("update:visible", val);
    },
  });
  // å‡€é‡(吨) = æ¯›é‡ - çš®é‡
  const netWeight = computed(() => {
    const g = formState.value.grossWeight;
    const t = formState.value.tareWeight;
    if ([g, t].some(v => v === null || v === undefined || v === "")) {
      return null;
    }
    return Number(g) - Number(t);
  });
  // äºé‡(吨) = å‡€é‡çš„è´Ÿæ•°
  const lossQuantity = computed(() => {
    if (netWeight.value === null) {
      return null;
    }
    return -netWeight.value;
  });
  // å®žé™…入库体积(升) = å®žé™…净重(kg) Ã· å¯†åº¦(kg/L),净重吨 Ã— 1000 è½¬ kg
  const actualVolume = computed(() => {
    const d = formState.value.density;
    if (netWeight.value === null || d === null || d === undefined || d === "" || Number(d) <= 0) {
      return 0;
    }
    return Math.round(((netWeight.value * 1000) / Number(d)) * 100) / 100;
  });
  // å‡ºåº“吨位(吨) = å‡€é‡ï¼ˆéœ€æ±‚未给公式,暂按净重带出)
  const outTonnage = computed(() => netWeight.value);
  let { proxy } = getCurrentInstance();
  const closeModal = () => {
    proxy.resetForm("formRef");
    formState.value = buildEmptyForm();
    isShow.value = false;
  };
  // ç”µè¯è¾“入限制:只允许数字,最多11位
  const phoneInput = field => {
    formState.value[field] = String(formState.value[field] ?? "")
      .replace(/\D/g, "")
      .slice(0, 11);
  };
  // äº§å“é€‰æ‹©å¤„理
  const handleProductSelect = async products => {
    if (products && products.length > 0) {
      const product = products[0];
      formState.value.productId = product.productId;
      formState.value.productName = product.productName;
      formState.value.model = product.model;
      formState.value.productModelId = product.id;
      formState.value.unit = product.unit;
      // æ²¹å“é€‰é¡¹ä¸Žå•†å“åç§°åŒæ­¥ï¼šå‘½ä¸­æ²¹å“é€‰é¡¹åˆ™è‡ªåŠ¨å¸¦å‡ºè¯¥æ²¹å“ï¼Œæœªå‘½ä¸­åˆ™æ¸…ç©ºå¹¶ç¦ç”¨
      formState.value.oilProduct = matchedOilOption.value
        ? product.productName
        : undefined;
      showProductSelectDialog.value = false;
      proxy.$refs["formRef"]?.validateField("productModelId");
    }
  };
  // å‚¨åº“选择改变:记录库名-公司名
  const depotChange = id => {
    const depot = oilDepotList.value.find(item => item.id === id);
    formState.value.oilDepotName = depot
      ? `${depot.depotName}-${depot.company || ""}`
      : "";
  };
  // å‚¨ç½é€‰æ‹©æ”¹å˜ï¼šè®°å½•储罐号
  const tankChange = id => {
    const tank = tankList.value.find(item => item.id === id);
    formState.value.tankNo = tank?.tankNo || "";
  };
  // å®¢æˆ·é€‰æ‹©æ”¹å˜ï¼šç»‘定客户档案(私海),自动带出对接人与对接电话
  const customerChange = id => {
    const customer = customerList.value.find(item => item.id === id);
    if (customer) {
      formState.value.customerName = customer.customerName || "";
      formState.value.contactPerson = customer.contactPerson || "";
      formState.value.contactPhone = customer.contactPhone || "";
    } else {
      formState.value.customerName = "";
      formState.value.contactPerson = "";
      formState.value.contactPhone = "";
    }
  };
  const handleSubmit = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        if (!formState.value.productModelId) {
          proxy.$modal.msgError("请选择产品");
          return;
        }
        // è®¡ç®—结果随表单提交
        formState.value.outTonnage = outTonnage.value;
        formState.value.netWeight = netWeight.value;
        formState.value.lossQuantity = lossQuantity.value;
        formState.value.actualVolume = actualVolume.value;
        // ç…§ç‰‡è½¬ JSON å­—符串提交(后端字段为文本存储)
        formState.value.loadingPhotos = JSON.stringify(formState.value.loadingPhotos || []);
        formState.value.poundPhotos = JSON.stringify(formState.value.poundPhotos || []);
        addOilOutRecordOnly(formState.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeModal();
          emit("completed");
        });
      }
    });
  };
  onMounted(() => {
    allOilDepot().then(res => {
      oilDepotList.value = res?.data || [];
    });
    productTreeList().then(res => {
      const oilNode = findOilNode(res);
      oilProductOptions.value = oilNode?.children || [];
    });
    allTankInfo().then(res => {
      tankList.value = res?.data || [];
    });
    listCustomer({ current: 1, size: 1000, type: 0 }).then(res => {
      customerList.value = res?.data?.records || [];
    });
  });
</script>
<style scoped lang="scss">
  // è¡¨å•分栏标题:蓝色竖条 + æµ…蓝底色,突出分区
  .form-section-title {
    margin: 18px 0 14px;
    padding: 8px 14px;
    font-size: 15px;
    font-weight: 600;
    color: #1d6fd8;
    background: linear-gradient(90deg, rgba(64, 158, 255, 0.14), rgba(64, 158, 255, 0.02));
    border-left: 4px solid #409eff;
    border-radius: 3px;
  }
  // å‚¨åº“名称下拉选项:小卡片样式
  .oil-depot-option {
    padding: 8px 10px;
    .oil-depot-option-title {
      font-size: 14px;
      font-weight: 600;
      color: #303133;
      margin-bottom: 4px;
    }
    .oil-depot-option-line {
      font-size: 12px;
      color: #909399;
      line-height: 1.8;
    }
  }
  // å‚¨ç½å·ä¸‹æ‹‰é€‰é¡¹ï¼šå°å¡ç‰‡æ ·å¼
  .tank-option {
    padding: 8px 10px;
    .tank-option-title {
      font-size: 14px;
      font-weight: 600;
      color: #303133;
      margin-bottom: 4px;
    }
    .tank-option-line {
      font-size: 12px;
      color: #909399;
      line-height: 1.8;
    }
  }
  // å®¢æˆ·åç§°ä¸‹æ‹‰é€‰é¡¹ï¼šå°å¡ç‰‡æ ·å¼
  .customer-option {
    padding: 8px 10px;
    .customer-option-title {
      font-size: 14px;
      font-weight: 600;
      color: #303133;
      margin-bottom: 4px;
    }
    .customer-option-line {
      font-size: 12px;
      color: #909399;
      line-height: 1.8;
    }
  }
</style>
<style lang="scss">
  // ä¸‹æ‹‰é¢æ¿æ¸²æŸ“在 body ä¸‹ï¼Œéœ€å…¨å±€æ ·å¼
  .oil-depot-select-popper {
    .el-select-dropdown__item {
      height: auto;
      line-height: normal;
      padding: 4px 0;
    }
    .oil-depot-option {
      border: 1px solid #e4e7ed;
      border-radius: 6px;
      background-color: #fafbfc;
      &:hover {
        border-color: #409eff;
        background-color: #f0f7ff;
      }
    }
  }
  // å‚¨ç½å·ä¸‹æ‹‰é¢æ¿ï¼ˆbody ä¸‹ï¼Œéœ€å…¨å±€æ ·å¼ï¼‰
  .tank-select-popper {
    .el-select-dropdown__item {
      height: auto;
      line-height: normal;
      padding: 4px 0;
    }
    .tank-option {
      border: 1px solid #e4e7ed;
      border-radius: 6px;
      background-color: #fafbfc;
      &:hover {
        border-color: #409eff;
        background-color: #f0f7ff;
      }
    }
  }
  // å®¢æˆ·åç§°ä¸‹æ‹‰é¢æ¿ï¼ˆbody ä¸‹ï¼Œéœ€å…¨å±€æ ·å¼ï¼‰
  .customer-select-popper {
    .el-select-dropdown__item {
      height: auto;
      line-height: normal;
      padding: 4px 0;
    }
    .customer-option {
      border: 1px solid #e4e7ed;
      border-radius: 6px;
      background-color: #fafbfc;
      &:hover {
        border-color: #409eff;
        background-color: #f0f7ff;
      }
    }
  }
</style>
src/views/inventoryManagement/stockManagement/Record.vue
@@ -49,6 +49,10 @@
      <div style="display: flex; gap: 10px; flex-wrap: nowrap;">
        <el-button type="primary"
                   @click="isShowNewModal = true">新增库存</el-button>
        <el-button type="primary"
                   @click="isShowOilInModal = true">新增油品入库</el-button>
        <el-button type="primary"
                   @click="isShowOilOutModal = true">新增油品出库</el-button>
        <el-button type="info"
                   plain
                   icon="Upload"
@@ -77,36 +81,47 @@
                         width="60" />
        <el-table-column label="产品名称"
                         prop="productName"
                         min-width="120"
                         show-overflow-tooltip />
        <el-table-column label="规格型号"
                         prop="model"
                         min-width="120"
                         show-overflow-tooltip />
        <el-table-column label="单位"
                         prop="unit"
                         min-width="70"
                         show-overflow-tooltip />
        <el-table-column label="批号"
                         prop="batchNo"
                         min-width="150"
                         show-overflow-tooltip />
        <el-table-column label="合格库存数量"
                         prop="qualifiedQuantity"
                         min-width="110"
                         show-overflow-tooltip />
        <el-table-column label="不合格库存数量"
                         prop="unQualifiedQuantity"
                         min-width="120"
                         show-overflow-tooltip />
        <el-table-column label="合格冻结数量"
                         prop="qualifiedLockedQuantity"
                         min-width="110"
                         show-overflow-tooltip />
        <el-table-column label="不合格冻结数量"
                         prop="unQualifiedLockedQuantity"
                         min-width="120"
                         show-overflow-tooltip />
        <el-table-column label="库存预警数量"
                         prop="warnNum"
                         min-width="110"
                         show-overflow-tooltip />
        <el-table-column label="备注"
                         prop="remark"
                         min-width="150"
                         show-overflow-tooltip />
        <el-table-column label="最近更新时间"
                         prop="updateTime"
                         min-width="160"
                         show-overflow-tooltip />
        <el-table-column fixed="right"
                         label="操作"
@@ -135,6 +150,12 @@
    <new-stock-inventory v-if="isShowNewModal"
                         v-model:visible="isShowNewModal"
                         :top-product-parent-id="props.productId"
                         @completed="handleQuery" />
    <oil-stock-in-dialog v-if="isShowOilInModal"
                         v-model:visible="isShowOilInModal"
                         @completed="handleQuery" />
    <oil-stock-out-dialog v-if="isShowOilOutModal"
                          v-model:visible="isShowOilOutModal"
                         @completed="handleQuery" />
    <subtract-stock-inventory v-if="isShowSubtractModal"
                              v-model:visible="isShowSubtractModal"
@@ -172,6 +193,12 @@
  const NewStockInventory = defineAsyncComponent(() =>
    import("@/views/inventoryManagement/stockManagement/New.vue")
  );
  const OilStockInDialog = defineAsyncComponent(() =>
    import("@/views/inventoryManagement/stockManagement/OilStockInDialog.vue")
  );
  const OilStockOutDialog = defineAsyncComponent(() =>
    import("@/views/inventoryManagement/stockManagement/OilStockOutDialog.vue")
  );
  const SubtractStockInventory = defineAsyncComponent(() =>
    import("@/views/inventoryManagement/stockManagement/Subtract.vue")
  );
@@ -196,6 +223,10 @@
  const total = ref(0);
  // æ˜¯å¦æ˜¾ç¤ºæ–°å¢žå¼¹æ¡†
  const isShowNewModal = ref(false);
  // æ˜¯å¦æ˜¾ç¤ºæ²¹å“å…¥åº“弹框
  const isShowOilInModal = ref(false);
  // æ˜¯å¦æ˜¾ç¤ºæ²¹å“å‡ºåº“弹框
  const isShowOilOutModal = ref(false);
  // æ˜¯å¦æ˜¾ç¤ºé¢†ç”¨å¼¹æ¡†
  const isShowSubtractModal = ref(false);
  // æ˜¯å¦æ˜¾ç¤ºå†»ç»“/解冻弹框
src/views/procurementManagement/procurementLedger/index.vue
@@ -161,6 +161,42 @@
                         prop="projectName"
                         width="320"
                         show-overflow-tooltip />
        <el-table-column label="采购类别"
                         prop="purchaseCategory"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="提货方式"
                         prop="pickupMethod"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="采购油品"
                         prop="purchaseOilProduct"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="物流方式"
                         prop="logisticsMethod"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="采购油库"
                         prop="oilDepotName"
                         width="150"
                         show-overflow-tooltip />
        <el-table-column label="客户名称"
                         prop="customerName"
                         width="150"
                         show-overflow-tooltip />
        <el-table-column label="最佳到站日期"
                         prop="bestArrivalDate"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="采购数量(吨)"
                         prop="purchaseQuantity"
                         width="120"
                         align="center" />
        <el-table-column label="采购申请订单状态"
                         prop="purchaseOrderStatus"
                         width="140"
                         show-overflow-tooltip />
        <el-table-column label="审批状态"
                         prop="approvalStatus"
                         width="100"
@@ -300,17 +336,10 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="签订日期:"
                          prop="executionDate">
            <el-form-item label="最佳到站日期:"
                          prop="bestArrivalDate">
              <el-date-picker style="width: 100%"
                              v-model="form.executionDate"
                              v-model="form.bestArrivalDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
@@ -318,9 +347,16 @@
                              clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
          <el-col :span="8">
            <el-form-item label="录入人:"
                          prop="recorderId">
              <el-select v-model="form.recorderId"
@@ -334,7 +370,19 @@
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
          <el-col :span="8">
            <el-form-item label="签订日期:"
                          prop="executionDate">
              <el-date-picker style="width: 100%"
                              v-model="form.executionDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="录入日期:"
                          prop="entryDate">
              <el-date-picker style="width: 100%"
@@ -344,6 +392,144 @@
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <div class="form-section-title">采购入库提交</div>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="申请提交时间:"
                          prop="applicationSubmitTime">
              <el-date-picker style="width: 100%"
                              v-model="form.applicationSubmitTime"
                              value-format="YYYY-MM-DD HH:mm:ss"
                              format="YYYY-MM-DD HH:mm:ss"
                              type="datetime"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="采购类别:"
                          prop="purchaseCategory">
              <el-radio-group v-model="form.purchaseCategory"
                              @change="purchaseCategoryChange">
                <el-radio value="客存">客存</el-radio>
                <el-radio value="代储">代储</el-radio>
                <el-radio value="采购入库">采购入库</el-radio>
                <el-radio value="采购直销">采购直销</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="采购油品:"
                          prop="purchaseOilProduct">
              <el-select v-model="form.purchaseOilProduct"
                         placeholder="请选择"
                         clearable
                         style="width: 100%">
                <el-option label="æ±½#92"
                           value="æ±½#92" />
                <el-option label="æ±½#95"
                           value="æ±½#95" />
                <el-option label="柴#0"
                           value="柴#0" />
                <el-option label="柴#-35"
                           value="柴#-35" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="物流方式:"
                          prop="logisticsMethod">
              <el-select v-model="form.logisticsMethod"
                         placeholder="请选择"
                         clearable
                         style="width: 100%">
                <el-option label="厂家配送"
                           value="厂家配送" />
                <el-option label="委外物流"
                           value="委外物流" />
                <el-option label="客户自提"
                           value="客户自提" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="提货方式:"
                          prop="pickupMethod">
              <el-radio-group v-model="form.pickupMethod">
                <el-radio value="暂不提货">暂不提货</el-radio>
                <el-radio value="提货">提货</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="采购油库:"
                          prop="oilDepotId">
              <el-select v-model="form.oilDepotId"
                         placeholder="请选择"
                         clearable
                         popper-class="oil-depot-select-popper"
                         style="width: 100%">
                <el-option v-for="item in oilDepotList"
                           :key="item.id"
                           :label="item.depotName"
                           :value="item.id">
                  <div class="oil-depot-option">
                    <div class="oil-depot-option-title">{{ item.depotName }}</div>
                    <div class="oil-depot-option-line">负责人:{{ item.chargePerson }}</div>
                    <div class="oil-depot-option-line">联系电话:{{ item.phone }}</div>
                    <div class="oil-depot-option-line">所属公司:{{ item.company }}</div>
                    <div class="oil-depot-option-line">启用日期:{{ item.enableDate }}</div>
                  </div>
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="客户名称:"
                          prop="customerName">
              <el-input v-model="form.customerName"
                        placeholder="仅采购类别为代储/采购直销时可填写"
                        clearable
                        :disabled="!['代储', '采购直销'].includes(form.purchaseCategory)" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="采购数量(吨):"
                          prop="purchaseQuantity">
              <el-input-number v-model="form.purchaseQuantity"
                               :precision="0"
                               :step="1"
                               :min="0"
                               placeholder="请输入"
                               style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <div class="form-section-title">系统信息</div>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="采购申请订单状态:"
                          prop="purchaseOrderStatus">
              <el-select v-model="form.purchaseOrderStatus"
                         placeholder="请选择"
                         clearable
                         style="width: 100%">
                <el-option label="已申请,未装车"
                           value="已申请,未装车" />
                <el-option label="以出厂,未入库"
                           value="以出厂,未入库" />
                <el-option label="已入库"
                           value="已入库" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
@@ -733,6 +919,7 @@
    delPurchaseTemplate,
    batchGeneratePurchaseInboundSteps,
  } from "@/api/procurementManagement/procurementLedger.js";
  import { allOilDepot } from "@/api/basicData/oilDepot.js";
  import useFormData from "@/hooks/useFormData.js";
  const FileList = defineAsyncComponent(() =>
    import("@/components/Dialog/FileList.vue")
@@ -749,6 +936,7 @@
  const productOptions = ref([]);
  const salesContractList = ref([]);
  const supplierList = ref([]);
  const oilDepotList = ref([]);
  const tableLoading = ref(false);
  const recordId = ref();
  const fileListDialogVisible = ref(false);
@@ -931,6 +1119,16 @@
      paymentMethod: "",
      executionDate: "",
      isChecked: false,
      purchaseCategory: "",
      purchaseOilProduct: "",
      logisticsMethod: "",
      pickupMethod: "",
      oilDepotId: "",
      customerName: "",
      bestArrivalDate: "",
      purchaseQuantity: "",
      purchaseOrderStatus: "",
      applicationSubmitTime: "",
    },
    rules: {
      purchaseContractNumber: [
@@ -942,6 +1140,18 @@
      supplierId: [{ required: true, message: "请输入", trigger: "blur" }],
      entryDate: [{ required: true, message: "请选择", trigger: "change" }],
      executionDate: [{ required: true, message: "请选择", trigger: "change" }],
      purchaseCategory: [
        { required: true, message: "请选择采购类别", trigger: "change" },
      ],
      purchaseOilProduct: [
        { required: true, message: "请选择采购油品", trigger: "change" },
      ],
      oilDepotId: [
        { required: true, message: "请选择采购油库", trigger: "change" },
      ],
      purchaseQuantity: [
        { required: true, message: "请输入采购数量", trigger: "change" },
      ],
    },
  });
  const { form, rules } = toRefs(data);
@@ -1311,9 +1521,10 @@
    isTemplateNameDuplicate.value = false;
    try {
      // å¹¶è¡ŒåŠ è½½åŸºç¡€æ•°æ®
      const [salesRes, supplierRes] = await Promise.all([
      const [salesRes, supplierRes, oilDepotRes] = await Promise.all([
        getSalesNo(),
        getOptions(),
        allOilDepot(),
      ]);
      salesContractList.value = salesRes || [];
@@ -1321,6 +1532,7 @@
      supplierList.value = (supplierRes.data || []).filter(
        item => item.isWhite === 0
      );
      oilDepotList.value = oilDepotRes?.data || [];
      form.value.entryDate = getCurrentDate();
@@ -1896,6 +2108,13 @@
      }
    }
  };
  // é‡‡è´­ç±»åˆ«æ”¹å˜ï¼šä»…代储/采购直销时客户名称可用,切换后清空
  const purchaseCategoryChange = val => {
    if (!["代储", "采购直销"].includes(val)) {
      form.value.customerName = "";
    }
  };
  // é”€å”®åˆåŒé€‰æ‹©æ”¹å˜æ–¹æ³•
  const salesLedgerChange = async row => {
    console.log("row", row);
@@ -1984,6 +2203,18 @@
</script>
<style scoped lang="scss">
  // è¡¨å•分栏标题:蓝色竖条 + æµ…蓝底色,突出分区
  .form-section-title {
    margin: 18px 0 14px;
    padding: 8px 14px;
    font-size: 15px;
    font-weight: 600;
    color: #1d6fd8;
    background: linear-gradient(90deg, rgba(64, 158, 255, 0.14), rgba(64, 158, 255, 0.02));
    border-left: 4px solid #409eff;
    border-radius: 3px;
  }
  .invalid-row {
    opacity: 0.6;
    background-color: #f5f7fa;
@@ -2001,6 +2232,24 @@
  .select-button-group {
    display: flex;
    align-items: center;
  }
  // é‡‡è´­æ²¹åº“下拉选项:小卡片样式
  .oil-depot-option {
    padding: 8px 10px;
    .oil-depot-option-title {
      font-size: 14px;
      font-weight: 600;
      color: #303133;
      margin-bottom: 4px;
    }
    .oil-depot-option-line {
      font-size: 12px;
      color: #909399;
      line-height: 1.8;
    }
  }
  // å®¡æ‰¹äººèŠ‚ç‚¹å®¹å™¨æ ·å¼
@@ -2064,3 +2313,25 @@
    }
  }
</style>
<style lang="scss">
  // ä¸‹æ‹‰é¢æ¿æ¸²æŸ“在 body ä¸‹ï¼Œéœ€å…¨å±€æ ·å¼
  .oil-depot-select-popper {
    .el-select-dropdown__item {
      height: auto;
      line-height: normal;
      padding: 4px 0;
    }
    .oil-depot-option {
      border: 1px solid #e4e7ed;
      border-radius: 6px;
      background-color: #fafbfc;
      &:hover {
        border-color: #409eff;
        background-color: #f0f7ff;
      }
    }
  }
</style>