提交 50739242 authored 作者: 张烨's avatar 张烨

feat:市场准入限制-概览页图表增加ai智能总结

上级 5d43cdf1
......@@ -3,7 +3,10 @@
<div class="icon">
<img src="./tip-icon.svg" alt="">
</div>
<div class="text text-tip-2 text-primary-50-clor">{{ tipText }}</div>
<div class="text text-tip-2 text-primary-50-clor">
<div v-if="ellipsis" :title="tipText" class="one-line-ellipsis">{{ tipText }}</div>
<div v-else>{{ tipText }}</div>
</div>
</div>
</template>
......@@ -23,7 +26,10 @@ const props = defineProps({
type: String,
default: '2023.1至2025.12'
},
ellipsis: {
type: Boolean,
default: false
}
})
const tipText = computed(() => props.text || `数据来源:${props.dataSource},数据时间:${props.dataTime}`)
......@@ -48,5 +54,9 @@ const tipText = computed(() => props.text || `数据来源:${props.dataSource}
height: 100%;
}
}
.text {
width: 20px;
flex: auto;
}
}
</style>
\ No newline at end of file
......@@ -152,7 +152,7 @@
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="summarize1" />
<AiPane :aiContent="aiContent.content1" />
</div>
</div>
......@@ -188,7 +188,7 @@
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="summarize2" />
<AiPane :aiContent="aiContent.content2" />
</div>
</div>
</div>
......@@ -442,6 +442,7 @@ import setChart from "@/utils/setChart";
import DefaultIcon2 from "@/assets/icons/default-icon2.png";
import tipsTcon from "./assets/images/tips-icon.png";
import { ElMessage } from "element-plus";
import { getAIReport, getNearYearList } from "@/views/marketAccessRestrictions/utils/index.ts"
import { useGotoNewsDetail } from '@/router/modules/news';
......@@ -722,17 +723,16 @@ const handleClickPerson = async item => {
} catch (error) { }
};
// 获取最近年份列表
const currentYear = new Date().getFullYear();
const getYearList = (count = 6) => {
const yearOptions = [];
for (let i = 0; i < count; i++) {
const year = currentYear - i;
yearOptions.push({ label: year.toString(), value: year.toString() });
}
return yearOptions;
};
const yearList = getYearList();
const yearList = getNearYearList();
// 获取AI智能报告
const aiContent = reactive({
content1: "正在生成...",
content2: "正在生成...",
})
const onAIReport = (data, key) => {
getAIReport(data).then(res => { aiContent[key] = res })
}
// 行政令发布频度
const chart1Data = ref({
......@@ -745,7 +745,6 @@ const box5Params = reactive({
proposeName: '',
loading: false,
})
const summarize1 = ref()
const handleGetDecreeYearOrder = async () => {
box5Params.loading = true
try {
......@@ -763,61 +762,17 @@ const handleGetDecreeYearOrder = async () => {
chart1Data.value.dataY = res.data.map(item => {
return item.count;
});
onChartInterpretation({ type: "柱状图", name: "数量变化趋势", data: res.data }, summarize1)
onAIReport({ type: "柱状图", name: "数量变化趋势", data: res.data }, "content1")
} else {
chart1Data.value.dataX = [];
chart1Data.value.dataY = [];
aiContent.content1 = ""
}
} catch (error) {
console.error("行政令发布频度error", error);
}
box5Params.loading = false
};
// AI智能总结
const onChartInterpretation = async (text, param) => {
param.value = "正在生成..."
// 👇 新增:超时 + 终止请求(只加这一段)
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000); // 10秒超时
try {
const response = await fetch('/aiAnalysis/chart_interpretation', {
method: 'POST',
headers: {
"X-API-Key": "aircasKEY19491001",
'Content-Type': 'application/json',
},
body: JSON.stringify({ text }),
signal: controller.signal // 👇 新增:绑定中断信号
});
clearTimeout(timeout); // 👇 新增:请求成功清除定时器
if (!response.ok) throw new Error(`HTTP 错误 ${response.status}`);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let summarize = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const content = line.substring(6);
const textMatch = content.match(/"解读":\s*"([^"]*)"/);
if (textMatch && textMatch[1]) summarize = textMatch[1];
}
}
}
param.value = summarize
} catch (err) {
param.value = "系统异常,生成失败";
}
}
const handleBox5 = async () => {
await handleGetDecreeYearOrder();
......@@ -863,7 +818,6 @@ const box6Params = reactive({
proposeName: '',
loading: false,
});
const summarize2 = ref()
const handleGetDecreeArea = async () => {
box6Params.loading = true
try {
......@@ -880,7 +834,10 @@ const handleGetDecreeArea = async () => {
value: item.count
};
});
onChartInterpretation({ type: "环形图", name: "领域分布情况", data: res.data }, summarize2)
onAIReport({ type: "环形图", name: "领域分布情况", data: res.data }, "content2")
} else {
chart2Data.value = []
aiContent.content2 = ""
}
} catch (error) {
console.error("政令科技领域error", error);
......
......@@ -6,7 +6,7 @@
<div class="data-item" v-for="(item, index) in props.listData" :key="index">
<div class="item-head">
<div class="item-name">{{ `(${onNumToChinese(Number(index)+1)}). ${item.title}` }}</div>
<div class="button-box" @click="onNavigateTo(item)">
<div class="button-box" @click="onNavigateTo()">
<div class="button-icon">
<img src="../assets/icons/open.png" alt="" />
</div>
......@@ -48,8 +48,7 @@ const props = defineProps({
}
})
const onNavigateTo = (item:any) => {
console.log(item)
const onNavigateTo = () => {
const page = router.resolve({
name: "MarketSingleReportOriginal",
query: { ...route.query }
......
......@@ -15,27 +15,45 @@ const getBarChart = (nameList, valueList) => {
},
yAxis: {
type: 'value',
splitLine: {
show: false
name: "项",
nameLocation: 'end',
nameGap: 12,
nameTextStyle: {
color: '#666',
fontSize: 14,
fontWeight: 400,
padding: [0, 0, 6, -26]
},
show: false
axisLabel: {
formatter: '{value}',
color: '#666',
fontSize: 14,
fontWeight: 400
},
xAxis: {
type: 'category',
data: nameList.map(item => {
return item.name
}),
splitLine: {
show: false
show: true,
lineStyle: {
color: '#e7f3ff',
type: 'dashed',
}
},
axisTick: {
show: false
},
xAxis: {
type: 'category',
data: nameList.map(item => item.name),
axisLine: {
show: false
show: true,
lineStyle: {
color: '#e7f3ff',
},
},
axisLabel: {
show: true
show: true,
textStyle: {
color: 'rgba(95, 101, 108, 1)',
fontFamily: 'Microsoft YaHei',
fontsize: 14,
}
}
},
series: [{
......
......@@ -11,12 +11,12 @@
<el-option label="调查中" value="1" />
<el-option label="调查结束" value="0" />
</el-select>
<el-select v-model="filterParty" placeholder="全部原告/被告" class="filter-select" clearable>
<!-- <el-select v-model="filterParty" placeholder="全部原告/被告" class="filter-select" clearable>
<el-option label="全部原告/被告" value="" />
</el-select>
<el-select v-model="filterReason" placeholder="全部原因" class="filter-select" clearable>
<el-option label="全部原因" value="" />
</el-select>
</el-select> -->
</div>
</div>
<div class="select-box">
......
......@@ -11,12 +11,12 @@
<el-option label="调查中" value="1" />
<el-option label="调查结束" value="0" />
</el-select>
<el-select v-model="filterParty" placeholder="全部原告/被告" class="filter-select" clearable>
<!-- <el-select v-model="filterParty" placeholder="全部原告/被告" class="filter-select" clearable>
<el-option label="全部原告/被告" value="" />
</el-select>
<el-select v-model="filterReason" placeholder="全部原因" class="filter-select" clearable>
<el-option label="全部原因" value="" />
</el-select>
</el-select> -->
</div>
</div>
<div class="select-box">
......
......@@ -22,8 +22,8 @@
</div>
</div>
<div class="content-list">
<div class="content-item">
<AnalysisBox title="232调查数量年度变化趋势">
<div class="content-item" v-loading="box1Loading">
<AnalysisBox title="数量变化趋势">
<template #header-btn>
<div class="header-btn-box">
<ActionButton :type="activeName === '1' ? 'active' : 'normal'" name="发起调查" @click="onStatNum('1')"></ActionButton>
......@@ -32,33 +32,48 @@
</template>
<div class="box-main">
<div class="box-head" ref="chart1"></div>
<TipTab style="margin-top: 16px;" />
<TipTab text="美对华232调查案件的数量变化趋势,数据来源:美国商务部官网" style="margin-top: 16px;" />
</div>
</AnalysisBox>
</div>
<div class="content-item">
<AnalysisBox title="调查案件领域分布">
<div class="content-item" v-loading="box2Loading">
<AnalysisBox title="领域分布情况">
<template #header-btn>
<el-select v-model="box2Paarams.years" @change="handleGetStatArea()" placeholder="选择时间" style="width:120px; margin-right:12px;">
<el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
<div class="box-main">
<div class="box-head" ref="chart2"></div>
<TipTab style="margin-top: -16px;" />
<TipTab text="美对华232调查案件的领域分布情况,数据来源:美国商务部官网" style="margin-top: -16px;" />
</div>
</AnalysisBox>
</div>
</div>
<div class="content-list">
<div class="content-item">
<AnalysisBox title="关税税率">
<div class="content-item" v-loading="box3Loading">
<AnalysisBox title="关税变化分布">
<template #header-btn>
<el-select v-model="box3Paarams.years" @change="onSearchTariff()" placeholder="选择时间" style="width:120px; margin-right:12px;">
<el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
<div class="box-main">
<div class="box-head" ref="chart3"></div>
<TipTab style="margin-top: -16px;" />
<TipTab text="美对华232调查案件导致的关税变化分布,数据来源:美国商务部官网" style="margin-top: -16px;" />
</div>
</AnalysisBox>
</div>
<div class="content-item">
<AnalysisBox title="被调查国家分布">
<div class="content-item" v-loading="box4Loading">
<AnalysisBox title="国家分布情况">
<template #header-btn>
<el-select v-model="box4Paarams.years" @change="handleGetSearchCountry()" placeholder="选择时间" style="width:120px; margin-right:12px;">
<el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
<div class="box-main">
<div class="box-head" ref="chart4"></div>
<TipTab style="margin-top: -16px;" />
<TipTab text="美232调查所涉及的国家分布情况,数据来源:美国商务部官网" style="margin-top: -16px;" />
</div>
</AnalysisBox>
</div>
......@@ -66,7 +81,7 @@
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from "vue";
import { ref, onMounted, nextTick, reactive } from "vue";
import {
getStatCount,
getStatArea,
......@@ -77,6 +92,8 @@ import {
import createLineChart from "@/views/marketAccessRestrictions/utils/baseLineChart";
import createPieChart from "@/views/marketAccessRestrictions/utils/basePiechart.js";
import { getNearYearList } from "@/views/marketAccessRestrictions/utils/index.ts";
const yearList = getNearYearList();
// 数量统计
const totalCaseNum = ref(0)
......@@ -96,71 +113,106 @@ const handleGetStat = async () => {
} catch (error) {}
}
const activeName = ref('1');
const chart1 = ref(null);
const activeName = ref('1');
const box1Loading = ref(false);
const onStatNum = async (event) => {
if (event) activeName.value = event;
box1Loading.value = true;
let chart1Data = { title: [], list: [] };
try {
const res = await getStatNum({ sortCode: 232, searchStatus:activeName.value })
console.log('232调查数量年度变化趋势', res);
console.log('数量变化趋势', res);
if (res.code === 200 && res.data) {
const chart1Data = {
title: res.data.map(item => item.searchYorM),
list: [
{
name: "232调查", value: res.data.map(item => item.searchCount)
}
]
};
nextTick(() => { createLineChart(chart1, chart1Data) });
chart1Data.title = res.data.map(item => item.searchYorM),
chart1Data.list = [{ name: "232调查", value: res.data.map(item => item.searchCount) }]
} else {
chart1Data.title = [];
chart1Data.list = [];
}
} catch (error) {
chart1Data.title = [];
chart1Data.list = [];
}
nextTick(() => { createLineChart(chart1, chart1Data) });
box1Loading.value = false;
}
const chart2 = ref(null);
const box2Paarams = reactive({
sortCode: 232,
years: '2025'
})
const box2Loading = ref(false);
const handleGetStatArea = async () => {
box2Loading.value = true;
let chart2Data = []
try {
const res = await getStatArea({ sortCode: "232" });
console.log('调查案件领域分布', res);
const res = await getStatArea(box2Paarams);
console.log('领域分布情况', res);
if (res.code === 200 && res.data) {
let chart2Data = res.data.map(item => ({ name: item.areaname, value: item.areacount }) );
nextTick(() => { createPieChart(chart2, chart2Data) });
chart2Data = res.data.map(item => ({ name: item.areaname, value: item.areacount }) );
} else {
chart2Data = [];
}
} catch (error) {
console.error("获取调查案件领域分布失败", error);
chart2Data = [];
}
nextTick(() => { createPieChart(chart2, chart2Data) });
box2Loading.value = false;
};
const chart3 = ref(null);
const box3Paarams = reactive({
sortCode: 232,
years: '2025'
})
const box3Loading = ref(false);
const onSearchTariff = async () => {
box3Loading.value = true;
let chart3Data = []
try {
const res = await getSearchTariff({ sortCode: "232" });
console.log('关税税率', res);
const res = await getSearchTariff(box3Paarams);
console.log('关税变化分布', res);
if (res.code === 200 && res.data?.length) {
const chart3Data = [
chart3Data = [
{ name: "税率25%+", value: res.data[0].TARIFF25 },
{ name: "税率11%-25%", value: res.data[0].TARIFF11 },
{ name: "税率1%-10%", value: res.data[0].TARIFF1 },
{ name: "税率0%", value: res.data[0].TARIFF0 },
];
nextTick(() => { createPieChart(chart3, chart3Data) });
} else {
chart3Data = [];
}
}catch (error) {}
}catch (error) {
chart3Data = [];
}
nextTick(() => { createPieChart(chart3, chart3Data) });
box3Loading.value = false;
}
const chart4 = ref(null);
const box4Paarams = reactive({
sortCode: 232,
years: '2025'
})
const box4Loading = ref(false);
const handleGetSearchCountry = async () => {
box4Loading.value = true;
let chart4Data = []
try {
const res = await getSearchCountry({ sortCode: 232 });
const res = await getSearchCountry(box4Paarams);
console.log('国家分布情况', res);
if (res.code === 200 && res.data) {
let chart4Data = res.data.map(item => ({ name: item.COUNTRY, value: item.NUM }) );
nextTick(() => { createPieChart(chart4, chart4Data) });
chart4Data = res.data.map(item => ({ name: item.COUNTRY, value: item.NUM }) );
} else {
chart4Data = []
}
} catch (error) {
console.error("获取受调查国家分布失败", error);
chart4Data = []
}
nextTick(() => { createPieChart(chart4, chart4Data) });
box4Loading.value = false;
};
onMounted(() => {
......@@ -168,7 +220,6 @@ onMounted(() => {
onStatNum()
handleGetStatArea()
onSearchTariff()
handleGetSearchCountry()
});
</script>
......
......@@ -2,44 +2,49 @@
<div class="wrap">
<div class="content-list">
<div class="content-item" v-loading="box1Loading">
<AnalysisBox title="对华301调查年度数量趋势">
<AnalysisBox title="数量变化趋势">
<template #header-btn>
<div class="warning-text">{{ `${inProgressCount}项调查仍在进行中` }}</div>
</template>
<div class="box-main">
<div class="box-head" ref="box1Chart"></div>
<TipTab style="margin-top: 16px" />
<TipTab text="美对华301调查案件的数量变化趋势,数据来源:美国贸易代表办公室官网" style="margin-top: 16px" />
</div>
</AnalysisBox>
</div>
<div class="content-item" v-loading="box2Loading">
<AnalysisBox title="301调查国家分布">
<AnalysisBox title="国家分布情况">
<template #header-btn>
<el-select v-model="box2Paarams.years" @change="handleGetSearchCountry()" placeholder="选择时间" style="width:120px; margin-right:12px;">
<el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
<div class="box-main">
<div class="box-head" id="box2Chart"></div>
<TipTab style="margin-top: 16px" />
<TipTab text="美301调查所涉及的国家分布情况,数据来源:美国贸易代表办公室官网" style="margin-top: 16px" />
</div>
</AnalysisBox>
</div>
</div>
<div class="content-list">
<div class="content-item" v-loading="box3Loading">
<AnalysisBox title="301调查方向及结果分布">
<AnalysisBox title="调查方向及结果分布">
<div class="box-main">
<div class="box-head" id="box3Chart"></div>
<TipTab style="margin-top: 16px;" />
<TipTab text="美301调查方向及结果分布情况,数据来源:美国贸易代表办公室官网" style="margin-top: 16px;" />
</div>
</AnalysisBox>
</div>
<div class="content-item" v-loading="box4Loading">
<AnalysisBox title="301调查领域分布">
<AnalysisBox title="领域分布情况">
<template #header-btn>
<el-select v-model="selectYear" @change="handleGetStatArea()" placeholder="选择时间" style="width:120px; margin-right:12px;">
<el-select v-model="box4Paarams.years" @change="handleGetStatArea()" placeholder="选择时间" style="width:120px; margin-right:12px;">
<el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
<div class="box-main">
<div class="box-head" ref="box4Chart"></div>
<TipTab style="margin-top: -16px;" />
<TipTab text="美对华301调查案件的领域分布情况,数据来源:美国贸易代表办公室官网" style="margin-top: -16px;" />
</div>
</AnalysisBox>
</div>
......@@ -48,7 +53,7 @@
</template>
<script setup>
import { ref, onMounted, nextTick } from "vue";
import { ref, onMounted, nextTick, reactive } from "vue";
import setChart from "@/utils/setChart";
import getBarChart from "./utils/barChart";
import getSankeyChart from "./utils/sankey";
......@@ -60,48 +65,76 @@ import {
} from "@/api/marketAccessRestrictions";
import createLineChart from "@/views/marketAccessRestrictions/utils/baseLineChart";
import createPieChart from "@/views/marketAccessRestrictions/utils/basePiechart.js";
import { getNearYearList } from "@/views/marketAccessRestrictions/utils/index.ts";
const yearList = getNearYearList();
const inProgressCount = ref(0);
const box1Loading = ref(false);
const box2Loading = ref(false);
const box3Loading = ref(false);
const box4Loading = ref(false);
const box1Chart = ref(null);
const box1ChartData = ref({
title: [],
list: []
});
const box1Chart = ref(null);
const box1Loading = ref(false);
const handleGetStatNum = async () => {
box1Loading.value = true;
try {
const res = await getStatNum({
byYorM: "12",
sortCode: "301"
});
const res = await getStatNum({ byYorM: "12", sortCode: "301" });
console.log('数量变化趋势', res)
if (res.code === 200 && res.data) {
const sortedData = res.data.sort((a, b) => parseInt(a.searchYorM) - parseInt(b.searchYorM));
box1ChartData.value.title = sortedData.map(item => item.searchYorM);
box1ChartData.value.list = [{ name: "301调查", value: sortedData.map(item => item.searchCount) }]
console.log(box1ChartData.value);
inProgressCount.value = res.data.reduce((acc, cur) => acc + (cur.inSearchCount || 0), 0);
nextTick(() => { createLineChart(box1Chart, box1ChartData.value) })
}
} catch (error) {
console.error("获取调查年度数量趋势失败", error);
} finally {
}
box1Loading.value = false;
};
const box2ChartData = ref({
title: [],
data: []
});
const box2Paarams = reactive({
sortCode: 301,
years: '2025'
})
const box2Loading = ref(false);
const handleGetSearchCountry = async () => {
box2Loading.value = true;
try {
const res = await getSearchCountry(box2Paarams);
console.log('国家分布情况', res)
if (res.code === 200 && res.data) {
box2ChartData.value = {
title: res.data.map(item => ({
img: item.COUNTRYIMAGE ? (item.COUNTRYIMAGE.startsWith("http") ? item.COUNTRYIMAGE : `http://${item.COUNTRYIMAGE}`) : "",
name: item.COUNTRY
})),
data: res.data.map(item => item.NUM)
};
const box2Chart = getBarChart(box2ChartData.value.title, box2ChartData.value.data);
setChart(box2Chart, "box2Chart");
}
} catch (error) {
console.error("获取受调查国家分布失败", error);
}
box2Loading.value = false;
};
const box3ChartData = ref({
nodes: [],
links: []
});
const box3Loading = ref(false);
const handleGetSearchDirection = async () => {
box3Loading.value = true;
try {
const res = await getSearchDirection({ sortCode: "301" });
console.log('调查方向及结果分布', res)
if (res.code === 200 && res.data) {
const nodes = [];
const links = [];
......@@ -130,29 +163,22 @@ const handleGetSearchDirection = async () => {
}
} catch (error) {
console.error("获取调查方向及结果分布失败", error);
} finally {
box3Loading.value = false;
}
box3Loading.value = false;
};
// 获取最近年份列表
const currentYear = new Date().getFullYear();
const getYearList = (count = 6) => {
const yearOptions = [];
for (let i = 0; i < count; i++) {
const year = currentYear - i;
yearOptions.push({ label: `${year.toString()}年`, value: year.toString() });
}
return yearOptions;
};
const yearList = getYearList();
const selectYear = ref('2025');
const box4ChartData = ref([]);
const box4Chart = ref(null)
const box4Paarams = reactive({
sortCode: 301,
years: '2025'
})
const box4Loading = ref(false);
const handleGetStatArea = async () => {
box4Loading.value = true;
try {
const res = await getStatArea({years: selectYear.value, sortCode: 301});
const res = await getStatArea(box4Paarams);
console.log('领域分布情况', res)
if (res.code === 200 && res.data) {
box4ChartData.value = res.data.map(item => ({name: item.areaname, value: item.areacount}));
} else {
......@@ -165,32 +191,6 @@ const handleGetStatArea = async () => {
box4Loading.value = false;
};
const box2ChartData = ref({
title: [],
data: []
});
const handleGetSearchCountry = async () => {
box2Loading.value = true;
try {
const res = await getSearchCountry({ sortCode: 301 });
if (res.code === 200 && res.data) {
box2ChartData.value = {
title: res.data.map(item => ({
img: item.COUNTRYIMAGE ? (item.COUNTRYIMAGE.startsWith("http") ? item.COUNTRYIMAGE : `http://${item.COUNTRYIMAGE}`) : "",
name: item.COUNTRY
})),
data: res.data.map(item => item.NUM)
};
const box2Chart = getBarChart(box2ChartData.value.title, box2ChartData.value.data);
setChart(box2Chart, "box2Chart");
}
} catch (error) {
console.error("获取受调查国家分布失败", error);
} finally {
box2Loading.value = false;
}
};
onMounted(() => {
handleGetStatNum();
handleGetSearchCountry();
......
......@@ -3,18 +3,6 @@ import * as echarts from "echarts";
const getBarChart = (nameList, valueList) => {
const option = {
tooltip: {},
title: {
text: '单位:万人',
right: 26,
top: 10,
textStyle: {
color: 'rgba(95, 101, 108, 1)',
fontSize: 14,
fontFamily: 'Microsoft YaHei',
fontstyle: 'Regular',
fontWeight: 'normal'
}
},
grid: {
top: 40,
bottom: 0,
......@@ -24,23 +12,47 @@ const getBarChart = (nameList, valueList) => {
},
yAxis: {
type: 'value',
name: "项",
nameLocation: 'end',
nameGap: 12,
nameTextStyle: {
color: '#666',
fontSize: 14,
fontWeight: 400,
padding: [0, 0, 6, -26]
},
axisLabel: {
formatter: '{value}',
color: '#666',
fontSize: 14,
fontWeight: 400
},
splitLine: {
show: true,
lineStyle: {
color: '#e7f3ff',
type: 'dashed',
}
},
},
xAxis: {
type: 'category',
data: nameList.map(item => {
return item.name
}),
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLine: {
show: false
show: true,
lineStyle: {
color: '#e7f3ff',
},
},
axisLabel: {
show: true
show: true,
textStyle: {
color: 'rgba(95, 101, 108, 1)',
fontFamily: 'Microsoft YaHei',
fontsize: 14,
}
}
},
series: [{
......
......@@ -17,7 +17,7 @@
<div class="data-item" v-for="(item, index) in suggestionList" :key="index">
<div class="item-head">
<div class="item-name">{{ item.title }}</div>
<div class="button-box">
<div class="button-box" @click="onNavigateTo()">
<div class="button-icon">
<img src="@/views/marketAccessRestrictions/assets/icons/open.png" alt="" />
</div>
......@@ -52,6 +52,7 @@
<script setup>
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import router from "@/router";
import { getReportAnalyze } from "@/api/marketAccessRestrictions/index.js";
import SurveyConclusion from "@/views/marketAccessRestrictions/com/SurveyConclusion.vue";
import AiTips from "@/views/marketAccessRestrictions/com/AiTips.vue";
......@@ -65,6 +66,14 @@ const supplyList = ref([]);
const box4List = ref([]);
const suggestionList = ref([]);
const onNavigateTo = () => {
const page = router.resolve({
name: "MarketSingleReportOriginal",
query: { ...route.query }
});
window.open(page.href, "_blank");
}
const getData = async () => {
// 行业背景
getReportAnalyze({ searchId, type: "01" }).then((res) => {
......@@ -119,23 +128,6 @@ const getData = async () => {
});
};
const groupData = (data) => {
const groups = {};
data.forEach((item) => {
const title = item.TITLE || "其他";
if (!groups[title]) {
groups[title] = [];
}
groups[title].push({
title: item.CONTENT
});
});
return Object.keys(groups).map((title) => ({
title,
data: groups[title]
}));
};
const groupSuggestionData = (data) => {
const groups = {};
data.forEach((item) => {
......@@ -217,6 +209,7 @@ onMounted(() => {
display: flex;
align-items: center;
margin-left: 50px;
cursor: pointer;
.button-icon {
width: 16px;
height: 16px;
......
<template>
<div class="wrapper">
<div class="left">
<div class="page-left">
<AnalysisBox title="基本信息" :showAllBtn="false" height="auto">
<div class="box1-main">
<div class="box1-item">
......@@ -56,7 +56,7 @@
</AnalysisBox>
<SurveyAffiche title="调查公告" :listData="box2Data"></SurveyAffiche>
</div>
<div class="right">
<div class="page-right">
<AnalysisBox :showAllBtn="false" height="auto">
<template #custom-title>
<div class="btn-box">
......@@ -91,13 +91,11 @@
<div class="box3-main2-item" v-for="(item, index) in box3Data2" :key="index">
<div class="box3-main2-item-header">
<div class="title">{{ item.title }}</div>
<div class="more">
<div class="more" >
<div class="icon">
<img src="./assets/images/open-active.png" alt="" />
</div>
<div class="text">
{{ "跳转原文" }}
</div>
<div class="text" @click="onNavigateTo()">{{ "跳转原文" }}</div>
</div>
</div>
<div class="box3-main2-item-content">
......@@ -116,6 +114,7 @@
<script setup>
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import router from "@/router";
import {
getSearchBlurb,
getRelatedEvents,
......@@ -137,6 +136,14 @@ const handleClickBox3Btn = btn => {
box3BtnActive.value = btn;
};
const onNavigateTo = () => {
const page = router.resolve({
name: "MarketSingleReportOriginal",
query: { ...route.query }
});
window.open(page.href, "_blank");
}
const box2Data = ref([]);
const handleGetSearchBlurb = async () => {
try {
......@@ -218,13 +225,13 @@ onMounted(() => {
width: 1600px;
margin: 20px auto;
display: flex;
.left {
.page-left {
width: 520px;
display: flex;
flex-direction: column;
gap: 16px;
}
.right {
.page-right {
width: 20px;
flex: auto;
display: flex;
......@@ -336,7 +343,7 @@ onMounted(() => {
padding: 15px 20px;
.box3-main1 {
.box3-main1-item {
margin-bottom: 20px;
margin-bottom: 30px;
display: flex;
.left {
width: 10px;
......@@ -358,7 +365,6 @@ onMounted(() => {
.right {
margin-left: 17px;
.header {
height: 24px;
display: flex;
gap: 16px;
color: var(--color-main-active);
......@@ -366,18 +372,16 @@ onMounted(() => {
font-style: Bold;
font-size: 18px;
font-weight: 700;
line-height: 24px;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
padding-bottom: 10px;
}
.content {
border-top: 1px solid #eaecee;
margin-top: 10px;
margin-bottom: 36px;
padding: 10px 0;
padding-top: 10px;
width: 971px;
max-height: 60px;
min-height: 0;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
......@@ -422,9 +426,11 @@ onMounted(() => {
justify-content: flex-end;
align-items: center;
gap: 7px;
cursor: pointer;
.icon {
width: 12px;
height: 12px;
font-size: 0;
img {
width: 100%;
height: 100%;
......@@ -432,13 +438,10 @@ onMounted(() => {
}
.text {
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 12px;
font-weight: 400;
line-height: 14px;
line-height: 12px;
letter-spacing: 0px;
text-align: justify;
}
}
}
......
......@@ -93,14 +93,14 @@ const createLineChart = (chartDom, data, option={}) => {
{
type: 'value',
position: 'left',
name: '数量',
name: option.unitY || "数量",
nameLocation: 'end',
nameGap: 12,
nameTextStyle: {
color: '#666',
fontSize: 14,
fontWeight: 400,
padding: [0, 0, 6, -20]
padding: [0, 0, 6, -26]
},
axisLabel: {
formatter: '{value}',
......
......@@ -42,3 +42,70 @@ export const getDateBefore = (num: number) => {
return `${year}-${month}-${day}`;
}
/**
* 获取最近年份列表
* @param num 需要几年前的列表
*/
export const getNearYearList = (num=6) => {
const currentYear = new Date().getFullYear();
const yearOptions = [];
for (let i = 0; i < num; i++) {
const year = currentYear - i;
yearOptions.push({ label: year.toString()+'年', value: year.toString() });
}
return yearOptions;
};
/**
* AI智能总结
* @param data 需要分析的数据
*/
export const getAIReport = async (data:any) => {
let word = ""
// 👇 新增:超时 + 终止请求(只加这一段)
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10*1000); // 10秒超时
try {
const res = await fetch('/aiAnalysis/chart_interpretation', {
method: 'POST',
headers: {
"X-API-Key": "aircasKEY19491001",
'Content-Type': 'application/json',
},
body: JSON.stringify({text: JSON.stringify(data)}),
signal: controller.signal // 👇 新增:绑定中断信号
});
clearTimeout(timeout); // 👇 新增:请求成功清除定时器
if (!res.ok) throw new Error(`HTTP 错误 ${res.status}`);
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let summarize = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const content = line.substring(6);
const textMatch = content.match(/"解读":\s*"([^"]*)"/);
if (textMatch && textMatch[1]) summarize = textMatch[1];
}
}
}
word = summarize
} catch (err) {
word = "系统异常,生成失败";
}
return word
}
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论