提交 95cb197b authored 作者: yanpeng's avatar yanpeng

Merge branch 'master' into yp-dev

...@@ -14,6 +14,7 @@ lerna-debug.log* ...@@ -14,6 +14,7 @@ lerna-debug.log*
# Dependencies # Dependencies
node_modules node_modules
*node_modules
.pnpm .pnpm
.npm .npm
......
...@@ -29,6 +29,25 @@ import { ElMessage } from "element-plus"; ...@@ -29,6 +29,25 @@ import { ElMessage } from "element-plus";
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
import useTagsViewStore from '@/stores/tagsView.js'
const tagsViewStore = useTagsViewStore()
// 在路由全局守卫中处理
router.beforeEach((to, from, next) => {
// 路由允许添加标签(排除掉隐藏的布局页如 /404, /login 等)
if (to.path.includes('dataLibrary')) {
tagsViewStore.addView({
path: to.path,
name: to.name, // 对应组件的 name,用于缓存
meta: { ...to.meta },
active: true
})
}
next()
})
const isShowAiBox = ref(false); const isShowAiBox = ref(false);
......
...@@ -36,6 +36,47 @@ function parseChartInterpretationArray(buffer) { ...@@ -36,6 +36,47 @@ function parseChartInterpretationArray(buffer) {
throw new Error("无法解析图表解读 JSON 数组"); throw new Error("无法解析图表解读 JSON 数组");
} }
/**
* 从数组结果中提取可展示的解读文本
* @param {unknown[]} arr
* @returns {string}
*/
function pickInterpretationText(arr) {
if (!Array.isArray(arr) || arr.length === 0) {
return "";
}
const first = arr[0] || {};
return (
first["解读"] ||
first["interpretation"] ||
first["analysis"] ||
first["content"] ||
""
);
}
/**
* 从非标准 JSON 文本中兜底提取“解读”字段(兼容单引号/双引号)
* 示例:
* [{'图表标题': '数量变化趋势', '解读': 'xxx'}]
* [{"图表标题":"数量变化趋势","解读":"xxx"}]
* @param {string} text
* @returns {string}
*/
function extractInterpretationFromLooseText(text) {
const raw = String(text || "");
if (!raw) {
return "";
}
const reg =
/["']解读["']\s*:\s*["']([\s\S]*?)["']\s*(?:[,}\]])/;
const m = raw.match(reg);
if (!m || !m[1]) {
return "";
}
return String(m[1]).replace(/\\n/g, "\n").trim();
}
/** /**
* 图表解读(SSE 流式) * 图表解读(SSE 流式)
* @param {object} data - 请求体 * @param {object} data - 请求体
...@@ -44,9 +85,15 @@ function parseChartInterpretationArray(buffer) { ...@@ -44,9 +85,15 @@ function parseChartInterpretationArray(buffer) {
* @returns {Promise<{data: unknown[]}>} * @returns {Promise<{data: unknown[]}>}
*/ */
export function getChartAnalysis(data, options = {}) { export function getChartAnalysis(data, options = {}) {
const { onChunk } = options; const onDelta =
typeof options?.onChunk === "function"
? options.onChunk
: typeof options?.onInterpretationDelta === "function"
? options.onInterpretationDelta
: null;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let buffer = ""; let buffer = "";
let latestInterpretation = "";
let settled = false; let settled = false;
const abortController = new AbortController(); const abortController = new AbortController();
...@@ -119,9 +166,18 @@ export function getChartAnalysis(data, options = {}) { ...@@ -119,9 +166,18 @@ export function getChartAnalysis(data, options = {}) {
buffer += raw; buffer += raw;
} }
// 兜底:非标准 JSON(如单引号 Python 风格)时,尝试直接从文本提取“解读”
const looseInterpretation = extractInterpretationFromLooseText(raw);
if (looseInterpretation) {
latestInterpretation = looseInterpretation;
safeResolve({ data: [{ 解读: looseInterpretation }] });
abortController.abort();
return;
}
// 每收到一条消息即回调,用于流式渲染 // 每收到一条消息即回调,用于流式渲染
if (chunk && typeof onChunk === "function") { if (chunk && onDelta) {
onChunk(chunk); onDelta(chunk);
} }
// 如果 buffer 已经拼完 markdown code fence,则提前解析并中断连接 // 如果 buffer 已经拼完 markdown code fence,则提前解析并中断连接
...@@ -129,6 +185,10 @@ export function getChartAnalysis(data, options = {}) { ...@@ -129,6 +185,10 @@ export function getChartAnalysis(data, options = {}) {
if (trimmed.endsWith("```")) { if (trimmed.endsWith("```")) {
try { try {
const arr = parseChartInterpretationArray(trimmed); const arr = parseChartInterpretationArray(trimmed);
const interpretation = pickInterpretationText(arr);
if (interpretation) {
latestInterpretation = interpretation;
}
safeResolve({ data: arr }); safeResolve({ data: arr });
abortController.abort(); abortController.abort();
} catch (_) { } } catch (_) { }
...@@ -137,8 +197,22 @@ export function getChartAnalysis(data, options = {}) { ...@@ -137,8 +197,22 @@ export function getChartAnalysis(data, options = {}) {
onclose: () => { onclose: () => {
try { try {
const arr = parseChartInterpretationArray(buffer); const arr = parseChartInterpretationArray(buffer);
const interpretation = pickInterpretationText(arr);
if (interpretation) {
latestInterpretation = interpretation;
}
safeResolve({ data: arr }); safeResolve({ data: arr });
} catch (e) { } catch (e) {
// 兜底:整体 buffer 不是标准 JSON(如单引号)时直接提取“解读”
const looseInterpretation = extractInterpretationFromLooseText(buffer);
if (looseInterpretation) {
safeResolve({ data: [{ 解读: looseInterpretation }] });
return;
}
if (latestInterpretation) {
safeResolve({ data: [{ 解读: latestInterpretation }] });
return;
}
safeReject(e); safeReject(e);
} }
}, },
......
...@@ -1156,6 +1156,15 @@ ...@@ -1156,6 +1156,15 @@
| -------- | -------- | ----- | -------- | -------- | ------ | | -------- | -------- | ----- | -------- | -------- | ------ |
|areas|区域名称列表|query|false|array|string| |areas|区域名称列表|query|false|array|string|
|researchTypeIds|研究类型ID列表|query|false|array|string| |researchTypeIds|研究类型ID列表|query|false|array|string|
|domainIds|科技领域 ID 列表(逗号分隔)|query|false|string||
|startDate|发布时间起 YYYY-MM-DD(与政策追踪发布时间逻辑一致)|query|false|string||
|endDate|发布时间止 YYYY-MM-DD|query|false|string||
|category|分类(如调查项目)|query|false|string||
|pageNum|页码|query|false|integer||
|pageSize|每页条数|query|false|integer||
|sortFun|排序|query|false|boolean||
|thinkTankId|智库 ID(详情页动态列表限定当前智库)|query|false|string||
|keyword|关键词搜索(智库动态)|query|false|string||
|token|Token Request Header|header|false|string|| |token|Token Request Header|header|false|string||
......
// 智库概览信息 // 智库概览信息
import request from "@/api/request.js"; import request, { getToken } from "@/api/request.js";
// 智库列表 // 智库列表
export function getThinkTankList() { export function getThinkTankList() {
...@@ -87,7 +87,11 @@ export function getHylyList() { ...@@ -87,7 +87,11 @@ export function getHylyList() {
} }
//获取智库报告 /**
* 智库概览/智库动态-智库报告、调查项目
* GET /api/thinkTankOverview/report
* 常用 query:pageNum, pageSize, sortFun, domainIds, startDate, endDate, category(调查项目), thinkTankId(详情页), keyword(动态搜索)
*/
export function getThinkTankReport(params) { export function getThinkTankReport(params) {
return request({ return request({
method: 'GET', method: 'GET',
...@@ -158,6 +162,7 @@ export function getThinkDynamicsReport(params) { ...@@ -158,6 +162,7 @@ export function getThinkDynamicsReport(params) {
// 智库领域观点分析(流式) // 智库领域观点分析(流式)
// [POST] 8.140.26.4:10029/report-domain-view-analysis // [POST] 8.140.26.4:10029/report-domain-view-analysis
// 每次请求体:{ domain, report_view_list }(一个 domain);多领域由前端按领域循环多次调用
export function postReportDomainViewAnalysis(data) { export function postReportDomainViewAnalysis(data) {
return request({ return request({
method: 'POST', method: 'POST',
...@@ -167,6 +172,86 @@ export function postReportDomainViewAnalysis(data) { ...@@ -167,6 +172,86 @@ export function postReportDomainViewAnalysis(data) {
}) })
} }
/**
* 智库领域观点分析(真正流式,逐 chunk 回调)
* @param {object} data
* @param {{ onReasoningChunk?: (chunk: string) => void, onMessage?: (msg: any) => void }} handlers
*/
export async function postReportDomainViewAnalysisStream(data, handlers = {}) {
const { onReasoningChunk, onMessage } = handlers
const token = getToken()
const response = await fetch('/intelligent-api/report-domain-view-analysis', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(token ? { token } : {})
},
body: JSON.stringify(data)
})
if (!response.ok) {
throw new Error(`流式分析请求失败: ${response.status}`)
}
// 兜底:非流式返回时仍可读取文本继续后续解析
if (!response.body) {
return await response.text()
}
const reader = response.body.getReader()
const decoder = new TextDecoder('utf-8')
let done = false
let pending = ''
let fullText = ''
while (!done) {
const result = await reader.read()
done = result.done
if (result.value) {
const chunkText = decoder.decode(result.value, { stream: !done })
fullText += chunkText
pending += chunkText
const lines = pending.split(/\r?\n/)
pending = lines.pop() ?? ''
for (const rawLine of lines) {
const line = String(rawLine || '').trim()
if (!line || !line.startsWith('data:')) continue
const jsonText = line.slice(5).trim()
if (!jsonText || jsonText === '[DONE]') continue
try {
const msg = JSON.parse(jsonText)
if (typeof onMessage === 'function') onMessage(msg)
if (msg?.type === 'reasoning' && msg?.chunk != null && typeof onReasoningChunk === 'function') {
const c = String(msg.chunk)
if (c) onReasoningChunk(c)
}
} catch (e) {
// 忽略非 JSON 数据行
}
}
}
}
// 处理最后一行残留
const last = String(pending || '').trim()
if (last.startsWith('data:')) {
const jsonText = last.slice(5).trim()
if (jsonText && jsonText !== '[DONE]') {
try {
const msg = JSON.parse(jsonText)
if (typeof onMessage === 'function') onMessage(msg)
if (msg?.type === 'reasoning' && msg?.chunk != null && typeof onReasoningChunk === 'function') {
const c = String(msg.chunk)
if (c) onReasoningChunk(c)
}
} catch (e) {
// ignore
}
}
}
return fullText
}
//提出建议领域分布 //提出建议领域分布
export function getThinkPolicyIndustry(params) { export function getThinkPolicyIndustry(params) {
...@@ -240,18 +325,12 @@ export function getThinkTankInfoBranch(params) { ...@@ -240,18 +325,12 @@ export function getThinkTankInfoBranch(params) {
}) })
} }
//获取经费来源统计
export function getThinkTankFundsTotal(params) {
return request({
method: 'GET',
url: `/api/thinkTankInfo/fundsTotal/${params}`,
})
}
//获取经费来源 //获取经费来源
export function getThinkTankFundsSource(params) { export function getThinkTankFundsSource(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/thinkTankInfo/fundsSource/${params}`, url: `/api/thinkTankInfo/fundsStatistics/${params}`,
}) })
} }
...@@ -265,9 +344,17 @@ export function getThinkTankResearchAreae(params) { ...@@ -265,9 +344,17 @@ export function getThinkTankResearchAreae(params) {
//获取核心研究人员 //获取核心研究人员
export function getThinkTankPerson(params) { export function getThinkTankPerson(params) {
const { thinkTankId, currentPage, pageSize } = params
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/thinkTankInfo/person/${params}`, url: `/api/thinkTankInfo/person/page`,
params: {
currentPage,
pageNum: currentPage,
page: currentPage,
pageSize,
thinkTankId
}
}) })
} }
......
<template> <template>
<div class="warnning-pane-wrapper" :style="{ width: width ? width : '1600px', height: height ? height : 'auto', minHeight: height ? undefined : '116px' }" <div class="warnning-pane-wrapper" :style="{ width: width ? width : '1600px' }"
:class="{ :class="{
level1: warnningLevel === '特别重大风险', level1: warnningLevel === '特别重大风险',
level2: warnningLevel === '重大风险', level2: warnningLevel === '重大风险',
...@@ -27,12 +27,22 @@ ...@@ -27,12 +27,22 @@
</div> </div>
</div> </div>
<div class="warnning-pane-content text-regular"> <div class="warnning-pane-content text-regular">
{{ warnningContent }} {{ showContent }}
<div class="show-all-btn" v-if="props.warnningContent.length > 185" @click.stop="handleClickShowAll">
<div class="text text-tip-2">
{{ isShowAllContent? '收起' : '展开' }}
</div>
<div class="icon">
<img v-if="!isShowAllContent" src="./icons/down.png" alt="">
<img v-else src="./icons/up.png" alt="">
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import {ref, computed} from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
...@@ -50,6 +60,14 @@ const props = defineProps( ...@@ -50,6 +60,14 @@ const props = defineProps(
} }
) )
const showContent = computed(() => {
if(!isShowAllContent.value) {
return props.warnningContent.length > 185 ? props.warnningContent.slice(0,185) + '...' : props.warnningContent
} else {
return props.warnningContent
}
})
const emit = defineEmits(['clickPane']) const emit = defineEmits(['clickPane'])
const handleClickPane = () => { const handleClickPane = () => {
...@@ -57,6 +75,12 @@ const handleClickPane = () => { ...@@ -57,6 +75,12 @@ const handleClickPane = () => {
emit('clickPane') emit('clickPane')
} }
const isShowAllContent = ref(false)
const handleClickShowAll = () => {
isShowAllContent.value = !isShowAllContent.value
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
...@@ -64,7 +88,8 @@ const handleClickPane = () => { ...@@ -64,7 +88,8 @@ const handleClickPane = () => {
border-radius: 10px; border-radius: 10px;
border: 1px solid var(--color-primary-100); border: 1px solid var(--color-primary-100);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
cursor: pointer; // cursor: pointer;
height: max-content;
} }
.level1 { .level1 {
...@@ -146,12 +171,40 @@ const handleClickPane = () => { ...@@ -146,12 +171,40 @@ const handleClickPane = () => {
.warnning-pane-content{ .warnning-pane-content{
width: calc(100% - 40px); width: calc(100% - 40px);
margin: 0 auto; margin: 0 auto;
margin-bottom: 16px; // height: 60px;
min-height: 60px; // display: -webkit-box;
height: auto; // /* 2. 设置内部布局方向为垂直 */
display: block; // -webkit-box-orient: vertical;
overflow: visible; // /* 3. 限制显示的行数为 2 行 */
white-space: pre-wrap; // -webkit-line-clamp: 2;
word-break: break-word; // /* 4. 隐藏超出部分 */
// overflow: hidden;
// /* 5. 设置文本溢出显示省略号 */
// text-overflow: ellipsis;
position: relative;
.show-all-btn {
position: absolute;
right: 0px;
bottom: 4px;
width: 48px;
height: 22px;
display: flex;
gap: 4px;
cursor: pointer;
.text{
color: var(--text-primary-50-color);
&:hover{
color: var(--color-primary-100);
}
}
.icon{
width: 16px;
height: 16px;
img{
width: 100%;
height: 100%;
}
}
}
} }
</style> </style>
\ No newline at end of file
<template> <template>
<p class="p-regular-rereg"> <p class="p-regular-rereg">
<span class="text-regular" v-for="(segment, index) in processedText" :key="index"> <span class="text-regular" v-for="(segment, index) in processedText" :key="index">
<a v-if="segment.isEntity" :href="`https://cn.bing.com/search?q=${segment.entity?.text_span}`" <span v-if="segment.isEntity" @click="$emit('onEntityClick', segment.entity)" class="entity-link">
class="entity-link" target="_blank" rel="noopener noreferrer"> {{ segment.entity?.text_span }}
{{ segment.entity?.text_span }} <img :src="SearchIcon" :width="10" :height="10" alt="search" />
<img :src="SearchIcon" :width="10" :height="10" alt="search" /> </span>
</a> <span v-else>
<span v-else> {{ segment.text }}
{{ segment.text }} </span>
</span> </span>
</span> </p>
</p>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { TextEntity } from '@/api/intelligent'; import { TextEntity } from "@/api/intelligent";
import { ref, watch, onMounted } from 'vue'; import { ref, watch, onMounted } from "vue";
import SearchIcon from './images/search.png' import SearchIcon from "./images/search.png";
export interface ProcessedTextSegment { export interface ProcessedTextSegment {
text: string text: string;
isEntity: boolean isEntity: boolean;
entity?: TextEntity entity?: TextEntity;
} }
const props = defineProps({ const props = defineProps({
text: { text: {
type: String, type: String,
default: '' default: ""
}, },
entities: { entities: {
type: Array<TextEntity>, type: Array<TextEntity>,
default: () => [] default: () => []
} }
}) });
const emit = defineEmits(["onEntityClick"]);
// 处理后的文本段 // 处理后的文本段
const processedText = ref<ProcessedTextSegment[]>([]) const processedText = ref<ProcessedTextSegment[]>([]);
// 处理文本,识别并替换实体 // 处理文本,识别并替换实体
const processText = () => { const processText = () => {
console.log('props.entities.length', props.entities.length) console.log("props.entities.length", props.entities.length);
if (!props.text || !props.entities) { if (!props.text || !props.entities) {
// console.log('props.text', props.entities.length) // console.log('props.text', props.entities.length)
processedText.value = [{ text: '', isEntity: false }] processedText.value = [{ text: "", isEntity: false }];
return return;
} }
const result = [] const result = [];
let currentPosition = 0 let currentPosition = 0;
// 按实体文本长度排序,优先匹配长文本 // 按实体文本长度排序,优先匹配长文本
const sortedEntities = [...props.entities].sort((a, b) => const sortedEntities = [...props.entities].sort((a, b) => b.text_span.length - a.text_span.length);
b.text_span.length - a.text_span.length
)
while (currentPosition < props.text.length) { while (currentPosition < props.text.length) {
let matched = false let matched = false;
for (const entity of sortedEntities) { for (const entity of sortedEntities) {
const entityText = entity.text_span const entityText = entity.text_span;
const endPosition = currentPosition + entityText.length const endPosition = currentPosition + entityText.length;
if (props.text.substring(currentPosition, endPosition) === entityText) { if (props.text.substring(currentPosition, endPosition) === entityText) {
// 如果当前位置是实体,添加到结果 // 如果当前位置是实体,添加到结果
result.push({ result.push({
isEntity: true, isEntity: true,
entity: { ...entity } entity: { ...entity }
}) });
currentPosition = endPosition currentPosition = endPosition;
matched = true matched = true;
break break;
} }
} }
if (!matched) { if (!matched) {
// 如果不是实体,收集普通文本 // 如果不是实体,收集普通文本
let nextEntityStart = props.text.length let nextEntityStart = props.text.length;
for (const entity of sortedEntities) { for (const entity of sortedEntities) {
const pos = props.text.indexOf(entity.text_span, currentPosition) const pos = props.text.indexOf(entity.text_span, currentPosition);
if (pos !== -1 && pos < nextEntityStart) { if (pos !== -1 && pos < nextEntityStart) {
nextEntityStart = pos nextEntityStart = pos;
} }
} }
if (nextEntityStart > currentPosition) { if (nextEntityStart > currentPosition) {
const plainText = props.text.substring(currentPosition, nextEntityStart) const plainText = props.text.substring(currentPosition, nextEntityStart);
result.push({ result.push({
text: plainText, text: plainText,
isEntity: false isEntity: false
}) });
currentPosition = nextEntityStart currentPosition = nextEntityStart;
} else { } else {
// 没有更多实体,添加剩余文本 // 没有更多实体,添加剩余文本
const remainingText = props.text.substring(currentPosition) const remainingText = props.text.substring(currentPosition);
if (remainingText) { if (remainingText) {
result.push({ result.push({
text: remainingText, text: remainingText,
isEntity: false isEntity: false
}) });
} }
currentPosition = props.text.length currentPosition = props.text.length;
} }
} }
} }
processedText.value = result processedText.value = result;
} };
// 监听文本和实体变化 // 监听文本和实体变化
watch(() => props.text, processText) watch(() => props.text, processText);
watch(() => props.entities, processText, { deep: true }) watch(() => props.entities, processText, { deep: true });
// 初始化处理 // 初始化处理
onMounted(processText) onMounted(processText);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@use '@/styles/common.scss'; @use "@/styles/common.scss";
.entity-link { .entity-link {
color: var(--color-primary-100); color: var(--color-primary-100);
&:hover {
cursor: pointer;
}
} }
.p-regular-rereg { .p-regular-rereg {
text-indent: 2em; text-indent: 2em;
margin: 4px 0; margin: 4px 0;
} }
</style> </style>
\ No newline at end of file
<template> <template>
<div class="full-width"> <div class="full-width">
<div class="flex-display" style="align-items: center;"> <div class="flex-display" style="align-items: center">
<common-text class="text-title-3-bold" color="var(--text-primary-80-color)">{{ isOpenTranslation <common-text class="text-title-3-bold" color="var(--text-primary-80-color)">{{
? '中文' : '原文' }}</common-text> isOpenTranslation ? "中文" : "原文"
<div class="flex-fill" style="margin: 0 10px;"> }}</common-text>
<el-divider></el-divider> <div class="flex-fill" style="margin: 0 10px">
</div> <el-divider></el-divider>
<el-button v-if="showMoreVisible" @click="() => { showMore = !showMore; updateText() }"> </div>
{{ showMore ? '收起' : '展开' }} <el-button
<el-icon> v-if="showMoreVisible"
<arrow-up v-if="showMore" /> @click="
<arrow-down v-else /> () => {
</el-icon> showMore = !showMore;
</el-button> updateText();
</div> }
<el-row :gutter="32"> "
<el-col :span="textColSpan" v-for="(item, index) in allTexts" :key="index"> >
<!-- <p class="p-news-content"> {{ item }}</p> --> {{ showMore ? "收起" : "展开" }}
<intelligent-entity-text :text="item" <el-icon>
:entities="isHighlightEntity ? textEntities : []"></intelligent-entity-text> <arrow-up v-if="showMore" />
</el-col> <arrow-down v-else />
</el-row> </el-icon>
</div> </el-button>
</div>
<el-row :gutter="32">
<el-col :span="textColSpan" v-for="(item, index) in allTexts" :key="index">
<!-- <p class="p-news-content"> {{ item }}</p> -->
<intelligent-entity-text
:text="item"
@on-entity-click="e => $emit('onEntityClick', e)"
:entities="isHighlightEntity ? textEntities : []"
></intelligent-entity-text>
</el-col>
</el-row>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import '@/styles/container.scss'; import "@/styles/container.scss";
import '@/styles/common.scss'; import "@/styles/common.scss";
import { ref, watch, onMounted } from 'vue'; import { ref, watch, onMounted } from "vue";
import { TextEntity } from '@/api/intelligent'; import { TextEntity } from "@/api/intelligent";
import IntelligentEntityText from '@/components/base/texts/IntelligentEntityText.vue'; import IntelligentEntityText from "@/components/base/texts/IntelligentEntityText.vue";
import { ElIcon, ElButton, ElDivider, ElRow, ElCol } from 'element-plus'; import { ElIcon, ElButton, ElDivider, ElRow, ElCol } from "element-plus";
import CommonText from './CommonText.vue'; import CommonText from "./CommonText.vue";
const allTexts = ref([]); const allTexts = ref([]);
const textColSpan = ref(12); const textColSpan = ref(12);
...@@ -39,71 +51,73 @@ const hasTranslation = ref(false); ...@@ -39,71 +51,73 @@ const hasTranslation = ref(false);
const showMore = ref(false); const showMore = ref(false);
const showMoreVisible = ref(false); const showMoreVisible = ref(false);
const props = defineProps({ const props = defineProps({
//段落列表: 原始文本 //段落列表: 原始文本
textsRaw: { textsRaw: {
type: Array<String>, type: Array<String>,
default: () => [] default: () => []
}, },
//段落列表: 翻译文本 //段落列表: 翻译文本
textsTranslate: { textsTranslate: {
type: Array<String>, type: Array<String>,
default: () => [] default: () => []
}, },
//是否显示翻译 //是否显示翻译
isOpenTranslation: { isOpenTranslation: {
type: Boolean, type: Boolean,
default: true default: true
}, },
//是否高亮实体 //是否高亮实体
isHighlightEntity: { isHighlightEntity: {
type: Boolean, type: Boolean,
default: true default: true
}, },
//实体列表 //实体列表
textEntities: { textEntities: {
type: Array<TextEntity>, type: Array<TextEntity>,
default: () => [] default: () => []
} }
}) });
const emit = defineEmits(["onEntityClick"]);
function updateText() { function updateText() {
const tempTexts = [] const tempTexts = [];
const tempRaws = props.textsRaw ?? [] const tempRaws = props.textsRaw ?? [];
const tempTranslates = props.textsTranslate ?? [] const tempTranslates = props.textsTranslate ?? [];
hasTranslation.value = tempTranslates.length > 0 hasTranslation.value = tempTranslates.length > 0;
if (hasTranslation.value && props.isOpenTranslation) { if (hasTranslation.value && props.isOpenTranslation) {
// 遍历原始文本和翻译文本,将它们交替添加到 tempTexts 中,并保持原始文本和翻译文本的的数量一致 // 遍历原始文本和翻译文本,将它们交替添加到 tempTexts 中,并保持原始文本和翻译文本的的数量一致
const maxCount = Math.max(tempRaws.length, tempTranslates.length) const maxCount = Math.max(tempRaws.length, tempTranslates.length);
for (let i = 0; i < maxCount; i++) { for (let i = 0; i < maxCount; i++) {
if (i < tempTranslates.length) { if (i < tempTranslates.length) {
tempTexts.push(tempTranslates[i]); tempTexts.push(tempTranslates[i]);
} else { } else {
tempTexts.push(''); tempTexts.push("");
} }
if (i < tempRaws.length) { if (i < tempRaws.length) {
tempTexts.push(tempRaws[i]); tempTexts.push(tempRaws[i]);
} else { } else {
tempTexts.push(''); tempTexts.push("");
} }
} }
console.log(tempTexts.length) console.log(tempTexts.length);
textColSpan.value = 12; textColSpan.value = 12;
showMoreVisible.value = tempTexts.length > 6; showMoreVisible.value = tempTexts.length > 6;
allTexts.value = showMore.value ? tempTexts : tempTexts.slice(0, 6); allTexts.value = showMore.value ? tempTexts : tempTexts.slice(0, 6);
} else { } else {
textColSpan.value = 24; textColSpan.value = 24;
showMoreVisible.value = tempRaws.length > 3; showMoreVisible.value = tempRaws.length > 3;
allTexts.value = showMore.value ? tempRaws : tempRaws.slice(0, 3); allTexts.value = showMore.value ? tempRaws : tempRaws.slice(0, 3);
} }
} }
watch(() => [props.textsRaw, props.textsTranslate, props.isOpenTranslation], () => { watch(
updateText(); () => [props.textsRaw, props.textsTranslate, props.isOpenTranslation],
}) () => {
updateText();
}
);
onMounted(() => { onMounted(() => {
updateText(); updateText();
}) });
</script> </script>
\ No newline at end of file
...@@ -65,4 +65,5 @@ router.beforeEach((to, from, next) => { ...@@ -65,4 +65,5 @@ router.beforeEach((to, from, next) => {
next(); next();
}); });
export default router; export default router;
//企业主页 //企业主页
import { useGotoPage } from "../common.js";
const companyPages = () => import('@/views/companyPages/index.vue') const companyPages = () => import('@/views/companyPages/index.vue')
const companyPages2 = () => import('@/views/companyPages2/index.vue') const companyPages2 = () => import('@/views/companyPages2/index.vue')
...@@ -28,4 +29,9 @@ const companyPagesRoutes = [ ...@@ -28,4 +29,9 @@ const companyPagesRoutes = [
] ]
export function useGotoCompanyPages() {
const gotoPage = useGotoPage();
return (id, isNewTabs = true) => gotoPage("/companyPages/" + id, {}, isNewTabs)
}
export default companyPagesRoutes export default companyPagesRoutes
\ No newline at end of file
// 综合搜索 // 综合搜索
const ComprehensiveSearch = () => import('@/views/comprehensiveSearch/index.vue') const ComprehensiveSearch = () => import("@/views/comprehensiveSearch/index.vue");
const SearchResults = () => import('@/views/comprehensiveSearch/searchResults/index.vue') const SearchResults = () => import("@/views/comprehensiveSearch/searchResults/index.vue");
const Chat = () => import('@/views/comprehensiveSearch/chat/index.vue') const Chat = () => import("@/views/comprehensiveSearch/chat/index.vue");
const comprehensiveSearchRoutes = [ const comprehensiveSearchRoutes = [
// 综合搜索 // 综合搜索
{ {
path: "/comprehensiveSearch", path: "/comprehensiveSearch",
name: "comprehensiveSearch", name: "comprehensiveSearch",
component: ComprehensiveSearch, component: ComprehensiveSearch,
meta: { meta: {
title: "搜索-科技安全" title: "搜索-科技安全"
} }
}, },
{ {
path: "/searchResults", path: "/searchResults",
name: "searchResults", name: "searchResults",
component: SearchResults, component: SearchResults,
meta: { meta: {
title: "搜索结果", title: "搜索结果",
dynamicTitle: true dynamicTitle: true
} }
}, },
{ {
path: "/chat", path: "/chat",
name: "chat", name: "chat",
component: Chat, component: Chat,
meta: { meta: {
title: "智能问答" title: "智能问答"
} }
}, }
];
]
import { useGotoPage } from "../common.js"; import { useGotoPage } from "../common.js";
export function useGotoComprehensiveSearch() { export function useGotoComprehensiveSearch() {
const gotoPage = useGotoPage(); const gotoPage = useGotoPage();
return (isNewTabs = true) => gotoPage("/comprehensiveSearch/", {}, isNewTabs) return (isNewTabs = true) => gotoPage("/comprehensiveSearch/", {}, isNewTabs);
} }
export function useGotoSearchResults() { export function useGotoSearchResults() {
const gotoPage = useGotoPage(); const gotoPage = useGotoPage();
return (isNewTabs = true) => gotoPage("/searchResults/", {searchText, areaName}, isNewTabs)
return (searchText, areaName, isNewTabs = true) => gotoPage("/searchResults/", { searchText, areaName }, isNewTabs);
} }
export default comprehensiveSearchRoutes export default comprehensiveSearchRoutes;
\ No newline at end of file
...@@ -11,14 +11,35 @@ const setChart = (option, chartId, allowClick, selectParam) => { ...@@ -11,14 +11,35 @@ const setChart = (option, chartId, allowClick, selectParam) => {
let chart = echarts.init(chartDom); let chart = echarts.init(chartDom);
chart.setOption(option); chart.setOption(option);
if (allowClick) { if (allowClick) {
// 3. 添加点击事件监听
chart.on('click', function (params) { chart.on('click', function (params) {
switch (selectParam.moduleType) { switch (selectParam.moduleType) {
case '国会法案': case '国会法案':
// 判断点击的是否为饼图的数据项 // 判断点击的是否为饼图的数据项
if (params.componentType === 'series' && params.seriesType === 'pie') { if (params.componentType === 'series' && params.seriesType === 'pie') {
console.log('点击的扇形名称:', params.name); console.log('点击的扇形名称:', params.name);
selectParam.domains = JSON.stringify([params.name]) if (selectParam.key === '领域') {
selectParam.domains = JSON.stringify([params.name])
} else if (selectParam.key === '议院委员会') {
if (params.name === '众议院' || params.name === '参议院') {
selectParam.selectedCongress = params.name
selectParam.selectedOrg = ''
} else {
selectParam.selectedOrg = params.name
selectParam.selectedCongress = ''
}
}
const route = router.resolve({
path: "/dataLibrary/countryBill",
query: selectParam
});
window.open(route.href, "_blank");
} else if (params.componentType === 'series' && params.seriesType === 'bar') {
if (params.name === '已立法') {
selectParam.selectedStauts = 1
} else {
selectParam.selectedStauts = 0
}
const route = router.resolve({ const route = router.resolve({
path: "/dataLibrary/countryBill", path: "/dataLibrary/countryBill",
query: selectParam query: selectParam
......
...@@ -866,14 +866,20 @@ const handleBox7Data = async () => { ...@@ -866,14 +866,20 @@ const handleBox7Data = async () => {
if (t1 !== t2) return t1 - t2; if (t1 !== t2) return t1 - t2;
return (b.value ?? 0) - (a.value ?? 0); return (b.value ?? 0) - (a.value ?? 0);
}); });
const selectParam = {
moduleType: '国会法案',
key: '议院委员会',
selectedDate: box7selectetedTime.value,
}
const box7Chart = getDoublePieChart(data1, data2); const box7Chart = getDoublePieChart(data1, data2);
setChart(box7Chart, "box7Chart"); setChart(box7Chart, "box7Chart", true, selectParam);
box7AiData.value = { inner: data1, outer: data2 }; box7AiData.value = { inner: data1, outer: data2 };
} else { } else {
// 接口异常(如500)时,清空图表数据以避免报错或显示错误信息 // 接口异常(如500)时,清空图表数据以避免报错或显示错误信息
box7HasData.value = false; box7HasData.value = false;
setChart({}, "box7Chart"); setChart({}, "box7Chart", true, selectParam);
box7AiData.value = { inner: [], outer: [] }; box7AiData.value = { inner: [], outer: [] };
} }
} catch (error) { } catch (error) {
...@@ -987,9 +993,9 @@ const handleBox9Data = async () => { ...@@ -987,9 +993,9 @@ const handleBox9Data = async () => {
); );
const selectParam = { const selectParam = {
moduleType: '国会法案', moduleType: '国会法案',
proposedDateStart: box9selectetedTime.value, selectedDate: box9selectetedTime.value,
status: box9LegislativeStatus.value === '提出法案' ? 0 : 1 status: box9LegislativeStatus.value === '提出法案' ? 0 : 1,
isInvolveCn: 1
} }
box9ChartInstance = setChart(box9Chart, "box9Chart", true, selectParam); box9ChartInstance = setChart(box9Chart, "box9Chart", true, selectParam);
} }
...@@ -1166,9 +1172,17 @@ const handleBox8Data = async () => { ...@@ -1166,9 +1172,17 @@ const handleBox8Data = async () => {
box8HasData.value = true; box8HasData.value = true;
box8Summary.value = countMap.get("完成立法") || 0; box8Summary.value = countMap.get("完成立法") || 0;
box8StageList.value = stages; box8StageList.value = stages;
const selectParam = {
moduleType: '国会法案',
key: '领域',
selectedDate: box8selectetedTime.value,
isInvolveCn: 1
}
await nextTick(); await nextTick();
const box8Chart = getBox8ChartOption(stages); const box8Chart = getBox8ChartOption(stages);
box8ChartInstance = setChart(box8Chart, "box8Chart"); box8ChartInstance = setChart(box8Chart, "box8Chart", true, selectParam);
} else { } else {
const data = box8MockDataByYear[box8selectetedTime.value]; const data = box8MockDataByYear[box8selectetedTime.value];
if (data && data.stages && data.stages.length > 0) { if (data && data.stages && data.stages.length > 0) {
...@@ -1177,12 +1191,12 @@ const handleBox8Data = async () => { ...@@ -1177,12 +1191,12 @@ const handleBox8Data = async () => {
box8StageList.value = data.stages; box8StageList.value = data.stages;
await nextTick(); await nextTick();
const box8Chart = getBox8ChartOption(data.stages); const box8Chart = getBox8ChartOption(data.stages);
box8ChartInstance = setChart(box8Chart, "box8Chart"); box8ChartInstance = setChart(box8Chart, "box8Chart", true, selectParam);
} else { } else {
box8HasData.value = false; box8HasData.value = false;
box8Summary.value = 0; box8Summary.value = 0;
box8StageList.value = []; box8StageList.value = [];
setChart({}, "box8Chart"); setChart({}, "box8Chart", true, selectParam);
} }
} }
} catch (error) { } catch (error) {
...@@ -1194,12 +1208,12 @@ const handleBox8Data = async () => { ...@@ -1194,12 +1208,12 @@ const handleBox8Data = async () => {
box8StageList.value = data.stages; box8StageList.value = data.stages;
await nextTick(); await nextTick();
const box8Chart = getBox8ChartOption(data.stages); const box8Chart = getBox8ChartOption(data.stages);
box8ChartInstance = setChart(box8Chart, "box8Chart"); box8ChartInstance = setChart(box8Chart, "box8Chart" , true, selectParam);
} else { } else {
box8HasData.value = false; box8HasData.value = false;
box8Summary.value = 0; box8Summary.value = 0;
box8StageList.value = []; box8StageList.value = [];
setChart({}, "box8Chart"); setChart({}, "box8Chart", true, selectParam);
} }
} }
}; };
......
...@@ -39,7 +39,7 @@ const handleClickItem = (item) => { ...@@ -39,7 +39,7 @@ const handleClickItem = (item) => {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
.left { .left {
display: flex; display: flex;
gap: 8px; gap: 8px;
......
...@@ -111,13 +111,13 @@ const siderList = ref([ ...@@ -111,13 +111,13 @@ const siderList = ref([
{ {
name: '科技法案', name: '科技法案',
icon: Icon1, icon: Icon1,
active: true, active: false,
isExpanded: true, isExpanded: false,
children: [ children: [
{ {
name: '国会法案', name: '国会法案',
path: '/dataLibrary/countryBill', path: '/dataLibrary/countryBill',
active: true, active: false,
}, },
{ {
name: '州法案', name: '州法案',
...@@ -477,6 +477,118 @@ const handleClickToolBox = () => { ...@@ -477,6 +477,118 @@ const handleClickToolBox = () => {
}; };
onMounted(() => { onMounted(() => {
const path = route.path
switch (path) {
case '/dataLibrary/countryBill':
siderList.value[0].active = true
siderList.value[0].isExpanded = true
siderList.value[0].children[0].active = true
break
case '/dataLibrary/stateBill':
siderList.value[0].active = true
siderList.value[0].isExpanded = true
siderList.value[0].children[1].active = true
break
case '/dataLibrary/dataDecree':
siderList.value[1].active = true
break
case '/dataLibrary/dataThinkTank':
siderList.value[2].active = true
break
case '/dataLibrary/dataEntityList':
siderList.value[3].active = true
siderList.value[3].isExpanded = true
siderList.value[3].children[0].active = true
break
case '/dataLibrary/dataCommerceControlList':
siderList.value[3].active = true
siderList.value[3].isExpanded = true
siderList.value[3].children[1].active = true
break
case '/dataLibrary/dataEntityListEvent':
siderList.value[3].active = true
siderList.value[3].isExpanded = true
siderList.value[3].children[2].active = true
break
case '/dataLibrary/dataCommerceControlListEvent':
siderList.value[3].active = true
siderList.value[3].isExpanded = true
siderList.value[3].children[3].active = true
break
case '/dataLibrary/sDNList':
siderList.value[4].active = true
siderList.value[4].isExpanded = true
siderList.value[4].children[0].active = true
break
case '/dataLibrary/mREList':
siderList.value[4].active = true
siderList.value[4].isExpanded = true
siderList.value[4].children[1].active = true
break
case '/dataLibrary/sDNListEvent':
siderList.value[4].active = true
siderList.value[4].isExpanded = true
siderList.value[4].children[2].active = true
break
case '/dataLibrary/mREListEvent':
siderList.value[4].active = true
siderList.value[4].isExpanded = true
siderList.value[4].children[3].active = true
break
case '/dataLibrary/case337':
siderList.value[5].active = true
siderList.value[5].isExpanded = true
siderList.value[5].children[0].active = true
break
case '/dataLibrary/case232':
siderList.value[5].active = true
siderList.value[5].isExpanded = true
siderList.value[5].children[1].active = true
break
case '/dataLibrary/case301':
siderList.value[5].active = true
siderList.value[5].isExpanded = true
siderList.value[5].children[2].active = true
break
case '/dataLibrary/congressMan':
siderList.value[6].active = true
siderList.value[6].isExpanded = true
siderList.value[6].children[0].active = true
break
case '/dataLibrary/technologyLeader':
siderList.value[6].active = true
siderList.value[6].isExpanded = true
siderList.value[6].children[1].active = true
break
case '/dataLibrary/minister':
siderList.value[6].active = true
siderList.value[6].isExpanded = true
siderList.value[6].children[2].active = true
break
case '/dataLibrary/thinkTankResearcher':
siderList.value[6].active = true
siderList.value[6].isExpanded = true
siderList.value[6].children[3].active = true
break
case '/dataLibrary/technologyCompany':
siderList.value[7].active = true
siderList.value[7].isExpanded = true
siderList.value[7].children[0].active = true
break
case '/dataLibrary/researchUniversity':
siderList.value[7].active = true
siderList.value[7].isExpanded = true
siderList.value[7].children[1].active = true
break
case '/dataLibrary/keyLab':
siderList.value[7].active = true
siderList.value[7].isExpanded = true
siderList.value[7].children[2].active = true
break
}
}) })
...@@ -683,7 +795,8 @@ onMounted(() => { ...@@ -683,7 +795,8 @@ onMounted(() => {
.icon { .icon {
width: 16px; width: 16px;
height: 16px; height: 16px;
&:hover{
&:hover {
border-radius: 8px; border-radius: 8px;
background: var(--color-primary-10); background: var(--color-primary-10);
} }
......
...@@ -77,7 +77,7 @@ import { useRoute } from "vue-router"; ...@@ -77,7 +77,7 @@ import { useRoute } from "vue-router";
import AiBox from "@/components/AiBox.vue"; import AiBox from "@/components/AiBox.vue";
import { getPersonType } from "@/api/common/index"; import { getPersonType } from "@/api/common/index";
// import { useDraggable } from "@vueuse/core"; // import { useDraggable } from "@vueuse/core";
import ModuleHeader from '@/components/base/ModuleHeader/index.vue' import ModuleHeader from '@/components/base/moduleHeader/index.vue'
import Menu1 from "@/assets/icons/overview/menu1.png"; import Menu1 from "@/assets/icons/overview/menu1.png";
import Menu2 from "@/assets/icons/overview/menu2.png"; import Menu2 from "@/assets/icons/overview/menu2.png";
...@@ -120,7 +120,7 @@ const handleGetPersonType = async () => { ...@@ -120,7 +120,7 @@ const handleGetPersonType = async () => {
personTypeList.value = []; personTypeList.value = [];
} }
window.sessionStorage.setItem("personTypeList", JSON.stringify(personTypeList.value)); window.sessionStorage.setItem("personTypeList", JSON.stringify(personTypeList.value));
} catch (error) {} } catch (error) { }
}; };
const isCurrentOverview = computed(() => { const isCurrentOverview = computed(() => {
...@@ -298,7 +298,7 @@ body { ...@@ -298,7 +298,7 @@ body {
text-align: justify; text-align: justify;
} }
.el-popper[data-popper-placement^="top"] > .el-popper__arrow:before { .el-popper[data-popper-placement^="top"]>.el-popper__arrow:before {
display: none; display: none;
} }
......
...@@ -25,20 +25,21 @@ ...@@ -25,20 +25,21 @@
<img src="@/assets/icons/subject-icon.png" /> <img src="@/assets/icons/subject-icon.png" />
</template> </template>
<el-space :size="16" direction="vertical" fill class="full-width common-padding"> <el-space :size="16" direction="vertical" fill class="full-width common-padding">
<el-space v-for="(item, index) in subjectData.slice(0, 3)" :key="index" class="mouse-hover" <el-space v-for="(item, index) in subjectData.slice(0, 3)" :key="index"
@click="() => gotoNewsDetail(item.newsId)" alignment="center"> @click="() => gotoNewsDetail(item.newsId)" alignment="center">
<common-text class="text-bold" <common-text class="text-bold text-hover"
:color="index === 0 ? 'var(--color-red-100)' : (index === 1 ? 'var(--color-orange-100)' : 'var(--text-primary-65-color)')"> :color="index === 0 ? 'var(--color-red-100)' : (index === 1 ? 'var(--color-orange-100)' : 'var(--text-primary-65-color)')">
{{ `${index + 1}` }} {{ `${index + 1}` }}
</common-text> </common-text>
<common-text class="text-bold" color="var(--text-primary-80-color)">{{ <common-text class="text-bold text-hover" color="var(--text-primary-80-color)">{{
item.newsTitle item.newsTitle
}}</common-text> }}</common-text>
</el-space> </el-space>
<el-space v-for="(item, index) in subjectData.slice(3)" :key="index" class="mouse-hover"> <el-space v-for="(item, index) in subjectData.slice(3)" :key="index"
<common-text class="text-regular" color="var(--text-primary-80-color)">{{ @click="() => gotoNewsDetail(item.newsId)">
<common-text class="text-regular text-hover" color="var(--text-primary-80-color)">{{
"• " + item.newsTitle "• " + item.newsTitle
}}</common-text> }}</common-text>
</el-space> </el-space>
</el-space> </el-space>
</box-background> </box-background>
...@@ -48,6 +49,7 @@ ...@@ -48,6 +49,7 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import '@/styles/common.scss';
import '@/styles/container.scss'; import '@/styles/container.scss';
import '@/styles/radio.scss'; import '@/styles/radio.scss';
import { useGotoNewsModule, useGotoNewsDetail } from "@/router/modules/news"; import { useGotoNewsModule, useGotoNewsDetail } from "@/router/modules/news";
......
...@@ -330,11 +330,11 @@ onMounted(async () => { ...@@ -330,11 +330,11 @@ onMounted(async () => {
.box1 { .box1 {
margin-top: 19px; margin-top: 19px;
width: 1600px; width: 1600px;
height: 1173px;
.box1-main { .box1-main {
margin-top: 8px; margin-top: 8px;
height: 1097px; min-height: 778px;
padding-left: 21px; padding-left: 21px;
padding-right: 50px; padding-right: 50px;
padding-bottom: 21px; padding-bottom: 21px;
...@@ -421,7 +421,7 @@ onMounted(async () => { ...@@ -421,7 +421,7 @@ onMounted(async () => {
.item-box { .item-box {
width: 506px; width: 506px;
height: 100%; min-height: 639.2px;
border-top: 1px solid rgb(234, 236, 238); border-top: 1px solid rgb(234, 236, 238);
.item { .item {
......
import * as echarts from 'echarts' import * as echarts from 'echarts'
/** 政策追踪「研究领域变化趋势」图例分页:每页条数(与概览数量变化趋势逻辑一致,条数按产品要求为 4) */
export const POLICY_TRACKING_LEGEND_PAGE_SIZE = 4
const colorList = [ const colorList = [
'rgba(5, 95, 194, 1)', 'rgba(5, 95, 194, 1)',
'rgba(19, 168, 168, 1)', 'rgba(19, 168, 168, 1)',
...@@ -35,22 +32,16 @@ const parseRgba = (colorStr) => { ...@@ -35,22 +32,16 @@ const parseRgba = (colorStr) => {
/** /**
* @param {{ title: unknown[], data: Array<{ name: string, value: unknown[], color?: string }> }} chartInput * @param {{ title: unknown[], data: Array<{ name: string, value: unknown[], color?: string }> }} chartInput
* @param {{ legendShowCount?: number, legendPageIndex?: number }} [options]
*/ */
const getMultiLineChart = (chartInput, options = {}) => { const getMultiLineChart = (chartInput) => {
const title = chartInput.title const title = chartInput.title
const series = chartInput.data || [] const series = chartInput.data || []
const legendShowCount =
typeof options.legendShowCount === 'number' && options.legendShowCount > 0
? options.legendShowCount
: POLICY_TRACKING_LEGEND_PAGE_SIZE
const rawPageIndex = Number(options.legendPageIndex) || 0
const allNames = series.map((item) => item.name) const allNames = series.map((item) => item.name)
const pageCount = Math.max(1, Math.ceil(allNames.length / legendShowCount)) const lineSize = Math.ceil(allNames.length / 3)
const legendPageIndex = Math.min(Math.max(0, rawPageIndex), pageCount - 1) const legendLine1 = allNames.slice(0, lineSize)
const legendStart = legendPageIndex * legendShowCount const legendLine2 = allNames.slice(lineSize, lineSize * 2)
const legendData = allNames.slice(legendStart, legendStart + legendShowCount) const legendLine3 = allNames.slice(lineSize * 2)
const xCount = Array.isArray(title) ? title.length : 0 const xCount = Array.isArray(title) ? title.length : 0
const labelFontSize = xCount > 8 ? 10 : xCount > 5 ? 11 : 12 const labelFontSize = xCount > 8 ? 10 : xCount > 5 ? 11 : 12
...@@ -91,31 +82,71 @@ const getMultiLineChart = (chartInput, options = {}) => { ...@@ -91,31 +82,71 @@ const getMultiLineChart = (chartInput, options = {}) => {
}, },
/* 贴满 #box3Chart:四边 0,由 containLabel 在网格内为轴文字留位,避免左侧/底部大块留白 */ /* 贴满 #box3Chart:四边 0,由 containLabel 在网格内为轴文字留位,避免左侧/底部大块留白 */
grid: { grid: {
top: 50, top: 92,
right: 10, right: 10,
bottom: 0, bottom: 0,
left: 20, left: 20,
containLabel: true containLabel: true
}, },
legend: { legend: [
show: true, {
type: 'plain', show: true,
data: legendData, type: 'plain',
top: 4, data: legendLine1,
left: 'center', top: 4,
icon: 'circle', left: 'center',
textStyle: { icon: 'circle',
fontFamily: 'Source Han Sans CN', textStyle: {
fontWeight: 400, fontFamily: 'Source Han Sans CN',
fontSize: 14, fontWeight: 400,
lineHeight: 24, fontSize: 14,
letterSpacing: 0, lineHeight: 24,
align: 'left', letterSpacing: 0,
color: 'rgb(95, 101, 108)' align: 'left',
color: 'rgb(95, 101, 108)'
},
itemWidth: 12,
itemHeight: 12
}, },
itemWidth: 12, {
itemHeight: 12 show: legendLine2.length > 0,
}, type: 'plain',
data: legendLine2,
top: 30,
left: 'center',
icon: 'circle',
textStyle: {
fontFamily: 'Source Han Sans CN',
fontWeight: 400,
fontSize: 14,
lineHeight: 24,
letterSpacing: 0,
align: 'left',
color: 'rgb(95, 101, 108)'
},
itemWidth: 12,
itemHeight: 12
},
{
show: legendLine3.length > 0,
type: 'plain',
data: legendLine3,
top: 56,
left: 'center',
icon: 'circle',
textStyle: {
fontFamily: 'Source Han Sans CN',
fontWeight: 400,
fontSize: 14,
lineHeight: 24,
letterSpacing: 0,
align: 'left',
color: 'rgb(95, 101, 108)'
},
itemWidth: 12,
itemHeight: 12
}
],
color: colorList, color: colorList,
xAxis: [ xAxis: [
{ {
...@@ -142,6 +173,16 @@ const getMultiLineChart = (chartInput, options = {}) => { ...@@ -142,6 +173,16 @@ const getMultiLineChart = (chartInput, options = {}) => {
yAxis: [ yAxis: [
{ {
type: 'value', type: 'value',
name: '数量',
nameLocation: 'end',
nameGap: 20,
nameTextStyle: {
color: 'rgb(132, 136, 142)',
fontFamily: 'Source Han Sans CN',
fontWeight: 400,
fontSize: 11,
padding: [0, 0, 0, -20] // 👈 这个是左移 4px(上、右、下、左)
},
splitNumber: 4, splitNumber: 4,
axisLabel: { axisLabel: {
color: 'rgb(132, 136, 142)', color: 'rgb(132, 136, 142)',
......
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论