mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 01:50:30 +08:00
feat(m9): add CSV export for contract-sn report
This commit is contained in:
+30
@@ -4,11 +4,18 @@ import cn.craftlabs.platform.api.service.ReportService;
|
||||
import cn.craftlabs.platform.api.web.dto.CallbackStatsResponse;
|
||||
import cn.craftlabs.platform.api.web.dto.ContractSnReportRow;
|
||||
import cn.craftlabs.platform.api.web.dto.ProjectHealthRow;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@@ -39,4 +46,27 @@ public class ReportController {
|
||||
public List<ProjectHealthRow> getProjectHealth() {
|
||||
return reportService.getProjectHealth();
|
||||
}
|
||||
|
||||
@GetMapping("/export")
|
||||
public ResponseEntity<Resource> exportReport(
|
||||
@RequestParam String type,
|
||||
@RequestParam(required = false) Long projectId,
|
||||
@RequestParam(required = false) Long contractId) {
|
||||
|
||||
String csv = reportService.exportReport(type, projectId, contractId);
|
||||
|
||||
byte[] bytes = csv.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] bom = {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};
|
||||
byte[] withBom = new byte[bom.length + bytes.length];
|
||||
System.arraycopy(bom, 0, withBom, 0, bom.length);
|
||||
System.arraycopy(bytes, 0, withBom, bom.length, bytes.length);
|
||||
|
||||
ByteArrayResource resource = new ByteArrayResource(withBom);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=report-" + type + "-" + LocalDate.now() + ".csv")
|
||||
.contentType(MediaType.parseMediaType("text/csv; charset=utf-8"))
|
||||
.body(resource);
|
||||
}
|
||||
}
|
||||
|
||||
+29
@@ -162,6 +162,35 @@ public class ReportService {
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public String exportReport(String type, Long projectId, Long contractId) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if ("contract-sn".equals(type)) {
|
||||
sb.append("合同名称,客户,行项,应发,实发,已激活,缺口,状态\n");
|
||||
List<ContractSnReportRow> rows = getContractSnReport(projectId, contractId);
|
||||
for (ContractSnReportRow r : rows) {
|
||||
sb.append(escapeCsv(r.getContractTitle())).append(",");
|
||||
sb.append(escapeCsv(r.getCustomerName())).append(",");
|
||||
sb.append(escapeCsv(r.getLineItemName())).append(",");
|
||||
sb.append(r.getExpectedCount()).append(",");
|
||||
sb.append(r.getIssuedCount()).append(",");
|
||||
sb.append(r.getActivatedCount()).append(",");
|
||||
sb.append(r.getGapCount()).append(",");
|
||||
sb.append(escapeCsv(r.getStatus())).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String escapeCsv(String value) {
|
||||
if (value == null) return "";
|
||||
if (value.contains(",") || value.contains("\"") || value.contains("\n")) {
|
||||
return "\"" + value.replace("\"", "\"\"") + "\"";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String resolveCustomerName(Long customerId) {
|
||||
if (customerId == null) {
|
||||
return null;
|
||||
|
||||
@@ -373,6 +373,9 @@ export function updateNotificationConfig(body) {
|
||||
export function getContractSnReport(params) {
|
||||
return axios.get('/api/v1/reports/contract-sn', { params });
|
||||
}
|
||||
export function exportReport(params) {
|
||||
return axios.get('/api/v1/reports/export', { params, responseType: 'blob' });
|
||||
}
|
||||
export function getCallbackStats(params) {
|
||||
return axios.get('/api/v1/reports/callback-stats', { params });
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<div class="toolbar">
|
||||
<span class="title">合同 SN 报表</span>
|
||||
<div class="actions">
|
||||
<el-button @click="handleExport" :loading="exporting">导出 CSV</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="load">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -49,11 +50,12 @@
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useAuthStore } from "../stores/auth";
|
||||
import { getContractSnReport } from "../api/platform";
|
||||
import { getContractSnReport, exportReport } from "../api/platform";
|
||||
import { apiErrorMessage } from "../utils/apiErrorMessage";
|
||||
|
||||
const auth = useAuthStore();
|
||||
const loading = ref(false);
|
||||
const exporting = ref(false);
|
||||
const rows = ref([]);
|
||||
const kpi = reactive({ totalLineItems: 0, totalIssued: 0, totalActivated: 0, totalGap: 0 });
|
||||
|
||||
@@ -83,6 +85,25 @@ async function load() {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleExport() {
|
||||
exporting.value = true
|
||||
try {
|
||||
const response = await exportReport({ type: 'contract-sn' })
|
||||
const url = window.URL.createObjectURL(new Blob([response.data]))
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `contract-sn-report-${new Date().toISOString().slice(0, 10)}.csv`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
} catch (e) {
|
||||
ElMessage.error('导出失败')
|
||||
} finally {
|
||||
exporting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user