提交 765774b3 authored 作者: yanpeng's avatar yanpeng

resolve confict

...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
"@traptitech/markdown-it-katex": "^3.6.0", "@traptitech/markdown-it-katex": "^3.6.0",
"axios": "^1.12.2", "axios": "^1.12.2",
"echarts": "^5.4.3", "echarts": "^5.4.3",
"echarts-liquidfill": "^3.1.0",
"echarts-wordcloud": "^2.1.0", "echarts-wordcloud": "^2.1.0",
"element-plus": "^2.4.4", "element-plus": "^2.4.4",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
...@@ -2894,6 +2895,14 @@ ...@@ -2894,6 +2895,14 @@
"zrender": "5.6.1" "zrender": "5.6.1"
} }
}, },
"node_modules/echarts-liquidfill": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/echarts-liquidfill/-/echarts-liquidfill-3.1.0.tgz",
"integrity": "sha512-5Dlqs/jTsdTUAsd+K5LPLLTgrbbNORUSBQyk8PSy1Mg2zgHDWm83FmvA4s0ooNepCJojFYRITTQ4GU1UUSKYLw==",
"peerDependencies": {
"echarts": "^5.0.1"
}
},
"node_modules/echarts-wordcloud": { "node_modules/echarts-wordcloud": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmmirror.com/echarts-wordcloud/-/echarts-wordcloud-2.1.0.tgz", "resolved": "https://registry.npmmirror.com/echarts-wordcloud/-/echarts-wordcloud-2.1.0.tgz",
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
"@traptitech/markdown-it-katex": "^3.6.0", "@traptitech/markdown-it-katex": "^3.6.0",
"axios": "^1.12.2", "axios": "^1.12.2",
"echarts": "^5.4.3", "echarts": "^5.4.3",
"echarts-liquidfill": "^3.1.0",
"echarts-wordcloud": "^2.1.0", "echarts-wordcloud": "^2.1.0",
"element-plus": "^2.4.4", "element-plus": "^2.4.4",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
......
...@@ -57,8 +57,14 @@ import Portal1 from "@/views/portals/portal1/index.vue"; ...@@ -57,8 +57,14 @@ import Portal1 from "@/views/portals/portal1/index.vue";
import Portal2 from "@/views/portals/portal2/index.vue"; import Portal2 from "@/views/portals/portal2/index.vue";
// 综合搜索 // 综合搜索
<<<<<<< HEAD
import ComprehensiveSearch from "@/views/comprehensiveSearch/index.vue"; import ComprehensiveSearch from "@/views/comprehensiveSearch/index.vue";
import SearchResults from "@/views/comprehensiveSearch/searchResults/index.vue"; import SearchResults from "@/views/comprehensiveSearch/searchResults/index.vue";
=======
import ComprehensiveSearch from '@/views/comprehensiveSearch/index.vue'
import SearchResults from '@/views/comprehensiveSearch/searchResults/index.vue'
import Chat from '@/views/comprehensiveSearch/chat/index.vue'
>>>>>>> ed1ebab4dc5b104b872872f2eb293e8abf909c56
const routes = [ const routes = [
// 智能写报 // 智能写报
......
...@@ -212,7 +212,7 @@ ...@@ -212,7 +212,7 @@
<div class="icon"> <div class="icon">
<img src="./assets/images/box2-footer-icon.png" alt="" /> <img src="./assets/images/box2-footer-icon.png" alt="" />
</div> </div>
<div class="text">{{ "风险处理" }}</div> <div class="text">{{ "查看更多" }}</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -1707,7 +1707,7 @@ onMounted(async () => { ...@@ -1707,7 +1707,7 @@ onMounted(async () => {
height: 100%; height: 100%;
} }
.inner-box { .inner-box {
width: 330px; width: 100%;
height: 93px; height: 93px;
background: rgba(10, 18, 30, 0.75); background: rgba(10, 18, 30, 0.75);
position: absolute; position: absolute;
...@@ -1719,7 +1719,7 @@ onMounted(async () => { ...@@ -1719,7 +1719,7 @@ onMounted(async () => {
height: 30px; height: 30px;
display: flex; display: flex;
.inner-box-title { .inner-box-title {
width: 270px; flex: 9;
color: rgba(255, 255, 255, 1); color: rgba(255, 255, 255, 1);
font-family: Microsoft YaHei; font-family: Microsoft YaHei;
font-size: 16px; font-size: 16px;
...@@ -1730,7 +1730,7 @@ onMounted(async () => { ...@@ -1730,7 +1730,7 @@ onMounted(async () => {
white-space: nowrap; white-space: nowrap;
} }
.inner-box-time { .inner-box-time {
width: 60px; flex: 2;
height: 30px; height: 30px;
color: rgba(255, 255, 255, 0.65); color: rgba(255, 255, 255, 0.65);
font-family: Microsoft YaHei; font-family: Microsoft YaHei;
...@@ -1743,7 +1743,7 @@ onMounted(async () => { ...@@ -1743,7 +1743,7 @@ onMounted(async () => {
} }
} }
.inner-box-content { .inner-box-content {
width: 330px; width: 100%;
height: 40px; height: 40px;
overflow: hidden; overflow: hidden;
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
...@@ -1757,7 +1757,7 @@ onMounted(async () => { ...@@ -1757,7 +1757,7 @@ onMounted(async () => {
} }
} }
.box2 { .box2 {
flex: 1; width: 520px;
height: 450px; height: 450px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1); box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
...@@ -1871,16 +1871,16 @@ onMounted(async () => { ...@@ -1871,16 +1871,16 @@ onMounted(async () => {
} }
.box2-footer { .box2-footer {
position: absolute; position: absolute;
left: 40px; left: 30px;
bottom: 20px; bottom: 20px;
width: 430px; width: 460px;
height: 42px; height: 42px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
border-radius: 6px; border-radius: 6px;
background: rgba(22, 119, 255, 1); background: var(--color-main-active);
cursor: pointer; cursor: pointer;
.icon { .icon {
width: 16px; width: 16px;
...@@ -2792,4 +2792,15 @@ onMounted(async () => { ...@@ -2792,4 +2792,15 @@ onMounted(async () => {
} }
} }
} }
:deep(.el-input__wrapper) {
box-shadow: none;
}
:deep(.el-input__wrapper:hover) {
box-shadow: none !important;
}
:deep(.el-input__wrapper.is-focus) {
box-shadow: none !important;
}
</style> </style>
\ No newline at end of file
<template>
<div class="wrapper">
<div class="header"><span>首页 </span>> <span>综合检索 </span>> <span>智能问答 </span></div>
<div class="main">
<div class="left">
<div class="left-header">
<div class="icon">
<img src="./assets/images/ai.png" alt="" />
</div>
<el-input placeholder="新对话" v-model="newChatTitle" style="width: 220px" />
</div>
<div class="left-main">
<div class="left-main-title">{{ "历史对话" }}</div>
<div class="left-list">
<div
class="left-list-item"
:class="{ itemActive: currentChatId === item.id }"
v-for="(item, index) in chatList"
:key="index"
>
{{ item.title }}
</div>
</div>
</div>
</div>
<div class="right">
<div class="right-header">{{ curChatTitle }}</div>
<div class="right-main">
<div class="right-main-content">
<div class="chat-content" ref="contentContainer">
<!-- 消息列表 -->
<div class="message-list">
<div v-for="(message, index) in messages" :key="index">
<!-- AI 消息 -->
<div v-if="message.type === 'ai'" class="ai-item">
<div class="ai-header">
<div class="icon">
<img src="./assets/images/ai-avator.png" alt="" />
</div>
<div class="text">{{ `已深度思考(用时6秒钟)` }}</div>
</div>
<div class="content ai-content" v-html="renderMarkdown(message.content)"></div>
</div>
<!-- 用户消息 -->
<div v-else class="user-item">
<div class="user-bubble">
<div class="user-content">{{ message.content }}</div>
</div>
</div>
</div>
<!-- 加载状态 -->
<div v-if="isLoading" class="ai-item">
<div class="ai-header">
<div class="icon">
<img src="./assets/images/ai-avator.png" alt="" />
</div>
<div class="text">{{ `正在思考中,请稍候` }}</div>
</div>
<div class="loading-indicator">
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
</div>
</div>
</div>
</div>
</div>
<div class="right-main-footer">
<el-input type="textarea" placeholder="发送消息" :rows="4" v-model="userInput" />
<div class="input-footer">
<div class="icon1">
<el-tooltip effect="dark" content="录音" placement="top">
<img src="./assets/images/microphone.png" alt="" />
</el-tooltip>
</div>
<div class="icon2">
<el-tooltip effect="dark" content="收藏" placement="top">
<img src="./assets/images/attach.png" alt="" />
</el-tooltip>
</div>
<div class="line"></div>
<div class="submit" @click="sendMessage">
<el-tooltip effect="dark" content="发送" placement="top">
<img src="./assets/images/submit.png" alt="" />
</el-tooltip>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import { fetchEventSource } from "@microsoft/fetch-event-source";
import MarkdownIt from "markdown-it";
import { ElMessage } from "element-plus";
import { getChat } from "@/api/chat";
const contentContainer = ref(null);
const userInput = ref("");
const isLoading = ref(false);
const abortController = ref(null);
// 消息数据
const messages = ref([
{
type: "user",
content: "你好"
},
{
type: "ai",
content: "您好!我是AI助手,有什么可以帮助您的吗?"
}
]);
// Markdown 渲染器
const md = new MarkdownIt();
// 渲染 markdown
const renderMarkdown = content => {
return md.render(content);
};
// 自动滚动到底部
const scrollToBottom = async () => {
await nextTick();
if (contentContainer.value) {
const container = contentContainer.value;
container.scrollTop = container.scrollHeight;
}
};
// 添加消息
const addMessage = (type, content) => {
messages.value.push({
type,
content,
timestamp: new Date().getTime()
});
scrollToBottom();
};
// 更新最后一条 AI 消息(用于流式输出)
const updateLastAIMessage = content => {
const lastMessage = messages.value[messages.value.length - 1];
if (lastMessage && lastMessage.type === "ai") {
lastMessage.content = content;
scrollToBottom();
}
};
// 使用 fetchEventSource 连接
const connectSSE = async question => {
// 添加用户消息
addMessage("user", question);
// 添加空的 AI 消息用于流式更新
addMessage("ai", "");
isLoading.value = true;
// 创建 AbortController 用于取消请求
abortController.value = new AbortController();
const params = {
query: "如何检索?",
knowledge_base_name: "kb_test251112",
top_k: 6,
score_threshold: 0.5,
metadata: { year: 2024 }
};
try {
await fetchEventSource("/chat/knowledge_base/search_docs", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(params),
signal: abortController.value.signal,
onopen: async response => {
console.log("SSE 连接已建立", response.status);
if (response.status !== 200) {
throw new Error(`请求失败: ${response.status}`);
}
},
onmessage: event => {
try {
if (event.data === "[DONE]") {
// 流式输出结束
isLoading.value = false;
return;
}
const data = JSON.parse(event.data);
if (data.type === "content" && data.content) {
// 流式更新内容
updateLastAIMessage(prev => prev + data.content);
} else if (data.type === "error") {
throw new Error(data.message || "请求失败");
}
} catch (error) {
console.error("解析 SSE 数据错误:", error);
}
},
onclose: () => {
console.log("SSE 连接已关闭");
isLoading.value = false;
},
onerror: error => {
console.error("SSE 连接错误:", error);
ElMessage.error("连接失败,请重试");
isLoading.value = false;
// 不要抛出错误,否则会重试
}
});
} catch (error) {
console.error("SSE 请求失败:", error);
if (error.name !== "AbortError") {
ElMessage.error(error.message || "请求失败");
}
isLoading.value = false;
}
};
const chat = async () => {
const params = {
query: "如何检索?",
knowledge_base_name: "kb_test251112",
top_k: 6,
score_threshold: 0.5,
metadata: { year: 2024 }
};
try {
const res = await getChat(params);
console.log("chat", res);
} catch (error) {
console.error(error);
}
};
// 发送消息
const sendMessage = async () => {
const question = userInput.value.trim();
if (!question) {
ElMessage.warning("请输入问题");
return;
}
if (isLoading.value) {
ElMessage.warning("请等待上一条消息回复完成");
return;
}
userInput.value = "";
await connectSSE(question);
// chat();
};
const newChatTitle = ref(""); // 新对话
const curChatTitle = ref("美国对中国制裁实体清单");
const currentChatId = ref(1);
const chatList = ref([
{
id: 1,
title: "美国对中国制裁实体清单"
},
{
id: 2,
title: "制作中国进口依赖度柱状图"
},
{
id: 3,
title: "列举 2025 年科技产品"
},
{
id: 4,
title: "获取美国国会议员名单"
},
{
id: 5,
title: "2000年以后美国金融法案列举"
},
{
id: 6,
title: "科研机构及高校仪器进口排名"
}
]);
onMounted(() => {
scrollToBottom();
});
onUnmounted(() => {
if (abortController.value) {
abortController.value.abort();
}
});
</script>
<style lang="scss" scoped>
:deep(.el-input__wrapper) {
box-shadow: none;
}
:deep(.el-input__wrapper:hover) {
box-shadow: none !important;
}
:deep(.el-input__wrapper.is-focus) {
box-shadow: none !important;
}
:deep(.el-input ::placeholder) {
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
}
:deep(.el-input__inner) {
color: #333;
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
}
:deep(.el-textarea__inner) {
box-shadow: none;
border-radius: 20px;
resize: none;
}
:deep(.el-textarea__inner:hover) {
box-shadow: none !important;
}
:deep(.el-textarea__inner.is-focus) {
box-shadow: none !important;
}
:deep(.el-textarea ::placeholder) {
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
}
:deep(.el-textarea__inner) {
color: #333;
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
}
.wrapper {
.header {
height: 64px;
color: #fff;
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
line-height: 64px;
background: url("../assets/images/header-bg.png");
box-sizing: border-box;
padding-left: 160px;
}
.main {
margin-top: 16px;
display: flex;
justify-content: center;
gap: 16px;
.left {
width: 300px;
height: 880px;
border-radius: 4px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.left-header {
width: 268px;
height: 48px;
margin: 16px auto;
box-sizing: border-box;
border: 1px solid rgba(5, 95, 194, 1);
border-radius: 8px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
display: flex;
.icon {
width: 20px;
height: 13px;
margin-top: 13px;
margin-left: 16px;
img {
width: 100%;
height: 100%;
}
}
}
.left-main {
margin-top: 24px;
.left-main-title {
margin-left: 29px;
width: 64px;
height: 24px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.left-list {
margin-top: 12px;
margin-left: 16px;
width: 268px;
height: 720px;
.left-list-item {
width: 268px;
height: 48px;
border-radius: 8px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 48px;
box-sizing: border-box;
padding: 0 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
&:hover {
background: rgba(246, 250, 255, 1);
}
}
.itemActive {
background: rgba(246, 250, 255, 1);
color: var(--color-main-active);
font-weight: 700;
}
}
}
}
.right {
width: 1284px;
height: 880px;
border-radius: 4px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.right-header {
height: 64px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
line-height: 64px;
box-sizing: border-box;
padding-left: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 700;
}
.right-main {
.right-main-content {
width: 926px;
margin-left: 166px;
height: 630px;
}
.right-main-footer {
margin: 10px auto;
width: 900px;
height: 160px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 20px;
background: rgba(255, 255, 255, 1);
.input-footer {
display: flex;
justify-content: flex-end;
.icon1 {
margin-top: 15px;
width: 24px;
height: 24px;
margin-right: 20px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
.icon2 {
margin-top: 15px;
width: 24px;
height: 24px;
margin-right: 20px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
.line {
margin-top: 15px;
margin-right: 20px;
width: 1px;
height: 24px;
background: rgba(234, 236, 238, 1);
}
.submit {
margin-top: 7px;
margin-right: 12px;
width: 40px;
height: 40px;
border-radius: 20px;
background: #b4cfed;
cursor: pointer;
&:hover {
background: rgba(5, 95, 194, 1);
}
img {
width: 24px;
height: 24px;
margin: 8px;
}
}
}
}
}
}
}
}
/* 对话内容区域 */
.chat-content {
overflow-y: auto;
// padding: 20px;
background: #fff;
width: 926px;
.message-list {
padding-bottom: 20px;
}
.ai-item {
width: 926px;
overflow: hidden;
.ai-header {
height: 22px;
display: flex;
.icon {
width: 10px;
height: 10px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 16px;
height: 22px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
}
}
.ai-content {
margin-top: 10px;
width: 900px;
margin-left: 26px;
}
}
.user-item {
margin-top: 32px;
height: 42px;
display: flex;
justify-content: flex-end;
.user-bubble {
line-height: 42px;
border-radius: 21px 0px 21px 21px;
background: rgba(234, 236, 238, 1);
box-sizing: border-box;
padding: 0 20px;
text-align: right;
word-wrap: break-word;
.user-content {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 42px;
letter-spacing: 0px;
text-align: left;
}
}
}
}
/* 加载指示器 */
.loading-indicator {
margin-left: 26px;
height: 30px;
display: flex;
gap: 4px;
padding: 11px 0;
}
.dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #999;
animation: bounce 1.4s infinite ease-in-out;
}
.dot:nth-child(1) {
animation-delay: -0.32s;
}
.dot:nth-child(2) {
animation-delay: -0.16s;
}
</style>
\ No newline at end of file
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<div class="search"> <div class="search">
<div class="search-left"> <div class="search-left">
<el-input placeholder="搜索关键词" style="width: 700px; height: 100%" /> <el-input placeholder="搜索关键词" style="width: 700px; height: 100%" />
<div class="btn1"> <div class="btn1" @click="handleToChat">
<div class="icon"> <div class="icon">
<img src="./assets/images/ai-icon.png" alt="" /> <img src="./assets/images/ai-icon.png" alt="" />
</div> </div>
...@@ -279,6 +279,13 @@ const box3List = ref([ ...@@ -279,6 +279,13 @@ const box3List = ref([
} }
]); ]);
// 点击智能问答,进入智能问答页
const handleToChat = () => {
router.push({
path: '/chat'
})
}
// 点击全文搜索,进入搜索结果页 // 点击全文搜索,进入搜索结果页
const handleToSearchResults = () => { const handleToSearchResults = () => {
router.push({ router.push({
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
<div class="header-right"> <div class="header-right">
<div class="header-right-header">{{ "关联检索" }}</div> <div class="header-right-header">{{ "关联检索" }}</div>
<div class="header-right-main"> <div class="header-right-main">
<div class="header-right-main-item" v-for="item,index in relatedSearchList" :key="index"> <div class="header-right-main-item" v-for="(item, index) in relatedSearchList" :key="index">
<div class="icon"> <div class="icon">
<img src="./assets/images/search-icon2.png" alt="" /> <img src="./assets/images/search-icon2.png" alt="" />
</div> </div>
...@@ -72,12 +72,39 @@ ...@@ -72,12 +72,39 @@
</div> </div>
</div> </div>
</div> </div>
<div class="main">
<div class="item" v-for="(item, index) in searchResults" :key="index">
<div class="item-left" v-if="item.img">
<img :src="item.img" alt="">
</div>
<div class="item-right">
<div class="title" v-html="renderContent(item.title)"></div>
<div class="content" v-html="renderContent(item.content)"></div>
<div class="item-right-footer">
<div class="time">{{ item.time }}</div>
<div
class="tag"
:class="{ tag1: item.tag.status === 1, tag2: item.tag.status === 2, tag3: item.tag.status === 3 }"
>
{{ item.tag.name }}
</div>
</div>
</div>
</div>
</div>
<div class="footer">
<el-pagination background layout="prev, pager, next" :total="96" />
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import Img1 from './assets/images/img1.png'
import Img2 from './assets/images/img2.png'
import Img3 from './assets/images/img3.png'
const selectTime = ref("全部时间"); const selectTime = ref("全部时间");
const selectRelation = ref("相关度优先"); const selectRelation = ref("相关度优先");
...@@ -181,13 +208,133 @@ const relationList = ref([ ...@@ -181,13 +208,133 @@ const relationList = ref([
// 关联检索 // 关联检索
const relatedSearchList = ref([ const relatedSearchList = ref([
'金融制度是如何建立的?', "金融制度是如何建立的?",
'金融有什么用?', "金融有什么用?",
'如何办理金融贷款?', "如何办理金融贷款?",
'2025年美国金融法案发布情况', "2025年美国金融法案发布情况",
'中国如何应对金融制裁', "中国如何应对金融制裁",
'中国金融制度建立历史' "中国金融制度建立历史"
]) ]);
const searchResults = ref([
{
img: Img1,
title: "美元 “上链”!美国联邦金融稳定币监管法案正式生效",
content:
"《指导与建立美国稳定币国家创新法案》落地,要求稳定币发行方以 1:1 比例储备美元现金、短期美债等高度流动资产,建立联邦与州双轨监管体系。",
time: "通过日期:2025-10- 05",
tag: {
name: "科技法案",
status: 3
}
},
{
img: Img2,
title: "划清监管边界!美国数字资产分类法案众议院高票通过",
content:
"《数字资产市场清晰法案》按去中心化属性划分资产类别,明确 “数字商品” 归 CFTC 监管、“数字资产证” 归 SEC 监管。创设 “金融成熟链机制”。",
time: "通过日期:2025-10- 05",
tag: {
name: "科技法案",
status: 3
}
},
{
img: null,
title: "划清监管边界!美国数字资产分类法案众议院高票通过",
content:
"《数字资产市场清晰法案》按去中心化属性划分资产类别,明确 “数字商品” 归 CFTC 监管、“数字资产证” 归 SEC 监管。创设 “金融成熟链机制”。",
time: "通过日期:2025-10- 05",
tag: {
name: "科技法案",
status: 3
}
},
{
img: null,
title: "划清监管边界!美国数字资产分类法案众议院高票通过",
content:
"《数字资产市场清晰法案》按去中心化属性划分资产类别,明确 “数字商品” 归 CFTC 监管、“数字资产证” 归 SEC 监管。创设 “金融成熟链机制”。",
time: "通过日期:2025-10- 05",
tag: {
name: "科技法案",
status: 3
}
},
{
img: null,
title: "严管金融稳定币发行!美国《STABLE 法案》推进审议",
content:
"法案禁止未授权发行稳定币,违规者将面临每日最高 10 万美元罚款,同时禁止发行方支付利息。要求发行方开展月度审计并公开储备报告,严防关联方资金占用。",
time: "通过日期:2025-10- 05",
tag: {
name: "科技法案",
status: 3
}
},
{
img: null,
title: "严管金融稳定币发行!美国《STABLE 法案》推进审议",
content:
"法案禁止未授权发行稳定币,违规者将面临每日最高 10 万美元罚款,同时禁止发行方支付利息。要求发行方开展月度审计并公开储备报告,严防关联方资金占用。",
time: "通过日期:2025-10- 05",
tag: {
name: "科技法案",
status: 3
}
},
{
img: null,
title: "拒绝政府数字货币!美国众议院通过反 CBDC 监控法案",
content:
"《反 CBDC 监控国家法案》以保护金融隐私为核心,拟永久禁止美联储发行数字美元,禁止其向个人直接或间接发行 CBDC,也不得将 CBDC 用于货币政策工具或开展相关。",
time: "通过日期:2025-10- 05",
tag: {
name: "科技法案",
status: 3
}
},
{
img: null,
title: "拒绝政府数字货币!美国众议院通过反 CBDC 监控法案",
content:
"《反 CBDC 监控国家法案》以保护金融隐私为核心,拟永久禁止美联储发行数字美元,禁止其向个人直接或间接发行 CBDC,也不得将 CBDC 用于货币政策工具或开展相关。",
time: "通过日期:2025-10- 05",
tag: {
name: "科技法案",
status: 3
}
},
{
img: null,
title: "拒绝政府数字货币!美国众议院通过反 CBDC 监控法案",
content:
"《反 CBDC 监控国家法案》以保护金融隐私为核心,拟永久禁止美联储发行数字美元,禁止其向个人直接或间接发行 CBDC,也不得将 CBDC 用于货币政策工具或开展相关。",
time: "通过日期:2025-10- 05",
tag: {
name: "科技法案",
status: 3
}
},
{
img: Img3,
title: "主流加密货币定调!美国 FIT-21 法案确立 “数字商品” 属性",
content:
"《21 世纪金融创新与技术法案》明确比特币、以太坊等主流加密货币归类为 “数字商品”,适用 CFTC 监管框架。建立 SEC 与 CFTC 跨机构协调机制并要求联合发布分类...",
time: "通过日期:2025-10- 05",
tag: {
name: "科技法案",
status: 3
}
}
]);
const renderContent = str => {
if (str.includes("金融")) {
return str.replace(/金融/g, '<span style="color: #CE4F51">金融</span>');
}
return str;
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
...@@ -233,12 +380,12 @@ const relatedSearchList = ref([ ...@@ -233,12 +380,12 @@ const relatedSearchList = ref([
border-radius: 20px; border-radius: 20px;
background: rgba(247, 248, 249, 1); background: rgba(247, 248, 249, 1);
display: flex; display: flex;
gap: 8px; gap: 8px;
margin-bottom: 8px; margin-bottom: 8px;
cursor: pointer; cursor: pointer;
&:hover{ &:hover {
background: rgb(234, 234, 234); background: rgb(234, 234, 234);
} }
.icon { .icon {
width: 16px; width: 16px;
height: 16px; height: 16px;
...@@ -449,5 +596,99 @@ const relatedSearchList = ref([ ...@@ -449,5 +596,99 @@ const relatedSearchList = ref([
} }
} }
} }
.main {
width: 913px;
height: 1464px;
margin-top: 36px;
margin-left: 264px;
.item {
width: 913px;
height: 132px;
margin-bottom: 16px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
display: flex;
.item-left {
width: 170px;
height: 115px;
img{
width: 100%;
height: 100%;
}
}
.item-right {
// width: 731px;
flex: 1;
margin-left: 12px;
.title {
height: 24px;
width: 731px;
overflow: hidden;
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 700;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.content {
margin-top: 10px;
height: 48px;
font-family: Microsoft YaHei;
font-size: 16px;
color: rgba(59, 65, 75, 1);
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.item-right-footer {
display: flex;
.time {
height: 22px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
}
.tag {
margin-left: 16px;
height: 24px;
line-height: 24px;
box-sizing: border-box;
padding: 0 8px;
border-radius: 4px;
}
.tag1 {
border: 1px solid rgba(255, 204, 199, 1);
background: rgba(255, 241, 240, 1);
color: rgba(255, 77, 79, 1);
}
.tag2 {
color: rgba(250, 173, 20, 1);
border: 1px solid rgba(255, 241, 184, 1);
background: rgba(255, 251, 230, 1);
}
.tag3 {
color: rgba(22, 119, 255, 1);
border: 1px solid rgba(186, 224, 255, 1);
background: rgba(230, 244, 255, 1);
}
}
}
}
}
.footer {
width: 913px;
margin-left: 264px;
height: 107px;
box-sizing: border-box;
padding-top: 10px;
display: flex;
justify-content: center;
}
} }
</style> </style>
\ No newline at end of file
...@@ -2778,4 +2778,15 @@ onMounted(async () => { ...@@ -2778,4 +2778,15 @@ onMounted(async () => {
.divide { .divide {
margin: 0 auto; margin: 0 auto;
} }
:deep(.el-input__wrapper) {
box-shadow: none;
}
:deep(.el-input__wrapper:hover) {
box-shadow: none !important;
}
:deep(.el-input__wrapper.is-focus) {
box-shadow: none !important;
}
</style> </style>
\ No newline at end of file
...@@ -1723,4 +1723,14 @@ onMounted(async () => { ...@@ -1723,4 +1723,14 @@ onMounted(async () => {
} }
} }
} }
:deep(.el-input__wrapper) {
box-shadow: none;
}
:deep(.el-input__wrapper:hover) {
box-shadow: none !important;
}
:deep(.el-input__wrapper.is-focus) {
box-shadow: none !important;
}
</style> </style>
<!-- 中M博弈资源支撑 --> <!-- 中M博弈资源支撑 -->
<template> <template>
<div class="resource-box"> <div class="resource-box">
<DivideHeader class="divide1" :titleText="'中美博弈资源支撑'"></DivideHeader> <DivideHeader class="divide1" :titleText="'中美博弈资源支撑'"></DivideHeader>
<div style="margin-top: 20px;height: 450px;display: flex;"> <div style="margin-top: 20px;height: 450px;display: flex;">
<div class="resource-content" style="width: 47.5%; display: block;"> <div class="resource-content" style="width: 47.5%; display: block;">
<div class="item-header"> <div class="item-header">
...@@ -25,16 +23,12 @@ ...@@ -25,16 +23,12 @@
</el-timeline-item> </el-timeline-item>
</el-timeline> </el-timeline>
</div> </div>
</div> </div>
<div class="resource-content" style="width: 47.5%;margin-left: 5%;"> <div class="resource-content" style="width: 47.5%;margin-left: 5%;">
<div class="item-header"> <div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img> <img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text" style="width: 130px;">创新主体排名</div> <div class="item-header-text" style="width: 130px;">创新主体排名</div>
<el-radio-group v-model="InnovationRankingBtn" size="small" @change="handleInnovationRankingBtn"> <el-radio-group v-model="InnovationRankingBtn" size="small" @change="handleInnovationRankingBtn">
<el-radio-button value="enterprise" border>科技企业 <el-radio-button value="enterprise" border>科技企业
</el-radio-button> </el-radio-button>
<el-radio-button value="lab" border>国家重点实验室</el-radio-button> <el-radio-button value="lab" border>国家重点实验室</el-radio-button>
...@@ -42,13 +36,11 @@ ...@@ -42,13 +36,11 @@
</el-radio-button> </el-radio-button>
<el-radio-button value="contractor" border>国防承包商</el-radio-button> <el-radio-button value="contractor" border>国防承包商</el-radio-button>
</el-radio-group> </el-radio-group>
</div> </div>
<div class="item-header-divider"></div> <div class="item-header-divider"></div>
<el-table :data="InnovationRanking" stripe style="width: 100%;padding: 5px 25px;" <el-table :data="InnovationRanking" stripe style="width: 100%;padding: 5px 25px;"
:header-cell-style="headerCellStyle"> :header-cell-style="headerCellStyle">
<el-table-column prop="name" label="创新主体" width="100" /> <el-table-column prop="name" label="创新主体" width="100" />
<el-table-column prop="markValue" align="right"> <el-table-column prop="markValue" align="right">
<template #header> <template #header>
<div class="custom-header"> <div class="custom-header">
...@@ -78,18 +70,20 @@ ...@@ -78,18 +70,20 @@
</el-table> </el-table>
</div> </div>
</div> </div>
<div style="margin-top: 20px;height: 450px;display: flex;"> <div style="margin-top: 20px;height: 450px;display: flex;">
<div class="resource-content" style="width: 47.5%; display: block;"> <div class="resource-content" style="width: 47.5%; display: block;">
<div class="item-header"> <div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img> <img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">研发投入情况</div> <div class="item-header-text">研发投入情况</div>
</div> </div>
<div class="item-header-divider"></div> <div class="item-header-divider"></div>
<el-radio-group v-model="putIntoBtn" size="small" @change="handlePutIntoBtn" style="margin-left: 20px;">
<el-radio-button value="development" border>研发经费
</el-radio-button>
<el-radio-button value="GDP" border>GDP占比</el-radio-button>
</el-radio-group>
<div style="display: flex;height: calc(100% - 60px);" id="char3"> <div style="display: flex;height: calc(100% - 60px);" id="char3">
</div> </div>
</div> </div>
<div class="resource-content" style="width: 47.5%;margin-left: 5%;"> <div class="resource-content" style="width: 47.5%;margin-left: 5%;">
...@@ -99,22 +93,26 @@ ...@@ -99,22 +93,26 @@
<div class="item-header-right"> 查看数据源> </div> <div class="item-header-right"> 查看数据源> </div>
</div> </div>
<div class="item-header-divider"></div> <div class="item-header-divider"></div>
<el-select v-model="areaSelect" placeholder="" style="width: 100px;float: right;margin-right: 20px;"
size="small">
<el-option v-for="item in timeList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<div style="display: flex;height: calc(100% - 60px);" id="char4"> <div style="display: flex;height: calc(100% - 60px);" id="char4">
</div> </div>
</div> </div>
</div> </div>
<div style="margin-top: 20px;height: 450px;display: flex;"> <div style="margin-top: 20px;height: 450px;display: flex;">
<div class="resource-content" style="width: 47.5%; display: block;"> <div class="resource-content" style="width: 47.5%; display: block;">
<div class="item-header"> <div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img> <img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">专利申请情况</div> <div class="item-header-text">专利申请情况</div>
</div> </div>
<div class="item-header-divider"></div> <div class="item-header-divider"></div>
<el-select v-model="patentSelect" placeholder="" style="width: 100px;float: right;margin-right: 20px;"
size="small">
<el-option v-for="item in timeList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<div style="display: flex;height: calc(100% - 60px);" id="char5"> <div style="display: flex;height: calc(100% - 60px);" id="char5">
</div> </div>
</div> </div>
<div class="resource-content" style="width: 47.5%;margin-left: 5%;"> <div class="resource-content" style="width: 47.5%;margin-left: 5%;">
...@@ -124,11 +122,13 @@ ...@@ -124,11 +122,13 @@
<div class="item-header-right"> 查看数据源> </div> <div class="item-header-right"> 查看数据源> </div>
</div> </div>
<div class="item-header-divider"></div> <div class="item-header-divider"></div>
<el-select v-model="paperSelect" placeholder="" style="width: 100px;float: right;margin-right: 20px;"
size="small">
<el-option v-for="item in timeList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<div style="display: flex;height: calc(100% - 60px);" id="char6"> <div style="display: flex;height: calc(100% - 60px);" id="char6">
</div> </div>
</div> </div>
</div> </div>
</div> </div>
...@@ -144,6 +144,30 @@ import radarChart2 from '../js/radarChart2.js' ...@@ -144,6 +144,30 @@ import radarChart2 from '../js/radarChart2.js'
import getMultiLineChart1 from "../js/multiLineChart1.js"; import getMultiLineChart1 from "../js/multiLineChart1.js";
const timeList = ref([
{
label: "近半年",
value: "近半年",
},
{
label: "近一年",
value: "近一年",
},
{
label: "近两年",
value: "近两年",
},
{
label: "近三年",
value: "近三年",
},
{
label: "近五年",
value: "近五年",
},
]);
const names = ['Jan', 'Feb', 'Mar', 'Apr', 'May'] const names = ['Jan', 'Feb', 'Mar', 'Apr', 'May']
const data1 = [120, 200, 150, 80, 70] const data1 = [120, 200, 150, 80, 70]
const data2 = [90, 230, 180, 110, 100] const data2 = [90, 230, 180, 110, 100]
...@@ -223,9 +247,20 @@ const getStatus = _percent => { ...@@ -223,9 +247,20 @@ const getStatus = _percent => {
const InnovationRankingBtn = ref('enterprise') const InnovationRankingBtn = ref('enterprise')
function handleInnovationRankingBtn() { function handleInnovationRankingBtn() {
}
//研发投入情况
const putIntoBtn = ref('development')
function handlePutIntoBtn() {
} }
//研发投入领域
const areaSelect = ref('近一年')
//专利申请情况 //专利申请情况
const patentSelect = ref('近一年')
const chart5Data = ref({ const chart5Data = ref({
title: [ title: [
"2024-09", "2024-09",
...@@ -253,6 +288,9 @@ const chart5Data = ref({ ...@@ -253,6 +288,9 @@ const chart5Data = ref({
] ]
}); });
//论文发表情况
const paperSelect = ref('近一年')
// 绘制echarts图表 // 绘制echarts图表
const setChart = (option, chartId) => { const setChart = (option, chartId) => {
let chartDom = document.getElementById(chartId); let chartDom = document.getElementById(chartId);
......
...@@ -12,78 +12,37 @@ ...@@ -12,78 +12,37 @@
</div> </div>
<div class="item-header-divider"></div> <div class="item-header-divider"></div>
<div style="display: flex;height: calc(100% - 60px);"> <div style="display: flex;height: calc(100% - 60px);">
<!-- <div class="thematic-btn-left" @click="changeBtn('timwLine', 'left')"> <Timeline :data="course" text-key="title" id-key="seq" />
<img class="thematic-btn-icon" src="@/assets/images/icon/card-btn-left.png"></img>
</div> -->
<Timeline :data="course" text-key="title" id-key="seq" @click-card="showDetail" />
<!-- <div class="timeLine-box">
<div style="height: calc(50% - 8px);display: flex">
<div v-for="(item, index) in timeLineOddIndexItems" class="time-item-box">
{{ item.titlle }}
</div>
</div>
<img style=" width: 100%; height: 10px;" src="@/assets/images/bg/time-line.png"></img>
<div style="height: calc(50% - 8px);display: flex;">
<div v-for="(item, index) in timeLineIndexItems" class="time-item-box">
{{ item.titlle }}
</div>
</div>
</div> -->
<div class="thematic-btn-right" @click="changeBtn('right')">
<img class="thematic-btn-icon" src="@/assets/images/icon/card-btn-right.png"></img>
</div>
</div> </div>
</div> </div>
<div class="thematic-content" style="margin-top: 20px;height: 350px;"> <div class="thematic-content" style="margin-top: 20px;height: 350px;">
<div class="item-header"> <div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img> <img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">打压遏制手段分布</div> <div class="item-header-text">打压遏制手段分布</div>
</div> </div>
<div class="item-header-divider"></div> <div class="item-header-divider"></div>
<div style="display: flex;height: calc(100% - 60px);"> <div style="display: flex;height: calc(100% - 60px);">
<div class="thematic-btn-left" @click="changeBtn('timwLine', 'left')"> <div class="thematic-btn-left" @click="changeBtn('left')">
<img class="thematic-btn-icon" src="@/assets/images/icon/card-btn-left.png"></img> <img class="thematic-btn-icon" src="@/assets/images/icon/card-btn-left.png"></img>
</div> </div>
<div class="cup-box" style="display: flex;"> <div class="cup-box" style="display: flex;">
<div v-for="item in distribution" class="cup-item-box" :style="{ <div v-for="item in distributionColor" class="cup-item-box" :style="{
color: item.borderColor color: `hsla(${item.color[0]}, ${item.color[1]}%, ${item.color[2]}%, ${1})`
}"> }">
<div class="cup-title"> <div class="cup-title">
{{ item.titlle }} {{ item.titlle }}
</div> </div>
<div class="cup" :style="{ <WaveBall :percent="item.value" :data="item" :color="item.color" :size="128" />
borderColor: item.bgColor1,
outlineColor: item.bgColor1,
}">
<!-- 水位 -->
<div class="water" :style="{
'--percent': item.value,
'--c1': item.bgColor1,
'--c2': item.bgColor2,
}"></div>
<!-- 文字 -->
<span class="txt" :style="{ color: item.borderColor, }">{{ item.text }}</span>
</div>
<div style="margin-top: 20px;"> <div style="margin-top: 20px;">
{{ item.change }} {{ item.change }}
</div> </div>
</div> </div>
</div> </div>
<div class="thematic-btn-right" @click="changeBtn('right')"> <div class="thematic-btn-right" @click="changeBtn('right')">
<img class="thematic-btn-icon" src="@/assets/images/icon/card-btn-right.png"></img> <img class="thematic-btn-icon" src="@/assets/images/icon/card-btn-right.png"></img>
</div> </div>
</div> </div>
</div> </div>
<div style="margin-top: 20px;height: 450px;display: flex;"> <div style="margin-top: 20px;height: 450px;display: flex;">
<div class="thematic-content" style="width: 47.5%; display: block;"> <div class="thematic-content" style="width: 47.5%; display: block;">
...@@ -94,20 +53,37 @@ ...@@ -94,20 +53,37 @@
数据来源:美国某某局</div> 数据来源:美国某某局</div>
</div> </div>
<div class="item-header-divider"></div> <div class="item-header-divider"></div>
<div style="display: flex;">
<el-radio-group v-model="strengthBtn" size="small" @change="handleStrengthBtn">
<el-radio-button value="all" border>全部领域
</el-radio-button>
<el-radio-button value="integratedCircuits" border>集成电路</el-radio-button>
<el-radio-button value="energy" border>能源
</el-radio-button>
<el-radio-button value="biotechnology" border>生物科技</el-radio-button>
<el-radio-button value="quantum" border>量子科技</el-radio-button>
<el-radio-button value="ai" border>人工智能</el-radio-button>
<el-radio-button value="network" border>通信网络</el-radio-button>
</el-radio-group>
<el-select v-model="strengthSelect" placeholder="" style="width: 100px;" size="small">
<el-option v-for="item in strengthTimeList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div style="display: flex;height: calc(100% - 60px);" id="chart1"> <div style="display: flex;height: calc(100% - 60px);" id="chart1">
</div> </div>
</div> </div>
<div class="thematic-content" style="width: 47.5%;margin-left: 5%;"> <div class="thematic-content" style="width: 47.5%;margin-left: 5%;">
<div class="item-header"> <div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img> <img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">打压遏制强度变化</div> <div class="item-header-text">打压遏制领域分布</div>
<div class="item-header-right"> <img class="item-header-right-icon" src="@/assets/images/icon/tips.png"></img> <div class="item-header-right"> <img class="item-header-right-icon" src="@/assets/images/icon/tips.png"></img>
数据来源:美国某某局</div> 数据来源:美国某某局</div>
</div> </div>
<div class="item-header-divider"></div> <div class="item-header-divider"></div>
<el-select v-model="areaSelect" placeholder="" style="width: 100px;float: right;" size="small">
<el-option v-for="item in areaTimeList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<div style="display: flex;height: calc(100% - 60px);" id="char2"> <div style="display: flex;height: calc(100% - 60px);" id="char2">
</div> </div>
</div> </div>
...@@ -121,10 +97,11 @@ import { onMounted, ref, computed } from "vue"; ...@@ -121,10 +97,11 @@ import { onMounted, ref, computed } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import DivideHeader from "@/components/DivideHeader.vue"; import DivideHeader from "@/components/DivideHeader.vue";
import Timeline from '../component/Timeline.vue' import Timeline from '../component/Timeline.vue'
import WaveBall from '../component/WaveBall.vue'
import getBarChart from '../js/barChart.js' import getBarChart from '../js/barChart.js'
import radarChart from '../js/radarChart.js' import radarChart from '../js/radarChart.js'
//对华打压历程
const course = ref([ const course = ref([
{ {
time: '2025年1月', time: '2025年1月',
...@@ -179,65 +156,69 @@ const course = ref([ ...@@ -179,65 +156,69 @@ const course = ref([
]) ])
function showDetail(item) { //打压遏制手段分布
console.log('点击了:', item);
}
// 过滤出奇数下标(1,3,5...)
const timeLineOddIndexItems = computed(() =>
timeLine.value.filter((_, idx) => idx % 2 === 1)
)
// 过滤出偶数下标(1,3,5...)
const timeLineIndexItems = computed(() =>
timeLine.value.filter((_, idx) => idx % 2 === 0)
)
const distribution = ref([ const distribution = ref([
{ {
titlle: '法案', "titlle": "法案",
value: 80, "value": 80,
text: '1626个', "text": "1626",
change: '较上个月+3', "unit": "个",
bgColor1: '#E6F4FF',//浅色 "change": "较上个月+3"
bgColor2: '#91CAFF',//深色 },
borderColor: '#0958D9', {
}, { "titlle": "行政令",
titlle: '法案', "value": 20,
value: 20, "text": "1626",
text: '1626个', "unit": "个",
change: '较上个月+3', "change": "较上个月+1"
bgColor1: '#FFFBE6',//浅色 },
bgColor2: '#FDE19A',//深色 {
borderColor: '#D48806', "titlle": "科技智库",
}, { "value": 10,
titlle: '法案', "text": "66",
value: 10, "unit": "次",
text: '1626个', "change": "较上个月+2"
change: '较上个月+3', },
bgColor1: '#E6F4FF',//浅色 {
bgColor2: '#91CAFF',//深色 "titlle": "出口管制",
borderColor: '#0958D9', "value": 33,
}, { "text": "66",
titlle: '法案', "unit": "次",
value: 40, "change": "较上个月+1"
text: '1626个', },
change: '较上个月+3', {
bgColor1: '#E6F4FF',//浅色 "titlle": "投融资限制",
bgColor2: '#91CAFF',//深色 "value": 80,
borderColor: '#0958D9', "text": "119",
}, { "unit": "次",
titlle: '法案', "change": "较上个月+1"
value: 50, },
text: '1626个', {
change: '较上个月+3', "titlle": "市场准入",
bgColor1: '#E6F4FF',//浅色 "value": 50,
bgColor2: '#91CAFF',//深色 "text": "223",
borderColor: '#0958D9', "unit": "次",
"change": "较上个月+1"
} }
]) ])
//随机生成颜色
const makeColors = () => {
let A = Math.floor(Math.random() * 360) // 随机色相
let B = 70 // 固定饱和度
let C = 50 // 固定亮度
return [A, B, C]
}
// 计算属性
const distributionColor = computed(() => {
for (let item in distribution.value) {
distribution.value[item].color = makeColors()
}
return distribution.value
})
const timeIndex = ref(0) const timeIndex = ref(0)
//时间轴及遏制手段分布按钮 //遏制手段分布按钮
function changeBtn(data, type) { function changeBtn(type) {
if (type === 'left') { if (type === 'left') {
timeIndex.value === 0 ? '' : timeIndex.value = timeIndex.value - 1 timeIndex.value === 0 ? '' : timeIndex.value = timeIndex.value - 1
} else { } else {
...@@ -246,7 +227,7 @@ function changeBtn(data, type) { ...@@ -246,7 +227,7 @@ function changeBtn(data, type) {
} }
} }
//打压遏制强度变化
const chart1Data = ref({ const chart1Data = ref({
name: [ name: [
"2024-12", "2024-12",
...@@ -256,6 +237,52 @@ const chart1Data = ref({ ...@@ -256,6 +237,52 @@ const chart1Data = ref({
value: [83.76, 76.72, 73.89, 72.16, 66.24, 65.47, 63.98, 62.12, 44.38, 24.79], value: [83.76, 76.72, 73.89, 72.16, 66.24, 65.47, 63.98, 62.12, 44.38, 24.79],
}); });
const strengthBtn = ref('all')
function handleStrengthBtn() {
}
//打压遏制强度变化时间选择器
const strengthSelect = ref("近一年");
const strengthTimeList = ref([
{
label: "近半年",
value: "近半年",
},
{
label: "近一年",
value: "近一年",
},
{
label: "近两年",
value: "近两年",
},
{
label: "近三年",
value: "近三年",
},
{
label: "近五年",
value: "近五年",
},
]);
//打压遏制领域分布
const areaSelect = ref("2025");
const areaTimeList = ref([
{
label: "2025",
value: "2025",
},
{
label: "2024",
value: "2024",
},
{
label: "2023",
value: "2023",
},
]);
// 绘制echarts图表 // 绘制echarts图表
const setChart = (option, chartId) => { const setChart = (option, chartId) => {
let chartDom = document.getElementById(chartId); let chartDom = document.getElementById(chartId);
...@@ -301,70 +328,6 @@ onMounted(() => { ...@@ -301,70 +328,6 @@ onMounted(() => {
} }
} }
/* 杯子:正圆 */
.cup {
position: relative;
width: 130px;
height: 130px;
/* 本身高度设为 0,内容靠子元素或绝对定位 */
border-radius: 50%;
border: 4px #2196f3 solid;
overflow: hidden;
/* 关键:把超出部分裁掉 */
display: flex;
align-items: flex-end;
/* 水永远贴底 */
justify-content: center;
border: 4px solid #2196f3;
/* 内圈实线 */
outline: 4px solid #2196f3;
/* 外圈实线 */
outline-offset: 4px;
/* 两线间距,想再宽调大即可 */
}
/* 水位层 */
.water {
width: 100%;
height: calc(var(--percent) * 1%);
/* 0~100 对应 0~100% */
background: linear-gradient(to top, var(--c1), var(--c2));
position: relative;
animation: wave 3s linear infinite;
}
/* 波浪 */
.water::before,
.water::after {
content: '';
position: absolute;
width: 400px;
height: 400px;
top: -350px;
/* 把圆心顶到上面去 */
left: 50%;
border-radius: 45%;
transform: translate(-50%, 0);
background: rgba(255, 255, 255, .4);
animation: rotate 7s linear infinite;
}
.water::after {
top: -370px;
border-radius: 40%;
opacity: .7;
animation-duration: 9s;
animation-direction: reverse;
}
/* 旋转动画 */
@keyframes rotate {
to {
transform: translate(-50%, 0) rotate(360deg);
}
}
/* 百分比文字 */ /* 百分比文字 */
.txt { .txt {
...@@ -477,11 +440,13 @@ onMounted(() => { ...@@ -477,11 +440,13 @@ onMounted(() => {
.thematic-btn-left { .thematic-btn-left {
height: 100%; height: 100%;
width: 24px; width: 24px;
padding-top: 130px;
} }
.thematic-btn-right { .thematic-btn-right {
height: 100%; height: 100%;
width: 24px; width: 24px;
padding-top: 130px;
} }
.thematic-btn-icon { .thematic-btn-icon {
......
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
<div class="title"> <div class="title">
{{ item.time }} {{ item.time }}
</div> </div>
<div class="name"> <div class="title">
{{ item.name }} {{ item.title }}
</div> </div>
<div class="content"> <div class="content">
{{ item.content }} {{ item.content }}
...@@ -89,20 +89,21 @@ export default { ...@@ -89,20 +89,21 @@ export default {
width: 100%; width: 100%;
position: relative; position: relative;
padding: 0 40px; padding: 0 40px;
box-sizing: border-box;
} }
.arrow { .arrow {
position: absolute; position: absolute;
top: 50%; top: 50%;
transform: translateY(-50%); /* 左右切换按钮 */
width: 32px; width: 24px;
height: 32px; height: 48px;
border-radius: 50%; font-size: 24px;
border: 1px solid #dcdfe6; border-color: #E7F3FF;
background: #fff; border: 0;
background: #E7F3FF;
cursor: pointer; cursor: pointer;
z-index: 10; z-index: 10;
color: #3E84D1;
} }
.arrow:disabled { .arrow:disabled {
...@@ -112,10 +113,13 @@ export default { ...@@ -112,10 +113,13 @@ export default {
.left { .left {
left: 0; left: 0;
border-radius: 4px 0px 0px 4px;
} }
.right { .right {
right: 0; right: 0;
border-radius: 0px 4px 4px 0px;
} }
.timeline-box { .timeline-box {
...@@ -172,7 +176,7 @@ export default { ...@@ -172,7 +176,7 @@ export default {
/* 向上节点:线往下伸 */ /* 向上节点:线往下伸 */
.dot.up::after { .dot.up::after {
bottom: 100%; bottom: 100%;
height: 120px; height: 180px;
/* 圆环底部 → 卡片顶 */ /* 圆环底部 → 卡片顶 */
} }
...@@ -180,26 +184,23 @@ export default { ...@@ -180,26 +184,23 @@ export default {
.dot.down::after { .dot.down::after {
top: 100%; top: 100%;
height: 120px; height: 180px;
} }
.card { .card {
position: absolute; position: absolute;
height: 120px; height: 180px;
width: 15vw; width: 15vw;
padding: 8px 12px; padding: 8px 12px;
text-align: center; text-align: left;
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
} }
.title { .title {
color: rgba(5, 95, 194, 1); color: rgba(5, 95, 194, 1);
font-size: 18px; font-size: 18px;
font-weight: 700; font-weight: 700;
line-height: 26px; line-height: 26px;
...@@ -207,9 +208,8 @@ export default { ...@@ -207,9 +208,8 @@ export default {
text-align: justify; text-align: justify;
} }
.name { .title {
color: var(----80, rgba(59, 65, 75, 1)); color: rgba(59, 65, 75, 1);
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 700;
line-height: 26px; line-height: 26px;
...@@ -217,7 +217,14 @@ export default { ...@@ -217,7 +217,14 @@ export default {
text-align: justify; text-align: justify;
} }
.content {} .content {
color: rgba(95, 101, 108, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: justify;
}
.card.up { .card.up {
bottom: 20px; bottom: 20px;
......
<template>
<div ref="ballDom" class="ball-box" :style="{ width: size + 'px', height: size + 'px' }" />
</template>
<script setup>
/**
* Vue3 水波进度球
* 用法:<WaterBall :percent="67" :size="200" />
*/
import { ref, watch, onMounted } from 'vue'
import * as echarts from 'echarts'
import 'echarts-liquidfill'
/* props */
const props = defineProps({
percent: { type: Number, default: 60 }, // 0~100
size: { type: Number, default: 200 }, // 画布宽高
data: { type: Object, default: {} },
color: { type: Array, default: [] },
})
/* dom */
const ballDom = ref(null)
let instance = null
// const color = ref([0, 0, 0])
// const makeColors = () => {
// props.color[0] = Math.floor(Math.random() * 360) // 随机色相
// props.color[1] = 70 // 固定饱和度
// props.color[2] = 50 // 固定亮度
// }
/* 颜色映射 */
/* 配置项 */
const makeOption = () => {
const p = Math.min(100, Math.max(0, props.percent)) / 100
console.log(props.color, 'colorcolorcolor')
return {
series: [{
type: 'liquidFill',
radius: '90%',
data: [
{ value: p, direction: 'right' },
{ value: p, direction: 'left' } // 两层波浪反向
],
color: [`hsla(${props.color[0]}, ${props.color[1]}%, ${props.color[2]}%, ${1})`, `hsla(${props.color[0]}, ${props.color[1]}%, ${props.color[2]}%, ${0.8})`],
waveAnimation: true,
animationEasingUpdate: 'cubicOut',
outline: {
show: true,
borderDistance: 4, // 第一层边框
itemStyle: {
borderWidth: 3,
borderColor: `hsla(${props.color[0]}, ${props.color[1]}%, ${props.color[2]}%, ${0.5})`
}
},
backgroundStyle: {
color: '#ffffff',
shadowColor: '#fff', //阴影
shadowBlur: 0, //阴影模糊
},
label: {
show: true,
formatter: `${props.data.text}` + `${props.data.unit}`,
fontSize: 24,
color: `hsla(${props.color[0]}, ${props.color[1]}%, ${props.color[2]}%, ${1})`,
// insideColor: '#fff'
}
}]
}
}
/* 初始化 */
const init = () => {
if (instance) instance.dispose()
instance = echarts.init(ballDom.value)
instance.setOption(makeOption())
}
/* 自动更新 */
watch(() => props.percent, () => {
instance?.setOption(makeOption())
})
onMounted(init)
</script>
<style scoped>
.ball-box {
margin: 0 auto;
}
</style>
\ No newline at end of file
...@@ -3,31 +3,79 @@ ...@@ -3,31 +3,79 @@
<div class="resource-box"> <div class="resource-box">
<DivideHeader class="divide1" :titleText="'中美科技实力对比'"></DivideHeader> <DivideHeader class="divide1" :titleText="'中美科技实力对比'"></DivideHeader>
<div style="margin-top: 20px;height: 450px;display: flex;"> <div style="margin-top: 20px;display: flex;">
<div class="resource-content" style="width:100%; display: flex;">
<div style="width: 50%;height: 100%;" id="char7"> <div class="resource-content" style="width:100%">
<el-radio-group v-model="strengthBtn" size="small" @change="handlestrengthBtn" style="margin-left: 15%;">
</div> <el-radio-button value="potential" border>竞争潜力
<div style="width: 50%;height: 100%;text-align: center;"> </el-radio-button>
<div v-for="value in data"> <el-radio-button value="potency" border>竞争效力</el-radio-button>
<div>{{ value.name }}</div> <el-radio-button value="strength" border>竞争实力
<div class="progress-row"> </el-radio-button>
<div class="progress-wrapper left"> </el-radio-group>
<el-progress :percentage="value.value[0]" :stroke-width="20" class="left-progress" /> <div style="display: flex;height: 350px;">
<span class="inner-text">{{ value.value[0] }}%</span> <div style="width: 50%;height: 100%;" id="char7">
</div>
<div style="width: 50%;height: 100%;text-align: center;padding:0 15px ;position: relative;">
<div class="title-box">
<div style="display: flex;width: 100%;">
<div class="unite">
<img class="unite-icon" src="@/assets/images/icon/united_USA.png" alt="" />
<div class="unite-title">
美国
</div>
</div>
<div class="unite-title" style="width: 33%;text-align: center;line-height: 40px;">
{{ data[0].name }}
</div>
<div class="unite" style=" justify-content: flex-end; ">
<div class="unite-title">
中国
</div>
<img class="unite-icon" src="@/assets/images/icon/united_CHN.png" alt="" />
</div>
</div> </div>
<div class="progress-row">
<!-- 右侧进度条 --> <div class="progress-wrapper left" :style="{ '--i': '40px', '--j': '-20px' }">
<div class="progress-wrapper right"> <el-progress :percentage="data[0].value[0]" :stroke-width="20" class="left-progress"
<el-progress :percentage="value.value[1]" :stroke-width="20" class="right-progress" /> :show-text="false" />
<span class="inner-text">{{ value.value[0] }}%</span> <span class="inner-text" style="font-size: 24px;line-height: 40px;top: 20px;">{{ data[0].value[0]
}}%</span>
</div>
<!-- 右侧进度条 -->
<div class="progress-wrapper right" :style="{ '--i': '40px' }">
<el-progress :percentage="data[0].value[1]" :stroke-width="20" class="right-progress"
:show-text="false" />
<span class="inner-text" style="font-size: 24px;line-height: 40px;top: 20px;">{{ data[0].value[0]
}}%</span>
</div>
</div>
</div>
<div v-for="value in data.slice(1)">
<div class="unite-title-small" style="margin-top: 10px;">{{ value.name }}</div>
<div class="progress-row">
<div class="progress-wrapper left" :style="{ '--i': '20px', '--j': '0px' }">
<el-progress :percentage="value.value[0]" :stroke-width="20" class="left-progress"
:show-text="false" />
<span class="inner-text">{{ value.value[0] }}%</span>
</div>
<!-- 右侧进度条 -->
<div class="progress-wrapper right" :style="{ '--i': '20px' }">
<el-progress :percentage="value.value[1]" :stroke-width="20" class="right-progress"
:show-text="false" />
<span class="inner-text">{{ value.value[0] }}%</span>
</div>
</div> </div>
</div> </div>
</div>
<!-- 左侧进度条 --> <!-- 左侧进度条 -->
</div>
</div> </div>
<div> <div>
</div> </div>
...@@ -37,7 +85,7 @@ ...@@ -37,7 +85,7 @@
</div> </div>
<SymmetryProgress :left-val="62" :right-val="88" />
...@@ -51,8 +99,11 @@ import * as echarts from "echarts"; ...@@ -51,8 +99,11 @@ import * as echarts from "echarts";
import DivideHeader from "@/components/DivideHeader.vue"; import DivideHeader from "@/components/DivideHeader.vue";
import radarChart3 from '../js/radarChart3.js' import radarChart3 from '../js/radarChart3.js'
//科技实力按钮
const strengthBtn = ref('enterprise')
function handlestrengthBtn() {
}
// 绘制echarts图表 // 绘制echarts图表
const setChart = (option, chartId) => { const setChart = (option, chartId) => {
let chartDom = document.getElementById(chartId); let chartDom = document.getElementById(chartId);
...@@ -102,9 +153,9 @@ onMounted(() => { ...@@ -102,9 +153,9 @@ onMounted(() => {
/* ========== 一行两个 ========== */ /* ========== 一行两个 ========== */
.progress-row { .progress-row {
display: flex; display: flex;
gap: 16px; // gap: 16px;
align-items: center; align-items: center;
padding: 10px; padding: 5px;
} }
/* ========== 公共外壳 ========== */ /* ========== 公共外壳 ========== */
...@@ -135,35 +186,93 @@ onMounted(() => { ...@@ -135,35 +186,93 @@ onMounted(() => {
white-space: nowrap; white-space: nowrap;
} }
.left-progress {
transform: scaleX(-1);
}
.left-progress :deep(.el-progress-bar__outer) { .left-progress :deep(.el-progress-bar__outer) {
border-radius: 10px 0 0 10px; border-radius: 0 var(--i) var(--i) 0;
clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 100%, 0 100%); clip-path: polygon(0px 0, 100% 0, 100% 100%, var(--i) 100%);
background: #aed6ff; background: rgba(231, 243, 255, 1);
overflow: hidden; overflow: hidden;
/* 读行内传进来的变量 */
height: var(--i) !important;
margin-left: var(--j) !important
} }
.left-progress :deep(.el-progress-bar__inner) { .left-progress :deep(.el-progress-bar__inner) {
border-radius: 10px 0 0 10px; border-radius: 0 var(--i) var(--i) 0;
clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 100%, 0 100%); clip-path: polygon(0px 0, 100% 0, 100% 100%, var(--i) 100%);
background: #aed6ff; background: rgba(174, 214, 255, 1);
/* 读行内传进来的变量 */
height: var(--i) !important;
margin-left: var(--j) !important
} }
/* ========== 右侧:左侧斜切 + 右侧圆角 ========== */ /* ========== 右侧:左侧斜切 + 右侧圆角 ========== */
.right-progress :deep(.el-progress-bar__outer) { .right-progress :deep(.el-progress-bar__outer) {
border-radius: 0 10px 10px 0; border-radius: 0 var(--i) var(--i) 0;
clip-path: polygon(10px 0, 100% 0, 100% 100%, 0 100%); clip-path: polygon(var(--i) 0, 100% 0, 100% 100%, 0 100%);
background: #ffccc7; background: #ffccc7;
overflow: hidden; overflow: hidden;
height: var(--i) !important;
} }
.right-progress :deep(.el-progress-bar__inner) { .right-progress :deep(.el-progress-bar__inner) {
border-radius: 0 10px 10px 0; border-radius: 0 var(--i) var(--i) 0;
clip-path: polygon(10px 0, 100% 0, 100% 100%, 0 100%); clip-path: polygon(var(--i) 0, 100% 0, 100% 100%, 0 100%);
background: #ff7875; background: #ff7875;
height: var(--i) !important;
}
.title-box {
height: 100px;
width: 100%;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 4px;
background: rgba(247, 248, 249, 1);
.unite {
display: flex;
width: 33%;
align-items: center;
.unite-icon {
width: 40px;
padding: 5px;
}
}
.unite-title {
color: rgba(59, 65, 75, 1);
font-size: 24px;
font-weight: 700;
letter-spacing: 0px;
text-align: left;
}
}
.unite-title-small {
color: rgba(59, 65, 75, 1);
font-size: 18px;
font-weight: 700;
letter-spacing: 0px;
} }
.resource-box { .resource-box {
height: 700px; height: 800px;
} }
.title-text { .title-text {
...@@ -174,6 +283,14 @@ onMounted(() => { ...@@ -174,6 +283,14 @@ onMounted(() => {
text-align: center; text-align: center;
} }
.title-text-small {
font-size: 24px;
font-weight: 700;
line-height: 42px;
margin: 40px;
text-align: center;
}
.title-img { .title-img {
width: 100%; width: 100%;
height: 42px; height: 42px;
...@@ -182,7 +299,7 @@ onMounted(() => { ...@@ -182,7 +299,7 @@ onMounted(() => {
.resource-content { .resource-content {
/* 容器 480 */ /* 容器 480 */
width: 100%; width: 100%;
height: 450px; height: 550px;
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: var(---, 10px); border-radius: var(---, 10px);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论