新增首页驾驶舱

This commit is contained in:
DESKTOP-FUDKNA8\znjsz 2024-04-12 10:13:58 +08:00
parent 67bfb9981a
commit feb2649c14
29 changed files with 10164 additions and 8561 deletions

Binary file not shown.

BIN
src/assets/bgearth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/assets/pinicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -1,68 +1,79 @@
import Vue from 'vue' import Vue from "vue";
import Element from 'element-ui' import Element from "element-ui";
// 修改如下样式文件,覆盖样式,或者新增样式文件 // 修改如下样式文件,覆盖样式,或者新增样式文件
import './assets/styles/element-variables.scss' import "./assets/styles/element-variables.scss";
import '@/assets/styles/index.scss' // global css import "@/assets/styles/index.scss"; // global css
import '@/assets/styles/ruoyi.scss' // ruoyi css import "@/assets/styles/ruoyi.scss"; // ruoyi css
import App from './App' import App from "./App";
import store from './store' import store from "./store";
import router from './router' import router from "./router";
import directive from './directive' // directive import directive from "./directive"; // directive
import plugins from './plugins' // plugins import plugins from "./plugins"; // plugins
import './assets/icons' // icon import "./assets/icons"; // icon
import './permission' // permission control import "./permission"; // permission control
import { getDicts } from "@/api/system/dict/data"; import { getDicts } from "@/api/system/dict/data";
import { parseTime, resetForm, handleTree, addBeginAndEndTime, divide } from "@/utils/ruoyi"; import {
parseTime,
resetForm,
handleTree,
addBeginAndEndTime,
divide,
} from "@/utils/ruoyi";
import Pagination from "@/components/Pagination"; import Pagination from "@/components/Pagination";
// 自定义表格工具扩展 // 自定义表格工具扩展
import RightToolbar from "@/components/RightToolbar" import RightToolbar from "@/components/RightToolbar";
// 代码高亮插件 // 代码高亮插件
// import hljs from 'highlight.js' // import hljs from 'highlight.js'
// import 'highlight.js/styles/github-gist.css' // import 'highlight.js/styles/github-gist.css'
import { DICT_TYPE, getDictDataLabel, getDictDatas, getDictDatas2 } from "@/utils/dict"; import {
DICT_TYPE,
getDictDataLabel,
getDictDatas,
getDictDatas2,
} from "@/utils/dict";
import './theme/index.css'; // 自定义主题包 - code-brick-zj import "./theme/index.css"; // 自定义主题包 - code-brick-zj
// 全局方法挂载 // 全局方法挂载
Vue.prototype.getDicts = getDicts Vue.prototype.getDicts = getDicts;
Vue.prototype.parseTime = parseTime Vue.prototype.parseTime = parseTime;
Vue.prototype.resetForm = resetForm Vue.prototype.resetForm = resetForm;
Vue.prototype.getDictDatas = getDictDatas Vue.prototype.getDictDatas = getDictDatas;
Vue.prototype.getDictDatas2 = getDictDatas2 Vue.prototype.getDictDatas2 = getDictDatas2;
Vue.prototype.getDictDataLabel = getDictDataLabel Vue.prototype.getDictDataLabel = getDictDataLabel;
Vue.prototype.DICT_TYPE = DICT_TYPE Vue.prototype.DICT_TYPE = DICT_TYPE;
Vue.prototype.handleTree = handleTree Vue.prototype.handleTree = handleTree;
Vue.prototype.addBeginAndEndTime = addBeginAndEndTime Vue.prototype.addBeginAndEndTime = addBeginAndEndTime;
Vue.prototype.divide = divide Vue.prototype.divide = divide;
// 全局组件挂载 // 全局组件挂载
Vue.component('DictTag', DictTag) Vue.component("DictTag", DictTag);
Vue.component('Pagination', Pagination) Vue.component("Pagination", Pagination);
Vue.component('RightToolbar', RightToolbar) Vue.component("RightToolbar", RightToolbar);
// 字典标签组件 // 字典标签组件
import DictTag from '@/components/DictTag' import DictTag from "@/components/DictTag";
// 头部标签插件 // 头部标签插件
import VueMeta from 'vue-meta' import VueMeta from "vue-meta";
Vue.use(directive) Vue.use(directive);
Vue.use(plugins) Vue.use(plugins);
Vue.use(VueMeta) Vue.use(VueMeta);
// Form Generator 组件需要使用到 tinymce // Form Generator 组件需要使用到 tinymce
import Tinymce from '@/components/tinymce/index.vue' import Tinymce from "@/components/tinymce/index.vue";
Vue.component('tinymce', Tinymce) Vue.component("tinymce", Tinymce);
import '@/assets/icons' import "@/assets/icons";
import request from "@/utils/request" import request from "@/utils/request";
console.log(request) console.log(request);
Vue.prototype.$axios = request Vue.prototype.$axios = request;
import '@/styles/index.scss' import "@/styles/index.scss";
// 默认点击背景不关闭弹窗 // 默认点击背景不关闭弹窗
import ElementUI from 'element-ui' import ElementUI from "element-ui";
ElementUI.Dialog.props.closeOnClickModal.default = false ElementUI.Dialog.props.closeOnClickModal.default = false;
/** /**
* If you don't want to use mock-server * If you don't want to use mock-server
@ -77,11 +88,11 @@ Vue.use(Element, {
size: localStorage.getItem("size") || "medium", // set element-ui default size size: localStorage.getItem("size") || "medium", // set element-ui default size
}); });
Vue.config.productionTip = false Vue.config.productionTip = false;
new Vue({ new Vue({
el: '#app', el: "#app",
router, router,
store, store,
render: h => h(App) render: (h) => h(App),
}) });

View File

@ -2,7 +2,7 @@
* @Author: zwq * @Author: zwq
* @Date: 2022-08-24 11:19:43 * @Date: 2022-08-24 11:19:43
* @LastEditors: zwq * @LastEditors: zwq
* @LastEditTime: 2024-04-02 09:34:56 * @LastEditTime: 2024-04-09 16:56:16
* @Description: * @Description:
*/ */
import { listData } from "@/api/system/dict/data"; //数据字典接口 import { listData } from "@/api/system/dict/data"; //数据字典接口
@ -14,7 +14,7 @@ export default {
createURL: '', //新增接口 createURL: '', //新增接口
updateURL: '', //编辑提交接口 updateURL: '', //编辑提交接口
infoURL: '', //编辑时获取单条数据接口 infoURL: '', //编辑时获取单条数据接口
codeURL: '', //获取code接口返回结果为dataForm.code字段 codeURL: null, //获取code接口返回结果为dataForm.code字段
optionArrUrl: [], //需要获取下拉框的方法数组 optionArrUrl: [], //需要获取下拉框的方法数组
optionArr: {}, //需要获取下拉框的方法数组的返回结果 optionArr: {}, //需要获取下拉框的方法数组的返回结果
dictNameList: [], //数据字典name数组 dictNameList: [], //数据字典name数组
@ -35,7 +35,7 @@ export default {
if (this.urlOptions.optionArrUrl.length > 0) { if (this.urlOptions.optionArrUrl.length > 0) {
this.getArr() this.getArr()
} }
if (this.urlOptions.dictNameList > 0) { if (this.urlOptions.dictNameList.length > 0) {
this.getDict() this.getDict()
} }
this.$nextTick(() => { this.$nextTick(() => {
@ -78,7 +78,7 @@ export default {
}, },
/** 查询字典数据列表 */ /** 查询字典数据列表 */
getDict() { getDict() {
this.dictNameList.forEach((item,index)=>{ this.urlOptions.dictNameList.forEach((item,index)=>{
const queryParams = { const queryParams = {
pageNo: 1, pageNo: 1,
pageSize: 99, pageSize: 99,

View File

@ -0,0 +1,64 @@
<!--
filename: Bipv.vue
author: liubin
date: 2024-04-10 15:39:54
description:
-->
<template>
<right-chart-base
:legend="legend"
:series="series"
:xAxis="xAxis"
:vHeight="20.5"
class="bipv-chart"
/>
</template>
<script>
import RightChartBase from "./RightChartBase.vue";
export default {
name: "BipvChart",
components: { RightChartBase },
props: {
legend: {
type: Array,
default: () => [
{ label: "2024年目标值", color: "#f3c000" },
{ label: "2023年", color: "#12f7f1" },
{ label: "2024年", color: "#58adfa" },
],
},
xAxis: {
type: Array,
default: () => ["成都", "邯郸", "株洲", "瑞昌", "heels", "socks"],
},
series: {
type: Array,
default: () => [
{
name: "2024年目标值",
data: Array.from({ length: 7 }, () =>
Math.floor(Math.random() * 1000)
),
},
{
name: "2023年",
data: Array.from({ length: 7 }, () =>
Math.floor(Math.random() * 1000)
),
},
{
name: "2024年",
data: Array.from({ length: 7 }, () =>
Math.floor(Math.random() * 1000)
),
},
],
},
},
};
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,54 @@
<!--
filename: chip-invest.vue
author: liubin
date: 2024-04-10 08:59:28
description:
-->
<template>
<left-chart-base
:legend="legend"
:series="series"
:xAxis="xAxis"
class="chip-invest-chart"
/>
</template>
<script>
import LeftChartBase from "./LeftChartBase.vue";
export default {
name: "chip-investChart",
components: { LeftChartBase },
props: {
legend: {
type: Array,
default: () => [
{ label: "2023年", color: "#12f7f1" },
{ label: "2024年", color: "#58adfa" },
],
},
xAxis: {
type: Array,
default: () => ["成都", "邯郸", "株洲", "瑞昌", "heels", "socks"],
},
series: {
type: Array,
default: () => [
{
name: "2023年",
data: Array.from({ length: 7 }, () =>
Math.floor(Math.random() * 1000)
),
},
{
name: "2024年",
data: Array.from({ length: 7 }, () =>
Math.floor(Math.random() * 1000)
),
},
],
},
},
};
</script>

View File

@ -0,0 +1,195 @@
<!--
filename: ChipYield.vue
author: liubin
date: 2024-04-11 10:46:47
description:
-->
<template>
<div class="chip-yield">
<chip-yield-chart />
<section class="right-part">
<div class="yield-location">
<section class="btn-group">
<button
@click="activeLoc = '1'"
:class="activeLoc === '1' ? 'active' : ''"
>
成都
</button>
<button
@click="activeLoc = '2'"
:class="activeLoc === '2' ? 'active' : ''"
>
邯郸
</button>
<button
@click="activeLoc = '3'"
:class="activeLoc === '3' ? 'active' : ''"
>
株洲
</button>
<button
@click="activeLoc = '4'"
:class="activeLoc === '4' ? 'active' : ''"
>
瑞昌
</button>
</section>
<section class="btn-group">
<button
@click="activeLoc = '5'"
:class="activeLoc === '5' ? 'active' : ''"
class="fixwidth"
>
佳木斯
</button>
<button
@click="activeLoc = '6'"
:class="activeLoc === '6' ? 'active' : ''"
>
凯盛光伏
</button>
<button
@click="activeLoc = '7'"
:class="activeLoc === '7' ? 'active' : ''"
>
蚌埠兴科
</button>
</section>
</div>
<div class="yield-summary">
<div class="legend">
<span class="legend-label">2024年累计</span>
<span class="legend-value deep-green">40,100</span>
</div>
<div class="legend">
<span class="legend-label">2024年目标</span>
<span class="legend-value">30,100</span>
</div>
<div class="legend">
<span class="legend-label">2023年累计</span>
<span class="legend-value deep-blue">400</span>
</div>
</div>
</section>
</div>
</template>
<script>
import ChipYieldChart from "./ChipYieldChart.vue";
export default {
name: "ChipYield",
components: { ChipYieldChart },
props: {},
data() {
return {
activeLoc: "1",
};
},
};
</script>
<style scoped lang="scss">
.chip-yield {
height: 0;
flex: 1;
display: flex;
gap: 12px;
.yield-location {
margin-top: 2.2vh;
.btn-group {
margin-top: 8px;
display: flex;
border-radius: 4px;
overflow: hidden;
button {
flex: 1;
appearance: none;
outline: none;
border: none;
cursor: pointer;
background: #01306c;
color: #fff;
padding: 8px 6px;
font-size: 0.63vw;
position: relative;
}
button.fixwidth {
flex: unset;
width: 2.765vw;
}
button:not(:first-child)::before {
content: "";
display: inline-block;
width: 2px;
height: 60%;
background: #052141;
position: absolute;
top: 20%;
left: -1px;
}
button.active {
background: #1d74d8;
}
}
}
.yield-summary {
margin-top: 3vh;
display: grid;
gap: 0.56vw;
grid-auto-rows: 40px;
grid-template-columns: 1fr 1fr;
.legend {
border-radius: 4px;
padding: 1px;
display: flex;
flex-direction: column;
font-size: 0.725vw;
padding-left: 0.88vw;
position: relative;
&::before {
content: "";
position: absolute;
top: 20%;
left: 0;
width: 0.52vw;
height: 0.52vw;
border-radius: 2px;
background: #ccc;
display: inline-block;
}
&:first-child::before {
background: #11f0e8;
}
&:nth-child(2)::before {
background: #003982;
}
&:last-child::before {
background: #0551c7;
}
.deep-blue {
color: #0551c7;
}
.deep-green {
color: #11f0e8;
}
}
}
}
</style>

View File

@ -0,0 +1,194 @@
<!--
filename: ChipYield.vue
author: liubin
date: 2024-04-11 10:46:47
description:
-->
<template>
<chart-container class="chip-yield-chart" :noScroll="true">
<div
ref="chart"
style="width: 10vw"
:style="{ height: vHeight + 'vh' }"
></div>
</chart-container>
</template>
<script>
import ChartContainerVue from "../components/ChartContainer.vue";
import chartMixin from "../mixins/chart.js";
import screenfull from "screenfull";
export default {
name: "ChipYield",
components: { ChartContainer: ChartContainerVue },
mixins: [chartMixin],
props: {
vHeight: {
type: Number,
default: 22,
},
},
data() {
return {
isFullscreen: false,
options: {
grid: {
left: 0,
right: 0,
bottom: 0,
top: 0,
containLabel: true,
},
tooltip: {},
title: {
text: "78%",
left: "48%",
top: "40%",
textAlign: "center",
textStyle: {
fontWeight: 600,
fontSize: 32,
color: "#fffd",
},
subtext: "\u200224年累计产出\u2002",
subtextStyle: {
fontSize: 14,
fontWeight: 100,
color: "#fffd",
align: "right",
},
},
series: [
// series - 2024
{
type: "pie",
name: "2024目标",
radius: ["70%", "85%"],
center: ["50%", "52%"],
emptyCircleStyle: {
color: "#042c5f33",
},
},
// series - 2024
{
type: "pie",
radius: ["70%", "85%"],
center: ["50%", "52%"],
avoidLabelOvervlap: false,
label: {
show: false,
// position: "center",
},
labelLine: {
show: false,
},
data: [
{
value: 90,
name: "2024累计产出",
selected: false,
itemStyle: {
borderJoin: "round",
borderCap: "round",
borderWidth: 12,
borderRadius: "50%",
color: {
type: "linear",
x: 1,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: "#4CF0E811" },
{ offset: 1, color: "#4CF0E8" },
],
},
},
},
{
value: 20,
name: "-",
itemStyle: { color: "transparent" },
label: { show: false },
},
],
},
// series2 - 2023
{
type: "pie",
radius: ["55%", "70%"],
center: ["50%", "52%"],
avoidLabelOvervlap: false,
label: {
show: false,
},
labelLine: {
show: false,
},
data: [
{
value: 90,
name: "2023累计产出",
selected: false,
itemStyle: {
borderJoin: "round",
borderCap: "round",
borderWidth: 12,
borderRadius: "50%",
color: {
type: "linear",
x: 1,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: "#1065ff66" },
{ offset: 1, color: "#1065ff" },
],
},
},
},
{
value: 20,
name: "-",
itemStyle: { color: "transparent" },
label: { show: false },
},
],
},
],
},
};
},
watch: {
/** 全屏状态切换时,对柱子粗细和字体大小进行相应调整 */
isFullscreen(val) {
// this.options.title.top = val ? "40%" : "37%";
this.options.title.textStyle.fontSize = val ? 48 : 32;
this.options.title.subtextStyle.fontSize = val ? 18 : 14;
this.initOptions(this.options);
},
},
mounted() {
/** 清除 MIN_WIDTH, 此处比较特殊, 需要这么一步, 去除 chart mixin 的滚动条功能 */
this.MIN_WIDTH = 0;
this.initOptions(this.options);
if (screenfull.isEnabled) {
screenfull.on("change", () => {
this.isFullscreen = screenfull.isFullscreen;
});
}
},
};
</script>
<style scoped lang="scss">
.chip-yield-chart {
height: 100%;
flex: 1;
overflow: visible;
}
</style>

View File

@ -0,0 +1,54 @@
<!--
filename: fto.vue
author: liubin
date: 2024-04-10 08:59:28
description:
-->
<template>
<left-chart-base
:legend="legend"
:series="series"
:xAxis="xAxis"
class="fto-chart"
/>
</template>
<script>
import LeftChartBase from "./LeftChartBase.vue";
export default {
name: "FtoChart",
components: { LeftChartBase },
props: {
legend: {
type: Array,
default: () => [
{ label: "2023年", color: "#12f7f1" },
{ label: "2024年", color: "#58adfa" },
],
},
xAxis: {
type: Array,
default: () => ["成都", "邯郸", "株洲", "瑞昌", "heels", "socks"],
},
series: {
type: Array,
default: () => [
{
name: "2023年",
data: Array.from({ length: 7 }, () =>
Math.floor(Math.random() * 1000)
),
},
{
name: "2024年",
data: Array.from({ length: 7 }, () =>
Math.floor(Math.random() * 1000)
),
},
],
},
},
};
</script>

View File

@ -0,0 +1,245 @@
<!--
filename: left-chart-base
author: liubin
date: 2024-04-10 08:59:28
description:
-->
<template>
<chart-container class="left-chart-base">
<div class="legend">
<span
v-for="item in legend"
:key="item.label"
class="legend-item"
:style="{ fontSize: isFullscreen ? '0.58vw' : '0.54vw' }"
>{{ item.label }}</span
>
</div>
<div
ref="chart"
style="max-width: 22vw"
:style="{ height: vHeight + 'vh' }"
></div>
</chart-container>
</template>
<script>
import screenfull from "screenfull";
import ChartContainerVue from "../components/ChartContainer.vue";
import chartMixin from "../mixins/chart.js";
export default {
name: "LeftChartBase",
components: {
ChartContainer: ChartContainerVue,
},
mixins: [chartMixin],
props: {
vHeight: {
type: Number,
default: 34,
},
legend: {
type: Array,
required: true,
},
xAxis: {
type: Array,
required: true,
},
series: {
type: Array,
required: true,
},
},
data() {
return {
isFullscreen: false,
options: {
grid: {
left: "3%",
right: "4%",
bottom: "0",
top: "15%",
containLabel: true,
},
tooltip: {},
xAxis: {
axisTick: {
show: false,
},
axisLine: {
lineStyle: {
color: "#4561AE",
},
},
axisLabel: {
color: "#fff",
fontSize: 12,
},
data: this.xAxis,
},
yAxis: {
name: "单位/片",
nameTextStyle: {
color: "#fff",
fontSize: 12,
},
axisTick: {
show: false,
},
axisLabel: {
color: "#fff",
fontSize: 12,
},
axisLine: {
show: true,
lineStyle: {
color: "#4561AE",
},
},
splitLine: {
lineStyle: {
color: "#4561AE",
},
},
},
series: [
{
name: "2023年",
type: "bar",
barWidth: 12,
itemStyle: {
borderRadius: [10, 10, 0, 0],
color: {
type: "linear",
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: "#12f7f1", // 0%
},
{
offset: 0.35,
color: "#12f7f177", // 100%
},
{
offset: 0.75,
color: "#12f7f133", // 100%
},
{
offset: 1,
color: "transparent", // 100%
},
],
global: false, // false
},
},
data: this.series[0].data,
},
{
name: "2024年",
type: "bar",
barWidth: 12,
// tooltip: {
// valueFormatter: function (value) {
// return value + " ml";
// },
// },
itemStyle: {
borderRadius: [10, 10, 0, 0],
color: {
type: "linear",
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: "#57abf8", // 0%
},
{
offset: 1,
color: "#364BFE66", // 100%
},
],
global: false, // false
},
},
data: this.series[1].data,
},
],
},
};
},
watch: {
/** 全屏状态切换时,对柱子粗细和字体大小进行相应调整 */
isFullscreen(val) {
this.options.series.map((item) => {
item.barWidth = val ? 18 : 12;
});
this.options.xAxis.axisLabel.fontSize = val ? 18 : 12;
this.options.yAxis.axisLabel.fontSize = val ? 18 : 12;
this.options.yAxis.nameTextStyle.fontSize = val ? 18 : 12;
this.initOptions(this.options);
},
},
mounted() {
this.initOptions(this.options);
if (screenfull.isEnabled) {
screenfull.on("change", () => {
this.isFullscreen = screenfull.isFullscreen;
});
}
},
};
</script>
<style scoped lang="scss">
.left-chart-base {
// position: relative;
.legend {
position: absolute;
top: 5.2vh;
right: 1vw;
}
.legend-item {
position: relative;
// font-size: 12px;
margin-right: 0.67vw;
&::before {
content: "";
display: inline-block;
// width: 10px;
// height: 10px;
width: 0.531vw;
height: 0.531vw;
background-color: #ccc;
border-radius: 2px;
margin-right: 0.22vw;
}
}
.legend-item:nth-child(1):before {
background-color: #12f7f1;
}
.legend-item:nth-child(2):before {
background-color: #58adfa;
}
/** add more */
// .legend-item:nth-child(3):before {
// background-color: #f7f1;
// }
}
</style>

View File

@ -0,0 +1,308 @@
<!--
filename: right-chart-base
author: liubin
date: 2024-04-10 08:59:28
description:
-->
<template>
<chart-container class="right-chart-base">
<div class="legend">
<span
v-for="item in legend"
:key="item.label"
class="legend-item"
:style="{ fontSize: isFullscreen ? '0.58vw' : '0.54vw' }"
>{{ item.label }}</span
>
</div>
<div
ref="chart"
style="max-width: 22vw"
:style="{ height: vHeight + 'vh' }"
></div>
</chart-container>
</template>
<script>
import screenfull from "screenfull";
import ChartContainerVue from "../components/ChartContainer.vue";
import chartMixin from "../mixins/chart.js";
export default {
name: "RightChartBase",
components: {
ChartContainer: ChartContainerVue,
},
mixins: [chartMixin],
props: {
vHeight: {
type: Number,
default: 34,
},
legend: {
type: Array,
required: true,
},
xAxis: {
type: Array,
required: true,
},
series: {
type: Array,
required: true,
},
},
data() {
return {
isFullscreen: false,
options: {
grid: {
left: "3%",
right: "4%",
bottom: "0",
top: "18%",
containLabel: true,
},
tooltip: {},
xAxis: {
axisTick: {
show: false,
},
axisLine: {
lineStyle: {
color: "#4561AE",
},
},
axisLabel: {
color: "#fff",
fontSize: 12,
},
data: this.xAxis,
},
yAxis: {
name: "单位/片",
nameTextStyle: {
color: "#fff",
fontSize: 12,
},
axisTick: {
show: false,
},
axisLabel: {
color: "#fff",
fontSize: 12,
},
axisLine: {
show: true,
lineStyle: {
color: "#4561AE",
},
},
splitLine: {
lineStyle: {
color: "#4561AE",
},
},
},
series: [
{
name: "2024年目标值",
type: "line",
lineStyle: {
color: "#f3c000",
},
itemStyle: {
color: "#f3c000",
},
areaStyle: {
color: {
type: "linear",
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: "#f3c000", // 0%
},
{
offset: 0.55,
color: "#f3c00033",
},
{
offset: 1,
color: "transparent", // 100%
},
],
global: false, // false
},
},
data: this.series[0].data,
},
{
name: "2023年",
type: "bar",
barWidth: 12,
itemStyle: {
borderRadius: [10, 10, 0, 0],
color: {
type: "linear",
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: "#12f7f1", // 0%
},
{
offset: 0.35,
color: "#12f7f177", // 100%
},
{
offset: 0.75,
color: "#12f7f133", // 100%
},
{
offset: 1,
color: "transparent", // 100%
},
],
global: false, // false
},
},
data: this.series[1].data,
},
{
name: "2024年",
type: "bar",
barWidth: 12,
// tooltip: {
// valueFormatter: function (value) {
// return value + " ml";
// },
// },
itemStyle: {
borderRadius: [10, 10, 0, 0],
color: {
type: "linear",
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: "#57abf8", // 0%
},
{
offset: 1,
color: "#364BFE66", // 100%
},
],
global: false, // false
},
},
data: this.series[2].data,
},
],
},
};
},
watch: {
/** 全屏状态切换时,对柱子粗细和字体大小进行相应调整 */
isFullscreen(val) {
this.options.series.map((item) => {
item.barWidth = val ? 18 : 12;
});
this.options.xAxis.axisLabel.fontSize = val ? 18 : 12;
this.options.yAxis.axisLabel.fontSize = val ? 18 : 12;
this.options.yAxis.nameTextStyle.fontSize = val ? 18 : 12;
this.initOptions(this.options);
},
},
mounted() {
this.initOptions(this.options);
if (screenfull.isEnabled) {
screenfull.on("change", () => {
this.isFullscreen = screenfull.isFullscreen;
});
}
},
};
</script>
<style scoped lang="scss">
.right-chart-base {
// position: relative;
.legend {
position: absolute;
top: 5.2vh;
right: 1vw;
}
.legend-item {
position: relative;
// font-size: 12px;
margin-right: 0.67vw;
&::before {
content: "";
display: inline-block;
// width: 10px;
// height: 10px;
width: 0.531vw;
height: 0.531vw;
background-color: #ccc;
border-radius: 2px;
margin-right: 0.22vw;
}
}
.legend-item:nth-child(1):before {
// width: 12px;
// height: 2px;
width: 0.638vw;
height: 0.1064vw;
background-color: #f3c000;
position: absolute;
top: 50%;
// left: -16px;
left: -0.851vw;
transform: translateY(-50%);
}
.legend-item:nth-child(1):after {
background-color: #f3c000;
content: "";
display: inline-block;
position: absolute;
// width: 6px;
// height: 6px;
width: 0.3191vw;
height: 0.3191vw;
border-radius: 100%;
top: 50%;
left: -0.851vw;
// left: -16px;
transform: translateY(-50%) translateX(50%);
}
.legend-item:nth-child(2):before {
background-color: #12f7f1;
}
.legend-item:nth-child(3):before {
background-color: #58adfa;
}
/** add more */
// .legend-item:nth-child(3):before {
// background-color: #f7f1;
// }
}
</style>

View File

@ -0,0 +1,64 @@
<!--
filename: Bipv.vue
author: liubin
date: 2024-04-10 15:39:54
description:
-->
<template>
<right-chart-base
:legend="legend"
:series="series"
:xAxis="xAxis"
:vHeight="20.5"
class="std-chart"
/>
</template>
<script>
import RightChartBase from "./RightChartBase.vue";
export default {
name: "StdChart",
components: { RightChartBase },
props: {
legend: {
type: Array,
default: () => [
{ label: "2024年目标值", color: "#f3c000" },
{ label: "2023年", color: "#12f7f1" },
{ label: "2024年", color: "#58adfa" },
],
},
xAxis: {
type: Array,
default: () => ["成都", "邯郸", "株洲", "瑞昌", "heels", "socks"],
},
series: {
type: Array,
default: () => [
{
name: "2024年目标值",
data: Array.from({ length: 7 }, () =>
Math.floor(Math.random() * 1000)
),
},
{
name: "2023年",
data: Array.from({ length: 7 }, () =>
Math.floor(Math.random() * 1000)
),
},
{
name: "2024年",
data: Array.from({ length: 7 }, () =>
Math.floor(Math.random() * 1000)
),
},
],
},
},
};
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,69 @@
<!--
filename: ChartContainer.vue
author: liubin
date: 2024-04-10 08:54:33
description:
todo: 实现滑动条 和动态宽高
-->
<template>
<div class="chart-container" :class="{ 'no-scroll': noScroll }">
<slot />
</div>
</template>
<script>
export default {
name: "ChartContainer",
components: {},
props: {
noScroll: {
type: Boolean,
default: false,
},
},
data() {
return {};
},
computed: {},
methods: {},
};
</script>
<style scoped lang="scss">
.chart-container {
height: 0;
flex: 1;
overflow-x: scroll;
}
.no-scroll::-webkit-scrollbar {
width: 0;
height: 0;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-button {
width: 0;
height: 0;
// width: 10px;
// height: 10px;
// background: #14305f;
}
::-webkit-scrollbar-track {
background: #14305f;
border: 0 none;
border-radius: 0;
}
::-webkit-scrollbar-thumb {
background: #004798;
border: 0 none;
border-radius: 6px;
}
</style>

View File

@ -0,0 +1,129 @@
<!--
filename: CompanyInfo.vue
author: liubin
date: 2024-04-08 15:27:04
description:
-->
<template>
<div class="company-info" :style="styles">
<div class="corner bl"></div>
<div class="corner br"></div>
<h2>{{ info.companyName }}</h2>
<ul>
<li v-for="item in info.items" :key="item.label">
{{ item.label }} {{ item.value | currency }}
</li>
</ul>
</div>
</template>
<script>
export default {
name: "CompanyInfo",
components: {},
props: {
info: {
type: Object,
required: true,
default: () => ({}),
},
position: {
type: Object,
required: true,
default: () => ({ x: 0, y: 0 }),
},
},
filters: {
currency(value) {
return value.toLocaleString();
},
},
data() {
return {};
},
computed: {
styles() {
return {
left: `${this.position.x}%`,
top: `${this.position.y}%`,
};
},
},
methods: {},
};
</script>
<style scoped lang="scss">
@font-face {
font-family: 优设标题黑;
src: url(../../../assets/YouSheBiaoTiHei-2.ttf);
}
.company-info {
color: #fff;
background: #4443;
position: absolute;
z-index: 9999;
// padding: 14px;
padding: 0.745vw;
backdrop-filter: blur(2px);
border-radius: 4px;
transform: translate(-50%, -100%);
box-shadow: inset 0 0 12px 2px #fff3;
//
font-size: 1vw;
}
h2 {
margin: 6px 0;
font-family: 优设标题黑;
color: #fff;
// font-size: 24px;
font-size: 1.276em;
}
ul,
li {
padding: 0;
margin: 0;
list-style: none;
}
ul {
display: flex;
flex-direction: column;
gap: 6px;
}
li {
font-size: 0.851em;
}
.corner {
position: absolute;
bottom: 0;
// width: 12px;
// height: 12px;
width: 0.639vw;
height: 0.639vw;
border-style: solid;
border-color: transparent;
}
.bl {
left: 0;
border-left: 0.1596vw solid #ffa600;
border-bottom: 0.1596vw solid #ffa600;
// border-left: 3px solid #ffa600;
// border-bottom: 3px solid #ffa600;
}
.br {
right: 0;
// border-right: 3px solid #ffa600;
// border-bottom: 3px solid #ffa600;
border-right: 0.1596vw solid #ffa600;
border-bottom: 0.1596vw solid #ffa600;
}
</style>

View File

@ -0,0 +1,182 @@
<!--
filename: Container.vue
author: liubin
date: 2024-04-09 10:44:09
description:
-->
<template>
<div class="dashboard-container">
<!-- decoration -->
<div class="corner tl"></div>
<div class="corner tr"></div>
<div v-if="side == 'left'" class="corner bl"></div>
<div v-if="side == 'right'" class="corner br"></div>
<!-- content -->
<div
class="container-head"
:class="[side == 'left' ? 'gradient-to-right' : 'gradient-to-left']"
>
<Icon :icon="icon"></Icon>
<h2 class="container-title">{{ title }}</h2>
</div>
<div
class="container-body"
:class="[
side == 'left' ? 'body-gradient-to-right' : 'body-gradient-to-left',
]"
>
<slot />
</div>
</div>
</template>
<script>
import ContainerIconVue from "./ContainerIcon.vue";
export default {
name: "DashboardContainer",
components: {
Icon: ContainerIconVue,
},
props: {
side: {
type: String,
default: "left",
},
icon: {
type: String,
default: "cube",
},
title: {
type: String,
default: "Default Title",
},
},
data() {
return {};
},
computed: {},
methods: {},
};
</script>
<style scoped lang="scss">
.dashboard-container {
height: 0;
flex: 1;
display: flex;
flex-direction: column;
position: relative;
box-shadow: inset 0 0 20px 1px #fff1;
// &::after {
// content: "";
// position: absolute;
// display: inline-block;
// width: 60%;
// height: 0.31415vh;
// border-radius: 2px;
// left: 8%;
// bottom: 0;
// background: linear-gradient(to right, #024798, transparent);
// z-index: 0;
// }
.container-head {
// height: 40px;
height: 3.8vh;
padding: 8px;
display: flex;
align-items: center;
gap: 8px;
.container-title {
font-size: 1.18vw;
line-height: 1.39vw;
font-weight: normal;
letter-spacing: 2px;
}
}
.container-body {
padding: 12px;
display: flex;
flex-direction: column;
flex: 1;
height: 0;
}
.corner {
z-index: 1;
position: absolute;
// width: 16px;
// height: 16px;
width: 0.95vw;
height: 0.95vw;
}
.corner.tl {
border-top: 2px solid #0175dc;
border-left: 2px solid #0175dc;
top: 0;
left: 0;
}
.corner.tr {
top: 0;
right: 0;
border-top: 2px solid #0175dc;
border-right: 2px solid #0175dc;
}
.corner.bl {
// width: 20px;
// height: 20px;
width: 1.064vw;
height: 1.064vw;
bottom: 0;
left: 0;
// border-left: 10px solid #0175dc;
// border-bottom: 10px solid #0175dc;
// border-top: 10px solid transparent;
// border-right: 10px solid transparent;
border-left: 0.532vw solid #0175dc;
border-bottom: 0.532vw solid #0175dc;
border-top: 0.532vw solid transparent;
border-right: 0.532vw solid transparent;
}
.corner.br {
bottom: 0;
right: 0;
// width: 20px;
// height: 20px;
width: 1.064vw;
height: 1.064vw;
// border-left: 10px solid transparent;
// border-bottom: 10px solid #0175dc;
// border-top: 10px solid transparent;
// border-right: 10px solid #0175dc;
border-left: 0.532vw solid transparent;
border-bottom: 0.532vw solid #0175dc;
border-top: 0.532vw solid transparent;
border-right: 0.532vw solid #0175dc;
}
.gradient-to-right {
background: linear-gradient(to right, #0c3f68cc, transparent);
}
.gradient-to-left {
background: linear-gradient(to left, #0c3f68cc, transparent);
}
.body-gradient-to-right {
background: linear-gradient(to right, #0003, transparent);
}
.body-gradient-to-left {
background: linear-gradient(to left, #0003, transparent);
}
}
</style>

View File

@ -0,0 +1,58 @@
<!--
filename: ContainerIcon.vue
author: liubin
date: 2024-04-09 16:41:36
description:
-->
<template>
<div class="container-icon" :style="bgStyle"></div>
</template>
<script>
import cube from "@/assets/images/homeindex/fto-icon.png";
import chip from "@/assets/images/homeindex/chip-icon.png";
import chip2 from "@/assets/images/homeindex/chip-icon-2.png";
import bipv from "@/assets/images/homeindex/bipv-icon.png";
import std from "@/assets/images/homeindex/std-icon.png";
export default {
name: "ContainerIcon",
components: {},
props: {
icon: {
type: String,
default: "cube",
},
},
data() {
return {};
},
computed: {
bgStyle() {
return {
cube:
"background: url(" + cube + ") no-repeat center center / 100% 100%",
chip:
"background: url(" + chip + ") no-repeat center center / 100% 100%",
chip2:
"background: url(" + chip2 + ") no-repeat center center / 100% 100%",
bipv:
"background: url(" + bipv + ") no-repeat center center / 100% 100%",
std: "background: url(" + std + ") no-repeat center center / 100% 100%",
}[this.icon];
},
},
methods: {},
};
</script>
<style scoped lang="scss">
.container-icon {
// width: 32px;
// height: 32px;
width: 1.701vw;
height: 1.701vw;
background: #ccc2;
}
</style>

View File

@ -0,0 +1,102 @@
<!--
filename: Header.vue
author: liubin
date: 2024-04-09 09:04:12
description:
-->
<template>
<header class="dashboard-header">
<div class="btn" :style="btnStyle" @click="toggleFullscreen"></div>
<h1>发电玻璃智能管控平台地图总览</h1>
<span class="side left">晴转多云 14</span>
<span class="side right">23:12|星期一|2023.12.13</span>
</header>
</template>
<script>
import screenfull from "screenfull";
import fullscreenIcon from "@/assets/images/homeindex/fullscreen.png";
import fullscreenExitIcon from "@/assets/images/homeindex/exit-fullscreen.png";
export default {
name: "DashboardHeader",
components: {},
props: {},
data() {
return {
isFullscreen: false,
};
},
computed: {
btnStyle() {
return {
backgroundImage: `url(${
this.isFullscreen ? fullscreenExitIcon : fullscreenIcon
})`,
};
},
},
methods: {
toggleFullscreen() {
screenfull.toggle();
this.isFullscreen = !this.isFullscreen;
//
const el = document.querySelector(".dashboard-factory-all");
el.style.width = this.isFullscreen ? "100vw" : "calc(100vw - 54px)";
el.style.left = this.isFullscreen ? "0" : "54px";
},
},
};
</script>
<style scoped lang="scss">
.dashboard-header {
height: 8vh;
background: url(../../../assets/images/homeindex/header-bg.png) no-repeat 0 0 /
100% 100%;
position: relative;
display: grid;
place-content: center;
.btn {
cursor: pointer;
position: absolute;
width: 2vw;
height: 2vw;
top: 50%;
right: 1.18vw;
background-size: 100% 100%;
background-position: 0 0;
}
}
h1 {
// font-size: 32px;
font-size: 1.65vw;
// line-height: 45px;
line-height: 1.25;
letter-spacing: 0.32vw;
user-select: none;
text-shadow: 0 4px 0 rgba(0, 0, 0, 0.5);
}
.side {
position: absolute;
font-size: 1.18vw;
// line-height: 24px;
line-height: 1.277vw;
letter-spacing: 2px;
color: #69b4ff;
}
.left {
left: 22vw;
top: 4.9vh;
}
.right {
right: 15vw;
top: 4.9vh;
}
</style>

View File

@ -0,0 +1,101 @@
import * as echarts from "echarts";
function __resizeHandler(entries) {
for (const entry of entries) {
if (entry.contentBoxSize) {
// manipulate contentBoxSize
const contentBoxSize = Array.isArray(entry.contentBoxSize)
? entry.contentBoxSize[0]
: entry.contentBoxSize;
this.chart_mixin_chartInstance.resize({
width:
contentBoxSize.inlineSize < this.MIN_WIDTH
? this.MIN_WIDTH
: contentBoxSize.inlineSize,
height: contentBoxSize.blockSize,
});
} else {
// manipulate contentRect
this.chart_mixin_chartInstance.resize({
width:
entry.contentRect.width < this.MIN_WIDTH
? this.MIN_WIDTH
: entry.contentRect.width,
height: entry.contentRect.height,
});
}
}
}
export default {
data() {
const resizeObserver = new ResizeObserver(__resizeHandler.bind(this));
return {
MIN_WIDTH: 400,
chart_mixin_chartInstance: null,
chart_mixin_observer: resizeObserver,
chart_mixin_options: {
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
tooltip: {},
legend: {
data: ["Sales"],
},
xAxis: {
data: [
"shirt",
"cardign",
"chiffon shirt",
"pants",
"heels",
"socks",
],
},
yAxis: {},
series: [
{
name: "Sales",
type: "bar",
data: [5, 20, 36, 10, 10, 20],
},
],
},
};
},
mounted() {
// this.$nextTick(() => {
// this.initChart().then(() => {
// this.initOptions(this.chart_mixin_options);
// this.initListener();
// });
// });
this.initChart();
this.initListener();
},
methods: {
initChart() {
(this.$refs.chart ||
console.warn('[mixins/chart] 注意是否有 ref="chart" 的元素存在')) &&
(this.chart_mixin_chartInstance = echarts.init(this.$refs.chart));
// return new Promise((resolve, reject) => {
// this.$refs.chart ? resolve(true) : reject(false);
// });
},
initOptions(options) {
this.chart_mixin_chartInstance.setOption(options);
},
initListener() {
this.chart_mixin_observer.observe(this.$refs.chart);
},
},
beforeDestroy() {
if (this.chart_mixin_chartInstance) {
this.chart_mixin_chartInstance.dispose();
}
},
};

View File

@ -1,98 +1,229 @@
<template> <template>
<div class="dashboard-editor-container"> <div class="dashboard-factory-all">
<!-- 首页http://localhost:81/index --> <div id="map-container">
<panel-group @handleSetLineChartData="handleSetLineChartData" /> <div class="databoard">
<db-header class="db-header"></db-header>
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;"> <div class="db-body">
<line-chart :chart-data="lineChartData" /> <div class="db-left">
</el-row> <db-container icon="cube" title="FTO投入">
<fto-chart />
<el-row :gutter="32"> </db-container>
<el-col :xs="24" :sm="24" :lg="8"> <db-container icon="chip2" title="芯片投入">
<div class="chart-wrapper"> <chip-invest-chart />
<raddar-chart /> </db-container>
</div> </div>
</el-col> <div class="db-right">
<el-col :xs="24" :sm="24" :lg="8"> <db-container side="right" icon="chip" title="芯片产出">
<div class="chart-wrapper"> <chip-yield-chart />
<pie-chart /> </db-container>
<db-container
side="right"
icon="std"
title="标准组件产出"
>
<std-chart />
</db-container>
<db-container side="right" icon="bipv" title="BIPV产出">
<bipv-chart />
</db-container>
</div> </div>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<bar-chart />
</div> </div>
</el-col> </div>
</el-row> </div>
<company-info
v-if="visible"
:info="info"
:position="hintPosition"
@close="closeHint"
/>
</div> </div>
</template> </template>
<script> <script>
import PanelGroup from './dashboard/PanelGroup' import store from "@/store";
import LineChart from './dashboard/LineChart' import DashboardHeader from "./dashboard/components/Header.vue";
import RaddarChart from './dashboard/RaddarChart' import CompanyInfo from "./dashboard/components/CompanyInfo.vue";
import PieChart from './dashboard/PieChart' import Container from "./dashboard/components/Container.vue";
import BarChart from './dashboard/BarChart' import ChartContainer from "./dashboard/components/ChartContainer.vue";
import FtoChart from "./dashboard/charts/Fto.vue";
import ChipInvestChart from "./dashboard/charts/ChipInvest.vue";
import BipvChart from "./dashboard/charts/Bipv.vue";
import ChipYieldChart from "./dashboard/charts/ChipYield.vue";
import StdChart from "./dashboard/charts/StdChart.vue";
const lineChartData = { const LOCATIONS = [
newVisitis: { //
expectedData: [100, 120, 161, 134, 105, 160, 165], { x: 67, y: 20 },
actualData: [120, 82, 91, 154, 162, 140, 145] //
}, { x: 46, y: 56 },
messages: { // 1
expectedData: [200, 192, 120, 144, 160, 130, 140], { x: 60, y: 52 },
actualData: [180, 160, 151, 106, 145, 150, 130] // 2
}, { x: 61, y: 53 },
purchases: { // 西
expectedData: [80, 100, 121, 104, 105, 90, 100], { x: 60, y: 58 },
actualData: [120, 90, 100, 138, 142, 130, 130] //
}, { x: 56, y: 60 },
shoppings: { ];
expectedData: [130, 140, 141, 142, 145, 150, 160],
actualData: [120, 82, 91, 154, 162, 140, 130]
}
}
export default { export default {
name: 'Index', name: "Index",
components: { components: {
PanelGroup, CompanyInfo,
LineChart, DbHeader: DashboardHeader,
RaddarChart, DbContainer: Container,
PieChart, ChartContainer,
BarChart FtoChart,
ChipInvestChart,
ChipYieldChart,
BipvChart,
StdChart,
}, },
data() { data() {
return { return {
lineChartData: lineChartData.newVisitis visible: false,
} hintPosition: null,
};
},
mounted() {
this.$nextTick(() => {
this.initPins();
// echarts
window.onresize = () => {};
});
},
beforeRouteEnter(to, from, next) {
next((vm) => {
store.dispatch("app/closeSideBar", { withoutAnimation: true });
});
}, },
methods: { methods: {
handleSetLineChartData(type) { initPins() {
this.lineChartData = lineChartData[type] LOCATIONS.map((loc) => {
const pin = document.createElement("div");
pin.className = "pin";
pin.style.left = `${loc.x}%`;
pin.style.top = `${loc.y}%`;
pin.addEventListener("mouseenter", () => {
this.showHint(loc);
});
pin.addEventListener("mouseleave", () => {
this.closeHint();
});
document.getElementById("map-container").appendChild(pin);
});
},
closeHint() {
this.visible = false;
this.hintPosition = null;
},
showHint(position) {
this.hintPosition = position;
const templateInfo = {
companyName: "",
items: [
{ label: "FTO投入", value: Math.floor(Math.random() * 1000000) },
{ label: "芯片产出", value: Math.floor(Math.random() * 10000000) },
{ label: "芯片投入", value: Math.floor(Math.random() * 1000000) },
{ label: "标准组件产出", value: Math.floor(Math.random() * 1000000) },
],
};
if (position === LOCATIONS[0]) {
templateInfo.companyName = "佳木斯中建材光电材料有限公司";
} else if (position === LOCATIONS[1]) {
templateInfo.companyName = "成都中建材光电材料有限公司";
} else if (position === LOCATIONS[2]) {
templateInfo.companyName = "蚌埠兴科玻璃有限公司";
} else if (position === LOCATIONS[3]) {
templateInfo.companyName = "凯盛光伏材料有限公司(本部)";
} else if (position === LOCATIONS[4]) {
templateInfo.companyName = "瑞昌中建材光电材料有限公司";
} else if (position === LOCATIONS[5]) {
templateInfo.companyName = "中建材(株洲)光电材料有限公司";
} }
}
} this.info = templateInfo;
this.$nextTick(() => {
this.visible = true;
});
},
},
};
</script> </script>
<style lang="scss" scoped> <style>
.dashboard-editor-container { .hideSidebar.mobile .dashboard-factory-all {
padding: 32px; left: 0 !important;
background-color: rgb(240, 242, 245); width: 100vw !important;
position: relative; }
</style>
.chart-wrapper { <style lang="scss" scoped>
background: #fff; .dashboard-factory-all {
padding: 16px 16px 0; background: #151516;
margin-bottom: 32px; position: fixed;
} top: 0;
left: 54px;
height: 100vh;
width: calc(100vw - 54px);
z-index: 1001;
color: #fff;
} }
@media (max-width:1024px) { #map-container {
.chart-wrapper { height: 100%;
padding: 8px; background: url(../assets/bgearth.png) no-repeat 0 0 / 100% 100%;
position: relative;
.databoard {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
display: flex;
flex-direction: column;
}
.db-body {
flex: 1;
display: flex;
justify-content: space-between;
}
.db-left,
.db-right {
// width: 420px;
width: 24vw;
padding: 20px;
display: flex;
flex-direction: column;
gap: 20px;
} }
} }
</style> </style>
<style>
.pin {
display: inline-block;
position: absolute;
z-index: 9998;
width: 1.702vw;
height: 1.702vw;
/*
width: 32px;
height: 32px;
*/
background: url(../assets/pinicon.png) no-repeat 0 0 / 100% 100%;
transition: transform 0.1s linear;
cursor: pointer;
}
.pin:hover {
transform: scale(1.2);
}
</style>

16512
yarn.lock

File diff suppressed because it is too large Load Diff