first
This commit is contained in:
847
admin-web/src/pages/reports/index.vue
Normal file
847
admin-web/src/pages/reports/index.vue
Normal file
@@ -0,0 +1,847 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import QRCode from "qrcode";
|
||||
import {
|
||||
adminApi,
|
||||
type AdminManualInspectionPayload,
|
||||
type AdminReportDetail,
|
||||
type AdminReportListItem,
|
||||
} from "../../api/admin";
|
||||
import OrderStatusTag from "../../components/OrderStatusTag.vue";
|
||||
|
||||
function createInspectionPayload(): AdminManualInspectionPayload {
|
||||
return {
|
||||
report_header: {
|
||||
report_no: "",
|
||||
report_title: "安心验检查单",
|
||||
report_status: "pending_publish",
|
||||
service_provider: "anxinyan",
|
||||
institution_name: "安心验",
|
||||
publish_time: "",
|
||||
},
|
||||
product_info: {
|
||||
product_name: "",
|
||||
category_name: "",
|
||||
brand_name: "",
|
||||
color: "",
|
||||
size_spec: "",
|
||||
serial_no: "",
|
||||
},
|
||||
result_info: {
|
||||
result_status: "authentic",
|
||||
result_text: "正品",
|
||||
result_desc: "",
|
||||
},
|
||||
appraisal_info: {
|
||||
appraiser_name: "",
|
||||
reviewer_name: "",
|
||||
appraisal_time: "",
|
||||
},
|
||||
valuation_info: {
|
||||
condition_grade: "",
|
||||
condition_desc: "",
|
||||
valuation_min: "",
|
||||
valuation_max: "",
|
||||
valuation_desc: "",
|
||||
},
|
||||
risk_notice_text: "",
|
||||
};
|
||||
}
|
||||
|
||||
const loading = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
const drawerVisible = ref(false);
|
||||
const inspectionDrawerVisible = ref(false);
|
||||
const inspectionSubmitting = ref(false);
|
||||
const publishingId = ref<number | null>(null);
|
||||
const detailQrDataUrl = ref("");
|
||||
|
||||
const keyword = ref("");
|
||||
const serviceProvider = ref("");
|
||||
const reportStatus = ref("");
|
||||
|
||||
const reports = ref<AdminReportListItem[]>([]);
|
||||
const detail = ref<AdminReportDetail | null>(null);
|
||||
const inspectionForm = ref<AdminManualInspectionPayload>(createInspectionPayload());
|
||||
const route = useRoute();
|
||||
|
||||
const canPublishCurrentReport = computed(() => detail.value?.report_header.report_status === "pending_publish");
|
||||
const canEditCurrentInspection = computed(
|
||||
() => detail.value?.report_header.report_type === "inspection" && detail.value?.report_header.report_status !== "published",
|
||||
);
|
||||
const inspectionDrawerTitle = computed(() => (inspectionForm.value.id ? "编辑补录检查单" : "补录检查单"));
|
||||
|
||||
const providerOptions = [
|
||||
{ label: "全部服务", value: "" },
|
||||
{ label: "实物鉴定", value: "anxinyan" },
|
||||
{ label: "中检鉴定", value: "zhongjian" },
|
||||
];
|
||||
|
||||
const statusOptions = [
|
||||
{ label: "全部状态", value: "" },
|
||||
{ label: "已发布", value: "published" },
|
||||
{ label: "待发布", value: "pending_publish" },
|
||||
{ label: "草稿中", value: "draft" },
|
||||
{ label: "已更新", value: "updated" },
|
||||
{ label: "已作废", value: "invalid" },
|
||||
];
|
||||
|
||||
const inspectionStatusOptions = [
|
||||
{ label: "草稿保存", value: "draft" },
|
||||
{ label: "待发布", value: "pending_publish" },
|
||||
{ label: "直接发布", value: "published" },
|
||||
];
|
||||
|
||||
const resultOptions = [
|
||||
{ label: "正品", value: "authentic", text: "正品" },
|
||||
{ label: "存疑", value: "uncertain", text: "存疑" },
|
||||
{ label: "非正品", value: "not_authentic", text: "非正品" },
|
||||
];
|
||||
|
||||
function applyProviderPreset(force = false) {
|
||||
const provider = inspectionForm.value.report_header.service_provider;
|
||||
const title = provider === "zhongjian" ? "中检检查单" : "安心验检查单";
|
||||
const institution = provider === "zhongjian" ? "中检合作机构" : "安心验";
|
||||
|
||||
if (force || !inspectionForm.value.report_header.report_title) {
|
||||
inspectionForm.value.report_header.report_title = title;
|
||||
}
|
||||
if (force || !inspectionForm.value.report_header.institution_name) {
|
||||
inspectionForm.value.report_header.institution_name = institution;
|
||||
}
|
||||
}
|
||||
|
||||
function syncResultText() {
|
||||
const matched = resultOptions.find((item) => item.value === inspectionForm.value.result_info.result_status);
|
||||
if (matched && !inspectionForm.value.result_info.result_text) {
|
||||
inspectionForm.value.result_info.result_text = matched.text;
|
||||
}
|
||||
}
|
||||
|
||||
function previewEvidence(url: string) {
|
||||
if (!url) return;
|
||||
window.open(url, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
|
||||
function evidenceTypeLabel(fileType?: string) {
|
||||
return fileType === "image" ? "图片" : fileType === "video" ? "视频" : fileType === "pdf" ? "PDF" : "附件";
|
||||
}
|
||||
|
||||
const imageEvidenceList = computed(() =>
|
||||
(detail.value?.evidence_attachments || []).filter((item) => item.file_type === "image"),
|
||||
);
|
||||
|
||||
const fileEvidenceList = computed(() =>
|
||||
(detail.value?.evidence_attachments || []).filter((item) => item.file_type !== "image"),
|
||||
);
|
||||
|
||||
function openInspectionCreate() {
|
||||
inspectionForm.value = createInspectionPayload();
|
||||
applyProviderPreset(true);
|
||||
syncResultText();
|
||||
inspectionDrawerVisible.value = true;
|
||||
}
|
||||
|
||||
function openInspectionEditFromDetail() {
|
||||
if (!detail.value) return;
|
||||
|
||||
inspectionForm.value = {
|
||||
id: detail.value.report_header.id,
|
||||
report_header: {
|
||||
report_no: detail.value.report_header.report_no,
|
||||
report_title: detail.value.report_header.report_title,
|
||||
report_status: detail.value.report_header.report_status,
|
||||
service_provider: detail.value.report_header.service_provider,
|
||||
institution_name: detail.value.report_header.institution_name,
|
||||
publish_time: detail.value.report_header.publish_time || "",
|
||||
},
|
||||
product_info: {
|
||||
product_name: detail.value.product_info.product_name || "",
|
||||
category_name: detail.value.product_info.category_name || "",
|
||||
brand_name: detail.value.product_info.brand_name || "",
|
||||
color: detail.value.product_info.color || "",
|
||||
size_spec: detail.value.product_info.size_spec || "",
|
||||
serial_no: detail.value.product_info.serial_no || "",
|
||||
},
|
||||
result_info: {
|
||||
result_status: detail.value.result_info.result_status || "authentic",
|
||||
result_text: detail.value.result_info.result_text || "",
|
||||
result_desc: detail.value.result_info.result_desc || "",
|
||||
},
|
||||
appraisal_info: {
|
||||
appraiser_name: detail.value.appraisal_info.appraiser_name || "",
|
||||
reviewer_name: detail.value.appraisal_info.reviewer_name || "",
|
||||
appraisal_time: detail.value.appraisal_info.appraisal_time || "",
|
||||
},
|
||||
valuation_info: {
|
||||
condition_grade: detail.value.valuation_info.condition_grade || "",
|
||||
condition_desc: detail.value.valuation_info.condition_desc || "",
|
||||
valuation_min: detail.value.valuation_info.valuation_min ?? "",
|
||||
valuation_max: detail.value.valuation_info.valuation_max ?? "",
|
||||
valuation_desc: detail.value.valuation_info.valuation_desc || "",
|
||||
},
|
||||
risk_notice_text: detail.value.risk_notice_text || "",
|
||||
};
|
||||
inspectionDrawerVisible.value = true;
|
||||
}
|
||||
|
||||
async function syncQrCode(url: string) {
|
||||
if (!/^https?:\/\//i.test(url)) {
|
||||
detailQrDataUrl.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
detailQrDataUrl.value = await QRCode.toDataURL(url, {
|
||||
width: 220,
|
||||
margin: 1,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
detailQrDataUrl.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchReports() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await adminApi.getReports({
|
||||
keyword: keyword.value,
|
||||
service_provider: serviceProvider.value,
|
||||
status: reportStatus.value,
|
||||
});
|
||||
if (response.code !== 0) {
|
||||
ElMessage.error(response.message || "报告列表加载失败");
|
||||
return;
|
||||
}
|
||||
reports.value = response.data.list;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("报告列表加载失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDetail(id: number) {
|
||||
detailLoading.value = true;
|
||||
detailQrDataUrl.value = "";
|
||||
try {
|
||||
const response = await adminApi.getReportDetail(id);
|
||||
if (response.code !== 0) {
|
||||
ElMessage.error(response.message || "报告详情加载失败");
|
||||
return;
|
||||
}
|
||||
detail.value = response.data;
|
||||
await syncQrCode(response.data.verify_info.verify_qrcode_url || response.data.verify_info.report_page_url || "");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("报告详情加载失败");
|
||||
} finally {
|
||||
detailLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function openDetail(row: AdminReportListItem) {
|
||||
drawerVisible.value = true;
|
||||
await loadDetail(row.id);
|
||||
}
|
||||
|
||||
function parseReportId(value: unknown) {
|
||||
const raw = Array.isArray(value) ? value[0] : value;
|
||||
const id = Number(raw || 0);
|
||||
return Number.isInteger(id) && id > 0 ? id : 0;
|
||||
}
|
||||
|
||||
async function openDetailFromRouteQuery() {
|
||||
const reportId = parseReportId(route.query.report_id);
|
||||
if (!reportId) {
|
||||
return;
|
||||
}
|
||||
if (drawerVisible.value && detail.value?.report_header.id === reportId) {
|
||||
return;
|
||||
}
|
||||
drawerVisible.value = true;
|
||||
await loadDetail(reportId);
|
||||
}
|
||||
|
||||
async function publishReport(row: Pick<AdminReportListItem, "id" | "report_status"> | { id: number; report_status: string }) {
|
||||
if (row.report_status !== "pending_publish") {
|
||||
ElMessage.warning("仅待发布报告可以执行发布");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm("发布后用户端将可查看正式报告并进行验真,是否继续?", "发布报告", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确认发布",
|
||||
cancelButtonText: "取消",
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
publishingId.value = row.id;
|
||||
try {
|
||||
const response = await adminApi.publishReport(row.id);
|
||||
if (response.code !== 0) {
|
||||
ElMessage.error(response.message || "报告发布失败");
|
||||
return;
|
||||
}
|
||||
|
||||
ElMessage.success(response.message || "报告已发布");
|
||||
await fetchReports();
|
||||
|
||||
if (drawerVisible.value && detail.value?.report_header.id === row.id) {
|
||||
await loadDetail(row.id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("报告发布失败");
|
||||
} finally {
|
||||
publishingId.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
function validateInspectionForm() {
|
||||
const { report_header, product_info, result_info } = inspectionForm.value;
|
||||
if (!report_header.report_title.trim()) {
|
||||
ElMessage.warning("请填写检查单标题");
|
||||
return false;
|
||||
}
|
||||
if (!report_header.institution_name.trim()) {
|
||||
ElMessage.warning("请填写出具机构");
|
||||
return false;
|
||||
}
|
||||
if (!product_info.product_name.trim()) {
|
||||
ElMessage.warning("请填写商品名称");
|
||||
return false;
|
||||
}
|
||||
if (!result_info.result_text.trim()) {
|
||||
ElMessage.warning("请填写鉴定结论");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function saveInspection() {
|
||||
if (!validateInspectionForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
inspectionSubmitting.value = true;
|
||||
try {
|
||||
const response = await adminApi.saveInspectionReport(inspectionForm.value);
|
||||
if (response.code !== 0) {
|
||||
ElMessage.error(response.message || "检查单保存失败");
|
||||
return;
|
||||
}
|
||||
|
||||
ElMessage.success(response.message || "检查单已保存");
|
||||
inspectionDrawerVisible.value = false;
|
||||
await fetchReports();
|
||||
drawerVisible.value = true;
|
||||
await loadDetail(response.data.id);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("检查单保存失败");
|
||||
} finally {
|
||||
inspectionSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function copyText(value: string, label: string) {
|
||||
if (!value) {
|
||||
ElMessage.warning(`${label}为空`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (navigator.clipboard?.writeText) {
|
||||
await navigator.clipboard.writeText(value);
|
||||
} else {
|
||||
const input = document.createElement("textarea");
|
||||
input.value = value;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(input);
|
||||
}
|
||||
ElMessage.success(`${label}已复制`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error(`${label}复制失败`);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
applyProviderPreset(true);
|
||||
syncResultText();
|
||||
fetchReports();
|
||||
openDetailFromRouteQuery();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.query.report_id,
|
||||
() => {
|
||||
openDetailFromRouteQuery();
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card class="panel-card" shadow="never">
|
||||
<div class="filters-row" style="justify-content: space-between;">
|
||||
<div class="filters-row">
|
||||
<el-input v-model="keyword" placeholder="搜索报告编号 / 鉴定单号 / 订单号 / 商品名称" clearable style="width: 340px" />
|
||||
<el-select v-model="serviceProvider" placeholder="服务类型" style="width: 160px">
|
||||
<el-option v-for="item in providerOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-select v-model="reportStatus" placeholder="报告状态" style="width: 160px">
|
||||
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-button type="primary" @click="fetchReports">查询</el-button>
|
||||
</div>
|
||||
<el-button type="primary" plain @click="openInspectionCreate">补录检查单</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="panel-card orders-table" shadow="never">
|
||||
<el-table v-loading="loading" :data="reports" stripe>
|
||||
<el-table-column prop="report_no" label="报告编号" min-width="180" />
|
||||
<el-table-column prop="appraisal_no" label="鉴定单号" min-width="180" />
|
||||
<el-table-column prop="report_type_text" label="类型" min-width="120" />
|
||||
<el-table-column prop="report_title" label="报告标题" min-width="180" />
|
||||
<el-table-column prop="product_name" label="商品名称" min-width="220" />
|
||||
<el-table-column prop="service_provider_text" label="服务类型" min-width="120" />
|
||||
<el-table-column label="报告状态" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<OrderStatusTag :status="row.report_status_text" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="institution_name" label="出具机构" min-width="160" />
|
||||
<el-table-column prop="publish_time" label="发布时间" min-width="170" />
|
||||
<el-table-column label="操作" fixed="right" width="220">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="openDetail(row)">查看详情</el-button>
|
||||
<el-button
|
||||
v-if="row.report_type === 'inspection' && row.report_status !== 'published'"
|
||||
link
|
||||
type="success"
|
||||
@click="openDetail(row).then(() => openInspectionEditFromDetail())"
|
||||
>
|
||||
编辑检查单
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.report_status === 'pending_publish'"
|
||||
link
|
||||
type="warning"
|
||||
:loading="publishingId === row.id"
|
||||
@click="publishReport(row)"
|
||||
>
|
||||
发布报告
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-drawer v-model="drawerVisible" size="62%" title="报告详情">
|
||||
<div v-loading="detailLoading" v-if="detail" class="detail-grid">
|
||||
<div style="grid-column: 1 / -1; display: flex; justify-content: flex-end; gap: 12px; margin-bottom: 8px">
|
||||
<el-button v-if="canEditCurrentInspection" type="success" plain @click="openInspectionEditFromDetail">
|
||||
编辑检查单
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="canPublishCurrentReport"
|
||||
type="primary"
|
||||
:loading="publishingId === detail.report_header.id"
|
||||
@click="publishReport({ id: detail.report_header.id, report_status: detail.report_header.report_status })"
|
||||
>
|
||||
发布报告
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<div class="detail-card__title">报告概览</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">报告编号</div>
|
||||
<div class="detail-value">{{ detail.report_header.report_no }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">报告类型</div>
|
||||
<div class="detail-value">{{ detail.report_header.report_type_text }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">报告标题</div>
|
||||
<div class="detail-value">{{ detail.report_header.report_title }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">报告状态</div>
|
||||
<div class="detail-value">
|
||||
<OrderStatusTag :status="detail.report_header.report_status_text" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">出具机构</div>
|
||||
<div class="detail-value">{{ detail.report_header.institution_name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<div class="detail-card__title">商品信息</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">商品名称</div>
|
||||
<div class="detail-value">{{ detail.product_info.product_name || "-" }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">品类 / 品牌</div>
|
||||
<div class="detail-value">{{ detail.product_info.category_name || "-" }} / {{ detail.product_info.brand_name || "-" }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">颜色 / 规格</div>
|
||||
<div class="detail-value">{{ detail.product_info.color || "-" }} / {{ detail.product_info.size_spec || "-" }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">序列号</div>
|
||||
<div class="detail-value">{{ detail.product_info.serial_no || "-" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<div class="detail-card__title">鉴定结果</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">结论</div>
|
||||
<div class="detail-value">{{ detail.result_info.result_text || "-" }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">说明</div>
|
||||
<div class="detail-value">{{ detail.result_info.result_desc || "-" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<div class="detail-card__title">鉴定信息</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">服务类型</div>
|
||||
<div class="detail-value">{{ detail.report_header.service_provider_text }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">鉴定师</div>
|
||||
<div class="detail-value">{{ detail.appraisal_info.appraiser_name || "-" }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">鉴定时间</div>
|
||||
<div class="detail-value">{{ detail.appraisal_info.appraisal_time || "-" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<div class="detail-card__title">评级与估值</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">成色评级</div>
|
||||
<div class="detail-value">{{ detail.valuation_info.condition_grade || "-" }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">估值区间</div>
|
||||
<div class="detail-value">¥{{ detail.valuation_info.valuation_min || 0 }} - ¥{{ detail.valuation_info.valuation_max || 0 }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">估值说明</div>
|
||||
<div class="detail-value">{{ detail.valuation_info.valuation_desc || "-" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card" style="grid-column: 1 / -1">
|
||||
<div class="detail-card__title">证据附件</div>
|
||||
<div v-if="detail.evidence_attachments.length" class="report-evidence-stack">
|
||||
<div v-if="imageEvidenceList.length" class="report-evidence-section">
|
||||
<div class="report-evidence-section__title">图片证据</div>
|
||||
<div class="report-evidence-gallery">
|
||||
<div
|
||||
v-for="attachment in imageEvidenceList"
|
||||
:key="attachment.file_id"
|
||||
class="report-evidence-gallery__item"
|
||||
@click="previewEvidence(attachment.file_url)"
|
||||
>
|
||||
<img :src="attachment.thumbnail_url || attachment.file_url" :alt="attachment.name || '证据图片'" />
|
||||
<div class="report-evidence-gallery__caption">{{ attachment.name || "未命名图片" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="fileEvidenceList.length" class="report-evidence-section">
|
||||
<div class="report-evidence-section__title">视频 / 文档证据</div>
|
||||
<div class="report-evidence-list">
|
||||
<div v-for="attachment in fileEvidenceList" :key="attachment.file_id" class="report-evidence-card">
|
||||
<div class="report-evidence-card__preview" @click="previewEvidence(attachment.file_url)">
|
||||
<div class="report-evidence-card__filetype">{{ evidenceTypeLabel(attachment.file_type) }}</div>
|
||||
</div>
|
||||
<div class="report-evidence-card__body">
|
||||
<div class="detail-value" style="margin-top: 0; word-break: break-word;">{{ attachment.name || attachment.file_url }}</div>
|
||||
<div class="detail-label" style="margin-top: 6px;">{{ evidenceTypeLabel(attachment.file_type) }}</div>
|
||||
<el-button size="small" style="margin-top: 10px" @click="previewEvidence(attachment.file_url)">查看附件</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="detail-card__desc">
|
||||
<div class="detail-value">当前报告未附带证据附件</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card" style="grid-column: 1 / -1">
|
||||
<div class="detail-card__title">扫码与公开链接</div>
|
||||
<div style="display: grid; grid-template-columns: 220px 1fr; gap: 24px; align-items: start;">
|
||||
<div
|
||||
style="width: 220px; height: 220px; border-radius: 16px; border: 1px dashed var(--admin-border); display: flex; align-items: center; justify-content: center; overflow: hidden; background: #fff;"
|
||||
>
|
||||
<el-image v-if="detailQrDataUrl" :src="detailQrDataUrl" fit="contain" style="width: 200px; height: 200px" />
|
||||
<div v-else style="padding: 16px; text-align: center; color: var(--admin-text-subtle); line-height: 1.7;">
|
||||
请先在系统配置中填写 H5 页面根地址,再生成可扫码的公开链接。
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; gap: 14px;">
|
||||
<div class="detail-card__desc" style="margin: 0;">
|
||||
<div class="detail-label">扫码打开报告页</div>
|
||||
<div class="detail-value" style="word-break: break-all;">{{ detail.verify_info.verify_qrcode_url || "-" }}</div>
|
||||
<el-button size="small" style="margin-top: 8px" @click="copyText(detail.verify_info.verify_qrcode_url, '报告链接')">复制报告链接</el-button>
|
||||
</div>
|
||||
<div class="detail-card__desc" style="margin: 0;">
|
||||
<div class="detail-label">H5 验真页</div>
|
||||
<div class="detail-value" style="word-break: break-all;">{{ detail.verify_info.verify_url || "-" }}</div>
|
||||
<el-button size="small" style="margin-top: 8px" @click="copyText(detail.verify_info.verify_url, '验真链接')">复制验真链接</el-button>
|
||||
</div>
|
||||
<div class="detail-card__desc" style="margin: 0;">
|
||||
<div class="detail-label">验真状态 / 次数</div>
|
||||
<div class="detail-value">{{ detail.verify_info.verify_status }} / {{ detail.verify_info.verify_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card" style="grid-column: 1 / -1">
|
||||
<div class="detail-card__title">风险说明</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-value">{{ detail.risk_notice_text || "-" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<el-drawer v-model="inspectionDrawerVisible" size="56%" :title="inspectionDrawerTitle">
|
||||
<div style="display: grid; gap: 24px;">
|
||||
<el-card shadow="never">
|
||||
<template #header>基础信息</template>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="检查单编号">
|
||||
<el-input v-model="inspectionForm.report_header.report_no" placeholder="可留空,系统自动生成" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="检查单标题">
|
||||
<el-input v-model="inspectionForm.report_header.report_title" placeholder="请输入检查单标题" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="服务类型">
|
||||
<el-select v-model="inspectionForm.report_header.service_provider" style="width: 100%" @change="applyProviderPreset()">
|
||||
<el-option label="实物鉴定" value="anxinyan" />
|
||||
<el-option label="中检鉴定" value="zhongjian" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="保存状态">
|
||||
<el-select v-model="inspectionForm.report_header.report_status" style="width: 100%">
|
||||
<el-option v-for="item in inspectionStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="出具机构">
|
||||
<el-input v-model="inspectionForm.report_header.institution_name" placeholder="请输入出具机构" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发布时间">
|
||||
<el-date-picker
|
||||
v-model="inspectionForm.report_header.publish_time"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="直接发布时可指定发布时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<template #header>商品信息</template>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="商品名称"><el-input v-model="inspectionForm.product_info.product_name" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="品类"><el-input v-model="inspectionForm.product_info.category_name" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="品牌"><el-input v-model="inspectionForm.product_info.brand_name" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="颜色"><el-input v-model="inspectionForm.product_info.color" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="规格 / 尺寸"><el-input v-model="inspectionForm.product_info.size_spec" /></el-form-item></el-col>
|
||||
<el-col :span="24"><el-form-item label="序列号 / 编码"><el-input v-model="inspectionForm.product_info.serial_no" /></el-form-item></el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<template #header>鉴定结果</template>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结果类型">
|
||||
<el-select v-model="inspectionForm.result_info.result_status" style="width: 100%" @change="syncResultText">
|
||||
<el-option v-for="item in resultOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结果文案">
|
||||
<el-input v-model="inspectionForm.result_info.result_text" placeholder="例如:正品 / 存疑 / 非正品" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="结果说明">
|
||||
<el-input v-model="inspectionForm.result_info.result_desc" type="textarea" :rows="4" placeholder="请输入检查结论说明" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<template #header>鉴定与估值信息</template>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="鉴定师"><el-input v-model="inspectionForm.appraisal_info.appraiser_name" /></el-form-item></el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="鉴定时间">
|
||||
<el-date-picker
|
||||
v-model="inspectionForm.appraisal_info.appraisal_time"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12"><el-form-item label="成色评级"><el-input v-model="inspectionForm.valuation_info.condition_grade" placeholder="例如 A / B+" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="最低估值"><el-input v-model="inspectionForm.valuation_info.valuation_min" type="number" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="最高估值"><el-input v-model="inspectionForm.valuation_info.valuation_max" type="number" /></el-form-item></el-col>
|
||||
<el-col :span="24"><el-form-item label="成色说明"><el-input v-model="inspectionForm.valuation_info.condition_desc" type="textarea" :rows="3" /></el-form-item></el-col>
|
||||
<el-col :span="24"><el-form-item label="估值说明"><el-input v-model="inspectionForm.valuation_info.valuation_desc" type="textarea" :rows="3" /></el-form-item></el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<template #header>风险说明</template>
|
||||
<el-form-item label="页面说明文案">
|
||||
<el-input v-model="inspectionForm.risk_notice_text" type="textarea" :rows="4" placeholder="请输入风险提示与适用说明" />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
||||
<el-button @click="inspectionDrawerVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="inspectionSubmitting" @click="saveInspection">保存检查单</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.report-evidence-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.report-evidence-section__title {
|
||||
color: var(--admin-text-main);
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.report-evidence-gallery {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 14px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.report-evidence-gallery__item {
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #efe8d9;
|
||||
background: #fcfaf5;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.report-evidence-gallery__item img {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.report-evidence-gallery__caption {
|
||||
padding: 10px 12px;
|
||||
color: var(--admin-text-main);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.report-evidence-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
gap: 14px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.report-evidence-card {
|
||||
display: grid;
|
||||
grid-template-columns: 96px minmax(0, 1fr);
|
||||
gap: 14px;
|
||||
padding: 14px;
|
||||
border-radius: 16px;
|
||||
background: #fcfaf5;
|
||||
border: 1px solid #efe8d9;
|
||||
}
|
||||
|
||||
.report-evidence-card__preview {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #efe8d9;
|
||||
background: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.report-evidence-card__preview img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.report-evidence-card__filetype {
|
||||
color: var(--admin-progress);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.report-evidence-card__body {
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user