wlzboy
2026-02-05 57e98ac3f59e9ca12d3fdbc6f89c9c0b1f86be4d
ruoyi-ui/src/views/task/general/detail.vue
@@ -8,7 +8,7 @@
      
      <!-- 基本信息 -->
      <el-descriptions title="基本信息" :column="2" border>
        <el-descriptions-item label="任务编号">{{ taskDetail.taskCode }}</el-descriptions-item>
        <el-descriptions-item label="任务编号">{{ taskDetail.showTaskCode }}</el-descriptions-item>
        <el-descriptions-item label="任务类型">
          <dict-tag :options="dict.type.sys_task_type" :value="taskDetail.taskType"/>
        </el-descriptions-item>
@@ -32,46 +32,315 @@
      <!-- 急救转运任务扩展信息 -->
      <el-descriptions v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && taskDetail.emergencyInfo" title="急救转运信息" :column="2" border style="margin-top: 20px;">
        <el-descriptions-item label="患者姓名">{{ taskDetail.emergencyInfo.patientName }}</el-descriptions-item>
        <el-descriptions-item label="患者性别">
          <dict-tag :options="dict.type.sys_user_sex" :value="taskDetail.emergencyInfo.patientGender"/>
        <el-descriptions-item label="联系人">
          <span v-if="taskDetail.emergencyInfo.patientContact">{{ taskDetail.emergencyInfo.patientContact }}</span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="患者年龄">{{ taskDetail.emergencyInfo.patientAge }}</el-descriptions-item>
        <el-descriptions-item label="联系电话">{{ taskDetail.emergencyInfo.contactPhone }}</el-descriptions-item>
        <el-descriptions-item label="接送医院" :span="2">{{ taskDetail.emergencyInfo.hospitalName }}</el-descriptions-item>
        <el-descriptions-item label="就诊科室" :span="2">
          <dict-tag v-if="taskDetail.emergencyInfo.hospitalDepartment" :options="dict.type.hospital_department" :value="taskDetail.emergencyInfo.hospitalDepartment"/>
        <el-descriptions-item label="联系电话">
          <span v-if="taskDetail.emergencyInfo.patientPhone">{{ taskDetail.emergencyInfo.patientPhone }}</span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="患者姓名">
          <span v-if="taskDetail.emergencyInfo.patientName">{{ taskDetail.emergencyInfo.patientName }}</span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="患者性别">
          <dict-tag v-if="taskDetail.emergencyInfo.patientGender" :options="dict.type.sys_user_sex" :value="taskDetail.emergencyInfo.patientGender"/>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="身份信息" :span="2">
          <span v-if="taskDetail.emergencyInfo.patientIdCard">{{ taskDetail.emergencyInfo.patientIdCard }}</span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="病情描述" :span="2">
          <span v-if="taskDetail.emergencyInfo.illnessDescription">{{ taskDetail.emergencyInfo.illnessDescription }}</span>
          <span v-if="taskDetail.emergencyInfo.patientCondition">{{ taskDetail.emergencyInfo.patientCondition }}</span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="特殊需求" :span="2">
          <span v-if="taskDetail.emergencyInfo.specialRequirements">{{ taskDetail.emergencyInfo.specialRequirements }}</span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="是否需要担架">
          <el-tag v-if="taskDetail.emergencyInfo.needsStretcher == 1" type="success" size="small">是</el-tag>
          <el-tag v-else type="info" size="small">否</el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="是否需要轮椅">
          <el-tag v-if="taskDetail.emergencyInfo.needsWheelchair == 1" type="success" size="small">是</el-tag>
          <el-tag v-else type="info" size="small">否</el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="是否需要氧气">
          <el-tag v-if="taskDetail.emergencyInfo.needsOxygen == 1" type="success" size="small">是</el-tag>
          <el-tag v-else type="info" size="small">否</el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="紧急程度">
          <el-tag v-if="taskDetail.emergencyInfo.urgencyLevel === 'HIGH'" type="danger" size="small">紧急</el-tag>
          <el-tag v-else-if="taskDetail.emergencyInfo.urgencyLevel === 'MEDIUM'" type="warning" size="small">一般</el-tag>
          <el-tag v-else-if="taskDetail.emergencyInfo.urgencyLevel === 'LOW'" type="info" size="small">不急</el-tag>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="陪同人数">{{ taskDetail.emergencyInfo.companionCount || 0 }} 人</el-descriptions-item>
        <el-descriptions-item label="预估费用">{{ taskDetail.emergencyInfo.estimatedCost || '--' }} 元</el-descriptions-item>
      </el-descriptions>
      <!-- 旧系统同步信息(仅急救转运任务显示) -->
      <el-descriptions v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && taskDetail.emergencyInfo" title="旧系统同步信息" :column="2" border style="margin-top: 20px;">
        <el-descriptions-item label="服务单同步状态">
          <el-tag v-if="taskDetail.emergencyInfo.syncStatus === 0" type="info" size="small">
            <i class="el-icon-warning"></i> 未同步
          </el-tag>
          <el-tag v-else-if="taskDetail.emergencyInfo.syncStatus === 1" type="warning" size="small">
            <i class="el-icon-loading"></i> 同步中
          </el-tag>
          <el-tag v-else-if="taskDetail.emergencyInfo.syncStatus === 2" type="success" size="small">
            <i class="el-icon-success"></i> 同步成功
          </el-tag>
          <el-tag v-else-if="taskDetail.emergencyInfo.syncStatus === 3" type="danger" size="small">
            <i class="el-icon-error"></i> 同步失败
          </el-tag>
          <span v-else style="color: #C0C4CC;">--</span>
          <!-- 未同步或同步失败时显示同步按钮 -->
          <el-button
            v-if="taskDetail.emergencyInfo.syncStatus === 0 || taskDetail.emergencyInfo.syncStatus === 3"
            type="primary"
            size="mini"
            icon="el-icon-refresh"
            :loading="syncingServiceOrder"
            @click="syncServiceOrder"
            style="margin-left: 10px;"
          >同步服务单</el-button>
        </el-descriptions-item>
        <el-descriptions-item label="服务单号">
          <span v-if="taskDetail.emergencyInfo.legacyServiceOrdId">
            <el-tag type="primary" size="small">{{ taskDetail.emergencyInfo.legacyServiceOrdId }}</el-tag>
          </span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="服务单编码">
          <span v-if="taskDetail.emergencyInfo.serviceCode">
            <el-tag type="success" size="small">{{ taskDetail.emergencyInfo.serviceCode }}</el-tag>
          </span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="服务单同步时间">
          <span v-if="taskDetail.emergencyInfo.syncTime">{{ parseTime(taskDetail.emergencyInfo.syncTime) }}</span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="服务单同步错误" :span="1">
          <span v-if="taskDetail.emergencyInfo.syncErrorMsg" style="color: #F56C6C;">{{ taskDetail.emergencyInfo.syncErrorMsg }}</span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="调度单同步状态">
          <el-tag v-if="taskDetail.emergencyInfo.dispatchSyncStatus === 0" type="info" size="small">
            <i class="el-icon-warning"></i> 未同步
          </el-tag>
          <el-tag v-else-if="taskDetail.emergencyInfo.dispatchSyncStatus === 1" type="warning" size="small">
            <i class="el-icon-loading"></i> 同步中
          </el-tag>
          <el-tag v-else-if="taskDetail.emergencyInfo.dispatchSyncStatus === 2" type="success" size="small">
            <i class="el-icon-success"></i> 同步成功
          </el-tag>
          <el-tag v-else-if="taskDetail.emergencyInfo.dispatchSyncStatus === 3" type="danger" size="small">
            <i class="el-icon-error"></i> 同步失败
          </el-tag>
          <span v-else style="color: #C0C4CC;">--</span>
          <!-- 未同步或同步失败时显示同步按钮 -->
          <el-button
            v-if="taskDetail.emergencyInfo.dispatchSyncStatus === 0 || taskDetail.emergencyInfo.dispatchSyncStatus === 3"
            type="primary"
            size="mini"
            icon="el-icon-refresh"
            :loading="syncingDispatchOrder"
            @click="syncDispatchOrder"
            style="margin-left: 10px;"
          >同步调度单</el-button>
        </el-descriptions-item>
        <el-descriptions-item label="调度单号">
          <span v-if="taskDetail.emergencyInfo.legacyDispatchOrdId">
            <el-tag type="primary" size="small">{{ taskDetail.emergencyInfo.legacyDispatchOrdId }}</el-tag>
          </span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="调度单编码">
          <span v-if="taskDetail.emergencyInfo.dispatchCode">
            <el-tag type="success" size="small">{{ taskDetail.emergencyInfo.dispatchCode }}</el-tag>
          </span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="调度单同步时间">
          <span v-if="taskDetail.emergencyInfo.dispatchSyncTime">{{ parseTime(taskDetail.emergencyInfo.dispatchSyncTime) }}</span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="调度单同步错误" :span="1">
          <span v-if="taskDetail.emergencyInfo.dispatchSyncErrorMsg" style="color: #F56C6C;">{{ taskDetail.emergencyInfo.dispatchSyncErrorMsg }}</span>
          <span v-else style="color: #C0C4CC;">--</span>
        </el-descriptions-item>
        <el-descriptions-item label="任务状态同步" :span="2">
          <el-alert
            title="提示:任务状态会自动同步到旧系统的调度单中,如果因网络等原因未同步,可点击下方按钮手动同步。"
            type="info"
            :closable="false"
            show-icon
            style="margin-bottom: 10px;">
          </el-alert>
          <el-button
            v-if="taskDetail.emergencyInfo.legacyDispatchOrdId && taskDetail.emergencyInfo.legacyDispatchOrdId > 0"
            type="warning"
            size="small"
            icon="el-icon-refresh"
            :loading="syncingTaskStatus"
            @click="syncTaskStatus"
          >同步任务状态到旧系统</el-button>
          <el-tag v-else type="info" size="small">
            <i class="el-icon-warning"></i> 请先同步调度单
          </el-tag>
        </el-descriptions-item>
      </el-descriptions>
      <!-- 支付信息(仅急救转运任务显示) -->
      <el-card v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && paymentInfo" class="box-card" style="margin-top: 20px;">
        <div slot="header" class="clearfix">
          <span>支付信息</span>
          <!-- 已完成且未申请发票时显示申请发票按钮 -->
          <el-button
            v-if="canApplyInvoice"
            style="float: right; padding: 3px 0"
            type="text"
            @click="handleApplyInvoice"
            v-hasPermi="['system:invoice:add']"
          >
            <i class="el-icon-document-add"></i> 申请发票
          </el-button>
        </div>
        <!-- 支付概览 -->
        <el-descriptions :column="3" border>
          <el-descriptions-item label="基本价格">
            <span style="color: #409EFF; font-weight: bold;">¥{{ paymentInfo.transferPrice || '0.00' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="附加费用总额">
            <span style="color: #E6A23C; font-weight: bold;">¥{{ paymentInfo.additionalAmount || '0.00' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="总金额">
            <span style="color: #67C23A; font-weight: bold;">¥{{ paymentInfo.totalAmount || '0.00' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="已支付金额">
            <span style="color: #67C23A; font-weight: bold;">¥{{ paymentInfo.paidAmount || '0.00' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="剩余未付金额">
            <span style="color: #F56C6C; font-weight: bold;">¥{{ getRemainingAmount() }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="支付状态">
            <el-tag v-if="getRemainingAmount() <= 0" type="success" size="small">
              <i class="el-icon-success"></i> 已结清
            </el-tag>
            <el-tag v-else-if="paymentInfo.paidAmount > 0" type="warning" size="small">
              <i class="el-icon-warning"></i> 部分支付
            </el-tag>
            <el-tag v-else type="info" size="small">
              <i class="el-icon-warning"></i> 未支付
            </el-tag>
          </el-descriptions-item>
        </el-descriptions>
        <!-- 附加费用明细 -->
        <div style="margin-top: 20px;">
          <h4 style="margin-bottom: 10px;">附加费用明细</h4>
          <el-table :data="paymentInfo.additionalFees" border>
            <el-table-column label="费用类型" align="center" prop="feeType" width="120">
              <template slot-scope="scope">
                <dict-tag :options="dict.type.task_additional_fee_type" :value="scope.row.feeType"/>
              </template>
            </el-table-column>
            <el-table-column label="费用名称" align="center" prop="feeName" />
            <el-table-column label="单价" align="center" prop="unitAmount" width="120">
              <template slot-scope="scope">
                <span>¥{{ scope.row.unitAmount }}</span>
              </template>
            </el-table-column>
            <el-table-column label="数量" align="center" prop="quantity" width="80" />
            <el-table-column label="小计" align="center" prop="totalAmount" width="120">
              <template slot-scope="scope">
                <span style="color: #E6A23C; font-weight: bold;">¥{{ scope.row.totalAmount }}</span>
              </template>
            </el-table-column>
            <el-table-column label="同步状态" align="center" width="120">
              <template slot-scope="scope">
                <el-tag v-if="scope.row.syncStatus === 0" type="info" size="small">
                  <i class="el-icon-warning"></i> 未同步
                </el-tag>
                <el-tag v-else-if="scope.row.syncStatus === 1" type="warning" size="small">
                  <i class="el-icon-loading"></i> 同步中
                </el-tag>
                <el-tag v-else-if="scope.row.syncStatus === 2" type="success" size="small">
                  <i class="el-icon-success"></i> 同步成功
                </el-tag>
                <el-tag v-else-if="scope.row.syncStatus === 3" type="danger" size="small">
                  <i class="el-icon-error"></i> 同步失败
                </el-tag>
                <span v-else style="color: #C0C4CC;">--</span>
              </template>
            </el-table-column>
            <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip>
              <template slot-scope="scope">
                <span v-if="scope.row.remark">{{ scope.row.remark }}</span>
                <span v-else style="color: #C0C4CC;">--</span>
              </template>
            </el-table-column>
          </el-table>
          <!-- 空状态提示 -->
          <div v-if="!paymentInfo.additionalFees || paymentInfo.additionalFees.length === 0" style="text-align: center; padding: 40px 0; color: #909399;">
            <i class="el-icon-document" style="font-size: 48px; display: block; margin-bottom: 12px;"></i>
            <span>暂无附加费用</span>
          </div>
        </div>
        <!-- 已支付记录 -->
        <div style="margin-top: 20px;">
          <h4 style="margin-bottom: 10px;">已支付记录</h4>
          <el-table :data="paymentInfo.paidPayments" border>
            <el-table-column label="商户订单号" align="center" prop="outTradeNo" width="200" show-overflow-tooltip />
            <el-table-column label="支付金额" align="center" prop="settlementAmount" width="120">
              <template slot-scope="scope">
                <span style="color: #67C23A; font-weight: bold;">¥{{ scope.row.settlementAmount }}</span>
              </template>
            </el-table-column>
            <el-table-column label="支付方式" align="center" prop="paymentMethod" width="120">
              <template slot-scope="scope">
                <dict-tag :options="dict.type.task_payment_method" :value="scope.row.paymentMethod"/>
              </template>
            </el-table-column>
            <el-table-column label="支付状态" align="center" prop="payStatus" width="120">
              <template slot-scope="scope">
                <el-tag v-if="scope.row.payStatus === 'PAID'" type="success" size="small">
                  <i class="el-icon-success"></i> 已支付
                </el-tag>
                <el-tag v-else-if="scope.row.payStatus === 'PENDING'" type="warning" size="small">
                  <i class="el-icon-time"></i> 待支付
                </el-tag>
                <el-tag v-else-if="scope.row.payStatus === 'FAILED'" type="danger" size="small">
                  <i class="el-icon-error"></i> 失败
                </el-tag>
                <el-tag v-else type="info" size="small">
                  {{ scope.row.payStatus }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column label="交易号" align="center" prop="tradeNo" width="150" show-overflow-tooltip />
            <el-table-column label="支付时间" align="center" prop="payTime" width="180">
              <template slot-scope="scope">
                <span v-if="scope.row.payTime">{{ parseTime(scope.row.payTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
                <span v-else style="color: #C0C4CC;">--</span>
              </template>
            </el-table-column>
            <el-table-column label="同步状态" align="center" width="120">
              <template slot-scope="scope">
                <el-tag v-if="scope.row.syncStatus === 0" type="info" size="small">
                  <i class="el-icon-warning"></i> 未同步
                </el-tag>
                <el-tag v-else-if="scope.row.syncStatus === 1" type="warning" size="small">
                  <i class="el-icon-loading"></i> 同步中
                </el-tag>
                <el-tag v-else-if="scope.row.syncStatus === 2" type="success" size="small">
                  <i class="el-icon-success"></i> 同步成功
                </el-tag>
                <el-tag v-else-if="scope.row.syncStatus === 3" type="danger" size="small">
                  <i class="el-icon-error"></i> 同步失败
                </el-tag>
                <span v-else style="color: #C0C4CC;">--</span>
              </template>
            </el-table-column>
            <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip>
              <template slot-scope="scope">
                <span v-if="scope.row.remark">{{ scope.row.remark }}</span>
                <span v-else style="color: #C0C4CC;">--</span>
              </template>
            </el-table-column>
          </el-table>
          <!-- 空状态提示 -->
          <div v-if="!paymentInfo.paidPayments || paymentInfo.paidPayments.length === 0" style="text-align: center; padding: 40px 0; color: #909399;">
            <i class="el-icon-document" style="font-size: 48px; display: block; margin-bottom: 12px;"></i>
            <span>暂无支付记录</span>
          </div>
        </div>
      </el-card>
      <!-- 福祉车任务扩展信息 -->
      <el-descriptions v-if="taskDetail.taskType === 'WELFARE' && taskDetail.welfareInfo" title="福祉车服务信息" :column="2" border style="margin-top: 20px;">
@@ -99,11 +368,53 @@
      </el-descriptions>
      <!-- 操作按钮 -->
      <div style="margin-top: 20px; text-align: center;">
      <!-- <div style="margin-top: 20px; text-align: center;">
        <el-button type="primary" @click="handleEdit" v-hasPermi="['task:general:edit']">编辑任务</el-button>
        <el-button type="success" @click="handleAssign" v-hasPermi="['task:general:assign']">分配任务</el-button>
        <el-button type="warning" @click="handleStatusChange" v-hasPermi="['task:general:status']">状态变更</el-button>
        <el-button type="info" @click="handleVehicleAssign" v-hasPermi="['task:general:assign']">分配车辆</el-button>
      </div> -->
    </el-card>
    <!-- 执行人员列表 -->
    <el-card class="box-card" style="margin-top: 20px;">
      <div slot="header" class="clearfix">
        <span>执行人员</span>
      </div>
      <el-table :data="taskDetail.assignees" v-loading="assigneeLoading">
        <el-table-column label="序号" align="center" width="60">
          <template slot-scope="scope">
            <span>{{ scope.$index + 1 }}</span>
          </template>
        </el-table-column>
        <el-table-column label="姓名" align="center" prop="userName">
          <template slot-scope="scope">
            <span>{{ scope.row.userName }}</span>
            <el-tag v-if="scope.row.isPrimary === '1'" type="warning" size="mini" style="margin-left: 8px;">
              <i class="el-icon-star-on"></i> 负责人
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="角色类型" align="center" prop="userType" width="120">
          <template slot-scope="scope">
            <el-tag v-if="scope.row.userType === 'driver'" type="primary" size="small">司机</el-tag>
            <el-tag v-else-if="scope.row.userType === 'doctor'" type="success" size="small">医生</el-tag>
            <el-tag v-else-if="scope.row.userType === 'nurse'" type="info" size="small">护士</el-tag>
            <el-tag v-else type="" size="small">{{ scope.row.userType }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="创建时间" align="center" prop="createTime" width="180">
          <template slot-scope="scope">
            <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
          </template>
        </el-table-column>
      </el-table>
      <!-- 空状态提示 -->
      <div v-if="!taskDetail.assignees || taskDetail.assignees.length === 0" style="text-align: center; padding: 40px 0; color: #909399;">
        <i class="el-icon-user" style="font-size: 48px; display: block; margin-bottom: 12px;"></i>
        <span>暂无执行人员</span>
      </div>
    </el-card>
@@ -156,11 +467,38 @@
      </div>
      
      <el-table :data="taskDetail.attachments" v-loading="attachmentLoading">
        <el-table-column label="文件名" align="center" prop="fileName" />
        <el-table-column label="文件类型" align="center" prop="fileType" />
        <el-table-column label="文件大小" align="center" prop="fileSize">
        <el-table-column label="缩略图" align="center" width="120">
          <template slot-scope="scope">
            <span>{{ formatFileSize(scope.row.fileSize) }}</span>
            <el-image
              v-if="isImage(scope.row.fileType)"
              :src="scope.row.fileUrl"
              :preview-src-list="[scope.row.fileUrl]"
              fit="cover"
              style="width: 80px; height: 80px; border-radius: 4px; cursor: pointer;"
            >
              <div slot="error" class="image-slot">
                <i class="el-icon-picture-outline" style="font-size: 40px; color: #C0C4CC;"></i>
              </div>
            </el-image>
            <div v-else style="text-align: center;">
              <i class="el-icon-document" style="font-size: 40px; color: #909399;"></i>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="业务分类" align="center" prop="attachmentCategory" width="150">
          <template slot-scope="scope">
            <dict-tag :options="dict.type.sys_attachment_category" :value="scope.row.attachmentCategory"/>
          </template>
        </el-table-column>
        <el-table-column label="同步状态" align="center" width="120">
          <template slot-scope="scope">
            <el-tag v-if="scope.row.syncedToImageData === 0" type="info" size="small">
              <i class="el-icon-warning"></i> 未同步
            </el-tag>
            <el-tag v-else-if="scope.row.syncedToImageData === 1" type="success" size="small">
              <i class="el-icon-success"></i> 已同步
            </el-tag>
            <span v-else style="color: #C0C4CC;">--</span>
          </template>
        </el-table-column>
        <el-table-column label="上传时间" align="center" prop="uploadTime" width="180">
@@ -187,6 +525,74 @@
          </template>
        </el-table-column>
      </el-table>
    </el-card>
    <!-- 附加费用列表 -->
    <el-card class="box-card" style="margin-top: 20px;">
      <div slot="header" class="clearfix">
        <span>附加费用列表</span>
      </div>
      <el-table :data="additionalFeeList" v-loading="feeLoading">
        <el-table-column label="费用类型" align="center" prop="feeType" width="120">
          <template slot-scope="scope">
            <dict-tag :options="dict.type.task_additional_fee_type" :value="scope.row.feeType"/>
          </template>
        </el-table-column>
        <el-table-column label="费用名称" align="center" prop="feeName" />
        <el-table-column label="单价" align="center" prop="unitAmount" width="120">
          <template slot-scope="scope">
            <span>¥{{ scope.row.unitAmount }}</span>
          </template>
        </el-table-column>
        <el-table-column label="数量" align="center" prop="quantity" width="80" />
        <el-table-column label="总金额" align="center" prop="totalAmount" width="120">
          <template slot-scope="scope">
            <span style="color: #E6A23C; font-weight: bold;">¥{{ scope.row.totalAmount }}</span>
          </template>
        </el-table-column>
        <el-table-column label="同步状态" align="center" width="120">
          <template slot-scope="scope">
            <el-tag v-if="scope.row.syncStatus === 0" type="info" size="small">
              <i class="el-icon-warning"></i> 未同步
            </el-tag>
            <el-tag v-else-if="scope.row.syncStatus === 1" type="warning" size="small">
              <i class="el-icon-loading"></i> 同步中
            </el-tag>
            <el-tag v-else-if="scope.row.syncStatus === 2" type="success" size="small">
              <i class="el-icon-success"></i> 同步成功
            </el-tag>
            <el-tag v-else-if="scope.row.syncStatus === 3" type="danger" size="small">
              <i class="el-icon-error"></i> 同步失败
            </el-tag>
            <span v-else style="color: #C0C4CC;">--</span>
          </template>
        </el-table-column>
        <el-table-column label="同步时间" align="center" prop="syncTime" width="180">
          <template slot-scope="scope">
            <span v-if="scope.row.syncTime">{{ parseTime(scope.row.syncTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
            <span v-else style="color: #C0C4CC;">--</span>
          </template>
        </el-table-column>
        <el-table-column label="创建时间" align="center" prop="createdTime" width="180">
          <template slot-scope="scope">
            <span>{{ parseTime(scope.row.createdTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
          </template>
        </el-table-column>
        <el-table-column label="创建者" align="center" prop="createdBy" width="100" />
        <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip>
          <template slot-scope="scope">
            <span v-if="scope.row.remark">{{ scope.row.remark }}</span>
            <span v-else style="color: #C0C4CC;">--</span>
          </template>
        </el-table-column>
      </el-table>
      <!-- 空状态提示 -->
      <div v-if="!additionalFeeList || additionalFeeList.length === 0" style="text-align: center; padding: 40px 0; color: #909399;">
        <i class="el-icon-document" style="font-size: 48px; display: block; margin-bottom: 12px;"></i>
        <span>暂无附加费用</span>
      </div>
    </el-card>
    <!-- 操作日志 -->
@@ -341,33 +747,54 @@
    </el-dialog>
    <!-- 上传附件对话框 -->
    <el-dialog title="上传附件" :visible.sync="uploadOpen" width="500px" append-to-body>
      <el-upload
        class="upload-demo"
        drag
        :action="uploadUrl"
        :headers="uploadHeaders"
        :data="uploadData"
        :on-success="handleUploadSuccess"
        :on-error="handleUploadError"
        :before-upload="beforeUpload"
        multiple>
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        <div class="el-upload__tip" slot="tip">只能上传jpg/png/pdf/doc/docx文件,且不超过10MB</div>
      </el-upload>
    <el-dialog title="上传附件" :visible.sync="uploadOpen" width="500px" append-to-body @close="cancelUpload">
      <el-form ref="uploadForm" :model="uploadForm" :rules="uploadRules" label-width="100px">
        <el-form-item label="业务分类" prop="category">
          <el-select v-model="uploadForm.category" placeholder="请选择业务分类" clearable style="width: 100%;">
            <el-option
              v-for="dict in dict.type.sys_attachment_category"
              :key="dict.value"
              :label="dict.label"
              :value="dict.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="附件" prop="files">
          <el-upload
            ref="upload"
            class="upload-demo"
            :action="uploadUrl"
            :headers="uploadHeaders"
            :data="uploadData"
            :on-success="handleUploadSuccess"
            :on-error="handleUploadError"
            :before-upload="beforeUpload"
            :file-list="fileList"
            :auto-upload="false"
            multiple
            drag>
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
            <div class="el-upload__tip" slot="tip">只能上传jpg/png/pdf/doc/docx文件,且不超过100MB</div>
          </el-upload>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitUpload">确 定</el-button>
        <el-button @click="cancelUpload">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import { getTask, updateTask, assignTask, changeTaskStatus, uploadAttachment, deleteAttachment, getTaskVehicles, getAvailableVehicles, assignVehiclesToTask, unassignVehicleFromTask } from "@/api/task";
import { getTask, updateTask, assignTask, changeTaskStatus, uploadAttachment, deleteAttachment, getTaskVehicles, getAvailableVehicles, assignVehiclesToTask, unassignVehicleFromTask, getPaymentInfo, syncServiceOrder, syncDispatchOrder, syncTaskStatus } from "@/api/task";
import { listUser } from "@/api/system/user";
import { getToken } from "@/utils/auth";
export default {
  name: "TaskDetail",
  dicts: ['sys_task_type', 'sys_task_status', 'sys_vehicle_type', 'sys_task_vehicle_status', 'sys_user_sex', 'hospital_department'],
  dicts: ['sys_task_type', 'sys_task_status', 'sys_vehicle_type', 'sys_task_vehicle_status', 'sys_user_sex', 'hospital_department', 'sys_attachment_category', 'task_additional_fee_type', 'task_payment_method'],
  data() {
    return {
      // 任务详情
@@ -386,6 +813,12 @@
      vehicleAssignOpen: false,
      // 是否显示上传对话框
      uploadOpen: false,
      // 上传表单
      uploadForm: {
        category: null
      },
      // 文件列表
      fileList: [],
      // 编辑表单
      editForm: {},
      // 分配表单
@@ -401,8 +834,14 @@
      // 加载状态
      vehicleLoading: false,
      attachmentLoading: false,
      feeLoading: false,
      assigneeLoading: false,
      // 附加费用列表
      additionalFeeList: [],
      // 支付信息
      paymentInfo: null,
      // 上传相关
      uploadUrl: process.env.VUE_APP_BASE_API + "/task/attachment/upload/" + this.$route.params.taskId,
      uploadUrl: process.env.VUE_APP_BASE_API + "/task/attachment/upload/" + (new URLSearchParams(window.location.search).get('taskId') || ''),
      uploadHeaders: {
        Authorization: "Bearer " + getToken()
      },
@@ -433,19 +872,80 @@
        vehicleIds: [
          { required: true, message: "车辆不能为空", trigger: "change" }
        ]
      }
      },
      uploadRules: {
        category: [
          { required: true, message: "业务分类不能为空", trigger: "change" }
        ]
      },
      // 同步加载状态
      syncingServiceOrder: false,
      syncingDispatchOrder: false,
      syncingTaskStatus: false,
      // 发票申请状态
      hasInvoiceApplied: false,
      invoiceStatus: null // 0-待审核, 1-已通过, 2-已驳回
    };
  },
  created() {
    this.getTaskDetail();
    this.getUserList();
    this.getAdditionalFeeList();
    // 初始化上传URL
    this.uploadUrl = process.env.VUE_APP_BASE_API + "/task/attachment/upload/" + this.$route.params.taskId;
    // 检查发票申请状态
    this.checkInvoiceStatus();
  },
  computed: {
    /** 是否可以申请发票 */
    canApplyInvoice() {
      // 只有急救转运任务
      if (this.taskDetail.taskType !== 'EMERGENCY_TRANSFER') return false;
      // 任务必须已完成
      if (this.taskDetail.taskStatus !== 'COMPLETED') return false;
      // 未申请过发票,或者曾被驳回
      return !this.hasInvoiceApplied || this.invoiceStatus === 2;
    }
  },
  methods: {
    /** 获取任务详情 */
    getTaskDetail() {
      getTask(this.$route.params.taskId).then(response => {
        this.taskDetail = response.data;
        // 任务详情加载完成后,加载支付信息
        // console.log("TaskDetail", this.taskDetail);
        this.loadPaymentInfo();
      });
    },
    /** 获取附加费用列表 */
    getAdditionalFeeList() {
      this.feeLoading = true;
      getPaymentInfo(this.$route.params.taskId).then(response => {
        this.additionalFeeList = response.data.additionalFees || [];
        this.feeLoading = false;
      }).catch(() => {
        this.feeLoading = false;
      });
    },
    /** 加载支付信息 */
    loadPaymentInfo() {
      //EMERGENCY_TRANSFER
      if (this.taskDetail.taskType === 'EMERGENCY_TRANSFER') {
        getPaymentInfo(this.$route.params.taskId).then(response => {
          // console.log("PaymentInfo", response.data);
          this.paymentInfo = response.data;
        }).catch(() => {
          this.paymentInfo = null;
        });
      }
    },
    /** 计算剩余未付金额 */
    getRemainingAmount() {
      if (!this.paymentInfo) return '0.00';
      const total = parseFloat(this.paymentInfo.totalAmount || 0);
      const paid = parseFloat(this.paymentInfo.paidAmount || 0);
      const remaining = total - paid;
      return remaining > 0 ? remaining.toFixed(2) : '0.00';
    },
    /** 获取用户列表 */
    getUserList() {
@@ -507,6 +1007,10 @@
    },
    /** 上传附件 */
    handleUpload() {
      this.uploadForm = {
        category: null
      };
      this.fileList = [];
      this.uploadOpen = true;
    },
    /** 取消车辆分配 */
@@ -601,22 +1105,67 @@
    },
    /** 上传前检查 */
    beforeUpload(file) {
      // 检查是否选择了业务分类
      if (!this.uploadForm.category) {
        this.$message.error('请先选择业务分类!');
        return false;
      }
      // 更新uploadData,确保每次上传都带有category参数
      this.uploadData = {
        category: this.uploadForm.category
      };
      const isValidType = ['image/jpeg', 'image/png', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'].includes(file.type);
      const isLt10M = file.size / 1024 / 1024 < 10;
      const isLt10M = file.size / 1024 / 1024 < 100;
      if (!isValidType) {
        this.$message.error('只能上传 JPG/PNG/PDF/DOC/DOCX 格式的文件!');
        return false;
      }
      if (!isLt10M) {
        this.$message.error('上传文件大小不能超过 10MB!');
        this.$message.error('上传文件大小不能超过 100MB!');
        return false;
      }
      return isValidType && isLt10M;
      return true;
    },
    /** 提交上传 */
    submitUpload() {
      this.$refs["uploadForm"].validate(valid => {
        if (valid) {
          // 检查是否选择了文件
          const fileList = this.$refs.upload.uploadFiles;
          if (!fileList || fileList.length === 0) {
            this.$message.warning('请选择要上传的文件');
            return;
          }
          // 触发上传
          this.$refs.upload.submit();
        }
      });
    },
    /** 取消上传 */
    cancelUpload() {
      this.uploadOpen = false;
      this.uploadForm = {
        category: null
      };
      this.fileList = [];
      if (this.$refs.upload) {
        this.$refs.upload.clearFiles();
      }
    },
    /** 上传成功 */
    handleUploadSuccess(response, file, fileList) {
      this.$modal.msgSuccess("上传成功");
      this.uploadOpen = false;
      this.getTaskDetail();
      // 检查是否所有文件都上传完成
      const allDone = fileList.every(f => f.status === 'success' || f.status === 'fail');
      if (allDone) {
        this.$modal.msgSuccess("上传成功");
        this.cancelUpload();
        this.getTaskDetail();
      }
    },
    /** 上传失败 */
    handleUploadError(err, file, fileList) {
@@ -640,6 +1189,96 @@
        return typeItem ? typeItem.label : vehicleType;
      }
      return vehicleType;
    },
    /** 判断是否为图片类型 */
    isImage(fileType) {
      if (!fileType) return false;
      const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
      return imageTypes.includes(fileType.toLowerCase());
    },
    /** 手动同步服务单 */
    syncServiceOrder() {
      this.$modal.confirm('是否确认同步服务单到旧系统?').then(() => {
        this.syncingServiceOrder = true;
        return syncServiceOrder(this.taskDetail.taskId);
      }).then(() => {
        this.$modal.msgSuccess("服务单同步成功");
        // 重新加载任务详情
        this.getTaskDetail();
      }).catch(() => {
        // 处理取消和错误
      }).finally(() => {
        this.syncingServiceOrder = false;
      });
    },
    /** 手动同步调度单 */
    syncDispatchOrder() {
      this.$modal.confirm('是否确认同步调度单到旧系统?').then(() => {
        this.syncingDispatchOrder = true;
        return syncDispatchOrder(this.taskDetail.taskId);
      }).then(() => {
        this.$modal.msgSuccess("调度单同步成功");
        // 重新加载任务详情
        this.getTaskDetail();
      }).catch(() => {
        // 处理取消和错误
      }).finally(() => {
        this.syncingDispatchOrder = false;
      });
    },
    /** 手动同步任务状态 */
    syncTaskStatus() {
      this.$modal.confirm('是否确认同步任务状态到旧系统?').then(() => {
        this.syncingTaskStatus = true;
        return syncTaskStatus(this.taskDetail.taskId);
      }).then(() => {
        this.$modal.msgSuccess("任务状态同步成功");
        // 重新加载任务详情
        this.getTaskDetail();
      }).catch(() => {
        // 处理取消和错误
      }).finally(() => {
        this.syncingTaskStatus = false;
      });
    },
    /** 检查发票申请状态 */
    checkInvoiceStatus() {
      // 调用后端接口检查该任务是否已申请发票
      this.$axios.get(`/system/invoice/checkTaskInvoice/${this.$route.params.taskId}`)
        .then(response => {
          if (response.code === 200 && response.data) {
            this.hasInvoiceApplied = true;
            this.invoiceStatus = response.data.status;
          }
        })
        .catch(() => {
          // 忽略错误,默认未申请
        });
    },
    /** 申请发票 */
    handleApplyInvoice() {
      // 跳转到发票申请页面,带上任务信息
      const taskInfo = {
        taskId: this.taskDetail.taskId,
        taskCode: this.taskDetail.taskCode || this.taskDetail.showTaskCode,
        legacyServiceOrderId: this.taskDetail.emergencyInfo?.legacyServiceOrdId,
        serviceCode: this.taskDetail.emergencyInfo?.serviceCode,
        departure: this.taskDetail.departureAddress,
        destination: this.taskDetail.destinationAddress,
        completionTime: this.parseTime(this.taskDetail.actualEndTime),
        transferPrice: this.paymentInfo?.transferPrice || this.paymentInfo?.totalAmount
      };
      // 将任务信息存储到 sessionStorage
      sessionStorage.setItem('invoiceTaskInfo', JSON.stringify(taskInfo));
      // 跳转到发票申请页面
      this.$router.push({
        path: '/system/invoice/apply',
        query: { taskId: this.taskDetail.taskId }
      });
    }
  }
};