| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829 |
- <!-- 预警报告上传 -->
- <template>
- <div class="app-container warning-report-page" v-loading="pageLoading">
- <el-card shadow="never" class="page-card header-card">
- <div class="page-header">
- <div>
- <div class="page-title">预警报告上传</div>
- <div class="page-subtitle">提供预警报告附件上传功能,结合设备报警曲线、关联设备和周边要素分析,描述预警发生的具体内容</div>
- </div>
- </div>
- </el-card>
- <el-card shadow="never" class="page-card">
- <el-form :model="queryParams" :inline="true">
- <el-form-item label="预警单号">
- <el-input
- v-model="queryParams.warningNo"
- placeholder="请输入预警单号"
- clearable
- style="width: 220px"
- @keyup.enter="loadWarningOptions"
- />
- </el-form-item>
- <el-form-item label="预警类型">
- <el-select v-model="queryParams.warningType" clearable placeholder="请选择预警类型" style="width: 180px">
- <el-option
- v-for="item in warningTypeOptions"
- :key="item.dictValue"
- :label="item.dictLabel"
- :value="item.dictValue"
- />
- </el-select>
- </el-form-item>
- <el-form-item label="预警级别">
- <el-select v-model="queryParams.warningLevel" clearable placeholder="请选择预警级别" style="width: 180px">
- <el-option
- v-for="item in warningLevelOptions"
- :key="item.dictValue"
- :label="item.dictLabel"
- :value="item.dictValue"
- />
- </el-select>
- </el-form-item>
- <el-form-item label="选择预警">
- <el-select
- v-model="selectedWarningId"
- filterable
- clearable
- placeholder="请选择一条预警"
- style="width: 360px"
- @change="handleWarningChange"
- >
- <el-option
- v-for="item in warningOptions"
- :key="item.warningId"
- :label="item.warningNo"
- :value="item.warningId"
- >
- <div class="warning-option">
- <span>{{ item.warningNo }}</span>
- <el-tag :type="getLevelTagType(item.warningLevel)">{{ item.warningLevelText }}</el-tag>
- </div>
- </el-option>
- </el-select>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" icon="Search" @click="loadWarningOptions">查询</el-button>
- <el-button icon="Refresh" @click="resetQuery">重置</el-button>
- </el-form-item>
- </el-form>
- </el-card>
- <div class="report-container">
- <div class="left-panel">
- <el-card shadow="never" class="section-card">
- <template #header>
- <div class="section-title">报告上传</div>
- </template>
- <div
- class="upload-area"
- :class="{ 'drag-over': isDragOver }"
- @dragover.prevent="isDragOver = true"
- @dragleave.prevent="isDragOver = false"
- @drop.prevent="handleDrop"
- >
- <el-icon class="upload-icon"><UploadFilled /></el-icon>
- <div class="upload-text">
- <div class="primary-text">点击或拖拽文件至此区域</div>
- <div class="support-text">支持 `.pdf .doc .docx .zip`,单个文件不超过 20MB</div>
- </div>
- <el-upload
- ref="uploadRef"
- class="inline-upload"
- action="#"
- :auto-upload="false"
- :show-file-list="false"
- :on-change="handleFileChange"
- :limit="1"
- accept=".pdf,.doc,.docx,.zip"
- >
- <el-button type="primary">选择文件</el-button>
- </el-upload>
- </div>
- <div v-if="uploadedFile" class="file-info">
- <div class="file-item">
- <el-icon><Document /></el-icon>
- <span class="file-name">{{ uploadedFile.name }}</span>
- <span class="file-size">{{ formatFileSize(uploadedFile.size) }}</span>
- <el-icon class="remove-file" @click="removeFile"><CircleCloseFilled /></el-icon>
- </div>
- </div>
- <div class="report-description">
- <div class="field-title">报告摘要</div>
- <el-input
- v-model="reportSummary"
- type="textarea"
- :rows="5"
- maxlength="500"
- show-word-limit
- placeholder="请输入报告摘要、诊断结论或补充说明"
- />
- </div>
- <el-button
- type="success"
- class="submit-btn"
- :disabled="!selectedWarningId || !uploadedFile"
- :loading="submitLoading"
- @click="submitReport"
- >
- <el-icon><Upload /></el-icon>
- 提交预警报告
- </el-button>
- </el-card>
- <el-card shadow="never" class="section-card">
- <template #header>
- <div class="section-title">预警基础信息</div>
- </template>
- <el-empty v-if="!warningDetail.basicInfo" description="请先选择预警" />
- <el-descriptions v-else :column="1" border size="small">
- <el-descriptions-item label="预警单号">{{ currentBasicInfo.warningNo || "-" }}</el-descriptions-item>
- <el-descriptions-item label="预警类型">{{ currentBasicInfo.warningTypeText || "-" }}</el-descriptions-item>
- <el-descriptions-item label="预警级别">{{ currentBasicInfo.warningLevelText || "-" }}</el-descriptions-item>
- <el-descriptions-item label="发布人">{{ currentBasicInfo.publisher || "-" }}</el-descriptions-item>
- <el-descriptions-item label="发布时间">{{ currentBasicInfo.publishTime || "-" }}</el-descriptions-item>
- <el-descriptions-item label="权属单位">{{ currentBasicInfo.ownershipUnit || "-" }}</el-descriptions-item>
- <el-descriptions-item label="状态">
- <WarningStatusTag :status="currentBasicInfo.status">
- {{ getStatusText(currentBasicInfo.status) }}
- </WarningStatusTag>
- </el-descriptions-item>
- </el-descriptions>
- </el-card>
- <el-card shadow="never" class="section-card">
- <template #header>
- <div class="section-title">已有附件</div>
- </template>
- <el-empty v-if="!attachmentList.length" description="暂无已上传附件" />
- <div v-else class="attachment-list">
- <div v-for="(item, index) in attachmentList" :key="index" class="attachment-item">
- <el-icon><Document /></el-icon>
- <el-link :href="buildAttachmentUrl(item.attachmentUrl)" target="_blank" type="primary" class="attachment-name">
- {{ item.fileName || item.originalName || item.attachmentName || "附件" }}
- </el-link>
- <span class="attachment-size">{{ item.attachmentSize ? formatFileSize(item.attachmentSize) : "" }}</span>
- <span class="attachment-time">{{ item.createTime || item.uploadTime || "-" }}</span>
- </div>
- </div>
- </el-card>
- </div>
- <div class="right-panel">
- <el-card shadow="never" class="section-card">
- <el-tabs v-model="activeTab" class="analysis-tabs">
- <el-tab-pane label="设备报警曲线" name="curve">
- <div ref="curveChartRef" class="curve-chart"></div>
- <el-alert
- :title="curveSummary.title"
- :description="curveSummary.description"
- type="warning"
- :closable="false"
- />
- </el-tab-pane>
- <el-tab-pane label="关联设备" name="device">
- <el-descriptions :column="1" border size="small">
- <el-descriptions-item label="设备名称">{{ currentBasicInfo.deviceName || "-" }}</el-descriptions-item>
- <el-descriptions-item label="设备编码">{{ currentBasicInfo.deviceCode || "-" }}</el-descriptions-item>
- <el-descriptions-item label="权属单位">{{ currentBasicInfo.ownershipUnit || "-" }}</el-descriptions-item>
- <el-descriptions-item label="处置人">{{ currentBasicInfo.handler || "-" }}</el-descriptions-item>
- <el-descriptions-item label="状态"><WarningStatusTag :status="currentBasicInfo.status">{{ getStatusText(currentBasicInfo.status) }}</WarningStatusTag></el-descriptions-item>
- </el-descriptions>
- </el-tab-pane>
- <el-tab-pane label="周边要素分析" name="environment">
- <div class="factors-grid">
- <div class="factor-card">
- <div class="factor-label">关联报警</div>
- <div class="factor-value">{{ alarmList.length }}</div>
- <div class="factor-desc">报警曲线分析样本数</div>
- </div>
- <div class="factor-card">
- <div class="factor-label">处置记录</div>
- <div class="factor-value">{{ disposalHistory.length }}</div>
- <div class="factor-desc">流程处置和跟踪情况</div>
- </div>
- <div class="factor-card">
- <div class="factor-label">历史附件</div>
- <div class="factor-value">{{ attachmentList.length }}</div>
- <div class="factor-desc">已上传的资料与报告数量</div>
- </div>
- <div class="factor-card">
- <div class="factor-label">电子存档</div>
- <div class="factor-value">{{ hasArchive ? "已生成" : "未生成" }}</div>
- <div class="factor-desc">预警归档与存档状态</div>
- </div>
- </div>
- <el-card shadow="never" class="analysis-text-card">
- <template #header>
- <span>综合周边要素结论</span>
- </template>
- <p>{{ environmentConclusion }}</p>
- </el-card>
- </el-tab-pane>
- </el-tabs>
- <div class="warning-description">
- <div class="desc-header">
- <el-icon><InfoFilled /></el-icon>
- <h4>预警具体内容描述</h4>
- </div>
- <div class="desc-content">
- <p class="warning-content">{{ currentBasicInfo.warningContent || "暂无预警描述" }}</p>
- <p v-if="reportSummary"><strong>报告补充摘要:</strong>{{ reportSummary }}</p>
- </div>
- </div>
- </el-card>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import WarningStatusTag from "../components/WarningStatusTag.vue"
- import { useWarningStatuses } from '@/views/subSystem/lifeCompany/warningWorkflow'
- const { warningStatusOptions, databaseStatusLabels } = useWarningStatuses()
- import { computed, nextTick, onBeforeUnmount, onMounted, ref } from "vue";
- import { ElMessage } from "element-plus";
- import { CircleCloseFilled, Document, InfoFilled, Upload, UploadFilled } from "@element-plus/icons-vue";
- import * as echarts from "echarts";
- import {
- getAlertTypeDictionaryData,
- getLinkedAlarmListData,
- uploadWarningReportData,
- getdbyjlistData,
- updateYjxxData
- } from "@/api/smxyjldczxt/yjxxfb/yjfb.js";
- import { getWarningFullDetailData } from "@/api/pipeNetwork/earlyWarning";
- import { buildMinioAttachmentUrl } from "@/utils/minioAttachment";
- const pageLoading = ref(false);
- const submitLoading = ref(false);
- const isDragOver = ref(false);
- const uploadedFile = ref(null);
- const reportSummary = ref("");
- const activeTab = ref("curve");
- const uploadRef = ref();
- const curveChartRef = ref(null);
- let curveChart = null;
- const warningTypeOptions = ref([]);
- const warningLevelOptions = ref([]);
- const warningOptions = ref([]);
- const selectedWarningId = ref("");
- const warningDetail = ref({});
- const minioBaseUrl = (import.meta.env.VITE_APP_MINIO_BASE_URL || "").trim();
- const queryParams = ref({
- warningNo: "",
- warningType: "",
- warningLevel: "",
- status: ""
- });
- const warningTypeLabelMap = computed(() => {
- return warningTypeOptions.value.reduce((acc, item) => {
- acc[item.dictValue] = item.dictLabel;
- return acc;
- }, {});
- });
- const warningLevelLabelMap = computed(() => {
- return warningLevelOptions.value.reduce((acc, item) => {
- acc[item.dictValue] = item.dictLabel;
- return acc;
- }, {});
- });
- const warningStatusLabelMap = databaseStatusLabels;
- const currentBasicInfo = computed(() => {
- const basic = warningDetail.value.basicInfo || {};
- return {
- deviceCode: basic.deviceCode || basic.device_code || "",
- deviceName: basic.deviceName || basic.device_name || "",
- warningId: basic.warningId || basic.warning_id || "",
- warningNo: basic.warningNo || basic.warning_no || "",
- warningType: basic.warningType || basic.warning_type || "",
- warningTypeText: warningTypeLabelMap.value[basic.warningType || basic.warning_type] || basic.warningType || basic.warning_type || "-",
- warningLevel: basic.warningLevel || basic.warning_level || "",
- warningLevelText: warningLevelLabelMap.value[basic.warningLevel || basic.warning_level] || basic.warningLevel || basic.warning_level || "-",
- ownershipUnit: basic.ownershipUnit || basic.ownership_unit || "",
- publisher: basic.publisher || "",
- publishTime: basic.publishTime || basic.publish_time || "",
- handler: basic.handler || "",
- status: basic.status || "",
- statusText: warningStatusLabelMap.value[basic.status] || basic.statusText || basic.status || "",
- warningContent: basic.warningContent || basic.warning_content || "",
- warningSpecial: basic.warningSpecial || basic.warning_special || "",
- remark: basic.remark || ""
- };
- });
- const alarmList = computed(() => warningDetail.value.alarmList || []);
- const attachmentList = computed(() => warningDetail.value.attachmentList || []);
- const disposalHistory = computed(() => warningDetail.value.disposalHistory || []);
- const hasArchive = computed(() => !!warningDetail.value.electronicArchive);
- const curveSummary = computed(() => {
- if (!alarmList.value.length) {
- return {
- title: "曲线分析",
- description: "当前预警暂无关联报警数据,无法生成设备报警曲线分析。"
- };
- }
- const maxAlarm = [...alarmList.value].sort((a, b) => Number(b.actual_value || b.actualValue || 0) - Number(a.actual_value || a.actualValue || 0))[0];
- return {
- title: "曲线分析",
- description: `当前共关联 ${alarmList.value.length} 条报警记录,峰值实际值为 ${maxAlarm?.actual_value || maxAlarm?.actualValue || "-"},建议结合预警附件与现场情况形成完整书面报告。`
- };
- });
- const environmentConclusion = computed(() => {
- if (!warningDetail.value.basicInfo) {
- return "请选择预警后查看周边要素分析结论。";
- }
- return `本次预警“${currentBasicInfo.value.warningNo || "-"}”当前包含 ${alarmList.value.length} 条关联报警、${disposalHistory.value.length} 条处置记录、${attachmentList.value.length} 份附件资料。结合关联设备“${currentBasicInfo.value.deviceName || currentBasicInfo.value.deviceCode || "-"}”与电子存档情况,建议在上传报告时补充现场核查结论、报警峰值说明和后续处置建议。`;
- });
- function getLevelTagType(level) {
- return {
- "0": "danger",
- "1": "danger",
- "2": "warning",
- "3": "success",
- "4": "info"
- }[level] || "info";
- }
- function getStatusText(status) {
- return databaseStatusLabels.value[status] || status || "-"
- }
- function validateFile(file) {
- const allowExt = [".pdf", ".doc", ".docx", ".zip"];
- const lowerName = file.name.toLowerCase();
- const extOk = allowExt.some((ext) => lowerName.endsWith(ext));
- if (!extOk) {
- ElMessage.error("仅支持 .pdf、.doc、.docx、.zip 格式");
- return false;
- }
- if (file.size > 20 * 1024 * 1024) {
- ElMessage.error("文件大小不能超过20MB");
- return false;
- }
- return true;
- }
- function handleFileChange(file) {
- if (!file?.raw || !validateFile(file.raw)) {
- return;
- }
- uploadedFile.value = file.raw;
- }
- function handleDrop(event) {
- isDragOver.value = false;
- const file = event.dataTransfer.files[0];
- if (file && validateFile(file)) {
- uploadedFile.value = file;
- }
- }
- function removeFile() {
- uploadedFile.value = null;
- uploadRef.value?.clearFiles?.();
- }
- function formatFileSize(bytes) {
- if (!bytes) return "0 B";
- const units = ["B", "KB", "MB", "GB"];
- const index = Math.floor(Math.log(bytes) / Math.log(1024));
- return `${(bytes / 1024 ** index).toFixed(2)} ${units[index]}`;
- }
- function buildAttachmentUrl(path) {
- if (!path) return "";
- return buildMinioAttachmentUrl(path);
- }
- function normalizeWarningRow(item) {
- const warningId = item.warningId || item.warning_id;
- const warningLevel = item.warningLevel || item.warning_level;
- return {
- warningId,
- warningNo: item.warningNo || item.warning_no || "",
- warningType: item.warningType || item.warning_type,
- warningLevel,
- warningLevelText: warningLevelLabelMap.value[warningLevel] || warningLevel
- };
- }
- async function loadDicts() {
- const [typeRes, levelRes] = await Promise.all([
- getAlertTypeDictionaryData({ dictType: "alert_type" }),
- getAlertTypeDictionaryData({ dictType: "alert_level" })
- ]);
- warningTypeOptions.value = typeRes.rows || [];
- warningLevelOptions.value = levelRes.rows || [];
- }
- async function loadWarningOptions() {
- pageLoading.value = true;
- try {
- const res = await getdbyjlistData({
- pageNum: 1,
- pageSize: 200,
- warningNo: queryParams.value.warningNo || undefined,
- warningType: queryParams.value.warningType || undefined,
- warningLevel: queryParams.value.warningLevel || undefined,
- status: queryParams.value.status || undefined
- });
- warningOptions.value = (res.data?.records || []).map(normalizeWarningRow);
- if (!selectedWarningId.value && warningOptions.value.length) {
- selectedWarningId.value = warningOptions.value[0].warningId;
- await loadWarningDetail(selectedWarningId.value);
- } else if (selectedWarningId.value && !warningOptions.value.some((item) => item.warningId === selectedWarningId.value)) {
- selectedWarningId.value = "";
- warningDetail.value = {};
- renderCurveChart();
- }
- } finally {
- pageLoading.value = false;
- }
- }
- async function loadWarningDetail(warningId) {
- if (!warningId) {
- warningDetail.value = {};
- renderCurveChart();
- return;
- }
- pageLoading.value = true;
- try {
- const res = await getWarningFullDetailData(warningId);
- warningDetail.value = res.data || {};
- await nextTick();
- renderCurveChart();
- } finally {
- pageLoading.value = false;
- }
- }
- async function handleWarningChange(value) {
- await loadWarningDetail(value);
- }
- function buildChartData() {
- const alarms = [...alarmList.value].sort((a, b) => String(a.createTime || "").localeCompare(String(b.createTime || "")));
- return {
- xData: alarms.map((item) => String(item.createTime || "").slice(5, 16) || "-"),
- actualData: alarms.map((item) => Number(item.actual_value || item.actualValue || 0)),
- thresholdData: alarms.map((item) => {
- const raw = item.warning_value || item.warningValue || "";
- const numeric = String(raw).match(/-?\d+(\.\d+)?/);
- return numeric ? Number(numeric[0]) : null;
- })
- };
- }
- function renderCurveChart() {
- if (!curveChartRef.value) return;
- if (!curveChart) {
- curveChart = echarts.init(curveChartRef.value);
- window.addEventListener("resize", resizeChart);
- }
- const chartData = buildChartData();
- curveChart.setOption({
- tooltip: { trigger: "axis" },
- legend: { top: 0, data: ["实际值", "预警阈值"] },
- grid: { left: 40, right: 20, top: 36, bottom: 30, containLabel: true },
- xAxis: {
- type: "category",
- data: chartData.xData.length ? chartData.xData : ["暂无数据"]
- },
- yAxis: {
- type: "value",
- name: "数值"
- },
- series: [
- {
- name: "实际值",
- type: "line",
- smooth: true,
- data: chartData.actualData.length ? chartData.actualData : [0],
- lineStyle: { color: "#e6a23c", width: 2 },
- areaStyle: { color: "rgba(230,162,60,0.12)" }
- },
- {
- name: "预警阈值",
- type: "line",
- smooth: true,
- data: chartData.thresholdData.length ? chartData.thresholdData : [0],
- lineStyle: { color: "#f56c6c", type: "dashed", width: 2 }
- }
- ]
- });
- }
- function resizeChart() {
- curveChart?.resize();
- }
- async function submitReport() {
- if (!selectedWarningId.value) {
- ElMessage.warning("请先选择预警");
- return;
- }
- if (!uploadedFile.value) {
- ElMessage.warning("请先选择报告文件");
- return;
- }
- submitLoading.value = true;
- try {
- const res = await uploadWarningReportData(selectedWarningId.value, [uploadedFile.value]);
- if (res.code !== 200) {
- ElMessage.error(res.msg || "报告上传失败");
- return;
- }
- ElMessage.success("预警报告上传成功");
- removeFile();
- reportSummary.value = "";
- await loadWarningDetail(selectedWarningId.value);
- } finally {
- submitLoading.value = false;
- }
- }
- function resetQuery() {
- queryParams.value = {
- warningNo: "",
- warningType: "",
- warningLevel: "",
- status: ""
- };
- selectedWarningId.value = "";
- warningDetail.value = {};
- loadWarningOptions();
- }
- onMounted(async () => {
- await loadDicts();
- await loadWarningOptions();
- });
- onBeforeUnmount(() => {
- window.removeEventListener("resize", resizeChart);
- curveChart?.dispose();
- curveChart = null;
- });
- </script>
- <style scoped>
- .warning-report-page {
- background: #f5f7fb;
- }
- .page-card {
- border-radius: 16px;
- margin-bottom: 16px;
- }
- .header-card {
- background: linear-gradient(135deg, #eff6ff, #f8fbff);
- border: 1px solid #dbeafe;
- }
- .page-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 16px;
- }
- .page-title {
- font-size: 22px;
- font-weight: 700;
- color: #1e3a8a;
- }
- .page-subtitle {
- margin-top: 8px;
- font-size: 13px;
- color: #64748b;
- }
- .warning-option {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .report-container {
- display: grid;
- grid-template-columns: 360px minmax(0, 1fr);
- gap: 16px;
- }
- .section-card {
- border-radius: 16px;
- margin-bottom: 16px;
- }
- .section-title {
- font-size: 16px;
- font-weight: 600;
- color: #1f2937;
- }
- .upload-area {
- border: 2px dashed #dcdfe6;
- border-radius: 12px;
- padding: 28px 16px;
- text-align: center;
- transition: all 0.2s ease;
- background: #fafafa;
- }
- .upload-area.drag-over {
- border-color: #409eff;
- background: #ecf5ff;
- }
- .upload-icon {
- font-size: 46px;
- color: #909399;
- margin-bottom: 10px;
- }
- .upload-text {
- margin-bottom: 14px;
- }
- .primary-text {
- font-size: 14px;
- color: #374151;
- }
- .support-text {
- margin-top: 6px;
- font-size: 12px;
- color: #9ca3af;
- }
- .file-info,
- .attachment-list {
- margin-top: 16px;
- }
- .file-item,
- .attachment-item {
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 10px 12px;
- border-radius: 10px;
- background: #f8fafc;
- border: 1px solid #edf2f7;
- }
- .attachment-item + .attachment-item {
- margin-top: 8px;
- }
- .file-name,
- .attachment-name {
- flex: 1;
- font-size: 13px;
- color: #303133;
- }
- .file-size,
- .attachment-time {
- font-size: 12px;
- color: #909399;
- }
- .remove-file {
- color: #f56c6c;
- cursor: pointer;
- font-size: 16px;
- }
- .report-description {
- margin-top: 20px;
- }
- .field-title {
- margin-bottom: 10px;
- font-size: 14px;
- font-weight: 600;
- color: #374151;
- }
- .submit-btn {
- width: 100%;
- margin-top: 16px;
- }
- .curve-chart {
- width: 100%;
- height: 280px;
- margin-bottom: 16px;
- }
- .factors-grid {
- display: grid;
- grid-template-columns: repeat(4, minmax(0, 1fr));
- gap: 12px;
- margin-bottom: 16px;
- }
- .factor-card {
- padding: 16px;
- border-radius: 14px;
- background: #f8fafc;
- border: 1px solid #edf2f7;
- }
- .factor-label {
- font-size: 13px;
- color: #64748b;
- }
- .factor-value {
- margin: 10px 0 6px;
- font-size: 24px;
- font-weight: 700;
- color: #111827;
- }
- .factor-desc {
- font-size: 12px;
- color: #94a3b8;
- }
- .analysis-text-card {
- margin-top: 8px;
- }
- .warning-description {
- margin-top: 20px;
- background: #fef9e6;
- border-left: 4px solid #e6a23c;
- border-radius: 12px;
- padding: 16px 20px;
- }
- .desc-header {
- display: flex;
- align-items: center;
- gap: 8px;
- margin-bottom: 12px;
- }
- .desc-header h4 {
- margin: 0;
- font-size: 16px;
- font-weight: 600;
- color: #e6a23c;
- }
- .desc-content {
- font-size: 14px;
- color: #303133;
- line-height: 1.8;
- }
- .desc-content p {
- margin: 0 0 8px;
- }
- @media (max-width: 1200px) {
- .report-container {
- grid-template-columns: 1fr;
- }
- .factors-grid {
- grid-template-columns: repeat(2, minmax(0, 1fr));
- }
- }
- @media (max-width: 768px) {
- .factors-grid {
- grid-template-columns: 1fr;
- }
- }
- </style>
|