Compare commits
No commits in common. "main" and "develop" have entirely different histories.
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar"]
|
||||||
|
}
|
34
README.md
34
README.md
@ -1,3 +1,33 @@
|
|||||||
# k-line
|
# asideviewts
|
||||||
|
|
||||||
k线图
|
This template should help get you started developing with Vue 3 in Vite.
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||||
|
|
||||||
|
## Type Support for `.vue` Imports in TS
|
||||||
|
|
||||||
|
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
||||||
|
|
||||||
|
## Customize configuration
|
||||||
|
|
||||||
|
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Hot-Reload for Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type-Check, Compile and Minify for Production
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
1
env.d.ts
vendored
Normal file
1
env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
20
index.html
Normal file
20
index.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Vite App</title>
|
||||||
|
<style>
|
||||||
|
body{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
3513
package-lock.json
generated
Normal file
3513
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
package.json
Normal file
32
package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "asideviewts",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "run-p type-check \"build-only {@}\" --",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"build-only": "vite build",
|
||||||
|
"type-check": "vue-tsc --build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"echarts": "^5.6.0",
|
||||||
|
"element-plus": "^2.9.1",
|
||||||
|
"pinia": "^2.3.1",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-router": "^4.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tsconfig/node22": "^22.0.0",
|
||||||
|
"@types/node": "^22.10.2",
|
||||||
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"@vue/tsconfig": "^0.7.0",
|
||||||
|
"npm-run-all2": "^7.0.2",
|
||||||
|
"typescript": "~5.6.3",
|
||||||
|
"vite": "^6.0.5",
|
||||||
|
"vite-plugin-vue-devtools": "^7.6.8",
|
||||||
|
"vue-tsc": "^2.1.10"
|
||||||
|
}
|
||||||
|
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
9
src/App.vue
Normal file
9
src/App.vue
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import { RouterView } from 'vue-router'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<RouterView />
|
||||||
|
</template>
|
||||||
|
|
101
src/assets/base.css
Normal file
101
src/assets/base.css
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/* color palette from <https://github.com/vuejs/theme> */
|
||||||
|
:root {
|
||||||
|
--vt-c-white: #ffffff;
|
||||||
|
--vt-c-white-soft: #f8f8f8;
|
||||||
|
--vt-c-white-mute: #f2f2f2;
|
||||||
|
|
||||||
|
--vt-c-black: #181818;
|
||||||
|
--vt-c-black-soft: #222222;
|
||||||
|
--vt-c-black-mute: #282828;
|
||||||
|
|
||||||
|
--vt-c-indigo: #2c3e50;
|
||||||
|
|
||||||
|
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||||
|
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||||
|
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||||
|
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||||
|
|
||||||
|
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||||
|
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||||
|
--vt-c-text-dark-1: var(--vt-c-white);
|
||||||
|
--vt-c-text-dark-2: rgb(168, 168, 168);
|
||||||
|
|
||||||
|
|
||||||
|
--my-common-bgc-1:#fff;
|
||||||
|
--my-common-bgc-2:#1F1F1F;
|
||||||
|
--my-common-fc-1:#fff;
|
||||||
|
--my-common-fc-2:#000;
|
||||||
|
|
||||||
|
--my-light-bgc-1: #ffffff;
|
||||||
|
--my-light-bgc-2: #000000;
|
||||||
|
|
||||||
|
--my-light-br-1: #e7ebf3;
|
||||||
|
--my-light-br-2: #ffffff;
|
||||||
|
|
||||||
|
--my-light-fc-1: #414040;
|
||||||
|
--my-light-fc-2: #414040;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* semantic color variables for this project */
|
||||||
|
/* :root {
|
||||||
|
--color-background: var(--vt-c-white);
|
||||||
|
--color-background-soft: var(--vt-c-white-soft);
|
||||||
|
--color-background-mute: var(--vt-c-white-mute);
|
||||||
|
|
||||||
|
--color-border: var(--vt-c-divider-light-2);
|
||||||
|
--color-border-hover: var(--vt-c-divider-light-1);
|
||||||
|
|
||||||
|
--color-heading: var(--vt-c-text-light-1);
|
||||||
|
--color-text: var(--vt-c-text-light-1);
|
||||||
|
|
||||||
|
--section-gap: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--color-background: var(--vt-c-black);
|
||||||
|
--color-background-soft: var(--vt-c-black-soft);
|
||||||
|
--color-background-mute: var(--vt-c-black-mute);
|
||||||
|
|
||||||
|
--color-border: var(--vt-c-divider-dark-2);
|
||||||
|
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||||
|
|
||||||
|
--color-heading: var(--vt-c-text-dark-1);
|
||||||
|
--color-text: var(--vt-c-text-dark-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
color: var(--color-text);
|
||||||
|
background: var(--color-background);
|
||||||
|
transition:
|
||||||
|
color 0.5s,
|
||||||
|
background-color 0.5s;
|
||||||
|
line-height: 1.6;
|
||||||
|
font-family:
|
||||||
|
Inter,
|
||||||
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
'Segoe UI',
|
||||||
|
Roboto,
|
||||||
|
Oxygen,
|
||||||
|
Ubuntu,
|
||||||
|
Cantarell,
|
||||||
|
'Fira Sans',
|
||||||
|
'Droid Sans',
|
||||||
|
'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
font-size: 15px;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
} */
|
1
src/assets/logo.svg
Normal file
1
src/assets/logo.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
After Width: | Height: | Size: 276 B |
35
src/assets/main.css
Normal file
35
src/assets/main.css
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
@import './base.css';
|
||||||
|
|
||||||
|
#app {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
.green {
|
||||||
|
text-decoration: none;
|
||||||
|
color: hsla(160, 100%, 37%, 1);
|
||||||
|
transition: 0.4s;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
a:hover {
|
||||||
|
background-color: hsla(160, 100%, 37%, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
|
}
|
840
src/end/DayDesign.vue
Normal file
840
src/end/DayDesign.vue
Normal file
@ -0,0 +1,840 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onMounted, inject, ref, nextTick, onUnmounted, onBeforeUnmount } from 'vue'
|
||||||
|
|
||||||
|
// 引入pinia
|
||||||
|
import useEcharts from '@/store/usethemestore'
|
||||||
|
const echartsState = useEcharts()
|
||||||
|
echartsState.optionConfigData()
|
||||||
|
|
||||||
|
//引入echarts
|
||||||
|
let echarts = inject('echarts')
|
||||||
|
|
||||||
|
// 外层盒子容器,为了计算图形在内部的高度分配问题
|
||||||
|
const chartsInfo = ref(null)
|
||||||
|
// K线图的容器
|
||||||
|
const mKline = ref(null)
|
||||||
|
|
||||||
|
// 存储各个图形所分配的高度信息
|
||||||
|
let heightList = ref([])
|
||||||
|
// 计算高度、设置高度
|
||||||
|
const dynamicsHeight = () => {
|
||||||
|
// 高度
|
||||||
|
let domHeightList = chartsInfo.value.offsetHeight
|
||||||
|
// 判断是否进行了底部选项的点击事件,如果点击了则会往数组中添加信息,则长度就会改变
|
||||||
|
|
||||||
|
if (clicklist.value.length) {
|
||||||
|
// 先清空数组,避免
|
||||||
|
heightList.value = []
|
||||||
|
// 将处理过后的数据加入数组
|
||||||
|
heightList.value.push(domHeightList * (1 - clicklist.value.length * 0.15))
|
||||||
|
// 设置最上面K线图的高度
|
||||||
|
mKline.value.style.height = `${heightList.value[0]}px`
|
||||||
|
// 设置新添加图表的高度
|
||||||
|
heightList.value.push(domHeightList * 0.15)
|
||||||
|
|
||||||
|
console.log(heightList.value)
|
||||||
|
} else {
|
||||||
|
// 先清空数组,避免之前的数据没有被清空
|
||||||
|
heightList.value = []
|
||||||
|
|
||||||
|
// 设置只有K线图的高度的父容器的全部大小
|
||||||
|
mKline.value.style.height = `${domHeightList}px`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义图表配置数组
|
||||||
|
let chartConfigs = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'MAVOL',
|
||||||
|
attId: 'charts1',
|
||||||
|
status: false,
|
||||||
|
options: {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis', // 触发类型为坐标轴
|
||||||
|
axisPointer: {
|
||||||
|
// 坐标轴指示器
|
||||||
|
type: 'cross', // 类型为交叉
|
||||||
|
label: {
|
||||||
|
backgroundColor: '#6a7985', // 标签背景色
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
xAxisIndex: [0, 0],
|
||||||
|
start: 20,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'Email',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
data: [120, 132, 101, 134, 90, 230, 210],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Union Ads',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
data: [220, 182, 191, 234, 290, 330, 310],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Video Ads',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
data: [150, 232, 201, 154, 190, 330, 410],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Direct',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
data: [320, 332, 301, 334, 390, 330, 320],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Search Engine',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'VOL',
|
||||||
|
attId: 'charts2',
|
||||||
|
status: false,
|
||||||
|
options: {
|
||||||
|
// title: {
|
||||||
|
// text: 'Stacked Area Chart',
|
||||||
|
// },
|
||||||
|
// tooltip: {
|
||||||
|
// trigger: 'axis',
|
||||||
|
// axisPointer: {
|
||||||
|
// type: 'cross',
|
||||||
|
// label: {
|
||||||
|
// backgroundColor: '#6a7985',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// legend: {
|
||||||
|
// data: ['Email', 'Union Ads', 'Video Ads', 'Direct', 'Search Engine'],
|
||||||
|
// },
|
||||||
|
// toolbox: {
|
||||||
|
// feature: {
|
||||||
|
// saveAsImage: {},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// grid: {
|
||||||
|
// left: '3%',
|
||||||
|
// right: '4%',
|
||||||
|
// bottom: '3%',
|
||||||
|
// containLabel: true,
|
||||||
|
// },
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
xAxisIndex: [0, 0],
|
||||||
|
start: 20,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'Email',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
areaStyle: {},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [120, 132, 101, 134, 90, 230, 210],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Union Ads',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
areaStyle: {},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [220, 182, 191, 234, 290, 330, 310],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Video Ads',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
areaStyle: {},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [150, 232, 201, 154, 190, 330, 410],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Direct',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
areaStyle: {},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [320, 332, 301, 334, 390, 330, 320],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Search Engine',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
},
|
||||||
|
areaStyle: {},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'MACD',
|
||||||
|
attId: 'charts3',
|
||||||
|
status: false,
|
||||||
|
options:{
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
xAxisIndex: [0, 0],
|
||||||
|
start: 20,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: 'KDJ',
|
||||||
|
attId: 'charts4',
|
||||||
|
status: false,
|
||||||
|
options:{
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
xAxisIndex: [0, 0],
|
||||||
|
start: 20,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: 'RSI',
|
||||||
|
attId: 'charts5',
|
||||||
|
status: false,
|
||||||
|
options:{
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
xAxisIndex: [0, 0],
|
||||||
|
start: 20,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// 储存点击底部选项的状态的数据
|
||||||
|
const clicklist = ref([])
|
||||||
|
|
||||||
|
// 处理点击事件并添加图表
|
||||||
|
const addChart = (item) => {
|
||||||
|
// 判断当前点击的图表的状态,如果已经存在则不进行操作
|
||||||
|
if (clicklist.value.includes(item.name)) {
|
||||||
|
// 移除图表
|
||||||
|
echarts.dispose(item.name)
|
||||||
|
// 先移除当前的图表信息
|
||||||
|
clicklist.value = clicklist.value.filter((content) => content !== item.name)
|
||||||
|
// 遍历数组,找到对应名称的图表并设置状态为false
|
||||||
|
chartConfigs.value.forEach((element) => {
|
||||||
|
if (element.name == item.name) {
|
||||||
|
element.status = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 调用高度计算函数,重新分配高度
|
||||||
|
dynamicsHeight()
|
||||||
|
// 延迟执行,确保图表已经创建
|
||||||
|
nextTick(() => {
|
||||||
|
chartConfigs.value.forEach((element) => {
|
||||||
|
if (element.name == item.name) {
|
||||||
|
element.status = false
|
||||||
|
// 调用configecharts函数,刷新图表,防止图形重叠
|
||||||
|
configecharts()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 往数组里面添加参数信息
|
||||||
|
clicklist.value.push(item.name)
|
||||||
|
// 遍历数组,找到对应名称的图表并设置状态为true
|
||||||
|
chartConfigs.value.forEach((element) => {
|
||||||
|
if (element.name == item.name) {
|
||||||
|
element.status = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 调用高度计算函数,重新分配高度
|
||||||
|
dynamicsHeight()
|
||||||
|
// 延迟执行,确保图表已经创建
|
||||||
|
nextTick(() => {
|
||||||
|
chartConfigs.value.forEach((element) => {
|
||||||
|
if (element.name == item.name) {
|
||||||
|
element.status = true
|
||||||
|
// 调用configecharts函数,刷新图表,防止图形重叠
|
||||||
|
configecharts()
|
||||||
|
// 调用commonecharts函数,会根据传入的参数进行图表的绘制
|
||||||
|
commonecharts(item.attId, item.options)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const upColor = '#ec0000';
|
||||||
|
const upBorderColor = '#8A0000';
|
||||||
|
const downColor = '#00da3c';
|
||||||
|
const downBorderColor = '#008F28';
|
||||||
|
// Each item: open,close,lowest,highest
|
||||||
|
const data0 = splitData([
|
||||||
|
['2013/1/24', 2320.26, 2320.26, 2287.3, 2362.94],
|
||||||
|
['2013/1/25', 2300, 2291.3, 2288.26, 2308.38],
|
||||||
|
['2013/1/28', 2295.35, 2346.5, 2295.35, 2346.92],
|
||||||
|
['2013/1/29', 2347.22, 2358.98, 2337.35, 2363.8],
|
||||||
|
['2013/1/30', 2360.75, 2382.48, 2347.89, 2383.76],
|
||||||
|
['2013/1/31', 2383.43, 2385.42, 2371.23, 2391.82],
|
||||||
|
['2013/2/1', 2377.41, 2419.02, 2369.57, 2421.15],
|
||||||
|
['2013/2/4', 2425.92, 2428.15, 2417.58, 2440.38],
|
||||||
|
['2013/2/5', 2411, 2433.13, 2403.3, 2437.42],
|
||||||
|
['2013/2/6', 2432.68, 2434.48, 2427.7, 2441.73],
|
||||||
|
['2013/2/7', 2430.69, 2418.53, 2394.22, 2433.89],
|
||||||
|
['2013/2/8', 2416.62, 2432.4, 2414.4, 2443.03],
|
||||||
|
['2013/2/18', 2441.91, 2421.56, 2415.43, 2444.8],
|
||||||
|
['2013/2/19', 2420.26, 2382.91, 2373.53, 2427.07],
|
||||||
|
['2013/2/20', 2383.49, 2397.18, 2370.61, 2397.94],
|
||||||
|
['2013/2/21', 2378.82, 2325.95, 2309.17, 2378.82],
|
||||||
|
['2013/2/22', 2322.94, 2314.16, 2308.76, 2330.88],
|
||||||
|
['2013/2/25', 2320.62, 2325.82, 2315.01, 2338.78],
|
||||||
|
['2013/2/26', 2313.74, 2293.34, 2289.89, 2340.71],
|
||||||
|
['2013/2/27', 2297.77, 2313.22, 2292.03, 2324.63],
|
||||||
|
['2013/2/28', 2322.32, 2365.59, 2308.92, 2366.16],
|
||||||
|
['2013/3/1', 2364.54, 2359.51, 2330.86, 2369.65],
|
||||||
|
['2013/3/4', 2332.08, 2273.4, 2259.25, 2333.54],
|
||||||
|
['2013/3/5', 2274.81, 2326.31, 2270.1, 2328.14],
|
||||||
|
['2013/3/6', 2333.61, 2347.18, 2321.6, 2351.44],
|
||||||
|
['2013/3/7', 2340.44, 2324.29, 2304.27, 2352.02],
|
||||||
|
['2013/3/8', 2326.42, 2318.61, 2314.59, 2333.67],
|
||||||
|
['2013/3/11', 2314.68, 2310.59, 2296.58, 2320.96],
|
||||||
|
['2013/3/12', 2309.16, 2286.6, 2264.83, 2333.29],
|
||||||
|
['2013/3/13', 2282.17, 2263.97, 2253.25, 2286.33],
|
||||||
|
['2013/3/14', 2255.77, 2270.28, 2253.31, 2276.22],
|
||||||
|
['2013/3/15', 2269.31, 2278.4, 2250, 2312.08],
|
||||||
|
['2013/3/18', 2267.29, 2240.02, 2239.21, 2276.05],
|
||||||
|
['2013/3/19', 2244.26, 2257.43, 2232.02, 2261.31],
|
||||||
|
['2013/3/20', 2257.74, 2317.37, 2257.42, 2317.86],
|
||||||
|
['2013/3/21', 2318.21, 2324.24, 2311.6, 2330.81],
|
||||||
|
['2013/3/22', 2321.4, 2328.28, 2314.97, 2332],
|
||||||
|
['2013/3/25', 2334.74, 2326.72, 2319.91, 2344.89],
|
||||||
|
['2013/3/26', 2318.58, 2297.67, 2281.12, 2319.99],
|
||||||
|
['2013/3/27', 2299.38, 2301.26, 2289, 2323.48],
|
||||||
|
['2013/3/28', 2273.55, 2236.3, 2232.91, 2273.55],
|
||||||
|
['2013/3/29', 2238.49, 2236.62, 2228.81, 2246.87],
|
||||||
|
['2013/4/1', 2229.46, 2234.4, 2227.31, 2243.95],
|
||||||
|
['2013/4/2', 2234.9, 2227.74, 2220.44, 2253.42],
|
||||||
|
['2013/4/3', 2232.69, 2225.29, 2217.25, 2241.34],
|
||||||
|
['2013/4/8', 2196.24, 2211.59, 2180.67, 2212.59],
|
||||||
|
['2013/4/9', 2215.47, 2225.77, 2215.47, 2234.73],
|
||||||
|
['2013/4/10', 2224.93, 2226.13, 2212.56, 2233.04],
|
||||||
|
['2013/4/11', 2236.98, 2219.55, 2217.26, 2242.48],
|
||||||
|
['2013/4/12', 2218.09, 2206.78, 2204.44, 2226.26],
|
||||||
|
['2013/4/15', 2199.91, 2181.94, 2177.39, 2204.99],
|
||||||
|
['2013/4/16', 2169.63, 2194.85, 2165.78, 2196.43],
|
||||||
|
['2013/4/17', 2195.03, 2193.8, 2178.47, 2197.51],
|
||||||
|
['2013/4/18', 2181.82, 2197.6, 2175.44, 2206.03],
|
||||||
|
['2013/4/19', 2201.12, 2244.64, 2200.58, 2250.11],
|
||||||
|
['2013/4/22', 2236.4, 2242.17, 2232.26, 2245.12],
|
||||||
|
['2013/4/23', 2242.62, 2184.54, 2182.81, 2242.62],
|
||||||
|
['2013/4/24', 2187.35, 2218.32, 2184.11, 2226.12],
|
||||||
|
['2013/4/25', 2213.19, 2199.31, 2191.85, 2224.63],
|
||||||
|
['2013/4/26', 2203.89, 2177.91, 2173.86, 2210.58],
|
||||||
|
['2013/5/2', 2170.78, 2174.12, 2161.14, 2179.65],
|
||||||
|
['2013/5/3', 2179.05, 2205.5, 2179.05, 2222.81],
|
||||||
|
['2013/5/6', 2212.5, 2231.17, 2212.5, 2236.07],
|
||||||
|
['2013/5/7', 2227.86, 2235.57, 2219.44, 2240.26],
|
||||||
|
['2013/5/8', 2242.39, 2246.3, 2235.42, 2255.21],
|
||||||
|
['2013/5/9', 2246.96, 2232.97, 2221.38, 2247.86],
|
||||||
|
['2013/5/10', 2228.82, 2246.83, 2225.81, 2247.67],
|
||||||
|
['2013/5/13', 2247.68, 2241.92, 2231.36, 2250.85],
|
||||||
|
['2013/5/14', 2238.9, 2217.01, 2205.87, 2239.93],
|
||||||
|
['2013/5/15', 2217.09, 2224.8, 2213.58, 2225.19],
|
||||||
|
['2013/5/16', 2221.34, 2251.81, 2210.77, 2252.87],
|
||||||
|
['2013/5/17', 2249.81, 2282.87, 2248.41, 2288.09],
|
||||||
|
['2013/5/20', 2286.33, 2299.99, 2281.9, 2309.39],
|
||||||
|
['2013/5/21', 2297.11, 2305.11, 2290.12, 2305.3],
|
||||||
|
['2013/5/22', 2303.75, 2302.4, 2292.43, 2314.18],
|
||||||
|
['2013/5/23', 2293.81, 2275.67, 2274.1, 2304.95],
|
||||||
|
['2013/5/24', 2281.45, 2288.53, 2270.25, 2292.59],
|
||||||
|
['2013/5/27', 2286.66, 2293.08, 2283.94, 2301.7],
|
||||||
|
['2013/5/28', 2293.4, 2321.32, 2281.47, 2322.1],
|
||||||
|
['2013/5/29', 2323.54, 2324.02, 2321.17, 2334.33],
|
||||||
|
['2013/5/30', 2316.25, 2317.75, 2310.49, 2325.72],
|
||||||
|
['2013/5/31', 2320.74, 2300.59, 2299.37, 2325.53],
|
||||||
|
['2013/6/3', 2300.21, 2299.25, 2294.11, 2313.43],
|
||||||
|
['2013/6/4', 2297.1, 2272.42, 2264.76, 2297.1],
|
||||||
|
['2013/6/5', 2270.71, 2270.93, 2260.87, 2276.86],
|
||||||
|
['2013/6/6', 2264.43, 2242.11, 2240.07, 2266.69],
|
||||||
|
['2013/6/7', 2242.26, 2210.9, 2205.07, 2250.63],
|
||||||
|
['2013/6/13', 2190.1, 2148.35, 2126.22, 2190.1]
|
||||||
|
]);
|
||||||
|
function splitData(rawData) {
|
||||||
|
const categoryData = [];
|
||||||
|
const values = [];
|
||||||
|
for (var i = 0; i < rawData.length; i++) {
|
||||||
|
categoryData.push(rawData[i].splice(0, 1)[0]);
|
||||||
|
values.push(rawData[i]);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
categoryData: categoryData,
|
||||||
|
values: values
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function calculateMA(dayCount) {
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0, len = data0.values.length; i < len; i++) {
|
||||||
|
if (i < dayCount) {
|
||||||
|
result.push('-');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var sum = 0;
|
||||||
|
for (var j = 0; j < dayCount; j++) {
|
||||||
|
sum += +data0.values[i - j][1];
|
||||||
|
}
|
||||||
|
result.push(sum / dayCount);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 绘制初始的Echarts图表,始终会在页面中。并返回当前图表的实例化信息,便于使用echarts.connect()进行连接
|
||||||
|
const configecharts = (options = {}) => {
|
||||||
|
// 先将容器中的_echarts_instance_清除,不处理会导致高度变化了但图表不刷新
|
||||||
|
document.getElementById('main').removeAttribute('_echarts_instance_')
|
||||||
|
// 初始化k线图图表
|
||||||
|
let chartBox = echarts.init(document.getElementById('main'))
|
||||||
|
// 直接使用pinia中储存的option数据
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'cross'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['日K', 'MA5', 'MA10', 'MA20', 'MA30']
|
||||||
|
},
|
||||||
|
grid: [{
|
||||||
|
left: '3%',
|
||||||
|
right: '3%',
|
||||||
|
bottom: '5%',
|
||||||
|
}],
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: data0.categoryData,
|
||||||
|
boundaryGap: false,
|
||||||
|
axisLine: { onZero: false },
|
||||||
|
splitLine: { show: false },
|
||||||
|
min: 'dataMin',
|
||||||
|
max: 'dataMax'
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
scale: true,
|
||||||
|
splitArea: {
|
||||||
|
show: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 50,
|
||||||
|
end: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
show: true,
|
||||||
|
type: 'slider',
|
||||||
|
top: '90%',
|
||||||
|
start: 50,
|
||||||
|
end: 100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '日K',
|
||||||
|
type: 'candlestick',
|
||||||
|
data: data0.values,
|
||||||
|
itemStyle: {
|
||||||
|
color: upColor,
|
||||||
|
color0: downColor,
|
||||||
|
borderColor: upBorderColor,
|
||||||
|
borderColor0: downBorderColor
|
||||||
|
},
|
||||||
|
markPoint: {
|
||||||
|
label: {
|
||||||
|
formatter: function (param) {
|
||||||
|
return param != null ? Math.round(param.value) + '' : '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
name: 'Mark',
|
||||||
|
coord: ['2013/5/31', 2300],
|
||||||
|
value: 2300,
|
||||||
|
itemStyle: {
|
||||||
|
color: 'rgb(41,60,85)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'highest value',
|
||||||
|
type: 'max',
|
||||||
|
valueDim: 'highest'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lowest value',
|
||||||
|
type: 'min',
|
||||||
|
valueDim: 'lowest'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'average value on close',
|
||||||
|
type: 'average',
|
||||||
|
valueDim: 'close'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tooltip: {
|
||||||
|
formatter: function (param) {
|
||||||
|
return param.name + '<br>' + (param.data.coord || '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
markLine: {
|
||||||
|
symbol: ['none', 'none'],
|
||||||
|
data: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'from lowest to highest',
|
||||||
|
type: 'min',
|
||||||
|
valueDim: 'lowest',
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 10,
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'max',
|
||||||
|
valueDim: 'highest',
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 10,
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
name: 'min line on close',
|
||||||
|
type: 'min',
|
||||||
|
valueDim: 'close'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'max line on close',
|
||||||
|
type: 'max',
|
||||||
|
valueDim: 'close'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MA5',
|
||||||
|
type: 'line',
|
||||||
|
data: calculateMA(5),
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MA10',
|
||||||
|
type: 'line',
|
||||||
|
data: calculateMA(10),
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MA20',
|
||||||
|
type: 'line',
|
||||||
|
data: calculateMA(20),
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MA30',
|
||||||
|
type: 'line',
|
||||||
|
data: calculateMA(30),
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
// 配置图表数据信息
|
||||||
|
chartBox.setOption(option)
|
||||||
|
|
||||||
|
// mergeOptions方法可以合并两个对象,并返回合并后的对象。
|
||||||
|
// 更新信息,可有可无?,调用时配置
|
||||||
|
chartBox.setOption(options)
|
||||||
|
|
||||||
|
// 图表的分组,用于联动
|
||||||
|
chartBox.group = 'group1'
|
||||||
|
// 根据页面大小自动响应图表大小
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
chartBox.resize()
|
||||||
|
})
|
||||||
|
return chartBox
|
||||||
|
}
|
||||||
|
|
||||||
|
//绘制一个点击事件传参的Echarts图表。并返回当前图表的实例化信息,便于使用echarts.connect()进行连接
|
||||||
|
const commonecharts = (containerId, options = {}) => {
|
||||||
|
// 先将容器中的_echarts_instance_清除,不处理会导致高度变化了但图表不刷新
|
||||||
|
document.getElementById(containerId).removeAttribute('_echarts_instance_')
|
||||||
|
// 初始化图表
|
||||||
|
const chart = echarts.init(document.getElementById(containerId))
|
||||||
|
// 图表的配置信息
|
||||||
|
const option = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: '10%',
|
||||||
|
left: '3%',
|
||||||
|
right: '3%',
|
||||||
|
bottom: '20%',
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
// 设置图表实例的配置项
|
||||||
|
chart.setOption(option)
|
||||||
|
// mergeOptions方法可以合并两个对象,并返回合并后的对象。
|
||||||
|
chart.setOption(options)
|
||||||
|
|
||||||
|
// 图表的分组,用于联动
|
||||||
|
chart.group = 'group1'
|
||||||
|
// 根据页面大小自动响应图表大小
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
chartBox2.resize()
|
||||||
|
})
|
||||||
|
return chart
|
||||||
|
}
|
||||||
|
// 连接所有图表
|
||||||
|
echarts.connect('group1')
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
dynamicsHeight()
|
||||||
|
configecharts()
|
||||||
|
})
|
||||||
|
onBeforeUnmount(()=>{
|
||||||
|
echarts.disconnect()
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="box">
|
||||||
|
<div class="chartsinfo" ref="chartsInfo">
|
||||||
|
<!-- K线图 -->
|
||||||
|
<div
|
||||||
|
id="main"
|
||||||
|
ref="mKline"
|
||||||
|
class="flex-item"
|
||||||
|
:style="{ height: `${heightList[0]}px` }"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="chartConfigs[0].status"
|
||||||
|
id="charts1"
|
||||||
|
ref="charts1"
|
||||||
|
:style="{ height: `${heightList[1]}px` }"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-if="chartConfigs[1].status"
|
||||||
|
id="charts2"
|
||||||
|
ref="charts2"
|
||||||
|
:style="{ height: `${heightList[1]}px` }"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-if="chartConfigs[2].status"
|
||||||
|
id="charts3"
|
||||||
|
ref="charts3"
|
||||||
|
:style="{ height: `${heightList[1]}px` }"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-if="chartConfigs[3].status"
|
||||||
|
id="charts4"
|
||||||
|
ref="charts4"
|
||||||
|
:style="{ height: `${heightList[1]}px` }"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-if="chartConfigs[4].status"
|
||||||
|
id="charts5"
|
||||||
|
ref="charts5"
|
||||||
|
:style="{ height: `${heightList[1]}px` }"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部的各个图表选项信息 -->
|
||||||
|
<ul class="typelist">
|
||||||
|
<li
|
||||||
|
v-for="(item, index) in chartConfigs"
|
||||||
|
:key="index"
|
||||||
|
@click="addChart(item)"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.box {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.chartsinfo {
|
||||||
|
height: calc(100vh - 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.typelist {
|
||||||
|
margin-top: auto;
|
||||||
|
position: relative;
|
||||||
|
z-index: 99999999;
|
||||||
|
color: #191919;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 12px;
|
||||||
|
display: flex;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typelist > li {
|
||||||
|
padding: 2px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
color: #2d98b9;
|
||||||
|
}
|
||||||
|
</style>
|
957
src/end/EndDesign.vue
Normal file
957
src/end/EndDesign.vue
Normal file
@ -0,0 +1,957 @@
|
|||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
onMounted,
|
||||||
|
inject,
|
||||||
|
ref,
|
||||||
|
nextTick,
|
||||||
|
onBeforeUnmount,
|
||||||
|
} from 'vue'
|
||||||
|
|
||||||
|
import {
|
||||||
|
calculateMA,
|
||||||
|
calculateStdDeviation,
|
||||||
|
calculateMACD,
|
||||||
|
calculateKDJ,
|
||||||
|
calculateRSI,
|
||||||
|
calculateVOL,
|
||||||
|
} from './computedInfo'
|
||||||
|
|
||||||
|
let charts1 = ref(null)
|
||||||
|
let charts2 = ref(null)
|
||||||
|
let charts3 = ref(null)
|
||||||
|
let charts4 = ref(null)
|
||||||
|
|
||||||
|
// 请求后端数据
|
||||||
|
let szdata = ref([])
|
||||||
|
let kdata = ref([])
|
||||||
|
let xdata = ref([])
|
||||||
|
let closedata = ref([])
|
||||||
|
let ma5 = ref()
|
||||||
|
// 标准差
|
||||||
|
let StdDev = ref()
|
||||||
|
let topStdDev = ref()
|
||||||
|
let downStdDev = ref()
|
||||||
|
|
||||||
|
let rsi1 = ref()
|
||||||
|
let rsi2 = ref()
|
||||||
|
let rsi3 = ref()
|
||||||
|
|
||||||
|
let vol = ref()
|
||||||
|
let interval = ref()
|
||||||
|
|
||||||
|
import request from '@/utils/request'
|
||||||
|
let baseURL = 'http://127.0.0.1:8015'
|
||||||
|
const redata = () => {
|
||||||
|
try {
|
||||||
|
request({
|
||||||
|
url: baseURL + '/stock',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
symbol: 'sz000001',
|
||||||
|
start_date: '2021-01-01',
|
||||||
|
end_date: '2022-12-31',
|
||||||
|
},
|
||||||
|
}).then((res) => {
|
||||||
|
xdata.value = Object.values(res.date)
|
||||||
|
let { open, close, low, high, volume } = res
|
||||||
|
for (let i = 0; i < Object.keys(open).length; i++) {
|
||||||
|
kdata.value.push([open[i], close[i], low[i], high[i]])
|
||||||
|
closedata.value.push(close[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算5日移动平均线
|
||||||
|
ma5.value = calculateMA(closedata.value, 5)
|
||||||
|
|
||||||
|
StdDev.value = calculateStdDeviation(closedata.value)
|
||||||
|
|
||||||
|
topStdDev.value = closedata.value.map((item) =>
|
||||||
|
parseFloat((item + StdDev.value).toFixed(2))
|
||||||
|
)
|
||||||
|
downStdDev.value = closedata.value.map((item) =>
|
||||||
|
parseFloat((item - StdDev.value).toFixed(2))
|
||||||
|
)
|
||||||
|
|
||||||
|
// MACD数据
|
||||||
|
let { dif, dea, macd } = calculateMACD(closedata.value)
|
||||||
|
|
||||||
|
// 准备数据数组,同时设置颜色
|
||||||
|
macd = macd.map(function (value) {
|
||||||
|
return {
|
||||||
|
value: value,
|
||||||
|
itemStyle: {
|
||||||
|
color: value >= 0 ? '#FF0000' : '#00FF00', // 根据值的正负设置颜色
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
chartConfigs.value[0].options.series[0].data = dif
|
||||||
|
chartConfigs.value[0].options.series[1].data = dea
|
||||||
|
chartConfigs.value[0].options.series[2].data = macd
|
||||||
|
chartConfigs.value[0].options.xAxis.data = xdata.value
|
||||||
|
|
||||||
|
// vol数据
|
||||||
|
vol.value = calculateVOL(close, high, low)
|
||||||
|
chartConfigs.value[1].options.xAxis.data = xdata.value
|
||||||
|
chartConfigs.value[1].options.series[0].data = vol.value
|
||||||
|
|
||||||
|
// KDJ数据
|
||||||
|
let { kValues, dValues, jValues } = calculateKDJ(res)
|
||||||
|
chartConfigs.value[2].options.series[0].data = kValues
|
||||||
|
chartConfigs.value[2].options.series[1].data = dValues
|
||||||
|
chartConfigs.value[2].options.series[2].data = jValues
|
||||||
|
chartConfigs.value[2].options.xAxis.data = xdata.value
|
||||||
|
|
||||||
|
// RSI数据
|
||||||
|
rsi1.value = calculateRSI(closedata.value)
|
||||||
|
rsi2.value = calculateRSI(closedata.value, 12)
|
||||||
|
rsi3.value = calculateRSI(closedata.value, 24)
|
||||||
|
chartConfigs.value[3].options.series[0].data = rsi1.value
|
||||||
|
chartConfigs.value[3].options.series[1].data = rsi2.value
|
||||||
|
chartConfigs.value[3].options.series[2].data = rsi3.value
|
||||||
|
chartConfigs.value[3].options.xAxis.data = xdata.value
|
||||||
|
|
||||||
|
dynamicsHeight()
|
||||||
|
configecharts()
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.error('请求失败:', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//引入echarts
|
||||||
|
let echarts = inject('echarts')
|
||||||
|
|
||||||
|
// 外层盒子容器,为了计算图形在内部的高度分配问题
|
||||||
|
const chartsInfo = ref(null)
|
||||||
|
// K线图的容器
|
||||||
|
const mKline = ref(null)
|
||||||
|
|
||||||
|
// 存储各个图形所分配的高度信息
|
||||||
|
let heightList = ref([])
|
||||||
|
// 计算高度、设置高度
|
||||||
|
const dynamicsHeight = () => {
|
||||||
|
// 高度
|
||||||
|
let domHeightList = chartsInfo.value.offsetHeight
|
||||||
|
// 判断是否进行了底部选项的点击事件,如果点击了则会往数组中添加信息,则长度就会改变
|
||||||
|
|
||||||
|
// if (clicklist.value.length) {
|
||||||
|
// // 先清空数组,避免
|
||||||
|
// heightList.value = []
|
||||||
|
// // 将处理过后的数据加入数组
|
||||||
|
// heightList.value.push(domHeightList * (1 - clicklist.value.length * 0.15))
|
||||||
|
// // 设置最上面K线图的高度
|
||||||
|
// mKline.value.style.height = `${heightList.value[0]}px`
|
||||||
|
// // 设置新添加图表的高度
|
||||||
|
// heightList.value.push(domHeightList * 0.15)
|
||||||
|
|
||||||
|
// console.log(heightList.value)
|
||||||
|
// } else {
|
||||||
|
// // 先清空数组,避免之前的数据没有被清空
|
||||||
|
// heightList.value = []
|
||||||
|
|
||||||
|
// // 设置只有K线图的高度的父容器的全部大小
|
||||||
|
// mKline.value.style.height = `${domHeightList}px`
|
||||||
|
// }
|
||||||
|
|
||||||
|
console.log(chartsInfo.value.children.length)
|
||||||
|
// 子元素长度
|
||||||
|
let childrenLength = chartsInfo.value.children.length
|
||||||
|
|
||||||
|
if (childrenLength) {
|
||||||
|
// 先清空数组,避免
|
||||||
|
heightList.value = []
|
||||||
|
// 将处理过后的数据加入数组
|
||||||
|
heightList.value.push(domHeightList * (1 - childrenLength * 0.15))
|
||||||
|
// 设置最上面K线图的高度
|
||||||
|
mKline.value.style.height = `${heightList.value[0]}px`
|
||||||
|
// 设置新添加图表的高度
|
||||||
|
heightList.value.push(domHeightList * 0.15)
|
||||||
|
|
||||||
|
console.log(heightList.value)
|
||||||
|
} else {
|
||||||
|
// 先清空数组,避免之前的数据没有被清空
|
||||||
|
heightList.value = []
|
||||||
|
|
||||||
|
// 设置只有K线图的高度的父容器的全部大小
|
||||||
|
mKline.value.style.height = `${domHeightList}px`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义图表配置数组
|
||||||
|
let chartConfigs = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'MACD',
|
||||||
|
attId: 'charts1',
|
||||||
|
status: false,
|
||||||
|
options: {
|
||||||
|
animation: false,
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis', // 触发类型为坐标轴
|
||||||
|
axisPointer: {
|
||||||
|
// 坐标轴指示器
|
||||||
|
type: 'cross', // 类型为交叉
|
||||||
|
label: {
|
||||||
|
backgroundColor: '#6a7985', // 标签背景色
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'slider',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'DIF',
|
||||||
|
type: 'line',
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: 'rgba(204,102,0,1)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
symbol: 'none',
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DEA',
|
||||||
|
type: 'line',
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: 'rgba(0,128,255,1)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
symbol: 'none',
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MACD',
|
||||||
|
type: 'bar',
|
||||||
|
barWidth: '1',
|
||||||
|
// symbol: 'none',
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'VOL',
|
||||||
|
attId: 'charts2',
|
||||||
|
status: false,
|
||||||
|
options: {
|
||||||
|
animation: false,
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'cross',
|
||||||
|
label: {
|
||||||
|
backgroundColor: '#6a7985',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'slider',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
// splitNumber: 3 ,
|
||||||
|
interval: 10,
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'VOL',
|
||||||
|
type: 'bar',
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'KDJ',
|
||||||
|
attId: 'charts3',
|
||||||
|
status: false,
|
||||||
|
options: {
|
||||||
|
animation: false,
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis', // 触发类型为坐标轴
|
||||||
|
axisPointer: {
|
||||||
|
// 坐标轴指示器
|
||||||
|
type: 'cross', // 类型为交叉
|
||||||
|
label: {
|
||||||
|
backgroundColor: '#6a7985', // 标签背景色
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
xAxisIndex: [0, 0],
|
||||||
|
start: 20,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'slider',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'K',
|
||||||
|
type: 'line',
|
||||||
|
data: [],
|
||||||
|
symbol: 'none',
|
||||||
|
smooth: true,
|
||||||
|
// lineStyle: {
|
||||||
|
// width: 1,
|
||||||
|
// },
|
||||||
|
// areaStyle: {
|
||||||
|
// opacity: 0.5,
|
||||||
|
// },
|
||||||
|
// itemStyle: {
|
||||||
|
// color: 'rgba(0,128,255,1)',
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'D',
|
||||||
|
type: 'line',
|
||||||
|
data: [],
|
||||||
|
symbol: 'none',
|
||||||
|
smooth: true,
|
||||||
|
// lineStyle: {
|
||||||
|
// width: 1,
|
||||||
|
// },
|
||||||
|
// areaStyle: {
|
||||||
|
// opacity: 0.5,
|
||||||
|
// },
|
||||||
|
// itemStyle: {
|
||||||
|
// color: 'rgba(0,128,255,1)',
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'J',
|
||||||
|
type: 'line',
|
||||||
|
data: [],
|
||||||
|
symbol: 'none',
|
||||||
|
smooth: true,
|
||||||
|
// lineStyle: {
|
||||||
|
// width: 1,
|
||||||
|
// },
|
||||||
|
// areaStyle: {
|
||||||
|
// opacity: 0.5,
|
||||||
|
// },
|
||||||
|
// itemStyle: {
|
||||||
|
// color: 'rgba(0,128,255,1)',
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: 'RSI',
|
||||||
|
attId: 'charts4',
|
||||||
|
status: false,
|
||||||
|
options: {
|
||||||
|
animation: false,
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis', // 触发类型为坐标轴
|
||||||
|
axisPointer: {
|
||||||
|
// 坐标轴指示器
|
||||||
|
type: 'cross', // 类型为交叉
|
||||||
|
label: {
|
||||||
|
backgroundColor: '#6a7985', // 标签背景色
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
xAxisIndex: [0, 0],
|
||||||
|
start: 20,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'slider',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'rsi1',
|
||||||
|
type: 'line',
|
||||||
|
data: [],
|
||||||
|
symbol: 'none',
|
||||||
|
smooth: true,
|
||||||
|
// lineStyle: {
|
||||||
|
// width: 1,
|
||||||
|
// },
|
||||||
|
// areaStyle: {
|
||||||
|
// opacity: 0.5,
|
||||||
|
// },
|
||||||
|
// itemStyle: {
|
||||||
|
// color: 'rgba(0,128,255,1)',
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'rsi2',
|
||||||
|
type: 'line',
|
||||||
|
data: [],
|
||||||
|
symbol: 'none',
|
||||||
|
smooth: true,
|
||||||
|
// lineStyle: {
|
||||||
|
// width: 1,
|
||||||
|
// },
|
||||||
|
// areaStyle: {
|
||||||
|
// opacity: 0.5,
|
||||||
|
// },
|
||||||
|
// itemStyle: {
|
||||||
|
// color: 'rgba(0,128,255,1)',
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'rsi3',
|
||||||
|
type: 'line',
|
||||||
|
data: [],
|
||||||
|
symbol: 'none',
|
||||||
|
smooth: true,
|
||||||
|
// lineStyle: {
|
||||||
|
// width: 1,
|
||||||
|
// },
|
||||||
|
// areaStyle: {
|
||||||
|
// opacity: 0.5,
|
||||||
|
// },
|
||||||
|
// itemStyle: {
|
||||||
|
// color: 'rgba(0,128,255,1)',
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// 储存点击底部选项的状态的数据
|
||||||
|
const clicklist = ref([])
|
||||||
|
|
||||||
|
// 处理点击事件并添加图表
|
||||||
|
const addChart = (item) => {
|
||||||
|
// 检查数组中是否已经存在具有相同domId的元素
|
||||||
|
// let domToRemove = chartBoxList.value.find(element => element.domId === item.attId)
|
||||||
|
// if(domToRemove){
|
||||||
|
// domToRemove.chart.dispose()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let indexToRemove = chartBoxList.value.findIndex(element => element.domId === item.attId)
|
||||||
|
|
||||||
|
// // 如果找到了,移除那个元素
|
||||||
|
// if (indexToRemove !== -1) {
|
||||||
|
// chartBoxList.value.splice(indexToRemove, 1)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 判断当前点击的图表的状态,如果已经存在则不进行操作
|
||||||
|
if (clicklist.value.includes(item.name)) {
|
||||||
|
// 先移除当前的图表信息
|
||||||
|
// clicklist.value = clicklist.value.filter((content) => content !== item.name)
|
||||||
|
|
||||||
|
// if (item.attId === 'charts1') {
|
||||||
|
// charts1.value.style.display = 'none'
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 调用高度计算函数,重新分配高度
|
||||||
|
dynamicsHeight()
|
||||||
|
// 延迟执行,确保图表已经创建
|
||||||
|
nextTick(() => {
|
||||||
|
chartConfigs.value.forEach((element) => {
|
||||||
|
if (element.name == item.name) {
|
||||||
|
element.status = false
|
||||||
|
|
||||||
|
// 调用configecharts函数,刷新图表,防止图形重叠
|
||||||
|
configecharts()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 往数组里面添加参数信息
|
||||||
|
// clicklist.value.push(item.name)
|
||||||
|
// 遍历数组,找到对应名称的图表并设置状态为true
|
||||||
|
chartConfigs.value.forEach((element) => {
|
||||||
|
if (element.name == item.name) {
|
||||||
|
element.status = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 调用高度计算函数,重新分配高度
|
||||||
|
dynamicsHeight()
|
||||||
|
// 延迟执行,确保图表已经创建
|
||||||
|
nextTick(() => {
|
||||||
|
chartConfigs.value.forEach((element) => {
|
||||||
|
if (element.name == item.name) {
|
||||||
|
element.status = true
|
||||||
|
|
||||||
|
// 调用configecharts函数,刷新图表,防止图形重叠
|
||||||
|
configecharts()
|
||||||
|
// 调用commonecharts函数,会根据传入的参数进行图表的绘制
|
||||||
|
// commonecharts(item.attId, item.options)
|
||||||
|
if (!clicklist.value.includes(item.name)) {
|
||||||
|
commonecharts(item.attId, item.options)
|
||||||
|
clicklist.value.push(item.name)
|
||||||
|
}
|
||||||
|
// if (item.attId === 'charts1') {
|
||||||
|
// charts1.value.style.display = 'block'
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制初始的Echarts图表,始终会在页面中。并返回当前图表的实例化信息,便于使用echarts.connect()进行连接
|
||||||
|
const configecharts = (options = {}) => {
|
||||||
|
// 先将容器中的_echarts_instance_清除,不处理会导致高度变化了但图表不刷新
|
||||||
|
document.getElementById('main').removeAttribute('_echarts_instance_')
|
||||||
|
// 初始化k线图图表
|
||||||
|
let chartBox = echarts.init(document.getElementById('main'))
|
||||||
|
// 直接使用pinia中储存的option数据
|
||||||
|
const option = {
|
||||||
|
animation: false,
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis', // 触发类型为坐标轴
|
||||||
|
axisPointer: {
|
||||||
|
// 坐标轴指示器
|
||||||
|
type: 'cross', // 类型为交叉
|
||||||
|
label: {
|
||||||
|
backgroundColor: '#6a7985', // 标签背景色
|
||||||
|
},
|
||||||
|
animation: false,
|
||||||
|
},
|
||||||
|
// formatter: (params)=> {
|
||||||
|
// let str = '<div style="padding: 3px 12px;width: 161px;background: #FFFFFF;border: 1px solid #DCDFE6;box-shadow:none; opacity: 1;border-radius: 4px;"><span style="color:#606062;font-size: 12px;">' + params[0].axisValue + "</span><br />";
|
||||||
|
// params.forEach((item) => {
|
||||||
|
// str +=
|
||||||
|
// '<span style="color:#f00;font-size: 12px;color: #1D1D20;"><span style="float:left; margin-top:8px; margin-right:5px;border-radius:50%;width:6px;height:6px;background-color:'+item.color+'"></span>' + " " + item.data + "</span>";
|
||||||
|
// });
|
||||||
|
// str += '</div>'
|
||||||
|
// return str;
|
||||||
|
|
||||||
|
// // console.log(params[0]);
|
||||||
|
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['KLine', 'MA5', 'BOLL1', 'BOLL2'],
|
||||||
|
},
|
||||||
|
grid: [
|
||||||
|
{
|
||||||
|
left: '3%',
|
||||||
|
right: '3%',
|
||||||
|
bottom: '5%',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
data: xdata.value,
|
||||||
|
gridIndex: 0,
|
||||||
|
scale: true,
|
||||||
|
boundaryGap: false,
|
||||||
|
axisLine: { onZero: false },
|
||||||
|
splitLine: { show: false },
|
||||||
|
splitNumber: 20,
|
||||||
|
min: 'dataMin',
|
||||||
|
max: 'dataMax',
|
||||||
|
sampling: 'lttb', // 最大程度保证采样后线条的趋势,形状和极值。
|
||||||
|
},
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
gridIndex: 0,
|
||||||
|
scale: true,
|
||||||
|
splitArea: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'slider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'KLine',
|
||||||
|
type: 'candlestick',
|
||||||
|
xAxisIndex: 0,
|
||||||
|
yAxisIndex: 0,
|
||||||
|
data: kdata.value,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#ef232a',
|
||||||
|
color0: '#14b143',
|
||||||
|
borderColor: '#ef232a',
|
||||||
|
borderColor0: '#14b143',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// markArea: {
|
||||||
|
// silent: true,
|
||||||
|
// itemStyle: {
|
||||||
|
// normal: {
|
||||||
|
// color: 'Honeydew',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// data: [],
|
||||||
|
// },
|
||||||
|
// markPoint: {
|
||||||
|
// data: [
|
||||||
|
// { type: 'max', name: '最大值' },
|
||||||
|
// { type: 'min', name: '最小值' },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// markLine: {
|
||||||
|
// label: {
|
||||||
|
// normal: {
|
||||||
|
// position: 'middle',
|
||||||
|
// textStyle: { color: 'Blue', fontSize: 12 },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// data: [],
|
||||||
|
// symbol: ['circle', 'none'],
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MA5',
|
||||||
|
type: 'line',
|
||||||
|
xAxisIndex: 0,
|
||||||
|
yAxisIndex: 0,
|
||||||
|
data: ma5.value,
|
||||||
|
symbol: 'none',
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
normal: {
|
||||||
|
opacity: 0.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BOLL1',
|
||||||
|
type: 'line',
|
||||||
|
xAxisIndex: 0,
|
||||||
|
yAxisIndex: 0,
|
||||||
|
data: topStdDev.value,
|
||||||
|
symbol: 'none',
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
normal: {
|
||||||
|
opacity: 0.5,
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// areaStyle: {
|
||||||
|
// // 线条样式
|
||||||
|
// normal: {
|
||||||
|
// // 右,下,左,上
|
||||||
|
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1,
|
||||||
|
// [
|
||||||
|
// // 渐变色
|
||||||
|
// {
|
||||||
|
// offset: 0,
|
||||||
|
// color: 'rgba(255, 46, 106, 0.21)'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// offset: 1,
|
||||||
|
// color: 'rgba(255, 46, 106, 0)'
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
// false
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BOLL2',
|
||||||
|
type: 'line',
|
||||||
|
xAxisIndex: 0,
|
||||||
|
yAxisIndex: 0,
|
||||||
|
data: downStdDev.value,
|
||||||
|
symbol: 'none',
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
normal: {
|
||||||
|
opacity: 0.5,
|
||||||
|
color: 'green',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// areaStyle: {
|
||||||
|
// // 线条样式
|
||||||
|
// normal: {
|
||||||
|
// // 右,下,左,上
|
||||||
|
// color:'white'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// name: '',
|
||||||
|
// type: 'line',
|
||||||
|
// xAxisIndex: 0,
|
||||||
|
// yAxisIndex: 0,
|
||||||
|
// data: ma5.value,
|
||||||
|
// smooth: true,
|
||||||
|
// lineStyle: {
|
||||||
|
// normal: {
|
||||||
|
// opacity: 0.5,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
// 配置图表数据信息
|
||||||
|
chartBox.setOption(option)
|
||||||
|
|
||||||
|
// mergeOptions方法可以合并两个对象,并返回合并后的对象。
|
||||||
|
// 更新信息,可有可无?,调用时配置
|
||||||
|
chartBox.setOption(options)
|
||||||
|
|
||||||
|
// 图表的分组,用于联动
|
||||||
|
chartBox.group = 'group1'
|
||||||
|
// 根据页面大小自动响应图表大小
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
chartBox.resize()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// chart实例化数组
|
||||||
|
let chartBoxList = ref([])
|
||||||
|
//绘制一个点击事件传参的Echarts图表。并返回当前图表的实例化信息,便于使用echarts.connect()进行连接
|
||||||
|
const commonecharts = (containerId, options = {}) => {
|
||||||
|
// 先将容器中的_echarts_instance_清除,不处理会导致高度变化了但图表不刷新
|
||||||
|
document.getElementById(containerId).removeAttribute('_echarts_instance_')
|
||||||
|
// 初始化图表
|
||||||
|
const chart = echarts.init(document.getElementById(containerId))
|
||||||
|
|
||||||
|
chartBoxList.value.push({
|
||||||
|
chart,
|
||||||
|
chartid: chart.id,
|
||||||
|
domId: containerId,
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(chartBoxList.value)
|
||||||
|
|
||||||
|
// 图表的配置信息
|
||||||
|
const option = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: xdata.value,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: '10%',
|
||||||
|
left: '3%',
|
||||||
|
right: '3%',
|
||||||
|
bottom: '20%',
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: [1, 2, 3, 4, 5, 6, 7, 2, 12, 12, 34],
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
// 设置图表实例的配置项
|
||||||
|
chart.setOption(option)
|
||||||
|
// mergeOptions方法可以合并两个对象,并返回合并后的对象。
|
||||||
|
chart.setOption(options)
|
||||||
|
|
||||||
|
// 图表的分组,用于联动
|
||||||
|
chart.group = 'group1'
|
||||||
|
|
||||||
|
// 根据页面大小自动响应图表大小
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
chart.resize()
|
||||||
|
})
|
||||||
|
|
||||||
|
return chart
|
||||||
|
}
|
||||||
|
// 连接所有图表
|
||||||
|
echarts.connect('group1')
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
redata()
|
||||||
|
const chart = echarts.init(charts1.value)
|
||||||
|
console.log(chart)
|
||||||
|
|
||||||
|
charts1.value.style.height = `${200}px`
|
||||||
|
|
||||||
|
// 图表的配置信息
|
||||||
|
const option = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: '10%',
|
||||||
|
left: '3%',
|
||||||
|
right: '3%',
|
||||||
|
bottom: '20%',
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: [1, 2, 3, 4, 5, 6, 7, 2, 12, 12, 34],
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
// 设置图表实例的配置项
|
||||||
|
chart.setOption(option)
|
||||||
|
// mergeOptions方法可以合并两个对象,并返回合并后的对象。
|
||||||
|
// chart.setOption(options)
|
||||||
|
|
||||||
|
// 图表的分组,用于联动
|
||||||
|
chart.group = 'group1'
|
||||||
|
|
||||||
|
// 根据页面大小自动响应图表大小
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
chart.resize()
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
// echarts.disconnect()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="box">
|
||||||
|
<div class="chartsinfo" ref="chartsInfo">
|
||||||
|
<!-- K线图 -->
|
||||||
|
<div
|
||||||
|
id="main"
|
||||||
|
ref="mKline"
|
||||||
|
class="flex-item"
|
||||||
|
:style="{ height: `${heightList[0]}px` }"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="charts1"
|
||||||
|
ref="charts1"
|
||||||
|
:style="{ height: `${heightList[1]}px` }"
|
||||||
|
></div>
|
||||||
|
<!-- <div
|
||||||
|
v-if="chartConfigs[1].status"
|
||||||
|
id="charts2"
|
||||||
|
ref="charts2"
|
||||||
|
:style="{ height: `${heightList[1]}px` }"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-if="chartConfigs[2].status"
|
||||||
|
id="charts3"
|
||||||
|
ref="charts3"
|
||||||
|
:style="{ height: `${heightList[1]}px` }"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-if="chartConfigs[3].status"
|
||||||
|
id="charts4"
|
||||||
|
ref="charts4"
|
||||||
|
:style="{ height: `${heightList[1]}px` }"
|
||||||
|
></div> -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部的各个图表选项信息 -->
|
||||||
|
<ul class="typelist">
|
||||||
|
<li
|
||||||
|
v-for="(item, index) in chartConfigs"
|
||||||
|
:key="index"
|
||||||
|
@click="addChart(item)"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.box {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.chartsinfo {
|
||||||
|
height: calc(100vh - 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.typelist {
|
||||||
|
margin-top: auto;
|
||||||
|
position: relative;
|
||||||
|
z-index: 99999999;
|
||||||
|
width: 100%;
|
||||||
|
color: #191919;
|
||||||
|
font-size: 12px;
|
||||||
|
display: flex;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
.typelist > li {
|
||||||
|
padding: 2px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.typelist > li:hover {
|
||||||
|
color: gold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
color: #2d98b9;
|
||||||
|
}
|
||||||
|
</style>
|
1752
src/end/NewDesign.vue
Normal file
1752
src/end/NewDesign.vue
Normal file
File diff suppressed because it is too large
Load Diff
32
src/end/NodataView.vue
Normal file
32
src/end/NodataView.vue
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<script setup></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="con">
|
||||||
|
<div class="nodata">
|
||||||
|
<div class="text">当前股票没有数据!</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.con {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--my-common-bgc-2);
|
||||||
|
}
|
||||||
|
.nodata {
|
||||||
|
min-height: calc(100vh - 50px);
|
||||||
|
/* height: calc(100vh - 22px); */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.text{
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
/*字体粗细*/
|
||||||
|
-webkit-text-stroke: 1px var(--my-common-fc-1);
|
||||||
|
/*描边*/
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
293
src/end/computedInfo.js
Normal file
293
src/end/computedInfo.js
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
// 计算移动平均线MA的函数,接收收盘价格数组和计算所需的天数作为参数
|
||||||
|
export function calculateMA (closingPrices, dayCount) {
|
||||||
|
// 检查数据点的数量是否足够计算移动平均线
|
||||||
|
if (closingPrices.length < dayCount) {
|
||||||
|
return null // 数据不足,返回null
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = []
|
||||||
|
// 从第dayCount个数据点开始计算(因为前dayCount-1个数据点不足以计算一个完整的移动平均线)
|
||||||
|
for (let i = 0; i <= closingPrices.length - dayCount; i++) {
|
||||||
|
let sum = 0
|
||||||
|
// 计算当前窗口内所有价格的总和
|
||||||
|
for (let j = 0; j < dayCount; j++) {
|
||||||
|
sum += closingPrices[i + j]
|
||||||
|
}
|
||||||
|
// 计算并添加移动平均线值到结果数组
|
||||||
|
result.push(sum / dayCount)
|
||||||
|
}
|
||||||
|
result = result.map(item => parseFloat(item.toFixed(2)))
|
||||||
|
return result // 返回计算出的移动平均线值数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算标准差的函数
|
||||||
|
export function calculateStdDeviation (prices) {
|
||||||
|
if (prices.length === 0) {
|
||||||
|
throw new Error("价格数组不能为空")
|
||||||
|
}
|
||||||
|
// 计算平均值
|
||||||
|
const mean = prices.reduce((sum, price) => sum + price, 0) / prices.length
|
||||||
|
|
||||||
|
// 计算每个价格与平均值的差的平方和
|
||||||
|
const varianceSum = prices.reduce((sum, price) => sum + Math.pow(price - mean, 2), 0)
|
||||||
|
|
||||||
|
// 计算方差(样本方差使用N-1作为分母)
|
||||||
|
const variance = varianceSum / (prices.length - 1)
|
||||||
|
|
||||||
|
// 计算标准差(方差的平方根),取两位小数
|
||||||
|
const stDeviation = parseFloat(Math.sqrt(variance).toFixed(2))
|
||||||
|
|
||||||
|
return stDeviation
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function calculateEMA (prices, period) {
|
||||||
|
let ema = []
|
||||||
|
let multiplier = 2 / (period + 1)
|
||||||
|
let previousEMA = prices.reduce((sum, price) => sum + price, 0) / period // 初始EMA为前period天的收盘价平均值
|
||||||
|
|
||||||
|
ema.push(previousEMA) // 添加第一个EMA值
|
||||||
|
|
||||||
|
for (let i = period; i < prices.length; i++) {
|
||||||
|
let currentEMA = (prices[i] - previousEMA) * multiplier + previousEMA
|
||||||
|
ema.push(currentEMA)
|
||||||
|
previousEMA = currentEMA
|
||||||
|
}
|
||||||
|
|
||||||
|
// 我们只关心最后一个EMA值(即最新周期的EMA),但这里返回整个EMA数组以便调试
|
||||||
|
return ema
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 计算DIF的函数,但dif数组长度将受限于价格数组和EMA计算所需的最小长度
|
||||||
|
export function calculateDIF (prices, shortPeriod, longPeriod) {
|
||||||
|
// 确保价格数组足够长以计算所需的EMA
|
||||||
|
if (prices.length < longPeriod + 1) {
|
||||||
|
return [] // 或抛出错误
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算整个价格数组的EMA,但通常只关注最近的EMA值
|
||||||
|
let shortEMA = calculateEMA(prices, shortPeriod)
|
||||||
|
let longEMA = calculateEMA(prices, longPeriod)
|
||||||
|
|
||||||
|
// DIF数组,从两个EMA数组都有值的起始位置开始计算
|
||||||
|
let dif = []
|
||||||
|
let startIndex = Math.max(shortEMA.length - 1, longEMA.length - 1) - (longPeriod - 1) // 找到两个EMA都能计算的起始索引(可能需要根据实际情况调整)
|
||||||
|
// 注意:这里的startIndex计算可能需要根据实际需求进行调整,因为它依赖于EMA数组的实际可用长度和所需的计算周期。
|
||||||
|
// 下面的循环确保了只有当两个EMA数组在相同索引处都有值时,才计算DIF。
|
||||||
|
for (let i = startIndex; i < Math.min(shortEMA.length, longEMA.length); i++) {
|
||||||
|
dif.push(shortEMA[i] - longEMA[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果需要,可以在dif数组前面填充null或0,以使其长度与prices相同(但这在金融分析上可能没有意义)
|
||||||
|
// while (dif.length < prices.length) {
|
||||||
|
// dif.unshift(null); // 或0,或其他适当的填充值
|
||||||
|
// }
|
||||||
|
|
||||||
|
return dif // 返回DIF数组,其长度可能小于prices数组
|
||||||
|
}
|
||||||
|
// 计算DEA的函数
|
||||||
|
export function calculateDEA (dif, deaPeriod) {
|
||||||
|
// DEA就是DIF的EMA,所以直接使用calculateEMA函数计算DIF数组的EMA
|
||||||
|
return calculateEMA(dif, deaPeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MACD指标的计算函数
|
||||||
|
export function calculateMACD (
|
||||||
|
prices,
|
||||||
|
shortPeriod = 12,
|
||||||
|
longPeriod = 26,
|
||||||
|
deaPeriod = 9
|
||||||
|
) {
|
||||||
|
function calculateEMA (prices, period) {
|
||||||
|
let ema = []
|
||||||
|
let multiplier = 2 / (period + 1)
|
||||||
|
let sum = 0
|
||||||
|
for (let i = 0; i < period; i++) {
|
||||||
|
sum += prices[i]
|
||||||
|
}
|
||||||
|
let previousEMA = sum / period
|
||||||
|
ema.push(previousEMA)
|
||||||
|
for (let i = period; i < prices.length; i++) {
|
||||||
|
let currentEMA = (prices[i] - previousEMA) * multiplier + previousEMA
|
||||||
|
ema.push(currentEMA)
|
||||||
|
previousEMA = currentEMA
|
||||||
|
}
|
||||||
|
return ema
|
||||||
|
}
|
||||||
|
|
||||||
|
let shortEMA = calculateEMA(prices, shortPeriod)
|
||||||
|
let longEMA = calculateEMA(prices, longPeriod)
|
||||||
|
let dif = []
|
||||||
|
for (let i = 0; i < Math.min(shortEMA.length, longEMA.length); i++) {
|
||||||
|
dif.push(shortEMA[i] - longEMA[i])
|
||||||
|
}
|
||||||
|
let dea = calculateEMA(dif, deaPeriod)
|
||||||
|
let macd = []
|
||||||
|
for (let i = 0; i < Math.min(dif.length, dea.length); i++) {
|
||||||
|
macd.push((dif[i] - dea[i]) * 2)
|
||||||
|
}
|
||||||
|
// 将dif, dea, macd中的值保留两位小数
|
||||||
|
dif = dif.map((item) => parseFloat(item.toFixed(2)))
|
||||||
|
dea = dea.map((item) => parseFloat(item.toFixed(2)))
|
||||||
|
macd = macd.map((item) => parseFloat(item.toFixed(2)))
|
||||||
|
return {
|
||||||
|
dif,
|
||||||
|
dea,
|
||||||
|
macd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------计算KDJ指标的函数----------------------------
|
||||||
|
// 数据整理
|
||||||
|
export function changeShape (res) {
|
||||||
|
// 获取对象的键(这里我们知道键是 'date', 'low', 'high', 'close',但为了通用性,我们还是获取所有键)
|
||||||
|
const keys = Object.keys(res)
|
||||||
|
|
||||||
|
// 初始化一个空数组来存储结果
|
||||||
|
let result = []
|
||||||
|
|
||||||
|
// 假设所有对象的属性(date, low, high, close)都有相同的索引范围
|
||||||
|
// 这里我们取第一个对象的键集合来确定索引范围(假设它们都有0和1这两个键)
|
||||||
|
const indexRange = Object.keys(res.date) // 或者使用任何其他对象的键集合,因为它们应该有相同的索引
|
||||||
|
|
||||||
|
// 遍历索引范围
|
||||||
|
for (let index of indexRange) {
|
||||||
|
// 将索引转换为数字类型,以便进行数学运算(虽然在这个例子中它们已经是数字字符串)
|
||||||
|
const numIndex = parseInt(index, 10)
|
||||||
|
|
||||||
|
// 收集对应索引的值到一个子数组中
|
||||||
|
const row = [
|
||||||
|
res.date[numIndex],
|
||||||
|
res.low[numIndex],
|
||||||
|
res.high[numIndex],
|
||||||
|
res.close[numIndex]
|
||||||
|
]
|
||||||
|
|
||||||
|
// 将子数组添加到结果数组中
|
||||||
|
result.push(row)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
// 计算函数
|
||||||
|
export function calculateKDJ (prices) {
|
||||||
|
|
||||||
|
prices = changeShape(prices)
|
||||||
|
let lows = []
|
||||||
|
let highs = []
|
||||||
|
let closes = []
|
||||||
|
// 提取最低价、最高价、收盘价数组
|
||||||
|
prices.forEach((price) => {
|
||||||
|
lows.push(price[1])
|
||||||
|
highs.push(price[2])
|
||||||
|
closes.push(price[3])
|
||||||
|
})
|
||||||
|
let kValues = []
|
||||||
|
let dValues = []
|
||||||
|
let jValues = []
|
||||||
|
let period = 9 // 默认的计算周期,可以根据实际情况调整
|
||||||
|
for (let i = 0; i < closes.length; i++) {
|
||||||
|
if (i < period - 1) {
|
||||||
|
kValues.push(10)
|
||||||
|
dValues.push(10)
|
||||||
|
jValues.push(10)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 计算RSV值(未成熟随机值)
|
||||||
|
let lowPeriod = Math.min(...lows.slice(i - period + 1, i + 1))
|
||||||
|
let highPeriod = Math.max(...highs.slice(i - period + 1, i + 1))
|
||||||
|
let rsv = ((closes[i] - lowPeriod) / (highPeriod - lowPeriod)) * 100
|
||||||
|
if (i === period - 1) {
|
||||||
|
kValues.push(rsv)
|
||||||
|
dValues.push(rsv)
|
||||||
|
} else {
|
||||||
|
// 计算K值,一般采用加权移动平均的方式,这里简化为前一日K值的2/3加上今日RSV的1/3
|
||||||
|
let k = (2 / 3) * kValues[i - 1] + (1 / 3) * rsv
|
||||||
|
kValues.push(k)
|
||||||
|
// 计算D值,一般是前一日D值的2/3加上今日K值的1/3
|
||||||
|
let d = (2 / 3) * dValues[i - 1] + (1 / 3) * k
|
||||||
|
dValues.push(d)
|
||||||
|
}
|
||||||
|
// 计算J值,一般公式为3*K - 2*D
|
||||||
|
let j = 3 * kValues[i] - 2 * dValues[i]
|
||||||
|
jValues.push(j)
|
||||||
|
}
|
||||||
|
kValues = kValues.map(item => parseFloat(item.toFixed(2)))
|
||||||
|
dValues = dValues.map(item => parseFloat(item.toFixed(2)))
|
||||||
|
jValues = jValues.map(item => parseFloat(item.toFixed(2)))
|
||||||
|
return {
|
||||||
|
kValues,
|
||||||
|
dValues,
|
||||||
|
jValues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ----------------------------计算RSI指标的函数----------------------------
|
||||||
|
// RSI 的时间跨度推荐 6、12、24 三个值,6 日近似一周的时间周期,12 日可以看作半个月的时间跨度,而 24 日约为一个月的时间跨度。
|
||||||
|
// 短期 RSI 对于价格的变化情况反应比较灵敏,RSI 的值波动较大;长期 RSI 值反应相对迟钝,其波动相对较小。
|
||||||
|
export function calculateRSI (closePrices, period = 6) {
|
||||||
|
let gains = []
|
||||||
|
let losses = []
|
||||||
|
let rsi = []
|
||||||
|
|
||||||
|
for (let i = 0; i < closePrices.length; i++) {
|
||||||
|
if (i === 0) {
|
||||||
|
gains.push(0)
|
||||||
|
losses.push(0)
|
||||||
|
} else {
|
||||||
|
let diff = closePrices[i] - closePrices[i - 1]
|
||||||
|
if (diff > 0) {
|
||||||
|
gains.push(diff)
|
||||||
|
losses.push(0)
|
||||||
|
} else {
|
||||||
|
gains.push(0)
|
||||||
|
losses.push(Math.abs(diff))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= period - 1) {
|
||||||
|
let sumGain = 0
|
||||||
|
let sumLoss = 0
|
||||||
|
for (let j = i - (period - 1); j <= i; j++) {
|
||||||
|
sumGain += gains[j]
|
||||||
|
sumLoss += losses[j]
|
||||||
|
}
|
||||||
|
|
||||||
|
let avgGain = sumGain / period
|
||||||
|
let avgLoss = sumLoss / period
|
||||||
|
let rs = avgGain / avgLoss
|
||||||
|
rsi.push(100 - (100 / (1 + rs)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rsi = rsi.map(item => parseFloat(item.toFixed(2)))
|
||||||
|
return rsi
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------计算VOL指标的函数----------------------------
|
||||||
|
|
||||||
|
export function calculateVOL (close, high, low) {
|
||||||
|
let arr = []
|
||||||
|
let volvalues = Object.values(close)
|
||||||
|
let highvalues = Object.values(high)
|
||||||
|
let lowvalues = Object.values(low)
|
||||||
|
|
||||||
|
for (let i = 0; i < volvalues.length; i++) {
|
||||||
|
let change = (highvalues[i] + lowvalues[i]) / 2
|
||||||
|
arr.push({
|
||||||
|
value: volvalues[i],
|
||||||
|
itemStyle: {
|
||||||
|
color: volvalues[i] >= change ? '#FF0000' : '#00FF00' // 根据值的正负设置颜色
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
1767
src/endnew/NewStyleDesign.vue
Normal file
1767
src/endnew/NewStyleDesign.vue
Normal file
File diff suppressed because it is too large
Load Diff
32
src/endtype/HourLineWrapper.vue
Normal file
32
src/endtype/HourLineWrapper.vue
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<component :is="currentComponent" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent, computed } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import NewDesign from '@/end/NewDesign.vue';
|
||||||
|
import NewStyleDesign from '@/endnew/NewStyleDesign.vue';
|
||||||
|
import NodataView from '@/end/NodataView.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const currentComponent = computed(() => {
|
||||||
|
// 根据路由参数动态加载组件
|
||||||
|
if (route.params.type === 'vertical') {
|
||||||
|
return NewDesign;
|
||||||
|
} else if (route.params.type === 'grid') {
|
||||||
|
return NewStyleDesign;
|
||||||
|
}
|
||||||
|
// 默认加载的组件
|
||||||
|
return NewDesign;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentComponent
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
24
src/main.ts
Normal file
24
src/main.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import './assets/base.css'
|
||||||
|
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
|
||||||
|
// 使用 element-plus
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
|
|
||||||
|
import { createPinia } from "pinia";
|
||||||
|
|
||||||
|
// 引入echarts
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(ElementPlus)
|
||||||
|
app.config.globalProperties.$echarts = echarts
|
||||||
|
|
||||||
|
app.use(router)
|
||||||
|
app.use(createPinia())
|
||||||
|
|
||||||
|
app.mount('#app')
|
63
src/router/index.ts
Normal file
63
src/router/index.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||||
|
|
||||||
|
// const route = useRoute();
|
||||||
|
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'root',
|
||||||
|
redirect: '/thenew', // 默认重定向到 /thenew
|
||||||
|
component: () => import('@/views/TheNewView.vue'), // 顶级路由组件
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'thenew',
|
||||||
|
name: 'thenew',
|
||||||
|
// component: () => import('@/views/HomeView.vue'), // HomeView 是 TheNewView 的子组件
|
||||||
|
redirect: '/thenew/nodata', // 默认重定向到 /thenew/hour-line
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'hour-line',
|
||||||
|
name: 'hour-line',
|
||||||
|
component: () => import('@/end/NewDesign.vue') // 嵌套子路由
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'hour-line2',
|
||||||
|
name: 'hour-line2',
|
||||||
|
component: () => import('@/endnew/NewStyleDesign.vue') // 嵌套子路由
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'day-line',
|
||||||
|
name: 'day-line',
|
||||||
|
component: () => import('@/end/NodataView.vue') // 嵌套子路由
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'week-line',
|
||||||
|
name: 'week-line',
|
||||||
|
component: () => import('@/end/NodataView.vue') // 嵌套子路由
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'month-line',
|
||||||
|
name: 'month-line',
|
||||||
|
component: () => import('@/end/NodataView.vue') // 嵌套子路由
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'year-line',
|
||||||
|
name: 'year-line',
|
||||||
|
component: () => import('@/end/NodataView.vue') // 嵌套子路由
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'nodata',
|
||||||
|
name: 'nodata',
|
||||||
|
component: () => import('@/end/NodataView.vue') // 嵌套子路由
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
5
src/shims-vue.d.ts
vendored
Normal file
5
src/shims-vue.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
declare module '*.vue' {
|
||||||
|
import { DefineComponent } from 'vue';
|
||||||
|
const component: DefineComponent<{}, {}, any>;
|
||||||
|
export default component;
|
||||||
|
}
|
5
src/store/index.ts
Normal file
5
src/store/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { createPinia } from "pinia";
|
||||||
|
|
||||||
|
const pinia = createPinia()
|
||||||
|
|
||||||
|
export default pinia
|
0
src/store/usestylestore.ts
Normal file
0
src/store/usestylestore.ts
Normal file
30
src/store/usethemestore.ts
Normal file
30
src/store/usethemestore.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
//数组处理
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const useTheme = defineStore("Theme", {
|
||||||
|
state: () => ({
|
||||||
|
theme: false,
|
||||||
|
fontcolor:'#fff',
|
||||||
|
style:'vertical'
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
toggleFontcolor(){
|
||||||
|
if(this.theme){
|
||||||
|
this.fontcolor = '#000'
|
||||||
|
}else{
|
||||||
|
this.fontcolor = '#fff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleDarkTheme(){
|
||||||
|
this.theme = false
|
||||||
|
},
|
||||||
|
optionConfigData () {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default useTheme
|
2
src/uilts/vscode.ts
Normal file
2
src/uilts/vscode.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
// export const vscode= window.acquireVsCodeApi();
|
37
src/utils/echarts.js
Normal file
37
src/utils/echarts.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//这个必须要引入的
|
||||||
|
import * as echarts from "echarts/core";
|
||||||
|
|
||||||
|
/** 引入柱状图and折线图图表,图表后缀都为 Chart*/
|
||||||
|
import { BarChart, LineChart } from "echarts/charts";
|
||||||
|
|
||||||
|
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
|
||||||
|
import {
|
||||||
|
TitleComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
DatasetComponent,
|
||||||
|
TransformComponent,
|
||||||
|
} from "echarts/components";
|
||||||
|
|
||||||
|
// 标签自动布局,全局过渡动画等特性
|
||||||
|
import { LabelLayout, UniversalTransition } from "echarts/features";
|
||||||
|
|
||||||
|
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
|
||||||
|
import { CanvasRenderer } from "echarts/renderers";
|
||||||
|
|
||||||
|
// 注册必须的组件
|
||||||
|
echarts.use([
|
||||||
|
TitleComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
DatasetComponent,
|
||||||
|
TransformComponent,
|
||||||
|
BarChart,
|
||||||
|
LabelLayout,
|
||||||
|
UniversalTransition,
|
||||||
|
CanvasRenderer,
|
||||||
|
LineChart,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
export default echarts;
|
45
src/utils/http.js
Normal file
45
src/utils/http.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
const request = axios.create({
|
||||||
|
method :"GET",
|
||||||
|
timeout:2000,
|
||||||
|
})
|
||||||
|
// 请求拦截器
|
||||||
|
request.interceptors.request.use(config => {
|
||||||
|
console.log("请求拦截成功")
|
||||||
|
// 例如给后端传递token,config是配置参数
|
||||||
|
//如有需要:注意使用token的时候需要引入cookie方法或者用本地localStorage等方法,推荐js-cookie
|
||||||
|
//const token = getCookie('名称');//这里取token之前,你肯定需要先拿到token,存一下
|
||||||
|
//if(token){
|
||||||
|
//config.params = {'token':token} //如果要求携带在参数中
|
||||||
|
//config.headers.token= token; //如果要求携带在请求头中
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
console.log('请求拦截失败')
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// 响应拦截器
|
||||||
|
request.interceptors.response.use(res => {
|
||||||
|
console.log("响应拦截成功")
|
||||||
|
if (res.status != 200) {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
console.log("响应拦截失败")
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
export default (url, method = "GET", params = {}) => {
|
||||||
|
return request({
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// export default request
|
||||||
|
|
||||||
|
|
57
src/utils/request.js
Normal file
57
src/utils/request.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// 进行axios的二次封装
|
||||||
|
import axios from "axios";
|
||||||
|
// 第一步:利用axios对象的create方法,去创建axios实例(其他的配置:基础路径,超时的时间)
|
||||||
|
let request=axios.create({
|
||||||
|
// 基础路径
|
||||||
|
baseURL:import.meta.env.VITE_APP_BASE_API,//基础路径上会携带/api
|
||||||
|
timeout:5000//超时的时间的设置
|
||||||
|
});
|
||||||
|
// 第二步:request实例添加请求与响应拦截器
|
||||||
|
request.interceptors.request.use((config)=>{
|
||||||
|
// config配置对象,headers属性请求头,经常给服务器端携带公共参数
|
||||||
|
// 返回配置对象
|
||||||
|
return config
|
||||||
|
})
|
||||||
|
// 第三步:响应拦截器
|
||||||
|
request.interceptors.response.use((response)=>{
|
||||||
|
// 成功回调
|
||||||
|
// 简化数据
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
if(response.status==200){
|
||||||
|
return response.data.message
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
},(error)=>{
|
||||||
|
|
||||||
|
// 失败回调:处理http网络问题
|
||||||
|
// 定义一个变量:存储网络错误信息
|
||||||
|
console.log(error);
|
||||||
|
|
||||||
|
let message=''
|
||||||
|
// http状态码
|
||||||
|
let status=error.response.status
|
||||||
|
switch(status){
|
||||||
|
case 401:
|
||||||
|
message='TOKEN过期'
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
message ='无权访问'
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
message ='请求地址错误'
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
|
message ='服务器出现问题'
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message='网络出现问题'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 对外暴露
|
||||||
|
export default request
|
2
src/utils/vscode.ts
Normal file
2
src/utils/vscode.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
// export const vscode= window.acquireVsCodeApi();
|
240
src/views/HomeView.vue
Normal file
240
src/views/HomeView.vue
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onMounted, ref, watch} from 'vue' // 主要
|
||||||
|
import { RouterView, useRouter } from 'vue-router'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
|
import usethemestore from '../store/usethemestore'
|
||||||
|
const themeStore = usethemestore()
|
||||||
|
const { theme, style } = storeToRefs(themeStore)
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
let routerViewKey = ref(0) // 用于强制重新渲染 RouterView 的 key
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
message: {
|
||||||
|
type: String,
|
||||||
|
default: '', // 默认值为空
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const lightFunc = () => {
|
||||||
|
theme.value = true
|
||||||
|
document.documentElement.style.setProperty('--my-common-bgc-2', '#fff');
|
||||||
|
document.documentElement.style.setProperty('--my-light-br-1', '#e7ebf3');
|
||||||
|
document.documentElement.style.setProperty('--my-common-bgc-1', '#1F1F1F');
|
||||||
|
document.documentElement.style.setProperty('--my-common-fc-1', '#1F1F1F');
|
||||||
|
|
||||||
|
}
|
||||||
|
const darkFunc = () => {
|
||||||
|
theme.value = false
|
||||||
|
document.documentElement.style.setProperty('--my-common-bgc-2', '#1F1F1F');
|
||||||
|
document.documentElement.style.setProperty('--my-light-br-1', '#ffffff');
|
||||||
|
document.documentElement.style.setProperty('--my-common-bgc-1', '#fff');
|
||||||
|
document.documentElement.style.setProperty('--my-common-fc-1', '#fff');
|
||||||
|
}
|
||||||
|
|
||||||
|
const verticalFunc = () => {
|
||||||
|
style.value = 'vertical'
|
||||||
|
router.push({
|
||||||
|
path: `/thenew/hour-line`,
|
||||||
|
params: { type: 'vertical' },
|
||||||
|
})
|
||||||
|
// routerViewKey.value = routerViewKey.value + 1
|
||||||
|
|
||||||
|
}
|
||||||
|
const gridFunc = () => {
|
||||||
|
style.value = 'grid'
|
||||||
|
router.push({
|
||||||
|
path: `/thenew/hour-line2`,
|
||||||
|
params: { type: 'grid' },
|
||||||
|
})
|
||||||
|
// routerViewKey.value = routerViewKey.value + 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watch(() => props.message, (news,old) => {
|
||||||
|
routerViewKey.value = routerViewKey.value + 1
|
||||||
|
router.push(`/thenew/hour-line`)
|
||||||
|
activeIndex.value = 0
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const headerData = ref([
|
||||||
|
'日K',
|
||||||
|
'周K',
|
||||||
|
'月K',
|
||||||
|
'季K',
|
||||||
|
'年K',
|
||||||
|
// '1分',
|
||||||
|
// '3分',
|
||||||
|
// '5分',
|
||||||
|
// '15分',
|
||||||
|
// '30分',
|
||||||
|
// '1小时',
|
||||||
|
// '2小时',
|
||||||
|
// '3小时',
|
||||||
|
// '4小时',
|
||||||
|
])
|
||||||
|
// 模拟路由数据
|
||||||
|
let routerList = [
|
||||||
|
'hour-line',
|
||||||
|
'day-line',
|
||||||
|
'week-line',
|
||||||
|
'month-line',
|
||||||
|
'year-line',
|
||||||
|
]
|
||||||
|
// 定义一个响应式变量来存储当前激活的项的索引
|
||||||
|
const activeIndex = ref(0)
|
||||||
|
// 定义一个处理点击事件的函数
|
||||||
|
function handleClick(index) {
|
||||||
|
// 更新当前激活的项的索引
|
||||||
|
activeIndex.value = index
|
||||||
|
// 跳转路由
|
||||||
|
router.push(`/thenew/${routerList[index]}`)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="content">
|
||||||
|
<ul class="timelist">
|
||||||
|
<li
|
||||||
|
class="item"
|
||||||
|
v-for="(item, index) in headerData"
|
||||||
|
:key="item"
|
||||||
|
:class="{ active: activeIndex === index }"
|
||||||
|
@click="handleClick(index)"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</li>
|
||||||
|
<li class="item">
|
||||||
|
<el-dropdown :hide-on-click="false">
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
<div v-if="theme">
|
||||||
|
<svg t="1739345471358" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7366" width="16" height="16"><path d="M85.333333 128h853.333334v768H85.333333V128z m85.333334 85.333333v597.333334h682.666666V384h-341.333333V213.333333H170.666667z" p-id="7367" fill="#2c2c2c"></path></svg>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<svg t="1739345471358" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7366" width="16" height="16"><path d="M85.333333 128h853.333334v768H85.333333V128z m85.333334 85.333333v597.333334h682.666666V384h-341.333333V213.333333H170.666667z" p-id="7367" fill="#ffffff"></path></svg>
|
||||||
|
</div>
|
||||||
|
<!-- <svg t="1737685184492" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1032" width="16" height="16"><path d="M831.085714 896h-612.571428V437.028571l-118.857143-118.857142 201.142857-201.142858 10.057143 10.057143c113.371429 113.371429 298.057143 113.371429 412.342857 0l10.057143-10.057143L950.857143 334.628571l-118.857143 118.857143V896z m-585.142857-27.428571h557.714286V441.6l106.971428-106.971429L732.342857 155.428571C610.742857 266.971429 423.314286 266.971429 301.714286 155.428571L138.057143 318.171429l106.971428 106.971428V868.571429z" fill="#2c2c2c" p-id="1033"></path></svg> -->
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<div @click="verticalFunc">
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="light"
|
||||||
|
content="垂直"
|
||||||
|
placement="left"
|
||||||
|
>
|
||||||
|
<svg t="1739337693471" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6034" width="16" height="16"><path d="M85.333333 213.333333h853.333334v597.333334H85.333333V213.333333z m85.333334 85.333334v85.333333h682.666666V298.666667H170.666667z m682.666666 170.666666H170.666667v85.333334h682.666666v-85.333334z m0 170.666667H170.666667v85.333333h682.666666v-85.333333z" p-id="6035" fill="#2c2c2c"></path></svg>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<div @click="gridFunc">
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="light"
|
||||||
|
content="网格"
|
||||||
|
placement="left"
|
||||||
|
>
|
||||||
|
<svg t="1739339260343" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7012" width="14" height="14"><path d="M85.333333 128h853.333334v768H85.333333V128z m85.333334 170.666667v213.333333h298.666666V298.666667H170.666667z m384 0v213.333333h298.666666V298.666667h-298.666666z m298.666666 298.666666h-298.666666v213.333334h298.666666v-213.333334z m-384 213.333334v-213.333334H170.666667v213.333334h298.666666z" p-id="7013" fill="#2c2c2c"></path></svg>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</li>
|
||||||
|
<li class="item">
|
||||||
|
<el-dropdown :hide-on-click="false">
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
<div v-if="theme">
|
||||||
|
<svg t="1737685184492" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1032" width="16" height="16"><path d="M831.085714 896h-612.571428V437.028571l-118.857143-118.857142 201.142857-201.142858 10.057143 10.057143c113.371429 113.371429 298.057143 113.371429 412.342857 0l10.057143-10.057143L950.857143 334.628571l-118.857143 118.857143V896z m-585.142857-27.428571h557.714286V441.6l106.971428-106.971429L732.342857 155.428571C610.742857 266.971429 423.314286 266.971429 301.714286 155.428571L138.057143 318.171429l106.971428 106.971428V868.571429z" fill="#2c2c2c" p-id="1033"></path></svg>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<svg t="1737685184492" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1032" width="16" height="16"><path d="M831.085714 896h-612.571428V437.028571l-118.857143-118.857142 201.142857-201.142858 10.057143 10.057143c113.371429 113.371429 298.057143 113.371429 412.342857 0l10.057143-10.057143L950.857143 334.628571l-118.857143 118.857143V896z m-585.142857-27.428571h557.714286V441.6l106.971428-106.971429L732.342857 155.428571C610.742857 266.971429 423.314286 266.971429 301.714286 155.428571L138.057143 318.171429l106.971428 106.971428V868.571429z" fill="#ffffff" p-id="1033"></path></svg>
|
||||||
|
</div>
|
||||||
|
<!-- <svg t="1737685184492" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1032" width="16" height="16"><path d="M831.085714 896h-612.571428V437.028571l-118.857143-118.857142 201.142857-201.142858 10.057143 10.057143c113.371429 113.371429 298.057143 113.371429 412.342857 0l10.057143-10.057143L950.857143 334.628571l-118.857143 118.857143V896z m-585.142857-27.428571h557.714286V441.6l106.971428-106.971429L732.342857 155.428571C610.742857 266.971429 423.314286 266.971429 301.714286 155.428571L138.057143 318.171429l106.971428 106.971428V868.571429z" fill="#2c2c2c" p-id="1033"></path></svg> -->
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<div @click="lightFunc">
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="light"
|
||||||
|
content="Light"
|
||||||
|
placement="left"
|
||||||
|
>
|
||||||
|
<svg t="1737685432082" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3218" width="16" height="16"><path d="M504.832 144h24.08c6.608 0 11.952 4.48 11.952 9.984v80.208c0 5.504-5.344 9.984-11.952 9.984h-24.08c-6.608 0-11.968-4.48-11.968-9.984V153.984c0-5.52 5.36-9.984 11.968-9.984z m10.304 541.92c96.432 0 174.608-78.176 174.608-174.608 0-96.448-78.176-174.624-174.608-174.624S340.528 414.88 340.528 511.312c0 96.432 78.176 174.608 174.608 174.608z m0 48c-122.944 0-222.608-99.68-222.608-222.608 0-122.944 99.664-222.624 222.608-222.624s222.608 99.68 222.608 222.624c0 122.928-99.68 222.608-222.608 222.608z m-10.24-355.792a9.52 9.52 0 0 1 10.24 9.488v247.2a9.696 9.696 0 0 1-10.448 9.648 133.584 133.584 0 0 1 0.208-266.336z m263.184-135.056l17.024 17.024c4.672 4.672 5.28 11.632 1.376 15.536L729.6 332.512c-3.904 3.904-10.864 3.296-15.52-1.376l-17.04-17.024c-4.672-4.672-5.28-11.632-1.376-15.552l56.88-56.864c3.904-3.92 10.864-3.296 15.52 1.376z m110.528 261.76v24.08c0 6.608-4.48 11.952-9.984 11.952h-80.208c-5.504 0-9.984-5.344-9.984-11.952v-24.08c0-6.608 4.48-11.968 9.984-11.968h80.208c5.52 0 9.984 5.36 9.984 11.968z m-93.504 257.68l-17.024 17.024c-4.672 4.672-11.632 5.28-15.536 1.376l-56.88-56.864c-3.904-3.92-3.296-10.88 1.376-15.536l17.024-17.04c4.672-4.672 11.632-5.28 15.536-1.376l56.88 56.88c3.92 3.904 3.296 10.88-1.376 15.536z m-256.192 116.096h-24.08c-6.608 0-11.968-4.48-11.968-9.984v-80.208c0-5.504 5.36-9.984 11.968-9.984h24.08c6.608 0 11.952 4.48 11.952 9.984v80.208c0 5.52-5.344 9.984-11.952 9.984z m-268.528-100.448l-14.272-14.272a10.016 10.016 0 0 1 0-14.16l56.88-56.88a10.016 10.016 0 0 1 14.16 0l14.272 14.272a10.016 10.016 0 0 1 0 14.176l-56.864 56.864a10.016 10.016 0 0 1-14.176 0zM144 528.912v-24.08c0-6.608 4.48-11.968 9.984-11.968h80.208c5.504 0 9.984 5.36 9.984 11.968v24.08c0 6.608-4.48 11.952-9.984 11.952H153.984c-5.52 0-9.984-5.344-9.984-11.952z m104.64-268.8l17.024-17.04c4.672-4.672 11.632-5.28 15.536-1.376l56.88 56.864c3.904 3.92 3.296 10.88-1.376 15.552l-17.04 17.024c-4.672 4.672-11.616 5.28-15.52 1.376l-56.88-56.88c-3.92-3.904-3.296-10.88 1.36-15.52z" fill="#2c2c2c" p-id="3219"></path></svg>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<div @click="darkFunc">
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="light"
|
||||||
|
content="Dark"
|
||||||
|
placement="left"
|
||||||
|
>
|
||||||
|
<svg t="1737685471806" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3524" width="16" height="16"><path d="M504.832 144h24.08c6.608 0 11.952 4.48 11.952 9.984v80.208c0 5.504-5.344 9.984-11.952 9.984h-24.08c-6.608 0-11.968-4.48-11.968-9.984V153.984c0-5.52 5.36-9.984 11.968-9.984z m263.248 99.072l17.024 17.024c4.672 4.672 5.28 11.632 1.376 15.536L729.6 332.512c-3.904 3.904-10.864 3.296-15.52-1.376l-17.04-17.024c-4.672-4.672-5.28-11.632-1.376-15.552l56.88-56.864c3.904-3.92 10.864-3.296 15.52 1.376z m110.528 261.76v24.08c0 6.608-4.48 11.952-9.984 11.952h-80.208c-5.504 0-9.984-5.344-9.984-11.952v-24.08c0-6.608 4.48-11.968 9.984-11.968h80.208c5.52 0 9.984 5.36 9.984 11.968z m-93.504 257.68l-17.024 17.024c-4.672 4.672-11.632 5.28-15.536 1.376l-56.88-56.864c-3.904-3.92-3.296-10.88 1.376-15.536l17.024-17.04c4.672-4.672 11.632-5.28 15.536-1.376l56.88 56.88c3.92 3.904 3.296 10.88-1.376 15.536z m-256.192 116.096h-24.08c-6.608 0-11.968-4.48-11.968-9.984v-80.208c0-5.504 5.36-9.984 11.968-9.984h24.08c6.608 0 11.952 4.48 11.952 9.984v80.208c0 5.52-5.344 9.984-11.952 9.984z m-268.528-100.448l-14.272-14.272a10.016 10.016 0 0 1 0-14.16l56.88-56.88a10.016 10.016 0 0 1 14.16 0l14.272 14.272a10.016 10.016 0 0 1 0 14.176l-56.864 56.864a10.016 10.016 0 0 1-14.176 0zM144 528.912v-24.08c0-6.608 4.48-11.968 9.984-11.968h80.208c5.504 0 9.984 5.36 9.984 11.968v24.08c0 6.608-4.48 11.952-9.984 11.952H153.984c-5.52 0-9.984-5.344-9.984-11.952z m104.64-268.8l17.024-17.04c4.672-4.672 11.632-5.28 15.536-1.376l56.88 56.864c3.904 3.92 3.296 10.88-1.376 15.552l-17.04 17.024c-4.672 4.672-11.616 5.28-15.52 1.376l-56.88-56.88c-3.92-3.904-3.296-10.88 1.36-15.52zM515.12 685.92c96.432 0 174.608-78.176 174.608-174.608 0-96.448-78.176-174.624-174.608-174.624S340.528 414.88 340.528 511.312c0 96.432 78.176 174.608 174.608 174.608z m0 48c-122.944 0-222.608-99.68-222.608-222.608 0-122.944 99.664-222.624 222.608-222.624s222.608 99.68 222.608 222.624c0 122.928-99.68 222.608-222.608 222.608zM478.288 427.52a82.8 82.8 0 0 0-1.68 16.64c0 43.936 34.32 79.552 76.64 79.552a74.624 74.624 0 0 0 48.544-17.984c4.544-3.872 14.72-2.4 15.392 3.184 0.592 4.848 0.896 9.792 0.896 14.8 0 64.224-50.144 116.272-112 116.272s-112-52.048-112-116.272c0-48.064 28.096-89.312 68.176-107.024 6.976-3.088 17.312 4.576 16.032 10.832z" fill="#2c2c2c" p-id="3525"></path></svg>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="view">
|
||||||
|
<RouterView :key="routerViewKey"></RouterView>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.content {
|
||||||
|
/* width: 100vw; */
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.timelist {
|
||||||
|
/* background-color: white; */
|
||||||
|
background-color: var(--my-common-bgc-2) !important;
|
||||||
|
/* color: #414040; */
|
||||||
|
color: var(--my-common-fc-1);
|
||||||
|
font-size: 13px;
|
||||||
|
display: flex;
|
||||||
|
list-style: none;
|
||||||
|
border-bottom: 1px solid rgb(175, 173, 173);
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timelist > li {
|
||||||
|
padding: 0 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.timelist > li:nth-child(6){
|
||||||
|
margin-left: auto;
|
||||||
|
/* margin-right: 10px; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
color: #2d98b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view {
|
||||||
|
height: calc(100vh - 27px);
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
</style>
|
1237
src/views/TheNewView.vue
Normal file
1237
src/views/TheNewView.vue
Normal file
File diff suppressed because it is too large
Load Diff
12
tsconfig.app.json
Normal file
12
tsconfig.app.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
|
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||||
|
"exclude": ["src/**/__tests__/*"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
tsconfig.json
Normal file
11
tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
18
tsconfig.node.json
Normal file
18
tsconfig.node.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": "@tsconfig/node22/tsconfig.json",
|
||||||
|
"include": [
|
||||||
|
"vite.config.*",
|
||||||
|
"vitest.config.*",
|
||||||
|
"cypress.config.*",
|
||||||
|
"nightwatch.conf.*",
|
||||||
|
"playwright.config.*"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true,
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"types": ["node"]
|
||||||
|
}
|
||||||
|
}
|
30
vite.config.ts
Normal file
30
vite.config.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
base: './',
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
vueDevTools(),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
open: true,
|
||||||
|
proxy: {
|
||||||
|
"/api/v1": {
|
||||||
|
// target: "http://10.1.5.188:8011/",
|
||||||
|
target: "http://127.0.0.1:8012",
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: path => path.replace(/^\/api\/v1/, ''),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user