提交 6b608fa3 authored 作者: caijian's avatar caijian

Merge branch 'cj_dev'

......@@ -147,4 +147,132 @@ export function getSurvyInfo(params) {
url: `/api/marketsearchHome/statSortDetails`,
params
})
}
// 查询调查的所有发布领域 /marketsearchDetails/getSearchAllArea
export function getSearchAllArea(params) {
return request({
method: 'GET',
url: `/api/marketsearchDetails/getSearchAllArea`,
params
})
}
// 查询调查的所有发布时间
export function getSearchAllYear(params) {
return request({
method: 'GET',
url: `/api/marketsearchDetails/getSearchYears`,
params
})
}
// 调查简介 /marketsearchDetails/searchBlurb
export function getSearchBlurb(params) {
return request({
method: 'GET',
url: `/api/marketsearchDetails/searchBlurb`,
params
})
}
// 获取相关事件getRelatedEvents
export function getRelatedEvents(params) {
return request({
method: 'GET',
url: `/api/marketsearchDetails/getRelatedEvents`,
params
})
}
// 获取事件脉络 getSearchContext
export function getSearchContext(params) {
return request({
method: 'GET',
url: `/api/marketsearchDetails/getSearchContext`,
params
})
}
// getSearchMeasures 报复性措施
export function getSearchMeasures(params) {
return request({
method: 'GET',
url: `/api/marketsearchDetails/getSearchMeasures`,
params
})
}
// /marketsearchDetails/getSearchDirection 查询调查方向和结果信息
export function getSearchDirection(params) {
return request({
method: 'GET',
url: `/api/marketsearchDetails/getSearchDirection`,
params
})
}
// 中国公司受调查情况 /marketsearchDetails/statcnOrgCount
export function getStatcnOrgCount(params) {
return request({
method: 'GET',
url: `/api/marketsearchDetails/statcnOrgCount`,
params
})
}
// /marketsearchDetails/getSearchConclusion 查询调查结论
export function getSearchConclusion(params) {
return request({
method: 'GET',
url: `/api/marketsearchDetails/getSearchConclusion`,
params
})
}
// getReportAnalyze 报告分析
export function getReportAnalyze(params) {
return request({
method: 'GET',
url: `/api/marketsearchDetails/getReportAnalyze`,
params
})
}
// 被诉企业列表
export function getSuedOrg(params) {
return request({
method: 'GET',
url: `/api/marketsearchDetails/getSuedOrg`,
params
})
}
// 进出口数据
export function getOrgImportAndExport(params) {
return request({
method: 'GET',
url: `/api/organization/scale/getOrgImportAndExport`,
params
})
}
// 营收数据
export function getRevenue(params) {
return request({
method: 'GET',
url: `/api/organization/scale/revenue`,
params
})
}
// 净利润数据
export function getNetProfit(params) {
return request({
method: 'GET',
url: `/api/organization/scale/netProfit`,
params
})
}
\ No newline at end of file
<template>
<div class="case-wrapper">
<div class="wrapper-header">
<div class="header-filters">
<div class="search-box">
<el-input
v-model="searchText"
style="width: 240px; height: 32px"
placeholder="搜索调查案件"
@keyup.enter="handleSearch"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
<div class="dropdown-filters">
<el-select v-model="filterStage" placeholder="全部阶段" class="filter-select" clearable>
<el-option label="全部阶段" value="" />
<el-option label="调查中" value="1" />
<el-option label="调查结束" value="0" />
</el-select>
<el-select v-model="filterParty" placeholder="全部原告/被告" class="filter-select" clearable>
<el-option label="全部原告/被告" value="" />
</el-select>
<el-select v-model="filterReason" placeholder="全部原因" class="filter-select" clearable>
<el-option label="全部原因" value="" />
</el-select>
</div>
</div>
<div class="select-box">
<div class="paixu-btn" @click="handleSwithSort">
<div class="text">{{ "发布时间" }}</div>
<div class="icon2">
<img v-if="isSort" src="@/assets/icons/shengxu2.png" alt="" />
<img v-else src="@/assets/icons/jiangxu2.png" alt="" />
</div>
</div>
</div>
</div>
<div class="wrapper-main">
<div class="left">
<div class="left-box2">
<div class="left-box2-header">
<div class="icon"></div>
<div class="title">{{ "科技领域" }}</div>
</div>
<div class="left-box2-main">
<div class="checkbox-group">
<el-checkbox
v-model="checkAllAreas"
:indeterminate="isIndeterminateAreas"
@change="handleCheckAllAreasChange"
class="filter-checkbox"
>
全部领域
</el-checkbox>
<div class="area-grid">
<el-checkbox
v-for="area in surveyAreaList"
:key="area.id"
v-model="checkedAreaList"
:label="area.id"
class="filter-checkbox"
@change="handleCheckedAreasChange"
>
{{ area.name }}
</el-checkbox>
</div>
</div>
</div>
</div>
<div class="left-box1">
<div class="left-box1-header">
<div class="icon"></div>
<div class="title">{{ "发布时间" }}</div>
</div>
<div class="left-box1-main">
<div class="checkbox-group">
<el-checkbox
v-model="checkAllYears"
:indeterminate="isIndeterminateYears"
@change="handleCheckAllYearsChange"
class="filter-checkbox"
>
全部时间
</el-checkbox>
<el-checkbox
v-for="year in displayedYearList"
:key="year.id"
v-model="checkedSurveyYears"
:label="year.id"
class="filter-checkbox"
@change="handleCheckedYearsChange"
>
{{ year.name }}
</el-checkbox>
<div v-if="surveyYearList.length > 6" class="expand-btn" @click="isYearExpanded = !isYearExpanded">
{{ isYearExpanded ? "收起" : "更早" }}
<el-icon>
<ArrowUp v-if="isYearExpanded" />
<ArrowDown v-else />
</el-icon>
</div>
</div>
</div>
</div>
</div>
<div class="right">
<div class="right-header">
<div class="icon">
<img src="./assets/images/right-header-icon.png" alt="" />
</div>
<div class="title">{{ "232调查历程" }}</div>
</div>
<div class="right-main" v-loading="listLoading">
<div
class="timeline-item"
v-for="(item, index) in surveyInfoList"
:key="index"
@click="handleToSingleCase(item)"
>
<div class="timeline-date">
<div class="date-text">{{ item.timeZH || item.time }}</div>
</div>
<div class="timeline-line-box">
<div class="timeline-icon">
<img v-if="item.sortImageUrl" :src="item.sortImageUrl" alt="" />
<div v-else class="default-dot"></div>
</div>
<div class="timeline-line" v-if="index !== surveyInfoList.length - 1"></div>
</div>
<div class="timeline-content-card">
<div class="card-header">
<div class="tag-box">
<div class="tag-232">{{ item.tag }}</div>
<div class="title">{{ item.title }}</div>
</div>
<div class="status" :class="{ 'status-active': item.status === '调查中' }">
<span class="dot"></span> {{ item.status }}
</div>
</div>
<div class="card-body">
{{ item.content }}
</div>
<div class="card-footer">
<div class="area-tag" v-for="(area, idx) in item.areaList" :key="idx">
{{ area }}
</div>
</div>
</div>
</div>
</div>
<div class="right-footer">
<div class="footer-left">
{{ `共${totalDiscussNum}项调查` }}
</div>
<div class="footer-right">
<el-pagination
@current-change="handleCurrentChange"
:pageSize="pageSize"
:current-page="currentPage"
background
layout="prev, pager, next"
:total="totalDiscussNum"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, watch, computed } from "vue";
import router from "@/router";
import { useRoute } from "vue-router";
import { Search, ArrowDown, ArrowUp } from "@element-plus/icons-vue";
import { getSearchAllArea, getSearchAllYear, getSurveyList } from "@/api/marketAccessRestrictions";
const route = useRoute();
// 顶部过滤项
const searchText = ref("");
const filterStage = ref("");
const filterParty = ref("");
const filterReason = ref("");
const isSort = ref(false); // false 降序
const handleSwithSort = () => {
isSort.value = !isSort.value;
};
// 科技领域过滤
const surveyAreaList = ref([]);
const checkedAreaList = ref([]);
const checkAllAreas = ref(true);
const isIndeterminateAreas = ref(false);
const handleCheckAllAreasChange = val => {
checkedAreaList.value = val ? surveyAreaList.value.map(a => a.id) : [];
isIndeterminateAreas.value = false;
};
const handleCheckedAreasChange = value => {
const checkedCount = value.length;
checkAllAreas.value = checkedCount === surveyAreaList.value.length;
isIndeterminateAreas.value = checkedCount > 0 && checkedCount < surveyAreaList.value.length;
};
// 发布时间过滤
const surveyYearList = ref([]);
const checkedSurveyYears = ref([]);
const checkAllYears = ref(true);
const isIndeterminateYears = ref(false);
const isYearExpanded = ref(false);
const displayedYearList = computed(() => {
if (isYearExpanded.value) return surveyYearList.value;
return surveyYearList.value.slice(0, 6);
});
const handleCheckAllYearsChange = val => {
checkedSurveyYears.value = val ? surveyYearList.value.map(y => y.id) : [];
isIndeterminateYears.value = false;
};
const handleCheckedYearsChange = value => {
const checkedCount = value.length;
checkAllYears.value = checkedCount === surveyYearList.value.length;
isIndeterminateYears.value = checkedCount > 0 && checkedCount < surveyYearList.value.length;
};
// 数据列表
const surveyInfoList = ref([]);
const totalDiscussNum = ref(0);
const pageSize = ref(10);
const currentPage = ref(1);
const isInitializing = ref(true);
const listLoading = ref(false);
const handleFetchSurveyList = async () => {
listLoading.value = true;
try {
const params = {
currentPage: currentPage.value - 1,
pageSize: pageSize.value,
sortCode: "232",
publishYear: checkAllYears.value ? "" : checkedSurveyYears.value.toString(),
Area: checkAllAreas.value ? "" : checkedAreaList.value.toString(),
caseStatus: filterStage.value,
keywords: searchText.value,
sortField: "date",
sortOrder: isSort.value ? "asc" : "desc"
};
const res = await getSurveyList(params);
if (res.code === 200 && res.data) {
surveyInfoList.value = res.data.content.map(item => {
return {
time: item.SEARCHDATE,
timeZH: item.SEARCHDATEZH,
title: item.SEARCHNAME,
content: item.SEARCHDESC || item.SEARCHNAME,
status: item.CASESTATUS || "调查中",
areaList: item.searchArea || [],
tag: item.SORTCODE || "232",
id: item.SEARCHID,
sortImageUrl: item.SORTIMAGEURL
};
});
totalDiscussNum.value = res.data.totalElements || 0;
}
} catch (error) {
console.error("获取调查列表失败", error);
} finally {
listLoading.value = false;
}
};
const handleCurrentChange = val => {
currentPage.value = val;
handleFetchSurveyList();
};
const handleSearch = () => {
currentPage.value = 1;
handleFetchSurveyList();
};
const handleToSingleCase = item => {
const curRoute = router.resolve({
path: "/marketSingleCaseLayout/overview",
query: {
id: "232",
searchId: item.id + ""
}
});
window.open(curRoute.href, "_blank");
};
// 监听过滤条件
watch([checkedSurveyYears, checkedAreaList, isSort, filterStage, filterParty, filterReason], () => {
if (isInitializing.value) return;
currentPage.value = 1;
handleFetchSurveyList();
});
const handleGetSearchAllArea = async () => {
try {
const res = await getSearchAllArea({ sortCode: "232" });
if (res.code === 200 && res.data) {
surveyAreaList.value = res.data.map(item => ({
name: item.AREANAME,
id: item.AREACODE
}));
handleCheckAllAreasChange(true);
}
} catch (error) {}
};
const handleGetSearchAllYear = async () => {
try {
const res = await getSearchAllYear({ sortCode: "232" });
if (res.code === 200 && res.data) {
const sortedYears = res.data.sort((a, b) => b - a);
surveyYearList.value = sortedYears.map(item => ({
name: item + "年",
id: item
}));
handleCheckAllYearsChange(true);
}
} catch (error) {}
};
onMounted(async () => {
await Promise.all([handleGetSearchAllArea(), handleGetSearchAllYear()]);
isInitializing.value = false;
handleFetchSurveyList();
});
</script>
<style lang="scss" scoped>
.case-wrapper {
width: 1600px;
margin: 0 auto;
padding-top: 20px;
.wrapper-header {
height: 32px;
margin-bottom: 16px;
display: flex;
justify-content: space-between;
align-items: center;
.header-filters {
display: flex;
gap: 16px;
align-items: center;
.dropdown-filters {
display: flex;
gap: 12px;
.filter-select {
width: 140px;
:deep(.el-input__wrapper) {
background-color: #fff;
}
}
}
}
.select-box {
.paixu-btn {
display: flex;
align-items: center;
gap: 8px;
height: 32px;
border: 1px solid #e6e7e8;
border-radius: 4px;
background: #fff;
cursor: pointer;
padding: 0 12px;
.text {
font-size: 14px;
color: #5f656c;
}
.icon2 {
width: 10px;
img {
width: 100%;
}
}
}
}
}
.wrapper-main {
display: flex;
justify-content: space-between;
.left {
width: 300px;
min-height: 560px;
height: fit-content;
padding-bottom: 20px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: #fff;
.left-box1,
.left-box2 {
margin-top: 17px;
.left-box1-header,
.left-box2-header {
display: flex;
align-items: center;
.icon {
width: 8px;
height: 16px;
background: var(--color-main-active);
border-radius: 0 2px 2px 0;
}
.title {
margin-left: 17px;
color: var(--color-main-active);
font-size: 16px;
font-weight: 700;
}
}
}
.checkbox-group {
padding: 10px 0 0 25px;
display: flex;
flex-direction: column;
.area-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 4px;
}
.filter-checkbox {
margin-bottom: 8px;
height: 32px;
:deep(.el-checkbox__label) {
font-size: 16px;
color: #5f656c;
}
}
.expand-btn {
color: var(--color-main-active);
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
margin-top: 4px;
.el-icon {
margin-left: 4px;
}
}
}
}
.right {
width: 1284px;
min-height: 700px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: #fff;
padding-bottom: 20px;
.right-header {
height: 48px;
display: flex;
align-items: center;
border-bottom: 1px solid #eaeeef;
padding: 0 20px;
.icon {
width: 22px;
img {
width: 100%;
}
}
.title {
margin-left: 12px;
font-size: 20px;
font-weight: 700;
color: var(--color-main-active);
}
}
.right-main {
padding: 30px 40px;
.timeline-item {
display: flex;
cursor: pointer;
&:hover {
.timeline-content-card {
background-color: #f5f7fa;
}
}
.timeline-date {
width: 100px;
text-align: right;
padding-right: 20px;
.date-text {
font-size: 16px;
font-weight: 700;
color: var(--color-main-active);
line-height: 24px;
}
}
.timeline-line-box {
width: 40px;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.timeline-icon {
width: 32px;
height: 32px;
z-index: 2;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
}
.default-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: orange;
}
}
.timeline-line {
width: 2px;
flex: 1;
background: #eaeeef;
margin: 4px 0;
}
}
.timeline-content-card {
flex: 1;
// border: 1px solid #eaeeef;
border-radius: 8px;
padding: 16px 20px;
margin-left: 10px;
margin-bottom: 24px;
transition: all 0.3s;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
.tag-box {
display: flex;
align-items: center;
gap: 12px;
.tag-232 {
padding: 2px 10px;
border: 1px solid #b37feb;
background: #f9f0ff;
color: #722ed1;
border-radius: 4px;
font-weight: 700;
font-size: 16px;
}
.title {
font-size: 18px;
font-weight: 700;
color: #3b414b;
}
}
.status {
font-size: 16px;
color: #84888e;
.dot {
margin-right: 4px;
}
}
.status-active {
color: var(--color-main-active);
}
}
.card-body {
font-size: 16px;
color: #5f656c;
line-height: 1.6;
margin-bottom: 16px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
}
.card-footer {
display: flex;
gap: 8px;
.area-tag {
padding: 2px 12px;
background: #e6f7ff;
border: 1px solid #91d5ff;
color: #1890ff;
border-radius: 4px;
font-size: 14px;
}
}
}
}
}
.right-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 40px;
margin-top: 20px;
.footer-left {
font-size: 14px;
color: #5f656c;
}
}
}
}
}
</style>
......@@ -30,46 +30,74 @@
</div>
<div class="wrapper-main">
<div class="left">
<div class="left-box1">
<div class="left-box1-header">
<div class="left-box2">
<div class="left-box2-header">
<div class="icon"></div>
<div class="title">{{ "发布时间" }}</div>
<div class="title">{{ "科技领域" }}</div>
</div>
<div class="left-box1-main">
<div class="left-box2-main">
<div class="checkbox-group">
<el-checkbox
v-for="year in surveyYearList"
:key="year.id"
v-model="checkedSurveyYears"
:label="year.id"
v-model="checkAllAreas"
:indeterminate="isIndeterminateAreas"
@change="handleCheckAllAreasChange"
class="filter-checkbox"
@change="handleChangeCheckedSurveyYears"
>
{{ year.name }}
全部领域
</el-checkbox>
<el-checkbox
v-for="area in surveyAreaList"
:key="area.id"
v-model="checkedAreaList"
:label="area.id"
class="filter-checkbox"
@change="handleCheckedAreasChange"
>
{{ area.name }}
</el-checkbox>
</div>
</div>
</div>
<div class="left-box2">
<div class="left-box2-header">
<div class="left-box1">
<div class="left-box1-header">
<div class="icon"></div>
<div class="title">{{ "科技领域" }}</div>
<div class="title">{{ "发布时间" }}</div>
</div>
<div class="left-box2-main">
<div class="left-box1-main">
<div class="checkbox-group">
<el-checkbox
v-for="area in surveyAreaList"
:key="area.id"
v-model="checkedAreaList"
:label="area.id"
v-model="checkAllYears"
:indeterminate="isIndeterminateYears"
@change="handleCheckAllYearsChange"
class="filter-checkbox"
@change="handleChangeCheckedAreas"
>
{{ area.name }}
全部时间
</el-checkbox>
<el-checkbox
v-for="year in displayedYearList"
:key="year.id"
v-model="checkedSurveyYears"
:label="year.id"
class="filter-checkbox"
@change="handleCheckedYearsChange"
>
{{ year.name }}
</el-checkbox>
<div
v-if="surveyYearList.length > 6"
class="expand-btn"
@click="isYearExpanded = !isYearExpanded"
>
{{ isYearExpanded ? "收起" : "更早" }}
<el-icon>
<ArrowUp v-if="isYearExpanded" />
<ArrowDown v-else />
</el-icon>
</div>
</div>
</div>
</div>
</div>
<div class="right">
<div class="right-header">
......@@ -78,9 +106,9 @@
</div>
<div class="title">{{ "301调查历程" }}</div>
</div>
<div class="right-main">
<div class="right-main" v-loading="listLoading">
<div class="item" v-for="(item, index) in surveyInfoList" :key="index" @click="handleToSingleCase(item)">
<div class="item-left">{{ "2025年11月12日" }}</div>
<div class="item-left">{{ item.time }}</div>
<div class="item-center">
<div class="icon">
<!-- <img :src="item.img" alt="" /> -->
......@@ -89,7 +117,7 @@
</div>
<div class="item-right">
<div class="item-right-header">
<div class="tag tag1">{{ "337" }}</div>
<div class="tag tag1">{{ item.tag }}</div>
<div class="title">{{ item.title }}</div>
<div class="status">
<div class="status-icon"></div>
......@@ -105,7 +133,7 @@
</div>
<div class="flag-box">
<div class="flag" v-for="(vall, idxx) in item.countryList" :key="idxx">
<!-- <img src="" alt="" /> -->
<img :src="vall" alt="" style="width: 100%; height: 100%; border-radius: 50%;" />
</div>
</div>
</div>
......@@ -133,9 +161,11 @@
</template>
<script setup>
import { ref, onMounted, watch } from "vue";
import { ref, onMounted, watch, computed } from "vue";
import router from "@/router";
import { useRoute } from "vue-router";
import { Search, ArrowDown, ArrowUp } from "@element-plus/icons-vue";
import { getSearchAllArea, getSearchAllYear, getSurveyList } from "@/api/marketAccessRestrictions";
const route = useRoute();
const isSort = ref(true); // true 升序 false 倒序
......@@ -143,133 +173,108 @@ const handleSwithSort = () => {
isSort.value = !isSort.value;
};
const allYear = {
name: "全部时间",
selected: true
};
const surveyYearList = ref([]);
const checkedSurveyYears = ref([]);
const isYearExpanded = ref(false);
const surveyYearList = ref([
{
name: "2025年",
id: "2025"
},
{
name: "2024年",
id: "2024"
},
{
name: "2023年",
id: "2023"
},
{
name: "2022年",
id: "2022"
},
{
name: "2021年",
id: "2021"
const displayedYearList = computed(() => {
if (isYearExpanded.value) {
return surveyYearList.value;
}
]);
const checkedSurveyYears = ref(["2025"]);
return surveyYearList.value.slice(0, 6);
});
const surveyAreaList = ref([
{
name: "人工智能",
id: "人工智能"
},
{
name: "半导体/芯片",
id: "半导体/芯片"
},
{
name: "电子设备",
id: "电子设备"
},
{
name: "显示技术",
id: "显示技术"
}
]);
const checkAllYears = ref(false);
const isIndeterminateYears = ref(false);
const checkedAreaList = ref(['人工智能'])
const handleCheckAllYearsChange = (val) => {
checkedSurveyYears.value = val ? surveyYearList.value.map(y => y.id) : [];
isIndeterminateYears.value = false;
};
const surveyInfoList = ref([
{
time: "2025 11月12日",
survey: "337",
title: "外国制造的半导体器件及其下游产品和组件",
content:
"美国国际贸易委员会(ITC)投票决定对特定半导体器件及其下游计算产品和组件(Certain Semiconductor Devices, Computing Products Containing the Same,and Components Thereof)启动337调查。",
status: "部分终裁",
areaList: ["集成电路"],
countryList: ["a", "b"]
},
{
time: "202511月12日",
survey: "337",
title: "外国制造的半导体器件及其下游产品和组件",
content:
"美国国际贸易委员会(ITC)投票决定对特定半导体器件及其下游计算产品和组件(Certain Semiconductor Devices, Computing Products Containing the Same,and Components Thereof)启动337调查。",
status: "部分终裁",
areaList: ["集成电路"]
}
]);
const handleCheckedYearsChange = (value) => {
const checkedCount = value.length;
checkAllYears.value = checkedCount === surveyYearList.value.length;
isIndeterminateYears.value = checkedCount > 0 && checkedCount < surveyYearList.value.length;
};
const totalDiscussNum = ref(0);
const surveyAreaList = ref([]);
const checkedAreaList = ref([]);
const curSurveyInfoList = ref([]);
const checkAllAreas = ref(false);
const isIndeterminateAreas = ref(false);
const searchText = ref("");
const handleCheckAllAreasChange = (val) => {
checkedAreaList.value = val ? surveyAreaList.value.map(a => a.id) : [];
isIndeterminateAreas.value = false;
};
const handleSearch = () => {
curSurveyInfoList.value = surveyInfoList.value.filter(item => {
return item.title.indexOf(searchText.value) > -1;
});
const handleCheckedAreasChange = (value) => {
const checkedCount = value.length;
checkAllAreas.value = checkedCount === surveyAreaList.value.length;
isIndeterminateAreas.value = checkedCount > 0 && checkedCount < surveyAreaList.value.length;
};
watch(
() => surveyYearList.value,
val => {
const selectedArr = val
.filter(item => {
return item.selected;
})
.map(item => item.name);
let arr = [];
const totalDiscussNum = ref(0);
const pageSize = ref(10);
const currentPage = ref(1);
const isInitializing = ref(true);
const listLoading = ref(false);
selectedArr.forEach(item => {
let arr1 = surveyInfoList.value.filter(vall => {
return vall.releaseDate.indexOf(item) > -1;
});
arr = [...arr, ...arr1];
});
const surveyInfoList = ref([]);
curSurveyInfoList.value = arr;
},
{
deep: true
const handleFetchSurveyList = async () => {
listLoading.value = true;
try {
const params = {
currentPage: currentPage.value - 1,
pageSize: pageSize.value,
sortCode: "301",
publishYear: checkAllYears.value ? "" : checkedSurveyYears.value.toString(),
Area: checkAllAreas.value ? "" : checkedAreaList.value.toString(),
// keywords: searchText.value,
sortField: "date",
sortOrder: isSort.value ? "asc" : "desc"
};
const res = await getSurveyList(params);
if (res.code === 200 && res.data) {
surveyInfoList.value = res.data.content.map(item => {
return {
time: item.SEARCHDATEZH || item.SEARCHDATE,
title: item.SEARCHNAME,
content: item.SEARCHNAME, // 或者其他字段,API文档没给出具体描述字段,暂用名称
status: item.CASESTATUS,
areaList: item.searchArea || [],
countryList: item.countryImage || [],
tag: item.SORTCODE || "301",
id: item.SEARCHID
};
});
totalDiscussNum.value = res.data.totalElements || 0;
}
} catch (error) {
console.error("获取调查列表失败", error);
} finally {
listLoading.value = false;
}
);
};
watch(
() => surveyAreaList.value,
val => {
const selectedArr = val
.filter(item => {
return item.selected;
})
.map(item => item.name);
let arr = [];
const handleCurrentChange = (val) => {
currentPage.value = val;
handleFetchSurveyList();
};
selectedArr.forEach(item => {
let arr1 = surveyInfoList.value.filter(vall => {
return vall.area.indexOf(item) > -1 || item.indexOf(vall.area) > -1;
});
arr = [...arr, ...arr1];
});
const handleSearch = () => {
currentPage.value = 1;
handleFetchSurveyList();
};
curSurveyInfoList.value = arr;
watch(
[checkedSurveyYears, checkedAreaList, isSort],
() => {
if (isInitializing.value) return;
currentPage.value = 1;
handleFetchSurveyList();
},
{
deep: true
......@@ -277,18 +282,65 @@ watch(
);
const handleToSingleCase = item => {
console.log(item)
// router.push('/marketSingleCaseLayout/overview')
const curRoute = router.resolve({
path: "/marketSingleCaseLayout/overview",
query: {
id: route.query.id
id: route.query.id + '',
searchId: item.id + ''
}
});
window.open(curRoute.href, "_blank");
};
onMounted(() => {
curSurveyInfoList.value = surveyInfoList.value;
const handleGetSearchAllArea = async () => {
try {
const res = await getSearchAllArea({
sortCode: '301'
});
if(res.code === 200 && res.data) {
surveyAreaList.value = res.data.map(item => {
return {
name: item.AREANAME,
id: item.AREACODE
};
});
// 默认选中全部
checkAllAreas.value = true;
handleCheckAllAreasChange(true);
}
} catch (error) {
}
}
const handleGetSearchAllYear = async () => {
try {
const res = await getSearchAllYear({
sortCode: '301'
});
if(res.code === 200 && res.data) {
// 排序并格式化
const sortedYears = res.data.sort((a, b) => b - a);
surveyYearList.value = sortedYears.map(item => {
return {
name: item + '年',
id: item
};
});
// 默认选中全部
checkAllYears.value = true;
handleCheckAllYearsChange(true);
}
} catch (error) {
}
}
onMounted(async () => {
await Promise.all([handleGetSearchAllArea(), handleGetSearchAllYear()]);
isInitializing.value = false;
handleFetchSurveyList();
});
</script>
......@@ -365,7 +417,9 @@ onMounted(() => {
justify-content: space-between;
.left {
width: 360px;
height: 560px;
min-height: 560px;
height: fit-content;
padding-bottom: 20px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: rgba(255, 255, 255, 1);
......@@ -391,13 +445,9 @@ onMounted(() => {
line-height: 24px;
}
}
.left-box1-main {
// margin-top: 10px;
}
}
.left-box2 {
margin-top: 17px;
// height: 260px;
.left-box2-header {
display: flex;
.icon {
......@@ -417,10 +467,6 @@ onMounted(() => {
line-height: 24px;
}
}
.left-box2-main {
// margin-top: 10px;
}
}
}
.right {
......@@ -463,6 +509,7 @@ onMounted(() => {
padding-top: 6px;
height: 660px;
border-bottom: 1px solid rgba(230, 231, 232, 1);
overflow: auto;
.item {
height: 154px;
display: flex;
......@@ -537,6 +584,9 @@ onMounted(() => {
.title {
margin-left: 9px;
width: 880px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 26px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
......@@ -644,9 +694,26 @@ onMounted(() => {
.checkbox-group{
padding-top: 10px;
margin-left: 16px;
.expand-btn {
width: 140px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
color: var(--color-main-active);
font-size: 14px;
cursor: pointer;
user-select: none;
&:hover {
opacity: 0.8;
}
.el-icon {
margin-left: 4px;
}
}
}
.filter-checkbox {
width: 140px;
// background: orange;
margin-bottom: 8px;
}
</style>
\ No newline at end of file
<template>
<div class="case-wrapper">
<div class="wrapper-header">
<div class="header-filters">
<div class="search-box">
<el-input
v-model="searchText"
style="width: 240px; height: 32px"
placeholder="搜索调查案件"
@keyup.enter="handleSearch"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
<div class="dropdown-filters">
<el-select v-model="filterStage" placeholder="全部阶段" class="filter-select" clearable>
<el-option label="全部阶段" value="" />
<el-option label="调查中" value="1" />
<el-option label="调查结束" value="0" />
</el-select>
<el-select v-model="filterParty" placeholder="全部原告/被告" class="filter-select" clearable>
<el-option label="全部原告/被告" value="" />
</el-select>
<el-select v-model="filterReason" placeholder="全部原因" class="filter-select" clearable>
<el-option label="全部原因" value="" />
</el-select>
</div>
</div>
<div class="select-box">
<div class="paixu-btn" @click="handleSwithSort">
<div class="text">{{ "发布时间" }}</div>
<div class="icon2">
<img v-if="isSort" src="@/assets/icons/shengxu2.png" alt="" />
<img v-else src="@/assets/icons/jiangxu2.png" alt="" />
</div>
</div>
</div>
</div>
<div class="wrapper-main">
<div class="left">
<!-- 科技领域 -->
<div class="left-box">
<div class="left-header">
<div class="icon"></div>
<div class="title">{{ "科技领域" }}</div>
</div>
<div class="left-main">
<div class="checkbox-group">
<el-checkbox
v-model="checkAllAreas"
:indeterminate="isIndeterminateAreas"
@change="handleCheckAllAreasChange"
class="filter-checkbox"
>
全部领域
</el-checkbox>
<div class="area-grid">
<el-checkbox
v-for="area in surveyAreaList"
:key="area.id"
v-model="checkedAreaList"
:label="area.id"
class="filter-checkbox"
@change="handleCheckedAreasChange"
>
{{ area.name }}
</el-checkbox>
</div>
</div>
</div>
</div>
<!-- 发布时间 -->
<div class="left-box">
<div class="left-header">
<div class="icon"></div>
<div class="title">{{ "发布时间" }}</div>
</div>
<div class="left-main">
<div class="checkbox-group">
<el-checkbox
v-model="checkAllYears"
:indeterminate="isIndeterminateYears"
@change="handleCheckAllYearsChange"
class="filter-checkbox"
>
全部时间
</el-checkbox>
<div class="year-grid">
<el-checkbox
v-for="year in displayedYearList"
:key="year.id"
v-model="checkedSurveyYears"
:label="year.id"
class="filter-checkbox"
@change="handleCheckedYearsChange"
>
{{ year.name }}
</el-checkbox>
</div>
<div v-if="surveyYearList.length > 6" class="expand-btn" @click="isYearExpanded = !isYearExpanded">
{{ isYearExpanded ? "收起" : "更早" }}
<el-icon>
<ArrowUp v-if="isYearExpanded" />
<ArrowDown v-else />
</el-icon>
</div>
</div>
</div>
</div>
<!-- 受调查国家/地区 -->
<div class="left-box">
<div class="left-header">
<div class="icon"></div>
<div class="title">{{ "受调查国家/地区" }}</div>
</div>
<div class="left-main">
<div class="checkbox-group">
<el-checkbox
v-model="checkAllCountries"
:indeterminate="isIndeterminateCountries"
@change="handleCheckAllCountriesChange"
class="filter-checkbox"
>
全部
</el-checkbox>
<div class="country-grid">
<el-checkbox
v-for="country in surveyCountryList"
:key="country.id"
v-model="checkedCountryList"
:label="country.id"
class="filter-checkbox"
@change="handleCheckedCountriesChange"
>
{{ country.name }}
</el-checkbox>
</div>
</div>
</div>
</div>
</div>
<div class="right">
<div class="right-header">
<div class="icon">
<img src="./assets/images/right-header-icon.png" alt="" />
</div>
<div class="title">{{ "337调查历程" }}</div>
</div>
<div class="right-main" v-loading="listLoading">
<div
class="timeline-item"
v-for="(item, index) in surveyInfoList"
:key="index"
@click="handleToSingleCase(item)"
>
<div class="timeline-date">
<div class="date-text">{{ item.timeZH || item.time }}</div>
</div>
<div class="timeline-line-box">
<div class="timeline-icon">
<img v-if="item.sortImageUrl" :src="item.sortImageUrl" alt="" />
<div v-else class="default-dot"></div>
</div>
<div class="timeline-line" v-if="index !== surveyInfoList.length - 1"></div>
</div>
<div class="timeline-content-card">
<div class="card-header">
<div class="tag-box">
<div class="tag-337">{{ item.tag }}</div>
<div class="title">{{ item.title }}</div>
</div>
<div class="status" :class="{ 'status-active': true }">
<span class="dot"></span> {{ item.status }}
</div>
</div>
<div class="card-body">
{{ item.content }}
</div>
<div class="card-footer">
<div class="footer-left-tags">
<div class="area-tag" v-for="(area, idx) in item.areaList" :key="idx">
{{ area }}
</div>
</div>
<div class="footer-right-flags">
<div class="flag-icon" v-for="(flag, fidx) in item.countryList" :key="fidx">
<img :src="flag" alt="" />
</div>
</div>
</div>
</div>
</div>
</div>
<div class="right-footer">
<div class="footer-left">
{{ `共${totalDiscussNum}项调查` }}
</div>
<div class="footer-right">
<el-pagination
@current-change="handleCurrentChange"
:pageSize="pageSize"
:current-page="currentPage"
background
layout="prev, pager, next"
:total="totalDiscussNum"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, watch, computed } from "vue";
import router from "@/router";
import { useRoute } from "vue-router";
import { Search, ArrowDown, ArrowUp } from "@element-plus/icons-vue";
import { getSearchAllArea, getSearchAllYear, getSurveyList, getSearchAllCountry } from "@/api/marketAccessRestrictions";
const route = useRoute();
// 顶部过滤项
const searchText = ref("");
const filterStage = ref("");
const filterParty = ref("");
const filterReason = ref("");
const isSort = ref(false); // false 降序
const handleSwithSort = () => {
isSort.value = !isSort.value;
};
// 科技领域过滤
const surveyAreaList = ref([]);
const checkedAreaList = ref([]);
const checkAllAreas = ref(true);
const isIndeterminateAreas = ref(false);
const handleCheckAllAreasChange = val => {
checkedAreaList.value = val ? surveyAreaList.value.map(a => a.id) : [];
isIndeterminateAreas.value = false;
};
const handleCheckedAreasChange = value => {
const checkedCount = value.length;
checkAllAreas.value = checkedCount === surveyAreaList.value.length;
isIndeterminateAreas.value = checkedCount > 0 && checkedCount < surveyAreaList.value.length;
};
// 发布时间过滤
const surveyYearList = ref([]);
const checkedSurveyYears = ref([]);
const checkAllYears = ref(true);
const isIndeterminateYears = ref(false);
const isYearExpanded = ref(false);
const displayedYearList = computed(() => {
if (isYearExpanded.value) return surveyYearList.value;
return surveyYearList.value.slice(0, 6);
});
const handleCheckAllYearsChange = val => {
checkedSurveyYears.value = val ? surveyYearList.value.map(y => y.id) : [];
isIndeterminateYears.value = false;
};
const handleCheckedYearsChange = value => {
const checkedCount = value.length;
checkAllYears.value = checkedCount === surveyYearList.value.length;
isIndeterminateYears.value = checkedCount > 0 && checkedCount < surveyYearList.value.length;
};
// 受调查国家/地区过滤
const surveyCountryList = ref([]);
const checkedCountryList = ref([]);
const checkAllCountries = ref(true);
const isIndeterminateCountries = ref(false);
const handleCheckAllCountriesChange = val => {
checkedCountryList.value = val ? surveyCountryList.value.map(c => c.id) : [];
isIndeterminateCountries.value = false;
};
const handleCheckedCountriesChange = value => {
const checkedCount = value.length;
checkAllCountries.value = checkedCount === surveyCountryList.value.length;
isIndeterminateCountries.value = checkedCount > 0 && checkedCount < surveyCountryList.value.length;
};
// 数据列表
const surveyInfoList = ref([]);
const totalDiscussNum = ref(0);
const pageSize = ref(10);
const currentPage = ref(1);
const isInitializing = ref(true);
const listLoading = ref(false);
const handleFetchSurveyList = async () => {
listLoading.value = true;
try {
const params = {
currentPage: currentPage.value - 1,
pageSize: pageSize.value,
sortCode: "337",
publishYear: checkAllYears.value ? "" : checkedSurveyYears.value.toString(),
Area: checkAllAreas.value ? "" : checkedAreaList.value.toString(),
searchCountry: checkAllCountries.value ? "" : checkedCountryList.value.toString(),
caseStatus: filterStage.value,
keywords: searchText.value,
sortField: "date",
sortOrder: isSort.value ? "asc" : "desc"
};
const res = await getSurveyList(params);
if (res.code === 200 && res.data) {
surveyInfoList.value = res.data.content.map(item => {
return {
time: item.SEARCHDATE,
timeZH: item.SEARCHDATEZH,
title: item.SEARCHNAME,
content: item.SEARCHDESC || item.SEARCHNAME,
status: item.CASESTATUS || "调查中",
areaList: item.searchArea || [],
countryList: item.countryImage || [],
tag: item.SORTCODE || "337",
id: item.SEARCHID,
sortImageUrl: item.SORTIMAGEURL
};
});
totalDiscussNum.value = res.data.totalElements || 0;
}
} catch (error) {
console.error("获取调查列表失败", error);
} finally {
listLoading.value = false;
}
};
const handleCurrentChange = val => {
currentPage.value = val;
handleFetchSurveyList();
};
const handleSearch = () => {
currentPage.value = 1;
handleFetchSurveyList();
};
const handleToSingleCase = item => {
const curRoute = router.resolve({
path: "/marketSingleCaseLayout/overview",
query: {
id: "337",
searchId: item.id + ""
}
});
window.open(curRoute.href, "_blank");
};
// 监听过滤条件
watch(
[checkedSurveyYears, checkedAreaList, checkedCountryList, isSort, filterStage, filterParty, filterReason],
() => {
if (isInitializing.value) return;
currentPage.value = 1;
handleFetchSurveyList();
}
);
const handleGetSearchAllArea = async () => {
try {
const res = await getSearchAllArea({ sortCode: "337" });
if (res.code === 200 && res.data) {
surveyAreaList.value = res.data.map(item => ({
name: item.AREANAME,
id: item.AREACODE
}));
handleCheckAllAreasChange(true);
}
} catch (error) {}
};
const handleGetSearchAllYear = async () => {
try {
const res = await getSearchAllYear({ sortCode: "337" });
if (res.code === 200 && res.data) {
const sortedYears = res.data.sort((a, b) => b - a);
surveyYearList.value = sortedYears.map(item => ({
name: item + "年",
id: item
}));
handleCheckAllYearsChange(true);
}
} catch (error) {}
};
const handleGetSearchAllCountry = async () => {
try {
const res = await getSearchAllCountry();
if (res.code === 200 && res.data) {
surveyCountryList.value = res.data.map(item => ({
name: item.COUNTRYNAME,
id: item.COUNTRYID
}));
handleCheckAllCountriesChange(true);
}
} catch (error) {}
};
onMounted(async () => {
await Promise.all([handleGetSearchAllArea(), handleGetSearchAllYear(), handleGetSearchAllCountry()]);
isInitializing.value = false;
handleFetchSurveyList();
});
</script>
<style lang="scss" scoped>
.case-wrapper {
width: 1600px;
margin: 0 auto;
padding-top: 20px;
.wrapper-header {
height: 32px;
margin-bottom: 16px;
display: flex;
justify-content: space-between;
align-items: center;
.header-filters {
display: flex;
gap: 16px;
align-items: center;
.dropdown-filters {
display: flex;
gap: 12px;
.filter-select {
width: 140px;
:deep(.el-input__wrapper) {
background-color: #fff;
}
}
}
}
.select-box {
.paixu-btn {
display: flex;
align-items: center;
gap: 8px;
height: 32px;
border: 1px solid #e6e7e8;
border-radius: 4px;
background: #fff;
cursor: pointer;
padding: 0 12px;
.text {
font-size: 14px;
color: #5f656c;
}
.icon2 {
width: 10px;
img {
width: 100%;
}
}
}
}
}
.wrapper-main {
display: flex;
justify-content: space-between;
.left {
width: 300px;
min-height: 560px;
height: fit-content;
padding-bottom: 20px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: #fff;
.left-box {
margin-top: 17px;
.left-header {
display: flex;
align-items: center;
.icon {
width: 8px;
height: 16px;
background: var(--color-main-active);
border-radius: 0 2px 2px 0;
}
.title {
margin-left: 17px;
color: var(--color-main-active);
font-size: 16px;
font-weight: 700;
}
}
}
.checkbox-group {
padding: 10px 0 0 25px;
display: flex;
flex-direction: column;
.area-grid,
.year-grid,
.country-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 4px;
}
.filter-checkbox {
margin-bottom: 8px;
height: 32px;
:deep(.el-checkbox__label) {
font-size: 16px;
color: #5f656c;
}
}
.expand-btn {
color: var(--color-main-active);
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
margin-top: 4px;
.el-icon {
margin-left: 4px;
}
}
}
}
.right {
width: 1284px;
min-height: 700px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: #fff;
padding-bottom: 20px;
.right-header {
height: 48px;
display: flex;
align-items: center;
border-bottom: 1px solid #eaeeef;
padding: 0 20px;
.icon {
width: 22px;
img {
width: 100%;
}
}
.title {
margin-left: 12px;
font-size: 20px;
font-weight: 700;
color: var(--color-main-active);
}
}
.right-main {
padding: 30px 40px;
.timeline-item {
display: flex;
cursor: pointer;
&:hover {
.timeline-content-card {
background-color: #f5f7fa;
}
}
.timeline-date {
width: 100px;
text-align: right;
padding-right: 20px;
.date-text {
font-size: 16px;
font-weight: 700;
color: var(--color-main-active);
line-height: 24px;
}
}
.timeline-line-box {
width: 40px;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.timeline-icon {
width: 32px;
height: 32px;
z-index: 2;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
}
.default-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: orange;
}
}
.timeline-line {
width: 2px;
flex: 1;
background: #eaeeef;
margin: 4px 0;
}
}
.timeline-content-card {
flex: 1;
border-radius: 8px;
padding: 16px 20px;
margin-left: 10px;
margin-bottom: 24px;
transition: all 0.3s;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
.tag-box {
display: flex;
align-items: center;
gap: 12px;
.tag-337 {
padding: 2px 10px;
border: 1px solid #91caff;
background: #e6f4ff;
color: #055fc2;
border-radius: 4px;
font-weight: 700;
font-size: 16px;
}
.title {
font-size: 18px;
font-weight: 700;
color: #3b414b;
}
}
.status {
font-size: 16px;
color: #84888e;
.dot {
margin-right: 4px;
}
}
.status-active {
color: var(--color-main-active);
}
}
.card-body {
font-size: 16px;
color: #5f656c;
line-height: 1.6;
margin-bottom: 16px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
.footer-left-tags {
display: flex;
gap: 8px;
.area-tag {
padding: 2px 12px;
background: #e6f7ff;
border: 1px solid #91d5ff;
color: #1890ff;
border-radius: 4px;
font-size: 14px;
}
}
.footer-right-flags {
display: flex;
gap: 4px;
.flag-icon {
width: 24px;
height: 24px;
border-radius: 50%;
overflow: hidden;
border: 1px solid #eee;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
}
}
}
}
.right-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 40px;
margin-top: 20px;
.footer-left {
font-size: 14px;
color: #5f656c;
}
}
}
}
}
</style>
<template>
<div class="case-wrap">
<Discussion1 v-if="showDiscussion ==='337'"></Discussion1>
<Discussion2 v-else-if="showDiscussion ==='232'"></Discussion2>
<Discussion3 v-else></Discussion3>
<Discussion2 v-if="showDiscussion ==='232'"></Discussion2>
<Discussion3 v-if="showDiscussion ==='301'"></Discussion3>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from "vue";
// import Discussion1 from './337/index.vue'
// import Discussion2 from './232/index.vue'
import Discussion1 from './337/index.vue'
import Discussion2 from './232/index.vue'
import Discussion3 from './301/index.vue'
import { useRoute } from "vue-router";
......
<template>
<div class="wrap">
<div class="top">
<div class="box1 box">
<div class="box1 box" v-loading="box1Loading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">对华301调查年度数量趋势</div>
<div class="warning-text">
{{ "3项调查仍在进行中" }}
{{ `${inProgressCount}项调查仍在进行中` }}
</div>
<div class="header-right">
<div class="icon">
......@@ -33,7 +33,7 @@
</div>
</div>
</div>
<div class="box2 box">
<div class="box2 box" v-loading="box2Loading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">301调查国家分布</div>
......@@ -64,10 +64,10 @@
</div>
</div>
<div class="bottom">
<div class="box3 box">
<div class="box3 box" v-loading="box3Loading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">301调查方向及结果分布</div>
<div class="title">301调查 direction 及结果分布</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
......@@ -93,7 +93,7 @@
</div>
</div>
</div>
<div class="box4 box">
<div class="box4 box" v-loading="box4Loading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">301调查领域分布</div>
......@@ -138,389 +138,177 @@ import getLineChart from "./utils/lineChart";
import getBarChart from "./utils/barChart";
import getSankeyChart from "./utils/sankey";
import getPieChart from "./utils/piechart";
import Img1 from "./assets/images/1.png";
import Img2 from "./assets/images/2.png";
import Img3 from "./assets/images/3.png";
import Img4 from "./assets/images/4.png";
import Img5 from "./assets/images/5.png";
import Img6 from "./assets/images/6.png";
import Img7 from "./assets/images/7.png";
import getbarChart from "@/views/bill/utils/barchart";
import { getSearchCountry, getStatArea, getStatNum, getSearchDirection } from "@/api/marketAccessRestrictions";
const selectYear = ref("近五年");
const selectYear = ref("2025");
const yearList = ref([
{
label: "近五年",
value: "近五年"
label: "2025",
value: "2025"
},
{
label: "近三年",
value: "近三年"
label: "2024",
value: "2024"
},
{
label: "近一年",
value: "近一年"
label: "2023",
value: "2023"
}
]);
const inProgressCount = ref(0);
const box1Loading = ref(false);
const box2Loading = ref(false);
const box3Loading = ref(false);
const box4Loading = ref(false);
const box1ChartData = ref({
title: ["2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
data: [3, 1, 2, 3, 4, 5, 2, 4, 3, 5, 6, 4]
title: [],
data: []
});
const box2ChartData = ref({
title: [
{
img: Img1,
name: "中国"
},
{
img: Img2,
name: "俄罗斯"
},
{
img: Img3,
name: "巴西"
},
{
img: Img4,
name: "巴基斯坦"
},
{
img: Img5,
name: "印度"
},
{
img: Img6,
name: "日本"
},
{
img: Img7,
name: "德国"
const handleGetStatNum = async () => {
box1Loading.value = true;
try {
const res = await getStatNum({
byYorM: "12",
sortCode: "301"
});
if (res.code === 200 && res.data) {
const sortedData = res.data.sort((a, b) => parseInt(a.searchYorM) - parseInt(b.searchYorM));
box1ChartData.value.title = sortedData.map(item => item.searchYorM);
box1ChartData.value.data = sortedData.map(item => item.searchCount);
inProgressCount.value = res.data.reduce((acc, cur) => acc + (cur.inSearchCount || 0), 0);
const box1Chart = getLineChart(box1ChartData.value.title, box1ChartData.value.data);
setChart(box1Chart, "box1Chart");
}
],
data: [23, 24, 14, 13, 12, 7, 5]
} catch (error) {
console.error("获取调查年度数量趋势失败", error);
} finally {
box1Loading.value = false;
}
};
const box2ChartData = ref({
title: [],
data: []
});
const box3ChartData = ref({
nodes: [
{
name: "市场准入"
},
{
name: "知识产权"
},
{
name: "政府采购"
},
{
name: "技术转让"
},
{
name: "其他"
},
{
name: "产业补贴"
},
{
name: "WTO争端"
},
{
name: "协商解决"
},
{
name: "终止调查"
},
{
name: "仍在调查"
},
{
name: "实施解决"
}
],
links: [
{
source: "市场准入",
target: "WTO争端",
value: 100
},
{
source: "市场准入",
target: "协商解决",
value: 15
},
{
source: "市场准入",
target: "终止调查",
value: 12
},
{
source: "市场准入",
target: "仍在调查",
value: 11
},
{
source: "知识产权",
target: "WTO争端",
value: 68
},
{
source: "知识产权",
target: "协商解决",
value: 32
},
{
source: "知识产权",
target: "终止调查",
value: 26
},
{
source: "知识产权",
target: "仍在调查",
value: 24
},
{
source: "知识产权",
target: "实施解决",
value: 22
},
nodes: [],
links: []
});
{
source: "政府采购",
target: "WTO争端",
value: 61
},
{
source: "政府采购",
target: "协商解决",
value: 42
},
{
source: "政府采购",
target: "终止调查",
value: 21
},
{
source: "政府采购",
target: "仍在调查",
value: 28
},
{
source: "政府采购",
target: "实施解决",
value: 20
},
const handleGetSearchDirection = async () => {
box3Loading.value = true;
try {
const res = await getSearchDirection({
sortCode: "301"
});
if (res.code === 200 && res.data) {
const nodes = [];
const links = [];
const nodeNames = new Set();
{
source: "技术转让",
target: "WTO争端",
value: 55
},
{
source: "技术转让",
target: "协商解决",
value: 41
},
{
source: "技术转让",
target: "终止调查",
value: 12
},
{
source: "技术转让",
target: "仍在调查",
value: 35
},
{
source: "技术转让",
target: "实施解决",
value: 32
},
res.data.forEach(item => {
if (item.SEARCHDIRECTION) nodeNames.add(item.SEARCHDIRECTION);
if (item.SEARCHRESULT) nodeNames.add(item.SEARCHRESULT);
{
source: "其他",
target: "终止调查",
value: 11
},
{
source: "产业补贴",
target: "协商解决",
value: 22
},
{
source: "产业补贴",
target: "仍在调查",
value: 11
},
{
source: "产业补贴",
target: "实施解决",
value: 15
}
]
});
if (item.SEARCHDIRECTION && item.SEARCHRESULT) {
links.push({
source: item.SEARCHDIRECTION,
target: item.SEARCHRESULT,
value: item.SEARCHCOUNT || 0
});
}
});
const box4ChartData = ref([
{
name: "集成电路",
value: 50
},
{
name: "人工智能",
value: 46
},
{
name: "通信网络",
value: 40
},
{
name: "能源",
value: 32
},
{
name: "先进制造",
value: 31
},
{
name: "生物科技",
value: 31
},
{
name: "航空航天",
value: 30
},
{
name: "新材料",
value: 24
nodeNames.forEach(name => {
nodes.push({ name });
});
box3ChartData.value = { nodes, links };
const box3Chart = getSankeyChart(box3ChartData.value.nodes, box3ChartData.value.links);
setChart(box3Chart, "box3Chart");
}
} catch (error) {
console.error("获取调查方向及结果分布失败", error);
} finally {
box3Loading.value = false;
}
]);
};
const box4ChartData = ref([]);
// 切换年份
const handleSelectYear = val => {
selectYear.value = val;
if (val === "近一年") {
box4ChartData.value = [
{
name: "集成电路",
value: 42
},
{
name: "人工智能",
value: 38
},
{
name: "通信网络",
value: 32
},
{
name: "能源",
value: 28
},
{
name: "先进制造",
value: 29
},
{
name: "生物科技",
value: 22
},
{
name: "航空航天",
value: 26
},
{
name: "新材料",
value: 18
}
];
} else if (val === "近三年") {
box4ChartData.value = [
{
name: "集成电路",
value: 38
},
{
name: "人工智能",
value: 26
},
{
name: "通信网络",
value: 44
},
{
name: "能源",
value: 48
},
{
name: "先进制造",
value: 25
},
{
name: "生物科技",
value: 16
},
{
name: "航空航天",
value: 16
},
{
name: "新材料",
value: 19
}
];
} else {
box4ChartData.value = [
{
name: "集成电路",
value: 50
},
{
name: "人工智能",
value: 46
},
{
name: "通信网络",
value: 40
},
{
name: "能源",
value: 32
},
{
name: "先进制造",
value: 31
},
{
name: "生物科技",
value: 31
},
{
name: "航空航天",
value: 30
},
{
name: "新材料",
value: 24
}
];
}
const box4Chart = getPieChart(box4ChartData.value);
setChart(box4Chart, "box4Chart");
handleGetStatArea();
};
onMounted(() => {
const box1Chart = getLineChart(box1ChartData.value.title, box1ChartData.value.data);
setChart(box1Chart, "box1Chart");
const box2Chart = getBarChart(box2ChartData.value.title, box2ChartData.value.data);
setChart(box2Chart, "box2Chart");
const handleGetStatArea = async () => {
box4Loading.value = true;
const yearMap = {
2023: 2023,
2024: 2024,
2025: 2025
};
const params = {
years: yearMap[selectYear.value] || 2025,
sortCode: 301
};
try {
const res = await getStatArea(params);
if (res.code === 200 && res.data) {
box4ChartData.value = res.data
.filter(item => item.SORTNAME === "301调查")
.map(item => ({
name: item.AREANAME,
value: item.AREACOUNT
}));
const box4Chart = getPieChart(box4ChartData.value);
setChart(box4Chart, "box4Chart");
}
} catch (error) {
console.error("获取制裁领域分布失败", error);
} finally {
box4Loading.value = false;
}
};
const box3Chart = getSankeyChart(box3ChartData.value.nodes, box3ChartData.value.links);
setChart(box3Chart, "box3Chart");
const handleGetSearchCountry = async () => {
box2Loading.value = true;
try {
const res = await getSearchCountry({
sortCode: 301,
year: new Date().getFullYear() - 1
});
if (res.code === 200 && res.data) {
box2ChartData.value = {
title: res.data.map(item => ({
img: item.COUNTRYIMAGE ? (item.COUNTRYIMAGE.startsWith("http") ? item.COUNTRYIMAGE : `http://${item.COUNTRYIMAGE}`) : "",
name: item.COUNTRY
})),
data: res.data.map(item => item.NUM)
};
const box2Chart = getBarChart(box2ChartData.value.title, box2ChartData.value.data);
setChart(box2Chart, "box2Chart");
}
} catch (error) {
console.error("获取受调查国家分布失败", error);
} finally {
box2Loading.value = false;
}
};
const box4Chart = getPieChart(box4ChartData.value);
setChart(box4Chart, "box4Chart");
onMounted(() => {
handleGetStatNum();
handleGetSearchCountry();
handleGetStatArea();
handleGetSearchDirection();
});
</script>
......@@ -671,4 +459,4 @@ onMounted(() => {
}
}
}
</style>
\ No newline at end of file
</style>
......@@ -22,7 +22,7 @@
</div>
</div>
<div class="center">
<div class="box1">
<div class="box1" v-loading="box1Loading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">美国对华337调查年度趋势</div>
......@@ -39,8 +39,8 @@
</div>
</div>
<div class="box1-main" id="chart1"></div>
</div>
<div class="box2">
</div>
<div class="box2" v-loading="box2Loading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">调查案件领域分布</div>
......@@ -60,7 +60,7 @@
</div>
</div>
<div class="footer">
<div class="box3">
<div class="box3" v-loading="box3Loading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">中国公司受调查情况</div>
......@@ -106,7 +106,7 @@
<div class="box3-main1-right" id="chartMap"></div>
</div>
</div>
<div class="box4">
<div class="box4" v-loading="box4Loading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">调查结果分布</div>
......@@ -133,6 +133,8 @@ import * as echarts from "echarts";
import ChinaJson from "../../assets/json/China.json";
import { getStatcnOrgCount, getSearchResult, getStatArea, getStatNum } from "@/api/marketAccessRestrictions";
import getMultiLineChart from "./utils/multiLineChart";
import getBarChart from "./utils/barChart";
import getPieChart from "./utils/piechart";
......@@ -147,95 +149,198 @@ const setChart = (option, chartId) => {
return chart;
};
const box1Loading = ref(false);
const box2Loading = ref(false);
const box3Loading = ref(false);
const box4Loading = ref(false);
const btnActiveName = ref("注册地分布");
const provinceCoords = {
"北京": [116.46, 39.92],
"上海": [121.48, 31.22],
"天津": [117.2, 39.13],
"重庆": [106.54, 29.59],
"河北": [114.48, 38.03],
"山西": [112.53, 37.87],
"内蒙古": [111.65, 40.82],
"辽宁": [123.38, 41.8],
"吉林": [125.35, 43.88],
"黑龙江": [126.63, 45.75],
"江苏": [118.78, 32.04],
"浙江": [120.19, 30.26],
"安徽": [117.27, 31.86],
"福建": [119.3, 26.08],
"江西": [115.89, 28.68],
"山东": [117.0, 36.65],
"河南": [113.65, 34.76],
"湖北": [114.31, 30.52],
"湖南": [113.0, 28.21],
"广东": [113.23, 23.16],
"广西": [108.33, 22.84],
"海南": [110.35, 20.02],
"四川": [104.06, 30.67],
"贵州": [106.71, 26.57],
"云南": [102.73, 25.04],
"西藏": [91.11, 29.97],
"陕西": [108.95, 34.27],
"甘肃": [103.73, 36.03],
"青海": [101.74, 36.56],
"宁夏": [106.27, 38.47],
"新疆": [87.68, 43.77],
"香港": [114.17, 22.28],
"澳门": [113.54, 22.19],
"台湾": [121.5, 25.05]
};
const handleGetStatcnOrgCount = async (type) => {
box3Loading.value = true;
try {
const res = await getStatcnOrgCount({
type,
sortCode: "337"
});
if (res.code === 200 && res.data) {
if (type === "01") {
chart3Data.value = {
name: res.data.map(item => item.ORGNAME),
value: res.data.map(item => item.ORGCOUNT)
};
nextTick(() => {
let chart3 = getBarChart(chart3Data.value.name, chart3Data.value.value);
setChart(chart3, "chart3");
});
} else if (type === "02") {
mapData.value = res.data.map(item => {
const name = item.ORGPROVINCE//.replace(/省|市|自治区|特别行政区/g, "");
return {
name: item.ORGPROVINCE,
value: item.PROVINCECOUNT,
coord: provinceCoords[name] || [0, 0]
};
});
nextTick(() => {
let chartMap = getMapChart(mapData.value);
setChart(chartMap, "chartMap");
});
}
}
} catch (error) {
console.error("获取中国公司受调查情况失败", error);
} finally {
box3Loading.value = false;
}
};
const handleClickBox3Btn = name => {
btnActiveName.value = name;
if (name === "调查次数") {
nextTick(() => {
let chart3 = getBarChart(chart3Data.value.name, chart3Data.value.value);
setChart(chart3, "chart3");
});
handleGetStatcnOrgCount("01");
} else {
nextTick(() => {
let chartMap = getMapChart(mapData.value);
setChart(chartMap, "chartMap");
});
handleGetStatcnOrgCount("02");
}
};
const chart1Data = ref({
title: ["2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
data: [
{
name: "提出法案",
value: [145, 52, 84, 99, 71, 96, 128, 144, 140, 168, 188, 172]
}
]
title: [],
data: []
});
const chart2Data = ref([
{ name: "半导体", value: 50 },
{ name: "电子设备", value: 46 },
{ name: "显示技术", value: 40 },
{ name: "新能源", value: 32 },
{ name: "通信设备", value: 31 },
{ name: "汽车", value: 31 },
{ name: "轻工业制造", value: 30 },
{ name: "其他", value: 24 }
]);
const handleGetStatNum = async () => {
box1Loading.value = true;
try {
const res = await getStatNum({
byYorM: "12",
sortCode: "337"
});
if (res.code === 200 && res.data) {
const sortedData = res.data.sort((a, b) => parseInt(a.searchYorM) - parseInt(b.searchYorM));
chart1Data.value.title = sortedData.map(item => item.searchYorM);
chart1Data.value.data = [
{
name: "调查数量",
value: sortedData.map(item => item.searchCount)
}
];
nextTick(() => {
let chart1 = getMultiLineChart(chart1Data.value.title, chart1Data.value.data[0].value);
setChart(chart1, "chart1");
});
}
} catch (error) {
console.error("获取年度趋势数据失败", error);
} finally {
box1Loading.value = false;
}
};
const chart2Data = ref([]);
const chart2ColorList = ref(["#69B1FF", "#FFC069", "#87E8DE", "#597EF7", "#D6E4FF", "#FF7875", "#B37FEB", "#FFA39E"]);
const handleGetStatArea = async () => {
box2Loading.value = true;
try {
const res = await getStatArea({
sortCode: "337"
});
if (res.code === 200 && res.data) {
chart2Data.value = res.data
.filter(item => item.SORTCODE === "337" || item.SORTNAME === "337调查")
.map(item => ({
name: item.AREANAME,
value: item.AREACOUNT
}));
nextTick(() => {
let chart2 = getPieChart(chart2Data.value, chart2ColorList.value);
setChart(chart2, "chart2");
});
}
} catch (error) {
console.error("获取调查案件领域分布失败", error);
} finally {
box2Loading.value = false;
}
};
const chart3Data = ref({
name: [
"华为技术有限公司",
"大疆创新科技有限公司",
"TCL科技集团股份有限公司",
"中兴通讯股份有限公司",
"联想集团",
"比亚迪集团",
"宁德时代新能源科技股份有限公司",
"晶科智能科技有限公司"
],
value: [42, 35, 28, 19, 15, 12, 11, 8]
name: [],
value: []
});
const mapData = ref([
{ name: "北京", value: 10, coord: [116.46, 39.92] },
{ name: "上海", value: 9, coord: [121.48, 31.22] },
{ name: "广东", value: 15, coord: [113.23, 23.16] },
{ name: "江苏", value: 30, coord: [118.78, 32.04] },
{ name: "浙江", value: 20, coord: [120.19, 30.26] },
{ name: "四川", value: 4, coord: [104.06, 30.67] },
{ name: "陕西", value: 1, coord: [108.95, 34.27] },
{ name: "辽宁", value: 3, coord: [123.38, 41.8] }
// { name: "湖北", value: 2, coord: [114.31, 30.52] },
// { name: "山东", value: 12, coord: [117.0, 36.65] },
]);
const chart4Data = ref([
{ name: "和解/同意令", value: 50 },
{ name: "裁定不侵权", value: 46 },
{ name: "裁定侵权", value: 40 },
{ name: "申诉方撤诉", value: 31 },
{ name: "其他", value: 24 }
]);
const chart4ColorList = ref(["#69B1FF", "#FFC069", "#87E8DE", "#D6E4FF", "#FFA39E"]);
const mapData = ref([]);
onMounted(() => {
let chart1 = getMultiLineChart(chart1Data.value.title, chart1Data.value.data[0].value);
setChart(chart1, "chart1");
const chart4Data = ref([]);
const chart4ColorList = ref(["#69B1FF", "#FFC069", "#87E8DE", "#597EF7", "#D6E4FF", "#FF7875", "#B37FEB", "#FFA39E"]);
let chart2 = getPieChart(chart2Data.value, chart2ColorList.value);
setChart(chart2, "chart2");
const handleGetSearchResult = async () => {
box4Loading.value = true;
try {
const res = await getSearchResult({
sortCode: "337"
});
if (res.code === 200 && res.data) {
chart4Data.value = res.data.map(item => ({
name: item.RESULTNAME,
value: item.RESULTNUM
}));
nextTick(() => {
let chart4 = getPieChart(chart4Data.value, chart4ColorList.value);
setChart(chart4, "chart4");
});
}
} catch (error) {
console.error("获取调查结果分布失败", error);
} finally {
box4Loading.value = false;
}
};
let chart3 = getBarChart(chart3Data.value.name, chart3Data.value.value);
setChart(chart3, "chart3");
onMounted(() => {
handleGetStatNum();
let chartMap = getMapChart(mapData.value);
setChart(chartMap, "chartMap");
handleGetStatArea();
let chart4 = getPieChart(chart4Data.value, chart4ColorList.value);
setChart(chart4, "chart4");
handleGetStatcnOrgCount(btnActiveName.value === "调查次数" ? "01" : "02");
handleGetSearchResult();
});
</script>
......
......@@ -15,7 +15,7 @@
</div>
<div class="box1-main">
<div class="box1-main-item" v-for="(bg, index) in bgList" :key="index">
<div class="id">{{ index + 1 + "." }}</div>
<!-- <div class="id">{{ index + 1 + "." }}</div> -->
<div class="title">{{ bg.title }}</div>
</div>
</div>
......@@ -61,7 +61,7 @@
</div>
<div class="box2-main-item-content">
<div class="content-item" v-for="(val, idx) in demand.data" :key="idx">
<div class="id">{{ idx + 1 + "." }}</div>
<!-- <div class="id">{{ idx + 1 + "." }}</div> -->
<div class="title">{{ val.title }}</div>
</div>
</div>
......@@ -112,7 +112,7 @@
</div>
<div class="box3-main-item-content">
<div class="content-item" v-for="(val, idx) in supply.data" :key="idx">
<div class="id">{{ idx + 1 + "." }}</div>
<!-- <div class="id">{{ idx + 1 + "." }}</div> -->
<div class="title">{{ val.title }}</div>
</div>
</div>
......@@ -163,7 +163,7 @@
</div>
<div class="box4-main-item-content">
<div class="content-item" v-for="(val, idx) in item.data" :key="idx">
<div class="id">{{ idx + 1 + "." }}</div>
<!-- <div class="id">{{ idx + 1 + "." }}</div> -->
<div class="title">{{ val.title }}</div>
</div>
</div>
......@@ -204,7 +204,7 @@
<div class="box5-main-item-header">{{ suggestion.title }}</div>
<div class="box5-main-item-content">
<div class="content-item" v-for="(val, idx) in suggestion.data" :key="idx">
<div class="content-item-title">{{ idx + 1 + "." + val.title }}</div>
<div class="content-item-title">{{val.title }}</div>
<div class="content-item-info">
<div class="desc">{{ val.info.desc }}</div>
<div class="info-bill" v-if="val.info.bill">
......@@ -240,179 +240,98 @@
<script setup>
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import { getReportAnalyze } from "@/api/marketAccessRestrictions/index.js";
const bgList = ref([
{
title: "钒被美国内政部列入《2018 年关键矿产清单》(依据第 13817 号行政命令),需满足 “对经济与国家安全至关重要、供应链易受中断、无供应将致重大后果” 三大标准,是国防(军事装备钛合金)、关键基础设施(高强度低合金钢材)及化工、储能领域的必需物资(P1-411 至 P1-423、P1-441 至 P1-451)。"
},
{
title: "全球钒生产分三类:原生生产(采矿)、联合生产(炼钢副产品,占全球 2019 年总产量 71%)、二次生产(废料回收);美国以二次生产为主(占全球二次产量 1/3,约全球总产 4%),原生生产仅能源燃料公司(Energy Fuels)1 家,2019 年复产但产量不足全球 1%(P1-206 至 P1-208、P1-224 至 P1-227)。"
},
{
title: "美国钒产业高度集中,2019 年仅 3 家企业(AMG Vanadium、U.S. Vanadium、Energy Fuels)从事生产,AMG Vanadium 是国内最大钒铁生产商,U.S. Vanadium 专注高纯度五氧化二钒,Gladieux Metals Recycling 因设施改造处于闲置状态(P1-230 至 P1-232、P1-246 至 P1-248、P1-252 至 P1-254)。"
},
{
title: "美国对钒进口实施贸易管控:自 1995 年起对中国、南非、俄罗斯等国钒铁征收反倾销税(中国全国统一税率 66.71%、南非 116%),2018 年后对华加征 301 条款关税(10%-25%);同时通过《确保关键矿产安全可靠供应的联邦战略》,与加拿大、澳大利亚签订关键矿产合作协议(P1-369 至 P1-371、P1-401 至 P1-406、P1-693 至 P1-712)。"
},
{
title: "美国钒生产依赖进口原料:二次生产所需废催化剂、灰渣等 80% 来自海外(2019 年含钒废料进口 90% 来自加拿大),仅 Energy Fuels 使用 100% 本土原料,2016 年以来其产量占国内需求的 1.4%(P1-493 至 P1-495、P1-503、P1-682 至 P1-683)。"
}
]);
const route = useRoute();
const searchId = route.query.searchId || "232";
const demandList = ref([
{
title: "(一)美国市场需求",
data: [
{
title: "2016-2019 年美国钒表观消费量年均 8590 吨(含钒量),国内产量仅 340 万千克 / 年,进口依赖度超 80%(年均进口 780 万千克)(P1-481、P1-661)。"
},
{
title: "钢铁产业是最大需求领域,占总需求 90%(2019 年约 7731 吨含钒量),用于高强度低合金(HSLA)钢(抗震钢筋、桥梁)、工具钢(机械加工),其中国防用钢占钢铁需求 3%,对应钒需求 230 吨 / 年(P1-279 至 P1-283、P1-438、P1-443)。"
},
{
title: "钛产业占总需求 5%(约 430 吨 / 年),90% 用于航空航天领域(商用 55%、军用 10%),是 Ti-6Al-4V 合金核心成分(成本占比 12%-14%),2020 年受新冠疫情冲击,航空航天需求下滑致钛产业钒需求骤减(P1-291 至 P1-293、P1-437、P1-738 至 P1-739)。"
},
{
title: "非冶金领域占总需求 5%(约 430 吨 / 年),包括化工催化剂(硫酸生产,硫酸为工业基础原料)、储能(钒液流电池,当前占比不足 1%,2027 年增速预计 13%-42%)(P1-299 至 P1-306、P1-450、P1-746)。"
},
{
title: "2020 年新冠疫情对美国钒需求冲击显著:钢铁产量同比降 18%,航空航天领域钛需求锐减,钒液流电池等新兴需求未形成规模支撑(P1-732 至 P1-733、P1-738 至 P1-739)。"
}
]
},
{
title: "(二)国际市场需求",
data: [
{
title: "全球钒需求以钢铁产业为主(占比 90%-93%),中国是最大消费国(占全球需求 50%-60%),其钢铁产业(尤其是钢筋)钒用量直接影响全球市场(P1-279、P1-321、P1-716 至 P1-717)。"
},
{
title: "国际航空航天领域钛合金用钒需求稳定,占全球钒总需求 3%-5%,集中于波音、空客供应链,2020 年受疫情冲击需求下滑,长期随航空业复苏有望恢复年均 2%-3% 增长(P1-291 至 P1-293、P1-738 至 P1-739)。"
},
{
title: "钒液流电池是全球钒需求新增长点:当前占比不足 1%,Roskill 预测 2027 年增速 13%/ 年,Bushveld Minerals 预测 42%/ 年,若成本降至 100 美元 /kWh,2030 年或占全球消费 10%-15%(P1-305 至 P1-306、P1-746)。"
},
{
title: "化工领域(硫酸生产催化剂)是全球钒需求的稳定补充,硫酸消费量被视为 “国家工业发展指标”,支撑钒在非冶金领域的基础需求(P1-299 至 P1-302)。"
}
]
}
]);
const bgList = ref([]);
const demandList = ref([]);
const supplyList = ref([]);
const box4List = ref([]);
const suggestionList = ref([]);
const supplyList = ref([
{
title: "(一)美国供应情况",
data: [
{
title: "原生供应:仅 Energy Fuels 具备产能,2019 年生产 460 吨含钒量(五氧化二钒)后因价格低迷停产;矿山资源含 660 万千克可采钒量,需价格回升至 10 美元 / 磅以上才具备复产动力(P1-208 至 P1-210、P1-213、P1-588 至 P1-589)。"
},
{
title: "二次供应:AMG Vanadium(俄亥俄州)2016-2019 年钒铁年均产量 [数据缺失],2021 年新工厂投产后产能翻倍至 5500 吨 / 年(含钒量);U.S. Vanadium(阿肯色州)是国内唯一高纯度五氧化二钒生产商;Gladieux(得克萨斯州)改造完成后将恢复普通级五氧化二钒生产,预计 2021 年后投产(P1-230 至 P1-232、P1-238 至 P1-240、P1-246 至 P1-248、P1-252 至 P1-254)。"
},
{
title: "潜在产能:内华达州 Gibellini 项目(2023 年投产,年产能 2400 吨含钒量)、Carlin 项目(16 年周期总产能 4600 万千克含钒量),需钒价达 7.76 美元 / 磅盈亏平衡线才具备经济性(P1-214 至 P1-217、P1-218 至 P1-219、P1-751 至 P1-752)。"
},
{
title: "原料依赖:二次生产所需废催化剂、五氧化二钒等原料 80% 进口,2019 年含钒废料进口 90% 来自加拿大,仅 Energy Fuels 使用 100% 本土原料,供应链自主性不足(P1-493 至 P1-495、P1-503、P1-682 至 P1-683)。"
}
]
},
{
title: "(二)国际供应情况",
data: [
{
title: "全球产能集中:2019 年全球钒产量 10.3 万吨(矿山 7.3 万吨 + 二次 3 万吨),生产集中于 4 国 —— 中国(55%,约 5.6 万吨)、俄罗斯(18%,约 1.8 万吨)、南非(8%,约 0.8 万吨)、巴西(7%,约 0.7 万吨),中国通过钢铁联合生产(占全球联合产量 71%)主导供应(P1-208、P1-224、P1-321、P1-724)。"
},
{
title: "贸易流向特征:中国虽产量高,但国内钢铁需求大,出口仅占全球 15%;欧盟(占全球出口 27%)、南非(13%)、巴西(13%)是主要出口方;美国进口来源前三位为加拿大(钒铁 43%)、奥地利(25%)、南非(钒氧化物 10%),2019 年进口额 4.78 亿美元(P1-346、P1-350、P1-354、P1-481)。"
},
{
title: "成本竞争格局:中国、俄罗斯联合生产现金成本低于 8 美元 / 磅,哈萨克斯坦新项目宣称 “全球最低成本”;美国原生生产需 10-13 美元 / 磅才盈利,二次生产依赖回收费用(AMG Vanadium 回收收益覆盖 80% 运营成本),整体竞争力弱于新兴市场(P1-588 至 P1-589、P1-724、P1-729)。"
},
{
title: "新增产能规划:加拿大(BlackRock Metals 项目 2021 年投产,年产能 4000 吨)、澳大利亚(Windimurra mine 等 5 个项目处于后期勘探,预计 2021 年后投产)、哈萨克斯坦(Balasausqandiq 项目计划 2023 年产能 1.3 万吨),若全部投产可使全球矿山产量接近翻倍(P1-322 至 P1-323、P1-324 至 P1-331、P1-338 至 P1-339、P1-724)。"
}
]
}
]);
const getData = async () => {
// 行业背景
getReportAnalyze({ searchId, type: "01" }).then((res) => {
if (res.data) {
bgList.value = res.data.map((item) => ({
title: item.CONTENT
}));
}
});
const box4List = ref([
{
title: "(一)美国未来产业格局",
data: [
{
title: "短期(2021-2023 年):AMG Vanadium 新工厂、Gladieux 改造项目投产后,钒铁自给率预计从 [数据缺失] 提升至 50% 以上,普通级五氧化二钒进口依赖度从 100% 降至 60% 以下;Gibellini 项目 2023 年投产将新增原生产能,进口依赖度进一步降低(P1-238 至 P1-240、P1-252 至 P1-254、P1-218 至 P1-219、P1-664 至 P1-669)。"
},
{
title: "中长期(2024-2030 年):若 Gibellini、Carlin 项目满产,美国原生钒产能可达 4800 吨 / 年(占国内需求 50% 以上),叠加二次产能扩张,进口依赖度或降至 50% 以下,供应链自主性显著提升(P1-214 至 P1-217、P1-218 至 P1-219、P1-774)。"
},
{
title: "政策驱动方向:美国或推进 “国防储备扩大”(建议储备 4800 吨含钒量五氧化二钒)、“回收促进”(回收税收优惠、EPA 监管优化),同时深化与加拿大、澳大利亚的关键矿产合作,分散供应风险(P1-780 至 P1-793、P1-794 至 P1-800、P1-693 至 P1-712)。"
},
{
title: "产业风险挑战:全球新增产能(哈萨克斯坦、加拿大、澳大利亚)或压低价格至 5-7 美元 / 磅,抑制美国原生产能投产;二次生产仍依赖进口原料,供应链风险未完全消除;若钒价长期低迷,部分规划产能可能推迟或取消(P1-724、P1-751 至 P1-755、P1-776)。"
}
]
},
{
title: "(二)国际产业格局",
data: [
{
title: "需求趋势:全球钢铁需求增速趋缓(预计年增 1.4% 至 2035 年),但高强度钢、工具钢占比提升支撑钒用量;储能需求成核心增长点,若钒液流电池成本达标,2030 年或占全球消费 10%-15%;国防、化工需求保持稳定(P1-734、P1-746、P1-299 至 P1-302)。"
},
{
title: "供应格局:中国仍将主导全球钒供应,但加拿大、澳大利亚、哈萨克斯坦新增产能将分流市场份额,全球供应集中度或从 2019 年的 88%(中、俄、南非、巴西)降至 2030 年的 75% 左右(P1-321、P1-322 至 P1-331、P1-338 至 P1-339、P1-724)。"
},
{
title: "价格影响因素:短期受全球新增产能释放影响,钒价或维持 5-8 美元 / 磅低位;中长期若储能需求爆发,或推动价格回升至 10-12 美元 / 磅,触发美国、澳大利亚等国原生产能投产(P1-588 至 P1-589、P1-724、P1-746)。"
},
{
title: "贸易与政策:全球关键矿产竞争加剧,美国、欧盟等或进一步强化对钒等关键矿产的供应链管控,推动与盟友的合作协议;中国政策(如钢铁标准、环保管控)仍将是影响全球钒市场的重要变量(P1-693 至 P1-712、P1-716 至 P1-717)。"
}
]
}
]);
// 市场需求
getReportAnalyze({ searchId, type: "02" }).then((res) => {
if (res.data) {
demandList.value = groupData(res.data);
}
});
const suggestionList = ref([
{
title: "(一)与盟友及合作伙伴合作",
data: [
{
title: "美国盟友的脆弱性",
info: {
desc: "美国盟友和合作伙伴的国家安全对美国国家安全至关重要,而两者都因盟友在钕铁硼磁体价值链对中国的依赖而受到削弱。"
}
},
{
title: "关键矿物多边合作",
info: {
desc: "共同的脆弱性凸显了当前关于关键矿物的多边及双边合作的价值,这些合作有助于推动美国及其盟友摆脱对潜在对手和国家安全威胁来源的依赖。"
}
}
]
},
{
title: "(二)强化国内供应",
data: [
{
title: "设立稀土税收抵免",
info: {
desc: "商务部建议政府支持《稀土磁体制造生产税收抵免法案》(H.R. 5033)或类似立法的通过。除稀土磁体税收抵免外,商务部还建议政府支持为可替代钕铁硼磁体的非稀土磁体,以及碳酸稀土、氧化物、金属、合金等上游稀土产品设立税收抵免。",
bill: "2024《稀土磁体制造生产税收抵免法案》"
}
},
{
title: "为国内制造提供额外支持",
info: {
desc: "商务部建议政府支持通过立法行动(如 “供应链韧性计划”)分配额外资金,以发展有韧性的供应链。此类计划的额外资金应支持对钕铁硼磁体价值链所有环节国内制造的投资。",
bill: "2024《两党基础设施法》"
}
// 国内外供应情况
getReportAnalyze({ searchId, type: "03" }).then((res) => {
if (res.data) {
supplyList.value = groupData(res.data);
}
});
// 未来产业格局
getReportAnalyze({ searchId, type: "04" }).then((res) => {
if (res.data) {
box4List.value = groupData(res.data);
}
});
// 调查建议
getReportAnalyze({ searchId, type: "05" }).then((res) => {
if (res.data) {
suggestionList.value = groupSuggestionData(res.data);
}
});
};
const groupData = (data) => {
const groups = {};
data.forEach((item) => {
const title = item.TITLE || "其他";
if (!groups[title]) {
groups[title] = [];
}
groups[title].push({
title: item.CONTENT
});
});
return Object.keys(groups).map((title) => ({
title,
data: groups[title]
}));
};
const groupSuggestionData = (data) => {
const groups = {};
data.forEach((item) => {
const groupTitle = "调查建议详情";
if (!groups[groupTitle]) {
groups[groupTitle] = [];
}
groups[groupTitle].push({
title: item.TITLE,
info: {
desc: item.CONTENT,
bill: item.BILLNAME
}
]
}
]);
});
});
return Object.keys(groups).map((title) => ({
title,
data: groups[title]
}));
};
onMounted(() => {});
onMounted(() => {
getData();
});
</script>
<style lang="scss" scoped>
......@@ -533,9 +452,9 @@ onMounted(() => {});
}
.box1 {
width: 1600px;
height: 588px;
// height: 588px;
.box1-main {
height: 425px;
// height: 425px;
margin-top: 2px;
overflow: hidden;
overflow-y: auto;
......@@ -564,9 +483,9 @@ onMounted(() => {});
}
.box2 {
width: 1600px;
height: 921px;
// height: 921px;
.box2-main {
height: 760px;
// height: 760px;
width: 1552px;
margin: 2px auto 0;
.box2-main-item {
......@@ -632,7 +551,7 @@ onMounted(() => {});
.id {
width: 18px;
}
.text {
.title {
flex: 1;
}
}
......@@ -642,9 +561,9 @@ onMounted(() => {});
}
.box3 {
width: 1600px;
height: 957px;
// height: 957px;
.box3-main {
height: 795px;
// height: 795px;
width: 1552px;
margin: 2px auto 0;
.box3-main-item {
......@@ -710,7 +629,7 @@ onMounted(() => {});
.id {
width: 18px;
}
.text {
.title {
flex: 1;
}
}
......@@ -720,9 +639,9 @@ onMounted(() => {});
}
.box4 {
width: 1600px;
height: 954px;
// height: 954px;
.box4-main {
height: 795px;
// height: 795px;
width: 1552px;
margin: 2px auto 0;
.box4-main-item {
......@@ -788,7 +707,7 @@ onMounted(() => {});
.id {
width: 18px;
}
.text {
.title {
flex: 1;
}
}
......@@ -798,9 +717,9 @@ onMounted(() => {});
}
.box5 {
width: 1600px;
height: 813px;
// height: 813px;
.box5-main {
height: 650px;
// height: 650px;
width: 1552px;
margin: 0 auto;
margin-top: 2px;
......@@ -889,7 +808,7 @@ onMounted(() => {});
.info-bill-right {
width: 20px;
height: 20px;
cursor: pointer;
cursor: pointer;
img {
width: 100%;
height: 100%;
......@@ -904,4 +823,4 @@ onMounted(() => {});
}
}
}
</style>
\ No newline at end of file
</style>
<template>
<div class="wrapper">
<div class="top">
<div class="box1 box">
<div class="deep-dig-container">
<div class="left-section">
<!-- Module 1: 被诉企业列表 -->
<div class="box company-list-box">
<div class="box-header">
<div class="header-left"></div>
<div class="title">同领域调查案件数量</div>
<div class="header-btn-box">
<div class="btn btnActive">{{ "半导体" }}</div>
</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
</div>
<div class="header-left-bar"></div>
<div class="title">被诉企业列表</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box1-main" id="chart1"></div>
<div class="box1-footer">
<div class="icon1">
<img src="./assets/images/icon1.png" alt="" />
</div>
<div class="text">
{{
"近年来美对华半导体337调查呈现案件频发、聚焦专利壁垒、力图阻断产业链升级的特点。"
}}
</div>
<div class="icon2">
<img src="./assets/images/icon2.png" alt="" />
</div>
</div>
</div>
<div class="box2 box">
<div class="box-header">
<div class="header-left"></div>
<div class="title">同领域调查涉及公司</div>
<div class="header-btn-box">
<div class="btn btnActive">{{ "半导体" }}</div>
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box2-main" id="chart2"></div>
<div class="box2-footer">
<div class="icon1">
<img src="./assets/images/icon1.png" alt="" />
</div>
<div class="text">
{{
"主要集中在存储、芯片设计及通信领域,如长江存储、长鑫存储、华为、中兴等"
}}
<div class="box-content">
<div class="filter-row">
<el-select v-model="selectedArea" placeholder="全部领域" class="area-select" clearable @change="fetchEnterpriseList">
<el-option label="全部领域" value="" />
<el-option v-for="item in areaOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-input
v-model="searchText"
placeholder="搜索实体"
class="search-input"
:suffix-icon="Search"
clearable
@input="handleSearchInput"
/>
</div>
<div class="icon2">
<img src="./assets/images/icon2.png" alt="" />
<div class="enterprise-list" v-loading="listLoading">
<div class="list-label">企业名称</div>
<div
v-for="(item, index) in enterpriseList"
:key="item.ORGID || index"
class="enterprise-item"
:class="{ active: activeIndex === index }"
@click="handleEnterpriseClick(item, index)"
>
<div class="icon">
<img src="@/assets/icons/default-icon1.png" alt="" />
</div>
<div class="name">{{ item.ORGNAME }}</div>
</div>
<el-empty v-if="!enterpriseList.length && !listLoading" description="暂无数据" />
</div>
</div>
</div>
</div>
<div class="footer">
<div class="box3 box">
<div class="right-section">
<!-- Module 2: 进出口数据 -->
<div class="box chart-box">
<div class="box-header">
<div class="header-left"></div>
<div class="title">关联企业</div>
<div class="header-btn-box">
<div
class="btn"
:class="{ btnActive: item.isActive }"
v-for="(item, index) in box3BtnList"
:key="index"
@click="handleClickBox3Btn(index)"
>
{{ item.name }}
<div class="header-left-bar"></div>
<div class="title">进出口数据</div>
<div class="header-right">
<div class="btn-group">
<div class="btn active">出口收入</div>
</div>
<div class="icon-group">
<div class="icon"><img src="@/assets/icons/box-header-icon1.png" alt="" /></div>
<div class="icon"><img src="@/assets/icons/box-header-icon2.png" alt="" /></div>
<div class="icon"><img src="@/assets/icons/box-header-icon3.png" alt="" /></div>
</div>
</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box3-main">
<div id="chart3"></div>
<div id="chart4"></div>
</div>
<div class="box3-footer">
<div class="icon1">
<img src="./assets/images/icon1.png" alt="" />
<div class="box-content chart-content">
<div class="chart-wrapper" v-loading="exportLoading">
<div ref="exportChartRef" class="echarts-container"></div>
</div>
<div class="text">
{{
"近年来美对华半导体337调查呈现案件频发、聚焦专利壁垒、力图阻断产业链升级的特点。"
}}
</div>
<div class="icon2">
<img src="./assets/images/icon2.png" alt="" />
<div class="footer-msg">
<div class="msg-left">
<img src="@/assets/icons/box-footer-left-icon.png" alt="" />
</div>
<div class="msg-center">列入实体清单后企业营收初期下降,后基本趋于稳定。</div>
<div class="msg-right">
<img src="@/assets/icons/box-footer-right-icon.png" alt="" />
</div>
</div>
</div>
</div>
<div class="box4 box">
<!-- Module 3: 企业经营情况 -->
<div class="box chart-box">
<div class="box-header">
<div class="header-left"></div>
<div class="title">和解可能性分析</div>
<div class="header-btn-box">
<div class="btn btnActive">{{ "半导体" }}</div>
</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box4-main">
<div id="chart5"></div>
<div class="box4-info-box">
<div class="item" v-for="(item, index) in infoList" :key="index">
<div class="item-left"></div>
<div class="item-right">
<div class="title">{{ item.title }}</div>
<div class="content">{{ item.content }}</div>
</div>
<div class="header-left-bar"></div>
<div class="title">企业经营情况</div>
<div class="header-right">
<div class="btn-group">
<div
class="btn"
:class="{ active: businessActiveBtn === '营收' }"
@click="handleBusinessToggle('营收')"
>营收</div>
<div
class="btn"
:class="{ active: businessActiveBtn === '净利润' }"
@click="handleBusinessToggle('净利润')"
>净利润</div>
</div>
<div class="icon-group">
<div class="icon"><img src="@/assets/icons/box-header-icon1.png" alt="" /></div>
<div class="icon"><img src="@/assets/icons/box-header-icon2.png" alt="" /></div>
<div class="icon"><img src="@/assets/icons/box-header-icon3.png" alt="" /></div>
</div>
</div>
</div>
<div class="box4-footer">
<div class="icon1">
<img src="./assets/images/icon1.png" alt="" />
<div class="box-content chart-content">
<div class="chart-wrapper" v-loading="businessLoading">
<div ref="businessChartRef" class="echarts-container"></div>
</div>
<div class="text">
{{
"近年来美对华半导体337调查呈现案件频发、聚焦专利壁垒、力图阻断产业链升级的特点。"
}}
</div>
<div class="icon2">
<img src="./assets/images/icon2.png" alt="" />
<div class="footer-msg">
<div class="msg-left">
<img src="@/assets/icons/box-footer-left-icon.png" alt="" />
</div>
<div class="msg-center">列入实体清单后企业研发资金投入逐渐提高。</div>
<div class="msg-right">
<img src="@/assets/icons/box-footer-right-icon.png" alt="" />
</div>
</div>
</div>
</div>
......@@ -166,522 +125,562 @@
</template>
<script setup>
import { ref, onMounted } from "vue";
import * as echarts from "echarts";
import getBarChart from "./utils/barChart";
import getBarChart1 from "./utils/barChart1";
import getPieChart from "./utils/pieChart";
const box3BtnList = ref([
{
name: "全部",
isActive: true,
},
{
name: "一加",
isActive: false,
},
{
name: "联想",
isActive: false,
},
{
name: "TCL",
isActive: false,
},
{
name: "其他",
isActive: false,
},
]);
const handleClickBox3Btn = (index) => {
box3BtnList.value.forEach((item) => {
item.isActive = false;
});
box3BtnList.value[index].isActive = true;
import { ref, onMounted, nextTick } from 'vue';
import { Search } from '@element-plus/icons-vue';
import { useRoute } from 'vue-router';
import * as echarts from 'echarts';
import { getSuedOrg, getOrgImportAndExport, getRevenue, getNetProfit } from '@/api/marketAccessRestrictions';
const route = useRoute();
const selectedArea = ref('');
const searchText = ref('');
const activeIndex = ref(0);
const businessActiveBtn = ref('营收');
const listLoading = ref(false);
const exportLoading = ref(false);
const businessLoading = ref(false);
const areaOptions = [
{ label: '人工智能', value: '1' },
{ label: '生物科技', value: '2' },
{ label: '新一代通信技术', value: '3' },
{ label: '量子科技', value: '4' },
{ label: '新能源', value: '5' },
{ label: '集成电路', value: '6' },
{ label: '海洋', value: '7' },
{ label: '先进制造', value: '8' },
{ label: '新材料', value: '9' },
{ label: '航空航天', value: '10' },
{ label: '太空', value: '13' },
{ label: '深海', value: '11' },
{ label: '极地', value: '12' },
{ label: '核', value: '14' },
{ label: '政治', value: '20' },
{ label: '外交', value: '21' },
{ label: '经济', value: '22' },
{ label: '军事', value: '23' },
{ label: '科技', value: '24' },
{ label: '安全', value: '25' },
{ label: '其他', value: '99' },
];
const enterpriseList = ref([]);
const exportChartRef = ref(null);
const businessChartRef = ref(null);
let exportChart = null;
let businessChart = null;
const chartLabels = ref([]);
const chartData = ref([]);
const businessLabels = ref([]);
const businessData = ref([]);
// 获取企业列表
const fetchEnterpriseList = async () => {
listLoading.value = true;
try {
const params = {
searchId: route.query.searchId,
orgName: searchText.value
}
if (selectedArea.value) {
params.area = selectedArea.value;
}
const res = await getSuedOrg(params);
if (res.code === 200) {
enterpriseList.value = res.data || [];
// 默认选中第一个并加载其进出口数据
if (enterpriseList.value.length > 0) {
handleEnterpriseClick(enterpriseList.value[0], 0);
}
}
} catch (error) {
console.error('获取被诉企业列表失败:', error);
} finally {
listLoading.value = false;
}
};
// 绘制echarts图表
const setChart = (option, chartId) => {
let chartDom = document.getElementById(chartId);
chartDom.removeAttribute("_echarts_instance_");
let chart = echarts.init(chartDom);
chart.setOption(option);
return chart;
// 获取进出口数据
const fetchExportData = async (orgId) => {
exportLoading.value = true;
chartLabels.value = [];
chartData.value = [];
try {
const res = await getOrgImportAndExport({ id: orgId });
if (res.code === 200 && res.data && res.data.length > 0) {
// 对数据按年份排序
const sortedData = res.data.sort((a, b) => a.year - b.year);
const labels = [];
const values = [];
sortedData.forEach(item => {
['q1', 'q2', 'q3', 'q4'].forEach(q => {
if (item[q] !== undefined && item[q] !== null) {
labels.push(`${item.year} ${q.toUpperCase()}`);
values.push(item[q]);
}
});
});
// 更新图表
chartLabels.value = labels;
chartData.value = values;
}
updateExportChart();
} catch (error) {
console.error('获取进出口数据失败:', error);
updateExportChart();
} finally {
exportLoading.value = false;
}
};
const chart1Data = ref({
title: [
"2014",
"2015",
"2016",
"2017",
"2018",
"2019",
"2020",
"2021",
"2022",
"2023",
"2024",
"2025",
],
data: [32, 32, 41, 33, 29, 31, 16, 31, 37, 31, 27, 27],
});
// 获取经营情况数据
const fetchBusinessData = async (orgId) => {
businessLoading.value = true;
businessLabels.value = [];
businessData.value = [];
try {
const api = businessActiveBtn.value === '营收' ? getRevenue : getNetProfit;
const res = await api({ id: orgId });
if (res.code === 200 && res.data && res.data.length > 0) {
const sortedData = res.data.sort((a, b) => a.year - b.year);
businessLabels.value = sortedData.map(item => item.year + '年');
businessData.value = sortedData.map(item => item.count);
}
updateBusinessChart();
} catch (error) {
console.error(`获取企业${businessActiveBtn.value}数据失败:`, error);
updateBusinessChart();
} finally {
businessLoading.value = false;
}
};
const chart2Data = ref({
title: [
"华为技术有限公司",
"大疆创新科技有限公司",
"TCL科技集团股份有限公司",
"中兴通讯股份有限公司",
"联想集团",
"比亚迪集团",
"宁德时代新能源科技股份有限公司",
"长江存储科技有限公司",
],
data: [42, 35, 28, 19, 15, 12, 11, 8],
});
const updateExportChart = () => {
if (exportChart) {
const lastValue = chartData.value[chartData.value.length - 1];
const lastLabel = chartLabels.value[chartLabels.value.length - 1];
exportChart.setOption(getOption(chartData.value, lastValue, chartLabels.value, lastLabel));
}
};
const chart3Data = ref([
{
name: "子公司",
value: 7,
},
{
name: "关联公司",
value: 7,
},
{
name: "合作伙伴",
value: 6,
},
{
name: "其他",
value: 4,
},
]);
const colorList1 = ref(["#4096FF", "#FFA39E", "#D6E4FF", "#FFC069"]);
const colorList2 = ref(["#69B1FF", "#FFC069", "#87E8DE", "#D6E4FF", "#FFA39E"]);
const chart4Data = ref([
{
name: "中国香港",
value: 7,
},
{
name: "美国",
value: 7,
},
{
name: "芬兰",
value: 6,
},
{
name: "中国大陆",
value: 4,
},
]);
const chart5Data = ref([
{
name: "和解/同意令",
value: 50,
},
{
name: "裁定不侵权",
value: 46,
},
{
name: "裁定侵权",
value: 40,
},
{
name: "申诉方撤诉",
value: 31,
},
{
name: "其他",
value: 24,
},
]);
const infoList = ref([
{
title: "专利无效宣告",
content: "被告企业可能通过证明Pantech专利无效来赢得案件",
},
{
title: "交叉许可协议",
content: "双方达成专利交叉许可协议,结束争端",
},
{
title: "和解协议",
content: "被告企业支付专利使用费以换取市场准入",
},
{
title: "有限排除令",
content: "如果侵权成立,ITC可能发布有限排除令,禁止涉案产品进入美国市场",
},
]);
const updateBusinessChart = () => {
if (businessChart) {
const lastValue = businessData.value[businessData.value.length - 1];
const lastLabel = businessLabels.value[businessLabels.value.length - 1];
businessChart.setOption(getOption(businessData.value, lastValue, businessLabels.value, lastLabel));
}
};
onMounted(() => {
let chart1 = getBarChart(chart1Data.value.title, chart1Data.value.data);
setChart(chart1, "chart1");
let searchTimer = null;
const handleSearchInput = () => {
if (searchTimer) clearTimeout(searchTimer);
searchTimer = setTimeout(() => {
fetchEnterpriseList();
}, 500);
};
const handleEnterpriseClick = (item, index) => {
activeIndex.value = index;
if (item.ORGID) {
fetchExportData(item.ORGID);
fetchBusinessData(item.ORGID);
}
};
let chart2 = getBarChart1(chart2Data.value.title, chart2Data.value.data);
setChart(chart2, "chart2");
const getOption = (data, pointValue, labels, pointLabel) => {
const hasData = data && data.length > 0;
return {
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderWidth: 0,
boxShadow: '0 0 10px rgba(0,0,0,0.1)',
textStyle: {
color: '#3B414B',
},
},
grid: {
top: '40',
left: '10',
right: '40',
bottom: '10',
containLabel: true,
},
xAxis: {
type: 'category',
boundaryGap: false,
data: labels || [],
axisLine: {
lineStyle: {
color: '#E0E0E0',
},
},
axisLabel: {
color: '#84888E',
fontSize: 12,
},
axisTick: {
show: false,
},
},
yAxis: {
type: 'value',
splitNumber: 5,
splitLine: {
lineStyle: {
type: 'dashed',
color: '#F0F0F0',
},
},
axisLabel: {
color: '#84888E',
fontSize: 12,
formatter: (value) => {
if (value >= 10000) {
return (value / 10000).toFixed(1) + '万';
}
return value;
}
},
},
series: [
{
name: '数据',
type: 'line',
smooth: true,
symbol: 'none',
data: data || [],
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(23, 107, 240, 0.2)' },
{ offset: 1, color: 'rgba(23, 107, 240, 0.02)' },
]),
},
lineStyle: {
color: '#176BF0',
width: 2,
},
markLine: hasData ? {
symbol: 'none',
label: {
show: true,
position: 'end',
formatter: '337调查',
color: '#F56C6C',
backgroundColor: '#fff',
padding: [2, 4],
fontSize: 12,
},
lineStyle: {
color: '#F56C6C',
type: 'dashed',
width: 1,
},
data: labels && labels.includes('2024 Q2') ? [{ xAxis: '2024 Q2' }] : [],
} : null,
markPoint: hasData ? {
symbol: 'circle',
symbolSize: 8,
label: {
show: true,
position: 'top',
offset: [0, -5],
formatter: '{c}亿元',
color: '#176BF0',
fontWeight: 'bold',
fontSize: 14,
},
itemStyle: {
color: '#176BF0',
borderColor: '#fff',
borderWidth: 2,
shadowBlur: 5,
shadowColor: 'rgba(0,0,0,0.2)',
},
data: [{ value: pointValue, xAxis: pointLabel || (labels ? labels[labels.length - 1] : ''), yAxis: pointValue }],
} : null,
},
],
};
};
let chart3 = getPieChart(chart3Data.value, colorList1.value);
setChart(chart3, "chart3");
const initCharts = () => {
if (exportChartRef.value) {
exportChart = echarts.init(exportChartRef.value);
exportChart.setOption(getOption([], 0, []));
}
let chart4 = getPieChart(chart4Data.value, colorList1.value);
setChart(chart4, "chart4");
if (businessChartRef.value) {
businessChart = echarts.init(businessChartRef.value);
businessChart.setOption(getOption([], 0, []));
}
};
const handleBusinessToggle = (type) => {
businessActiveBtn.value = type;
const currentOrg = enterpriseList.value[activeIndex.value];
if (currentOrg && currentOrg.ORGID) {
fetchBusinessData(currentOrg.ORGID);
}
};
let chart5 = getPieChart(chart5Data.value, colorList2.value);
setChart(chart5, "chart5");
onMounted(() => {
fetchEnterpriseList();
nextTick(() => {
initCharts();
window.addEventListener('resize', () => {
exportChart && exportChart.resize();
businessChart && businessChart.resize();
});
});
});
</script>
<style lang="scss" scoped>
.wrapper {
width: 100%;
height: 100%;
// padding: 0 160px;
overflow-y: auto;
.box-header {
height: 56px;
display: flex;
position: relative;
.header-left {
margin-top: 20px;
width: 8px;
height: 16px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.title {
margin-left: 14px;
margin-top: 16px;
height: 24px;
line-height: 24px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 700;
}
.header-btn-box {
position: absolute;
top: 14px;
right: 116px;
display: flex;
.btn {
margin-left: 8px;
height: 28px;
padding: 0 8px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 4px;
background: rgba(255, 255, 255, 1);
text-align: center;
line-height: 28px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
cursor: pointer;
}
.btnActive {
border: 1px solid var(--color-main-active);
background: rgba(246, 250, 255, 1);
color: var(--color-main-active);
}
}
.header-right {
position: absolute;
top: 14px;
right: 12px;
display: flex;
justify-content: flex-end;
gap: 4px;
.icon{
width: 28px;
height: 28px;
img{
width: 100%;
height: 100%;
}
}
}
}
.deep-dig-container {
display: flex;
padding: 16px 160px;
gap: 16px;
background: #f7f8f9;
.box {
width: 792px;
height: 410px;
background: #ffffff;
border-radius: 4px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: rgba(255, 255, 255, 1);
}
.top {
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.1);
display: flex;
margin: 0 160px;
margin-top: 16px;
flex-direction: column;
.box1 {
.box1-main {
height: 280px;
}
.box1-footer {
height: 40px;
margin: 0 auto;
margin-top: 19px;
width: 759px;
height: 40px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 251, 255, 1);
display: flex;
.icon1 {
margin-left: 12px;
margin-top: 10px;
width: 19px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 12px;
line-height: 40px;
width: 666px;
height: 40px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
}
.icon2 {
margin-left: 13px;
margin-top: 8px;
width: 24px;
height: 24px;
background: rgba(231, 243, 255, 1);
border-radius: 12px;
img {
width: 12px;
height: 12px;
margin-top: 6px;
margin-left: 6px;
}
}
.box-header {
height: 56px;
display: flex;
align-items: center;
padding: 0 16px;
position: relative;
border-bottom: 1px solid #f0f0f0;
.header-left-bar {
width: 8px;
height: 16px;
background: var(--color-main-active, #176bf0);
border-radius: 0 4px 4px 0;
position: absolute;
left: 0;
}
}
.box2 {
margin-left: 16px;
.box2-main {
height: 280px;
.title {
margin-left: 12px;
font-size: 18px;
font-weight: bold;
color: var(--color-main-active, #176bf0);
}
.box2-footer {
height: 40px;
margin: 0 auto;
margin-top: 19px;
width: 759px;
height: 40px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 251, 255, 1);
.header-right {
margin-left: auto;
display: flex;
.icon1 {
margin-left: 12px;
margin-top: 10px;
width: 19px;
height: 20px;
img {
width: 100%;
height: 100%;
align-items: center;
gap: 12px;
.btn-group {
display: flex;
border: 1px solid #e0e0e0;
border-radius: 4px;
overflow: hidden;
.btn {
padding: 4px 12px;
font-size: 14px;
color: #5f656c;
cursor: pointer;
background: #fff;
&.active {
background: #e7f3ff;
color: #176bf0;
border: 1px solid #176bf0;
margin: -1px;
}
&:not(:last-child) {
border-right: 1px solid #e0e0e0;
}
}
}
.text {
margin-left: 12px;
line-height: 40px;
width: 666px;
height: 40px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
.icon-group {
display: flex;
gap: 4px;
.icon {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
img {
width: 20px;
height: 20px;
}
}
}
.icon2 {
margin-left: 13px;
margin-top: 8px;
width: 24px;
height: 24px;
background: rgba(231, 243, 255, 1);
border-radius: 12px;
.icon {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
img {
width: 12px;
height: 12px;
margin-top: 6px;
margin-left: 6px;
width: 20px;
height: 20px;
}
}
}
}
.box-content {
padding: 16px;
flex: 1;
}
}
.footer {
margin: 0 160px;
margin-top: 16px;
display: flex;
.box3 {
.box3-main {
height: 280px;
.left-section {
width: 320px;
flex-shrink: 0;
.company-list-box {
height: 100%;
.filter-row {
display: flex;
#chart3 {
width: 396px;
gap: 8px;
margin-bottom: 16px;
.area-select {
flex: 0 0 100px;
}
#chart4 {
width: 396px;
.search-input {
flex: 1;
}
}
.box3-footer {
height: 40px;
margin: 0 auto;
margin-top: 19px;
width: 759px;
height: 40px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 251, 255, 1);
display: flex;
.icon1 {
margin-left: 12px;
margin-top: 10px;
width: 19px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 12px;
line-height: 40px;
width: 666px;
height: 40px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
.enterprise-list {
.list-label {
font-size: 14px;
font-weight: bold;
color: #3b414b;
margin-bottom: 12px;
}
.icon2 {
margin-left: 13px;
margin-top: 8px;
width: 24px;
height: 24px;
background: rgba(231, 243, 255, 1);
border-radius: 12px;
img {
width: 12px;
height: 12px;
margin-top: 6px;
margin-left: 6px;
.enterprise-item {
display: flex;
align-items: center;
padding: 10px 12px;
border-bottom: 1px solid #f5f5f5;
cursor: pointer;
transition: background 0.2s;
&:hover {
background: #f9f9f9;
}
}
}
}
.box4 {
margin-left: 16px;
.box4-main {
height: 280px;
display: flex;
#chart5 {
width: 396px;
}
.box4-info-box {
width: 385px;
.item {
min-height: 66px;
display: flex;
.item-left {
width: 6px;
height: 6px;
border-radius: 3px;
background: rgba(255, 77, 79, 1);
margin-top: 16px;
&.active {
background: #f0f7ff;
border-left: 3px solid #176bf0;
padding-left: 9px;
.name {
color: #176bf0;
font-weight: bold;
}
.item-right {
margin-left: 10px;
.title {
margin-top: 5px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
}
.content {
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
}
.icon {
width: 16px;
height: 16px;
margin-right: 12px;
img {
width: 100%;
height: 100%;
}
}
.name {
font-size: 14px;
color: #3b414b;
}
}
}
.box4-footer {
height: 40px;
margin: 0 auto;
margin-top: 19px;
width: 759px;
height: 40px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 251, 255, 1);
}
}
.right-section {
flex: 1;
display: flex;
flex-direction: column;
gap: 16px;
.chart-box {
height: 400px;
.chart-content {
display: flex;
.icon1 {
margin-left: 12px;
margin-top: 10px;
width: 19px;
height: 20px;
img {
flex-direction: column;
padding-bottom: 12px;
.chart-wrapper {
flex: 1;
position: relative;
.echarts-container {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 12px;
line-height: 40px;
width: 666px;
height: 40px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
}
.icon2 {
margin-left: 13px;
margin-top: 8px;
width: 24px;
height: 24px;
background: rgba(231, 243, 255, 1);
border-radius: 12px;
img {
width: 12px;
height: 12px;
margin-top: 6px;
margin-left: 6px;
.footer-msg {
margin-top: 12px;
background: #f6fbff;
border: 1px solid #e7f3ff;
border-radius: 4px;
padding: 8px 12px;
display: flex;
align-items: center;
.msg-left {
width: 18px;
height: 18px;
margin-right: 8px;
img {
width: 100%;
}
}
.msg-center {
flex: 1;
font-size: 14px;
color: #176bf0;
}
.msg-right {
width: 16px;
height: 16px;
cursor: pointer;
img {
width: 100%;
}
}
}
}
}
}
}
</style>
\ No newline at end of file
</style>
<template>
<div class="wrapper">
<div class="top">
<div class="box1 box">
<div class="box-header">
<div class="header-left"></div>
<div class="title">同领域调查案件数量</div>
<div class="header-btn-box">
<div class="btn btnActive">{{ "半导体" }}</div>
</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box1-main" id="chart1"></div>
<div class="box1-footer">
<div class="icon1">
<img src="./assets/images/icon1.png" alt="" />
</div>
<div class="text">
{{
"近年来美对华半导体337调查呈现案件频发、聚焦专利壁垒、力图阻断产业链升级的特点。"
}}
</div>
<div class="icon2">
<img src="./assets/images/icon2.png" alt="" />
</div>
</div>
</div>
<div class="box2 box">
<div class="box-header">
<div class="header-left"></div>
<div class="title">同领域调查涉及公司</div>
<div class="header-btn-box">
<div class="btn btnActive">{{ "半导体" }}</div>
</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box2-main" id="chart2"></div>
<div class="box2-footer">
<div class="icon1">
<img src="./assets/images/icon1.png" alt="" />
</div>
<div class="text">
{{
"主要集中在存储、芯片设计及通信领域,如长江存储、长鑫存储、华为、中兴等"
}}
</div>
<div class="icon2">
<img src="./assets/images/icon2.png" alt="" />
</div>
</div>
</div>
</div>
<div class="footer">
<div class="box3 box">
<div class="box-header">
<div class="header-left"></div>
<div class="title">关联企业</div>
<div class="header-btn-box">
<div
class="btn"
:class="{ btnActive: item.isActive }"
v-for="(item, index) in box3BtnList"
:key="index"
@click="handleClickBox3Btn(index)"
>
{{ item.name }}
</div>
</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box3-main">
<div id="chart3"></div>
<div id="chart4"></div>
</div>
<div class="box3-footer">
<div class="icon1">
<img src="./assets/images/icon1.png" alt="" />
</div>
<div class="text">
{{
"近年来美对华半导体337调查呈现案件频发、聚焦专利壁垒、力图阻断产业链升级的特点。"
}}
</div>
<div class="icon2">
<img src="./assets/images/icon2.png" alt="" />
</div>
</div>
</div>
<div class="box4 box">
<div class="box-header">
<div class="header-left"></div>
<div class="title">和解可能性分析</div>
<div class="header-btn-box">
<div class="btn btnActive">{{ "半导体" }}</div>
</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box4-main">
<div id="chart5"></div>
<div class="box4-info-box">
<div class="item" v-for="(item, index) in infoList" :key="index">
<div class="item-left"></div>
<div class="item-right">
<div class="title">{{ item.title }}</div>
<div class="content">{{ item.content }}</div>
</div>
</div>
</div>
</div>
<div class="box4-footer">
<div class="icon1">
<img src="./assets/images/icon1.png" alt="" />
</div>
<div class="text">
{{
"近年来美对华半导体337调查呈现案件频发、聚焦专利壁垒、力图阻断产业链升级的特点。"
}}
</div>
<div class="icon2">
<img src="./assets/images/icon2.png" alt="" />
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import * as echarts from "echarts";
import getBarChart from "./utils/barChart";
import getBarChart1 from "./utils/barChart1";
import getPieChart from "./utils/pieChart";
const box3BtnList = ref([
{
name: "全部",
isActive: true,
},
{
name: "一加",
isActive: false,
},
{
name: "联想",
isActive: false,
},
{
name: "TCL",
isActive: false,
},
{
name: "其他",
isActive: false,
},
]);
const handleClickBox3Btn = (index) => {
box3BtnList.value.forEach((item) => {
item.isActive = false;
});
box3BtnList.value[index].isActive = true;
};
// 绘制echarts图表
const setChart = (option, chartId) => {
let chartDom = document.getElementById(chartId);
chartDom.removeAttribute("_echarts_instance_");
let chart = echarts.init(chartDom);
chart.setOption(option);
return chart;
};
const chart1Data = ref({
title: [
"2014",
"2015",
"2016",
"2017",
"2018",
"2019",
"2020",
"2021",
"2022",
"2023",
"2024",
"2025",
],
data: [32, 32, 41, 33, 29, 31, 16, 31, 37, 31, 27, 27],
});
const chart2Data = ref({
title: [
"华为技术有限公司",
"大疆创新科技有限公司",
"TCL科技集团股份有限公司",
"中兴通讯股份有限公司",
"联想集团",
"比亚迪集团",
"宁德时代新能源科技股份有限公司",
"长江存储科技有限公司",
],
data: [42, 35, 28, 19, 15, 12, 11, 8],
});
const chart3Data = ref([
{
name: "子公司",
value: 7,
},
{
name: "关联公司",
value: 7,
},
{
name: "合作伙伴",
value: 6,
},
{
name: "其他",
value: 4,
},
]);
const colorList1 = ref(["#4096FF", "#FFA39E", "#D6E4FF", "#FFC069"]);
const colorList2 = ref(["#69B1FF", "#FFC069", "#87E8DE", "#D6E4FF", "#FFA39E"]);
const chart4Data = ref([
{
name: "中国香港",
value: 7,
},
{
name: "美国",
value: 7,
},
{
name: "芬兰",
value: 6,
},
{
name: "中国大陆",
value: 4,
},
]);
const chart5Data = ref([
{
name: "和解/同意令",
value: 50,
},
{
name: "裁定不侵权",
value: 46,
},
{
name: "裁定侵权",
value: 40,
},
{
name: "申诉方撤诉",
value: 31,
},
{
name: "其他",
value: 24,
},
]);
const infoList = ref([
{
title: "专利无效宣告",
content: "被告企业可能通过证明Pantech专利无效来赢得案件",
},
{
title: "交叉许可协议",
content: "双方达成专利交叉许可协议,结束争端",
},
{
title: "和解协议",
content: "被告企业支付专利使用费以换取市场准入",
},
{
title: "有限排除令",
content: "如果侵权成立,ITC可能发布有限排除令,禁止涉案产品进入美国市场",
},
]);
onMounted(() => {
let chart1 = getBarChart(chart1Data.value.title, chart1Data.value.data);
setChart(chart1, "chart1");
let chart2 = getBarChart1(chart2Data.value.title, chart2Data.value.data);
setChart(chart2, "chart2");
let chart3 = getPieChart(chart3Data.value, colorList1.value);
setChart(chart3, "chart3");
let chart4 = getPieChart(chart4Data.value, colorList1.value);
setChart(chart4, "chart4");
let chart5 = getPieChart(chart5Data.value, colorList2.value);
setChart(chart5, "chart5");
});
</script>
<style lang="scss" scoped>
.wrapper {
width: 100%;
height: 100%;
// padding: 0 160px;
overflow-y: auto;
.box-header {
height: 56px;
display: flex;
position: relative;
.header-left {
margin-top: 20px;
width: 8px;
height: 16px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.title {
margin-left: 14px;
margin-top: 16px;
height: 24px;
line-height: 24px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 700;
}
.header-btn-box {
position: absolute;
top: 14px;
right: 116px;
display: flex;
.btn {
margin-left: 8px;
height: 28px;
padding: 0 8px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 4px;
background: rgba(255, 255, 255, 1);
text-align: center;
line-height: 28px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
cursor: pointer;
}
.btnActive {
border: 1px solid var(--color-main-active);
background: rgba(246, 250, 255, 1);
color: var(--color-main-active);
}
}
.header-right {
position: absolute;
top: 14px;
right: 12px;
display: flex;
justify-content: flex-end;
gap: 4px;
.icon{
width: 28px;
height: 28px;
img{
width: 100%;
height: 100%;
}
}
}
}
.box {
width: 792px;
height: 410px;
border-radius: 4px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: rgba(255, 255, 255, 1);
}
.top {
display: flex;
margin: 0 160px;
margin-top: 16px;
.box1 {
.box1-main {
height: 280px;
}
.box1-footer {
height: 40px;
margin: 0 auto;
margin-top: 19px;
width: 759px;
height: 40px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 251, 255, 1);
display: flex;
.icon1 {
margin-left: 12px;
margin-top: 10px;
width: 19px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 12px;
line-height: 40px;
width: 666px;
height: 40px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
}
.icon2 {
margin-left: 13px;
margin-top: 8px;
width: 24px;
height: 24px;
background: rgba(231, 243, 255, 1);
border-radius: 12px;
img {
width: 12px;
height: 12px;
margin-top: 6px;
margin-left: 6px;
}
}
}
}
.box2 {
margin-left: 16px;
.box2-main {
height: 280px;
}
.box2-footer {
height: 40px;
margin: 0 auto;
margin-top: 19px;
width: 759px;
height: 40px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 251, 255, 1);
display: flex;
.icon1 {
margin-left: 12px;
margin-top: 10px;
width: 19px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 12px;
line-height: 40px;
width: 666px;
height: 40px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
}
.icon2 {
margin-left: 13px;
margin-top: 8px;
width: 24px;
height: 24px;
background: rgba(231, 243, 255, 1);
border-radius: 12px;
img {
width: 12px;
height: 12px;
margin-top: 6px;
margin-left: 6px;
}
}
}
}
}
.footer {
margin: 0 160px;
margin-top: 16px;
display: flex;
.box3 {
.box3-main {
height: 280px;
display: flex;
#chart3 {
width: 396px;
}
#chart4 {
width: 396px;
}
}
.box3-footer {
height: 40px;
margin: 0 auto;
margin-top: 19px;
width: 759px;
height: 40px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 251, 255, 1);
display: flex;
.icon1 {
margin-left: 12px;
margin-top: 10px;
width: 19px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 12px;
line-height: 40px;
width: 666px;
height: 40px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
}
.icon2 {
margin-left: 13px;
margin-top: 8px;
width: 24px;
height: 24px;
background: rgba(231, 243, 255, 1);
border-radius: 12px;
img {
width: 12px;
height: 12px;
margin-top: 6px;
margin-left: 6px;
}
}
}
}
.box4 {
margin-left: 16px;
.box4-main {
height: 280px;
display: flex;
#chart5 {
width: 396px;
}
.box4-info-box {
width: 385px;
.item {
min-height: 66px;
display: flex;
.item-left {
width: 6px;
height: 6px;
border-radius: 3px;
background: rgba(255, 77, 79, 1);
margin-top: 16px;
}
.item-right {
margin-left: 10px;
.title {
margin-top: 5px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
}
.content {
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
}
}
}
}
.box4-footer {
height: 40px;
margin: 0 auto;
margin-top: 19px;
width: 759px;
height: 40px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 251, 255, 1);
display: flex;
.icon1 {
margin-left: 12px;
margin-top: 10px;
width: 19px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 12px;
line-height: 40px;
width: 666px;
height: 40px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
}
.icon2 {
margin-left: 13px;
margin-top: 8px;
width: 24px;
height: 24px;
background: rgba(231, 243, 255, 1);
border-radius: 12px;
img {
width: 12px;
height: 12px;
margin-top: 6px;
margin-left: 6px;
}
}
}
}
}
}
</style>
\ No newline at end of file
......@@ -72,14 +72,14 @@ const navList = ref([
isShow: true,
path: "/marketSingleCaseLayout/overview"
},
{
name: "报告解析",
icon: NavIcon2,
activeIcon: NavIcon2Active,
isActive: false,
isShow: route.query.id === "337",
path: "/marketSingleCaseLayout/deepdig"
},
// {
// name: "报告解析",
// icon: NavIcon2,
// activeIcon: NavIcon2Active,
// isActive: false,
// isShow: route.query.id === "337",
// path: "/marketSingleCaseLayout/deepdig"
// },
{
name: "影响分析",
icon: NavIcon3,
......@@ -98,7 +98,7 @@ const handleClickNav = index => {
router.push({
path: navList.value[index].path,
query: {
id: route.query.id
...route.query
}
});
};
......
<template>
<div class="wrapper">
<div class="wrapper" v-loading="loading">
<div class="left">
<div class="box1">
<div class="box-header">
......@@ -20,39 +20,35 @@
<div class="box1-main">
<div class="box1-item">
<div class="box1-item-left">{{ "案卷编号:" }}</div>
<div class="box1-item-right">{{ "250414-0065" }}</div>
<div class="box1-item-right">{{ baseInfo.SEARCHNUM || '-' }}</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "调查时间:" }}</div>
<div class="box1-item-right">
{{ "2021年9月21日" }}
{{ baseInfo.SEARCHDATE || '-' }}
</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "调查概括:" }}</div>
<div class="box1-item-right">
{{
"商务部长已启动一项调查,已确定进口药品和药品原料(包括成品药、医疗对策、关键投入品(如活性药物成分)和主要起始原料)及其衍生产品对国家安全的影响"
}}
{{ baseInfo.SEARCHDESC || '-' }}
</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "涉及专利:" }}</div>
<div class="box1-item-right">
{{ "注册专利号7745847、9093473、9147747、9184292" }}
{{ baseInfo.patentCode || '-' }}
</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "调查背景:" }}</div>
<div class="box1-item-right">
{{
"2025年3月1日,总统发布第14号行政命令,《应对木材、木料进口对国家安全的威胁》(90FR11359),指示商务部长根据《贸易发展法》第232条启动调查,已确定木材、木料及其衍生产品进口对国家安全的影响。2025年3月10日,部长启动了第232条调查。 "
}}
{{ baseInfo.SEARCHBACKGROUND || '-' }}
</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "调查范围:" }}</div>
<div class="box1-item-right">{{ "本次调查旨在评估汝铁硼磁铁进口对美国国家安全的影响" }}</div>
<div class="box1-item-right">{{ baseInfo.SEARCHSCOPE || '-' }}</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "法律依据:" }}</div>
......@@ -60,23 +56,24 @@
<div class="box1-item-right1-item" v-for="(item, index) in reasonList" :key="index">
{{ item }}
</div>
<div v-if="!reasonList.length">-</div>
</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "调查结论:" }}</div>
<div class="box1-item-right">
{{ "目前钒进口的数量和情况并未对第232节所定义的国家安全构成损害威胁。" }}
{{ baseInfo.SEARCHRESULTBASIC || '-' }}
</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "增加关税:" }}</div>
<div class="box1-item-right">{{ "是,增加15%" }}</div>
<div class="box1-item-right">{{ baseInfo.ISTARIFF === 'Y' ? `是,增加${baseInfo.ADDTARIFF}%` : '否' }}</div>
</div>
</div>
</div>
</div>
<div class="right">
<div class="box2">
<div class="box2" v-loading="box2Loading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">调查脉络</div>
......@@ -131,7 +128,7 @@
</div>
</div>
</div>
<div class="box3">
<div class="box3" v-loading="box3Loading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">调查结论</div>
......@@ -156,7 +153,7 @@
</div>
</div>
<div class="box3-main-item-content" v-for="(val, idx) in item.data" :key="idx">
<div class="content-item">{{ idx + 1 + "." + val }}</div>
<div class="content-item">{{ val }}</div>
</div>
</div>
</div>
......@@ -177,55 +174,93 @@
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import { getSearchBlurb, getSearchContext, getSearchConclusion } from "@/api/marketAccessRestrictions";
const route = useRoute();
const loading = ref(false);
const box2Loading = ref(false);
const box3Loading = ref(false);
const baseInfo = ref({});
const reasonList = ref([]);
const timeLineList = ref([]);
const surveyResult = ref([]);
const reasonList = ref([
"1.EO 14017-应对木材、木料进口对国家安全的威胁;",
"2.EO 12443-应对木材、木料进口对国家安全的威胁;",
"3.EO 12443-应对木材、木料进口对国家安全的威胁;"
]);
const handleGetSearchBlurb = async () => {
loading.value = true;
try {
const res = await getSearchBlurb({
searchId: route.query.searchId,
sortCode: "232"
});
if (res.code === 200 && res.data) {
const data = res.data;
baseInfo.value = data;
const surveyResult = ref([
{
title: "(一). 钒对美国国家安全至关重要",
data: [
"钒是关键矿产。美国内政部将钒列入2017年12月20日发布的第13817号行政命令要求制定的《2018年关键矿产清单》。根据该行政命令,该清单明确钒对美国国家安全至关重要,若钒供应中断,将对美国经济和国家安全造成重大影响。",
"钒因其在钢铁和钛合金中的应用,是国防系统的必需物资。在航空航天用钛关键应用中,钒具有不可替代性,许多军用机身含有大量钒。",
"钒是关键基础设施的必需物资。钒钢合金是建筑业用高强度低合金(HSLA)钢产品的关键成分,也用于工具钢和高速钢,广泛应用于美国关键基础设施。此外,几乎所有含钒钛产品均用于关键交通或国防领域。",
"钒产业对美国国家安全相关的其他关键产业具有重大影响。如前所述,钒在钢铁和钛产品中具有不可或缺的用途,且美国的钒资源往往与铀资源共生。商务部近期发现,这三个产业的进口产品均对美国国家安全构成潜在损害威胁。"
]
},
{
title: "(二). 钒进口对美国钒产业经济福利的影响具有两面性",
data: [
"美国目前依赖钒进口。美国唯一的原生钒生产商因钒价格低迷,过去五年中仅一年有生产活动;国内二次生产商需进口大量原料。",
"国对钒进口的依赖并未加剧。尽管美国依赖进口满足民用需求,但美国主要钒铁和五氧化二钒生产商正在扩大或重启运营。随着这些举措的顺利完成,2021年美国利用含钒废料生产钒铁的产能预计将翻倍,格拉迪尼公司二次生产设施重启后,美国五氧化二钒产能也将显著提升。此外,多家国内采矿企业拥有闲置产能或正在勘探钒矿开发项目,若国内钒价格上涨或出现国家安全紧急情况,这些企业可能通过新建矿山等方式增加产能。",
"受国内价格持续低迷影响,美国钒产业可能面临重大财务挑战。但由于近期设施所有权变动、大量持续投资以及运营活动的阶段性缺失,难以准确评估该产业的财务健康状况。",
"美国拥有丰富的原生生产资源。至少有三家企业曾有钒矿生产历史,另有两个项目正在开发中。",
"钒的二次生产具有环境效益。二次生产所用含钒废料被美国环境保护局(EPA)归类为危险废物,但二次生产能够回收关键矿产,大幅减少废料填埋量,将其用于国防关键产品生产。"
]
// 法律依据
if (data.billData && data.billData !== "null") {
reasonList.value = [data.billData];
} else {
reasonList.value = [];
}
}
} catch (error) {
console.error("获取调查详情失败", error);
} finally {
loading.value = false;
}
]);
};
const timeLineList = ref([
{
time: "2021年6月",
content: "白宫报告《构建有韧性的供应链、重振美国制造业并促进广泛增长:依据第 14017 号行政令开展的百日审查》。"
},
{
time: "2021年9月21日",
content: "商务部长吉娜・雷蒙多启动了关于钕铁硼磁体进口对国家安全影响的 232 调查。"
},
{
time: "2021 年 9 月 27 日",
content: "商务部在《联邦公报》发布通知,宣布启动关于钕铁硼磁体进口对国家安全影响的调查。"
},
{
time: "2022 年 1 月 31 日",
content: "商务部向 60 家企业发放了调查。"
const handleGetSearchContext = async () => {
box2Loading.value = true;
try {
const res = await getSearchContext({
searchId: route.query.searchId
});
if (res.code === 200 && res.data) {
timeLineList.value = res.data.map(item => ({
time: item.CONTTIME,
content: item.CONTDESC
}));
}
} catch (error) {
console.error("获取事件脉络失败", error);
} finally {
box2Loading.value = false;
}
]);
};
const handleGetSearchConclusion = async () => {
box3Loading.value = true;
try {
const res = await getSearchConclusion({
searchId: route.query.searchId
});
if (res.code === 200 && res.data) {
surveyResult.value = res.data.map(item => {
// 将 CONTENT 按换行符分割,并过滤掉空行
const contentList = item.CONTENT ? item.CONTENT.split(/\r\n|\n/).filter(line => line.trim()) : [];
return {
title: item.TITLE,
data: contentList
};
});
}
} catch (error) {
console.error("获取调查结论失败", error);
} finally {
box3Loading.value = false;
}
};
onMounted(() => {
handleGetSearchBlurb();
handleGetSearchContext();
handleGetSearchConclusion();
});
</script>
<style lang="scss" scoped>
......@@ -657,4 +692,4 @@ const timeLineList = ref([
}
}
}
</style>
\ No newline at end of file
</style>
<template>
<div class="wrapper">
<div class="left">
<div class="box1">
<div class="box1" v-loading="leftLoading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">基本信息</div>
......@@ -14,12 +14,12 @@
<div class="box1-main">
<div class="box1-item">
<div class="box1-item-left">{{ "启动时间:" }}</div>
<div class="box1-item-right">{{ "2025年4月17日" }}</div>
<div class="box1-item-right">{{ baseInfo.SEARCHDATE || '-' }}</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "调查对象:" }}</div>
<div class="box1-item-right">
{{ "中国" }}
{{ baseInfo.RESPONDENTS || '-' }}
</div>
</div>
<div class="box1-item">
......@@ -28,7 +28,7 @@
<div class="icon">
<img src="./assets/images/icon1.png" alt="" />
</div>
<div class="text">{{ "美国贸易代表办公室 >" }}</div>
<div class="text">{{ (baseInfo.SEARCHORG || '-') + " >" }}</div>
</div>
</div>
<div class="box1-item">
......@@ -47,26 +47,29 @@
<div class="box1-item">
<div class="box1-item-left">{{ "调查状态:" }}</div>
<div class="box1-item-right">
{{ "启动调查、举行听证会及征询意见的通知 " }}
{{ baseInfo.SEARCHSTATUS || '-' }}
</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "请愿方:" }}</div>
<div class="box1-item-right">
{{
"联合钢铁、造纸与林业、橡胶、制造业、能源、相关工业与服务工人国际工会、AFL-CIO 联盟(“USW”)/ 国际机械师与航空航天工人协会(“IAM”)/ 国际锅炉工、铁船建造者、铁匠、锻造工及助手兄弟会,AFL-CIO/CLC(“IBB”)/ 国际电气工人兄弟会(“IBEW”)/ 海事贸易部,AFL-CIO(“MTD”)"
}}
{{ baseInfo.qyData || '-' }}
</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "依托政令:" }}</div>
<div class="box1-item-right3">
{{ "2025年2月4日总统政令《确保美国海事及造船业在世界保持主导地位》 >" }}
<template v-if="baseInfo.AdminstrativeData && baseInfo.AdminstrativeData.length">
<div v-for="(item, index) in baseInfo.AdminstrativeData" :key="index">
{{ item.ORDERNAME + " >" }}
</div>
</template>
<template v-else>-</template>
</div>
</div>
</div>
</div>
<div class="box2">
<div class="box2" v-loading="leftLoading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">调查公告</div>
......@@ -90,7 +93,7 @@
</div>
</div>
<div class="right">
<div class="box3">
<div class="box3" v-loading="box3Loading">
<div class="box-header">
<div class="btn-box">
<div
......@@ -149,7 +152,7 @@
</div>
</div>
</div>
<div class="box4">
<div class="box4" v-loading="box4Loading">
<div class="box-header">
<div class="header-left"></div>
<div class="title">相关事件</div>
......@@ -162,7 +165,11 @@
<div class="box4-main">
<div class="box4-main-item" v-for="(item, index) in box4Data" :key="index">
<div class="item-left">
<div class="item-left-tag" :class="{ tag1: item.tag.type === 1, tag2: item.tag.type === 2 }">
<div class="item-left-tag" :class="{
tag1: item.tag.type == '新闻',
tag2: item.tag.type == '行政令',
tag3: item.tag.type == '法案',
}">
{{ item.tag.name }}
</div>
</div>
......@@ -183,32 +190,21 @@
</template>
<script setup>
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import { getSearchBlurb, getRelatedEvents, getSearchContext, getSearchMeasures } from "@/api/marketAccessRestrictions";
const surveyAreaList = ref([
{
name: "先进制造",
type: 1
},
{
name: "海洋",
type: 2
}
]);
const route = useRoute();
const box2Data = ref([
{
time: "2025-02-20",
content: "美国贸易代表征求公众意见,针对中国针对海事、物流和造船业主导地位的调查第301条拟议行动"
},
{
time: "2025-02-20",
content: "美国贸易代表征求公众意见,针对中国针对海事、物流和造船业主导地位的调查第301条拟议行动"
},
{
time: "2025-02-20",
content: "美国贸易代表征求公众意见,针对中国针对海事、物流和造船业主导地位的调查第301条拟议行动"
}
]);
const leftLoading = ref(false);
const box3Loading = ref(false);
const box4Loading = ref(false);
const baseInfo = ref({});
const surveyAreaList = ref([]);
const box2Data = ref([]);
const box3Data1 = ref([]);
const box3Data2 = ref([]);
const box4Data = ref([]);
const box3BtnList = ref(["事件脉络", "报复性措施"]);
const box3BtnActive = ref("事件脉络");
......@@ -217,120 +213,128 @@ const handleClickBox3Btn = btn => {
box3BtnActive.value = btn;
};
const box3Data1 = ref([
{
time: "2024-03-12",
title: "递交请愿书",
content: "2024年3月12日,请愿人提交了一份关于中国在海事、物流和造船领域主导行为、政策和做法的301条请愿书。"
},
{
time: "2024-03-12",
title: "递交请愿书",
content: ""
},
{
time: "2024-03-12",
title: "递交请愿书",
content:
"2024年4月17日,在与相关咨询委员会和第301条委员会协商后,美国贸易代表启动了对中国在海事、物流和造船行业中追求主导地位的行为、政策和做法的调查。"
},
{
time: "2024-03-12",
title: "递交请愿书",
content: "2024年3月12日,请愿人提交了一份关于中国在海事、物流和造船领域主导行为、政策和做法的301条请愿书。"
},
{
time: "2024-03-12",
title: "递交请愿书",
content: "2024年3月12日,请愿人提交了一份关于中国在海事、物流和造船领域主导行为、政策和做法的301条请愿书。"
},
{
time: "2024-03-12",
title: "递交请愿书",
content: "2024年3月12日,请愿人提交了一份关于中国在海事、物流和造船领域主导行为、政策和做法的301条请愿书。"
},
{
time: "2024-03-12",
title: "递交请愿书",
content: "2024年3月12日,请愿人提交了一份关于中国在海事、物流和造船领域主导行为、政策和做法的301条请愿书。"
}
]);
const box3Data2 = ref([
{
title: "(一)海运服务费用",
data: [
{
content: `对中国船舶运营商和船舶所有者分阶段征收费用。该费用根据船舶的净吨位计算,适用于任何由中国运营商运营或由中国实体拥有的船舶,如附录 I 所示。如果一艘船舶在前往外国目的地之前多次进入美国港口,则该费用按每次轮次或美国港口航行序列征收。费用在前180天为零,随后设定为每净吨50美元,并在接下来的三年内逐步增加。`
},
{
content: `分阶段对中国建造船舶收取费用。此费用以以下两者中的较高者为基准:(i) 基于船舶净吨位的费用,或 (ii) 基于每个集装箱的费用。如果一艘船在前往外国目的地之前多次进入美国港口,该费用按每次航行轮次或美国港口访问串次收取。费用在前180天设定为0美元,随后在接下来的三年内按附录二所述逐步增加。某些中国建造的船舶不适用此费用,包括:注册于某些美国海事管理局计划(例如海上安全计划和油轮安全计划)的船舶;空载或吃水浅位到港的船舶;低于某些规模或载重阈值的船舶;从事近海运输(即从某些美国港口出发航程少于2,000海里的航程)的船舶;某些美国所有公司拥有的船舶;以及某些专用出口船舶。如果船舶运营商订购并接收等同规模的美国建造船舶,其有资格获得最长三年的费用减免。`
},
{
content: `对外国车辆运输船船舶运营商分阶段收取费用。根据附件三规定,该费用根据任何外国建造的车辆运输船的车等效单位(CEU)容量进行评估。费用在前180天设定为0美元,随后按进入的非美国建造船舶的CEU容量每单位收取150美元。如果运营商在此期间订购并接收等同或更大容量的美国建造船舶,其费用可以获得最多三年的减免。如果船舶运营商订购并接收至少同等尺寸的美国建造船舶,则有资格获得三年的费用减免。`
const handleGetSearchBlurb = async () => {
leftLoading.value = true;
try {
const res = await getSearchBlurb({
searchId: route.query.searchId,
sortCode: "301"
});
if (res.code === 200 && res.data) {
const data = res.data;
baseInfo.value = data;
// 调查领域
if (data.areaData) {
surveyAreaList.value = data.areaData.map(name => ({
name,
type: 1 // 默认为 type 1
}));
}
]
},
{
title: "(二)关于促进美国货物在美国船舶上运输的服务限制",
data: [
{
content: `对某些海上运输的限制。三年后,USTR 将实施限制,要求在海上运输一定比例的 LNG 出口时使用美国船舶,如附录 IV 所述。运营商或其不符合要求的 LNG 船舶可以在三年内获准运营,仿佛已满足该要求,前提是该运营商在此期间订购并接收一艘同等或更大容量的美国建造 LNG 船舶。USTR 将根据需要与美国能源部及其他机构协商,以提供有关该限制的通知和进一步的技术信息。`
// 调查公告
if (data.progress) {
box2Data.value = data.progress.map(p => ({
time: p.PROGRESSDATE,
content: p.PROGRESSSTAGE
}));
// 事件脉络
box3Data1.value = data.progress.map(p => ({
time: p.PROGRESSDATE,
title: p.PROGRESSSTAGE,
content: ""
}));
}
]
},
{
title: "(三)总统在第14269号行政命令《恢复美国海上主导地位》中指示的行动",
data: [
{
content: `根据总统在第14269号行政命令《恢复美国海上主导地位》中提出的指示,美国贸易代表正在提议对STS起重机征收额外关税,这一做法与行政命令中的描述一致,同时还对来自中国的集装箱和某些底盘征收关税,这符合第14269号行政命令关于其他中国货物处理设备的指示,涵盖税则编号和子目8609.00.00、8716.39.0090、8716.90.30和8716.90.50。`
// 报复性措施
if (data.SORTMEASURE) {
box3Data2.value = [
{
title: "报复性措施",
data: [{ content: data.SORTMEASURE }]
}
];
}
]
}
} catch (error) {
console.error("获取调查详情失败", error);
} finally {
leftLoading.value = false;
}
]);
};
const box4Data = ref([
{
title: "美国商务部工业与安全局发布实体清单,涉及多家中国半导体企业",
time: "2025年9月12日",
content:
"23家中国实体,包括复旦微电旗下多家公司、华岭股份等,指控这些实体“违背美国国家安全或外交政策利益”,包括为中国的...",
tag: {
name: "行政令",
type: 2
}
},
{
title: "美国白宫发布总统政令,提出将发展美国人工智能产业硬件支持放在新任期的科技首要地位",
time: "2025年9月11日",
content:
"9个中国实体​(8家企业和1名个人),例如湖北奇卡工业有限公司、广州雅凯国际货运代理有限公司等,指控这些实体为也门胡塞...",
tag: {
name: "行政令",
type: 2
const handleGetRelatedEvents = async () => {
box4Loading.value = true;
try {
const res = await getRelatedEvents({
searchId: route.query.searchId
});
if(res.code === 200 && res.data) {
box4Data.value = res.data.map(item => ({
title: item.NAME,
content: item.SUMMARY,
time: item.DATE,
tag: {
type: item.TYPE,
name: item.TYPE
}
}));
}
},
{
title: "美国财政部外国资产控制办公室指控中国企业及船只",
time: "2025年3月13日",
content:
"​4家中国企业和3艘关联船只​(如香港和顺运贸有限公司、华夏贸易有限公司等),指控这些公司拥有或运营向中国运送伊朗石油或...",
tag: {
name: "行政令",
type: 2
} catch (error) {
console.error("获取相关事件失败", error);
} finally {
box4Loading.value = false;
}
};
const handleGetSearchContext = async () => {
box3Loading.value = true;
try {
const res = await getSearchContext({
searchId: route.query.searchId
});
if(res.code === 200 && res.data) {
console.log(res.data)
box3Data1.value = res.data.map(item => ({
time: item.CONTTIME,
title: item.CONTNODE,
content: item.CONTDESC
}));
}
},
{
title: "美国国会通过《芯片科学》法案",
time: "2025年1月3日",
content:
"​多家中国实体,包括成都雷电微力科技股份有限公司、中国科学院长春光学精密机械与物理研究所等,指控这些实体与中国高超音...",
tag: {
name: "法案",
type: 1
} catch (error) {
console.error("获取事件脉络失败", error);
} finally {
box3Loading.value = false;
}
}
const handleGetSearchMeasures = async () => {
// box3Loading is shared with Context for now if they are called together
// or we can just set it true here as well
box3Loading.value = true;
try {
const res = await getSearchMeasures({
searchId: route.query.searchId
});
if(res.code === 200 && res.data) {
box3Data2.value = res.data.map(item => ({
title: item.TITLE,
data: [{ content: item.CONTENT }]
}));
}
} catch (error) {
console.error("获取报复性措施失败", error);
} finally {
box3Loading.value = false;
}
]);
};
onMounted(() => {
handleGetSearchBlurb();
handleGetRelatedEvents();
handleGetSearchContext();
handleGetSearchMeasures();
});
</script>
<style lang="scss" scoped>
......@@ -488,6 +492,11 @@ const box4Data = ref([
border: 1px solid rgba(145, 202, 255, 1);
background: rgba(230, 244, 255, 1);
}
.tag3 {
color: rgba(114, 46, 209, 1);
border: 1px solid rgba(211, 173, 247, 1);
background: rgba(249, 240, 255, 1);
}
}
.box1-item-right3 {
width: 346px;
......@@ -517,7 +526,7 @@ const box4Data = ref([
border-top: 1px solid rgba(234, 236, 238, 1);
.box2-item {
width: 483px;
height: 108px;
// height: 108px;
margin: 0 auto;
display: flex;
gap: 16px;
......@@ -546,7 +555,7 @@ const box4Data = ref([
}
.content {
width: 435px;
height: 60px;
// height: 60px;
margin-top: 4px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
......@@ -580,6 +589,7 @@ const box4Data = ref([
.box3-main1 {
.box3-main1-item {
// height: 140px;
margin-bottom: 20px;
display: flex;
.left {
......@@ -594,7 +604,7 @@ const box4Data = ref([
}
.line {
width: 2px;
height: 100%;
height: 130%;
margin-left: 4px;
background: #e6e7e8;
}
......@@ -738,13 +748,20 @@ const box4Data = ref([
padding: 0 8px;
}
.tag1 {
color: var(--color-main-active);
background: rgba(231, 243, 255, 1);
color: rgba(19, 168, 168, 1);
border: 1px solid rgba(135, 232, 222, 1);
background: rgba(230, 255, 251, 1);
}
.tag2 {
background: rgba(232, 189, 11, 0.1);
color: rgba(232, 189, 11, 1);
color: rgba(22, 119, 255, 1);
border: 1px solid rgba(145, 202, 255, 1);
background: rgba(230, 244, 255, 1);
}
.tag3 {
color: rgba(114, 46, 209, 1);
border: 1px solid rgba(211, 173, 247, 1);
background: rgba(249, 240, 255, 1);
}
}
.item-right {
width: 924px;
......
<template>
<div class="wrapper">
<div class="wrapper" v-loading="loading">
<div class="left">
<div class="box1">
<div class="box-header">
......@@ -20,34 +20,33 @@
<div class="box1-main">
<div class="box1-item">
<div class="box1-item-left">{{ "调查案号:" }}</div>
<div class="box1-item-right">{{ "337-TA-1443" }}</div>
<div class="box1-item-right">{{ baseInfo.SEARCHNUM || '-' }}</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "涉及产品:" }}</div>
<div class="box1-item-right">
{{
"特定外国制造的半导体器件及其下游产品和组件(Certain Foreign-Fabricated Semiconductor Devices, Products Containing the Same, and Components Thereof"
}}
{{ baseInfo.SEARCHPRODUCT || '-' }}
</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "调查类别:" }}</div>
<div class="box1-item-right">{{ "专利侵权调查" }}</div>
<div class="box1-item-right">{{ baseInfo.SEARCHTYPE || '-' }}</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "涉及专利:" }}</div>
<div class="box1-item-right">
{{ "注册专利号7745847、9093473、9147747、9184292" }}
{{ baseInfo.patentCode || '-' }}
</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "限制措施:" }}</div>
<div class="box1-item-right">{{ "有限排除令 、禁止令 " }}</div>
<div class="box1-item-right">{{ baseInfo.SORTMEASURE || '-' }}</div>
</div>
<div class="box1-item">
<div class="box1-item-left">{{ "调查领域:" }}</div>
<div class="box1-item-right1">
<div class="tag">{{ "半导体" }}</div>
<div class="tag" v-for="(item, index) in baseInfo.areaData" :key="index">{{ item }}</div>
<div v-if="!baseInfo.areaData || !baseInfo.areaData.length">-</div>
</div>
</div>
<div class="box1-item">
......@@ -86,27 +85,22 @@
<div class="box2-main">
<div class="box2-main-top">
<div class="left">
{{ "Lon" }}
{{ baseInfo.ORGNAME ? baseInfo.ORGNAME.substring(0, 3) : '-' }}
</div>
<div class="right">
<div class="title">{{ "爱尔兰Longitude Licensing Ltd" }}</div>
<div class="info">{{ "爱尔兰半导体零件制造公司" }}</div>
<div class="title">{{ baseInfo.ORGNAME || '-' }}</div>
<div class="info">{{ "-" }}</div>
</div>
</div>
<div class="box2-main-center">
{{
`Longitude Licensing Ltd. (“Longitude”) 是IPValue管理集团旗下企业,致力于在国际知识经济最活跃的领域开展业务。
公司总部位于爱尔兰共和国都柏林,以成为领先的知识产权管理公司为愿景,专注于为全球专利持有人实现专利知识产权价值最大化。自2013年7月成立以来,经度公司目前已获得知名科技企业授权,管理着总计超过8,000项有效专利和专利申请的组合。这些专利组合涵盖半导体设计与制造工艺、非易失性存储技术、显示技术及消费电子技术。我们计划将合作伙伴关系与许可活动拓展至相关技术领域。`
}}
{{ baseInfo.ORGBLURB || '-' }}
</div>
<div class="box2-main-footer">
<div class="box2-main-footer-left">
<img src="./assets/images/footer-icon1.png" alt="" />
</div>
<div class="box2-main-footer-center">
{{
'Pantech是韩国的一家通信设备公司,曾经是手机制造商,但现在可能已转型为专利持有实体。这类公司常被称为"非执业实体"(NPE)或"专利断言实体"(PAE),通过专利授权和诉讼获取收益。这些企业曾经是行业龙头,但因科技和市场形态巨变,加上自身改革步伐缓慢,经营状况每况愈下。卖掉实体业务部门后,留下来的是高达几万件的专利。'
}}
{{ baseInfo.ORGBLURB || '-' }}
</div>
<div class="box2-main-footer-right">
<img src="./assets/images/footer-icon2.png" alt="" />
......@@ -134,12 +128,13 @@
</div>
</div>
<div class="box3-main">
<div class="box3-main-box" v-for="item,index in caseList" :key="index">
<div class="box3-main-box" v-for="(item, index) in caseList" :key="index">
<div class="box3-main-box-header">{{ item.title }}</div>
<div class="box3-main-box-content">
<div class="content-item" v-for="val,idx in caseList[index].companyList" :key="idx">
<div class="content-item" v-for="(val, idx) in item.companyList" :key="idx">
<div class="icon">
<img :src="val.logo" alt="">
<img :src="val.logo" alt="" v-if="val.logo">
<div class="default-logo" v-else>{{ val.name ? val.name.substring(0, 1) : '' }}</div>
</div>
<div class="text">{{ val.name }}</div>
</div>
......@@ -152,127 +147,63 @@
</template>
<script setup>
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import { getSearchBlurb } from "@/api/marketAccessRestrictions";
import Logo1 from "./assets/images/logo1.png";
import Logo2 from "./assets/images/logo2.png";
import Logo3 from "./assets/images/logo3.png";
import Logo4 from "./assets/images/logo4.png";
import Logo5 from "./assets/images/logo5.png";
import Logo6 from "./assets/images/logo6.png";
const route = useRoute();
const loading = ref(false);
const baseInfo = ref({});
const processList = ref([]);
const caseList = ref([]);
const processList = ref([
{
time: "2025-02-18",
title: "申请立案"
},
{
time: "2025-03-21",
title: "立案"
},
{
time: "2025-08-14",
title: "部分仲裁"
},
{
time: "2025-09-18",
title: "部分终裁"
}
]);
const handleGetSearchBlurb = async () => {
loading.value = true;
try {
const res = await getSearchBlurb({
searchId: route.query.searchId,
sortCode: "337"
});
if (res.code === 200 && res.data) {
const data = res.data;
baseInfo.value = data;
// 案件进展
if (data.progress) {
processList.value = data.progress.map(p => ({
time: p.PROGRESSDATE,
title: p.PROGRESSSTAGE
}));
}
const caseList = ref([
{
title: "一加(OnePlus)及相关企业",
companyList: [
{
name: "深圳市万普拉斯科技有限公司 (中国广东)",
logo: Logo1
},
{
name: "OnePlus USA Corp. (美国德州欧文)",
logo: Logo1
}
]
},
{
title: "联想(Lenovo)及相关企业",
companyList: [
{
name: "联想集团 (中国北京)",
logo: Logo2
},
{
name: "Lenovo (United States) Inc. (美国北卡罗来纳州)",
logo: Logo2
},
{
name: "Motorola Mobility LLC (美国伊利诺伊州)",
logo: Logo2
}
]
},
{
title: "TCL及相关企业",
companyList: [
{
name: "TCL实业控股股份有限公司 (中国广东)",
logo: Logo3
},
{
name: "TCL Electronics Holdings Ltd. (中国香港)",
logo: Logo3
},
{
name: "TCL Communication Ltd. (中国香港)",
logo: Logo3
},
{
name: "TCL Communication Ltd. (中国香港)",
logo: Logo3
},
{
name: "TCL Communication Technology Holdings Ltd. (中国广东)",
logo: Logo3
},
{
name: "TCL Mobile International Ltd. (中国香港)",
logo: Logo3
},
{
name: "惠州TCL移动通信有限公司 (中国广东)",
logo: Logo3
},
{
name: "TCL Mobile Communication (HK) Company Ltd. (中国香港)",
logo: Logo3
}
]
},
{
title: "其他相关企业",
companyList: [
{
name: "Tinno USA, Inc. (美国德州普莱诺)",
logo: Logo4
},
{
name: "深圳市天珑移动技术有限公司 (中国广东)",
logo: Logo5
},
{
name: "HMD Global (芬兰)",
logo: Logo6
},
{
name: "HMD Global OY (芬兰)",
logo: Logo6
},
{
name: "HMD America, Inc. (美国佛罗里达州迈阿密)",
logo: Logo6
}
]
}
]);
// 被告信息分组
if (data.defendantOrg) {
const groups = {};
data.defendantOrg.forEach(item => {
const companyName = item.COMPANYNAME || "其他相关企业";
if (!groups[companyName]) {
groups[companyName] = {
title: companyName + (companyName !== "其他" ? "及相关企业" : ""),
companyList: []
};
}
groups[companyName].companyList.push({
name: item.COMNAME,
logo: "" // API 不提供 logo
});
});
caseList.value = Object.values(groups);
}
}
} catch (error) {
console.error("获取调查详情失败", error);
} finally {
loading.value = false;
}
};
onMounted(() => {
handleGetSearchBlurb();
});
</script>
<style lang="scss" scoped>
......@@ -407,6 +338,9 @@ const caseList = ref([
line-height: 24px;
}
.box1-item-right1 {
display: flex;
flex-wrap: wrap;
gap: 8px;
.tag {
margin-top: 3px;
margin-bottom: 3px;
......@@ -523,6 +457,7 @@ const caseList = ref([
font-weight: 400;
line-height: 24px;
height: 160px;
overflow-y: auto;
}
.box2-main-footer {
margin: 0 auto;
......@@ -554,6 +489,11 @@ const caseList = ref([
font-size: 16px;
font-weight: 400;
line-height: 24px;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.box2-main-footer-right {
margin-left: 13px;
......@@ -584,6 +524,8 @@ const caseList = ref([
margin-top: 1px;
margin-left: 24px;
margin-right: 27px;
height: 600px;
overflow-y: auto;
.box3-main-box {
margin-bottom: 18px;
.box3-main-box-header {
......@@ -608,10 +550,25 @@ const caseList = ref([
width: 24px;
height: 24px;
margin-top: 3px;
display: flex;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
}
.default-logo {
width: 24px;
height: 24px;
background: #e7f3ff;
color: var(--color-main-active);
font-size: 12px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}
}
.text {
margin-left: 13px;
......@@ -621,6 +578,11 @@ const caseList = ref([
font-size: 16px;
font-weight: 400;
line-height: 30px;
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 10px;
}
}
}
......@@ -629,4 +591,4 @@ const caseList = ref([
}
}
}
</style>
\ No newline at end of file
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论