| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881 |
- <template>
- <div
- class="diagnosis-report-page"
- v-loading="pageLoading"
- :element-loading-text="loadingText"
- >
- <div class="diagnosis-header">
- <div class="diagnosis-header__title-row">
- <span class="diagnosis-header__title">种植报告</span>
- <img class="diagnosis-header__icon" src="@/assets/img/report/star.png" alt="" />
- </div>
- <div class="diagnosis-header__desc">{{ headerDesc }}</div>
- <div class="diagnosis-header__base">{{ headerBase }}</div>
- </div>
- <div class="diagnosis-content">
- <div class="variety-bar">
- <div class="variety-bar__tags">
- <div v-for="item in varietyList" :key="item.id" class="variety-bar__tag"
- :class="{ active: item.id === activeVarietyId }" @click="activeVarietyId = item.id">
- {{ item.name }}
- </div>
- </div>
- <div class="variety-bar__add">
- <el-icon>
- <Plus />
- </el-icon>
- <span>新增品种</span>
- </div>
- </div>
- <div class="base-data">
- <div class="base-data__title">
- <img class="base-data__icon" src="@/assets/img/report/base-icon.png" alt="" />
- <span>基本信息</span>
- </div>
- <div class="base-data__body">
- <div v-if="!loading && !baseInfoList.length" class="base-data__empty">暂无基本信息</div>
- <div v-for="item in baseInfoList" :key="item.key" class="base-info-item">
- <div class="base-info-item__row">
- <span class="base-info-item__label">{{ item.label }}</span>
- <span class="base-info-item__value">{{ item.value }}</span>
- <span v-if="item.actionInline" class="base-info-action"
- @click="handleBaseAction(item.actionInline.key)">
- {{ item.actionInline.text }}
- <el-icon><Edit /></el-icon>
- </span>
- </div>
- <div v-if="item.actionBlock" class="base-info-action base-info-action--block"
- @click="handleBaseAction(item.actionBlock.key)">
- {{ item.actionBlock.text }}
- <el-icon><Edit /></el-icon>
- </div>
- </div>
- </div>
- </div>
- <div class="report-module">
- <div class="report-tabs">
- <div
- v-for="tab in reportTabs"
- :key="tab.key"
- class="report-tabs__item"
- :class="{ active: tab.key === activeReportTab }"
- @click="activeReportTab = tab.key"
- >
- <img class="report-tabs__icon" :src="tab.icon" alt="" />
- <span class="report-tabs__label">{{ tab.label }}</span>
- </div>
- </div>
- <div class="report-panel">
- <div class="report-panel__title">{{ activeReportTabMeta.label }}</div>
- <div
- v-if="activeReportHtml"
- class="report-panel__body"
- v-html="activeReportHtml"
- ></div>
- <div v-else class="report-panel__empty">暂无报告内容</div>
- </div>
- </div>
- </div>
- <adjustPhenologyPopup ref="adjustPhenologyPopupRef" @confirm="handlePhenologyConfirm" />
- <restingDatePopup
- ref="restingDatePopupRef"
- @confirm="handleRestingDateConfirm"
- @switchGrowing="handleSwitchGrowing"
- />
- <editPlantPointPopup ref="editPlantPointPopupRef" @confirm="handlePlantPointConfirm" />
- <!-- 暂无农场时不弹「完善农场信息」,逻辑保留备用
- <complete-farm-info-popup
- v-model:show="showCompleteFarmPopup"
- :image="completeFarmPopupImage"
- highlight-text="种植全析报告"
- @confirm="goCompleteFarmInfo"
- />
- -->
- <restore-planting-popup />
- </div>
- </template>
- <script setup>
- import { computed, onActivated, onDeactivated, onBeforeUnmount, ref } from "vue";
- import { useRouter } from "vue-router";
- // import { useStore } from "vuex";
- import { ElMessage } from "element-plus";
- import { Plus, Edit } from "@element-plus/icons-vue";
- import adjustPhenologyPopup from "../components/adjustPhenologyPopup.vue";
- import restingDatePopup from "../components/restingDatePopup.vue";
- import editPlantPointPopup from "../components/editPlantPointPopup.vue";
- // import completeFarmInfoPopup from "@/components/popup/completeFarmInfoPopup.vue";
- import tab1Icon from "@/assets/img/report/tab-1.png";
- import tab2Icon from "@/assets/img/report/tab-2.png";
- import tab3Icon from "@/assets/img/report/tab-3.png";
- import tab4Icon from "@/assets/img/report/tab-4.png";
- import tab5Icon from "@/assets/img/report/tab-5.png";
- // import completeFarmPopupImage from "@/assets/img/common/empty-bg-3.png";
- import restorePlantingPopup from "@/components/popup/restorePlantingPopup.vue";
- // ---------------------------------------------------------------------------
- // Props / Emits — 无
- // ---------------------------------------------------------------------------
- const router = useRouter();
- // const store = useStore();
- // ---------------------------------------------------------------------------
- // 常量
- // ---------------------------------------------------------------------------
- /** 报告未生成时轮询间隔(ms) */
- const REPORT_POLL_INTERVAL = 6000;
- /** 无农场时默认作物编码:水稻 */
- const NO_FARM_CROP = "PAD";
- /** 无定位时的默认点位(与录入页一致) */
- const DEFAULT_POINT = "POINT(113.6142086995688 23.585836479509055)";
- /** module1_basic 字段标签 → 基本信息展示项(labels 含接口原文别名) */
- const BASE_FIELD_META = [
- { key: "crop", labels: ["作物品种", "种植作物"], label: "种植作物:", actionInline: { key: "changeVariety", text: "更改品种" } },
- { key: "trait", labels: ["品种特质", "品种优势"], label: "品种特质:" },
- { key: "phenology", labels: ["物候信息", "物候期"], label: "物候信息:", actionInline: { key: "adjustPhenology", text: "调整物候" } },
- { key: "soil", labels: ["土壤条件"], label: "土壤条件:", actionBlock: { key: "soilSense", text: "土壤感知" } },
- { key: "terrain", labels: ["地形条件"], label: "地形条件:", actionInline: { key: "editLocation", text: "修改定位" } },
- ];
- /** Tab key → report/check 返回字段 */
- const REPORT_TAB_FIELD = {
- weather: "module2_weather",
- pest: "module3_pest",
- quality: "module4_quality",
- input: "module5_input",
- manage: "module6_management",
- };
- const reportTabs = [
- { key: "weather", label: "气象风险", icon: tab1Icon },
- { key: "pest", label: "病虫风险", icon: tab2Icon },
- { key: "quality", label: "品质潜力", icon: tab3Icon },
- { key: "input", label: "必要投入", icon: tab4Icon },
- { key: "manage", label: "农事管理", icon: tab5Icon },
- ];
- // ---------------------------------------------------------------------------
- // 页面状态
- // ---------------------------------------------------------------------------
- const loading = ref(false); // 首次拉取中
- const reportGenerating = ref(false); // 报告尚未生成,持续 loading + 轮询
- const varietyList = ref([]);
- const activeVarietyId = ref(null);
- const headerDesc = ref("");
- const headerBase = ref("");
- const baseInfoList = ref([]);
- /** 各 Tab 对应的 markdown 原文 */
- const moduleMarkdownMap = ref({});
- const activeReportTab = ref("weather");
- const adjustPhenologyPopupRef = ref(null);
- const restingDatePopupRef = ref(null);
- const editPlantPointPopupRef = ref(null);
- // const showCompleteFarmPopup = ref(false); // 无农场时展示「完善农场信息」
- let pollTimer = null;
- /** 当前轮询方式:farm | latlon */
- let pollMode = null;
- let pollParams = null;
- // ---------------------------------------------------------------------------
- // 计算属性
- // ---------------------------------------------------------------------------
- const pageLoading = computed(() => loading.value || reportGenerating.value);
- const loadingText = "报告生成中,请稍后...";
- const activeReportTabMeta = computed(
- () => reportTabs.find((tab) => tab.key === activeReportTab.value) || reportTabs[0]
- );
- const activeReportHtml = computed(() => {
- const field = REPORT_TAB_FIELD[activeReportTab.value];
- const md = moduleMarkdownMap.value[field] || "";
- return md ? markdownToHtml(md) : "";
- });
- // ---------------------------------------------------------------------------
- // 工具函数
- // ---------------------------------------------------------------------------
- // function getMiniUserId() {
- // return store.state.home.miniUserId || localStorage.getItem("MINI_USER_ID");
- // }
- function getSelectedFarm() {
- try {
- return JSON.parse(localStorage.getItem("selectedFarmData") || "{}");
- } catch {
- return {};
- }
- }
- /** 与 diagnosisReportPopup 同源:标记该农场/分区弹窗已读 */
- const DIAGNOSIS_POPUP_SHOWN_PREFIX = "DIAGNOSIS_REPORT_POPUP_SHOWN_";
- function markDiagnosisPopupShown(farmId, zoneId) {
- if (farmId == null || farmId === "") return;
- const key = `${DIAGNOSIS_POPUP_SHOWN_PREFIX}${farmId}_${zoneId ?? ""}`;
- localStorage.setItem(key, "1");
- }
- function hasFarmZone(farm = {}) {
- const farmId = farm.farm_id ?? farm.id;
- const zoneId = farm.zone_id ?? farm.zoneId;
- return farmId != null && farmId !== "" && zoneId != null && zoneId !== "";
- }
- /** 无农场报告用点位:优先用户定位 */
- function getPointText() {
- return localStorage.getItem("MINI_USER_LOCATION_POINT") || DEFAULT_POINT;
- }
- /** report6_latlon:有 records[0] 即视为报告已生成 */
- function pickLatlonReport(res) {
- const records = res?.data?.records;
- if (Array.isArray(records) && records.length) return records[0];
- return null;
- }
- /** 解析 module1_basic:兼容 `**标签**:` 与纯文本 `标签:`(内容可跨行) */
- function parseBasicSections(markdown = "") {
- const text = String(markdown).replace(/\r\n/g, "\n").trim();
- if (!text) return {};
- const collect = (re) => {
- const matches = [...text.matchAll(re)];
- if (!matches.length) return null;
- const sections = {};
- matches.forEach((match, index) => {
- const label = String(match[1] || "").trim();
- const start = match.index + match[0].length;
- const end = index + 1 < matches.length ? matches[index + 1].index : text.length;
- if (label) sections[label] = text.slice(start, end).trim();
- });
- return sections;
- };
- // 1) markdown:**作物品种**:xxx
- const mdSections = collect(/\*\*([^*]+)\*\*[::]\s*/g);
- if (mdSections) return mdSections;
- // 2) 纯文本:作物品种:xxx(按已知标签切分,避免正文里的冒号误伤)
- const knownLabels = [...new Set(BASE_FIELD_META.flatMap((meta) => meta.labels))];
- const escaped = knownLabels
- .map((label) => label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
- .join("|");
- if (!escaped) return {};
- return collect(new RegExp(`(?:^|\\n)(${escaped})[::]\\s*`, "g")) || {};
- }
- function findSectionValue(sections, labels) {
- for (const label of labels) {
- if (sections[label]) return sections[label];
- }
- return "";
- }
- function buildBaseInfoList(sections, report = {}) {
- return BASE_FIELD_META.map((meta) => {
- let value = findSectionValue(sections, meta.labels);
- // 种植作物兜底:接口顶层 crop / variety
- if (!value && meta.key === "crop") {
- const crop = report.crop || "";
- const variety = report.variety || "";
- value = [crop, variety].filter(Boolean).join("-");
- }
- return {
- key: meta.key,
- label: meta.label,
- value: value || "—",
- actionInline: meta.actionInline,
- actionBlock: meta.actionBlock,
- };
- });
- }
- /** 轻量 markdown → HTML(标题 / 粗体 / 列表) */
- function markdownToHtml(markdown = "") {
- const escapeHtml = (str) =>
- String(str)
- .replace(/&/g, "&")
- .replace(/</g, "<")
- .replace(/>/g, ">");
- const lines = String(markdown).replace(/\r\n/g, "\n").split("\n");
- const html = [];
- let inList = false;
- const flushList = () => {
- if (!inList) return;
- html.push("</ul>");
- inList = false;
- };
- const inline = (str) =>
- escapeHtml(str).replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
- lines.forEach((raw) => {
- const line = raw.trimEnd();
- const trimmed = line.trim();
- if (!trimmed) {
- flushList();
- return;
- }
- if (/^###\s+/.test(trimmed)) {
- flushList();
- html.push(`<h4>${inline(trimmed.replace(/^###\s+/, ""))}</h4>`);
- return;
- }
- if (/^##\s+/.test(trimmed)) {
- flushList();
- html.push(`<h3>${inline(trimmed.replace(/^##\s+/, ""))}</h3>`);
- return;
- }
- if (/^#\s+/.test(trimmed)) {
- flushList();
- html.push(`<h2>${inline(trimmed.replace(/^#\s+/, ""))}</h2>`);
- return;
- }
- if (/^[-*]\s+/.test(trimmed)) {
- if (!inList) {
- html.push("<ul>");
- inList = true;
- }
- html.push(`<li>${inline(trimmed.replace(/^[-*]\s+/, ""))}</li>`);
- return;
- }
- flushList();
- html.push(`<p>${inline(trimmed)}</p>`);
- });
- flushList();
- return html.join("");
- }
- function applyReportData(report, farm) {
- const region = report.region || farm.rawAddress || farm.farm_address || farm.city || "";
- const farmName = farm.farm_name || farm.name || "";
- headerDesc.value = region ? `${region}的飞鸟智慧农业` : "飞鸟智慧农业";
- headerBase.value = farmName || "我的基地";
- const cropName = report.variety || report.crop || farm.variety_name || farm.categoryLabel || "作物";
- varietyList.value = [{ id: report.id || 1, name: cropName }];
- activeVarietyId.value = varietyList.value[0].id;
- const sections = parseBasicSections(report.module1_basic || "");
- baseInfoList.value = buildBaseInfoList(sections, report);
- moduleMarkdownMap.value = {
- module2_weather: report.module2_weather || "",
- module3_pest: report.module3_pest || "",
- module4_quality: report.module4_quality || "",
- module5_input: report.module5_input || "",
- module6_management: report.module6_management || "",
- };
- }
- function resetReportView(farm = {}) {
- headerDesc.value = farm.rawAddress || farm.farm_address || farm.city
- ? `${farm.rawAddress || farm.farm_address || farm.city}的飞鸟智慧农业`
- : "飞鸟智慧农业";
- headerBase.value = farm.farm_name || farm.name || "我的基地";
- varietyList.value = [];
- activeVarietyId.value = null;
- baseInfoList.value = [];
- moduleMarkdownMap.value = {};
- }
- // ---------------------------------------------------------------------------
- // 业务逻辑:无农场弹窗 / 加载报告
- // ---------------------------------------------------------------------------
- /** 无果园信息时展示「完善农场信息」弹窗(暂不启用) */
- // async function checkCompleteFarmPopup() {
- // const id = getMiniUserId();
- // if (!id) {
- // showCompleteFarmPopup.value = false;
- // return;
- // }
- // try {
- // const res = await VE_API.questionnaire.getFarms({ id });
- // const list = Array.isArray(res?.data) ? res.data : [];
- // showCompleteFarmPopup.value = list.length === 0;
- // } catch (e) {
- // console.warn("[diagnosisReport] getFarms failed", e);
- // showCompleteFarmPopup.value = false;
- // }
- // }
- function stopReportPolling() {
- if (pollTimer) {
- clearInterval(pollTimer);
- pollTimer = null;
- }
- pollMode = null;
- pollParams = null;
- }
- /** 报告未就绪时保持 loading,并定时重试 */
- function startReportPolling(mode, params) {
- stopReportPolling();
- pollMode = mode;
- pollParams = params;
- pollTimer = setInterval(() => {
- if (pollMode === "farm") {
- pollFarmReport(pollParams);
- } else if (pollMode === "latlon") {
- pollLatlonReport(pollParams);
- }
- }, REPORT_POLL_INTERVAL);
- }
- async function pollFarmReport({ farmId, zoneId }) {
- try {
- const raw = await VE_API.questionnaire.checkReportGenerated({
- farm_id: farmId,
- zone_id: zoneId,
- });
- const res = Array.isArray(raw) ? raw[0] : raw;
- const report = res?.data?.report;
- if (res?.code === 200 && report) {
- // 进入报告页并拿到数据:标记该农场弹窗已读,避免再弹
- markDiagnosisPopupShown(farmId, zoneId);
- stopReportPolling();
- reportGenerating.value = false;
- applyReportData(report, getSelectedFarm());
- }
- } catch (error) {
- console.warn("[diagnosisReport] farm poll failed", error);
- }
- }
- async function pollLatlonReport({ pointText, crop }) {
- try {
- const raw = await VE_API.report.report6Latlon({
- point_text: pointText,
- crop: '水稻',
- });
- const res = Array.isArray(raw) ? raw[0] : raw;
- const report = pickLatlonReport(res);
- if (res?.code === 200 && report) {
- stopReportPolling();
- reportGenerating.value = false;
- applyReportData(report, {});
- }
- } catch (error) {
- console.warn("[diagnosisReport] latlon poll failed", error);
- }
- }
- /** 无农场:按定位点 + 水稻拉取 / 轮询六模块报告 */
- async function fetchLatlonReport() {
- const pointText = getPointText();
- const crop = NO_FARM_CROP;
- const params = { pointText, crop };
- loading.value = true;
- try {
- const raw = await VE_API.report.report6Latlon({
- point_text: pointText,
- crop: '水稻',
- });
- const res = Array.isArray(raw) ? raw[0] : raw;
- const report = pickLatlonReport(res);
- if (res?.code === 200 && report) {
- applyReportData(report, {});
- return;
- }
- resetReportView({});
- reportGenerating.value = true;
- startReportPolling("latlon", params);
- } catch (error) {
- console.error("fetchLatlonReport failed", error);
- resetReportView({});
- reportGenerating.value = false;
- ElMessage.error("报告加载失败");
- } finally {
- loading.value = false;
- }
- }
- async function fetchFarmReport(farm) {
- const farmId = farm.farm_id ?? farm.id;
- const zoneId = farm.zone_id ?? farm.zoneId;
- loading.value = true;
- try {
- const raw = await VE_API.questionnaire.checkReportGenerated({
- farm_id: farmId,
- zone_id: zoneId,
- });
- const res = Array.isArray(raw) ? raw[0] : raw;
- const report = res?.data?.report;
- if (res?.code === 200 && report) {
- markDiagnosisPopupShown(farmId, zoneId);
- applyReportData(report, farm);
- return;
- }
- resetReportView(farm);
- reportGenerating.value = true;
- startReportPolling("farm", { farmId, zoneId });
- } catch (error) {
- console.error("fetchFarmReport failed", error);
- resetReportView(farm);
- reportGenerating.value = false;
- ElMessage.error("报告加载失败");
- } finally {
- loading.value = false;
- }
- }
- async function fetchDiagnosisReport() {
- const farm = getSelectedFarm();
- stopReportPolling();
- reportGenerating.value = false;
- if (!hasFarmZone(farm)) {
- await fetchLatlonReport();
- return;
- }
- await fetchFarmReport(farm);
- }
- // ---------------------------------------------------------------------------
- // 事件处理 / 生命周期
- // ---------------------------------------------------------------------------
- const handleBaseAction = (key) => {
- if (key === "adjustPhenology") {
- adjustPhenologyPopupRef.value?.open();
- return;
- }
- if (key === "editLocation") {
- editPlantPointPopupRef.value?.open();
- return;
- }
- if (key === "soilSense") {
- router.push("/soil_sense");
- return;
- }
- console.log("base action:", key);
- };
- const handlePhenologyConfirm = (payload) => {
- restingDatePopupRef.value?.open(payload);
- };
- const handleSwitchGrowing = () => {
- adjustPhenologyPopupRef.value?.open();
- };
- const handleRestingDateConfirm = (payload) => {
- console.log("resting date confirm:", payload);
- };
- const handlePlantPointConfirm = (payload) => {
- console.log("plant point confirm:", payload);
- };
- // const goCompleteFarmInfo = () => {
- // router.push("/entry_information");
- // };
- // tabbar 切换时组件被 keep-alive 缓存,需在激活时重新拉报告 / 校验农场
- onActivated(() => {
- // checkCompleteFarmPopup(); // 暂无农场时不弹「完善农场信息」
- fetchDiagnosisReport();
- });
- onDeactivated(() => {
- stopReportPolling();
- });
- onBeforeUnmount(() => {
- stopReportPolling();
- });
- </script>
- <style lang="scss" scoped>
- .diagnosis-report-page {
- width: 100%;
- height: calc(100vh - 50px);
- overflow: auto;
- -webkit-overflow-scrolling: touch;
- background: #f5f6fe;
- box-sizing: border-box;
- padding-bottom: 20px;
- .diagnosis-header {
- height: 148px;
- padding: 26px 0 0 14px;
- box-sizing: border-box;
- background: url("@/assets/img/report/title-bg.png") no-repeat center center / 100% 100%;
- color: #fff;
- &__title-row {
- display: flex;
- align-items: center;
- gap: 6px;
- }
- &__title {
- font-size: 35px;
- font-family: "pangmenzhengdao";
- }
- &__icon {
- width: 24px;
- height: 24px;
- }
- }
- .diagnosis-content {
- padding: 0 10px;
- .variety-bar {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 10px 6px;
- background: #fff;
- border-radius: 8px;
- margin-top: -20px;
- &__tags {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: 6px;
- }
- &__tag {
- padding: 5px 16px;
- border-radius: 4px;
- background: #f2f3f5;
- color: #4e5969;
- &.active {
- background: #2199f8;
- color: #fff;
- }
- }
- &__add {
- display: flex;
- align-items: center;
- gap: 2px;
- padding: 5px 10px;
- border-radius: 4px;
- border: 1px solid #2199f8;
- background: rgba(33, 153, 248, 0.1);
- color: #2199f8;
- }
- }
- .base-data {
- margin: 10px 0;
- padding: 5px;
- // min-height: 382px;
- min-height: auto;
- background: url("@/assets/img/report/base-bg.png") no-repeat center top / 100% 100%;
- box-sizing: border-box;
- &__title {
- display: flex;
- align-items: center;
- gap: 5px;
- font-size: 17px;
- color: #fff;
- margin: -3px 0 0 -3px;
- }
- &__icon {
- width: 44px;
- height: 44px;
- }
- &__title>span {
- margin-top: -4px;
- }
- &__body {
- margin-top: -8px;
- padding: 10px;
- background: #fff;
- border-radius: 8px;
- .base-data__empty {
- padding: 20px 0;
- text-align: center;
- color: #999;
- font-size: 13px;
- }
- .base-info-item {
- margin-bottom: 8px;
- &__row {
- color: #333;
- }
- &__label {
- color: #999;
- }
- &__value {
- color: #333;
- }
- }
- .base-info-action {
- display: inline-flex;
- align-items: center;
- gap: 2px;
- margin-left: 6px;
- padding: 2px 8px;
- border-radius: 20px;
- background: rgba(33, 153, 248, 0.1);
- color: #2199f8;
- font-size: 12px;
- .el-icon {
- font-size: 12px;
- }
- &--block {
- display: inline-flex;
- margin: 8px 0 0;
- }
- }
- }
- }
- .report-module {
- margin-top: 4px;
- }
- .report-tabs {
- display: flex;
- align-items: stretch;
- gap: 8px;
- overflow-x: auto;
- -webkit-overflow-scrolling: touch;
- scrollbar-width: none;
- &::-webkit-scrollbar {
- display: none;
- }
- &__item {
- flex: 0 0 calc((100% - 24px) / 4);
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 5px 5px 14px;
- box-sizing: border-box;
- color: #ADADAD;
- background: url("@/assets/img/report/tab-bg.png") no-repeat center center / 100% 100%;
- &.active {
- background: url("@/assets/img/report/tab-act-bg.png") no-repeat center center / 100% 100%;
- &::after {
- opacity: 1;
- }
- .report-tabs__icon {
- filter: none;
- opacity: 1;
- }
- }
- }
- &__icon {
- width: 22px;
- height: 22px;
- filter: grayscale(1);
- opacity: 0.45;
- }
- }
- .report-panel {
- margin-top: 10px;
- padding: 16px 14px 18px;
- background: #fff;
- border-radius: 10px;
- &__title {
- position: relative;
- display: inline-block;
- font-size: 18px;
- font-weight: 600;
- color: #1d2129;
- line-height: 1.3;
- z-index: 1;
- &::after {
- content: "";
- position: absolute;
- left: 0;
- bottom: 2px;
- width: 36px;
- height: 8px;
- border-radius: 8px;
- background: linear-gradient(90deg, #8ed0ff 0%, rgba(142, 208, 255, 0.35) 100%);
- z-index: -1;
- }
- }
- &__empty {
- margin-top: 14px;
- padding: 24px 0;
- text-align: center;
- color: #999;
- font-size: 13px;
- }
- &__body {
- margin-top: 12px;
- color: #333;
- font-size: 14px;
- line-height: 1.7;
- word-break: break-word;
- :deep(h2) {
- margin: 0 0 10px;
- font-size: 16px;
- font-weight: 600;
- color: #1d2129;
- }
- :deep(h3) {
- margin: 14px 0 8px;
- font-size: 15px;
- font-weight: 600;
- color: #1d2129;
- }
- :deep(h4) {
- margin: 12px 0 6px;
- font-size: 14px;
- font-weight: 600;
- color: #4e5969;
- }
- :deep(p) {
- margin: 0 0 8px;
- }
- :deep(ul) {
- margin: 0 0 10px;
- padding-left: 18px;
- }
- :deep(li) {
- margin-bottom: 4px;
- }
- :deep(strong) {
- font-weight: 600;
- color: #1d2129;
- }
- }
- }
- }
- }
- </style>
|