fc
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
BIN
781700210223_.pic.jpg
Normal file
After Width: | Height: | Size: 134 KiB |
7
README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Vue 3 + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Vue</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
1683
package-lock.json
generated
Normal file
20
package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "btgxq-population-bigdata",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"echarts": "^5.4.3",
|
||||
"stylus": "^0.61.0",
|
||||
"vue": "^3.3.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.5.0",
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
}
|
BIN
public/assets/imgs/bg.png
Normal file
After Width: | Height: | Size: 320 KiB |
BIN
public/assets/imgs/card/bg1.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
public/assets/imgs/card/bg2.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
public/assets/imgs/card/bg3.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
public/assets/imgs/card/bg4.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
public/assets/imgs/card/bg5.png
Normal file
After Width: | Height: | Size: 466 B |
BIN
public/assets/imgs/card/bg6.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
public/assets/imgs/card/bg7.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
public/assets/imgs/card/bg8.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
public/assets/imgs/icon/man.png
Normal file
After Width: | Height: | Size: 669 B |
BIN
public/assets/imgs/icon/women.png
Normal file
After Width: | Height: | Size: 411 B |
BIN
public/assets/imgs/shbz/cjr.png
Normal file
After Width: | Height: | Size: 378 B |
BIN
public/assets/imgs/shbz/lnr.png
Normal file
After Width: | Height: | Size: 394 B |
BIN
public/assets/imgs/shbz/shjz.png
Normal file
After Width: | Height: | Size: 383 B |
BIN
public/assets/imgs/shbz/ty.png
Normal file
After Width: | Height: | Size: 372 B |
1
public/vite.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
64
src/App.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<CardTwoCount/>
|
||||
<PersonCount2 :bg-type="2"/>
|
||||
<PersonCount2/>
|
||||
<PersonCount1/>
|
||||
<PersonPercent/>
|
||||
<CardCount2/>
|
||||
<CardCount1 bg="assets/imgs/card/bg2.png"/>
|
||||
<Process/>
|
||||
<CircleWave/>
|
||||
<Bar id="bar" w="300" h="250" :x-data="barChart.xData" :data="barChart.data" />
|
||||
<BarSpike id="barSpike" w="300" h="250" :x-data="barChart.xData" :data="barChart.data" />
|
||||
<PolarHalf id="polarHalf" w="600" :data="polarHalfChart.data" :unit="polarHalfChart.unit" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import Bar from './components/echarts/Bar.vue';
|
||||
import BarSpike from './components/echarts/BarSpike.vue';
|
||||
import PolarHalf from './components/echarts/PolarHalf.vue';
|
||||
import CircleWave from './components/circle/CircleWave.vue';
|
||||
import Process from './components/process/Process.vue';
|
||||
import CardCount1 from './components/card/CardCount1.vue';
|
||||
import CardCount2 from './components/card/CardCount2.vue';
|
||||
import PersonPercent from './components/card/PersonPercent.vue';
|
||||
import PersonCount1 from './components/card/PersonCount1.vue';
|
||||
import PersonCount2 from './components/card/PersonCount2.vue';
|
||||
import CardTwoCount from './components/card/CardTwoCount.vue';
|
||||
|
||||
const barChart = reactive({
|
||||
xData: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||
data: [120, 200, 150, 80, 70, 110, 130]
|
||||
})
|
||||
|
||||
const polarHalfChart = reactive({
|
||||
unit: '人',
|
||||
data: [{
|
||||
name: '矛盾纠纷类',
|
||||
value: 40
|
||||
}, {
|
||||
name: '涉邪人员',
|
||||
value: 10
|
||||
}, {
|
||||
name: '吸毒人员',
|
||||
value: 20
|
||||
}, {
|
||||
name: '社会治安',
|
||||
value: 10
|
||||
}, {
|
||||
name: '重点上访',
|
||||
value: 900
|
||||
}]
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.main
|
||||
width 100%
|
||||
height 100%
|
||||
background-image url('assets/imgs/bg.png')
|
||||
</style>
|
1
src/assets/vue.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
After Width: | Height: | Size: 496 B |
58
src/components/card/CardCount1.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="container" :style="{backgroundImage: `url(${props.bg})`}">
|
||||
<div class="top">{{ props.title }}</div>
|
||||
<div class="bottom">
|
||||
<div class="left">{{ props.value }}</div>
|
||||
<div class="right">{{ props.unit }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, defineProps} from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
bg: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '标题'
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
unit: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.container
|
||||
display flex
|
||||
flex-direction column
|
||||
justify-content space-between
|
||||
width 200px
|
||||
height 55px
|
||||
padding 5px 30px
|
||||
background-repeat no-repeat
|
||||
background-size 100% 100%
|
||||
color #FFFFFF
|
||||
.top
|
||||
font-size 18px
|
||||
font-weight bold
|
||||
.bottom
|
||||
display flex
|
||||
justify-content space-between
|
||||
align-items center
|
||||
height 30px
|
||||
.left
|
||||
font-size 22px
|
||||
.right
|
||||
font-size 14px
|
||||
</style>
|
92
src/components/card/CardCount2.vue
Normal file
@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="card-count2-container">
|
||||
<div class="top">
|
||||
<div class="value">{{ props.value }}</div>
|
||||
<div class="title">
|
||||
<span>{{ props.title }}</span>
|
||||
<span>({{ props.unit }})</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="title">
|
||||
<div class="name">占比</div>
|
||||
<div class="value">{{ props.percent * 100 }}%</div>
|
||||
</div>
|
||||
<div class="process">
|
||||
<div class="value" :style="{width: props.percent * 100 +'px'}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineProps} from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '标题'
|
||||
},
|
||||
unit: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
percent: {
|
||||
type: Number,
|
||||
default: 0.5
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: 'black'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="stylus" scoped>
|
||||
.card-count2-container
|
||||
width 140px
|
||||
height 120px
|
||||
display flex
|
||||
flex-direction column
|
||||
.top
|
||||
display flex
|
||||
flex-direction column
|
||||
.value
|
||||
padding 5px
|
||||
text-align center
|
||||
font-weight bold
|
||||
font-size 22px
|
||||
.title
|
||||
padding 0 5px
|
||||
text-align center
|
||||
font-size 14px
|
||||
color #FFFFFF
|
||||
.bottom
|
||||
display flex
|
||||
flex-direction column
|
||||
align-items center
|
||||
margin-top 10px
|
||||
padding 0 10px
|
||||
.title
|
||||
display flex
|
||||
justify-content space-between
|
||||
font-size 12px
|
||||
width 110px
|
||||
color #FFFFFF
|
||||
.process
|
||||
width 110px
|
||||
height 20px
|
||||
background-color #1b4d70
|
||||
position relative
|
||||
.value
|
||||
position absolute
|
||||
width 100px
|
||||
height 6px
|
||||
top 7px
|
||||
left 5px
|
||||
background linear-gradient(90deg, #02b1f7, #FFF)
|
||||
|
||||
</style>
|
35
src/components/card/CardTwoCount.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<CardCount2 class="left"/>
|
||||
<CardCount2 class="right"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, defineProps} from 'vue';
|
||||
import CardCount2 from './CardCount2.vue';
|
||||
|
||||
const props = defineProps({
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.container
|
||||
width 360px
|
||||
height 180px
|
||||
background-image url(assets/imgs/card/bg8.png)
|
||||
background-size 100% 100%
|
||||
background-repeat no-repeat
|
||||
position relative
|
||||
.left
|
||||
width 120px
|
||||
position absolute
|
||||
top 6px
|
||||
left 50px
|
||||
.right
|
||||
width 120px
|
||||
position absolute
|
||||
top 6px
|
||||
right 44px
|
||||
</style>
|
37
src/components/card/PersonCount1.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="text">人员共计<spa class="value">{{ props.value }}</spa>人</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineProps } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.container
|
||||
width 140px
|
||||
height 70px
|
||||
background-color rgba(0, 0, 0, 0.3)
|
||||
background-image url(assets/imgs/card/bg5.png)
|
||||
background-repeat no-repeat
|
||||
background-size 100% 40%
|
||||
background-position bottom
|
||||
.text
|
||||
font-size 18px
|
||||
font-weight bold
|
||||
color #FFFFFF
|
||||
text-align center
|
||||
.value
|
||||
padding 5px
|
||||
font-size 28px
|
||||
color yellow
|
||||
</style>
|
47
src/components/card/PersonCount2.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="container" :style="{backgroundImage: `url(${bg})`}">
|
||||
<div class="value">{{ props.value }}</div>
|
||||
<div class="title">{{ props.title }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, defineProps} from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '标题'
|
||||
},
|
||||
bgType: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
})
|
||||
|
||||
console.log(props.bg);
|
||||
|
||||
const bg = ref('assets/imgs/card/bg6.png');
|
||||
if(props.bgType === 2) {
|
||||
bg.value = 'assets/imgs/card/bg7.png';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.container
|
||||
width 120px
|
||||
height 80px
|
||||
background-size 140% 100%
|
||||
background-position center
|
||||
text-align center
|
||||
color #FFF
|
||||
.value
|
||||
font-size 20px
|
||||
font-weight bold
|
||||
.title
|
||||
font-size 12px
|
||||
</style>
|
50
src/components/card/PersonPercent.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<img class="icon" src="assets/imgs/icon/man.png">
|
||||
<div class="value">{{ props.value }}%</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineProps } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'man'
|
||||
}
|
||||
});
|
||||
const color = ref('#00bab4');
|
||||
if(props.type != 'man') {
|
||||
color.value = '#ff5329';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.container
|
||||
width 150px
|
||||
height 100px
|
||||
padding 20px 10px
|
||||
position relative
|
||||
&:after
|
||||
content ' '
|
||||
width 40px
|
||||
height 2px
|
||||
background-color v-bind(color)
|
||||
position absolute
|
||||
top 22px
|
||||
left 48px
|
||||
.icon
|
||||
width 40px
|
||||
.value
|
||||
font-size 20px
|
||||
font-weight bold
|
||||
color v-bind(color)
|
||||
position absolute
|
||||
top 6px
|
||||
left 92px
|
||||
</style>
|
42
src/components/circle/CircleWave.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<svg class="svg" width="200" height="200" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Wave -->
|
||||
<g id="wave">
|
||||
<path id="wave-2" :fill="color" d="M 0 100 C 133.633 85.12 51.54 116.327 200 100 A 95 95 0 0 1 0 100 Z">
|
||||
<animate dur="5s" repeatCount="indefinite" attributeName="d" attributeType="XML" values="M0 100 C90 28, 92 179, 200 100 A95 95 0 0 1 0 100 Z;
|
||||
M0 100 C145 100, 41 100, 200 100 A95 95 0 0 1 0 100 Z;
|
||||
M0 100 C90 28, 92 179, 200 100 A95 95 0 0 1 0 100 Z"></animate>
|
||||
</path>
|
||||
</g>
|
||||
<text text-anchor="middle" font-size="42px" transform="translate(100,120)" :style="{fill: props.textColor}">{{ props.value }}%</text>
|
||||
<circle cx="100" cy="100" r="96" stroke-width="6" stroke="rgba(255,255,255,0.5)" fill="transparent"></circle>
|
||||
<circle cx="100" cy="100" r="98" stroke-width="2" :stroke="color" fill="none" class="percentage-pie-svg"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, defineProps} from 'vue';
|
||||
const props = defineProps({
|
||||
color: {
|
||||
type: String,
|
||||
default: 'blue'
|
||||
},
|
||||
textColor: {
|
||||
type: String,
|
||||
default: 'white'
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 20.0
|
||||
}
|
||||
})
|
||||
const color = ref(props.color);
|
||||
|
||||
</script>
|
||||
<style lang="stylus" scoped>
|
||||
.container
|
||||
.svg
|
||||
filter drop-shadow(0 0 5px v-bind(color))
|
||||
</style>
|
69
src/components/echarts/Bar.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div :id="domId" :style="{ width: w, height: h }"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineProps, inject } from 'vue';
|
||||
import { onMounted } from 'vue';
|
||||
const props = defineProps({
|
||||
id: String,
|
||||
w: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
h: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '柱状图'
|
||||
},
|
||||
xData: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
});
|
||||
const echarts = inject('echarts');
|
||||
const domId = ref(props.id);
|
||||
const w = ref(`${props.w}px`);
|
||||
const h = ref(`${props.h}px`);
|
||||
|
||||
const init = () => {
|
||||
var chart = echarts.init(document.getElementById(domId.value));
|
||||
chart.setOption({
|
||||
title: {
|
||||
text: props.title
|
||||
},
|
||||
tooltip: {},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: props.xData
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [{
|
||||
itemStyle: {
|
||||
opacity: 1
|
||||
},
|
||||
data: props.data,
|
||||
type: 'bar'
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
</style>
|
77
src/components/echarts/BarSpike.vue
Normal file
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div :id="domId" :style="{ width: w, height: h }"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineProps, inject } from 'vue';
|
||||
import { onMounted } from 'vue';
|
||||
const props = defineProps({
|
||||
id: String,
|
||||
w: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
h: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '尖峰图'
|
||||
},
|
||||
xData: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
});
|
||||
const echarts = inject('echarts');
|
||||
const domId = ref(props.id);
|
||||
const w = ref(`${props.w}px`);
|
||||
const h = ref(`${props.h}px`);
|
||||
|
||||
const init = () => {
|
||||
var chart = echarts.init(document.getElementById(domId.value));
|
||||
chart.setOption({
|
||||
title: {
|
||||
text: props.title
|
||||
},
|
||||
tooltip: {},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: props.xData
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [{
|
||||
type: 'pictorialBar',
|
||||
barCategoryGap: '0%',
|
||||
symbol: 'path://M0,10 L10,10 C5.5,10 5.5,5 5,0 C4.5,5 4.5,10 0,10 z',
|
||||
itemStyle: {
|
||||
opacity: 0.6
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
opacity: 1
|
||||
}
|
||||
},
|
||||
z: 10,
|
||||
data: props.data,
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
</style>
|
226
src/components/echarts/PolarHalf.vue
Normal file
@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div class="container" :style="{ width: w, height: h }">
|
||||
<div class="card" :style="{scale: cardScale, left: cardL, top: cardT}">
|
||||
<div :id="domId" class="chart"></div>
|
||||
<div class="info">
|
||||
<div class="label" v-for="item in infoLabelDataArray" :key="item">
|
||||
<span class="color" :style="{backgroundColor: item.color}">{{ item.item }}</span>
|
||||
<span class="title">{{ item.title }}</span>
|
||||
<span class="percent">{{ item.percent }}%</span>
|
||||
<span class="count">
|
||||
<span :style="{color: item.color}">{{ item.value }}</span>{{ props.unit }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineProps, inject, reactive } from 'vue';
|
||||
import { onMounted } from 'vue';
|
||||
import { listColorArray } from '../../utils/utils'
|
||||
const props = defineProps({
|
||||
id: String,
|
||||
w: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '极坐标-半圆'
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
unit: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
const echarts = inject('echarts');
|
||||
const domId = ref(props.id);
|
||||
const cardScale = ref(props.w / 600);
|
||||
const w = ref(`${props.w}px`);
|
||||
const h = ref(`${300 * cardScale.value}px`);
|
||||
const cardL = ref(`${ (props.w - 600) / 4 }px`);
|
||||
const cardT = ref(`${ (props.w - 600) / 4 }px`);
|
||||
const colors = listColorArray();
|
||||
const bgColor = '#0C1B32';
|
||||
const infoLabelDataArray = reactive([]);
|
||||
const init = () => {
|
||||
// 标签
|
||||
let labelArray = [];
|
||||
// 背景
|
||||
let seriesBgArray = [];
|
||||
let dataArray = [];
|
||||
let totalValue = 0;
|
||||
props.data.forEach((item, index) => {
|
||||
totalValue += item.value
|
||||
});
|
||||
props.data.forEach((item, index) => {
|
||||
labelArray.push(item.name);
|
||||
seriesBgArray.push({
|
||||
value: 180,
|
||||
itemStyle: {
|
||||
color: bgColor
|
||||
}
|
||||
});
|
||||
dataArray.push({
|
||||
value: (item.value / totalValue).toFixed(2) * 180,
|
||||
itemStyle: {
|
||||
color: colors[index],
|
||||
}
|
||||
})
|
||||
infoLabelDataArray.push({
|
||||
color: colors[index],
|
||||
title: item.name,
|
||||
percent: (item.value / totalValue * 100).toFixed(2),
|
||||
value: item.value
|
||||
})
|
||||
})
|
||||
|
||||
var chart = echarts.init(document.getElementById(domId.value));
|
||||
chart.setOption({
|
||||
title: {
|
||||
text: props.title
|
||||
},
|
||||
polar: {
|
||||
radius: [30, '80%'],
|
||||
},
|
||||
angleAxis: {
|
||||
max: 360,
|
||||
startAngle: 90,
|
||||
clockwise: false,
|
||||
axisLine: false,
|
||||
splitLine: false,
|
||||
},
|
||||
radiusAxis: {
|
||||
type: 'category',
|
||||
axisLine: false,
|
||||
data: labelArray,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 9,
|
||||
z: 1,
|
||||
animationDuration: 1500, // 动画时间
|
||||
coordinateSystem: 'polar',
|
||||
barGap: '-100%',
|
||||
showBackground: false,
|
||||
backgroundStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 1,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 0,
|
||||
colorStops: [
|
||||
{ offset: 1, color: bgColor },
|
||||
{ offset: 1, color: bgColor },
|
||||
{ offset: 1, color: bgColor },
|
||||
{ offset: 1, color: bgColor },
|
||||
{ offset: 1, color: bgColor },
|
||||
],
|
||||
global: false,
|
||||
},
|
||||
},
|
||||
data: seriesBgArray,
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 10,
|
||||
z: 2,
|
||||
coordinateSystem: 'polar',
|
||||
showBackground: true,
|
||||
backgroundStyle: {
|
||||
color: 'transparent',
|
||||
},
|
||||
data: dataArray
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.container
|
||||
position relative
|
||||
.card
|
||||
position absolute
|
||||
top 0
|
||||
left 0
|
||||
.chart
|
||||
width 300px
|
||||
height: 300px
|
||||
.info
|
||||
position absolute
|
||||
top 25px
|
||||
left 155px
|
||||
width 350px
|
||||
height 250px
|
||||
background-color blue
|
||||
padding 0 20px
|
||||
box-sizing border-box
|
||||
display flex
|
||||
flex-direction column
|
||||
justify-content space-around
|
||||
.label
|
||||
font-size 14px
|
||||
cursor pointer
|
||||
padding 3px 0
|
||||
span
|
||||
display inline-block
|
||||
height 30px
|
||||
line-height 30px
|
||||
vertical-align middle
|
||||
color #FFFFFF
|
||||
font-weight bold
|
||||
margin 0 5px
|
||||
.color
|
||||
width 30px
|
||||
height 15px
|
||||
background-color yellow
|
||||
.title
|
||||
width 100px
|
||||
.percent
|
||||
width 60px
|
||||
text-align right
|
||||
.count
|
||||
width 80px
|
||||
text-align right
|
||||
&:hover
|
||||
background linear-gradient(90deg, transparent, black)
|
||||
position relative
|
||||
&::after
|
||||
content ' '
|
||||
width 18px
|
||||
height 30px
|
||||
display block
|
||||
box-sizing border-box
|
||||
border-style solid
|
||||
border-color transparent transparent transparent black
|
||||
border-top-width 18px
|
||||
border-left-width 9px
|
||||
border-right-width 9px
|
||||
border-bottom-width 18px
|
||||
position absolute
|
||||
top 0px
|
||||
right -18px
|
||||
.divider
|
||||
width 5px
|
||||
height 105px
|
||||
top 32px
|
||||
left 158px
|
||||
background-color #EEEEEE
|
||||
position absolute
|
||||
|
||||
</style>
|
93
src/components/process/Process.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="left" v-if="props.icon">
|
||||
<img :src="props.icon"/>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="top">
|
||||
<div class="title">{{ props.title }}</div>
|
||||
<div class="sub-title">{{ props.value }}</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="process">
|
||||
<div class="value" :style="{width: (props.process * 300) + 'px'}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineProps } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: 'rgba(0, 0, 255)'
|
||||
},
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: 'rgba(0, 0, 255, 0.3)'
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '标题'
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: '0'
|
||||
},
|
||||
process: {
|
||||
type: Number,
|
||||
default: 0.8
|
||||
}
|
||||
});
|
||||
const color = ref(props.color)
|
||||
const bgColor = ref(props.bgColor)
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.container
|
||||
display flex
|
||||
.left
|
||||
margin-right 10px
|
||||
img
|
||||
width 50px
|
||||
height 50px
|
||||
.right
|
||||
display flex
|
||||
flex-direction column
|
||||
.top
|
||||
display flex
|
||||
justify-content space-between
|
||||
align-items center
|
||||
height 22px
|
||||
margin 5px 0
|
||||
.title
|
||||
font-size 16px
|
||||
.sub-title
|
||||
font-size 12px
|
||||
color v-bind(color)
|
||||
.bottom
|
||||
width 300px
|
||||
.process
|
||||
background-color v-bind(bgColor)
|
||||
width 100%
|
||||
height 10px
|
||||
position relative
|
||||
.value
|
||||
background linear-gradient(90deg, v-bind(color), #FFF)
|
||||
box-shadow 0 0 3px rgba(0, 0, 0, 0.3)
|
||||
position absolute
|
||||
top 0
|
||||
left 0
|
||||
width 120px
|
||||
height: 10px
|
||||
border-radius 0 5px 5px 0
|
||||
|
||||
</style>
|
0
src/components/table/ScrollTable.vue
Normal file
14
src/main.js
Normal file
@ -0,0 +1,14 @@
|
||||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
import * as echarts from 'echarts';
|
||||
import App from './App.vue'
|
||||
|
||||
const createEcharts = {
|
||||
install(app, options) {
|
||||
app.provide('echarts', echarts);
|
||||
}
|
||||
}
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(createEcharts);
|
||||
app.mount('#app');
|
20
src/style.css
Normal file
@ -0,0 +1,20 @@
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%
|
||||
}
|
3
src/utils/utils.js
Normal file
@ -0,0 +1,3 @@
|
||||
export const listColorArray = () => {
|
||||
return ['red', 'orange', 'yello', 'green', 'blue', 'grey'];
|
||||
}
|
7
vite.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
})
|