新疆-融邦环保
1.「油品出库就生成出库台账,不用区分出库类别了」+「代储出库也需要出库扣库存,选批号,生成发货台账」
2.车辆管理与出库残油联动
已修改4个文件
86 ■■■■■ 文件已修改
src/components/AttachmentUpload/image/index.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/index.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/vehicle/index.vue 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/AttachmentUpload/image/index.vue
@@ -1,6 +1,6 @@
<script setup>
import {Plus} from '@element-plus/icons-vue'
import {uploadFile} from '@/api/basicData/common'
import {uploadFile, uploadPublicFile} from '@/api/basicData/common'
const props = defineProps({
  fileList: {
@@ -38,6 +38,12 @@
  uploadFieldName: {
    type: String,
    default: 'files',
  },
  // 主数据长期可看的图片走 /common/public/upload(publicKey 永久地址),
  // 默认仍走 /common/upload(带 2 小时失效的 token)以保持既有行为
  permanent: {
    type: Boolean,
    default: false,
  },
})
@@ -204,7 +210,7 @@
  proxy.$modal.loading('图片上传中,请稍候...')
  try {
    const response = await uploadFile(formData)
    const response = await (props.permanent ? uploadPublicFile(formData) : uploadFile(formData))
    const responseList = extractResponseArray(response).map((item, index) => normalizeResponseItem(item, index))
    if (!responseList.length) {
src/views/basicData/product/index.vue
@@ -9,8 +9,7 @@
                  @clear="searchFilter"
                  clearable
                  prefix-icon="Search" />
        <el-button v-if="false"
                   type="primary"
        <el-button type="primary"
                   @click="openProDia('addOne')"
                   style="margin-left: 10px">新增产品大类</el-button>
      </div>
src/views/basicData/vehicle/index.vue
@@ -83,6 +83,19 @@
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="车辆照片"
                         min-width="100"
                         align="center">
          <template #default="scope">
            <el-image v-if="scope.row.vehiclePhotos && scope.row.vehiclePhotos.length"
                      :src="scope.row.vehiclePhotos[0]"
                      :preview-src-list="scope.row.vehiclePhotos"
                      preview-teleported
                      fit="cover"
                      style="width: 60px; height: 44px; border-radius: 4px" />
            <span v-else>--</span>
          </template>
        </el-table-column>
        <el-table-column label="备注"
                         prop="remark"
                         min-width="160"
@@ -196,6 +209,16 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="车辆照片:"
                          prop="vehiclePhotos">
              <ImageUpload v-model:file-list="form.vehiclePhotos"
                           :limit="10"
                           permanent />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </FormDialog>
  </div>
@@ -203,6 +226,7 @@
<script setup>
  import FormDialog from "@/components/Dialog/FormDialog.vue";
  import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
  import pagination from "@/components/PIMTable/Pagination.vue";
  import { ref, reactive, toRefs, getCurrentInstance } from "vue";
  import { ElMessageBox } from "element-plus";
@@ -239,6 +263,7 @@
      loadCapacity: null,
      status: "启用",
      remark: "",
      vehiclePhotos: [],
    },
    rules: {
      plateNumber: [{ required: true, message: "请输入车牌号", trigger: "blur" }],
@@ -299,6 +324,8 @@
        loadCapacity,
        status: status || "启用",
        remark,
        // 接口返回的就是 URL 字符串数组,直接喂给 el-upload 回显
        vehiclePhotos: Array.isArray(row.vehiclePhotos) ? [...row.vehiclePhotos] : [],
      };
    } else {
      form.value = {
@@ -309,6 +336,7 @@
        loadCapacity: null,
        status: "启用",
        remark: "",
        vehiclePhotos: [],
      };
    }
  };
@@ -317,7 +345,15 @@
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        const api = form.value.id ? updateVehicle : addVehicle;
        api(form.value).then(() => {
        // 后端 vehiclePhotos 是 List<String>,只存 URL;上传组件给的是 {name, url} 对象数组,
        // 直接传对象会 JSON 解析失败报 400。另外传 null 不更新,想删光必须显式传 []
        const payload = {
          ...form.value,
          vehiclePhotos: (form.value.vehiclePhotos || [])
            .map(item => (typeof item === "string" ? item : item?.url || item?.previewURL || ""))
            .filter(Boolean),
        };
        api(payload).then(() => {
          proxy.$modal.msgSuccess("操作成功");
          closeDia();
          getList();
src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
@@ -32,7 +32,6 @@
            </el-form-item>
          </el-col>
        </el-row>
        <template v-if="!isStorageBound">
          <el-row :gutter="30">
            <el-col :span="24">
              <el-form-item label="产品名称"
@@ -40,12 +39,12 @@
                            :rules="[
                {
                required: true,
                message: '请选择产品',
              message: '请选择产品规格',
                trigger: 'change',
              }
            ]">
                <!-- 绑定卖油台账后,产品只能从该台账明细里选(代储台账没有明细) -->
                <el-select v-if="hasBoundLedger"
              <!-- 绑定卖油台账后产品只能从该台账明细里选;代储台账没有明细,仍走产品选择弹窗 -->
              <el-select v-if="useLedgerProducts"
                           v-model="formState.productModelId"
                           placeholder="请选择台账产品"
                           filterable
@@ -81,7 +80,6 @@
              </el-form-item>
            </el-col>
          </el-row>
        </template>
        <div class="form-section-title">出库信息</div>
        <el-row :gutter="30">
          <el-col :span="8">
@@ -105,9 +103,7 @@
          <el-col :span="8">
            <el-form-item label="出库批次"
                          prop="batchNo"
                          :rules="isSalesOut
                ? [{ required: true, message: '销售类出库必须选择出库批次', trigger: 'change' }]
                : []">
                          :rules="[{ required: true, message: '油品出库必须选择出库批次', trigger: 'change' }]">
              <el-select v-model="formState.batchNo"
                         placeholder="请选择"
                         clearable
@@ -396,7 +392,9 @@
          <el-col :span="12">
            <el-form-item label="车挂牌号"
                          prop="trailerPlateNo"
                          :rules="[{ required: true, message: '请输入车挂牌号', trigger: 'blur' }]">
                          :rules="isSalesOut
                ? [{ required: true, message: '请输入车挂牌号', trigger: 'blur' }]
                : []">
              <el-input v-model="formState.trailerPlateNo"
                        :readonly="!!formState.vehicleId"
                        placeholder="请输入"
@@ -455,7 +453,7 @@
          </el-col>
        </el-row>
        <el-row :gutter="30"
                v-if="isSalesOut">
                v-if="formState.productModelId">
          <el-col :span="24">
            <el-form-item label="可用量">
              <div class="available-qty-tip">
@@ -567,8 +565,10 @@
  const productModelCache = ref(null);
  const hasBoundLedger = computed(() => !!formState.value.salesLedgerId);
  // 绑代储台账:不用选产品,油库/储罐由台账带入
  // 绑代储台账:油库/储罐由台账带入,但产品仍要选(代储出库也要扣库存)
  const isStorageBound = computed(() => boundLedgerRow.value?.ledgerType === "代储");
  // 只有卖油台账有产品明细可挑;代储台账没明细,走产品选择弹窗
  const useLedgerProducts = computed(() => hasBoundLedger.value && !isStorageBound.value);
  const fetchBatchNoOptions = () => {
    batchNoOptions.value = [];
@@ -588,14 +588,15 @@
    });
  };
  // 销售类出库必须给出库批次(后端新增时即校验,且审批通过会按批次扣库存)
  // 车辆只有销售类出库必填(销售类才按车算「上次剩余油量」,代储/客存残油按 0)
  const isSalesOut = computed(() => formState.value.outCategory === "销售");
  // 可用量提示:车辆 + 规格(+批次)确定后向后端取「上次剩余 / 当前库存 / 可用量」
  // 可用量提示:规格(+批次+车辆)确定后向后端取「上次剩余 / 当前库存 / 可用量」。
  // 代储/客存没有车辆,不传 vehicleId,后端按上次剩余 0 处理,可用量 = 当前库存
  const fetchAvailableQty = () => {
    availableQty.value = null;
    const { vehicleId, productModelId, batchNo } = formState.value;
    if (!isSalesOut.value || !vehicleId || !productModelId) {
    if (!productModelId) {
      return;
    }
    getOilOutAvailableQty({ vehicleId, productModelId, batchNo })
@@ -911,9 +912,9 @@
  const handleSubmit = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        // 绑代储台账不用选产品(代储台账没有产品明细)
        if (!formState.value.productModelId && !isStorageBound.value) {
          proxy.$modal.msgError("请选择产品");
        // 销售/代储/客存都要扣库存,一律必须选规格(代储台账没明细,从产品弹窗选)
        if (!formState.value.productModelId) {
          proxy.$modal.msgError("请选择产品规格");
          return;
        }
        // 计算结果随表单提交