张诺
15 小时以前 ccd67e291e00a2ad9c29ad8df43de6fab5a4afed
src/views/salesManagement/salesQuotation/index.vue
@@ -310,6 +310,52 @@
            </el-form-item>
          </div>
        </el-card>
        <el-card class="form-card" shadow="hover">
          <template #header>
            <div class="card-header-wrapper">
              <el-icon class="card-icon"><Paperclip /></el-icon>
              <span class="card-title">附件材料</span>
            </div>
          </template>
          <div class="form-content">
            <el-form-item label="附件">
              <el-upload
                v-model:file-list="fileList"
                :action="upload.url"
                multiple
                ref="fileUpload"
                auto-upload
                :headers="upload.headers"
                :before-upload="handleBeforeUpload"
                :on-error="handleUploadError"
                :on-success="handleUploadSuccess"
                :on-remove="handleRemove"
                :on-preview="handlePreview"
              >
                <el-button type="primary">上传</el-button>
                <template #file="{ file }">
                  <div style="display:flex; align-items:center; gap: 10px; width: 100%;">
                    <span style="flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                      {{ file.name }}
                    </span>
                    <div style="display:flex; align-items:center; gap: 6px;">
                      <el-button link type="success" :icon="Download" @click="handleDownload(file)" />
                      <el-button link type="primary" :icon="View" @click="handlePreview(file)" />
                      <el-button link type="danger" :icon="Delete" @click="triggerRemoveFile(file)" />
                    </div>
                  </div>
                </template>
                <template #tip>
                  <div class="el-upload__tip">
                    文件格式支持 doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
                  </div>
                </template>
              </el-upload>
            </el-form-item>
          </div>
        </el-card>
      </el-form>
      </div>
    </FormDialog>
@@ -350,19 +396,25 @@
        <p>{{ currentQuotation.remark }}</p>
      </div>
    </el-dialog>
    <filePreview ref="filePreviewRef" />
  </div>
</template>
<script setup>
import { ref, reactive, computed, onMounted, markRaw, shallowRef } from 'vue'
import { ref, reactive, computed, onMounted, markRaw, shallowRef, getCurrentInstance } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search, Document, UserFilled, Box, EditPen, Plus, ArrowRight, Delete } from '@element-plus/icons-vue'
import { Search, Document, UserFilled, Box, EditPen, Plus, ArrowRight, Delete, Paperclip, View, Download } from '@element-plus/icons-vue'
import Pagination from '@/components/PIMTable/Pagination.vue'
import FormDialog from '@/components/Dialog/FormDialog.vue'
import {getQuotationList,addQuotation,updateQuotation,deleteQuotation} from '@/api/salesManagement/salesQuotation.js'
import {userListNoPage} from "@/api/system/user.js";
import {customerList} from "@/api/salesManagement/salesLedger.js";
import { customerList, delLedgerFile } from "@/api/salesManagement/salesLedger.js";
import {modelList, productTreeList} from "@/api/basicData/product.js";
import { getToken } from "@/utils/auth";
import filePreview from "@/components/filePreview/index.vue";
const { proxy } = getCurrentInstance()
// 响应式数据
const loading = ref(false)
@@ -384,6 +436,13 @@
const dialogVisible = ref(false)
const viewDialogVisible = ref(false)
const dialogTitle = ref('新增报价')
const fileList = ref([])
const fileUpload = ref()
const filePreviewRef = ref()
const upload = reactive({
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  headers: { Authorization: "Bearer " + getToken() },
})
const form = reactive({
  quotationNo: '',
  customer: '',
@@ -399,7 +458,8 @@
  otherFee: 0,
  discountRate: 0,
  discountAmount: 0,
  totalAmount: 0
  totalAmount: 0,
  tempFileIds: []
})
const baseRules = {
@@ -476,10 +536,28 @@
  handleSearch()
}
const normalizeQuotationFiles = (raw) => {
  const list =
    (raw && Array.isArray(raw.salesLedgerFiles) && raw.salesLedgerFiles) ||
    (raw && Array.isArray(raw.quotationFiles) && raw.quotationFiles) ||
    (raw && Array.isArray(raw.fileList) && raw.fileList) ||
    (raw && Array.isArray(raw.files) && raw.files) ||
    []
  return list
    .map((item) => ({
      id: item?.id,
      name: item?.fileName || item?.name || item?.originalName || item?.filename || "附件",
      url: item?.fileUrl || item?.url || item?.path || item?.tempPath,
      tempId: item?.tempId,
    }))
    .filter((i) => i.url)
}
const handleAdd = async () => {
  dialogTitle.value = '新增报价'
  isEdit.value = false
  resetForm()
  fileList.value = []
  // 重置审批人节点
  approverNodes.value = [{ id: 1, userId: null }]
  nextApproverId = 2
@@ -705,6 +783,7 @@
    userName: item.userName || ''
  }));
  
  fileList.value = normalizeQuotationFiles(row)
  dialogVisible.value = true
}
@@ -746,6 +825,9 @@
  form.discountRate = 0
  form.discountAmount = 0
  form.totalAmount = 0
  form.tempFileIds = []
  form.files = []
  fileList.value = []
}
const addProduct = () => {
@@ -787,6 +869,57 @@
  // 可以根据客户信息自动填充一些默认值
}
function handleBeforeUpload() {
  proxy?.$modal?.loading?.("正在上传文件,请稍候...")
  return true
}
function handleUploadError() {
  proxy?.$modal?.closeLoading?.()
  ElMessage.error("上传文件失败")
}
function handleUploadSuccess(res, file) {
  proxy?.$modal?.closeLoading?.()
  if (res?.code === 200) {
    file.tempId = res?.data?.tempId
    const url = res?.data?.tempPath || res?.data?.url
    if (url) file.url = url
    file.name = res?.data?.originalName || file?.name
    ElMessage.success("上传成功")
  } else {
    ElMessage.error(res?.msg || "上传失败")
    fileUpload.value?.handleRemove?.(file)
  }
}
function handleRemove(file) {
  if (!isEdit.value) return
  if (!file?.id) return
  delLedgerFile([file.id]).then((res) => {
    if (res?.code === 200) {
      ElMessage.success("删除成功")
    } else {
      ElMessage.error(res?.msg || "删除失败")
    }
  }).catch(() => {
    ElMessage.error("删除失败")
  })
}
const handleDownload = (file) => {
  if (!file?.url) return
  proxy?.$modal?.loading?.("正在下载文件,请稍候...")
  proxy.$download.name(file.url);
  proxy?.$modal?.closeLoading?.()
}
function handlePreview(file) {
  const url = file?.url || file?.response?.data?.tempPath || file?.response?.data?.url
  if (!url) return
  filePreviewRef.value?.open?.(url)
}
function triggerRemoveFile(file) {
  fileUpload.value?.handleRemove?.(file)
}
const handleSubmit = () => {
  formRef.value.validate((valid) => {
    if (valid) {
@@ -804,6 +937,13 @@
      
      // 收集所有节点的审批人id
      form.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
      form.files = fileList.value.map(f => ({
        tempId: f.tempId,
        name: f.name,
        url: f.url,
        uid: f.uid,
      })) || []
      
      // 计算所有产品的单价总和
      form.totalAmount = form.products.reduce((sum, product) => {
@@ -868,6 +1008,7 @@
        // 审批人(用于编辑时反显)
        approveUserIds: item.approveUserIds || '',
        remark: item.remark || '',
        salesLedgerFiles: normalizeQuotationFiles(item),
        products: item.products ? item.products.map(product => ({
          productId: product.productId || '',
          product: product.product || product.productName || '',