feat: 任务大厅增加一键导出功能,仅部门负责人可用
This commit is contained in:
parent
60fcfbb9ce
commit
b0898656d8
Binary file not shown.
@ -1,10 +1,19 @@
|
||||
package com.designhub.controller;
|
||||
|
||||
import com.designhub.common.ResultCode;
|
||||
import com.designhub.entity.User;
|
||||
import com.designhub.exception.BusinessException;
|
||||
import com.designhub.service.DepartmentService;
|
||||
import com.designhub.service.ExportService;
|
||||
import com.designhub.service.UserService;
|
||||
import com.designhub.util.JwtUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -16,9 +25,51 @@ import java.io.IOException;
|
||||
@RestController
|
||||
@RequestMapping("/api/export")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ExportController {
|
||||
|
||||
private final ExportService exportService;
|
||||
private final UserService userService;
|
||||
private final DepartmentService departmentService;
|
||||
private final JwtUtil jwtUtil;
|
||||
|
||||
@Operation(summary = "导出任务大厅任务明细", description = "只有部门负责人或管理员可以导出")
|
||||
@GetMapping("/task-hall")
|
||||
public void exportTaskHall(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
@Parameter(description = "任务状态") @RequestParam(required = false) String status,
|
||||
@Parameter(description = "发布部门ID") @RequestParam(required = false) Long publisherDeptId,
|
||||
@Parameter(description = "接收部门ID") @RequestParam(required = false) Long receiverDeptId,
|
||||
@Parameter(description = "品牌ID") @RequestParam(required = false) Long brandId,
|
||||
@Parameter(description = "类目ID") @RequestParam(required = false) Long categoryId,
|
||||
@Parameter(description = "平台ID") @RequestParam(required = false) Long platformId
|
||||
) throws IOException {
|
||||
// 获取当前用户
|
||||
String token = request.getHeader("Authorization");
|
||||
if (token != null && token.startsWith("Bearer ")) {
|
||||
token = token.substring(7);
|
||||
}
|
||||
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||
User user = userService.getUserById(userId);
|
||||
|
||||
if (user == null) {
|
||||
throw new BusinessException(ResultCode.UNAUTHORIZED, "用户未登录");
|
||||
}
|
||||
|
||||
// 检查权限:只有部门负责人或管理员可以导出
|
||||
boolean isAdmin = user.getIsAdmin() != null && user.getIsAdmin() == 1;
|
||||
boolean isDeptLeader = departmentService.isUserDepartmentLeader(userId);
|
||||
|
||||
if (!isAdmin && !isDeptLeader) {
|
||||
throw new BusinessException(ResultCode.FORBIDDEN, "只有部门负责人或管理员可以导出任务明细");
|
||||
}
|
||||
|
||||
log.info("用户 {} ({}) 导出任务大厅数据, isAdmin={}, isDeptLeader={}",
|
||||
user.getRealName(), userId, isAdmin, isDeptLeader);
|
||||
|
||||
exportService.exportTaskHall(response, status, publisherDeptId, receiverDeptId, brandId, categoryId, platformId);
|
||||
}
|
||||
|
||||
@Operation(summary = "导出任务列表")
|
||||
@GetMapping("/tasks")
|
||||
|
||||
@ -813,13 +813,16 @@ public class AnalyticsService {
|
||||
.isNotNull(TaskItem::getCreateTime)
|
||||
);
|
||||
|
||||
// 处理已完成的子任务的延误扣分
|
||||
// 处理已完成的子任务的延误扣分(与getDesignerDeductionDetails保持一致,使用deadline或claimTime)
|
||||
for (TaskItem item : completedTaskItems) {
|
||||
if (item.getEstimatedHours() != null && item.getEstimatedHours().compareTo(BigDecimal.ZERO) > 0
|
||||
&& item.getCreateTime() != null && item.getCompleteTime() != null) {
|
||||
if (item.getCompleteTime() != null) {
|
||||
|
||||
// 计算子任务的预期deadline:createTime + estimatedHours(使用工作时间计算)
|
||||
LocalDateTime expectedDeadline = workHoursCalculator.calculateDeadline(item.getCreateTime(), item.getEstimatedHours());
|
||||
// 优先使用数据库中已存储的deadline,如果没有则根据claimTime计算
|
||||
LocalDateTime expectedDeadline = item.getDeadline();
|
||||
if (expectedDeadline == null && item.getClaimTime() != null && item.getEstimatedHours() != null
|
||||
&& item.getEstimatedHours().compareTo(BigDecimal.ZERO) > 0) {
|
||||
expectedDeadline = workHoursCalculator.calculateDeadline(item.getClaimTime(), item.getEstimatedHours());
|
||||
}
|
||||
|
||||
// 如果完成时间超过预期deadline,计算延误扣分
|
||||
if (expectedDeadline != null && item.getCompleteTime().isAfter(expectedDeadline)) {
|
||||
@ -848,36 +851,36 @@ public class AnalyticsService {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进行中的子任务的延误扣分
|
||||
// 处理进行中的子任务的延误扣分(与getDesignerDeductionDetails保持一致,使用deadline或claimTime)
|
||||
for (TaskItem item : inProgressTaskItems) {
|
||||
if (item.getEstimatedHours() != null && item.getEstimatedHours().compareTo(BigDecimal.ZERO) > 0
|
||||
&& item.getCreateTime() != null) {
|
||||
// 优先使用数据库中已存储的deadline,如果没有则根据claimTime计算
|
||||
LocalDateTime expectedDeadline = item.getDeadline();
|
||||
if (expectedDeadline == null && item.getClaimTime() != null && item.getEstimatedHours() != null
|
||||
&& item.getEstimatedHours().compareTo(BigDecimal.ZERO) > 0) {
|
||||
expectedDeadline = workHoursCalculator.calculateDeadline(item.getClaimTime(), item.getEstimatedHours());
|
||||
}
|
||||
|
||||
// 如果当前时间超过预期deadline,计算延误扣分
|
||||
if (expectedDeadline != null && now.isAfter(expectedDeadline)) {
|
||||
BigDecimal itemScore = item.getTotalScore() != null ? item.getTotalScore() : BigDecimal.ZERO;
|
||||
|
||||
// 计算子任务的预期deadline:createTime + estimatedHours(使用工作时间计算)
|
||||
LocalDateTime expectedDeadline = workHoursCalculator.calculateDeadline(item.getCreateTime(), item.getEstimatedHours());
|
||||
|
||||
// 如果当前时间超过预期deadline,计算延误扣分
|
||||
if (expectedDeadline != null && now.isAfter(expectedDeadline)) {
|
||||
BigDecimal itemScore = item.getTotalScore() != null ? item.getTotalScore() : BigDecimal.ZERO;
|
||||
if (itemScore.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 计算延误小时数(只计算工作时间段内的延误)
|
||||
BigDecimal overdueHours = workHoursCalculator.calculateDelayHours(expectedDeadline, now);
|
||||
|
||||
if (itemScore.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 计算延误小时数(只计算工作时间段内的延误)
|
||||
BigDecimal overdueHours = workHoursCalculator.calculateDelayHours(expectedDeadline, now);
|
||||
if (overdueHours.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 计算扣分
|
||||
BigDecimal itemDeduction = itemScore
|
||||
.multiply(overdueHours)
|
||||
.multiply(deductionRate)
|
||||
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
|
||||
|
||||
if (overdueHours.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 计算扣分
|
||||
BigDecimal itemDeduction = itemScore
|
||||
.multiply(overdueHours)
|
||||
.multiply(deductionRate)
|
||||
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
|
||||
|
||||
// 扣分不能超过任务积分
|
||||
if (itemDeduction.compareTo(itemScore) > 0) {
|
||||
itemDeduction = itemScore;
|
||||
}
|
||||
|
||||
deductionScore = deductionScore.add(itemDeduction);
|
||||
// 扣分不能超过任务积分
|
||||
if (itemDeduction.compareTo(itemScore) > 0) {
|
||||
itemDeduction = itemScore;
|
||||
}
|
||||
|
||||
deductionScore = deductionScore.add(itemDeduction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,28 +6,268 @@ import com.designhub.mapper.*;
|
||||
import com.designhub.util.ExcelExportUtil;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 数据导出Service
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ExportService {
|
||||
|
||||
private final TaskMapper taskMapper;
|
||||
private final UserMapper userMapper;
|
||||
private final DepartmentMapper departmentMapper;
|
||||
private final DesignerService designerService;
|
||||
private final DemandTypeMapper demandTypeMapper;
|
||||
private final BrandMapper brandMapper;
|
||||
private final CategoryMapper categoryMapper;
|
||||
private final PlatformMapper platformMapper;
|
||||
private final org.springframework.jdbc.core.JdbcTemplate jdbcTemplate;
|
||||
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 导出任务大厅数据
|
||||
*/
|
||||
public void exportTaskHall(HttpServletResponse response, String status, Long publisherDeptId,
|
||||
Long receiverDeptId, Long brandId, Long categoryId, Long platformId) throws IOException {
|
||||
log.info("开始导出任务大厅数据: status={}, publisherDeptId={}, receiverDeptId={}, brandId={}, categoryId={}, platformId={}",
|
||||
status, publisherDeptId, receiverDeptId, brandId, categoryId, platformId);
|
||||
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<Task> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Task::getDeleted, 0);
|
||||
queryWrapper.eq(Task::getApprovalStatus, "approved"); // 只导出已审批的任务
|
||||
|
||||
if (status != null && !status.isEmpty()) {
|
||||
queryWrapper.eq(Task::getStatus, status);
|
||||
} else {
|
||||
// 默认导出待接单任务
|
||||
queryWrapper.eq(Task::getStatus, "pending");
|
||||
}
|
||||
|
||||
if (publisherDeptId != null) {
|
||||
queryWrapper.eq(Task::getPublisherDeptId, publisherDeptId);
|
||||
}
|
||||
if (receiverDeptId != null) {
|
||||
queryWrapper.eq(Task::getReceiverDeptId, receiverDeptId);
|
||||
}
|
||||
if (brandId != null) {
|
||||
queryWrapper.eq(Task::getBrandId, brandId);
|
||||
}
|
||||
if (categoryId != null) {
|
||||
queryWrapper.eq(Task::getCategoryId, categoryId);
|
||||
}
|
||||
if (platformId != null) {
|
||||
queryWrapper.eq(Task::getPlatformId, platformId);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc(Task::getCreateTime);
|
||||
|
||||
List<Task> tasks = taskMapper.selectList(queryWrapper);
|
||||
log.info("查询到 {} 条任务", tasks.size());
|
||||
|
||||
// 预加载关联数据
|
||||
Map<Long, String> taskTypeMap = loadTaskTypeMap();
|
||||
Map<Long, String> demandTypeMap = loadDemandTypeMap();
|
||||
Map<Long, String> brandMap = loadBrandMap();
|
||||
Map<Long, String> categoryMap = loadCategoryMap();
|
||||
Map<Long, String> platformMap = loadPlatformMap();
|
||||
Map<Long, String> userMap = loadUserMap();
|
||||
Map<Long, String> deptMap = loadDeptMap();
|
||||
|
||||
// 转换为导出数据
|
||||
List<TaskHallExportVO> exportList = new ArrayList<>();
|
||||
for (Task task : tasks) {
|
||||
TaskHallExportVO vo = new TaskHallExportVO();
|
||||
vo.setTaskNo(task.getTaskNo());
|
||||
vo.setTitle(task.getTitle());
|
||||
vo.setTaskType(taskTypeMap.getOrDefault(task.getTaskTypeId(), ""));
|
||||
vo.setDemandType(demandTypeMap.getOrDefault(task.getDemandTypeId(), ""));
|
||||
vo.setBrand(brandMap.getOrDefault(task.getBrandId(), ""));
|
||||
vo.setCategory(categoryMap.getOrDefault(task.getCategoryId(), ""));
|
||||
vo.setPlatform(platformMap.getOrDefault(task.getPlatformId(), ""));
|
||||
vo.setSpuInfo(task.getSpuInfo());
|
||||
vo.setScore(task.getScore() != null ? task.getScore().toString() : "0");
|
||||
vo.setEstimatedHours(task.getEstimatedHours() != null ? task.getEstimatedHours().toString() : "0");
|
||||
vo.setQuantity(task.getQuantity() != null ? task.getQuantity() : 1);
|
||||
vo.setPublisher(userMap.getOrDefault(task.getPublisherId(), ""));
|
||||
vo.setPublisherDept(deptMap.getOrDefault(task.getPublisherDeptId(), ""));
|
||||
vo.setReceiver(userMap.getOrDefault(task.getReceiverId(), ""));
|
||||
vo.setReceiverDept(deptMap.getOrDefault(task.getReceiverDeptId(), ""));
|
||||
vo.setStatus(getStatusText(task.getStatus()));
|
||||
vo.setReleaseType(getReleaseTypeText(task.getReleaseType()));
|
||||
vo.setIsUrgent(task.getIsUrgent() != null && task.getIsUrgent() == 1 ? "是" : "否");
|
||||
vo.setIsPipeline(task.getIsPipeline() != null && task.getIsPipeline() == 1 ? "是" : "否");
|
||||
vo.setCreateTime(task.getCreateTime() != null ? task.getCreateTime().format(DATE_FORMATTER) : "");
|
||||
vo.setDeadline(task.getDeadline() != null ? task.getDeadline().format(DATE_FORMATTER) : "");
|
||||
exportList.add(vo);
|
||||
}
|
||||
|
||||
// 定义表头
|
||||
String[] headers = {
|
||||
"任务编号", "任务标题", "任务类型", "需求类型", "品牌", "类目", "平台",
|
||||
"SPU信息", "任务积分", "预估工时(小时)", "数量", "发布人", "发布部门",
|
||||
"接收人", "接收部门", "状态", "发布类型", "是否紧急", "是否流水线",
|
||||
"创建时间", "截止时间"
|
||||
};
|
||||
|
||||
// 定义字段名
|
||||
String[] fieldNames = {
|
||||
"taskNo", "title", "taskType", "demandType", "brand", "category", "platform",
|
||||
"spuInfo", "score", "estimatedHours", "quantity", "publisher", "publisherDept",
|
||||
"receiver", "receiverDept", "status", "releaseType", "isUrgent", "isPipeline",
|
||||
"createTime", "deadline"
|
||||
};
|
||||
|
||||
// 生成文件名
|
||||
String fileName = "任务大厅数据_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
|
||||
// 导出Excel
|
||||
ExcelExportUtil.exportExcel(response, fileName, "任务明细", headers, fieldNames, exportList);
|
||||
log.info("任务大厅数据导出完成,共 {} 条", exportList.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态文本
|
||||
*/
|
||||
private String getStatusText(String status) {
|
||||
if (status == null) return "";
|
||||
return switch (status) {
|
||||
case "pending" -> "待接单";
|
||||
case "in_progress" -> "进行中";
|
||||
case "completed" -> "已完成";
|
||||
case "rejected" -> "已驳回";
|
||||
case "cancelled" -> "已取消";
|
||||
default -> status;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取发布类型文本
|
||||
*/
|
||||
private String getReleaseTypeText(String releaseType) {
|
||||
if (releaseType == null) return "";
|
||||
return switch (releaseType) {
|
||||
case "recruitment" -> "招募";
|
||||
case "assigned" -> "指派";
|
||||
case "default" -> "默认";
|
||||
default -> releaseType;
|
||||
};
|
||||
}
|
||||
|
||||
// 加载关联数据的辅助方法
|
||||
private Map<Long, String> loadTaskTypeMap() {
|
||||
Map<Long, String> map = new HashMap<>();
|
||||
try {
|
||||
jdbcTemplate.query("SELECT id, type_name FROM task_type", rs -> {
|
||||
map.put(rs.getLong("id"), rs.getString("type_name"));
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.warn("加载任务类型失败: {}", e.getMessage());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<Long, String> loadDemandTypeMap() {
|
||||
Map<Long, String> map = new HashMap<>();
|
||||
try {
|
||||
demandTypeMapper.selectList(null).forEach(t -> map.put(t.getId(), t.getName()));
|
||||
} catch (Exception e) {
|
||||
log.warn("加载需求类型失败: {}", e.getMessage());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<Long, String> loadBrandMap() {
|
||||
Map<Long, String> map = new HashMap<>();
|
||||
try {
|
||||
brandMapper.selectList(null).forEach(t -> map.put(t.getId(), t.getName()));
|
||||
} catch (Exception e) {
|
||||
log.warn("加载品牌失败: {}", e.getMessage());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<Long, String> loadCategoryMap() {
|
||||
Map<Long, String> map = new HashMap<>();
|
||||
try {
|
||||
categoryMapper.selectList(null).forEach(t -> map.put(t.getId(), t.getName()));
|
||||
} catch (Exception e) {
|
||||
log.warn("加载类目失败: {}", e.getMessage());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<Long, String> loadPlatformMap() {
|
||||
Map<Long, String> map = new HashMap<>();
|
||||
try {
|
||||
platformMapper.selectList(null).forEach(t -> map.put(t.getId(), t.getName()));
|
||||
} catch (Exception e) {
|
||||
log.warn("加载平台失败: {}", e.getMessage());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<Long, String> loadUserMap() {
|
||||
Map<Long, String> map = new HashMap<>();
|
||||
try {
|
||||
userMapper.selectList(null).forEach(t -> map.put(t.getId(), t.getRealName() != null ? t.getRealName() : t.getUsername()));
|
||||
} catch (Exception e) {
|
||||
log.warn("加载用户失败: {}", e.getMessage());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<Long, String> loadDeptMap() {
|
||||
Map<Long, String> map = new HashMap<>();
|
||||
try {
|
||||
departmentMapper.selectList(null).forEach(t -> map.put(t.getId(), t.getDeptName()));
|
||||
} catch (Exception e) {
|
||||
log.warn("加载部门失败: {}", e.getMessage());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务大厅导出VO
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class TaskHallExportVO {
|
||||
private String taskNo;
|
||||
private String title;
|
||||
private String taskType;
|
||||
private String demandType;
|
||||
private String brand;
|
||||
private String category;
|
||||
private String platform;
|
||||
private String spuInfo;
|
||||
private String score;
|
||||
private String estimatedHours;
|
||||
private Integer quantity;
|
||||
private String publisher;
|
||||
private String publisherDept;
|
||||
private String receiver;
|
||||
private String receiverDept;
|
||||
private String status;
|
||||
private String releaseType;
|
||||
private String isUrgent;
|
||||
private String isPipeline;
|
||||
private String createTime;
|
||||
private String deadline;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出任务列表
|
||||
*/
|
||||
|
||||
@ -679,3 +679,69 @@ export const confirmTaskProductFilled = (taskId: number) => {
|
||||
export const deleteTaskByTaskNo = (taskNo: string) => {
|
||||
return request.delete<any>(`/tasks/by-task-no/${taskNo}`)
|
||||
}
|
||||
|
||||
// ==================== 导出功能 ====================
|
||||
|
||||
/**
|
||||
* 导出任务大厅数据(仅部门负责人和管理员)
|
||||
*/
|
||||
export interface ExportTaskHallParams {
|
||||
status?: string
|
||||
publisherDeptId?: number
|
||||
receiverDeptId?: number
|
||||
brandId?: number
|
||||
categoryId?: number
|
||||
platformId?: number
|
||||
}
|
||||
|
||||
export const exportTaskHall = async (params: ExportTaskHallParams = {}) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.status) queryParams.append('status', params.status)
|
||||
if (params.publisherDeptId) queryParams.append('publisherDeptId', params.publisherDeptId.toString())
|
||||
if (params.receiverDeptId) queryParams.append('receiverDeptId', params.receiverDeptId.toString())
|
||||
if (params.brandId) queryParams.append('brandId', params.brandId.toString())
|
||||
if (params.categoryId) queryParams.append('categoryId', params.categoryId.toString())
|
||||
if (params.platformId) queryParams.append('platformId', params.platformId.toString())
|
||||
|
||||
const url = `/export/task-hall${queryParams.toString() ? '?' + queryParams.toString() : ''}`
|
||||
|
||||
// 使用 fetch 直接下载文件
|
||||
const token = localStorage.getItem('token')
|
||||
const response = await fetch(`/api${url}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': token ? `Bearer ${token}` : ''
|
||||
}
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
try {
|
||||
const errorJson = JSON.parse(errorText)
|
||||
throw new Error(errorJson.message || '导出失败')
|
||||
} catch {
|
||||
throw new Error('导出失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取文件名
|
||||
const contentDisposition = response.headers.get('Content-Disposition')
|
||||
let fileName = '任务大厅数据.xlsx'
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)
|
||||
if (fileNameMatch && fileNameMatch[1]) {
|
||||
fileName = decodeURIComponent(fileNameMatch[1].replace(/['"]/g, ''))
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
const blob = await response.blob()
|
||||
const downloadUrl = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = downloadUrl
|
||||
link.download = fileName
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(downloadUrl)
|
||||
}
|
||||
|
||||
@ -36,6 +36,17 @@
|
||||
<Icon name="plus" size="xs" style="margin-right: 4px;" />
|
||||
发起任务
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="canExportTaskHall"
|
||||
type="default"
|
||||
size="middle"
|
||||
@click="handleExportTaskHall"
|
||||
:loading="exportLoading"
|
||||
style="margin-left: 8px;"
|
||||
>
|
||||
<Icon name="download" size="xs" style="margin-right: 4px;" />
|
||||
一键导出
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats-grid">
|
||||
@ -2210,7 +2221,8 @@ import * as configApi from '@/api/config'
|
||||
import * as authApi from '@/api/auth'
|
||||
import * as departmentApi from '@/api/department'
|
||||
import { getWorkHoursConfig } from '@/api/system'
|
||||
import type { Task, TaskApprovalDTO, TaskProduct } from '@/api/task'
|
||||
import type { Task, TaskApprovalDTO, TaskProduct, ExportTaskHallParams } from '@/api/task'
|
||||
import { exportTaskHall } from '@/api/task'
|
||||
import type { DesignerWithWorkload } from '@/api/user'
|
||||
import { useConfigStore } from '@/stores/config'
|
||||
import dayjs from 'dayjs'
|
||||
@ -2271,6 +2283,7 @@ const currentDeptView = ref<number | undefined>(undefined) // 当前选择的部
|
||||
const userDeptId = ref<number | null>(null)
|
||||
const userDeptName = ref<string>('')
|
||||
const userDeptType = ref<string>('')
|
||||
const exportLoading = ref(false) // 导出加载状态
|
||||
|
||||
// 获取当前有效的部门类型(根据选择的视角)
|
||||
const effectiveDeptType = computed(() => {
|
||||
@ -2286,6 +2299,29 @@ const effectiveDeptId = computed(() => {
|
||||
return currentDeptView.value !== undefined ? currentDeptView.value : userDeptId.value
|
||||
})
|
||||
|
||||
// 计算是否有导出权限(部门负责人或管理员)
|
||||
const canExportTaskHall = computed(() => {
|
||||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}')
|
||||
if (!userInfo || !userInfo.id) return false
|
||||
|
||||
// 管理员可以导出
|
||||
if (userInfo.isAdmin === 1 || userInfo.isAdmin === true) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 部门负责人可以导出
|
||||
if (userInfo.isDepartmentLeader === 1 || userInfo.isDepartmentLeader === true) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 管理多个部门的用户也是部门负责人
|
||||
if (managedDepartments.value.length > 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
// 初始化部门视角数据
|
||||
const initDeptViewData = async () => {
|
||||
try {
|
||||
@ -4245,6 +4281,36 @@ const handleCreateTask = () => {
|
||||
router.push('/tasks/create')
|
||||
}
|
||||
|
||||
// 一键导出任务大厅数据
|
||||
const handleExportTaskHall = async () => {
|
||||
if (!canExportTaskHall.value) {
|
||||
message.warning('您没有导出权限,只有部门负责人或管理员可以导出')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
exportLoading.value = true
|
||||
|
||||
// 构建导出参数(使用当前筛选条件)
|
||||
const params: ExportTaskHallParams = {
|
||||
status: filters.value.status || 'pending',
|
||||
publisherDeptId: filters.value.publisherDeptId,
|
||||
receiverDeptId: filters.value.receiverDeptId,
|
||||
brandId: filters.value.brandId,
|
||||
categoryId: filters.value.categoryId,
|
||||
platformId: filters.value.platformId
|
||||
}
|
||||
|
||||
await exportTaskHall(params)
|
||||
message.success('导出成功')
|
||||
} catch (error: any) {
|
||||
console.error('导出失败:', error)
|
||||
message.error(error.message || '导出失败')
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载进行中任务
|
||||
// - 需求部门用户:只看母任务卡片(自己发起的任务)
|
||||
// - 接单部门用户:只看子任务卡片(自己接单的任务)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user