大屏模板 方案

This commit is contained in:
TimSpan 2025-05-13 16:26:43 +08:00
parent 7302d8cb65
commit 416588ef60
32 changed files with 3014 additions and 27 deletions

162
package-lock.json generated
View File

@ -8,10 +8,12 @@
"name": "management",
"version": "0.0.0",
"dependencies": {
"@kjgl77/datav-vue3": "^1.7.4",
"@vueuse/core": "^13.1.0",
"animate.css": "^4.1.1",
"axios": "^1.8.1",
"dayjs": "^1.11.13",
"echarts": "^5.6.0",
"event-source-polyfill": "^1.0.31",
"js-base64": "^3.7.7",
"jsencrypt": "^3.3.2",
@ -435,6 +437,15 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/runtime": {
"version": "7.27.1",
"resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.27.1.tgz",
"integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
"version": "7.27.0",
"resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.0.tgz",
@ -900,6 +911,52 @@
"node": ">=18"
}
},
"node_modules/@jiaminghi/bezier-curve": {
"version": "0.0.9",
"resolved": "https://registry.npmmirror.com/@jiaminghi/bezier-curve/-/bezier-curve-0.0.9.tgz",
"integrity": "sha512-u9xJPOEl6Dri2E9FfmJoGxYQY7vYJkURNX04Vj64tdi535tPrpkuf9Sm0lNr3QTKdHQh0DdNRsaa62FLQNQEEw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.5.5"
}
},
"node_modules/@jiaminghi/c-render": {
"version": "0.4.3",
"resolved": "https://registry.npmmirror.com/@jiaminghi/c-render/-/c-render-0.4.3.tgz",
"integrity": "sha512-FJfzj5hGj7MLqqqI2D7vEzHKbQ1Ynnn7PJKgzsjXaZpJzTqs2Yw5OSeZnm6l7Qj7jyPAP53lFvEQNH4o4j6s+Q==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.5.5",
"@jiaminghi/bezier-curve": "*",
"@jiaminghi/color": "*",
"@jiaminghi/transition": "*"
}
},
"node_modules/@jiaminghi/charts": {
"version": "0.2.18",
"resolved": "https://registry.npmmirror.com/@jiaminghi/charts/-/charts-0.2.18.tgz",
"integrity": "sha512-K+HXaOOeWG9OOY1VG6M4mBreeeIAPhb9X+khG651AbnwEwL6G2UtcAQ8GWCq6GzhczcLwwhIhuaHqRygwHC0sA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.5.5",
"@jiaminghi/c-render": "^0.4.3"
}
},
"node_modules/@jiaminghi/color": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/@jiaminghi/color/-/color-1.1.3.tgz",
"integrity": "sha512-ZY3hdorgODk4OSTbxyXBPxAxHPIVf9rPlKJyK1C1db46a50J0reFKpAvfZG8zMG3lvM60IR7Qawgcu4ZDO3+Hg==",
"license": "MIT"
},
"node_modules/@jiaminghi/transition": {
"version": "1.1.11",
"resolved": "https://registry.npmmirror.com/@jiaminghi/transition/-/transition-1.1.11.tgz",
"integrity": "sha512-owBggipoHMikDHHDW5Gc7RZYlVuvxHADiU4bxfjBVkHDAmmck+fCkm46n2JzC3j33hWvP9nSCAeh37t6stgWeg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.5.5"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.8",
"resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
@ -957,6 +1014,86 @@
"resolved": "https://registry.npmmirror.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
},
"node_modules/@kjgl77/datav-vue3": {
"version": "1.7.4",
"resolved": "https://registry.npmmirror.com/@kjgl77/datav-vue3/-/datav-vue3-1.7.4.tgz",
"integrity": "sha512-zYVTVKkklUxwtiNKS1qPBilm4rTW+WItfp0zVpaRAI8wgXkLSPbDR9xPq2+UcU/Jft7/DVdMfBp709E2ResuPQ==",
"license": "MIT",
"dependencies": {
"@jiaminghi/c-render": "^0.4.3",
"@jiaminghi/charts": "^0.2.18",
"@jiaminghi/color": "^1.1.3",
"@vueuse/core": "^10.11.1"
}
},
"node_modules/@kjgl77/datav-vue3/node_modules/@types/web-bluetooth": {
"version": "0.0.20",
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==",
"license": "MIT"
},
"node_modules/@kjgl77/datav-vue3/node_modules/@vueuse/core": {
"version": "10.11.1",
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-10.11.1.tgz",
"integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
"license": "MIT",
"dependencies": {
"@types/web-bluetooth": "^0.0.20",
"@vueuse/metadata": "10.11.1",
"@vueuse/shared": "10.11.1",
"vue-demi": ">=0.14.8"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@kjgl77/datav-vue3/node_modules/@vueuse/metadata": {
"version": "10.11.1",
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.11.1.tgz",
"integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@kjgl77/datav-vue3/node_modules/@vueuse/shared": {
"version": "10.11.1",
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.11.1.tgz",
"integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==",
"license": "MIT",
"dependencies": {
"vue-demi": ">=0.14.8"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@kjgl77/datav-vue3/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -2561,6 +2698,16 @@
"node": ">= 0.4"
}
},
"node_modules/echarts": {
"version": "5.6.0",
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "2.3.0",
"zrender": "5.6.1"
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.125",
"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.125.tgz",
@ -4415,6 +4562,12 @@
"resolved": "https://registry.npmmirror.com/treemate/-/treemate-0.3.11.tgz",
"integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg=="
},
"node_modules/tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
"license": "0BSD"
},
"node_modules/typescript": {
"version": "5.7.3",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.7.3.tgz",
@ -5084,6 +5237,15 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zrender": {
"version": "5.6.1",
"resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz",
"integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
"license": "BSD-3-Clause",
"dependencies": {
"tslib": "2.3.0"
}
}
}
}

View File

@ -12,14 +12,17 @@
"type-check": "vue-tsc --build"
},
"dependencies": {
"@kjgl77/datav-vue3": "^1.7.4",
"@vueuse/core": "^13.1.0",
"animate.css": "^4.1.1",
"axios": "^1.8.1",
"dayjs": "^1.11.13",
"echarts": "^5.6.0",
"event-source-polyfill": "^1.0.31",
"js-base64": "^3.7.7",
"jsencrypt": "^3.3.2",
"lodash-es": "^4.17.21",
"naive-ui": "^2.41.0",
"pinia": "^3.0.1",
"pinia-plugin-persistedstate": "^4.2.0",
"sass": "^1.85.1",
@ -27,8 +30,7 @@
"unplugin-auto-import": "^19.1.1",
"unplugin-vue-components": "^28.4.1",
"vue": "^3.5.13",
"vue-router": "^4.5.0",
"naive-ui": "^2.41.0"
"vue-router": "^4.5.0"
},
"devDependencies": {
"@tsconfig/node22": "^22.0.0",

View File

@ -0,0 +1,102 @@
// 颜色
$colors: (
"primary": #db9e3f,
"info-1": #4394e4,
"info": #4b67af,
"white": #ffffff,
"light": #f9f9f9,
"grey-1": #999999,
"grey": #666666,
"dark-1": #5f5f5f,
"dark": #222222,
"black-1": #171823,
"black": #000000,
);
// 字体大小
$base-font-size: 16px;
$font-sizes: (
xxs: 0.1,
//8px
xs: 0.125,
//10px
sm: 0.2875,
//12px
md: 0.1625,
//13px
lg: 0.175,
//14px
xl: 0.2,
//16px
xxl: 0.225,
//18px
xxxl: 0.25 //20px,,,,
);
// 宽高
.w-100 {
width: 100%;
}
.h-100 {
height: 100%;
}
//flex
.d-flex {
display: flex;
}
.flex-column {
flex-direction: column;
}
.flex-wrap {
flex-wrap: wrap;
}
.flex-nowrap {
flex-wrap: nowrap;
}
$flex-jc: (
start: flex-start,
end: flex-end,
center: center,
between: space-between,
around: space-around,
evenly: space-evenly,
);
$flex-ai: (
start: flex-start,
end: flex-end,
center: center,
stretch: stretch,
);
.flex-1 {
flex: 1;
}
//.mt-1 => margin top
//spacing
$spacing-types: (
m: margin,
p: padding,
);
$spacing-directions: (
t: top,
r: right,
b: bottom,
l: left,
);
$spacing-base-size: 16px;
$spacing-sizes: (
0: 0,
1: 0.25,
2: 0.5,
3: 1,
4: 1.5,
5: 3,
);

View File

@ -110,24 +110,31 @@
.mt#{$i} {
margin-top: #{$i}px !important;
}
.mr#{$i} {
margin-right: #{$i}px !important;
}
.mb#{$i} {
margin-bottom: #{$i}px !important;
}
.ml#{$i} {
margin-left: #{$i}px !important;
}
.pt#{$i} {
padding-top: #{$i}px !important;
}
.pr#{$i} {
padding-right: #{$i}px !important;
}
.pb#{$i} {
padding-bottom: #{$i}px !important;
}
.pl#{$i} {
padding-left: #{$i}px !important;
}
@ -484,4 +491,4 @@
/* 鼠标悬浮手指 */
.pointer {
cursor: pointer;
}
}

194
src/assets/scss/style.scss Normal file
View File

@ -0,0 +1,194 @@
@import "./variables";
// 全局样式
* {
margin: 0;
padding: 0;
list-style-type: none;
box-sizing: border-box;
outline: none;
}
html {
margin: 0;
padding: 0;
}
body {
// font-family: Arial, Helvetica, sans-serif;
// line-height: 1.2em;
// background-color: #f1f1f1;
margin: 0;
padding: 0;
overflow: hidden;
}
a {
color: #343440;
text-decoration: none;
box-sizing: border-box;
}
.clearfix {
&::after {
content: "";
display: table;
height: 0;
line-height: 0;
visibility: hidden;
clear: both;
}
}
// 图标
.iconfont {
font-size: 20px !important;
color: #5cd9e8;
}
//浮动
.float-r {
float: right;
}
//浮动
.float-l {
float: left;
}
// 字体加粗
.fw-b {
font-weight: bold;
}
//文章一行显示多余省略号显示
.title-item {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bg-color-black {
background-color: rgba(19, 25, 47, 0.6);
}
.bg-color-blue {
background-color: #1a5cd7;
}
.colorBlack {
color: #272727 !important;
&:hover {
color: #272727 !important;
}
}
.colorGrass {
color: #33cea0;
&:hover {
color: #33cea0 !important;
}
}
.colorRed {
color: #ff5722;
&:hover {
color: #ff5722 !important;
}
}
.colorText {
color: #d3d6dd !important;
&:hover {
color: #d3d6dd !important;
}
}
.colorBlue {
color: #257dff !important;
&:hover {
color: #257dff !important;
}
}
//颜色
@each $colorkey, $color in $colors {
.text-#{$colorkey} {
color: $color;
}
.bg-#{$colorkey} {
background-color: $color;
}
}
//对齐
@each $var in (left, center, right) {
.text-#{$var} {
text-align: $var !important;
}
}
//flex
@each $key, $value in $flex-jc {
.jc-#{$key} {
justify-content: $value;
}
}
@each $key, $value in $flex-ai {
.ai-#{$key} {
align-items: $value;
}
}
//字体
@each $fontkey, $fontvalue in $font-sizes {
.fs-#{$fontkey} {
font-size: $fontvalue * $base-font-size;
}
}
//.mt-1 => margin top
//spacing
@each $typekey, $type in $spacing-types {
//.m-1
@each $sizekey, $size in $spacing-sizes {
.#{$typekey}-#{$sizekey} {
#{$type}: $size * $spacing-base-size;
}
}
//.mx-1
@each $sizekey, $size in $spacing-sizes {
.#{$typekey}x-#{$sizekey} {
#{$type}-left: $size * $spacing-base-size;
#{$type}-right: $size * $spacing-base-size;
}
.#{$typekey}y-#{$sizekey} {
#{$type}-top: $size * $spacing-base-size;
#{$type}-bottom: $size * $spacing-base-size;
}
}
//.mt-1
@each $directionkey, $direction in $spacing-directions {
@each $sizekey, $size in $spacing-sizes {
.#{$typekey}#{$directionkey}-#{$sizekey} {
#{$type}-#{$direction}: $size * $spacing-base-size;
}
}
}
.#{$typekey} {
#{$type}: 0;
}
}

View File

@ -8,9 +8,9 @@ interface OBJ {
objectUrl: string;
preSignedUrl: string;
}
// export const getPreSignedUrl = async (fileName: string) => {
// const resp = await api.get<OBJ>("/file/getPreSignedUrl", {
// fileName,
// });
// return resp.data as OBJ;
// };
export const getPreSignedUrl = async (fileName: string) => {
const resp = await api.get<OBJ>("/file/getPreSignedUrl", {
fileName,
});
return resp.data as OBJ;
};

View File

@ -14,7 +14,7 @@
>
<n-button
dashed
style="margin-right: 5px;"
style="margin-right: 5px"
>
<Icon name="LineMdUpload" />
<span>选择照片上传</span>

View File

@ -1,19 +1,16 @@
import { createApp } from "vue";
// pinia store
import pinia from "@/stores";
import "animate.css";
import App from "./App.vue";
import router from "./router";
import "./assets/scss/common.scss";
import './assets/main.scss'
// import { initEnums, } from "@/utils/emnus";
// initEnums();
import "./assets/main.scss";
// import "./assets/scss/_variables.scss";
import './assets/scss/style.scss';
// @ts-ignore
import DataVVue3 from "@kjgl77/datav-vue3";
const app = createApp(App);
app.use(router);
app.use(pinia);
app.use(DataVVue3);
app.mount("#app");

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

View File

@ -0,0 +1,146 @@
<script setup lang="tsx">
import { ref, watch, defineProps, onMounted } from "vue";
import * as echarts from "echarts";
// props
const props = defineProps<{
cdata: {
category: string[];
rateData: number[];
barData: number[];
lineData: number[];
};
}>();
// DOM
const chartContainerRef = ref<HTMLDivElement | null>(null);
// echarts
let chartInstance: echarts.ECharts | null = null;
//
const initChart = (options: echarts.EChartsOption) => {
if (chartContainerRef.value) {
if (!chartInstance) {
chartInstance = echarts.init(chartContainerRef.value);
}
chartInstance.setOption(options);
}
};
//
const getOptions = (val: typeof props.cdata): echarts.EChartsOption => {
return {
tooltip: {
show: true,
trigger: "item",
axisPointer: {
type: "shadow",
label: {
show: true,
backgroundColor: "#7B7DDC",
},
},
},
legend: { show: true },
grid: {
// @ts-ignore
x: "8%",
width: "88%",
top: "5%",
bottom: "7%",
},
xAxis: {
data: val.category,
axisLine: { lineStyle: { color: "#B4B4B4" } },
axisTick: { show: false },
},
yAxis: [
{
splitLine: { show: false },
axisLine: { lineStyle: { color: "#B4B4B4" } },
axisLabel: { formatter: "{value} " },
},
{
splitLine: { show: false },
axisLine: { lineStyle: { color: "#B4B4B4" } },
axisLabel: { formatter: "{value} " },
},
],
series: [
{
name: "贯通率",
type: "line",
smooth: true,
showAllSymbol: true,
symbol: "emptyCircle",
symbolSize: 8,
yAxisIndex: 1,
itemStyle: {
// @ts-ignore
normal: {
color: "#F02FC2",
},
},
data: val.rateData,
},
{
name: "已贯通",
type: "bar",
barWidth: 10,
itemStyle: {
normal: {
barBorderRadius: 5,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#956FD4" },
{ offset: 1, color: "#3EACE5" },
]),
},
},
data: val.barData,
},
{
name: "计划贯通",
type: "bar",
barGap: "-100%",
barWidth: 10,
itemStyle: {
normal: {
barBorderRadius: 5,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(156,107,211,0.8)" },
{ offset: 0.2, color: "rgba(156,107,211,0.5)" },
{ offset: 1, color: "rgba(156,107,211,0.2)" },
]),
},
},
z: -12,
data: val.lineData,
},
],
};
};
// props
watch(
() => props.cdata,
(val) => {
initChart(getOptions(val));
},
{ immediate: true, deep: true }
);
onMounted(() => {
initChart(getOptions(props.cdata));
});
//
const height = "450px";
const width = "100%";
</script>
<template>
<div
ref="chartContainerRef"
:style="{ height, width }"
/>
</template>

View File

@ -0,0 +1,71 @@
<script setup lang="tsx">
import { reactive, onMounted } from "vue";
import Draw from "./draw.vue";
const cdata = reactive({
category: [
"市区",
"万州",
"江北",
"南岸",
"北碚",
"綦南",
"长寿",
"永川",
"璧山",
"江津",
"城口",
"大足",
"垫江",
"丰都",
"奉节",
"合川",
"江津区",
"开州",
"南川",
"彭水",
"黔江",
"石柱",
"铜梁",
"潼南",
"巫山",
"巫溪",
"武隆",
"秀山",
"酉阳",
"云阳",
"忠县",
"川东",
"检修",
],
lineData: [
18092, 20728, 24045, 28348, 32808, 36097, 39867, 44715, 48444, 50415,
56061, 62677, 59521, 67560, 18092, 20728, 24045, 28348, 32808, 36097,
39867, 44715, 48444, 50415, 36097, 39867, 44715, 48444, 50415, 50061,
32677, 49521, 32808,
],
barData: [
4600, 5000, 5500, 6500, 7500, 8500, 9900, 12500, 14000, 21500, 23200,
24450, 25250, 33300, 4600, 5000, 5500, 6500, 7500, 8500, 9900, 22500,
14000, 21500, 8500, 9900, 12500, 14000, 21500, 23200, 24450, 25250, 7500,
],
rateData: [] as string[],
});
//
const setData = () => {
cdata.rateData = cdata.barData.map((val, i) => {
const rate = val / cdata.lineData[i];
return rate.toFixed(2);
});
};
onMounted(() => {
setData();
});
</script>
<template>
<!-- @vue-ignore -->
<Draw :cdata="cdata" />
</template>

View File

@ -0,0 +1,47 @@
<template>
<div class="bottomLeft">
<div class="bg-color-black">
<div class="d-flex pt-2 pl-2">
<span>
<i class="iconfont icon-chart-line" />
</span>
<div class="d-flex">
<span class="fs-xl text mx-2 mt-1">数据统计图</span>
</div>
</div>
<div>
<chart />
</div>
</div>
</div>
</template>
<script setup>
import Chart from "./chart/index.vue";
</script>
<style lang="scss" scoped>
$box-height: 520px;
$box-width: 100%;
.bottomLeft {
padding: 20px 16px;
height: $box-height;
width: $box-width;
border-radius: 5px;
.bg-color-black {
height: $box-height - 35px;
border-radius: 10px;
}
.text {
color: #c3cbde;
}
.chart-box {
margin-top: 16px;
width: 170px;
height: 170px;
.active-ring-name {
padding-top: 10px;
}
}
}
</style>

View File

@ -0,0 +1,365 @@
import { defineComponent, watch, ref } from 'vue';
// 声明类型
const PropsType = {
cdata: {
type: Object,
require: true
}
} as const
// 定义主体
export default defineComponent({
props: PropsType,
setup(props) {
// 定义 ref
const chartRef = ref()
// 定义颜色
const colorList = {
linearYtoG: {
type: "linear",
x: 0,
y: 0,
x2: 1,
y2: 1,
colorStops: [
{
offset: 0,
color: "#f5b44d"
},
{
offset: 1,
color: "#28f8de"
}
]
},
linearGtoB: {
type: "linear",
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 0,
color: "#43dfa2"
},
{
offset: 1,
color: "#28f8de"
}
]
},
linearBtoG: {
type: "linear",
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 0,
color: "#1c98e8"
},
{
offset: 1,
color: "#28f8de"
}
]
},
areaBtoG: {
type: "linear",
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: "rgba(35,184,210,.2)"
},
{
offset: 1,
color: "rgba(35,184,210,0)"
}
]
}
}
// 配置项
let options ={}
// 监听
watch(
() => props.cdata,
(val: any) => {
options = {
title: {
text: "",
textStyle: {
color: "#D3D6DD",
fontSize: 24,
fontWeight: "normal"
},
subtext: val.year + "/" + val.weekCategory[6],
subtextStyle: {
color: "#fff",
fontSize: 16
},
top: 50,
left: 80
},
legend: {
top: 120,
left: 80,
orient: "vertical",
itemGap: 15,
itemWidth: 12,
itemHeight: 12,
data: ["平均指标", "我的指标"],
textStyle: {
color: "#fff",
fontSize: 14
}
},
tooltip: {
trigger: "item"
},
radar: {
center: ["68%", "27%"],
radius: "40%",
name: {
color: "#fff"
},
splitNumber: 8,
axisLine: {
lineStyle: {
color: colorList.linearYtoG,
opacity: 0.6
}
},
splitLine: {
lineStyle: {
color: colorList.linearYtoG,
opacity: 0.6
}
},
splitArea: {
areaStyle: {
color: "#fff",
opacity: 0.1,
shadowBlur: 25,
shadowColor: "#000",
shadowOffsetX: 0,
shadowOffsetY: 5
}
},
indicator: [
{
name: "服务态度",
max: val.maxData
},
{
name: "产品质量",
max: 10
},
{
name: "任务效率",
max: 12
},
{
name: "售后保障",
max: 3.5
}
]
},
grid: {
left: 90,
right: 80,
bottom: '15%',
top: "50%"
},
xAxis: {
type: "category",
position: "bottom",
axisLine: true,
axisLabel: {
color: "rgba(255,255,255,.8)",
fontSize: 12
},
data: val.weekCategory
},
// 下方Y轴
yAxis: {
name: "工单",
nameLocation: "end",
nameGap: 24,
nameTextStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 14
},
max: val.maxData,
splitNumber: 4,
axisLine: {
lineStyle: {
opacity: 0
}
},
splitLine: {
show: true,
lineStyle: {
color: "#fff",
opacity: 0.1
}
},
axisLabel: {
color: "rgba(255,255,255,.8)",
fontSize: 12
}
},
series: [
{
name: "",
type: "radar",
symbolSize: 0,
data: [
{
value: val.radarDataAvg[6],
name: "平均指标",
itemStyle: {
normal: {
color: "#f8d351"
}
},
lineStyle: {
normal: {
opacity: 0
}
},
areaStyle: {
normal: {
color: "#f8d351",
shadowBlur: 25,
shadowColor: "rgba(248,211,81,.3)",
shadowOffsetX: 0,
shadowOffsetY: -10,
opacity: 1
}
}
},
{
value: val.radarData[6],
name: "我的指标",
itemStyle: {
normal: {
color: "#43dfa2"
}
},
lineStyle: {
normal: {
opacity: 0
}
},
areaStyle: {
normal: {
color: colorList.linearGtoB,
shadowBlur: 15,
shadowColor: "rgba(0,0,0,.2)",
shadowOffsetX: 0,
shadowOffsetY: 5,
opacity: 0.8
}
}
}
]
},
{
name: "",
type: "line",
smooth: true,
symbol: "emptyCircle",
symbolSize: 8,
itemStyle: {
normal: {
color: "#fff"
}
},
lineStyle: {
normal: {
color: colorList.linearBtoG,
width: 3
}
},
areaStyle: {
normal: {
color: colorList.areaBtoG
}
},
data: val.weekLineData,
lineSmooth: true,
markLine: {
silent: true,
data: [
{
type: "average",
name: "平均值"
}
],
precision: 0,
label: {
normal: {
formatter: "平均值: \n {c}"
}
},
lineStyle: {
normal: {
color: "rgba(248,211,81,.7)"
}
}
},
tooltip: {
position: "top",
formatter: "{c} m",
backgroundColor: "rgba(28,152,232,.2)",
padding: 6
}
},
{
name: "占位背景",
type: "bar",
itemStyle: {
normal: {
show: true,
color: "#000",
opacity: 0
}
},
silent: true,
barWidth: "50%",
data: val.weekMaxData,
animation: false
}
]
}
// 手动触发更新
if (chartRef.value) {
// 通过初始化参数打入数据
chartRef.value.initChart(options)
}
},
{
immediate: true,
deep: true
}
)
return () => {
const height = "480px"
const width = "100%"
return <div>
<echart ref={chartRef} height={height} width={width} />
</div>
}
}
})

View File

@ -0,0 +1,92 @@
import { defineComponent, reactive, onMounted, ref, onUnmounted } from "vue";
import Draw from "./draw";
export default defineComponent({
components: {
Draw,
},
setup() {
const drawTiming = ref(0);
const cdata = reactive({
year: null,
weekCategory: [],
radarData: [],
radarDataAvg: [],
maxData: 12000,
weekMaxData: [],
weekLineData: [],
});
// methods
const setData = () => {
// 清空轮询数据
cdata.weekCategory = [];
cdata.weekMaxData = [];
cdata.weekLineData = [];
cdata.radarData = [];
cdata.radarDataAvg = [];
const dateBase = new Date();
cdata.year = dateBase.getFullYear();
// 周数据
for (let i = 0; i < 7; i++) {
// 日期
const date = new Date();
cdata.weekCategory.unshift(
[date.getMonth() + 1, date.getDate() - i].join("/")
);
// 折线图数据
cdata.weekMaxData.push(cdata.maxData);
const distance = Math.round(Math.random() * 11000 + 500);
cdata.weekLineData.push(distance);
// 雷达图数据
// 我的指标
const averageSpeed = +(Math.random() * 5 + 3).toFixed(3);
const maxSpeed = averageSpeed + +(Math.random() * 3).toFixed(2);
const hour = +(distance / 1000 / averageSpeed).toFixed(1);
const radarDayData = [distance, averageSpeed, maxSpeed, hour];
cdata.radarData.unshift(radarDayData);
// 平均指标
const distanceAvg = Math.round(Math.random() * 8000 + 4000);
const averageSpeedAvg = +(Math.random() * 4 + 4).toFixed(3);
const maxSpeedAvg = averageSpeedAvg + +(Math.random() * 2).toFixed(2);
const hourAvg = +(distance / 1000 / averageSpeed).toFixed(1);
const radarDayDataAvg = [
distanceAvg,
averageSpeedAvg,
maxSpeedAvg,
hourAvg,
];
cdata.radarDataAvg.unshift(radarDayDataAvg);
}
};
// 定时函数
const drawTimingFn = () => {
setData();
drawTiming.value = setInterval(() => {
setData();
}, 6000);
};
// 生命周期
onMounted(() => {
drawTimingFn();
});
onUnmounted(() => {
clearInterval(drawTiming.value);
});
return () => {
return (
<div>
<Draw cdata={cdata} />
</div>
);
};
},
});

View File

@ -0,0 +1,47 @@
<template>
<div class="bottomRight">
<div class="bg-color-black">
<div class="d-flex pt-2 pl-2">
<span>
<i class="iconfont icon-chart-area" />
</span>
<div class="d-flex">
<span class="fs-xl text mx-2 mt-1">数据统计图</span>
</div>
</div>
<div>
<chart />
</div>
</div>
</div>
</template>
<script setup>
import Chart from "./chart/index";
</script>
<style lang="scss" class>
$box-height: 520px;
$box-width: 100%;
.bottomRight {
padding: 14px 16px;
height: $box-height;
width: $box-width;
border-radius: 5px;
.bg-color-black {
height: $box-height - 30px;
border-radius: 10px;
}
.text {
color: #c3cbde;
}
.chart-box {
margin-top: 16px;
width: 170px;
height: 170px;
.active-ring-name {
padding-top: 10px;
}
}
}
</style>

View File

@ -0,0 +1,92 @@
import { defineComponent, watch, shallowReactive } from 'vue'
// 声明类型
const PropsType = {
tips: {
type: Number,
default: 50,
require: true
},
colorObj: {
type: Object,
default: () => ({
textStyle: "#3fc0fb",
series: {
color: ["#00bcd44a", "transparent"],
dataColor: {
normal: "#03a9f4",
shadowColor: "#97e2f5"
}
}
})
}
} as const
// 定义主体
export default defineComponent({
props: PropsType,
setup(props) {
// 配置项
let options = shallowReactive({title:null,series:null})
watch(
() => props.tips,
(val: any) => {
options = {
title: {
text: val * 1 + "%",
x: "center",
y: "center",
textStyle: {
color: props.colorObj.textStyle,
fontSize: 16
}
},
series: [
{
type: "pie",
radius: ["75%", "80%"],
center: ["50%", "50%"],
hoverAnimation: false,
color: props.colorObj.series.color,
label: {
normal: {
show: false
}
},
data: [
{
value: val,
itemStyle: {
normal: {
color: props.colorObj.series.dataColor.normal,
shadowBlur: 10,
shadowColor: props.colorObj.series.dataColor.shadowColor
}
}
},
{
value: 100 - val
}
]
}
]
}
},
{
immediate: true,
deep: true
}
)
return () => {
const height = "100px"
const width = "120px"
return <div>
<echart options={options} height={height} width={width} />
</div>
}
}
})

View File

@ -0,0 +1,232 @@
<template>
<div class="center">
<div class="up">
<div
class="bg-color-black item"
v-for="item in titleItem"
:key="item.title"
>
<p class="ml-3 colorBlue fw-b">{{ item.title }}</p>
<div>
<dv-digital-flop
class="dv-dig-flop ml-1 mt-1 pl-3"
:config="item.config"
/>
</div>
</div>
</div>
<div class="down">
<div class="ranking bg-color-black">
<span>
<i class="iconfont icon-tongji2"></i>
</span>
<span class="fs-xl text mx-2 mb-1">年度负责人组件达标榜</span>
<dv-scroll-ranking-board
class="dv-scr-rank-board"
:config="ranking"
/>
</div>
<div class="percent">
<div class="item bg-color-black">
<span>今日任务通过率</span>
<chart
:tips="rate[0].tips"
:colorObj="rate[0].colorData"
/>
</div>
<div class="item bg-color-black">
<span>今日任务达标率</span>
<chart
:tips="rate[1].tips"
:colorObj="rate[1].colorData"
/>
</div>
<div class="water">
<dv-water-level-pond
class="dv-wa-le-po"
:config="water"
/>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, onMounted } from "vue";
import Chart from "../center/chart/draw";
//
const titleDate = [
{
number: 1020,
text: "今年累计任务建次数",
},
{
number: 18,
text: "本月累计任务次数",
},
{
number: 4,
text: "今日累计任务次数",
},
{
number: 71,
text: "今年失败任务次数",
},
{
number: 949,
text: "今年失败成功次数",
},
{
number: 811,
text: "今年达标任务个数",
},
];
const titleItem = reactive<any[]>([]);
//
onMounted(() => {
setData();
});
//
const setData = () => {
titleDate.forEach((e) => {
titleItem.push({
title: e.text,
config: {
number: [e.number],
toFixed: 1,
textAlign: "left",
content: "{nt}",
style: {
fontSize: 26,
},
},
});
});
};
//
const ranking = reactive({
data: [
{ name: "周口", value: 55 },
{ name: "南阳", value: 120 },
{ name: "西峡", value: 78 },
{ name: "驻马店", value: 66 },
{ name: "新乡", value: 80 },
{ name: "新乡2", value: 80 },
{ name: "新乡3", value: 80 },
{ name: "新乡4", value: 80 },
{ name: "新乡5", value: 80 },
{ name: "新乡6", value: 80 },
],
carousel: "single",
unit: "人",
});
//
const water = reactive({
data: [24, 45],
shape: "roundRect",
formatter: "{value}%",
waveNum: 3,
});
//
const rate = reactive([
{
id: "centerRate1",
tips: 60,
colorData: {
textStyle: "#3fc0fb",
series: {
color: ["#00bcd44a", "transparent"],
dataColor: {
normal: "#03a9f4",
shadowColor: "#97e2f5",
},
},
},
},
{
id: "centerRate2",
tips: 40,
colorData: {
textStyle: "#67e0e3",
series: {
color: ["#faf3a378", "transparent"],
dataColor: {
normal: "#ff9800",
shadowColor: "#fcebad",
},
},
},
},
]);
</script>
<style lang="scss" scoped>
.center {
display: flex;
flex-direction: column;
.up {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
.item {
border-radius: 6px;
padding-top: 8px;
margin-top: 8px;
width: 32%;
height: 70px;
.dv-dig-flop {
width: 150px;
height: 30px;
}
}
}
.down {
padding: 6px 4px;
padding-bottom: 0;
width: 100%;
display: flex;
height: 255px;
justify-content: space-between;
.bg-color-black {
border-radius: 5px;
}
.ranking {
padding: 10px;
width: 59%;
.dv-scr-rank-board {
height: 220px;
}
}
.percent {
width: 40%;
display: flex;
flex-wrap: wrap;
.item {
width: 50%;
height: 120px;
span {
margin-top: 8px;
font-size: 14px;
display: flex;
justify-content: center;
}
}
.water {
width: 100%;
.dv-wa-le-po {
height: 120px;
}
}
}
}
}
</style>

View File

@ -0,0 +1,97 @@
import { defineComponent, ref, watch, shallowReactive } from 'vue'
// 声明类型
const PropsType = {
cdata: {
type: Object,
require: true
}
} as const
// 定义主体
export default defineComponent({
props: PropsType,
setup(props) {
// 定义 ref
const chartRef = ref()
// 配置项
let options = shallowReactive({color:null,tooltip:null,toolbox:null,calculable:null,legend:null,series:null})
watch(
() => props.cdata,
(val: any) => {
options = {
color: [
'#37a2da',
'#32c5e9',
'#9fe6b8',
'#ffdb5c',
'#ff9f7f',
'#fb7293',
'#e7bcf3',
'#8378ea'
],
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
toolbox: {
show: true
},
calculable: true,
legend: {
orient: 'horizontal',
icon: 'circle',
bottom: 0,
x: 'center',
data: val.xData,
textStyle: {
color: '#fff'
}
},
series: [
{
name: '通过率统计',
type: 'pie',
radius: [10, 50],
roseType: 'area',
center: ['50%', '40%'],
itemStyle: {
borderRadius: 5
},
label: {
show: true,
color: "#fff",
},
emphasis: {
label: {
show: false
}
},
data: val.seriesData
}
]
}
// 手动触发更新
if (chartRef.value) {
// 通过初始化参数打入数据
chartRef.value.initChart(options)
}
},
{
immediate: true,
deep: true
}
)
return () => {
const height = "220px"
const width = "260px"
return <div>
<echart ref={chartRef} options={options} height={height} width={width} />
</div>
}
}
})

View File

@ -0,0 +1,39 @@
import { defineComponent, onUnmounted, reactive } from 'vue'
import Draw from './draw'
export default defineComponent({
components: {
Draw,
},
setup() {
let intervalInstance = null
const cdata = reactive({
xData: ['数据1', '数据2', '数据3', '数据4', '数据5', '数据6'],
seriesData: [
{ value: 10, name: '数据1' },
{ value: 5, name: '数据2' },
{ value: 15, name: '数据3' },
{ value: 25, name: '数据4' },
{ value: 20, name: '数据5' },
{ value: 35, name: '数据6' },
],
})
intervalInstance = setInterval(() => {
const data = cdata.seriesData
cdata.seriesData = data.map((e) => {
return { value: e.value + 10, name: e.name }
})
}, 1000)
onUnmounted(() => {
clearInterval(intervalInstance)
})
return () => {
return (
<div>
<Draw cdata={cdata} />
</div>
)
}
},
})

View File

@ -0,0 +1,171 @@
<template>
<div class="centreLeft1">
<div class="bg-color-black">
<div class="d-flex pt-2 pl-2">
<span>
<i class="iconfont icon-tongji4" />
</span>
<div class="d-flex">
<span class="fs-xl text mx-2">任务通过率</span>
<dv-decoration-3 class="dv-dec-3" />
</div>
</div>
<div class="d-flex jc-center">
<chart />
</div>
<!-- 4个主要的数据 -->
<div class="bottom-data">
<div
class="item-box mt-2"
v-for="(item, index) in numberData"
:key="index"
>
<div class="d-flex jc-end">
<i
class="iconfont"
:class="[iconFont[index]]"
/>
<dv-digital-flop
class="dv-digital-flop"
:config="item.config"
/>
</div>
<p>
<span>{{ item.text }}</span>
<span class="colorYellow">()</span>
</p>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted, reactive } from "vue";
import Chart from "./chart/index";
export default defineComponent({
components: {
Chart,
},
setup() {
//
const dataArr = [
{
number: 150,
text: "今日构建总量",
},
{
number: 144,
text: "总共完成数量",
},
{
number: 361,
text: "正在编译数量",
},
{
number: 571,
text: "未通过数量",
},
];
//
const iconFont = [
"icon-diagnose",
"icon-monitoring",
"icon-cloudupload",
"icon-clouddownload",
];
const numberData = reactive([]);
let intervalInstance = null;
onMounted(() => {
setData();
changeTiming();
});
const setData = () => {
dataArr.forEach((e) => {
numberData.push({
config: {
number: [e.number],
toFixed: 1,
content: "{nt}",
style: {
fontSize: 24,
},
},
text: e.text,
});
});
};
const changeTiming = () => {
intervalInstance = setInterval(() => {
changeNumber();
}, 2000);
};
const changeNumber = () => {
numberData.forEach((item, index) => {
item.config.number[0] += ++index;
item.config = { ...item.config };
});
};
onUnmounted(() => {
clearInterval(intervalInstance);
});
return { numberData, iconFont };
},
});
</script>
<style lang="scss" scoped>
$box-width: 300px;
$box-height: 410px;
.centreLeft1 {
padding: 16px;
height: $box-height;
width: $box-width;
border-radius: 10px;
.bg-color-black {
height: $box-height - 30px;
border-radius: 10px;
}
.text {
color: #c3cbde;
}
.dv-dec-3 {
position: relative;
width: 100px;
height: 20px;
top: -3px;
}
.bottom-data {
.item-box {
& > div {
padding-right: 5px;
}
font-size: 14px;
float: right;
position: relative;
width: 50%;
color: #d3d6dd;
.dv-digital-flop {
width: 120px;
height: 30px;
}
i {
font-size: 20px;
line-height: 30px;
margin-left: 20px;
}
.colorYellow {
color: yellowgreen;
}
p {
text-align: center;
}
}
}
}
</style>

View File

@ -0,0 +1,217 @@
import { defineComponent, watch, shallowReactive, nextTick, ref, onUnmounted } from 'vue';
// 声明类型
const PropsType = {
cdata: {
type: Array,
require: true
}
} as const
// 定义主体
export default defineComponent({
props: PropsType,
setup(props) {
// 配置项
let options = shallowReactive({showLegendSymbol:null,tooltip:null,geo:null,series:null})
// 设置点的位置(经纬度)
const geoCoordMap = {
: [118.11022, 24.490474, 20],
: [119.206239, 26.275302, 20],
: [118.589421, 24.908853, 20],
: [117.561801, 24.510897, 20],
: [116.82978, 25.391603, 20],
: [119.007558, 25.591011, 20],
: [117.435001, 26.465444, 20],
: [118.178459, 27.535627, 20],
: [119.527082, 27.15924, 20],
}
const seriesData = [
{
name: '厦门市',
},
{
name: '福州市',
},
{
name: '泉州市',
},
{
name: '漳州市',
},
{
name: '龙岩市',
},
{
name: '莆田市',
},
{
name: '三明市',
},
{
name: '南平市',
},
{
name: '宁德市',
},
]
const convertData = function (data) {
const scatterData = [];
for (let i = 0; i < data.length; i++) {
const geoCoord = geoCoordMap[data[i].name];
if (geoCoord) {
scatterData.push({
name: data[i].name,
value: geoCoord.concat(data[i].value),
});
}
}
return scatterData;
}
// 监听
watch(
() => props.cdata,
(val: any) => {
options = {
showLegendSymbol: true,
tooltip: {
trigger: 'item',
textStyle: {
fontSize: 14,
lineHeight: 22,
},
position: point => {
// 固定在顶部
return [point[0] + 50, point[1] - 20];
},
// 如果需要自定义 tooltip样式需要使用formatter
/*
formatter: params => {
return `<div style=""> ... </div>`
}
*/
},
// 如果需要根据不同的数据展示深浅不一的颜色,则把这里打开
// visualMap: {
// min: 0,
// max: 10,
// show: false,
// seriesIndex: 0,
// // 颜色
// inRange: {
// color: ['rgba(41,166,206, .5)', 'rgba(69,117,245, .9)'],
// },
// },
// 底部背景
geo: [{
show: true,
aspectScale: 0.85, //长宽比
zoom: 1.16,
top: '10%',
left: '17%',
map: '福建',
roam: false,
itemStyle: {
normal: {
borderColor: '#7ad5ff7f',
shadowOffsetY: 5,
shadowBlur: 15,
areaColor: 'rgba(5,21,35,0.1)'
}
}
}],
series: [
{
name: '相关指数',
type: 'map',
aspectScale: 0.85, //长宽比
zoom: 1.16, //缩放
mapType: '福建', // 自定义扩展图表类型
top: '9%',
left: '16%',
itemStyle: {
normal: {
// 背景渐变色
areaColor: {
type: 'linear-gradient',
x: 0,
y: 300,
x2: 0,
y2: 0,
colorStops: [{
offset: 0,
color: 'RGBA(19,96,187,1)' // 0% 处的颜色
}, {
offset: 1,
color: 'RGBA(7,193,223,1)' // 50% 处的颜色
}],
global: true // 缺省为 false
},
borderColor: '#4ECEE6',
borderWidth: 1,
},
emphasis: {
areaColor: '#4f7fff',
borderColor: 'rgba(0,242,252,.6)',
borderWidth: 2,
shadowBlur: 10,
shadowColor: '#00f2fc',
},
},
label: {
formatter: params => `${params.name}`,
show: true,
position: 'insideRight',
textStyle: {
fontSize: 14,
color: '#efefef',
},
emphasis: {
textStyle: {
color: '#fff',
},
},
},
data: val,
},
{
type: 'effectScatter',
coordinateSystem: 'geo',
symbolSize: 7,
effectType: 'ripple',
legendHoverLink: false,
showEffectOn: 'render',
rippleEffect: {
period: 4,
scale: 2.5,
brushType: 'stroke',
},
zlevel: 1,
itemStyle: {
normal: {
color: '#99FBFE',
shadowBlur: 5,
shadowColor: '#fff',
},
},
data: convertData(seriesData),
},
],
}
},
{
immediate: true,
deep: true
}
)
return () => {
const height = "360px"
const width = "330px"
return <div>
<echart options={options} height={height} width={width} />
</div>
}
}
})

View File

@ -0,0 +1,58 @@
import { defineComponent, reactive } from 'vue'
import Draw from './draw'
export default defineComponent({
components: {
Draw
},
setup() {
const cdata = reactive([
{
// 名字需要与 “common/map/fujian.js” 地图数据文件里面定义的一一对应,不能是 “福州” 或者 “闽” 之类的缩写
name: '福州市',
value: 10,
elseData: {
// 这里放置地图 tooltip 里想显示的数据
}
},
{
name: '厦门市',
value: 9,
},
{
name: '漳州市',
value: 8,
},
{
name: '泉州市',
value: 7,
},
{
name: '三明市',
value: 6,
},
{
name: '莆田市',
value: 5,
},
{
name: '南平市',
value: 4,
},
{
name: '龙岩市',
value: 3,
},
{
name: '宁德市',
value: 2,
}
])
return () => {
return <div>
<Draw cdata={cdata} />
</div>
}
}
})

View File

@ -0,0 +1,54 @@
<template>
<div class="centreLeft1">
<div class="bg-color-black">
<div class="d-flex pt-2 pl-2">
<span>
<i class="iconfont icon-layer-group" />
</span>
<div class="d-flex">
<span class="fs-xl text mx-2">地图数据</span>
<dv-decoration-1 class="dv-dec-1" />
</div>
</div>
<div class="d-flex jc-center">
<chart />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import Chart from "./chart/index";
</script>
<style lang="scss" scoped>
.centreLeft1 {
$box-width: 300px;
$box-height: 410px;
padding: 16px;
height: $box-height;
min-width: $box-width;
border-radius: 5px;
.bg-color-black {
height: $box-height - 30px;
border-radius: 10px;
}
.text {
color: #c3cbde;
}
.dv-dec-1 {
position: relative;
width: 100px;
height: 20px;
top: -3px;
}
.chart-box {
margin-top: 16px;
width: 170px;
height: 170px;
.active-ring-name {
padding-top: 10px;
}
}
}
</style>

View File

@ -0,0 +1,131 @@
import { defineComponent, watch, shallowReactive } from 'vue'
// 声明类型
const PropsType = {
cdata: {
type: Object,
require: true
}
} as const
// 定义主体
export default defineComponent({
props: PropsType,
setup(props) {
// 定义固定配置项
const lineStyle = {
normal: {
width: 1,
opacity: 0.5
}
};
// 配置项
let options = shallowReactive({radar:null,series:null})
watch(
() => props.cdata,
(val: any) => {
options = {
radar: {
indicator: val.indicatorData,
shape: "circle",
splitNumber: 5,
radius: ["0%", "65%"],
name: {
textStyle: {
color: "rgb(238, 197, 102)"
}
},
splitLine: {
lineStyle: {
color: [
"rgba(238, 197, 102, 0.1)",
"rgba(238, 197, 102, 0.2)",
"rgba(238, 197, 102, 0.4)",
"rgba(238, 197, 102, 0.6)",
"rgba(238, 197, 102, 0.8)",
"rgba(238, 197, 102, 1)"
].reverse()
}
},
splitArea: {
show: false
},
axisLine: {
lineStyle: {
color: "rgba(238, 197, 102, 0.5)"
}
}
},
series: [
{
name: "北京",
type: "radar",
lineStyle: lineStyle,
data: val.dataBJ,
symbol: "none",
itemStyle: {
normal: {
color: "#F9713C"
}
},
areaStyle: {
normal: {
opacity: 0.1
}
}
},
{
name: "上海",
type: "radar",
lineStyle: lineStyle,
data: val.dataSH,
symbol: "none",
itemStyle: {
normal: {
color: "#B3E4A1"
}
},
areaStyle: {
normal: {
opacity: 0.05
}
}
},
{
name: "广州",
type: "radar",
lineStyle: lineStyle,
data: val.dataGZ,
symbol: "none",
itemStyle: {
normal: {
color: "rgb(238, 197, 102)"
}
},
areaStyle: {
normal: {
opacity: 0.05
}
}
}
]
}
},
{
immediate: true,
deep: true
}
)
return () => {
const height = "200px"
const width = "260px"
return <div>
<echart options={options} height={height} width={width} />
</div>
}
}
})

View File

@ -0,0 +1,51 @@
import { defineComponent, reactive } from 'vue'
import Draw from './draw'
export default defineComponent({
components: {
Draw
},
setup() {
const cdata = reactive({
indicatorData: [
{ name: "数据1", max: 300 },
{ name: "数据2", max: 250 },
{ name: "数据3", max: 300 },
{ name: "数据4", max: 5 },
{ name: "数据5", max: 200 },
{ name: "数据6", max: 100 }
],
dataBJ: [
[94, 69, 114, 2.08, 73, 39, 22],
[99, 73, 110, 2.43, 76, 48, 23],
[31, 12, 30, 0.5, 32, 16, 24],
[42, 27, 43, 1, 53, 22, 25],
[154, 117, 157, 3.05, 92, 58, 26],
[234, 185, 230, 4.09, 123, 69, 27],
[160, 120, 186, 2.77, 91, 50, 28]
],
dataGZ: [
[84, 94, 140, 2.238, 68, 18, 22],
[93, 77, 104, 1.165, 53, 7, 23],
[99, 130, 227, 3.97, 55, 15, 24],
[146, 84, 139, 1.094, 40, 17, 25],
[113, 108, 137, 1.481, 48, 15, 26],
[81, 48, 62, 1.619, 26, 3, 27],
[56, 48, 68, 1.336, 37, 9, 28]
],
dataSH: [
[91, 45, 125, 0.82, 34, 23, 1],
[65, 27, 78, 0.86, 45, 29, 2],
[83, 60, 84, 1.09, 73, 27, 3],
[109, 81, 121, 1.28, 68, 51, 4],
[106, 77, 114, 1.07, 55, 51, 5],
[109, 81, 121, 1.28, 68, 51, 6],
[106, 77, 114, 1.07, 55, 51, 7]
]
})
return () => {
return <div>
<draw cdata={cdata} />
</div>
}
}
})

View File

@ -0,0 +1,67 @@
<template>
<div class="centerRight1">
<div class="bg-color-black">
<div class="d-flex pt-2 pl-2">
<span>
<i class="iconfont icon-align-left" />
</span>
<span class="fs-xl text mx-2">产品销售渠道分析</span>
</div>
<div class="d-flex ai-center flex-column body-box">
<dv-capsule-chart
class="dv-cap-chart"
:config="config"
/>
<Chart />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive } from "vue";
import Chart from "./chart/index.tsx";
const config = reactive({
data: [
{ name: "南阳", value: 167 },
{ name: "周口", value: 67 },
{ name: "漯河", value: 123 },
{ name: "郑州", value: 55 },
{ name: "西峡", value: 98 },
],
});
</script>
<style lang="scss" scoped>
$box-height: 400px;
$box-width: 340px;
.centerRight1 {
padding: 5px;
height: $box-height;
width: $box-width;
border-radius: 5px;
.bg-color-black {
padding: 5px;
height: $box-height;
width: $box-width;
border-radius: 10px;
}
.text {
color: #c3cbde;
}
.body-box {
border-radius: 10px;
overflow: hidden;
.dv-cap-chart {
width: 100%;
height: 160px;
}
}
}
</style>

View File

@ -0,0 +1,75 @@
<template>
<div class="centerRight2">
<div class="bg-color-black">
<div class="d-flex pt-2 pl-2">
<span>
<i class="iconfont icon-vector" />
</span>
<div class="d-flex">
<span class="fs-xl text mx-2">任务完成排行榜</span>
</div>
</div>
<div class="d-flex mt-1 jc-center body-box">
<dv-scroll-board
class="dv-scr-board"
:config="config"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive } from "vue";
const config = reactive({
header: ["组件", "分支", "覆盖率"],
data: [
["组件1", "dev-1", "<span class='colorGrass'>↑75%</span>"],
["组件2", "dev-2", "<span class='colorRed'>↓33%</span>"],
["组件3", "dev-3", "<span class='colorGrass'>↑100%</span>"],
["组件4", "rea-1", "<span class='colorGrass'>↑94%</span>"],
["组件5", "rea-2", "<span class='colorGrass'>↑95%</span>"],
["组件6", "fix-2", "<span class='colorGrass'>↑63%</span>"],
["组件7", "fix-4", "<span class='colorGrass'>↑84%</span>"],
["组件8", "fix-7", "<span class='colorRed'>↓46%</span>"],
["组件9", "dev-2", "<span class='colorRed'>↓13%</span>"],
["组件10", "dev-9", "<span class='colorGrass'>↑76%</span>"],
],
rowNum: 7, //
headerHeight: 35,
headerBGC: "#0f1325", //
oddRowBGC: "#0f1325", //
evenRowBGC: "#171c33", //
index: true,
columnWidth: [50],
align: ["center"],
});
</script>
<style lang="scss" scoped>
$box-height: 410px;
$box-width: 300px;
.centerRight2 {
padding: 16px;
padding-top: 20px;
height: $box-height;
width: $box-width;
border-radius: 5px;
.bg-color-black {
height: $box-height - 30px;
border-radius: 10px;
}
.text {
color: #c3cbde;
}
.body-box {
border-radius: 10px;
overflow: hidden;
.dv-scr-board {
width: 270px;
height: 340px;
}
}
}
</style>

View File

@ -0,0 +1,39 @@
type ModuleInfo = {
name: string;
icon: string;
}[];
// 星期
export const WEEK = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
// 主题名称与副标题名称
export const title = "大数据可视化平台";
export const subtitle = ["数据分析1", "数据分析2", "vue-big-screen"];
export const moduleInfo: ModuleInfo = [
// 中间的几个模块
{
name: "任务通过率",
icon: "icon-chart-bar",
},
{
name: "地图数据",
icon: "icon-tongji4",
},
{
name: "产品销售渠道分析",
icon: "icon-align-left",
},
{
name: "任务完成排行榜",
icon: "icon-zhibiao2",
},
// 底部两个模块
{
name: "数据统计图",
icon: "icon-vector",
},
{
name: "工单修复以及满意度统计图",
icon: "icon-fenxi7",
},
];

View File

@ -1,9 +1,323 @@
<template>
<div>总览大屏</div>
<div class="app">
<div
id="index"
ref="appRef"
>
<div class="bg">
<dv-loading v-if="loading">Loading ...</dv-loading>
<div
v-else
class="host-body"
>
<div class="d-flex jc-center">
<dv-decoration-10 class="dv-dec-10" />
<div class="d-flex jc-center">
<dv-decoration-8
class="dv-dec-8"
:color="decorationColors"
/>
<div class="title">
<span class="title-text">{{ title }}</span>
<dv-decoration-6
class="dv-dec-6"
:reverse="true"
:color="['#50e3c2', '#67a1e5']"
/>
</div>
<dv-decoration-8
class="dv-dec-8"
:reverse="true"
:color="decorationColors"
/>
</div>
<dv-decoration-10 class="dv-dec-10-s" />
</div>
<!-- 第二行 -->
<div class="d-flex jc-between px-2">
<div class="d-flex aside-width">
<div class="react-left ml-4 react-l-s">
<span class="react-before"></span>
<span class="text">{{ subtitle[0] }}</span>
</div>
<div class="react-left ml-3">
<span class="text">{{ subtitle[1] }}</span>
</div>
</div>
<div class="d-flex aside-width">
<div class="react-right bg-color-blue mr-3">
<span class="text fw-b">{{ subtitle[2] }}</span>
</div>
<div class="react-right mr-4 react-l-s">
<span class="react-after"></span>
<span class="text">
{{ timeInfo.dateYear }} {{ timeInfo.dateWeek }}
{{ timeInfo.dateDay }}
</span>
</div>
</div>
</div>
<div class="body-box">
<!-- 第三行数据 -->
<div class="content-box">
<div>
<dv-border-box-12>
<!-- <CenterLeft1 /> -->
</dv-border-box-12>
</div>
<div>
<dv-border-box-12>
<!-- <CenterLeft2 /> -->
</dv-border-box-12>
</div>
<!-- 中间 -->
<div>
<!-- <Center /> -->
</div>
<!-- 中间 -->
<div>
<!-- <CenterRight1 /> -->
</div>
<div>
<dv-border-box-13>
<CenterRight2 />
</dv-border-box-13>
</div>
</div>
<!-- 第四行数据 -->
<div class="bototm-box">
<dv-border-box-13>
<BottomLeft />
</dv-border-box-13>
<dv-border-box-12>
<!-- <BottomRight /> -->
</dv-border-box-12>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted } from "vue";
import { formatTime } from "./utils/index";
import { WEEK } from "./constant/index";
import useDraw from "./utils/useDraw.ts";
import { title, subtitle, moduleInfo } from "./constant/index";
//
import BottomLeft from "./components/bottomLeft/index.vue";
import CenterRight2 from "./components/centerRight2/index.vue";
//
import CenterLeft1 from "./components/centerLeft1/index.vue";
import CenterLeft2 from "./components/centerLeft2/index.vue";
import Center from "./components/center/index.vue";
import CenterRight1 from "./components/centerRight1/index.vue";
import BottomRight from "./components/bottomRight/index.vue";
// *
const decorationColors = ["#568aea", "#000000"];
// *
const loading = ref<boolean>(true);
// *
const timeInfo = reactive({
setInterval: 0,
dateDay: "",
dateYear: "",
dateWeek: "",
});
// *
const { appRef, calcRate, windowDraw, unWindowDraw } = useDraw();
//
onMounted(() => {
cancelLoading();
handleTime();
windowDraw();
calcRate();
});
onUnmounted(() => {
unWindowDraw();
clearInterval(timeInfo.setInterval);
});
// loading
const cancelLoading = () => {
setTimeout(() => {
loading.value = false;
}, 500);
};
//
const handleTime = () => {
timeInfo.setInterval = setInterval(() => {
const date = new Date();
timeInfo.dateDay = formatTime(date, "HH: mm: ss");
timeInfo.dateYear = formatTime(date, "yyyy-MM-dd");
timeInfo.dateWeek = WEEK[date.getDay()];
}, 1000);
};
</script>
<style scoped lang="scss">
</style>
<style lang="scss" scoped>
.app {
width: 100vw;
height: 100vh;
background-color: #020308;
overflow: hidden;
#index {
color: #d3d6dd;
width: 1920px;
height: 1080px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transform-origin: left top;
/* transform-origin: center center; */
.bg {
width: 100%;
height: 100%;
padding: 16px 16px 0 16px;
background-image: url("./assets/pageBg.png");
background-size: cover;
background-position: center center;
}
.host-body {
.dv-dec-10,
.dv-dec-10-s {
width: 33.3%;
height: 5px;
}
.dv-dec-10-s {
transform: rotateY(180deg);
}
.dv-dec-8 {
width: 200px;
height: 50px;
}
.title {
position: relative;
width: 500px;
text-align: center;
background-size: cover;
background-repeat: no-repeat;
.title-text {
font-size: 24px;
position: absolute;
bottom: 0;
left: 50%;
transform: translate(-50%);
}
.dv-dec-6 {
position: absolute;
bottom: -30px;
left: 50%;
width: 250px;
height: 8px;
transform: translate(-50%);
}
}
//
.aside-width {
width: 40%;
}
.react-r-s,
.react-l-s {
background-color: #0f1325;
}
//
.react-right {
&.react-l-s {
text-align: right;
width: 500px;
}
font-size: 18px;
width: 300px;
line-height: 50px;
text-align: center;
transform: skewX(-45deg);
.react-after {
position: absolute;
right: -25px;
top: 0;
height: 50px;
width: 50px;
background-color: #0f1325;
transform: skewX(45deg);
}
.text {
display: inline-block;
transform: skewX(45deg);
}
}
.react-left {
&.react-l-s {
width: 500px;
text-align: left;
}
font-size: 18px;
width: 300px;
height: 50px;
line-height: 50px;
text-align: center;
transform: skewX(45deg);
background-color: #0f1325;
.react-before {
position: absolute;
left: -25px;
top: 0;
height: 50px;
width: 50px;
background-color: #0f1325;
transform: skewX(-45deg);
}
.text {
display: inline-block;
transform: skewX(-45deg);
}
}
.body-box {
margin-top: 16px;
display: flex;
flex-direction: column;
//
.content-box {
display: grid;
grid-template-columns: 2fr 3fr 5fr 3fr 2fr;
}
//
.bototm-box {
margin-top: 10px;
display: grid;
grid-template-columns: repeat(2, 50%);
}
}
}
}
}
</style>

View File

@ -0,0 +1,36 @@
/**
* @param {date} time
* @param {String} fmt yyyy-MM-ddyyyy-MM-dd HH:mm:ss
* @returns {String}
*/
export const formatTime = (
time: string | number | Date,
fmt: string
): string => {
if (!time) return ''
const date = new Date(time)
const o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'H+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
'q+': Math.floor((date.getMonth() + 3) / 3),
S: date.getMilliseconds(),
}
if (/(y+)/.test(fmt))
fmt = fmt.replace(
RegExp.$1,
(date.getFullYear() + '').substr(4 - RegExp.$1.length)
)
for (const k in o) {
if (new RegExp('(' + k + ')').test(fmt)) {
fmt = fmt.replace(
RegExp.$1,
// @ts-ignore: Unreachable code error
RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
)
}
}
return fmt
}

View File

@ -0,0 +1,69 @@
import { ref } from "vue";
export default function useDraw() {
// * 指向最外层容器
const appRef = ref();
// * 定时函数
const timer = ref(0);
// * 默认缩放值
const scale = {
width: "1",
height: "1",
};
// * 设计稿尺寸px
const baseWidth = 1920;
const baseHeight = 1080;
// * 需保持的比例默认1.77778
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5));
const calcRate = () => {
// 当前宽高比
const currentRate = parseFloat(
(window.innerWidth / window.innerHeight).toFixed(5)
);
if (appRef.value) {
if (currentRate > baseProportion) {
// 表示更宽
scale.width = (
(window.innerHeight * baseProportion) /
baseWidth
).toFixed(5);
scale.height = (window.innerHeight / baseHeight).toFixed(5);
appRef.value.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`;
} else {
// 表示更高
scale.height = (
window.innerWidth /
baseProportion /
baseHeight
).toFixed(5);
scale.width = (window.innerWidth / baseWidth).toFixed(5);
appRef.value.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`;
}
}
};
const resize = () => {
clearTimeout(timer.value);
timer.value = setTimeout(() => {
calcRate();
}, 200);
};
// 改变窗口大小重新绘制
const windowDraw = () => {
window.addEventListener("resize", resize);
};
// 改变窗口大小重新绘制
const unWindowDraw = () => {
window.removeEventListener("resize", resize);
};
return {
appRef,
calcRate,
windowDraw,
unWindowDraw,
};
}

View File

@ -1,15 +1,28 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["src/**/*", "src/**/*.vue", "src/**/*.d.ts"],
"exclude": ["src/**/__tests__/*", "node_modules", "dist", "management"],
"include": [
"src/**/*",
"src/**/*.vue",
"src/**/*.d.ts"
],
"exclude": [
"src/**/__tests__/*",
"node_modules",
"dist",
"management"
],
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"paths": {
"@/*": ["./src/*"]
"@/*": [
"./src/*"
]
},
"jsx": "preserve",
"jsxImportSource": "vue",
"verbatimModuleSyntax": true,
"types": ["naive-ui/volar.d.ts"]
"types": [
"naive-ui/volar.d.ts"
]
}
}
}