feat: 视频分析对接、首页完善联调数据
parent
d8c58dd183
commit
f00434a820
@ -0,0 +1,147 @@
|
||||
<!--
|
||||
* @Author: donghao donghao@supervision.ltd
|
||||
* @Date: 2025-08-14 13:38:30
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2025-09-10 11:42:43
|
||||
* @FilePath: \5G-Web\src\views\dashboard\components\SwpierMonitor.vue
|
||||
* @Description: 外观、撑杆统一封装轮播图&视频组件
|
||||
-->
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Player from "@/components/videoPlayer/Player.vue";
|
||||
import SwiperPlayer from "./swiperPlayer.vue";
|
||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||
import { Navigation, Scrollbar, Mousewheel, Pagination } from "swiper/modules";
|
||||
import "swiper/css";
|
||||
import "swiper/scss";
|
||||
import "swiper/scss/navigation";
|
||||
|
||||
interface Props {
|
||||
fileList: string[];
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: "update:value", val: boolean): void;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
fileList: [],
|
||||
});
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
|
||||
const modules = [Navigation, Scrollbar];
|
||||
const activeIndex = ref(-1);
|
||||
const swiperRef = ref(null);
|
||||
// const isPlaying = ref<boolean>(false); // 是否播放
|
||||
const currFile = ref<Record<string, any>>({}); // 详情数据
|
||||
|
||||
// const togglePlay = () => {
|
||||
// isPlaying.value = !isPlaying.value;
|
||||
// };
|
||||
|
||||
|
||||
const onSwiper = (swiper) => {
|
||||
swiperRef.value = swiper;
|
||||
console.log("Swiper 实例已获取:", swiper);
|
||||
};
|
||||
const onSlideChange = () => {
|
||||
console.log("slide change");
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.fileList,
|
||||
(newVal) => {
|
||||
currFile.value = newVal?.[0];
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full h-full swiper-images-wrap">
|
||||
<!-- 缩略图区域 -->
|
||||
<div class="flex" v-if="fileList?.length > 0">
|
||||
<div class="thumbnail-container">
|
||||
<swiper
|
||||
ref="swiperRef"
|
||||
:modules="modules"
|
||||
:slides-per-view="2"
|
||||
:space-between="12"
|
||||
:navigation="true"
|
||||
:scrollbar="{ draggable: false }"
|
||||
:centered-slides="false"
|
||||
:observer="true"
|
||||
:observeParents="true"
|
||||
@swiper="onSwiper"
|
||||
@slideChange="onSlideChange"
|
||||
>
|
||||
<swiper-slide
|
||||
v-for="(file, index) in fileList"
|
||||
:key="index"
|
||||
:class="{ 'active-slide': activeIndex === index }"
|
||||
>
|
||||
<img
|
||||
:src="file"
|
||||
v-if="file"
|
||||
class="cursor-pointer"
|
||||
/>
|
||||
<div v-else>
|
||||
<!-- 视频【图片】加载失败 -->
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.swiper-images-wrap {
|
||||
.thumbnail-container {
|
||||
overflow: visible;
|
||||
margin-right: 16px;
|
||||
.swiper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 1150px;
|
||||
.swiper-slide {
|
||||
img,
|
||||
video {
|
||||
border-radius: 4px;
|
||||
object-fit: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
.active-slide img,
|
||||
.active-slide video {
|
||||
width: 568px;
|
||||
border-radius: 4px;
|
||||
border: 2px solid #2ecce0;
|
||||
}
|
||||
.swiper-button-prev,
|
||||
.swiper-button-next {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
color: white;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.swiper-button-prev::after,
|
||||
.swiper-button-next::after {
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 修改按钮悬停样式 */
|
||||
.swiper-button-prev:hover,
|
||||
.swiper-button-next:hover {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,23 @@
|
||||
export const useDict = () => {
|
||||
// 外观监测故障类型
|
||||
const fetchAppearanceFaultTypeList = () => {
|
||||
return [
|
||||
"侧开门门板变形",
|
||||
"侧墙板外胀",
|
||||
"扶手变形",
|
||||
"扶梯变形",
|
||||
"配属标记不清",
|
||||
"下侧门折页圆销丢失",
|
||||
"载重标记不清",
|
||||
"搭扣未搭",
|
||||
];
|
||||
};
|
||||
|
||||
const fetchPoleFaultTypeList = () => {
|
||||
return ["钢管破损", "钢管锈蚀"];
|
||||
};
|
||||
return {
|
||||
fetchAppearanceFaultTypeList,
|
||||
fetchPoleFaultTypeList
|
||||
};
|
||||
};
|
@ -1,92 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array as PropType<Array<{ value: number; name: string }>>,
|
||||
required: true,
|
||||
},
|
||||
colors: { type: Array as PropType<Array<string>>, default: () => [] },
|
||||
});
|
||||
|
||||
const chartContainer = ref<HTMLDivElement | null>(null);
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
const colorsArr = ['#FFCC4A','#028FF5','#06EA7C','#8500FF','#FF7D05','#00D1FF']
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (!chartContainer.value) return;
|
||||
chartInstance = echarts.init(chartContainer.value);
|
||||
setTimeout(() => {
|
||||
chartInstance?.resize();
|
||||
updateChart();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
// 更新图表配置
|
||||
const updateChart = () => {
|
||||
if (!chartInstance) return;
|
||||
|
||||
chartInstance.setOption({
|
||||
legend: {
|
||||
type: "scroll",
|
||||
orient: "vertical",
|
||||
left: "70%",
|
||||
align: "left",
|
||||
top: "middle",
|
||||
itemWidth: 16, // 图例项宽度
|
||||
itemHeight: 8,
|
||||
textStyle: { color: "#FFF" },
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "pie",
|
||||
radius: ["30%", "80%"],
|
||||
center: ["35%", "50%"],
|
||||
label: { show: false },
|
||||
itemStyle: {
|
||||
color: (params) =>
|
||||
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: props.colors[params.dataIndex] },
|
||||
{ offset: 1, color: colorsArr[params.dataIndex] },
|
||||
]),
|
||||
},
|
||||
data: props.data,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
// 生命周期管理
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
});
|
||||
|
||||
// 响应式更新
|
||||
watch(
|
||||
() => props.data,
|
||||
async () => {
|
||||
await nextTick();
|
||||
updateChart();
|
||||
}
|
||||
);
|
||||
|
||||
// 窗口大小监听
|
||||
// onMounted(() => {
|
||||
// window.addEventListener("resize", () => chartInstance?.resize());
|
||||
// });
|
||||
|
||||
// onUnmounted(() => {
|
||||
// window.removeEventListener("resize", () => chartInstance?.resize());
|
||||
// });
|
||||
</script>
|
||||
<template>
|
||||
<div ref="chartContainer" style="width: 100%; height: 100%" />
|
||||
</template>
|
@ -1,92 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array as PropType<Array<{ value: number; name: string }>>,
|
||||
required: true,
|
||||
},
|
||||
colors: { type: Array as PropType<Array<string>>, default: () => [] },
|
||||
});
|
||||
|
||||
const chartContainer = ref<HTMLDivElement | null>(null);
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
const colorsArr = ['#3FE3FA','#FF4D00']
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (!chartContainer.value) return;
|
||||
chartInstance = echarts.init(chartContainer.value);
|
||||
setTimeout(() => {
|
||||
chartInstance?.resize();
|
||||
updateChart();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
// 更新图表配置
|
||||
const updateChart = () => {
|
||||
if (!chartInstance) return;
|
||||
|
||||
chartInstance.setOption({
|
||||
legend: {
|
||||
type: "scroll",
|
||||
orient: "vertical",
|
||||
left: "70%",
|
||||
align: "left",
|
||||
top: "middle",
|
||||
itemWidth: 16, // 图例项宽度
|
||||
itemHeight: 8,
|
||||
textStyle: { color: "#FFF" },
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "pie",
|
||||
radius: ["40%", "80%"],
|
||||
center: ["35%", "50%"],
|
||||
label: { show: false },
|
||||
itemStyle: {
|
||||
color: (params) =>
|
||||
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: props.colors[params.dataIndex] },
|
||||
{ offset: 1, color: colorsArr[params.dataIndex] },
|
||||
]),
|
||||
},
|
||||
data: props.data,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
// 生命周期管理
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
});
|
||||
|
||||
// 响应式更新
|
||||
watch(
|
||||
() => props.data,
|
||||
async () => {
|
||||
await nextTick();
|
||||
updateChart();
|
||||
}
|
||||
);
|
||||
|
||||
// 窗口大小监听
|
||||
// onMounted(() => {
|
||||
// window.addEventListener("resize", () => chartInstance?.resize());
|
||||
// });
|
||||
|
||||
// onUnmounted(() => {
|
||||
// window.removeEventListener("resize", () => chartInstance?.resize());
|
||||
// });
|
||||
</script>
|
||||
<template>
|
||||
<div ref="chartContainer" style="width: 100%; height: 100%" />
|
||||
</template>
|
@ -0,0 +1,451 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vue 3 + ECharts 饼图点击交互</title>
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.3);
|
||||
overflow: hidden;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #2c3e50;
|
||||
font-size: 2.8rem;
|
||||
margin-bottom: 10px;
|
||||
background: linear-gradient(45deg, #3498db, #8e44ad);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #7f8c8d;
|
||||
font-size: 1.2rem;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
flex: 1;
|
||||
min-width: 350px;
|
||||
height: 450px;
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
padding: 15px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.chart-container:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.info-panel {
|
||||
flex: 1;
|
||||
min-width: 350px;
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
padding: 25px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.info-header {
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 2px solid #f1f2f6;
|
||||
}
|
||||
|
||||
.info-header h2 {
|
||||
color: #2c3e50;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.default-info {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
color: #95a5a6;
|
||||
}
|
||||
|
||||
.default-info i {
|
||||
font-size: 5rem;
|
||||
margin-bottom: 20px;
|
||||
color: #ecf0f1;
|
||||
}
|
||||
|
||||
.info-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background: #f8f9fa;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.info-card:hover {
|
||||
transform: translateX(5px);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.info-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 15px;
|
||||
font-size: 1.2rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.info-name {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
color: #7f8c8d;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: 600;
|
||||
width: 120px;
|
||||
color: #34495e;
|
||||
}
|
||||
|
||||
.instructions {
|
||||
margin-top: 30px;
|
||||
background: #e8f4fc;
|
||||
border-radius: 15px;
|
||||
padding: 20px;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.instructions h3 {
|
||||
margin-bottom: 15px;
|
||||
color: #2980b9;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.instructions ul {
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.instructions li {
|
||||
margin: 10px 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: #f1c40f;
|
||||
color: #2c3e50;
|
||||
padding: 0 5px;
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
color: #7f8c8d;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>ECharts 饼图点击交互</h1>
|
||||
<p class="subtitle">本示例展示如何在 Vue 3 中为 ECharts 饼图添加点击事件,点击不同扇区查看详情信息</p>
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<div class="chart-container">
|
||||
<div ref="chartDom" style="width: 100%; height: 100%;"></div>
|
||||
</div>
|
||||
|
||||
<div class="info-panel">
|
||||
<div class="info-header">
|
||||
<h2>点击详情</h2>
|
||||
</div>
|
||||
|
||||
<div v-if="!clickData" class="default-info">
|
||||
<i class="fas fa-mouse-pointer"></i>
|
||||
<h3>点击饼图扇区查看详情</h3>
|
||||
<p>点击左侧饼图的任何部分将会在此显示详细信息</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="info-content">
|
||||
<div class="info-card">
|
||||
<div class="info-title">
|
||||
<div class="info-icon" :style="{ background: clickData.color }">
|
||||
<i :class="getIcon(clickData.name)"></i>
|
||||
</div>
|
||||
<div class="info-name">{{ clickData.name }}</div>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">数值:</span>
|
||||
<span>{{ clickData.value }}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">百分比:</span>
|
||||
<span>{{ clickData.percent }}%</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">描述:</span>
|
||||
<span>{{ getDescription(clickData.name) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-item">
|
||||
<span class="info-label">所属系列:</span>
|
||||
<span>{{ clickData.seriesName }}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">数据索引:</span>
|
||||
<span>{{ clickData.dataIndex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="instructions">
|
||||
<h3><i class="fas fa-lightbulb"></i> 实现说明</h3>
|
||||
<ul>
|
||||
<li>使用 ECharts 的 <span class="highlight">chart.on('click')</span> 方法监听饼图点击事件</li>
|
||||
<li>点击事件参数包含名称(name)、值(value)、百分比(percent)等关键数据</li>
|
||||
<li>通过 Vue 的响应式特性更新点击信息展示区域</li>
|
||||
<li>添加了窗口大小变化监听器,确保图表响应式调整</li>
|
||||
<li>在组件卸载时正确销毁 ECharts 实例和事件监听器</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>Vue 3 + ECharts 饼图交互示例 | 点击饼图扇区体验交互效果</p>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp, ref, onMounted, onBeforeUnmount } = Vue;
|
||||
|
||||
createApp({
|
||||
setup() {
|
||||
const chartDom = ref(null);
|
||||
let chartInstance = null;
|
||||
const clickData = ref(null);
|
||||
|
||||
// 饼图数据
|
||||
const chartData = [
|
||||
{ value: 1048, name: '搜索引擎' },
|
||||
{ value: 735, name: '直接访问' },
|
||||
{ value: 580, name: '邮件营销' },
|
||||
{ value: 484, name: '联盟广告' },
|
||||
{ value: 300, name: '视频广告' }
|
||||
];
|
||||
|
||||
// 颜色列表
|
||||
const colors = ['#5470C6', '#91CC75', '#FAC858', '#EE6666', '#73C0DE'];
|
||||
|
||||
// 根据名称获取图标
|
||||
const getIcon = (name) => {
|
||||
const icons = {
|
||||
'搜索引擎': 'fas fa-search',
|
||||
'直接访问': 'fas fa-globe',
|
||||
'邮件营销': 'fas fa-envelope',
|
||||
'联盟广告': 'fas fa-handshake',
|
||||
'视频广告': 'fas fa-video'
|
||||
};
|
||||
return icons[name] || 'fas fa-chart-pie';
|
||||
};
|
||||
|
||||
// 获取描述信息
|
||||
const getDescription = (name) => {
|
||||
const descriptions = {
|
||||
'搜索引擎': '通过Google、百度等搜索引擎带来的流量',
|
||||
'直接访问': '用户直接输入网址或书签访问',
|
||||
'邮件营销': '通过电子邮件营销活动带来的访问',
|
||||
'联盟广告': '通过联盟广告网络获得的流量',
|
||||
'视频广告': '来自YouTube、TikTok等平台的视频广告'
|
||||
};
|
||||
return descriptions[name] || '暂无描述信息';
|
||||
};
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (!chartDom.value) return;
|
||||
|
||||
chartInstance = echarts.init(chartDom.value);
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 'center',
|
||||
formatter: (name) => {
|
||||
const data = chartData.find(item => item.name === name);
|
||||
return `${name}: ${data.value}`;
|
||||
}
|
||||
},
|
||||
color: colors,
|
||||
series: [
|
||||
{
|
||||
name: '访问来源',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['40%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c}',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '18',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: true
|
||||
},
|
||||
data: chartData
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
|
||||
// 添加点击事件监听
|
||||
chartInstance.on('click', (params) => {
|
||||
clickData.value = {
|
||||
name: params.name,
|
||||
value: params.value,
|
||||
percent: params.percent,
|
||||
color: params.color,
|
||||
seriesName: params.seriesName,
|
||||
dataIndex: params.dataIndex
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// 处理窗口大小变化
|
||||
const handleResize = () => {
|
||||
chartInstance && chartInstance.resize();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.off('click');
|
||||
chartInstance.dispose();
|
||||
}
|
||||
window.removeEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
return {
|
||||
chartDom,
|
||||
clickData,
|
||||
getIcon,
|
||||
getDescription
|
||||
};
|
||||
}
|
||||
}).mount('#app');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,692 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>JSON数据解析器 - DeepSeek风格</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, #0a192f, #0f2a4a, #0a192f);
|
||||
color: #e6f1ff;
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
padding: 30px 0;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #64ffda;
|
||||
font-size: 3rem;
|
||||
margin-bottom: 15px;
|
||||
text-shadow: 0 0 15px rgba(100, 255, 218, 0.3);
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #a8b2d1;
|
||||
font-size: 1.2rem;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));
|
||||
gap: 25px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: rgba(17, 34, 64, 0.7);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.3);
|
||||
overflow: hidden;
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
border: 1px solid rgba(100, 255, 218, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(to right, #64ffda, #0a192f);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
|
||||
border-color: rgba(100, 255, 218, 0.3);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 22px 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
border-bottom: 1px solid rgba(100, 255, 218, 0.1);
|
||||
}
|
||||
|
||||
.card-header i {
|
||||
margin-right: 15px;
|
||||
font-size: 1.5rem;
|
||||
color: #64ffda;
|
||||
}
|
||||
|
||||
.card-header h2 {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 500;
|
||||
color: #ccd6f6;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-bottom: 25px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
display: block;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 500;
|
||||
color: #a8b2d1;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.json-input {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.json-input input {
|
||||
flex: 1;
|
||||
padding: 14px 20px;
|
||||
background: rgba(10, 25, 47, 0.7);
|
||||
border: 1px solid rgba(100, 255, 218, 0.2);
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
color: #e6f1ff;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.json-input input:focus {
|
||||
outline: none;
|
||||
border-color: #64ffda;
|
||||
box-shadow: 0 0 0 3px rgba(100, 255, 218, 0.2);
|
||||
}
|
||||
|
||||
.json-input button {
|
||||
padding: 14px 24px;
|
||||
background: linear-gradient(to right, #0a192f, #112240);
|
||||
color: #64ffda;
|
||||
border: 1px solid rgba(100, 255, 218, 0.3);
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.json-input button:hover {
|
||||
background: linear-gradient(to right, #112240, #0a192f);
|
||||
border-color: #64ffda;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.json-input button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.json-preview {
|
||||
min-height: 300px;
|
||||
max-height: 400px;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
background: rgba(10, 25, 47, 0.5);
|
||||
border: 1px solid rgba(100, 255, 218, 0.1);
|
||||
font-family: 'Fira Code', monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #a8b2d1;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
position: relative;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.json-preview.error {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
.json-preview.loading::before {
|
||||
content: "加载中...";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #64ffda;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.results-container {
|
||||
min-height: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stats {
|
||||
color: #a8b2d1;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.stats span {
|
||||
color: #64ffda;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
padding: 10px 18px;
|
||||
background: rgba(10, 25, 47, 0.7);
|
||||
color: #64ffda;
|
||||
border: 1px solid rgba(100, 255, 218, 0.3);
|
||||
border-radius: 8px;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: rgba(100, 255, 218, 0.1);
|
||||
border-color: #64ffda;
|
||||
}
|
||||
|
||||
.results-list {
|
||||
flex: 1;
|
||||
background: rgba(10, 25, 47, 0.5);
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
border: 1px solid rgba(100, 255, 218, 0.1);
|
||||
overflow-y: auto;
|
||||
max-height: 350px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 14px 20px;
|
||||
margin-bottom: 12px;
|
||||
background: rgba(17, 34, 64, 0.7);
|
||||
border-radius: 8px;
|
||||
border-left: 3px solid #64ffda;
|
||||
transition: all 0.3s;
|
||||
font-family: 'Fira Code', monospace;
|
||||
color: #ccd6f6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
background: rgba(17, 34, 64, 0.9);
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.item-id {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
li:hover .item-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.item-actions button {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #64ffda;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.item-actions button:hover {
|
||||
background: rgba(100, 255, 218, 0.1);
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
color: #8892b0;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 20px;
|
||||
color: #64ffda;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
height: 6px;
|
||||
background: rgba(100, 255, 218, 0.1);
|
||||
border-radius: 3px;
|
||||
margin: 15px 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, #64ffda, #0a192f);
|
||||
border-radius: 3px;
|
||||
width: 0%;
|
||||
transition: width 0.5s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.progress-bar::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
animation: progressShimmer 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes progressShimmer {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
color: rgba(168, 178, 209, 0.7);
|
||||
padding: 30px 0;
|
||||
margin-top: 50px;
|
||||
font-size: 0.9rem;
|
||||
border-top: 1px solid rgba(100, 255, 218, 0.1);
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.logo-circle {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: linear-gradient(135deg, #64ffda, #0a192f);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo-circle i {
|
||||
font-size: 12px;
|
||||
color: #0a192f;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
background: linear-gradient(to right, #64ffda, #a8b2d1);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-content {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.json-input {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: #8892b0;
|
||||
}
|
||||
|
||||
.status-dot.connected {
|
||||
background: #64ffda;
|
||||
box-shadow: 0 0 10px rgba(100, 255, 218, 0.5);
|
||||
}
|
||||
|
||||
.status-text {
|
||||
color: #8892b0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.notification {
|
||||
position: fixed;
|
||||
top: 30px;
|
||||
right: 30px;
|
||||
background: rgba(17, 34, 64, 0.95);
|
||||
color: #64ffda;
|
||||
padding: 15px 25px;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #64ffda;
|
||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
|
||||
transform: translateX(200%);
|
||||
transition: transform 0.3s ease-out;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.notification.show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="logo">
|
||||
<div class="logo-circle">
|
||||
<i class="fas fa-code"></i>
|
||||
</div>
|
||||
<div class="logo-text">JSON解析器</div>
|
||||
</div>
|
||||
<h1>JSON数据分析工具</h1>
|
||||
<p class="subtitle">从指定URL加载JSON数据并提取is_check为true的frame_id值</p>
|
||||
</header>
|
||||
|
||||
<div class="main-content">
|
||||
<!-- JSON数据展示 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-database"></i>
|
||||
<h2>JSON数据源</h2>
|
||||
<div class="status-indicator">
|
||||
<div class="status-dot" :class="{'connected': jsonData && !error}"></div>
|
||||
<div class="status-text">{{ jsonData && !error ? '已连接' : '未连接' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="input-group">
|
||||
<label for="jsonUrl">JSON数据URL:</label>
|
||||
<div class="json-input">
|
||||
<input type="text" v-model="jsonUrl" placeholder="输入JSON数据URL..." id="jsonUrl">
|
||||
<button @click="fetchData" :disabled="loading">
|
||||
<i class="fas fa-sync-alt" :class="{'fa-spin': loading}"></i>
|
||||
{{ loading ? '加载中...' : '加载数据' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress-container" v-if="loading">
|
||||
<div class="progress-bar" :style="{ width: progress + '%' }"></div>
|
||||
</div>
|
||||
|
||||
<div class="preview-container">
|
||||
<div class="json-preview" :class="{ 'loading': loading, 'error': error }">
|
||||
<template v-if="error">
|
||||
{{ error }}
|
||||
</template>
|
||||
<template v-else-if="jsonData">
|
||||
{{ formattedJson }}
|
||||
</template>
|
||||
<template v-else-if="loading">
|
||||
<!-- 加载中状态由CSS处理 -->
|
||||
</template>
|
||||
<template v-else>
|
||||
等待加载JSON数据...
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 解析结果展示 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
<h2>解析结果</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="results-container">
|
||||
<div class="results-header">
|
||||
<div class="stats">
|
||||
共找到 <span>{{ checkedIds.length }}</span> 个符合条件的 frame_id
|
||||
<span v-if="jsonData">(共解析 {{ jsonData.length }} 个对象)</span>
|
||||
</div>
|
||||
<button class="copy-btn" @click="copyResults">
|
||||
<i class="fas fa-copy"></i> 复制结果
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="results-list">
|
||||
<ul v-if="checkedIds.length">
|
||||
<li v-for="(id, index) in checkedIds" :key="id">
|
||||
<div class="item-id">frame_id: {{ id }}</div>
|
||||
<div class="item-actions">
|
||||
<button :title="'复制 ' + id" @click="copySingleItem(id)">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-else class="empty-state">
|
||||
<i class="fas fa-file-alt"></i>
|
||||
<h3>未找到匹配的数据</h3>
|
||||
<p>请加载JSON数据并确保其中包含is_check为true的对象</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>JSON数据分析工具 © {{ currentYear }} - 基于DeepSeek风格设计</p>
|
||||
</footer>
|
||||
|
||||
<div class="notification" :class="{ show: showNotification }">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
<div>{{ notificationMessage }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
jsonData: null,
|
||||
checkedIds: [],
|
||||
jsonUrl: 'http://192.168.10.14:8123/sftp/detect_log/waiguan/yolo_log.json',
|
||||
loading: false,
|
||||
error: null,
|
||||
progress: 0,
|
||||
showNotification: false,
|
||||
notificationMessage: ''
|
||||
},
|
||||
computed: {
|
||||
currentYear() {
|
||||
return new Date().getFullYear();
|
||||
},
|
||||
formattedJson() {
|
||||
if (!this.jsonData) return '';
|
||||
return JSON.stringify(this.jsonData, null, 2);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始加载数据
|
||||
this.fetchData();
|
||||
|
||||
// 每30秒自动刷新数据
|
||||
setInterval(() => {
|
||||
if (this.jsonUrl) {
|
||||
this.fetchData();
|
||||
}
|
||||
}, 30000);
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
this.progress = 0;
|
||||
|
||||
// 模拟进度条
|
||||
const progressInterval = setInterval(() => {
|
||||
if (this.progress < 90) {
|
||||
this.progress += 10;
|
||||
}
|
||||
}, 200);
|
||||
|
||||
axios.get(this.jsonUrl)
|
||||
.then(response => {
|
||||
this.jsonData = response.data;
|
||||
this.parseData();
|
||||
this.progress = 100;
|
||||
this.showNotificationFunc('数据加载成功!');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching JSON:', error);
|
||||
this.error = '无法加载数据:' + (error.message || '网络错误或URL无效');
|
||||
this.showNotificationFunc(this.error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
clearInterval(progressInterval);
|
||||
setTimeout(() => this.progress = 0, 500);
|
||||
});
|
||||
},
|
||||
parseData() {
|
||||
this.checkedIds = [];
|
||||
if (this.jsonData && Array.isArray(this.jsonData)) {
|
||||
this.jsonData.forEach(item => {
|
||||
if (item.is_check === true && item.frame_id) {
|
||||
this.checkedIds.push(item.frame_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
copyResults() {
|
||||
if (this.checkedIds.length === 0) return;
|
||||
|
||||
const text = this.checkedIds.join(', ');
|
||||
this.copyToClipboard(text);
|
||||
this.showNotificationFunc('已复制所有frame_id到剪贴板');
|
||||
},
|
||||
copySingleItem(id) {
|
||||
this.copyToClipboard(id);
|
||||
this.showNotificationFunc(`已复制 frame_id: ${id}`);
|
||||
},
|
||||
copyToClipboard(text) {
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = text;
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
},
|
||||
showNotificationFunc(message) {
|
||||
this.notificationMessage = message;
|
||||
this.showNotification = true;
|
||||
setTimeout(() => {
|
||||
this.showNotification = false;
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue