已添加2个文件
已修改3个文件
751 ■■■■■ 文件已修改
src/api/collaborativeApproval/customerVisit.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/ImageUpload/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/customerVisit/index.vue 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/deliveryLedger/index.vue 305 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 165 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/collaborativeApproval/customerVisit.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
import request from '@/utils/request'
// èŽ·å–æ‹œè®¿è®°å½•åˆ—è¡¨
export function getVisitRecords(query) {
  return request({
    url: '/customerVisits/listPage',
    method: 'get',
    params: query
  })
}
src/components/ImageUpload/index.vue
@@ -47,6 +47,8 @@
</template>
<script setup>
import { ref, computed, watch, getCurrentInstance, onMounted, nextTick } from "vue";
import { Plus } from "@element-plus/icons-vue";
import { getToken } from "@/utils/auth";
import { isExternal } from "@/utils/validate";
import Sortable from "sortablejs";
src/views/collaborativeApproval/customerVisit/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,269 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm" :inline="true">
        <el-form-item label="客户名称:">
          <el-input
            v-model="searchForm.customerName"
            placeholder="请输入客户名称"
            clearable
            prefix-icon="Search"
            style="width: 200px"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="拜访人:">
          <el-input
            v-model="searchForm.visitingPeople"
            placeholder="请输入拜访人"
            clearable
            prefix-icon="Search"
            style="width: 200px"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery">搜索</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="table_list">
      <el-table
        :data="tableData"
        border
        v-loading="tableLoading"
        style="width: 100%"
        height="calc(100vh - 18.5em)"
      >
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="客户名称" prop="customerName" width="150" show-overflow-tooltip />
        <el-table-column label="联系人" prop="contact" width="120" show-overflow-tooltip />
        <el-table-column label="联系电话" prop="contactPhone" width="140" show-overflow-tooltip />
        <el-table-column label="拜访目的" prop="purposeVisit" width="150" show-overflow-tooltip />
        <el-table-column label="拜访时间" prop="purposeDate" width="180" show-overflow-tooltip />
        <el-table-column label="拜访地点" prop="visitAddress" min-width="200" show-overflow-tooltip />
        <el-table-column label="拜访人" prop="visitingPeople" width="120" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" width="100" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="viewDetail(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>
    <!-- è¯¦æƒ…弹窗 -->
    <el-dialog
      v-model="detailVisible"
      title="客户拜访记录详情"
      width="600px"
      @close="closeDetail"
    >
      <div class="content-container">
        <!-- å®¢æˆ·ä¿¡æ¯ -->
        <div class="section">
          <div class="section-title">客户信息</div>
          <div class="info-item">
            <span class="info-label">客户名称</span>
            <span class="info-value">{{ detailForm.customerName || '-' }}</span>
          </div>
          <div class="info-item">
            <span class="info-label">联系人</span>
            <span class="info-value">{{ detailForm.contact || '-' }}</span>
          </div>
          <div class="info-item">
            <span class="info-label">联系电话</span>
            <span class="info-value">{{ detailForm.contactPhone || '-' }}</span>
          </div>
        </div>
        <!-- æ‹œè®¿ä¿¡æ¯ -->
        <div class="section">
          <div class="section-title">拜访信息</div>
          <div class="info-item">
            <span class="info-label">拜访目的</span>
            <span class="info-value">{{ detailForm.purposeVisit || '-' }}</span>
          </div>
          <div class="info-item">
            <span class="info-label">拜访时间</span>
            <span class="info-value">{{ detailForm.purposeDate || '-' }}</span>
          </div>
          <div class="info-item">
            <span class="info-label">拜访地点</span>
            <span class="info-value multi-line">{{ detailForm.visitAddress || '-' }}</span>
          </div>
          <div class="info-item">
            <span class="info-label">拜访人</span>
            <span class="info-value">{{ detailForm.visitingPeople || '-' }}</span>
          </div>
          <div class="info-item" v-if="detailForm.latitude && detailForm.longitude">
            <span class="info-label">经纬度</span>
            <span class="info-value">{{ detailForm.latitude }}, {{ detailForm.longitude }}</span>
          </div>
        </div>
        <!-- å¤‡æ³¨ä¿¡æ¯ -->
        <div class="section">
          <div class="section-title">备注信息</div>
          <div class="info-item remark-item">
            <span class="info-label">备注</span>
            <span class="info-value multi-line">{{ detailForm.remark || '-' }}</span>
          </div>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDetail">关闭</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
import pagination from '@/components/PIMTable/Pagination.vue'
import { getVisitRecords } from '@/api/collaborativeApproval/customerVisit.js'
const { proxy } = getCurrentInstance()
const tableData = ref([])
const tableLoading = ref(false)
const page = reactive({
  current: 1,
  size: 10,
})
const total = ref(0)
// æœç´¢è¡¨å•
const searchForm = reactive({
  customerName: '',
  visitingPeople: '',
})
// è¯¦æƒ…相关
const detailVisible = ref(false)
const detailForm = ref({})
// æŸ¥è¯¢åˆ—表
const handleQuery = () => {
  page.current = 1
  getList()
}
// åˆ†é¡µå˜åŒ–
const paginationChange = (obj) => {
  page.current = obj.page
  page.size = obj.limit
  getList()
}
// èŽ·å–åˆ—è¡¨æ•°æ®
const getList = () => {
  tableLoading.value = true
  getVisitRecords({ ...searchForm, ...page })
    .then((res) => {
      tableLoading.value = false
      if (res.code === 200) {
        tableData.value = res.data?.records || res.records || []
        total.value = res.data?.total || res.total || 0
      } else {
        proxy.$modal.msgError(res.msg || '获取数据失败')
      }
    })
    .catch(() => {
      tableLoading.value = false
    })
}
// æŸ¥çœ‹è¯¦æƒ…
const viewDetail = (row) => {
  detailForm.value = { ...row }
  detailVisible.value = true
}
// å…³é—­è¯¦æƒ…
const closeDetail = () => {
  detailVisible.value = false
  detailForm.value = {}
}
onMounted(() => {
  getList()
})
</script>
<style scoped lang="scss">
.table_list {
  margin-top: unset;
}
.content-container {
  padding: 10px;
}
.section {
  margin-bottom: 24px;
  &:last-child {
    margin-bottom: 0;
  }
}
.section-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
  margin-bottom: 16px;
  padding-bottom: 8px;
  border-bottom: 1px solid #e4e7ed;
}
.info-item {
  display: flex;
  margin-bottom: 12px;
  line-height: 1.6;
  &:last-child {
    margin-bottom: 0;
  }
  &.remark-item {
    flex-direction: column;
    align-items: flex-start;
    .info-label {
      margin-bottom: 8px;
    }
    .info-value {
      width: 100%;
    }
  }
}
.info-label {
  font-weight: 500;
  color: #606266;
  min-width: 100px;
  margin-right: 12px;
  flex-shrink: 0;
}
.info-value {
  color: #303133;
  flex: 1;
  word-break: break-all;
  &.multi-line {
    white-space: pre-wrap;
    word-break: break-word;
  }
}
</style>
src/views/salesManagement/deliveryLedger/index.vue
@@ -3,11 +3,15 @@
    <div class="search_form">
      <el-form :model="searchForm" :inline="true">
        <el-form-item label="销售订单号:">
          <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search"
          <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search" style="width: 200px"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="车牌号:">
          <el-input v-model="searchForm.shippingCarNumber" placeholder="请输入" clearable prefix-icon="Search"
          <el-input v-model="searchForm.shippingCarNumber" placeholder="请输入" clearable prefix-icon="Search" style="width: 200px"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="快递单号:">
          <el-input v-model="searchForm.expressNumber" placeholder="请输入" clearable prefix-icon="Search" style="width: 200px"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item>
@@ -24,13 +28,15 @@
        </div>
      </div>
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :row-key="(row) => row.id" style="width: 100%" height="calc(100vh - 18.5em)">
        :row-key="(row) => row.id" style="width: 100%" height="calc(100vh - 21.5em)">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="销售订单" prop="salesContractNo" show-overflow-tooltip />
        <el-table-column label="客户名称" prop="customerName" show-overflow-tooltip />
        <el-table-column label="发货时间" prop="shippingDate" show-overflow-tooltip />
        <el-table-column label="发货车牌号" prop="shippingCarNumber" show-overflow-tooltip />
        <el-table-column label="快递公司" prop="expressCompany" show-overflow-tooltip />
        <el-table-column label="快递单号" prop="expressNumber" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" width="150" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">编辑</el-button>
@@ -41,45 +47,99 @@
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增发货台账' : '编辑发货台账'" width="50%"
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增发货台账' : '编辑发货台账'" width="40%"
      @close="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="salesContractNo">
              <el-select v-model="form.salesContractNo" placeholder="请选择" clearable filterable
                @change="handleSalesOrderChange" style="width: 100%" :disabled="operationType === 'edit'">
                <el-option v-for="item in salesOrderOptions" :key="item.salesContractNo"
                  :label="item.salesContractNo" :value="item.salesContractNo">
                  {{ item.salesContractNo + ' - ' + item.customerName }}
                </el-option>
            <el-form-item label="发货类型:" prop="type">
              <el-select
                v-model="form.type"
                placeholder="请选择发货类型"
                style="width: 100%"
                @change="handleShippingTypeChange"
              >
                <el-option label="货车" value="货车" />
                <el-option label="快递" value="快递" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="客户名称:" prop="customerName">
              <el-input v-model="form.customerName" placeholder="请输入" clearable :disabled="operationType === 'edit'" />
            <el-form-item label="发货日期:" prop="shippingDate">
              <el-date-picker
                style="width: 100%"
                v-model="form.shippingDate"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                placeholder="请选择发货日期"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="发货时间:" prop="shippingDate">
              <el-date-picker style="width: 100%" v-model="form.shippingDate" value-format="YYYY-MM-DD"
                format="YYYY-MM-DD" type="date" placeholder="请选择" clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
          <el-col :span="24" v-if="form.type === '货车'">
            <el-form-item label="发货车牌号:" prop="shippingCarNumber">
              <el-input v-model="form.shippingCarNumber" placeholder="请输入" clearable />
              <el-input
                v-model="form.shippingCarNumber"
                placeholder="请输入发货车牌号"
                clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="24" v-else>
            <el-form-item label="快递公司:" prop="expressCompany">
              <el-input
                v-model="form.expressCompany"
                placeholder="请输入快递公司"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30" v-if="form.type === '快递'">
          <el-col :span="24">
            <el-form-item label="快递单号:" prop="expressNumber">
              <el-input
                v-model="form.expressNumber"
                placeholder="请输入快递单号"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="发货图片:">
              <el-upload
                v-model:file-list="deliveryFileList"
                :action="upload.url"
                multiple
                ref="deliveryFileUpload"
                auto-upload
                :headers="upload.headers"
                :data="{ type: 9 }"
                :before-upload="handleDeliveryBeforeUpload"
                :on-error="handleDeliveryUploadError"
                :on-success="handleDeliveryUploadSuccess"
                :on-remove="handleDeliveryRemove"
                list-type="picture-card"
                :limit="9"
                accept="image/png,image/jpeg,image/jpg"
              >
                <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
                <template #tip>
                  <div class="el-upload__tip">
                    æ”¯æŒ jpg、jpeg、png æ ¼å¼ï¼Œæœ€å¤šä¸Šä¼  9 å¼ ï¼Œå•张大小不超过 10MB
                  </div>
                </template>
              </el-upload>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
@@ -95,11 +155,15 @@
import pagination from "@/components/PIMTable/Pagination.vue";
import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
import { ElMessageBox } from "element-plus";
import { Plus } from "@element-plus/icons-vue";
import { getToken } from "@/utils/auth";
import { getCurrentDate } from "@/utils/index.js";
import {
  deliveryLedgerListPage,
  addOrUpdateDeliveryLedger,
  delDeliveryLedger,
} from "@/api/salesManagement/deliveryLedger.js";
import { delLedgerFile } from "@/api/salesManagement/salesLedger.js";
 
const { proxy } = getCurrentInstance();
@@ -112,6 +176,16 @@
  size: 100,
});
const total = ref(0);
const deliveryFileList = ref([]);
const javaApi = proxy.javaApi;
// ä¸Šä¼ é…ç½®
const upload = reactive({
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
});
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref("");
@@ -120,19 +194,31 @@
  searchForm: {
    salesContractNo: "", // é”€å”®è®¢å•号
    shippingCarNumber: "", // è½¦ç‰Œå·
    expressNumber: "", // å¿«é€’单号
  },
  form: {
    id: null,
    salesContractNo: "",
    customerName: "",
    type: "货车", // è´§è½¦, å¿«é€’
    shippingDate: "",
    shippingCarNumber: "",
    expressCompany: "",
    expressNumber: "", // å¿«é€’单号
  },
  rules: {
    salesContractNo: [{ required: true, message: "请选择销售订单", trigger: "change" }],
    customerName: [{ required: true, message: "请输入客户名称", trigger: "blur" }],
    type: [
      { required: true, message: "请选择发货类型", trigger: "change" }
    ],
    shippingDate: [{ required: true, message: "请选择发货时间", trigger: "change" }],
    shippingCarNumber: [{ required: true, message: "请输入发货车牌号", trigger: "blur" }],
    shippingCarNumber: [
      { validator: (_, value, callback) => validateShippingCarNumber(value, callback), trigger: "blur" }
    ],
    expressCompany: [
      { validator: (_, value, callback) => validateExpressCompany(value, callback), trigger: "blur" }
    ],
  },
});
const { form, rules } = toRefs(data);
@@ -181,22 +267,85 @@
// æ‰“开弹框
const openForm = async (type, row) => {
  operationType.value = type;
  const baseUrl = import.meta.env.VITE_APP_BASE_API;
  if (type === 'edit' && row) {
    form.value = {
      id: row.id ?? null,
      salesContractNo: row.salesContractNo ?? "",
      customerName: row.customerName ?? "",
      type: row.type || "货车",
      shippingDate: row.shippingDate || getCurrentDate(),
      shippingCarNumber: row.shippingCarNumber ?? "",
      expressCompany: row.expressCompany ?? "",
      expressNumber: row.expressNumber ?? "",
    };
    // å¦‚果有图片,将 commonFileList è½¬æ¢ä¸ºæ–‡ä»¶åˆ—表格式
    if (row.commonFileList && Array.isArray(row.commonFileList) && row.commonFileList.length > 0) {
      deliveryFileList.value = row.commonFileList.map((file, index) => {
        // å¤„理 URL:将 Windows è·¯å¾„转换为可访问的 URL
        let fileUrl = file.url || '';
        console.log('原始 URL:', fileUrl);
        // å¦‚æžœ URL æ˜¯ Windows è·¯å¾„格式(包含反斜杠),需要转换
        if (fileUrl && fileUrl.indexOf('\\') > -1) {
          // æŸ¥æ‰¾ uploads å…³é”®å­—的位置,从那里开始提取相对路径
          const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads');
          if (uploadsIndex > -1) {
            // ä»Ž uploads å¼€å§‹æå–路径,并将反斜杠替换为正斜杠
            const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/');
            fileUrl = '/' + relativePath;
            console.log('转换后的相对路径:', fileUrl);
          } else {
            // å¦‚果没有找到 uploads,提取最后一个目录和文件名
            const parts = fileUrl.split('\\');
            const fileName = parts[parts.length - 1];
            fileUrl = '/uploads/' + fileName;
            console.log('未找到 uploads,使用文件名:', fileUrl);
          }
        }
        // ç¡®ä¿æ‰€æœ‰éž http å¼€å¤´çš„ URL éƒ½æ‹¼æŽ¥ baseUrl
        if (fileUrl && !fileUrl.startsWith('http')) {
          // ç¡®ä¿è·¯å¾„以 / å¼€å¤´
          if (!fileUrl.startsWith('/')) {
            fileUrl = '/' + fileUrl;
          }
          // æ‹¼æŽ¥ baseUrl
          fileUrl = javaApi + fileUrl;
          console.log('最终拼接的 URL:', fileUrl);
        }
        return {
          uid: file.id || Date.now() + index,
          name: file.name || `image_${index + 1}.jpg`,
          url: fileUrl,
          status: 'success',
          response: {
            code: 200,
            data: {
              tempId: file.id,
              url: fileUrl
            }
          },
          tempId: file.id // ä¿å­˜æ–‡ä»¶ID,用于提交时使用
        };
      });
    } else {
      deliveryFileList.value = [];
    }
  } else {
    form.value = {
      id: null,
      salesContractNo: "",
      customerName: "",
      type: "货车",
      shippingDate: getCurrentDate(),
      shippingCarNumber: "",
      expressCompany: "",
      expressNumber: "",
    };
    deliveryFileList.value = [];
  }
  
  dialogFormVisible.value = true;
@@ -206,10 +355,18 @@
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      let tempFileIds = [];
      if (deliveryFileList.value !== null && deliveryFileList.value.length > 0) {
        tempFileIds = deliveryFileList.value.map((item) => item.tempId);
      }
      const payload = {
        id: form.value.id,
        type: form.value.type,
        shippingDate: form.value.shippingDate,
        shippingCarNumber: form.value.shippingCarNumber,
        shippingCarNumber: form.value.type === "货车" ? form.value.shippingCarNumber : "",
        expressCompany: form.value.type === "快递" ? form.value.expressCompany : "",
        expressNumber: form.value.type === "快递" ? form.value.expressNumber : "",
        tempFileIds: tempFileIds,
      };
      addOrUpdateDeliveryLedger(payload).then((res) => {
        proxy.$modal.msgSuccess("操作成功");
@@ -223,6 +380,7 @@
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  deliveryFileList.value = []; // æ¸…空文件列表
  dialogFormVisible.value = false;
};
@@ -284,14 +442,88 @@
    });
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0");
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
// å‘货类型校验:货车时要求车牌,快递时要求快递公司
const validateShippingCarNumber = (value, callback) => {
  if (form.value.type === "货车") {
    if (!value) return callback(new Error("请输入发货车牌号"));
  }
  callback();
};
const validateExpressCompany = (value, callback) => {
  if (form.value.type === "快递") {
    if (!value) return callback(new Error("请输入快递公司"));
  }
  callback();
};
// å‘货图片上传前校检
function handleDeliveryBeforeUpload(file) {
  // æ ¡æ£€æ–‡ä»¶ç±»åž‹
  const isImage = file.type === 'image/png' || file.type === 'image/jpeg' || file.type === 'image/jpg';
  if (!isImage) {
    proxy.$modal.msgError("只能上传 jpg、jpeg、png æ ¼å¼çš„图片!");
    return false;
  }
  // æ ¡æ£€æ–‡ä»¶å¤§å°
  const isLt10M = file.size / 1024 / 1024 < 10;
  if (!isLt10M) {
    proxy.$modal.msgError("上传图片大小不能超过 10MB!");
    return false;
  }
  proxy.$modal.loading("正在上传图片,请稍候...");
  return true;
}
// å‘货图片上传失败
function handleDeliveryUploadError(err) {
  proxy.$modal.msgError("上传图片失败");
  proxy.$modal.closeLoading();
}
// å‘货图片上传成功回调
function handleDeliveryUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
  if (res.code === 200) {
    file.tempId = res.data.tempId;
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.deliveryFileUpload.handleRemove(file);
  }
}
// ç§»é™¤å‘货图片
function handleDeliveryRemove(file) {
  console.log('file--', file)
  // å¦‚果是编辑模式且文件有 id,需要调用接口删除
  if (operationType.value === "edit") {
    let ids = [];
    ids.push(file.uid);
    delLedgerFile(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
      // ä»Žæ–‡ä»¶åˆ—表中移除
      const index = deliveryFileList.value.findIndex(item => item.uid === file.uid);
      if (index > -1) {
        deliveryFileList.value.splice(index, 1);
      }
    }).catch(() => {
      proxy.$modal.msgError("删除失败");
    });
  } else {
    // æ–°å¢žæ¨¡å¼æˆ–没有 id çš„æ–‡ä»¶ï¼Œç›´æŽ¥ä»Žåˆ—表中移除
    const index = deliveryFileList.value.findIndex(item => item.uid === file.uid);
    if (index > -1) {
      deliveryFileList.value.splice(index, 1);
    }
  }
}
// å‘货类型切换时清空对应字段
const handleShippingTypeChange = (val) => {
  if (val === "货车") {
    form.value.expressCompany = "";
    form.value.expressNumber = "";
  } else {
    form.value.shippingCarNumber = "";
  }
};
onMounted(() => {
  getList();
@@ -308,5 +540,12 @@
  justify-content: space-between;
  margin-bottom: 10px;
}
// éšè—å›¾ç‰‡ä¸Šä¼ ç»„件的预览按钮(放大镜)
:deep(.el-upload-list--picture-card .el-upload-list__item-actions) {
  .el-upload-list__item-preview {
    display: none;
  }
}
</style>
src/views/salesManagement/salesLedger/index.vue
@@ -46,15 +46,6 @@
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="单位" prop="unit" />
              <el-table-column label="产品状态" width="100px" align="center">
                <template #default="scope">
                  <el-tag v-if="scope.row.approveStatus === 0" type="info">未出库</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 1" type="success">已出库</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 2" type="warning">审核中</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 3" type="success">审核成功</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 4" type="danger">审核失败</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
@@ -73,13 +64,21 @@
        <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip />
        <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip />
        <el-table-column label="发货车牌" prop="shippingCarNumber" width="140" align="center" show-overflow-tooltip />
        <el-table-column label="发货状态" prop="shippingStatus" width="140" align="center" show-overflow-tooltip />
        <el-table-column label="发货日期" prop="shippingDate" width="140" align="center" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="140" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">编辑</el-button>
            <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">附件</el-button>
            <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>
            <el-button
              link
              type="primary"
              size="small"
              @click="openDeliveryForm(scope.row)"
              :disabled="scope.row.shippingStatus === '已发货'"
            >
              å‘è´§
            </el-button>
          </template>
        </el-table-column>
      </el-table>
@@ -426,15 +425,15 @@
            <el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef">
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="发货类型:" prop="shippingType">
                        <el-form-item label="发货类型:" prop="type">
                            <el-select
                                v-model="deliveryForm.shippingType"
                                v-model="deliveryForm.type"
                                placeholder="请选择发货类型"
                                style="width: 100%"
                                @change="handleShippingTypeChange"
                            >
                                <el-option label="货车" value="truck" />
                                <el-option label="快递" value="express" />
                                <el-option label="货车" value="货车" />
                                <el-option label="快递" value="快递" />
                            </el-select>
                        </el-form-item>
                    </el-col>
@@ -455,7 +454,7 @@
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24" v-if="deliveryForm.shippingType === 'truck'">
                    <el-col :span="24" v-if="deliveryForm.type === '货车'">
                        <el-form-item label="发货车牌号:" prop="shippingCarNumber">
                            <el-input
                                v-model="deliveryForm.shippingCarNumber"
@@ -474,6 +473,46 @@
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30" v-if="deliveryForm.type === '快递'">
                    <el-col :span="24">
                        <el-form-item label="快递单号:" prop="expressNumber">
                            <el-input
                                v-model="deliveryForm.expressNumber"
                                placeholder="请输入快递单号"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                    <el-row :gutter="30">
                        <el-col :span="24">
                            <el-form-item label="发货图片:">
                                <el-upload
                                    v-model:file-list="deliveryFileList"
                                    :action="upload.url"
                                    multiple
                                    ref="deliveryFileUpload"
                                    auto-upload
                                    :headers="upload.headers"
                                    :data="{ type: 9 }"
                                    :before-upload="handleDeliveryBeforeUpload"
                                    :on-error="handleDeliveryUploadError"
                                    :on-success="handleDeliveryUploadSuccess"
                                    :on-remove="handleDeliveryRemove"
                                    list-type="picture-card"
                                    :limit="9"
                                    accept="image/png,image/jpeg,image/jpg"
                                >
                                    <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
                                    <template #tip>
                                        <div class="el-upload__tip">
                                            æ”¯æŒ jpg、jpeg、png æ ¼å¼ï¼Œæœ€å¤šä¸Šä¼  9 å¼ ï¼Œå•张大小不超过 10MB
                                        </div>
                                    </template>
                                </el-upload>
                            </el-form-item>
                        </el-col>
                    </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
@@ -492,7 +531,7 @@
import {onMounted, ref, getCurrentInstance} from "vue";
import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
import { ElMessageBox, ElMessage } from "element-plus";
import { UploadFilled } from "@element-plus/icons-vue";
import { UploadFilled, Plus } from "@element-plus/icons-vue";
import useUserStore from "@/store/modules/user";
import { userListNoPage } from "@/api/system/user.js";
import FileList from '@/views/salesManagement/salesLedger/fileList.vue';
@@ -532,6 +571,7 @@
});
const total = ref(0);
const fileList = ref([]);
const deliveryFileList = ref([]);
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref("");
@@ -621,13 +661,15 @@
const currentDeliveryRow = ref(null);
const deliveryFormData = reactive({
  deliveryForm: {
    shippingType: "truck", // truck: è´§è½¦, express: å¿«é€’
    type: "货车", // è´§è½¦, å¿«é€’
    shippingDate: "",
    shippingCarNumber: "",
    expressCompany: "",
    expressNumber: "", // å¿«é€’单号
    shippingImages: "", // å‘货图片,多个用逗号分隔
  },
  deliveryRules: {
    shippingType: [
    type: [
      { required: true, message: "请选择发货类型", trigger: "change" }
    ],
    shippingDate: [
@@ -898,6 +940,47 @@
    delLedgerFile(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
    });
  }
}
// å‘货图片上传前校检
function handleDeliveryBeforeUpload(file) {
  // æ ¡æ£€æ–‡ä»¶ç±»åž‹
  const isImage = file.type === 'image/png' || file.type === 'image/jpeg' || file.type === 'image/jpg';
  if (!isImage) {
    proxy.$modal.msgError("只能上传 jpg、jpeg、png æ ¼å¼çš„图片!");
    return false;
  }
  // æ ¡æ£€æ–‡ä»¶å¤§å°
  const isLt10M = file.size / 1024 / 1024 < 10;
  if (!isLt10M) {
    proxy.$modal.msgError("上传图片大小不能超过 10MB!");
    return false;
  }
  proxy.$modal.loading("正在上传图片,请稍候...");
  return true;
}
// å‘货图片上传失败
function handleDeliveryUploadError(err) {
  proxy.$modal.msgError("上传图片失败");
  proxy.$modal.closeLoading();
}
// å‘货图片上传成功回调
function handleDeliveryUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
  if (res.code === 200) {
    file.tempId = res.data.tempId;
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.deliveryFileUpload.handleRemove(file);
  }
}
// ç§»é™¤å‘货图片
function handleDeliveryRemove(file) {
  // ä»Žæ–‡ä»¶åˆ—表中移除
  const index = deliveryFileList.value.findIndex(item => item.uid === file.uid);
  if (index > -1) {
    deliveryFileList.value.splice(index, 1);
  }
}
// æäº¤è¡¨å•
@@ -1447,13 +1530,13 @@
// å‘货类型校验:货车时要求车牌,快递时要求快递公司
const validateShippingCarNumber = (value, callback) => {
  if (deliveryForm.value.shippingType === "truck") {
  if (deliveryForm.value.type === "货车") {
    if (!value) return callback(new Error("请输入发货车牌号"));
  }
  callback();
};
const validateExpressCompany = (value, callback) => {
  if (deliveryForm.value.shippingType === "express") {
  if (deliveryForm.value.type === "快递") {
    if (!value) return callback(new Error("请输入快递公司"));
  }
  callback();
@@ -1647,23 +1730,16 @@
// æ‰“开发货弹框
const openDeliveryForm = (row) => {
  // getProductInventory({ salesLedgerId: row.id, type:1 }).then((res) => {
  //   currentDeliveryRow.value = row;
  //   deliveryForm.value = {
  //     shippingDate: getCurrentDate(),
  //     shippingCarNumber: "",
  //   };
  //   deliveryFormVisible.value = true;
  // }).catch(err => {
  //   ElMessage.error(err.msg);
  // });
    currentDeliveryRow.value = row;
  deliveryForm.value = {
    shippingType: "truck",
    type: "货车",
    shippingDate: getCurrentDate(),
    shippingCarNumber: "",
    expressCompany: "",
    expressNumber: "", // åˆå§‹åŒ–快递单号为空
    shippingImages: "", // åˆå§‹åŒ–图片为空
  };
  deliveryFileList.value = []; // åˆå§‹åŒ–文件列表为空
    deliveryFormVisible.value = true;
};
@@ -1671,12 +1747,18 @@
const submitDelivery = () => {
  proxy.$refs["deliveryFormRef"].validate((valid) => {
    if (valid) {
      let tempFileIds = [];
      if (deliveryFileList.value !== null && deliveryFileList.value.length > 0) {
        tempFileIds = deliveryFileList.value.map((item) => item.tempId);
      }
      addShippingInfo({
        salesLedgerId: currentDeliveryRow.value.id,
        shippingType: deliveryForm.value.shippingType,
        type: deliveryForm.value.type,
        shippingDate: deliveryForm.value.shippingDate,
        shippingCarNumber: deliveryForm.value.shippingType === "truck" ? deliveryForm.value.shippingCarNumber : "",
        expressCompany: deliveryForm.value.shippingType === "express" ? deliveryForm.value.expressCompany : "",
        shippingCarNumber: deliveryForm.value.type === "货车" ? deliveryForm.value.shippingCarNumber : "",
        expressCompany: deliveryForm.value.type === "快递" ? deliveryForm.value.expressCompany : "",
        expressNumber: deliveryForm.value.type === "快递" ? deliveryForm.value.expressNumber : "",
        tempFileIds: tempFileIds,
      })
        .then(() => {
          proxy.$modal.msgSuccess("发货成功");
@@ -1693,14 +1775,18 @@
// å…³é—­å‘货弹框
const closeDeliveryDia = () => {
  proxy.resetForm("deliveryFormRef");
  deliveryFileList.value = []; // æ¸…空文件列表
  deliveryForm.value.shippingImages = ""; // æ¸…空图片
  deliveryForm.value.expressNumber = ""; // æ¸…空快递单号
  deliveryFormVisible.value = false;
  currentDeliveryRow.value = null;
};
// å‘货类型切换时清空对应字段
const handleShippingTypeChange = (val) => {
  if (val === "truck") {
  if (val === "货车") {
    deliveryForm.value.expressCompany = "";
    deliveryForm.value.expressNumber = "";
  } else {
    deliveryForm.value.shippingCarNumber = "";
  }
@@ -1891,4 +1977,11 @@
        page-break-after: avoid;
    }
}
// éšè—å›¾ç‰‡ä¸Šä¼ ç»„件的预览按钮(放大镜)
:deep(.el-upload-list--picture-card .el-upload-list__item-actions) {
  .el-upload-list__item-preview {
    display: none;
  }
}
</style>