Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
1
合并请求
1
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
6714a4e9
提交
6714a4e9
authored
3月 22, 2026
作者:
朱政
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat:核心成员
上级
58537cbd
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
16 个修改的文件
包含
671 行增加
和
136 行删除
+671
-136
.env.development
.env.development
+4
-2
.env.production
.env.production
+4
-2
index.js
src/api/aiAnalysis/index.js
+282
-11
request.js
src/api/request.js
+16
-2
overview.js
src/api/thinkTank/overview.js
+29
-7
index.vue
src/components/base/TipTab/index.vue
+5
-3
setChart.js
src/utils/setChart.js
+4
-0
index.vue
src/views/thinkTank/ThinkTankDetail/PolicyTracking/index.vue
+0
-0
multiLineChart.js
...nk/ThinkTankDetail/PolicyTracking/utils/multiLineChart.js
+130
-49
index.vue
src/views/thinkTank/ThinkTankDetail/thinkInfo/index.vue
+66
-52
index.vue
src/views/thinkTank/TipTab/index.vue
+50
-0
tip-icon.svg
src/views/thinkTank/TipTab/tip-icon.svg
+6
-0
index.vue
src/views/thinkTank/allThinkTank/index.vue
+31
-3
index.vue
src/views/thinkTank/index.vue
+0
-0
multiLineChart.js
src/views/thinkTank/utils/multiLineChart.js
+42
-4
piechart.js
src/views/thinkTank/utils/piechart.js
+2
-1
没有找到文件。
.env.development
浏览文件 @
6714a4e9
VITE_BASE_API= '/api'
VITE_BASE_API= '/api'
\ No newline at end of file
# 图表解读等 /aiAnalysis 代理服务密钥(勿提交到公开仓库时可改为本地 .env.local)
VITE_AI_ANALYSIS_API_KEY=aircasKEY19491001
\ No newline at end of file
.env.production
浏览文件 @
6714a4e9
# 线上地址
# 线上地址
VITE_BASE_API= '/api'
VITE_BASE_API= '/api'
\ No newline at end of file
# 图表解读等 AI 服务(与部署环境一致时填写)
VITE_AI_ANALYSIS_API_KEY=aircasKEY19491001
\ No newline at end of file
src/api/aiAnalysis/index.js
浏览文件 @
6714a4e9
import
request
from
"@/api/request.js"
;
import
{
getToken
}
from
"@/api/request.js"
;
const
CHART_INTERPRETATION_PATH
=
"/aiAnalysis/chart_interpretation"
;
/**
* 从接口各类返回结构中取出「解读」纯文本(含流式拼出的 ```json [...] ``` 字符串)
* @param {unknown} raw
* @returns {string}
*/
export
function
extractChartInterpretationText
(
raw
)
{
if
(
raw
==
null
||
raw
===
""
)
{
return
""
;
}
if
(
Array
.
isArray
(
raw
))
{
const
first
=
raw
[
0
];
if
(
first
&&
typeof
first
===
"object"
)
{
const
v
=
first
[
"解读"
]
??
first
.
interpretation
;
if
(
v
!=
null
&&
String
(
v
).
trim
())
{
return
String
(
v
).
trim
();
}
}
return
""
;
}
if
(
typeof
raw
===
"object"
)
{
if
(
Array
.
isArray
(
raw
.
data
))
{
return
extractChartInterpretationText
(
raw
.
data
);
}
if
(
typeof
raw
.
data
===
"string"
)
{
return
extractChartInterpretationText
(
raw
.
data
);
}
if
(
typeof
raw
.
text
===
"string"
)
{
return
extractChartInterpretationText
(
raw
.
text
);
}
const
v
=
raw
[
"解读"
]
??
raw
.
interpretation
;
if
(
v
!=
null
)
{
return
String
(
v
).
trim
();
}
for
(
const
key
of
[
"result"
,
"content"
,
"items"
,
"list"
])
{
if
(
Array
.
isArray
(
raw
[
key
]))
{
const
t
=
extractChartInterpretationText
(
raw
[
key
]);
if
(
t
)
{
return
t
;
}
}
}
return
""
;
}
if
(
typeof
raw
===
"string"
)
{
const
t
=
raw
.
trim
();
if
(
!
t
)
{
return
""
;
}
try
{
return
extractChartInterpretationText
(
JSON
.
parse
(
t
));
}
catch
{
/* 非纯 JSON */
}
const
unfenced
=
t
.
replace
(
/^```
(?:
json
)?\s
*/i
,
""
)
.
replace
(
/```
\s
*$/i
,
""
)
.
trim
();
try
{
return
extractChartInterpretationText
(
JSON
.
parse
(
unfenced
));
}
catch
{
/* 继续尝试截取数组 */
}
const
arrMatch
=
unfenced
.
match
(
/
\[\s
*
\{[\s\S]
*"解读"
[\s\S]
*
\}\s
*
\]
/
);
if
(
arrMatch
)
{
try
{
return
extractChartInterpretationText
(
JSON
.
parse
(
arrMatch
[
0
]));
}
catch
{
/* ignore */
}
}
const
byRegex
=
extractInterpretationByRegex
(
t
);
if
(
byRegex
)
{
return
byRegex
;
}
return
""
;
}
return
""
;
}
/**
* 从非严格 JSON 的文本中用正则提取「解读」字段(流式拼完后仍解析失败时的兜底)
* @param {string} s
* @returns {string}
*/
function
extractInterpretationByRegex
(
s
)
{
if
(
!
s
||
typeof
s
!==
"string"
)
{
return
""
;
}
const
m
=
s
.
match
(
/"解读"
\s
*:
\s
*"
((?:[^
"
\\]
|
\\
.
)
*
)
"/
);
if
(
!
m
||
!
m
[
1
])
{
return
""
;
}
return
m
[
1
].
replace
(
/
\\
n/g
,
"
\
n"
).
replace
(
/
\\
r/g
,
"
\
r"
).
replace
(
/
\\
"/g
,
'"'
).
replace
(
/
\\\\
/g
,
"
\
\"
);
}
/**
* 解析 SSE 文本行(流式或整包响应体)
* @param {string} fullText
* @returns {string}
*/
function parseSseChartInterpretationLines(fullText) {
let textFragments = "";
let lastFromArray = "";
/** 单行 JSON.parse 失败的 data 片段,多为跨多行的同一 JSON(如数组) */
const unparsedPayloads = [];
const lines = fullText.split(/
\
r?
\
n/);
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed.startsWith("
data
:
")) {
continue;
}
const payload = trimmed.slice(5).trim();
if (!payload || payload === "
[
DONE
]
") {
continue;
}
let parsed;
try {
parsed = JSON.parse(payload);
} catch {
unparsedPayloads.push(payload);
continue;
}
if (Array.isArray(parsed)) {
const t = extractChartInterpretationText(parsed);
if (t) {
lastFromArray = t;
}
continue;
}
if (parsed && typeof parsed.text === "
string
") {
textFragments += parsed.text;
}
}
if (lastFromArray) {
return lastFromArray;
}
/* 合并跨多行 data: 的 JSON(例如 data: [ 、 data: { 、 data: "
图表标题
"…) */
if (unparsedPayloads.length > 0) {
const joinedNewline = unparsedPayloads.join("
\
n
").trim();
for (const candidate of [joinedNewline, unparsedPayloads.join("").trim()]) {
if (!candidate) {
continue;
}
try {
const t = extractChartInterpretationText(JSON.parse(candidate));
if (t) {
return t;
}
} catch {
/* ignore */
}
const t2 = extractChartInterpretationText(candidate);
if (t2) {
return t2;
}
const t3 = extractInterpretationByRegex(candidate);
if (t3) {
return t3;
}
}
}
const fromFragments = extractChartInterpretationText(textFragments);
if (fromFragments) {
return fromFragments;
}
const fragRegex = extractInterpretationByRegex(textFragments);
if (fragRegex) {
return fragRegex;
}
return extractChartInterpretationText(textFragments.trim());
}
/**
* @param {ReadableStream<Uint8Array> | null} body
* @param {(chunk: string) => void} [onDelta]
* @returns {Promise<string>}
*/
async function readSseChartInterpretationStream(body, onDelta) {
if (!body) {
return "";
}
const reader = body.getReader();
const decoder = new TextDecoder();
let all = "";
let lastPushed = "";
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
all += decoder.decode(value, { stream: true });
const now = parseSseChartInterpretationLines(all);
if (now && now !== lastPushed) {
lastPushed = now;
onDelta?.(now);
}
}
all += decoder.decode();
const fin = parseSseChartInterpretationLines(all);
if (fin && fin !== lastPushed) {
onDelta?.(fin);
}
return fin;
}
/**
* POST 图表解读:支持 SSE(data: {"
text
":"
...
"} / data: [...])与普通 JSON
* @param {Record<string, unknown>} body
* @param {(interpretation: string) => void} [onInterpretationDelta] 流式过程中有可读解读时回调
* @returns {Promise<string>} 解读正文
*/
export async function fetchChartInterpretation(body, onInterpretationDelta) {
const headers = {
"
Content
-
Type
": "
application
/
json
",
Accept: "
text
/
event
-
stream
,
application
/
json
,
text
/
plain
,
*
/*"
};
const token = getToken();
if (token) {
headers.token = token;
}
const aiApiKey = import.meta.env.VITE_AI_ANALYSIS_API_KEY;
if (aiApiKey) {
headers["X-API-Key"] = aiApiKey;
}
const res = await fetch(CHART_INTERPRETATION_PATH, {
method: "POST",
headers,
body: JSON.stringify(body)
});
if (!res.ok) {
const errText = await res.text().catch(() => "");
throw new Error(errText || `Request failed with status ${res.status}`);
}
const contentType = res.headers.get("content-type") || "";
if (contentType.includes("text/event-stream") || contentType.includes("event-stream")) {
return readSseChartInterpretationStream(res.body, onInterpretationDelta);
}
const text = await res.text();
if (/^\s*data:/m.test(text)) {
const out = parseSseChartInterpretationLines(text);
if (out) {
onInterpretationDelta?.(out);
}
return out;
}
let parsed;
try {
parsed = JSON.parse(text);
} catch {
const extracted = extractChartInterpretationText(text);
return extracted || text.trim();
}
const extracted = extractChartInterpretationText(parsed);
if (extracted) {
return extracted;
}
if (typeof parsed === "string") {
return extractChartInterpretationText(parsed) || parsed;
}
return extractChartInterpretationText(text) || text.trim();
}
// 图表解读(流式)
/**
/**
* @param {text}
* 图表解读(POST /chart_interpretation),兼容流式 SSE 与一次性 JSON
* @param {{ text: string }} data
* @param {{ onInterpretationDelta?: (s: string) => void }} [options]
* @returns {Promise<string>} 解读纯文本
*/
*/
export
function
getChartAnalysis
(
data
)
{
export
async
function
getChartAnalysis
(
data
,
options
=
{})
{
return
request
({
const
{
onInterpretationDelta
}
=
options
;
method
:
'POST'
,
return
fetchChartInterpretation
(
data
,
onInterpretationDelta
);
url
:
`/aiAnalysis/chart_interpretation`
,
}
data
,
})
}
\ No newline at end of file
src/api/request.js
浏览文件 @
6714a4e9
...
@@ -51,6 +51,14 @@ service.interceptors.request.use(config => {
...
@@ -51,6 +51,14 @@ service.interceptors.request.use(config => {
config
.
headers
[
'token'
]
=
token
config
.
headers
[
'token'
]
=
token
// config.headers['Authorization'] = `Bearer ${token}` // 如果后端需要Bearer格式可以使用这个
// config.headers['Authorization'] = `Bearer ${token}` // 如果后端需要Bearer格式可以使用这个
}
}
// 图表解读等 AI 分析服务(Vite 代理 /aiAnalysis)需要 X-API-Key
const
reqUrl
=
String
(
config
.
url
||
''
)
if
(
reqUrl
.
includes
(
'aiAnalysis'
))
{
const
aiApiKey
=
import
.
meta
.
env
.
VITE_AI_ANALYSIS_API_KEY
if
(
aiApiKey
)
{
config
.
headers
[
'X-API-Key'
]
=
aiApiKey
}
}
return
config
return
config
},
error
=>
{
},
error
=>
{
console
.
log
(
error
)
console
.
log
(
error
)
...
@@ -82,8 +90,14 @@ service.interceptors.response.use(
...
@@ -82,8 +90,14 @@ service.interceptors.response.use(
// 重复请求触发的取消不提示错误
// 重复请求触发的取消不提示错误
if
(
isCanceledError
)
return
Promise
.
reject
(
error
)
if
(
isCanceledError
)
return
Promise
.
reject
(
error
)
// 处理token过期或无效的情况
// 处理token过期或无效的情况(排除 AI 分析服务:其 401 多为 API Key 问题)
if
(
error
.
response
&&
(
error
.
response
.
status
===
401
||
error
.
response
.
status
===
403
))
{
const
errUrl
=
String
(
error
.
config
?.
url
||
''
)
const
isAiAnalysisRequest
=
errUrl
.
includes
(
'aiAnalysis'
)
if
(
error
.
response
&&
(
error
.
response
.
status
===
401
||
error
.
response
.
status
===
403
)
&&
!
isAiAnalysisRequest
)
{
ElMessage
({
ElMessage
({
message
:
'Token已过期,请重新登录'
,
message
:
'Token已过期,请重新登录'
,
type
:
'error'
,
type
:
'error'
,
...
...
src/api/thinkTank/overview.js
浏览文件 @
6714a4e9
...
@@ -35,13 +35,19 @@ export function getThinkTankRiskSignal() {
...
@@ -35,13 +35,19 @@ export function getThinkTankRiskSignal() {
})
})
}
}
// 政策建议趋势分布
/**
* 政策建议趋势分布(数量变化趋势)
* @param {{ startDate: string, endDate: string }} params - 如 2024-01-01 ~ 2024-12-31
*/
export
function
getThinkTankPolicyIndustryChange
(
params
)
{
export
function
getThinkTankPolicyIndustryChange
(
params
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
"GET"
,
url
:
`/api/thinkTankOverview/policyIndustryChange/
${
params
}
`
,
url
:
`/api/thinkTankOverview/policyIndustryChange`
,
params
:
{
})
startDate
:
params
.
startDate
,
endDate
:
params
.
endDate
}
});
}
}
// 政策建议领域分布
// 政策建议领域分布
...
@@ -193,15 +199,31 @@ export function getThinkPolicyIndustryChange(params) {
...
@@ -193,15 +199,31 @@ export function getThinkPolicyIndustryChange(params) {
})
})
}
}
//获取智库政策
/**
* 获取智库政策(政策追踪列表)
* GET /api/thinkTankInfo/policy
* Query: thinkTankId, startDate, endDate, orgIds, domainIds(科技领域/智库领域,逗号分隔 id), pageNum, pageSize, sortField, sortOrder, sortFun, reportId 等
*/
export
function
getThinkPolicy
(
params
)
{
export
function
getThinkPolicy
(
params
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
'GET'
,
url
:
`/api/thinkTankInfo/policy/
${
params
.
id
}
/
${
params
.
startDate
}
`
,
url
:
'/api/thinkTankInfo/policy'
,
params
params
})
})
}
}
/**
* 政府机构字典(政策追踪-涉及部门筛选项)
* GET /api/commonDict/gov/agency
* @returns {Promise<{ code: number, data: Array<{ id: string, name: string }> }>}
*/
export
function
getGovAgencyDict
()
{
return
request
({
method
:
'GET'
,
url
:
'/api/commonDict/gov/agency'
})
}
//智库百科基本信息
//智库百科基本信息
export
function
getThinkTankInfoBasic
(
params
)
{
export
function
getThinkTankInfoBasic
(
params
)
{
return
request
({
return
request
({
...
...
src/components/base/TipTab/index.vue
浏览文件 @
6714a4e9
...
@@ -24,17 +24,19 @@ const props = defineProps({
...
@@ -24,17 +24,19 @@ const props = defineProps({
<
/script
>
<
/script
>
<
style
lang
=
"scss"
scoped
>
<
style
lang
=
"scss"
scoped
>
.
tip
-
wrapper
{
.
tip
-
wrapper
{
width
:
100
%
;
width
:
100
%
;
display
:
flex
;
display
:
flex
;
gap
:
8
px
;
gap
:
8
px
;
justify
-
content
:
center
;
justify
-
content
:
center
;
align
-
items
:
center
;
align
-
items
:
center
;
height
:
22
px
;
height
:
22
px
;
.
icon
{
.
icon
{
width
:
16
px
;
width
:
16
px
;
height
:
16
px
;
height
:
16
px
;
img
{
img
{
width
:
100
%
;
width
:
100
%
;
height
:
100
%
;
height
:
100
%
;
}
}
...
...
src/utils/setChart.js
浏览文件 @
6714a4e9
...
@@ -9,6 +9,10 @@ const setChart = (option, chartId) => {
...
@@ -9,6 +9,10 @@ const setChart = (option, chartId) => {
chartDom
.
removeAttribute
(
"_echarts_instance_"
);
chartDom
.
removeAttribute
(
"_echarts_instance_"
);
let
chart
=
echarts
.
init
(
chartDom
);
let
chart
=
echarts
.
init
(
chartDom
);
chart
.
setOption
(
option
);
chart
.
setOption
(
option
);
// 容器可能受布局/异步渲染影响,强制一次 resize 保证 canvas 与容器一致
setTimeout
(()
=>
{
chart
.
resize
();
},
0
);
return
chart
;
return
chart
;
};
};
...
...
src/views/thinkTank/ThinkTankDetail/PolicyTracking/index.vue
浏览文件 @
6714a4e9
差异被折叠。
点击展开。
src/views/thinkTank/ThinkTankDetail/PolicyTracking/utils/multiLineChart.js
浏览文件 @
6714a4e9
import
*
as
echarts
from
'echarts'
import
*
as
echarts
from
'echarts'
import
{
size
,
split
}
from
'lodash'
// data: [{ name: string, value: number[] }, ...]
/** 政策追踪「研究领域变化趋势」图例分页:每页条数(与概览数量变化趋势逻辑一致,条数按产品要求为 4) */
const
getMultiLineChart
=
(
dataX
,
seriesData
)
=>
{
export
const
POLICY_TRACKING_LEGEND_PAGE_SIZE
=
4
const
colorList
=
[
'rgba(5, 95, 194, 1)'
,
'rgba(19, 168, 168, 1)'
,
'rgba(250, 140, 22, 1)'
,
'rgba(114, 46, 209, 1)'
,
'rgba(115, 209, 61, 1)'
,
'rgba(206, 79, 81, 1)'
,
'rgba(145, 202, 255, 1)'
,
'rgba(95, 101, 108, 1)'
,
'rgba(250, 84, 28, 1)'
,
'rgba(47, 84, 235, 1)'
,
'rgba(64, 150, 255, 1)'
,
'rgba(34, 41, 52, 1)'
,
'rgba(173, 198, 255, 1)'
,
'rgba(255, 169, 64, 1)'
]
const
parseRgba
=
(
colorStr
)
=>
{
const
match
=
colorStr
.
match
(
/rgba
\((\d
+
)
,
\s
*
(\d
+
)
,
\s
*
(\d
+
)
,
\s
*
(\d
+
(\.\d
+
)?)\)
/
)
if
(
match
)
{
return
{
r
:
parseInt
(
match
[
1
],
10
),
g
:
parseInt
(
match
[
2
],
10
),
b
:
parseInt
(
match
[
3
],
10
),
a
:
parseFloat
(
match
[
4
])
}
}
return
{
r
:
0
,
g
:
0
,
b
:
0
,
a
:
1
}
}
/**
* @param {{ title: unknown[], data: Array<{ name: string, value: unknown[], color?: string }> }} chartInput
* @param {{ legendShowCount?: number, legendPageIndex?: number }} [options]
*/
const
getMultiLineChart
=
(
chartInput
,
options
=
{})
=>
{
const
title
=
chartInput
.
title
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
pageCount
=
Math
.
max
(
1
,
Math
.
ceil
(
allNames
.
length
/
legendShowCount
))
const
legendPageIndex
=
Math
.
min
(
Math
.
max
(
0
,
rawPageIndex
),
pageCount
-
1
)
const
legendStart
=
legendPageIndex
*
legendShowCount
const
legendData
=
allNames
.
slice
(
legendStart
,
legendStart
+
legendShowCount
)
const
xCount
=
Array
.
isArray
(
title
)
?
title
.
length
:
0
const
labelFontSize
=
xCount
>
8
?
10
:
xCount
>
5
?
11
:
12
const
labelRotate
=
xCount
>
6
?
28
:
0
const
echartsSeries
=
series
.
map
((
item
,
index
)
=>
{
const
baseColor
=
item
.
color
||
colorList
[
index
%
colorList
.
length
]
||
`rgba(
${
Math
.
floor
(
Math
.
random
()
*
256
)}
,
${
Math
.
floor
(
Math
.
random
()
*
256
)}
,
${
Math
.
floor
(
Math
.
random
()
*
256
)}
, 1)`
const
{
r
,
g
,
b
}
=
parseRgba
(
baseColor
)
return
{
name
:
item
.
name
,
type
:
'line'
,
smooth
:
true
,
areaStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
`rgba(
${
r
}
,
${
g
}
,
${
b
}
, 0.3)`
},
{
offset
:
1
,
color
:
`rgba(
${
r
}
,
${
g
}
,
${
b
}
, 0)`
}
])
},
symbolSize
:
8
,
emphasis
:
{
focus
:
'series'
},
data
:
item
.
value
}
})
return
{
return
{
tooltip
:
{
tooltip
:
{
trigger
:
'axis'
,
trigger
:
'axis'
,
...
@@ -13,72 +89,78 @@ const getMultiLineChart = (dataX, seriesData) => {
...
@@ -13,72 +89,78 @@ const getMultiLineChart = (dataX, seriesData) => {
}
}
}
}
},
},
/* 贴满 #box3Chart:四边 0,由 containLabel 在网格内为轴文字留位,避免左侧/底部大块留白 */
grid
:
{
grid
:
{
top
:
'15%'
,
top
:
50
,
right
:
'5%'
,
right
:
10
,
bottom
:
'5%'
,
bottom
:
0
,
left
:
'5%'
,
left
:
20
,
containLabel
:
true
containLabel
:
true
},
},
legend
:
{
legend
:
{
icon
:
'circle'
,
show
:
true
,
show
:
true
,
top
:
10
,
type
:
'plain'
,
left
:
'10%'
,
data
:
legendData
,
top
:
4
,
left
:
'center'
,
icon
:
'circle'
,
textStyle
:
{
textStyle
:
{
fontSize
:
16
,
fontFamily
:
'Source Han Sans CN'
,
fontFamily
:
'Source Han Sans CN'
,
fontWeight
:
400
,
fontWeight
:
400
,
fontSize
:
14
,
lineHeight
:
24
,
lineHeight
:
24
,
letterSpacing
:
0
,
letterSpacing
:
0
,
align
:
'left'
,
align
:
'left'
,
color
:
'rgb(95, 101, 108)'
color
:
'rgb(95, 101, 108)'
},
},
itemWidth
:
12
,
itemWidth
:
12
,
itemHeight
:
12
,
itemHeight
:
12
},
},
color
:
colorList
,
xAxis
:
[
xAxis
:
[
{
{
type
:
'category'
,
type
:
'category'
,
boundaryGap
:
false
,
boundaryGap
:
false
,
data
:
dataX
,
data
:
title
,
axisLine
:
{
axisLine
:
{
lineStyle
:
{
lineStyle
:
{
color
:
'rgb(231, 243, 255)'
,
color
:
'rgb(231, 243, 255)'
},
}
},
},
axisLabel
:
{
axisLabel
:
{
color
:
'rgb(132, 136, 142)'
,
color
:
'rgb(132, 136, 142)'
,
fontFamily
:
'Microsoft YaHei'
,
fontFamily
:
'Source Han Sans CN'
,
fontWeight
:
400
,
fontWeight
:
400
,
fontSize
:
12
,
fontSize
:
labelFontSize
,
interval
:
0
,
hideOverlap
:
false
,
rotate
:
labelRotate
,
},
margin
:
6
}
}
}
],
],
yAxis
:
[
yAxis
:
[
{
{
type
:
'value'
,
type
:
'value'
,
splitLine
:{
splitNumber
:
4
,
show
:
true
,
axisLabel
:
{
lineStyle
:{
color
:
'rgb(132, 136, 142)'
,
color
:
"rgb(231, 243, 255)"
,
fontFamily
:
'Source Han Sans CN'
,
type
:
'dashed'
fontWeight
:
400
,
fontSize
:
11
,
margin
:
6
},
splitLine
:
{
show
:
true
,
lineStyle
:
{
color
:
'rgb(231, 243, 255)'
,
type
:
'dashed'
}
}
}
}
}
}
],
],
series
:
(
seriesData
||
[]).
map
(
item
=>
({
series
:
echartsSeries
name
:
item
.
name
,
type
:
'line'
,
emphasis
:
{
focus
:
'series'
},
data
:
item
.
value
}))
}
}
}
}
export
default
getMultiLineChart
export
default
getMultiLineChart
\ No newline at end of file
src/views/thinkTank/ThinkTankDetail/thinkInfo/index.vue
浏览文件 @
6714a4e9
...
@@ -180,7 +180,7 @@
...
@@ -180,7 +180,7 @@
</div>
</div>
</AnalysisBox>
</AnalysisBox>
</div>
</div>
<div
class=
"box"
>
<div
class=
"box
box-core-researchers
"
>
<!--
<div
class=
"box-header"
>
<!--
<div
class=
"box-header"
>
<div
class=
"header-left"
></div>
<div
class=
"header-left"
></div>
<div
class=
"title"
>
核心研究人员
</div>
<div
class=
"title"
>
核心研究人员
</div>
...
@@ -213,16 +213,17 @@
...
@@ -213,16 +213,17 @@
</div>
</div>
</div>
</div>
</div>
-->
</div>
-->
<AnalysisBox
title=
"核心研究人员"
>
<AnalysisBox
title=
"核心研究人员"
width=
"1104px"
height=
"900px"
>
<div
class=
"box3-main"
>
<div
class=
"box3-main"
>
<div
class=
"box3-main-
left
"
>
<div
class=
"box3-main-
top
"
>
<div
id=
"box3Chart"
></div>
<div
id=
"box3Chart"
></div>
<div
class=
"source"
>
<div
class=
"info"
><img
src=
"./images/image-exclamation.png"
></div>
<div
class=
"text"
>
数据来源:美国国会官网,数据时间:2015.1至2025.12
</div>
</div>
</div>
</div>
<div
class=
"box3-main-right"
>
<div
class=
"source"
>
<div
class=
"info"
><img
src=
"./images/image-exclamation.png"
></div>
<div
class=
"text"
>
数据来源:美国国会官网,数据时间:2015.1至2025.12
</div>
</div>
<div
class=
"box3-main-bottom"
>
<div
class=
"box3-right-item"
v-for=
"(item, index) in box3RightData"
:key=
"index"
>
<div
class=
"box3-right-item"
v-for=
"(item, index) in box3RightData"
:key=
"index"
>
<div
class=
"icon"
@
click=
"handleClickPerson(item)"
>
<div
class=
"icon"
@
click=
"handleClickPerson(item)"
>
<img
:src=
"item.imageUrl ? item.imageUrl : DefaultIcon1"
alt=
""
/>
<img
:src=
"item.imageUrl ? item.imageUrl : DefaultIcon1"
alt=
""
/>
...
@@ -244,7 +245,7 @@
...
@@ -244,7 +245,7 @@
</
template
>
</
template
>
<
script
setup
>
<
script
setup
>
import
{
ref
,
onMounted
}
from
"vue"
;
import
{
ref
,
onMounted
,
nextTick
}
from
"vue"
;
import
setChart
from
"@/utils/setChart"
;
import
setChart
from
"@/utils/setChart"
;
import
getPieChart
from
"./utils/piechart"
;
import
getPieChart
from
"./utils/piechart"
;
import
getTreeMapChart
from
"./utils/treeMapChart"
;
import
getTreeMapChart
from
"./utils/treeMapChart"
;
...
@@ -602,7 +603,7 @@ const handleBox3Chart = () => {
...
@@ -602,7 +603,7 @@ const handleBox3Chart = () => {
const
box3Chart
=
getTreeMapChart
(
treemapData
);
const
box3Chart
=
getTreeMapChart
(
treemapData
);
delete
box3Chart
.
series
[
0
].
itemStyle
;
delete
box3Chart
.
series
[
0
].
itemStyle
;
box3Chart
.
series
[
0
].
sort
=
false
;
box3Chart
.
series
[
0
].
sort
=
false
;
// 图表充满 box3-main-
left
,左右各留 12px
// 图表充满 box3-main-
top
,左右各留 12px
box3Chart
.
series
[
0
].
left
=
12
;
box3Chart
.
series
[
0
].
left
=
12
;
box3Chart
.
series
[
0
].
right
=
12
;
box3Chart
.
series
[
0
].
right
=
12
;
box3Chart
.
series
[
0
].
top
=
0
;
box3Chart
.
series
[
0
].
top
=
0
;
...
@@ -610,11 +611,10 @@ const handleBox3Chart = () => {
...
@@ -610,11 +611,10 @@ const handleBox3Chart = () => {
// 方块内文字与区域标题统一为黑色,方块内文字加 1px 白色描边(等效 CSS text-stroke: 1px #fff)
// 方块内文字与区域标题统一为黑色,方块内文字加 1px 白色描边(等效 CSS text-stroke: 1px #fff)
box3Chart
.
series
[
0
].
label
=
{
box3Chart
.
series
[
0
].
label
=
{
...
box3Chart
.
series
[
0
].
label
,
...
box3Chart
.
series
[
0
].
label
,
color
:
'rgb(51,51,51)'
,
color
:
'white'
,
textBorderColor
:
'#fff'
,
textBorderWidth
:
0.7
,
fontSize
:
16
,
// 可选:白色阴影兜底
textShadowColor
:
'#fff'
,
textShadowBlur
:
0
,
textShadowBlur
:
0
,
textShadowOffsetX
:
0
,
textShadowOffsetX
:
0
,
textShadowOffsetY
:
0
textShadowOffsetY
:
0
...
@@ -739,7 +739,9 @@ onMounted(() => {
...
@@ -739,7 +739,9 @@ onMounted(() => {
handleGetThinkTankResearchAreae
()
handleGetThinkTankResearchAreae
()
handleGetThinkPerson
()
handleGetThinkPerson
()
handleBox3Chart
();
nextTick
(()
=>
{
handleBox3Chart
();
});
});
});
</
script
>
</
script
>
...
@@ -876,7 +878,8 @@ onMounted(() => {
...
@@ -876,7 +878,8 @@ onMounted(() => {
.right
{
.right
{
width
:
1104px
;
width
:
1104px
;
height
:
1245px
;
/* 三栏:390 + 390 + 900,间距 16×2 */
height
:
1712px
;
margin-top
:
16px
;
margin-top
:
16px
;
display
:
flex
;
display
:
flex
;
flex-direction
:
column
;
flex-direction
:
column
;
...
@@ -885,6 +888,7 @@ onMounted(() => {
...
@@ -885,6 +888,7 @@ onMounted(() => {
.box
{
.box
{
width
:
1104px
;
width
:
1104px
;
height
:
390px
;
height
:
390px
;
flex-shrink
:
0
;
// box-sizing: border-box;
// box-sizing: border-box;
// border: 1px solid rgba(234, 236, 238, 1);
// border: 1px solid rgba(234, 236, 238, 1);
// border-radius: 10px;
// border-radius: 10px;
...
@@ -1230,59 +1234,62 @@ onMounted(() => {
...
@@ -1230,59 +1234,62 @@ onMounted(() => {
.box3-main
{
.box3-main
{
display
:
flex
;
display
:
flex
;
flex-direction
:
column
;
.box3-main-left
{
.box3-main-top
{
width
:
536px
;
width
:
1100px
;
height
:
326px
;
height
:
372px
;
margin-left
:
9px
;
box-sizing
:
border-box
;
box-sizing
:
border-box
;
padding-left
:
35px
;
#box3Chart
{
#box3Chart
{
width
:
536
px
;
width
:
1035
px
;
height
:
290
px
;
height
:
372
px
;
}
}
.source
{
margin
:
0
auto
;
margin-top
:
10px
;
/* 上下0,左右自动居中 */
width
:
370px
;
height
:
22px
;
display
:
flex
;
.info
{
}
width
:
16px
;
height
:
16px
;
margin-top
:
3px
;
img
{
.source
{
width
:
100%
;
margin
:
0
auto
;
height
:
100%
;
margin-top
:
10px
;
}
/* 上下0,左右自动居中 */
width
:
370px
;
height
:
22px
;
display
:
flex
;
.info
{
width
:
16px
;
height
:
16px
;
margin-top
:
3px
;
}
img
{
width
:
100%
;
height
:
100%
;
.text
{
font-family
:
"Source Han Sans CN"
;
font-weight
:
400
;
font-size
:
14px
;
line-height
:
22px
;
letter-spacing
:
0px
;
text-align
:
left
;
color
:
rgb
(
132
,
136
,
142
);
margin-left
:
8px
;
}
}
}
}
.text
{
font-family
:
"Source Han Sans CN"
;
font-weight
:
400
;
font-size
:
14px
;
line-height
:
22px
;
letter-spacing
:
0px
;
text-align
:
left
;
color
:
rgb
(
132
,
136
,
142
);
margin-left
:
8px
;
}
}
}
.box3-main-
right
{
.box3-main-
bottom
{
margin-left
:
28px
;
margin-left
:
28px
;
margin-top
:
12
px
;
margin-top
:
24
px
;
width
:
536px
;
width
:
536px
;
height
:
326px
;
height
:
326px
;
...
@@ -1356,9 +1363,15 @@ onMounted(() => {
...
@@ -1356,9 +1363,15 @@ onMounted(() => {
}
}
}
}
}
}
}
}
.box.box-core-researchers
{
width
:
1104px
;
height
:
900px
;
flex-shrink
:
0
;
}
}
}
}
}
</
style
>
</
style
>
\ No newline at end of file
src/views/thinkTank/TipTab/index.vue
0 → 100644
浏览文件 @
6714a4e9
<
template
>
<div
class=
"tip-wrapper"
>
<div
class=
"icon"
>
<img
src=
"./tip-icon.svg"
alt=
""
>
</div>
<div
class=
"text text-tip-2 text-primary-50-clor"
>
{{
`${text
}
`
}}
<
/div
>
<
/div
>
<
/template
>
<
script
setup
>
const
props
=
defineProps
({
dataSource
:
{
type
:
String
,
default
:
'美国国会官网'
}
,
dataTime
:
{
type
:
String
,
default
:
'2023.1至2025.12'
}
,
text
:
{
type
:
String
,
default
:
''
}
,
}
)
<
/script
>
<
style
lang
=
"scss"
scoped
>
.
tip
-
wrapper
{
width
:
100
%
;
display
:
flex
;
gap
:
8
px
;
justify
-
content
:
center
;
align
-
items
:
center
;
height
:
22
px
;
.
icon
{
width
:
16
px
;
height
:
16
px
;
img
{
width
:
100
%
;
height
:
100
%
;
}
}
}
<
/style>
\ No newline at end of file
src/views/thinkTank/TipTab/tip-icon.svg
0 → 100644
浏览文件 @
6714a4e9
<svg
viewBox=
"0 0 16 16"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"16.000000"
height=
"16.000000"
fill=
"none"
customFrame=
"#000000"
>
<rect
id=
"容器 704"
width=
"16.000000"
height=
"16.000000"
x=
"0.000000"
y=
"0.000000"
/>
<circle
id=
"椭圆 96"
cx=
"8"
cy=
"8"
r=
"7"
fill=
"rgb(230,231,232)"
/>
<circle
id=
"椭圆 97"
cx=
"8"
cy=
"4"
r=
"1"
fill=
"rgb(132,136,142)"
/>
<path
id=
"矩形 241"
d=
"M6.49996 6L8.00028 6.0004C8.55256 6.0004 9.00028 6.44811 9.00028 7.00039L9.00028 10.4992C9.00028 10.7754 9.22408 10.9989 9.50033 10.9992L9.50033 10.9997C9.77657 10.9998 10.0005 11.2236 10.0005 11.4998L10.0003 11.5001C10.0002 11.7765 9.77622 12.0006 9.49978 12.0006L8.00028 12.0004L6.50033 12.0004C6.22423 12.0004 6.00064 11.7767 6.00049 11.5006L6.00021 11.5005C6.00021 11.2243 6.22418 11.0003 6.50037 11.0003L6.50037 11.0006C6.77649 11.0007 7.00042 10.7766 7.00042 10.5005L7.00017 7.50005C7.00017 7.22376 6.77644 7.00047 6.50015 7.00002L6.49946 6.99922C6.22357 6.999 6 6.77565 6 6.49976C6.00011 6.22373 6.22393 6 6.49996 6Z"
fill=
"rgb(132,136,142)"
fill-rule=
"evenodd"
/>
</svg>
src/views/thinkTank/allThinkTank/index.vue
浏览文件 @
6714a4e9
...
@@ -24,7 +24,7 @@
...
@@ -24,7 +24,7 @@
</div>
</div>
<div
class=
"select-box"
>
<div
class=
"select-box"
>
<div
class=
"search-box"
>
<div
class=
"search-box"
>
<el-input
placeholder=
"搜索智库"
v-model=
"searchPolicy"
>
<el-input
placeholder=
"搜索智库"
v-model=
"searchPolicy"
@
keyup
.
enter=
"handleGetThinkTankList()"
>
<template
#
suffix
>
<template
#
suffix
>
<img
src=
"../assets/images/Line_Search.png"
class=
"search-icon"
alt=
"搜索"
<img
src=
"../assets/images/Line_Search.png"
class=
"search-icon"
alt=
"搜索"
@
click=
"handleGetThinkTankList()"
>
@
click=
"handleGetThinkTankList()"
>
...
@@ -70,6 +70,9 @@
...
@@ -70,6 +70,9 @@
{{ "近期美国智库机构发布涉华报告数量汇总" }}
{{ "近期美国智库机构发布涉华报告数量汇总" }}
</div>
</div>
</div>
</div>
<div
class=
"time-tab-pane"
>
<TimeTabPane
@
time-click=
"handleTimeClick"
/>
</div>
</div>
</div>
<div
class=
"all-item"
>
<div
class=
"all-item"
>
<div
class=
"item-card"
v-for=
"(item, index) in sortedCardList"
:key=
"item.id || index"
<div
class=
"item-card"
v-for=
"(item, index) in sortedCardList"
:key=
"item.id || index"
...
@@ -111,6 +114,7 @@ import { ref, reactive, computed, onMounted } from "vue";
...
@@ -111,6 +114,7 @@ import { ref, reactive, computed, onMounted } from "vue";
import
{
getAllThinkTankList
}
from
"@/api/thinkTank/overview"
import
{
getAllThinkTankList
}
from
"@/api/thinkTank/overview"
import
{
useRouter
}
from
'vue-router'
;
import
{
useRouter
}
from
'vue-router'
;
import
router
from
"@/router"
;
import
router
from
"@/router"
;
import
TimeTabPane
from
'@/components/base/TimeTabPane/index.vue'
const
cardList
=
ref
([]);
const
cardList
=
ref
([]);
// 按 reportNumber 从大到小排序,保证从左到右从上到下排列
// 按 reportNumber 从大到小排序,保证从左到右从上到下排列
const
sortedCardList
=
computed
(()
=>
{
const
sortedCardList
=
computed
(()
=>
{
...
@@ -125,6 +129,22 @@ const sortedCardList = computed(() => {
...
@@ -125,6 +129,22 @@ const sortedCardList = computed(() => {
const
currentPage
=
ref
(
1
)
const
currentPage
=
ref
(
1
)
const
pageSize
=
ref
(
15
)
const
pageSize
=
ref
(
15
)
const
total
=
ref
(
0
)
const
total
=
ref
(
0
)
const
timePeriod
=
ref
(
"WEEK"
)
const
handleTimeClick
=
item
=>
{
const
time
=
item
?.
time
if
(
time
===
"近一周"
)
{
timePeriod
.
value
=
"WEEK"
}
else
if
(
time
===
"近一月"
)
{
timePeriod
.
value
=
"MONTH"
}
else
if
(
time
===
"近一年"
)
{
timePeriod
.
value
=
"YEAR"
}
// 切换时间范围后重新拉取(从第一页开始)
currentPage
.
value
=
1
handleGetThinkTankList
()
}
const
handleCurrentChange
=
page
=>
{
const
handleCurrentChange
=
page
=>
{
currentPage
.
value
=
page
;
currentPage
.
value
=
page
;
handleGetThinkTankList
()
handleGetThinkTankList
()
...
@@ -142,7 +162,8 @@ const handleGetThinkTankList = async () => {
...
@@ -142,7 +162,8 @@ const handleGetThinkTankList = async () => {
// 后端通常是 0-based,这里做一次转换
// 后端通常是 0-based,这里做一次转换
currentPage
:
currentPage
.
value
-
1
,
currentPage
:
currentPage
.
value
-
1
,
pageSize
:
pageSize
.
value
,
pageSize
:
pageSize
.
value
,
keyword
:
searchPolicy
.
value
keyword
:
searchPolicy
.
value
,
timePeriod
:
timePeriod
.
value
});
});
console
.
log
(
"智库列表"
,
res
);
console
.
log
(
"智库列表"
,
res
);
cardList
.
value
=
[];
cardList
.
value
=
[];
...
@@ -391,6 +412,7 @@ onMounted(async () => {
...
@@ -391,6 +412,7 @@ onMounted(async () => {
height
:
36px
;
height
:
36px
;
margin-top
:
14px
;
margin-top
:
14px
;
display
:
flex
;
display
:
flex
;
justify-content
:
space-between
;
.title-info
{
.title-info
{
height
:
24px
;
height
:
24px
;
...
@@ -419,6 +441,11 @@ onMounted(async () => {
...
@@ -419,6 +441,11 @@ onMounted(async () => {
color
:
rgb
(
59
,
65
,
75
);
color
:
rgb
(
59
,
65
,
75
);
}
}
}
}
.time-tab-pane
{
width
:
248px
;
height
:
36px
;
}
}
}
.all-item
{
.all-item
{
...
@@ -463,7 +490,8 @@ onMounted(async () => {
...
@@ -463,7 +490,8 @@ onMounted(async () => {
padding
:
2px
8px
;
padding
:
2px
8px
;
/* 左右留空隙,更美观 */
/* 左右留空隙,更美观 */
white-space
:
nowrap
white-space
:
nowrap
;
z-index
:
9999
;
}
}
.item-header
{
.item-header
{
...
...
src/views/thinkTank/index.vue
浏览文件 @
6714a4e9
差异被折叠。
点击展开。
src/views/thinkTank/utils/multiLineChart.js
浏览文件 @
6714a4e9
import
*
as
echarts
from
'echarts'
;
import
*
as
echarts
from
'echarts'
;
const
getMultiLineChart
=
(
data
)
=>
{
/** 图例分页:每页展示的图例项数量(box5 数量变化趋势) */
export
const
MULTILINE_LEGEND_SHOW_COUNT
=
11
;
/**
* @param {{ title: unknown[], data: Array<{ name: string, value: unknown[], color?: string }> }} data
* @param {{ legendShowCount?: number, legendPageIndex?: number }} [options]
*/
const
getMultiLineChart
=
(
data
,
options
=
{})
=>
{
// 提取标题和系列数据
// 提取标题和系列数据
const
title
=
data
.
title
const
title
=
data
.
title
const
series
=
data
.
data
const
series
=
data
.
data
const
legendShowCount
=
typeof
options
.
legendShowCount
===
'number'
&&
options
.
legendShowCount
>
0
?
options
.
legendShowCount
:
MULTILINE_LEGEND_SHOW_COUNT
const
rawPageIndex
=
Number
(
options
.
legendPageIndex
)
||
0
const
allNames
=
series
.
map
((
item
)
=>
item
.
name
)
const
pageCount
=
Math
.
max
(
1
,
Math
.
ceil
(
allNames
.
length
/
legendShowCount
))
const
legendPageIndex
=
Math
.
min
(
Math
.
max
(
0
,
rawPageIndex
),
pageCount
-
1
)
const
legendStart
=
legendPageIndex
*
legendShowCount
const
legendData
=
allNames
.
slice
(
legendStart
,
legendStart
+
legendShowCount
)
// 定义配色数组
// 定义配色数组
const
colorList
=
[
const
colorList
=
[
'rgba(5, 95, 194, 1)'
,
// #055fc2
'rgba(5, 95, 194, 1)'
,
// #055fc2
...
@@ -48,6 +66,7 @@ const getMultiLineChart = (data) => {
...
@@ -48,6 +66,7 @@ const getMultiLineChart = (data) => {
return
({
return
({
name
:
item
.
name
,
name
:
item
.
name
,
type
:
'line'
,
type
:
'line'
,
smooth
:
true
,
// 新增/优化:面积填充渐变效果
// 新增/优化:面积填充渐变效果
areaStyle
:
{
areaStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
...
@@ -80,8 +99,9 @@ const getMultiLineChart = (data) => {
...
@@ -80,8 +99,9 @@ const getMultiLineChart = (data) => {
}
}
}
}
},
},
/* 顶部预留足够空间:多行图例 + Y 轴标题「数量」在纵轴顶端,避免与图例重合 */
grid
:
{
grid
:
{
top
:
'
15
%'
,
top
:
'
28
%'
,
right
:
'5%'
,
right
:
'5%'
,
bottom
:
'5%'
,
bottom
:
'5%'
,
left
:
'5%'
,
left
:
'5%'
,
...
@@ -89,10 +109,12 @@ const getMultiLineChart = (data) => {
...
@@ -89,10 +109,12 @@ const getMultiLineChart = (data) => {
},
},
legend
:
{
legend
:
{
show
:
true
,
show
:
true
,
top
:
10
,
type
:
'plain'
,
data
:
legendData
,
top
:
8
,
left
:
'center'
,
left
:
'center'
,
icon
:
'circle'
,
icon
:
'circle'
,
textStyle
:
{
textStyle
:
{
fontFamily
:
'Source Han Sans CN'
,
// 字体
fontFamily
:
'Source Han Sans CN'
,
// 字体
fontWeight
:
400
,
// 字重值(Regular对应400)
fontWeight
:
400
,
// 字重值(Regular对应400)
fontSize
:
14
,
// 字号
fontSize
:
14
,
// 字号
...
@@ -112,6 +134,22 @@ const getMultiLineChart = (data) => {
...
@@ -112,6 +134,22 @@ const getMultiLineChart = (data) => {
yAxis
:
[
yAxis
:
[
{
{
type
:
'value'
,
type
:
'value'
,
name
:
'数量'
,
/* 纵轴:end = 坐标轴最大值一端,即竖轴上方 */
nameLocation
:
'end'
,
/* 在默认基础上再向左约 20px:nameGap 为轴标题与轴线间距(左侧 Y 轴顶端时增大则标题更靠左) */
nameGap
:
20
,
nameRotate
:
0
,
nameTextStyle
:
{
color
:
'rgba(170, 173, 177, 1)'
,
fontFamily
:
'Source Han Sans CN'
,
fontWeight
:
400
,
fontSize
:
14
,
lineHeight
:
22
,
letterSpacing
:
0
,
align
:
'right'
,
verticalAlign
:
'bottom'
},
splitLine
:
{
splitLine
:
{
show
:
true
,
// 显示网格线
show
:
true
,
// 显示网格线
lineStyle
:
{
lineStyle
:
{
...
...
src/views/thinkTank/utils/piechart.js
浏览文件 @
6714a4e9
...
@@ -10,7 +10,8 @@ const getPieChart = (data) => {
...
@@ -10,7 +10,8 @@ const getPieChart = (data) => {
// left: '15%',
// left: '15%',
// containLabel: true
// containLabel: true
// },
// },
radius
:
[
70
,
100
],
// 使用百分比半径,避免固定像素导致饼图“看起来没铺满容器”
radius
:
[
'45%'
,
'60%'
],
height
:
'100%'
,
height
:
'100%'
,
left
:
'center'
,
left
:
'center'
,
width
:
'100%'
,
width
:
'100%'
,
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论