Upload.vue 8.99 KB
<template>
  <div class="upload-container">
    <el-card>
      <template #header>
        <div class="card-header">
          <span>上传工单</span>
        </div>
      </template>

      <div class="upload-section">
        <el-upload
          class="upload-demo"
          drag
          :auto-upload="false"
          :on-change="handleFileChange"
          :before-upload="beforeUpload"
          accept=".csv,.xlsx,.xls"
          :show-file-list="false"
        >
          <el-icon class="el-icon--upload"><upload-filled /></el-icon>
          <div class="el-upload__text">
            将文件拖到此处,或<em>点击上传</em>
          </div>
          <template #tip>
            <div class="el-upload__tip">
              请上传CSV或Excel文件,且不超过10MB
            </div>
          </template>
        </el-upload>

        <div style="margin-top: 20px; text-align: center;">
          <el-button type="primary" @click="handleDownloadTemplate">
            下载模板
          </el-button>
        </div>
      </div>

      <div class="filter-section" v-if="originalTableData.length > 0">
        <el-row :gutter="20" style="margin-bottom: 20px;">
          <el-col :span="6">
            <el-input
              v-model="filterForm.accNbr"
              placeholder="业务账号"
            ></el-input>
          </el-col>
          <el-col :span="6">
            <el-input
              v-model="filterForm.campaignIdStr"
              placeholder="工维人员工号"
            ></el-input>
          </el-col>
          <el-col :span="6">
            <el-button type="primary" @click="handleFilter">查询</el-button>
            <el-button @click="resetFilter">重置</el-button>
          </el-col>
          <el-col :span="6" style="text-align: right;">
            <el-button type="success" @click="submitData">确定提交</el-button>
          </el-col>
        </el-row>

        <el-table :data="tableData" border style="width: 100%" :row-class-name="tableRowClassName">
          <!-- <el-table-column label="状态" width="80" align="center">
            <template #default="scope">
              <el-icon v-if="scope.row.reason && scope.row.reason.trim() !== ''" color="#f56c6c" size="20">
                <Warning />
              </el-icon>
              <el-icon v-else color="#67c23a" size="20">
                <SuccessFilled />
              </el-icon>
            </template>
          </el-table-column> -->
          <el-table-column prop="reason" label="错误信息">
            <template #default="scope">
              <span v-if="scope.row.reason && scope.row.reason.trim() !== ''">
                <el-icon color="#f56c6c" style="margin-right: 5px;"><Warning /></el-icon>
                {{ scope.row.reason }}
              </span>
              <span v-else>{{ scope.row.reason }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="campaignId" label="工维人员工号"></el-table-column>
          <el-table-column prop="accNbr" label="业务账号"></el-table-column>
          <el-table-column prop="orderCode" label="工单号"></el-table-column>
          <el-table-column prop="addressName" label="装机地址"></el-table-column>
          <el-table-column prop="serviceName" label="工单类型"></el-table-column>
          <el-table-column prop="isFttrUser" :formatter="formatFttrUser" label="是否FTTR用户"></el-table-column>
          <el-table-column prop="terminalClass" label="设备类型"></el-table-column>
          <el-table-column prop="deviceNumber" label="设备串号"></el-table-column>
        </el-table>
      </div>
    </el-card>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { Warning, SuccessFilled } from '@element-plus/icons-vue'
import { submitWorkOrders, uploadOrderFile } from '../utils/api'

const tableData = ref([])
const originalTableData = ref([]) // 保存原始数据的副本
const filterForm = reactive({
  accNbr: '',
  campaignIdStr: ''
})

const formatFttrUser = (row, column, cellValue) => {
  if(cellValue === -1){
    return ''
  }
  return cellValue === '1' ? '是' : '否'
}

const beforeUpload = (file) => {
  const isLt10M = file.size / 1024 / 1024 < 10
  if (!isLt10M) {
    ElMessage.error('上传文件大小不能超过 10MB!')
  }
  return isLt10M
}

const handleFileChange = async (file) => {
  const isLt10M = file.size / 1024 / 1024 < 10
  if (!isLt10M) {
    ElMessage.error('上传文件大小不能超过 10MB!')
    return
  }

  try {
    // 使用自定义的uploadOrderFile接口上传文件
    const response = await uploadOrderFile(file.raw)

    if (response.code === 200) {
      ElMessage.success('文件上传成功')
      // 使用后端返回的真实数据
      const data = response.data.data || []
      tableData.value = data
      originalTableData.value = [...data] // 保存原始数据副本
    } else {
      ElMessage.error(response.msg || '文件上传失败')
    }
  } catch (error) {
    ElMessage.error('文件上传失败: ' + error.message)
    console.error('Upload error:', error)
  }
}

const handleFilter = () => {
  // 在前端进行筛选(基于已上传的数据)
  if (!filterForm.accNbr && !filterForm.campaignIdStr) {
    ElMessage.warning('请输入至少一个筛选条件')
    return
  }

  // 根据筛选条件过滤数据
  const filteredData = originalTableData.value.filter(item => {
    // 确保字段是字符串类型后再进行匹配
    const accNbrStr = item.accNbr != null ? String(item.accNbr) : ''
    const campaignIdStr = item.campaignId != null ? String(item.campaignId) : ''

    const matchAccNbr = !filterForm.accNbr ||
      (accNbrStr && accNbrStr.includes(filterForm.accNbr))
    const matchCampaignId = !filterForm.campaignIdStr ||
      (campaignIdStr && campaignIdStr.includes(filterForm.campaignIdStr))

    return matchAccNbr && matchCampaignId
  })

  // 更新表格数据
  tableData.value = filteredData

  ElMessage.success(`筛选完成,共找到 ${filteredData.length} 条记录`)
}

const resetFilter = () => {
  // 重置筛选表单
  filterForm.accNbr = ''
  filterForm.campaignIdStr = ''

  // 恢复原始数据
  tableData.value = [...originalTableData.value]

  ElMessage.info('筛选条件已重置')
}

// 表格行样式类名
const tableRowClassName = ({ row, rowIndex }) => {
  // 如果reason有值,则添加错误行样式
  if (row.reason && row.reason.trim() !== '') {
    return 'error-row'
  }
  return ''
}

const submitData = async () => {
  if (!originalTableData.value || originalTableData.value.length === 0) {
    ElMessage.warning('没有数据可提交')
    return
  }

  // 检查是否有错误信息的记录
  const hasErrorRecords = originalTableData.value.some(item => item.reason && item.reason.trim() !== '')

  if (hasErrorRecords) {
    ElMessage.error('信息有误,请修改表格内容后再提交')
    return
  }

  try {
    // 调用后端API提交数据
    const response = await submitWorkOrders({
      orderList: originalTableData.value
    })

    if (response.code === 200) {
      ElMessage.success('数据提交成功')
      // 清空表格数据
      tableData.value = []
      originalTableData.value = []
    } else {
      ElMessage.error(response.msg || '数据提交失败')
    }
  } catch (error) {
    ElMessage.error('数据提交失败: ' + error.message)
    console.error('Submit error:', error)
  }
}

const handleDownloadTemplate = async () => {
  try {
    // 动态导入Excel文件
    const module = await import('/src/assets/order_tem.xlsx')
    const response = await fetch(module.default)
    const blob = await response.blob()

    // 创建下载链接
    const url = window.URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = url
    link.download = '视频质检工单数据模板.xlsx'
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    window.URL.revokeObjectURL(url)

    ElMessage.success('模板下载成功')
  } catch (error) {
    ElMessage.error('模板下载失败: ' + error.message)
  }
}
</script>

<style scoped>
.upload-container {
  padding: 20px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.upload-section {
  margin-bottom: 30px;
}

/* 错误行样式 */
.error-row {
  background-color: #fef0f0 !important;
  border-left: 3px solid #f56c6c !important;
  animation: shake 0.5s ease-in-out;
}

.error-row:hover {
  background-color: #fde2e2 !important;
}

/* 错误行中的所有单元格样式 */
.error-row td {
  color: #f56c6c !important;
  font-weight: 500;
  border-color: #f56c6c !important;
}

/* 错误信息列样式 */
.error-row .cell {
  font-weight: bold;
  color: #f56c6c !important;
}

/* 特别强调错误信息单元格 */
.error-row .el-table_1_column_10 .cell {
  font-weight: bold;
  text-decoration: underline;
}

/* 抖动动画效果 */
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-2px); }
  75% { transform: translateX(2px); }
}
</style>