31
0
Fork 0

feat: 首页图表开发

develop
JINGYJ 1 year ago
parent fff9fc175c
commit 4daa662ef3

@ -47,6 +47,7 @@
"path": "^0.12.7", "path": "^0.12.7",
"pinia": "^2.1.4", "pinia": "^2.1.4",
"qs": "^6.11.2", "qs": "^6.11.2",
"typeit": "^8.7.1",
"responsive-storage": "^2.2.0", "responsive-storage": "^2.2.0",
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"vue": "^3.3.4", "vue": "^3.3.4",

@ -65,6 +65,9 @@ dependencies:
sortablejs: sortablejs:
specifier: ^1.15.0 specifier: ^1.15.0
version: 1.15.0 version: 1.15.0
typeit:
specifier: ^8.7.1
version: 8.7.1
vue: vue:
specifier: ^3.3.4 specifier: ^3.3.4
version: 3.3.4 version: 3.3.4
@ -8831,6 +8834,14 @@ packages:
engines: { node: ">=8" } engines: { node: ">=8" }
dev: true dev: true
/typeit@8.7.1:
resolution:
{
integrity: sha512-Bx/O4NMz10NWh9FWYtVwV4XwGHF9UDJfpCZPJRtw2/oUcahFAStU8J0t19aroPfTV6s1UlS5ICoqilOqmEnh2Q==
}
requiresBuild: true
dev: false
/typescript@5.0.4: /typescript@5.0.4:
resolution: resolution:
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,10 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="16" cy="16" r="16" fill="url(#paint0_linear_1959_23869)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.3993 8.80005C9.51566 8.80005 8.79932 9.51641 8.79932 10.4V21.6C8.79932 22.4837 9.51566 23.2 10.3993 23.2H21.5993C22.483 23.2 23.1993 22.4837 23.1993 21.6V10.4C23.1993 9.51641 22.483 8.80005 21.5993 8.80005H10.3993ZM12.7993 16.8C12.7993 16.3582 12.4411 16 11.9993 16C11.5575 16 11.1993 16.3582 11.1993 16.8V20C11.1993 20.4419 11.5575 20.8 11.9993 20.8C12.4411 20.8 12.7993 20.4419 12.7993 20V16.8ZM15.9993 13.6C16.4411 13.6 16.7993 13.9582 16.7993 14.4V20C16.7993 20.4419 16.4411 20.8 15.9993 20.8C15.5575 20.8 15.1993 20.4419 15.1993 20V14.4C15.1993 13.9582 15.5575 13.6 15.9993 13.6ZM20.7993 12C20.7993 11.5582 20.4411 11.2 19.9993 11.2C19.5575 11.2 19.1993 11.5582 19.1993 12V20C19.1993 20.4419 19.5575 20.8 19.9993 20.8C20.4411 20.8 20.7993 20.4419 20.7993 20V12Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_1959_23869" x1="16" y1="0" x2="16" y2="32" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFBC43"/>
<stop offset="1" stop-color="#FF850C"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,22 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" fill="#EEEEEE"/>
<g clip-path="url(#clip)">
<rect width="1440" height="900" transform="translate(-1048 -108)" fill="#F5F5F5"/>
<rect x="-16" y="-16" width="384" height="156" rx="8" fill="white"/>
<circle cx="16" cy="16" r="16" fill="url(#linear)"/>
<path d="M22.6668 9.66675H9.3335V18.3334H22.6668V9.66675Z" fill="white" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.3335 12.6667L17.3335 14.0001L15.3335 15.3334V12.6667Z" fill="white" stroke="#2ED7D7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6667 21.3333H10" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.6665 21.3333H21.9998" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.6665 21.3333C13.6665 21.8856 13.2188 22.3333 12.6665 22.3333C12.1142 22.3333 11.6665 21.8856 11.6665 21.3333C11.6665 20.781 12.1142 20.3333 12.6665 20.3333C13.2188 20.3333 13.6665 20.781 13.6665 21.3333Z" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs id="linear">
<linearGradient id="linear" x1="4.5" y1="7" x2="27.5" y2="30" gradientUnits="userSpaceOnUse">
<stop stop-color="#5FFCFC"/>
<stop offset="1" stop-color="#13C2C2"/>
</linearGradient>
<clipPath id="clip">
<rect width="1440" height="900" fill="white" transform="translate(-1048 -108)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -0,0 +1,32 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" fill="#EEEEEE"/>
<g id="&#233;&#166;&#150;&#233;&#161;&#181;" clip-path="url(#clip0_111_2646)">
<rect width="1440" height="900" transform="translate(-648 -108)" fill="#F5F5F5"/>
<g id="Group 427319220">
<g id="Group 427319136">
<rect id="Rectangle 34624181" x="-16" y="-16" width="384" height="156" rx="8" fill="white"/>
<g id="Frame 427318917">
<g id="Frame 427318916">
<g id="Icon">
<circle id="Ellipse 3" cx="16" cy="16" r="16" fill="url(#paint0_linear_111_2646)"/>
<g id="Frame">
<path id="Vector" d="M15.9999 17.1668L12.1111 14.8335L8.22217 17.1668V21.8335L12.1111 24.1668L15.9999 21.8335V17.1668Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_2" d="M23.7778 17.1668L19.8889 14.8335L16 17.1668V21.8335L19.8889 24.1668L23.7778 21.8335V17.1668Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_3" d="M19.8891 10.1668L16.0002 7.8335L12.1113 10.1668V14.8335L16.0002 17.1668L19.8891 14.8335V10.1668Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_111_2646" x1="16" y1="0" x2="16" y2="32" gradientUnits="userSpaceOnUse">
<stop stop-color="#7399FB"/>
<stop offset="1" stop-color="#1A59F8"/>
</linearGradient>
<clipPath id="clip0_111_2646">
<rect width="1440" height="900" fill="white" transform="translate(-648 -108)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@ -0,0 +1,44 @@
import { h, defineComponent } from "vue";
import TypeIt from "typeit";
// 打字机效果组件(只是简单的封装下,更多配置项参考 https://www.typeitjs.com/docs/vanilla/usage#options
export default defineComponent({
name: "TypeIt",
props: {
/** 打字速度,以每一步之间的毫秒数为单位,默认`200` */
speed: {
type: Number,
default: 200
},
values: {
type: Array,
defalut: []
},
className: {
type: String,
default: "type-it"
},
cursor: {
type: Boolean,
default: true
}
},
render() {
return h(
"span",
{
class: this.className
},
{
default: () => []
}
);
},
mounted() {
new TypeIt(`.${this.className}`, {
strings: this.values,
speed: this.speed,
cursor: this.cursor
}).go();
}
});

@ -5,11 +5,11 @@ import ElementPlus from "element-plus";
import { getServerConfig } from "./config"; import { getServerConfig } from "./config";
import { createApp, Directive } from "vue"; import { createApp, Directive } from "vue";
import { MotionPlugin } from "@vueuse/motion"; import { MotionPlugin } from "@vueuse/motion";
// import { useEcharts } from "@/plugins/echarts"; import { useEcharts } from "@/plugins/echarts";
import { injectResponsiveStorage } from "@/utils/responsive"; import { injectResponsiveStorage } from "@/utils/responsive";
// import Table from "@pureadmin/table"; import Table from "@pureadmin/table";
// import PureDescriptions from "@pureadmin/descriptions"; import PureDescriptions from "@pureadmin/descriptions";
// 引入重置样式 // 引入重置样式
import "./style/reset.scss"; import "./style/reset.scss";
@ -49,9 +49,11 @@ getServerConfig(app).then(async config => {
await router.isReady(); await router.isReady();
injectResponsiveStorage(app, config); injectResponsiveStorage(app, config);
setupStore(app); setupStore(app);
app.use(MotionPlugin).use(ElementPlus); app
// .use(useEcharts); .use(MotionPlugin)
// .use(Table); .use(ElementPlus)
// .use(PureDescriptions); .use(useEcharts)
.use(Table)
.use(PureDescriptions);
app.mount("#app"); app.mount("#app");
}); });

@ -1,7 +1,7 @@
import type { App } from "vue"; import type { App } from "vue";
import * as echarts from "echarts/core"; import * as echarts from "echarts/core";
import { CanvasRenderer } from "echarts/renderers"; import { CanvasRenderer } from "echarts/renderers";
import { PieChart, BarChart, LineChart } from "echarts/charts"; import { PieChart, BarChart, LineChart, RadarChart } from "echarts/charts";
import { import {
GridComponent, GridComponent,
TitleComponent, TitleComponent,
@ -19,6 +19,7 @@ use([
PieChart, PieChart,
BarChart, BarChart,
LineChart, LineChart,
RadarChart,
CanvasRenderer, CanvasRenderer,
GridComponent, GridComponent,
TitleComponent, TitleComponent,

@ -13,7 +13,8 @@ export default {
name: "VideoListPage", name: "VideoListPage",
component: () => import("@/views/videoList/index.vue"), component: () => import("@/views/videoList/index.vue"),
meta: { meta: {
title: "视频分析" title: "视频分析",
keepAlive: true
} }
}, },
{ {
@ -23,7 +24,7 @@ export default {
meta: { meta: {
showLink: false, showLink: false,
activePath: "/videoList/index", activePath: "/videoList/index",
title: "视频分析" title: "视频详情"
} }
} }
] ]

@ -1,40 +1,106 @@
.wave { .wave {
position: absolute;
width: 100%;
height: 100%;
left: 0;
bottom: 0;
z-index: 1;
}
.image_bg {
position: fixed; position: fixed;
width: 100%;
height: 100%; height: 100%;
left: 0; left: 0;
bottom: 0; bottom: 0;
z-index: -1; z-index: -2;
background: rgb(5, 40, 96);
}
.logo-small {
position: fixed;
/* width: 100%;
height: 100%; */
left: 10%;
top: 10%;
} }
.login-container { .login-container {
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
display: grid; /* display: grid;
/* grid-template-columns: repeat(2, 1fr); */ grid-template-columns: repeat(2, 1fr);
align-items: center;
justify-content: center;
grid-gap: 18rem; grid-gap: 18rem;
padding: 0 2rem; align-items: start; */
display: flex;
justify-content: center;
align-items: center;
/* padding: 0 2rem; */
/* justify-content: end; */
} }
.img { .img {
width: 35%;
/* height: 60vh; */
height: 556px;
display: flex; display: flex;
justify-content: flex-end; /* justify-content: flex-end; */
flex-direction: column;
justify-content: center;
align-items: center; align-items: center;
/* background-image: url("../assets/login/login-bg.png");
background-repeat: no-repeat;
background-size: cover;
background-position: left center; */
/* width: 500px; */
/* height: 380px; */
/* background: rgba( 224, 237, 255, 1); */
/* background-image: url('../assets/login/mapBg.png'); */
background-image: url("../assets/login/mapBg_cz.png");
border-radius: 16px 0 0 16px;
} }
.describe {
.img img { /* margin: 32px 0 64px; */
width: 500px; /* width: 480px; */
/* font-family: PingFang SC; */
/* font-size: 48px; */
/* font-weight: 400; */
height: 54px;
/* letter-spacing: 0em; */
text-align: left;
font-family: Open Sans;
font-size: 40px;
font-weight: 700;
line-height: 54px;
color: #fff;
}
.platform-logo {
width: 464px;
height: 68px;
/* text-align: left; */
} }
/* .img img {
width: 500px;
} */
.login-box { .login-box {
padding: 25px 0 24px;
width: 43%;
/* height: 60vh; */
height: 556px;
display: flex; display: flex;
flex-direction: column;
justify-content: center;
align-items: center; align-items: center;
text-align: center; text-align: center;
background: rgb(255, 255, 255);
background-image: url("../assets/login/right_cz.png");
background-position: 100% 100%;
background-repeat: no-repeat;
border-radius: 0 16px 16px 0;
} }
.login-form { .login-form {
width: 360px; margin-bottom: 130px;
width: 80%;
} }
.avatar { .avatar {
@ -45,8 +111,31 @@
.login-form h2 { .login-form h2 {
text-transform: uppercase; text-transform: uppercase;
margin: 15px 0; margin: 15px 0;
/* color: #999;
font: bold 200% Consolas, Monaco, monospace; */
/* font-family: "PingFang SC";
font-size: 32px;
font-weight: 400;
line-height: 45px;
letter-spacing: 0em;
text-align: left; */
height: 45px;
font-family: PingFang SC;
font-size: 32px;
font-weight: bold;
line-height: 45px;
text-align: left;
color: #333;
}
.login-form-footer {
font-family: PingFang SC;
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0em;
color: #999; color: #999;
font: bold 200% Consolas, Monaco, monospace; text-align: left;
} }
@media screen and (max-width: 1180px) { @media screen and (max-width: 1180px) {

@ -3,20 +3,30 @@ import Motion from "./utils/motion";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { message } from "@/utils/message"; import { message } from "@/utils/message";
import { loginRules } from "./utils/rule"; import { loginRules } from "./utils/rule";
import { useNav } from "@/layout/hooks/useNav"; // import { useNav } from "@/layout/hooks/useNav";
import type { FormInstance } from "element-plus"; import type { FormInstance } from "element-plus";
import { useLayout } from "@/layout/hooks/useLayout"; import { useLayout } from "@/layout/hooks/useLayout";
import { useUserStoreHook } from "@/store/modules/user"; import { useUserStoreHook } from "@/store/modules/user";
import { initRouter, getTopMenu } from "@/router/utils"; import { initRouter, getTopMenu } from "@/router/utils";
import { logo } from "./utils/static"; import { bg_cz, leftLogo_cz } from "./utils/static";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { ref, reactive, onMounted, onBeforeUnmount } from "vue"; // import update from "./components/update.vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange"; import {
ref,
reactive,
onMounted,
onBeforeUnmount,
toRaw,
nextTick
} from "vue";
// import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import dayIcon from "@/assets/svg/day.svg?component"; // import dayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component"; // import darkIcon from "@/assets/svg/dark.svg?component";
import Lock from "@iconify-icons/ri/lock-fill"; import Lock from "@iconify-icons/icon-park-outline/lock";
import User from "@iconify-icons/ri/user-3-fill"; import User from "@iconify-icons/icon-park-outline/user";
import PreviewClose from "@iconify-icons/icon-park-outline/preview-close";
import PreviewOpen from "@iconify-icons/icon-park-outline/preview-open";
defineOptions({ defineOptions({
name: "Login" name: "Login"
@ -24,13 +34,17 @@ defineOptions({
const router = useRouter(); const router = useRouter();
const loading = ref(false); const loading = ref(false);
const ruleFormRef = ref<FormInstance>(); const ruleFormRef = ref<FormInstance>();
// const checked = ref(false);
// const currentPage = computed(() => {
// return useUserStoreHook().currentPage;
// });
const { initStorage } = useLayout(); const { initStorage } = useLayout();
initStorage(); initStorage();
const { dataTheme, dataThemeChange } = useDataThemeChange(); // const { dataTheme, dataThemeChange } = useDataThemeChange();
dataThemeChange(); // dataThemeChange();
const { title } = useNav(); // const { title } = useNav();
const ruleForm = reactive({ const ruleForm = reactive({
username: "admin", username: "admin",
@ -55,9 +69,12 @@ const onLogin = async (formEl: FormInstance | undefined) => {
message("登录成功", { type: "success" }); message("登录成功", { type: "success" });
}); });
} else { } else {
loading.value = false; message("登录失败", { type: "error" });
return fields;
} }
})
.catch(() => {
loading.value = false;
message("登录失败", { type: "error" });
}); });
} else { } else {
loading.value = false; loading.value = false;
@ -65,7 +82,8 @@ const onLogin = async (formEl: FormInstance | undefined) => {
} }
}); });
}; };
const passwordType = ref("password");
const refInput = ref();
/** 使用公共函数,避免`removeEventListener`失效 */ /** 使用公共函数,避免`removeEventListener`失效 */
function onkeypress({ code }: KeyboardEvent) { function onkeypress({ code }: KeyboardEvent) {
if (code === "Enter") { if (code === "Enter") {
@ -73,9 +91,19 @@ function onkeypress({ code }: KeyboardEvent) {
} }
} }
function showPass() {
if (passwordType.value === "password") {
passwordType.value = "text";
} else {
passwordType.value = "password";
}
nextTick(() => {
refInput.value.focus();
});
}
onMounted(() => { onMounted(() => {
window.document.addEventListener("keypress", onkeypress); window.document.addEventListener("keypress", onkeypress);
console.log(logo);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
@ -85,26 +113,43 @@ onBeforeUnmount(() => {
<template> <template>
<div class="select-none"> <div class="select-none">
<div class="image_bg">
<!-- <img :src="bg" class="wave" /> --> <!-- <img :src="bg" class="wave" /> -->
<div class="flex-c absolute right-5 top-3"> <img :src="bg_cz" class="wave" />
</div>
<!-- <div class="logo-small">
<component :is="toRaw(sst)" />
</div> -->
<!-- <img :src="sst" class="logo-small" /> -->
<!-- <div class="flex-c absolute right-5 top-3"> -->
<!-- 主题 --> <!-- 主题 -->
<el-switch <!-- <el-switch
v-model="dataTheme" v-model="dataTheme"
inline-prompt inline-prompt
:active-icon="dayIcon" :active-icon="dayIcon"
:inactive-icon="darkIcon" :inactive-icon="darkIcon"
@change="dataThemeChange" @change="dataThemeChange"
/> /> -->
</div> <!-- </div> -->
<div class="login-container"> <div class="login-container">
<!-- <div class="img"> <div class="img">
<component :is="toRaw(illustration)" /> <!-- <div class="platform-logo">
<img :src="sst" />
</div> --> </div> -->
<!-- <component :is="toRaw(leftLogo)" /> -->
<component :is="toRaw(leftLogo_cz)" />
<!-- <component :is="toRaw(illustration)" /> -->
</div>
<div class="login-box"> <div class="login-box">
<div class="login-form"> <div class="login-form">
<logo class="logo" /> <!-- <avatar class="avatar" /> -->
<Motion> <Motion>
<h2 class="outline-none">{{ title }}</h2> <h2
class="outline-none"
style="font-size: 32px; font-weight: 400; text-align: center"
>
{{ "欢迎登录执法视频智能解析系统" }}
</h2>
</Motion> </Motion>
<el-form <el-form
@ -125,7 +170,6 @@ onBeforeUnmount(() => {
prop="username" prop="username"
> >
<el-input <el-input
clearable
v-model="ruleForm.username" v-model="ruleForm.username"
placeholder="账号" placeholder="账号"
:prefix-icon="useRenderIcon(User)" :prefix-icon="useRenderIcon(User)"
@ -136,20 +180,53 @@ onBeforeUnmount(() => {
<Motion :delay="150"> <Motion :delay="150">
<el-form-item prop="password"> <el-form-item prop="password">
<el-input <el-input
clearable ref="refInput"
show-password :type="passwordType"
v-model="ruleForm.password" v-model="ruleForm.password"
placeholder="密码" placeholder="密码"
:prefix-icon="useRenderIcon(Lock)" :prefix-icon="useRenderIcon(Lock)"
>
<template #suffix>
<el-icon
class="el-icon el-input__icon el-input__password"
@click="showPass"
>
<IconifyIconOffline
:icon="
passwordType == 'password'
? PreviewClose
: PreviewOpen
"
/> />
</el-icon>
</template>
</el-input>
</el-form-item> </el-form-item>
</Motion> </Motion>
<Motion :delay="250"> <Motion :delay="250">
<!-- <div class="w-full h-[20px] flex justify-between items-center">
<el-checkbox v-model="checked">
{{ "记住密码" }}
</el-checkbox>
<el-button link class="btn-color" type="primary">
{{ "忘记密码?" }}
</el-button>
</div> -->
<!-- <el-button
class="w-full mt-7 login-btn"
size="default"
type="primary"
color="#0457A9"
:loading="loading"
@click="onLogin(ruleFormRef)"
>
登录
</el-button> -->
<el-button <el-button
class="w-full mt-4" class="w-full mt-7 login-btn"
size="default" size="default"
type="primary" type="primary"
color="#004FB2"
:loading="loading" :loading="loading"
@click="onLogin(ruleFormRef)" @click="onLogin(ruleFormRef)"
> >
@ -157,6 +234,8 @@ onBeforeUnmount(() => {
</el-button> </el-button>
</Motion> </Motion>
</el-form> </el-form>
<!-- 忘记密码 -->
<!-- <update v-if="currentPage === 4" /> -->
</div> </div>
</div> </div>
</div> </div>
@ -172,7 +251,28 @@ onBeforeUnmount(() => {
padding: 0; padding: 0;
} }
.logo { .login-btn {
margin: 0 auto; height: 40px;
font-weight: 600;
// font-family: "PingFang SC";
color: #fff;
cursor: pointer;
}
.btn-color {
color: #0457a9;
}
.btn-color:hover {
color: #0457a9;
opacity: 0.5;
}
.el-input__icon {
cursor: pointer;
} }
// :deep(.el-button) {
// font-weight: bold;
// color: #fff;
// }
</style> </style>

@ -1,6 +1,9 @@
import bg from "@/assets/login/bg.png"; import bg from "@/assets/login/bg.png";
import bg_cz from "@/assets/login/bg_cz.png";
import leftLogo from "@/assets/login/leftLogo.svg?component";
import leftLogo_cz from "@/assets/login/leftLogo_cz.svg?component";
import avatar from "@/assets/login/avatar.svg?component"; import avatar from "@/assets/login/avatar.svg?component";
import illustration from "@/assets/login/illustration.svg?component";
import logo from "@/assets/login/logo.svg?component"; import logo from "@/assets/login/logo.svg?component";
import illustration from "@/assets/login/illustration.svg?component";
export { bg, avatar, illustration, logo }; export { bg, avatar, illustration, leftLogo, logo, bg_cz, leftLogo_cz };

@ -8,7 +8,7 @@ import { useRouter } from "vue-router";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
defineOptions({ defineOptions({
name: "VideoList" name: "VideoListPage"
}); });
const formInline = reactive({ const formInline = reactive({
@ -62,25 +62,25 @@ const columns: TableColumnList = [
}, },
{ {
label: "视频名称", label: "视频名称",
// width: 200, width: 300,
prop: "video_name" prop: "video_name"
}, },
{ {
label: "日期", label: "日期",
prop: "video_date", prop: "video_date",
// width: 140, width: 100,
sortable: true sortable: true
}, },
{ {
label: "异常数量", label: "异常数量",
// width: 100, width: 80,
prop: "abnormal_count" prop: "abnormal_count"
}, },
{ {
label: "异常表现", label: "异常表现",
prop: "abnormal_performance", prop: "abnormal_performance",
width: 370, width: 400,
slot: "violation" slot: "violation"
}, },
{ {
@ -90,7 +90,7 @@ const columns: TableColumnList = [
{ {
label: "操作", label: "操作",
fixed: "right", fixed: "right",
// width: 160, width: 100,
slot: "operation" slot: "operation"
} }
]; ];

@ -1,129 +1,25 @@
<script setup lang="ts"> <script setup lang="ts">
import { useWindowSize } from "@vueuse/core"; import { useWindowSize } from "@vueuse/core";
// import { Help } from "@element-plus/icons-vue";
import { ref, reactive, onMounted } from "vue"; import { ref, reactive, onMounted } from "vue";
// import { PureTableBar } from "@/components/RePureTableBar";
import { PureTable } from "@pureadmin/table"; import { PureTable } from "@pureadmin/table";
import { type PaginationProps } from "@pureadmin/table"; import { type PaginationProps } from "@pureadmin/table";
// import { getToken } from "@/utils/auth";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { videoUtil } from "./hook"; import { videoUtil } from "./hook";
// import { baseUrlApi } from "@/api/utils";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { isEmpty } from "@pureadmin/utils"; import { isEmpty } from "@pureadmin/utils";
import { Back } from "@element-plus/icons-vue"; import { Back } from "@element-plus/icons-vue";
import { import { getPicList, getTPList } from "@/api/videoParse";
getPicList,
getTPList
// submitAnalyse,
// beforeUploadVideo
} from "@/api/videoParse";
// import { fa } from "element-plus/es/locale";
// import type {
// UploadProps,
// UploadUserFile,
// UploadInstance,
// UploadRawFile
// } from "element-plus";
defineOptions({ defineOptions({
name: "videoParse" name: "videoParse"
}); });
const { columns, openDialog } = videoUtil(); const { columns, openDialog } = videoUtil();
// const fileList = ref<UploadUserFile[]>([]);
// const uploadFile = ref<UploadInstance>();
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const getParameter = isEmpty(route.params) ? route.query : route.params; const getParameter = isEmpty(route.params) ? route.query : route.params;
// console.log(getParameter); // console.log(getParameter);
// const submitUpload = () => {
// uploadFile.value!.submit();
// };
// const handleRemove: UploadProps["onRemove"] = (file, uploadFiles) => {
// console.log("", file, uploadFiles);
// };
// const handlePreview: UploadProps["onPreview"] = uploadFile => {
// console.log("", uploadFile);
// };
// const handleExceed: UploadProps["onExceed"] = files => {
// // ElMessage.warning(
// // `${fileList.value[0].name}`
// // );
// ElMessageBox.confirm(` ${fileList.value[0].name} ?`)
// .then(() => {
// console.log("");
// uploadFile.value!.clearFiles();
// const file = files[0] as UploadRawFile;
// file.uid = genFileId();
// uploadFile.value!.handleStart(file);
// })
// .catch(() => {
// console.log("");
// submitUpload();
// });
// };
// const handleChange: UploadProps["onChange"] = files => {
// if (files.status === "ready") {
// const fileName = files.name.substring(files.name.lastIndexOf(".") + 1);
// if (
// fileName === "mp4" ||
// fileName === "MP4" ||
// fileName === "avi" ||
// fileName === "AVI" ||
// fileName === "mkv" ||
// fileName === "MKV" ||
// fileName === "mov" ||
// fileName === "MOV"
// ) {
// const params = { video_name: files.name };
// beforeUploadVideo(params).then(response => {
// if (!response.success) {
// if (!(response.code === 201)) {
// fileList.value = [];
// ElMessage.warning(`${response.msg}`);
// return false;
// } else {
// ElMessage.warning(`${response.msg}`);
// }
// } else {
// console.log("");
// submitUpload();
// }
// });
// } else {
// ElMessage.warning(`${fileName}`);
// fileList.value = [];
// return false;
// }
// }
// };
// const successCallback: UploadProps["onSuccess"] = (
// response,
// uploadFile,
// uploadFiles
// ) => {
// if (!response.success) {
// fileList.value = [];
// ElMessage.warning(`${response.msg}`);
// handleRemove(uploadFile, uploadFiles);
// }
// };
// const beforeRemove: UploadProps["beforeRemove"] = uploadFile => {
// console.log("", uploadFile);
// return ElMessageBox.confirm(` ${uploadFile.name} ?`).then(
// () => true,
// () => false
// );
// };
const { height } = useWindowSize(); const { height } = useWindowSize();
const videoData = ref([]); const videoData = ref([]);
@ -215,18 +111,18 @@ function onSearch() {
}); });
} }
function spanStyle(type) { // function spanStyle(type) {
switch (type) { // switch (type) {
case 1: // case 1:
return "span-first"; // return "span-first";
case 2: // case 2:
return "span-second"; // return "span-second";
case 3: // case 3:
return "span-third"; // return "span-third";
default: // default:
return "span-default"; // return "span-default";
} // }
} // }
function tagStyle(type) { function tagStyle(type) {
switch (type) { switch (type) {
@ -253,23 +149,6 @@ function getRandType(type) {
} }
} }
// function startAnalyse() {
// let video_name = undefined;
// if (fileList.value.length > 0) {
// video_name = fileList.value[0].name;
// }
// const params = {
// video_name: video_name
// };
// submitAnalyse(params).then(response => {
// if (!response.success) {
// ElMessage.warning(`${response.msg}`);
// } else {
// onSearch();
// }
// });
// }
onMounted(() => { onMounted(() => {
onSearch(); onSearch();
// setInterval(() => { // setInterval(() => {
@ -298,253 +177,12 @@ onMounted(() => {
shadow="never" shadow="never"
:style="{ height: `calc(${height}px - 140px)` }" :style="{ height: `calc(${height}px - 140px)` }"
> >
<!-- <template #header> <div class="w-full h-[58px] flex items-center">
<div class="card-header"> <h4>异常标签</h4>
<span>视频分析</span>
</div>
</template>
<div class="file-upload flex justify-around">
<div class="upload-box">
<el-upload
v-model:file-list="fileList"
class="upload"
:headers="{ token: 'Bearer ' + getToken().accessToken }"
:action="baseUrlApi('tps/upload_video/')"
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
:limit="1"
:on-exceed="handleExceed"
:on-success="successCallback"
:on-change="handleChange"
ref="uploadFile"
:auto-upload="false"
accept=".mp4, .MP4, .avi, .AVI, .mkv, .MKV, .mov, .MOV"
>
<span>{{ "选择分析文件" }}</span
><el-button type="primary">选择文件</el-button> -->
<!-- <template #tip>
<div class="el-upload__tip">
jpg/png files with a size less than 500KB.
</div>
</template> -->
<!-- </el-upload>
</div>
<el-button type="success" size="large" @click="startAnalyse">
分析
<el-icon class="el-icon--right">
<Help />
</el-icon>
</el-button>
</div>
<el-divider border-style="dashed" /> -->
<!-- <el-empty description="暂无数据" v-show="videoData.length == 0" /> -->
<div class="analysis-content flex justify-between">
<div
class="analysis-box"
:style="{ height: hightFlag ? `360px` : `270px` }"
>
<span class="analysis-box-label">被执法人员推定</span>
<div class="analysis-table">
<div class="analysis-table-left">
<div class="analysis-box-lable w-full">
<span class="w-[40px]" />
<span class="w-[120px]" />
<span class="ml-6">目标清晰度</span>
</div>
<el-scrollbar :height="hightFlag ? `270px` : `180px`">
<div
v-for="(item, index) in personOddList"
:key="index"
class="scrollbar-item"
>
<span :class="spanStyle(item.id)">{{
2 * index + 1
}}</span>
<div class="defect-image">
<el-image
:src="item.pic_path"
:zoom-rate="1.2"
:preview-src-list="[item.pic_path]"
fit="cover"
class="w-[100%] h-[100%] rounded"
/>
</div>
<span>{{ item.pic_quality }}</span>
</div>
</el-scrollbar>
</div>
<!-- <el-divider
direction="vertical"
:style="{ height: `calc(${height}px - 60vh - 220px)` }"
/> -->
<div class="analysis-table-left">
<div class="analysis-box-lable w-full flex justify-end">
<span class="w-[40px]" />
<span class="w-[120px]" />
<span class="ml-1">目标清晰度</span>
</div>
<el-scrollbar :height="hightFlag ? `270px` : `180px`">
<div
v-for="(item, index) in personEvenList"
:key="index"
class="scrollbar-item"
>
<span :class="spanStyle(item.id)">{{
2 * index + 2
}}</span>
<div class="defect-image">
<el-image
:src="item.pic_path"
:zoom-rate="1.2"
:preview-src-list="[item.pic_path]"
fit="cover"
class="w-[100%] h-[100%] rounded"
/>
</div>
<span>{{ item.pic_quality }}</span>
</div>
</el-scrollbar>
</div>
</div>
</div>
<div
class="analysis-box"
:style="{ height: hightFlag ? `360px` : `270px` }"
>
<span class="analysis-box-label">被执法车辆推定</span>
<div class="analysis-table">
<div class="analysis-table-left">
<div class="analysis-box-lable w-full flex justify-end">
<span class="w-[40px]" />
<span class="w-[120px]" />
<span class="ml-1">目标清晰度</span>
</div>
<el-scrollbar :height="hightFlag ? `270px` : `180px`">
<div
v-for="(item, index) in carOddList"
:key="index"
class="scrollbar-item"
>
<span :class="spanStyle(item.id)">{{
2 * index + 1
}}</span>
<div class="defect-image">
<el-image
:src="item.pic_path"
:zoom-rate="1.2"
:preview-src-list="[item.pic_path]"
fit="cover"
class="w-[100%] h-[100%] rounded"
/>
<span>{{ item.car_number }}</span>
</div>
<span>{{ item.pic_quality }}</span>
</div>
</el-scrollbar>
</div>
<!-- <el-divider
direction="vertical"
:style="{ height: `calc(${height}px - 60vh - 240px)` }"
/> -->
<div class="analysis-table-left">
<div class="analysis-box-lable w-full flex justify-end">
<span class="w-[40px]" />
<span class="w-[120px]" />
<span class="ml-1">目标清晰度</span>
</div>
<el-scrollbar :height="hightFlag ? `270px` : `180px`">
<div
v-for="(item, index) in carEvenList"
:key="index"
class="scrollbar-item"
>
<span :class="spanStyle(item.id)">{{
2 * index + 2
}}</span>
<div class="defect-image">
<el-image
:src="item.pic_path"
:zoom-rate="1.2"
:preview-src-list="[item.pic_path]"
fit="cover"
class="w-[100%] h-[100%] rounded"
/>
<span>{{ item.car_number }}</span>
</div>
<span>{{ item.pic_quality }}</span>
</div>
</el-scrollbar>
</div>
</div>
</div> </div>
<div class="w-full h-[58px]">
<span :class="tagStyle('疑似遮挡')">{{ "疑似遮挡" }}</span>
</div> </div>
<!-- <PureTableBar
title="视频分析列表"
:columns="columns"
@refresh="onSearch"
>
<template v-slot="{ size, dynamicColumns }">
<pure-table
adaptive
align-whole="left"
table-layout="auto"
:loading="loading"
:size="size"
:data="videoData"
:columns="dynamicColumns"
:pagination="pagination"
:paginationSmall="size === 'small' ? true : false"
:header-cell-style="{
background: 'var(--el-table-row-hover-bg-color)',
color: 'var(--el-text-color-primary)'
// color: 'blue'
}"
@page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange"
>
<template #behavior="{ row }">
<span :class="tagStyle(row.abnormal_behavior)">{{
row.abnormal_behavior
}}</span>
</template>
<template #image="{ row, index }">
<el-image
preview-teleported
loading="lazy"
:src="row.abnormal_pic"
:preview-src-list="[row.abnormal_pic]"
:initial-index="index"
fit="cover"
class="h-[56px]"
/>
</template>
<template #exception_type="{ row }">
<span
:class="
row.exception_type == '异常1'
? 'isbehavior'
: 'default-behavior'
"
:type="getRandType(row.exception_type)"
>
{{ row.exception_type }}
</span>
</template>
<template #video="{ row }">
<el-button
class="reset-margin"
link
type="primary"
:size="size"
@click="handleUpdate(row)"
>
播放视频
</el-button>
</template>
</pure-table>
</template>
</PureTableBar> -->
<div class="w-full h-[58px] flex items-center"> <div class="w-full h-[58px] flex items-center">
<h4>视频分析列表</h4> <h4>视频分析列表</h4>
</div> </div>
@ -584,7 +222,7 @@ onMounted(() => {
<template #exception_type="{ row }"> <template #exception_type="{ row }">
<span <span
:class=" :class="
row.exception_type == '异常1' row.exception_type == '行为异常'
? 'isbehavior' ? 'isbehavior'
: 'default-behavior' : 'default-behavior'
" "
@ -800,6 +438,7 @@ onMounted(() => {
width: 64px; width: 64px;
height: 24px; height: 24px;
font-size: 12px; font-size: 12px;
line-height: 22px;
color: rgb(247 95 25 / 100%); color: rgb(247 95 25 / 100%);
text-align: center; text-align: center;
background: rgb(247 112 49 / 10%); background: rgb(247 112 49 / 10%);

@ -0,0 +1,117 @@
<script setup lang="ts">
import { ref, computed, watch, type Ref } from "vue";
import { useAppStoreHook } from "@/store/modules/app";
import {
delay,
useDark,
useECharts,
type EchartOptions
} from "@pureadmin/utils";
import * as echarts from "echarts/core";
const { isDark } = useDark();
const theme: EchartOptions["theme"] = computed(() => {
return isDark.value ? "dark" : "light";
});
const barChartRef = ref<HTMLDivElement | null>(null);
const { setOptions, resize } = useECharts(barChartRef as Ref<HTMLDivElement>, {
theme
});
setOptions(
{
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow"
}
},
grid: {
left: "5%",
right: "0%",
top: "5%",
bottom: "6%"
},
legend: {
show: false,
//@ts-expect-error
right: true,
data: ["watchers", "fork", "star"]
},
xAxis: [
{
type: "category",
axisTick: {
alignWithLabel: true
},
axisLabel: {
interval: 0
// width: "70",
// overflow: "truncate"
},
data: [
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月"
],
triggerEvent: true
}
],
yAxis: [
{
type: "value",
triggerEvent: true
}
],
series: [
{
name: "违规数量",
type: "bar",
barWidth: "30%",
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "#0463F2"
},
{
offset: 1,
color: "#0463F2"
}
])
},
data: [200, 320, 800, 700, 320, 600, 500, 320, 400, 1200, 20, 80]
}
],
addTooltip: true
},
{
name: "click",
callback: params => {
console.log("click", params);
}
}
);
watch(
() => useAppStoreHook().getSidebarStatus,
() => {
delay(600).then(() => resize());
}
);
</script>
<template>
<div ref="barChartRef" style="width: 100%; height: 35vh" />
</template>

@ -0,0 +1,171 @@
<script setup lang="ts">
// import { useIntervalFn } from "@vueuse/core";
import { ref, computed, watch, type Ref } from "vue";
import { useAppStoreHook } from "@/store/modules/app";
import {
delay,
useDark,
useECharts,
type EchartOptions
} from "@pureadmin/utils";
const { isDark } = useDark();
const theme: EchartOptions["theme"] = computed(() => {
return isDark.value ? "dark" : "default";
});
const lineChartRef = ref<HTMLDivElement | null>(null);
const { setOptions, resize } = useECharts(lineChartRef as Ref<HTMLDivElement>, {
theme
});
const xData = (() => {
const data: any[] = [];
for (let i = 1; i < 31; i++) {
data.push(`${i}`);
}
return data;
})();
setOptions(
{
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow"
}
},
grid: {
left: "6%",
right: "2%",
top: "5%",
bottom: "9%"
},
legend: {
show: false,
//@ts-expect-error
right: true,
data: ["fork", "star"]
},
calculable: true,
xAxis: [
{
triggerEvent: true,
type: "category",
splitLine: {
show: false
},
axisTick: {
show: false
},
data: xData
}
],
yAxis: [
{
triggerEvent: true,
type: "value",
splitLine: {
show: true,
lineStyle: {
// 线
type: "dashed", // 线
color: "#E0E0E0" // 线
}
},
axisLabel: {
show: true,
//Y
formatter: "{value}h"
},
axisLine: {
show: false
}
}
],
dataZoom: [
{
type: "slider",
show: false,
realtime: true,
startValue: 0,
endValue: 29
}
],
series: [
{
name: "耗费时长",
type: "line",
symbolSize: 6,
symbol: "circle",
color: "#477BFF",
markPoint: {
label: {
color: "#fff"
},
data: [
{
type: "max",
name: "最大值"
},
{
type: "min",
name: "最小值"
}
]
},
data: [
10, 9, 2, 2, 2, 3, 4, 5, 7, 9, 11, 20, 14, 12, 7, 3, 4, 5, 8, 2, 6, 3,
2, 10, 9, 5, 10, 9, 4, 8
]
}
],
addTooltip: true
},
{
name: "click",
callback: params => {
console.log("click", params);
}
},
{
name: "contextmenu",
callback: params => {
console.log("contextmenu", params);
}
},
//
{
type: "zrender",
name: "click",
callback: params => {
console.log("点击空白处", params);
}
}
);
// const a = 1;
// useIntervalFn(() => {
// if (a == xData.length - 24) {
// a = 0;
// }
// getInstance()!.dispatchAction({
// type: "dataZoom",
// startValue: a,
// endValue: a + 24
// });
// a++;
// }, 2000);
watch(
() => useAppStoreHook().getSidebarStatus,
() => {
delay(600).then(() => resize());
}
);
</script>
<template>
<div ref="lineChartRef" style="width: 100%; height: 25vh" />
</template>

@ -0,0 +1,180 @@
<script setup lang="ts">
import { ref, computed, watch, type Ref } from "vue";
import { useAppStoreHook } from "@/store/modules/app";
import {
delay,
useDark,
useECharts,
type EchartOptions
} from "@pureadmin/utils";
const { isDark } = useDark();
const theme: EchartOptions["theme"] = computed(() => {
return isDark.value ? "dark" : "light";
});
const pieChartRef = ref<HTMLDivElement | null>(null);
const { setOptions, resize } = useECharts(pieChartRef as Ref<HTMLDivElement>, {
theme
});
const trafficWay = [
{ value: 1600, name: "正常视频" },
{ value: 400, name: "无效视频" }
];
const mapData = [];
const color = ["#FFAB00", "#3B3EFF"];
const ColorCs = () => {
for (let i = 0; i < trafficWay.length; i++) {
mapData.push({
value: trafficWay[i].value,
name: trafficWay[i].name,
label: {
rich: {
percent: {
color: color[i]
}
}
}
});
}
return mapData;
};
setOptions(
{
tooltip: {
trigger: "item"
},
legend: {
show: false,
icon: "circle",
//@ts-expect-error
right: true
},
series: [
{
name: "视频分析",
type: "pie",
top: "10%",
bottom: "10%",
radius: "80%",
center: ["50%", "50%"],
color: ["#FFAB00", "#3B3EFF"],
// roseType: "radius",
label: {
formatter: function (params) {
let percent = "0";
let total = 0;
for (let i = 0; i < trafficWay.length; i++) {
total += trafficWay[i].value;
}
percent = (((params.value as any) / total) * 100).toFixed(0);
if (params.name !== "") {
// return params.name + '\n' + percent + '%';
return (
"{percent|" + percent + "%}" + "\n{name|" + params.name + "}"
);
} else {
return "";
}
},
padding: [0, -55],
rich: {
name: {
fontSize: 12,
padding: [5, 0],
align: "left"
},
percent: {
fontSize: 14,
align: "left",
fontWeight: "bolder",
padding: [5, 0]
}
}
},
labelLine: {
length: 30,
length2: 90 //线
},
data: ColorCs()
// emphasis: {
// itemStyle: {
// shadowBlur: 10,
// shadowOffsetX: 0,
// shadowColor: "rgba(0, 0, 0, 0.5)"
// }
// }
},
{
name: "",
type: "pie",
color: ["#155BD4", "rgba(255,255,255,0)"],
center: ["50%", "50%"],
radius: ["76%", "78%"],
avoidLabelOverlap: false,
// hoverAnimation: false,
tooltip: {
show: false
},
label: {
show: false,
position: "center"
},
emphasis: {
//
label: {
show: true //
},
labelLine: {
show: true, //线
// length: 50,
lineStyle: {
width: 1,
type: "solid"
}
}
},
labelLine: {
// normal: {
show: false
// }
},
data: new Array(80).fill(10).map(() => {
return {
name: "",
value: 60
};
})
}
]
},
{
name: "click",
callback: params => {
console.log("click", params);
}
},
//
{
type: "zrender",
name: "click",
callback: params => {
console.log("点击空白处", params);
}
}
);
watch(
() => useAppStoreHook().getSidebarStatus,
() => {
delay(600).then(() => resize());
}
);
</script>
<template>
<div ref="pieChartRef" style="width: 100%; height: 25vh" />
</template>

@ -0,0 +1,132 @@
<script setup lang="ts">
import { ref, computed, watch, type Ref } from "vue";
import { useAppStoreHook } from "@/store/modules/app";
import {
delay,
useDark,
useECharts,
type EchartOptions
} from "@pureadmin/utils";
import * as echarts from "echarts/core";
const { isDark } = useDark();
const theme: EchartOptions["theme"] = computed(() => {
return isDark.value ? "dark" : "light";
});
const radarChartRef = ref<HTMLDivElement | null>(null);
const { setOptions, resize } = useECharts(
radarChartRef as Ref<HTMLDivElement>,
{
theme
}
);
const dataMax = ref([
{ name: "遮挡", max: 15, color: "#333" },
{ name: "递烟", max: 15, color: "#333" },
{ name: "递钱", max: 15, color: "#333" },
{ name: "无被执行对象", max: 15, color: "#333" },
{ name: "抖动", max: 15, color: "#333" },
{ name: "静止", max: 15, color: "#333" }
]);
// const props = defineProps({
// radarInfo: {
// type: Array
// }
// });
// const radarData = ref([...props.radarInfo]);
const radarData = ref([{ name: "类型C", value: [5, 6, 7, 8, 9, 8] }]);
console.log(radarData);
setOptions(
{
title: {
text: "缺陷类型",
show: false,
left: "left",
textStyle: {
fontSize: 14,
color: "#333"
}
},
tooltip: {
trigger: "item"
},
radar: {
center: ["50%", "50%"], //
radius: 120, //
//
indicator: dataMax.value,
shape: "polygon", //, circle:,polygon:()
splitArea: {
areaStyle: {
//
color: ["#fff"]
}
},
axisLine: {
show: false
}
},
series: [
{
type: "radar",
label: {
show: false //
},
areaStyle: {
color: new echarts.graphic.LinearGradient(
0,
1,
0,
0,
[
{
offset: 0,
color: "rgba(255, 255,255, 1)"
},
{
offset: 1,
color: "rgba(87, 239, 211,0.5)"
}
],
false
)
},
// areaStyle: {}, //
itemStyle: {
//线
color: "#57EFD3"
},
lineStyle: {
//线
color: "#57EFD3"
},
symbolSize: 4, //
symbol: "circle", //
data: radarData.value
}
]
},
{
name: "click",
callback: params => {
console.log("click", params);
}
}
);
watch(
() => useAppStoreHook().getSidebarStatus,
() => {
delay(600).then(() => resize());
}
);
</script>
<template>
<div ref="radarChartRef" style="width: 100%; height: 35vh" />
</template>

@ -0,0 +1,232 @@
<script setup lang="ts">
import { ref, computed, watch, type Ref } from "vue";
import { useAppStoreHook } from "@/store/modules/app";
import {
delay,
useDark,
useECharts,
type EchartOptions
} from "@pureadmin/utils";
import * as echarts from "echarts/core";
const { isDark } = useDark();
const theme: EchartOptions["theme"] = computed(() => {
return isDark.value ? "dark" : "light";
});
const pieChartRef = ref<HTMLDivElement | null>(null);
const { setOptions, resize } = useECharts(pieChartRef as Ref<HTMLDivElement>, {
theme
});
const props = defineProps({
data: {
type: Array,
default: () => {
return [];
}
},
color: {
type: String,
default: ""
}
});
const xLabel = ["2017", "2018", "2019", "2020", "2021", "2022"];
const dataValue = ref(props.data);
const lineColor = ref(props.color);
setOptions(
{
// backgroundColor: "#00266b",
tooltip: {
trigger: "axis",
axisPointer: {
lineStyle: {
color: "rgb(255,255,255,0.3)"
}
}
},
legend: {
icon: "rect",
show: false,
top: "5%",
right: "5%",
itemWidth: 11, //
itemHeight: 7, //
textStyle: {
color: "#fff",
fontSize: 12,
padding: [5, 5, 5, 5]
}
},
grid: {
left: "1%",
right: "1%",
top: "5%",
bottom: "0%",
containLabel: true,
show: false
},
xAxis: [
{
type: "category",
boundaryGap: false,
axisLine: {
//线x
show: false,
lineStyle: {
color: "rgba(1, 58, 116,1)"
}
},
axisLabel: {
show: false,
//
// textStyle: {
color: "#FFFFFF",
fontSize: 12
// }
},
splitLine: {
show: false,
lineStyle: {
color: "rgba(1, 58, 116,1)"
}
},
axisTick: {
show: false
},
data: xLabel
}
],
yAxis: [
{
nameTextStyle: {
color: "white",
fontSize: 12,
padding: [0, 0, 0, -30]
},
// minInterval: 1,
type: "value",
splitLine: {
show: false,
lineStyle: {
color: "#1160a0",
type: "dashed"
}
},
axisLine: {
show: false,
lineStyle: {
color: "rgba(1, 58, 116,1)"
}
},
axisLabel: {
show: false,
// textStyle: {
color: "#fff",
fontSize: 12
// }
},
axisTick: {
show: false
}
}
],
series: [
{
name: "异常数量",
type: "line",
smooth: true,
showSymbol: false,
// lineStyle: {
// // normal: {
// width: 2,
// color: "#FECA50" // 线
// // }
// },
itemStyle: {
// normal: {
color: lineColor.value //
// borderColor: '#fff600',//
// borderWidth: 13//
// label: {
// show: false, //
// color: "#fff",
// position: "top", //
// formatter: function (res) {
// if (res.value) {
// return res.value;
// } else {
// return 0;
// }
// }
// }
// }
},
areaStyle: {
// itemStyle: {
//线4x0,y0,x2,y2(0~1);true
color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{
offset: 0,
color:
lineColor.value === "#FECA50"
? "rgba(254,202,80, 0.3)"
: "rgba(15,84,250, 0.3)"
},
{
offset: 0.6,
color:
lineColor.value === "#FECA50"
? "rgba(254,202,80, 0.2)"
: "rgba(15,84,250, 0.2)"
},
{
offset: 1,
color:
lineColor.value === "#FECA50"
? "rgba(254,202,80, 0)"
: "rgba(15,84,250, 0)"
}
],
false
)
// }
},
data: dataValue.value
}
]
},
{
name: "click",
callback: params => {
console.log("click", params);
}
},
//
{
type: "zrender",
name: "click",
callback: params => {
console.log("点击空白处", params);
}
}
);
watch(
() => useAppStoreHook().getSidebarStatus,
() => {
delay(600).then(() => resize());
}
);
</script>
<template>
<div ref="pieChartRef" style="width: 100%; height: 6vh" />
</template>

@ -0,0 +1,5 @@
import abnormal from "@/assets/home/abnormal.svg?component";
import resource from "@/assets/home/resource.svg?component";
import analysis from "@/assets/home/analysis.svg?component";
export { abnormal, resource, analysis };

@ -14,6 +14,7 @@
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"sourceMap": true, "sourceMap": true,
"ignoreDeprecations": "5.0",
"baseUrl": ".", "baseUrl": ".",
"allowJs": false, "allowJs": false,
"resolveJsonModule": true, "resolveJsonModule": true,

Loading…
Cancel
Save