Merge pull request #5 from simple321vip/release-1.2

Release 1.2
This commit is contained in:
Guan
2023-02-02 23:03:05 +08:00
committed by GitHub
60 changed files with 3476 additions and 660 deletions
Vendored
-3
View File
@@ -45,9 +45,6 @@ spec:
- name: "volume-m2" - name: "volume-m2"
hostPath: hostPath:
path: "/root/.m2/repository" path: "/root/.m2/repository"
- name: "maven-config"
configMap:
name: maven-config
- name: "volume-docker" - name: "volume-docker"
hostPath: hostPath:
path: "/var/run/docker.sock" path: "/var/run/docker.sock"
+8 -8
View File
@@ -1,17 +1,17 @@
## Vue 3 + Typescript + Vite + pinia + vue-router ## Vue 3 + Typescript + Vite + pinia + vue-router
## Create project document how to bulid a project ## about creating project document how to bulid a project
before create project, you should install node to promit npm and node command is standy. before create project, you should install node to promit npm and node command is standy.
npm -v npm -v
node -v<br /> node -v<br />
// 在想要创建的工程路径下执行以下命令,3到6秒就可以结束 // 在想要创建的工程路径下执行以下命令,3到6秒就可以结束
npm init vite@latest npm init vite@latest
// 1.填写 g-home // 1.填写 violin-home
// 2.选择vue // 2.选择vue
// 3.vue.ts // 3.vue.ts
cd g-home cd violin-home
npm install npm install
## vue-router ## vue-router
@@ -33,19 +33,19 @@
// to enter the home page: // to enter the home page:
localhost:3000/home?tenantId=XXX&account=YYY&token=ABCDEFG localhost:3000/home?tenantId=XXX&account=YYY&token=ABCDEFG
## 环境配置 ## profiles
添加了.env.development ebv.test .env.production 文件 添加了.env.development ebv.test .env.production 文件
development: development:
开发环境,使用mock,不需要后台联动 开发环境,使用mock,不需要后台联动
test: test:
测试环境需要 后台java程序支持,同时需要百度第三方认证。 测试环境需要 后台java程序支持,同时需要百度第三方认证。
prod: prod:
生产环境通过 jenkins cicd进行发布配置 生产环境通过 jenkins ci 持续集成 argocd 做cd 持续发布
# 云服务器 # prod server
nginx as a veb server, i use the official image to build this app image. nginx as a web server, i use the official image to build this app image.
## 发布 ## publish
- npm run build 生成dist 文件夹 - npm run build 生成dist 文件夹
## 百度网盘开放平台 ## 百度网盘开放平台
+9
View File
@@ -74,4 +74,13 @@ export default [
} }
}, },
}, },
{
url: '/wiki/api/v1/reader/blogs/count',
method: 'GET',
response: () => {
return {
count: 101,
}
},
},
] as MockMethod[] ] as MockMethod[]
+45
View File
@@ -0,0 +1,45 @@
import { MockMethod } from 'vite-plugin-mock'
export default [
{
url: '/bookmark/api/v1/bookmark/count',
method: 'get',
response: () => {
return {
count: 53,
}
}
},
{
url: '/bookmark/api/v1/bookmark',
method: 'get',
response: () => {
return [
{
bk_id: '001',
bk_type_id: '2',
bk_type_name: '3',
comment: '4',
url: '5'
},
{
bk_id: '002',
bk_type_id: '2',
bk_type_name: '3',
comment: '4',
url: '5'
}
]
}
},
{
url: '/bookmark/api/v1/bookmark_type',
method: 'get',
response: () => {
return [
]
}
},
] as MockMethod[]
+30
View File
@@ -0,0 +1,30 @@
import { MockMethod } from 'vite-plugin-mock'
export default [
{
url: '/calendar/api/v1/event',
method: 'get',
response: () => {
return [
{
date: '2023-01-09',
title: '春游',
eventInfo: '春节不觉晓,处处闻啼鸟,夜来风雨声,花落知多少',
},
{
date: '2023-01-30',
title: '月底发工资',
eventInfo: '发了工资少一半,还了信用卡再少一半,试问如何攒下钱,只有两个字:做梦',
}
]
}
},
{
url: '/calendar/api/v1/event',
method: 'put',
response: () => {
return {
}
}
},
] as MockMethod[]
+102
View File
@@ -130,5 +130,107 @@ export default [
} }
} }
}, },
{
url: '/trader/api/v1/accounts',
method: 'get',
response: ({ body, query }) => {
return {
accounts: [
{
gateway_name: 'cpt',
account_id: '7788',
balance: '7',
frozen: '8',
},
{
gateway_name: 'cpt',
account_id: '778899',
balance: '8',
frozen: '9',
}
],
positions: [
{
symbol: 'czce',
exchange: 'RM301',
direction: '1',
volume: 222,
price: 2500,
frozen: 3,
pnl: 2,
yd_volume: 1,
}
],
trades: [
{
symbol: 'string',
exchange: 'string',
direction: 'string',
volume: 2,
price: 4,
orderid: 'string',
tradeid: 'string',
offset: 'string',
}
],
orders: [{
symbol: 'string',
exchange: 'string',
direction: 'string',
volume: 6,
price: 4,
orderid: 'string',
type: 'string',
offset: 'string',
traded: 7,
status: 'string',
// datetime :datetime
reference: 'string',
}]
}
}
},
{
url: '/trader/api/v1/strategy_file',
method: 'get',
response: ({ body, query }) => {
return [
{
file_name: 'string',
class_name: 'string',
status: 'string',
}
]
}
},
{
url: '/trader/api/v1/strategies',
method: 'get',
response: ({ body, query }) => {
return [
{
strategy_name: 'string',
class_name: 'string',
vt_symbol: 'string',
setting: {},
status: 1,
}
]
}
},
{
url: '/trader/api/v1/strategy_file/load',
method: 'get',
response: ({ body, query }) => {
return [
{
class_names: 'asdasdas'
}
]
}
},
] as MockMethod[] ] as MockMethod[]
+28
View File
@@ -0,0 +1,28 @@
import { MockMethod } from 'vite-plugin-mock'
export default [
{
url: '/onenote/api/v1/one_note',
method: 'get',
response: () => {
return [
{
section_id: 'x111',
section_name: 'A',
page_list: [
{
page_id: 'y111',
section_id: 'x111',
page_name: 'B',
},
{
page_id: 'y222',
section_id: 'x111',
page_name: 'C',
},
]
}
]
}
}
] as MockMethod[]
-56
View File
@@ -1,56 +0,0 @@
server {
listen 8080;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /write {
alias /usr/share/nginx/html;
index index.html index.htm;
}
location /home {
alias /usr/share/nginx/html;
index index.html index.htm;
}
location ~ /view {
alias /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
+1555 -43
View File
File diff suppressed because it is too large Load Diff
+7 -2
View File
@@ -12,8 +12,8 @@
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^1.1.4", "@element-plus/icons-vue": "^1.1.4",
"@types/node": "^17.0.23", "@types/node": "^17.0.23",
"axios": "^0.26.1",
"aplayer": "^1.10.1", "aplayer": "^1.10.1",
"axios": "^0.26.1",
"clipboard": "^2.0.10", "clipboard": "^2.0.10",
"copy-to-clipboard": "^3.3.1", "copy-to-clipboard": "^3.3.1",
"echarts": "^5.3.2", "echarts": "^5.3.2",
@@ -24,9 +24,14 @@
"lodash-unified": "^1.0.2", "lodash-unified": "^1.0.2",
"md-editor-v3": "^1.11.6", "md-editor-v3": "^1.11.6",
"moment": "^2.29.4", "moment": "^2.29.4",
"node-sass": "^8.0.0",
"pinia": "^2.0.13", "pinia": "^2.0.13",
"qs": "^6.10.3", "qs": "^6.10.3",
"sass": "^1.57.1",
"sass-loader": "^13.2.0",
"save": "^2.4.0", "save": "^2.4.0",
"scss": "^0.2.4",
"style-loader": "^3.3.1",
"type-fest": "^2.12.2", "type-fest": "^2.12.2",
"vue": "^3.2.25", "vue": "^3.2.25",
"vue-router": "^4.0.14", "vue-router": "^4.0.14",
@@ -40,10 +45,10 @@
"@vitejs/plugin-vue": "^2.3.0", "@vitejs/plugin-vue": "^2.3.0",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"typescript": "^4.5.4", "typescript": "^4.5.4",
"v-contextmenu": "^3.0.0",
"vite": "^2.9.13", "vite": "^2.9.13",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-mock": "^2.9.6", "vite-plugin-mock": "^2.9.6",
"v-contextmenu": "^3.0.0",
"vue-tsc": "^0.29.8" "vue-tsc": "^0.29.8"
} }
} }
+20 -1
View File
@@ -92,5 +92,24 @@ const sortBlogs = (data: Object, btId: string) => {
}) })
} }
const countBlogs = () => {
return service({
url: '/wiki/api/v1/reader/blogs/count',
method: 'GET',
headers: headers,
})
}
export { listAll, updateBtName, putBlogType, removeBlogType, sortBlogTypes, getContent, putBlog, updateContent, deleteContent, sortBlogs } export {
listAll,
updateBtName,
putBlogType,
removeBlogType,
sortBlogTypes,
getContent,
putBlog,
updateContent,
deleteContent,
sortBlogs,
countBlogs
}
+9 -1
View File
@@ -49,10 +49,18 @@ const delete_bookmarks = (params: Object) => {
}) })
} }
const bookmarks_count = () => {
return service({
url: '/bookmark/api/v1/bookmark/count',
method: 'GET',
})
}
export { export {
search_bookmark, search_bookmark,
put_bookmark, put_bookmark,
delete_bookmark, delete_bookmark,
delete_bookmarks, delete_bookmarks,
update_bookmark update_bookmark,
bookmarks_count
} }
+26
View File
@@ -0,0 +1,26 @@
import { service } from '../utils/request'
const headers = {
'Content-Type': 'application/json;charsetset=UTF-8'
}
const put_event = (data: Object) => {
return service({
url: '/calendar/api/v1/event',
method: 'PUT',
data: data
})
}
const get_event = () => {
return service({
url: '/calendar/api/v1/event',
method: 'GET',
headers: headers
})
}
export {
get_event,
put_event,
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

+70
View File
@@ -0,0 +1,70 @@
<template>
<span class="headerAvatar">
<template v-if="picType === 'avatar'">
<el-avatar v-if="useTenantStore.tenant.headerImg" :size="30" :src="avatar" />
<el-avatar v-else :size="30" :src="noAvatar" />
</template>
<template v-if="picType === 'img'">
<img v-if="useTenantStore.tenant.headerImg" :src="avatar" class="avatar">
<img v-else :src="noAvatar" class="avatar">
</template>
<template v-if="picType === 'file'">
<img :src="file" class="file">
</template>
</span>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { tenantStore } from '@/store/modules/tenant'
import noAvatarPng from '@/assets/noBody.png'
const props = defineProps({
picType: {
type: String,
required: false,
default: 'avatar'
},
picSrc: {
type: String,
required: false,
default: ''
}
})
const path = ref(import.meta.env.VITE_BASE_API + '/')
const noAvatar = ref(noAvatarPng)
const useTenantStore = tenantStore()
const avatar = computed(() => {
if (props.picSrc === '') {
if (useTenantStore.tenant.headerImg !== '' && useTenantStore.tenant.headerImg.slice(0, 4) === 'http') {
return useTenantStore.tenant.headerImg
}
return path.value + useTenantStore.tenant.headerImg
} else {
if (props.picSrc !== '' && props.picSrc.slice(0, 4) === 'http') {
return props.picSrc
}
return path.value + props.picSrc
}
})
const file = computed(() => {
if (props.picSrc && props.picSrc.slice(0, 4) !== 'http') {
return path.value + props.picSrc
}
return props.picSrc
})
</script>
<style scoped>
.headerAvatar {
display: flex;
justify-content: center;
align-items: center;
margin-right: 8px;
}
.file {
width: 80px;
height: 80px;
position: relative;
}
</style>
+43 -35
View File
@@ -1,12 +1,11 @@
<template> <template>
<div class="4444"> <div>
<el-container> <el-container>
<el-aside width="150px"> <el-aside width="180px">
<SideBar></SideBar> <SideBar></SideBar>
</el-aside> </el-aside>
<el-container> <el-container>
<el-header <el-header>
style="border-bottom-style:solid; border-bottom-width: 0.5px; border-bottom-color:rgba(151, 151, 151, 0.3);">
<NavBar></NavBar> <NavBar></NavBar>
</el-header> </el-header>
<el-main> <el-main>
@@ -20,7 +19,6 @@
<div> <div>
<el-avatar @click="openGithub" src="https://github.githubassets.com/favicons/favicon.svg" /> <el-avatar @click="openGithub" src="https://github.githubassets.com/favicons/favicon.svg" />
</div> </div>
</el-footer> </el-footer>
</el-container> </el-container>
</el-container> </el-container>
@@ -31,6 +29,32 @@
import MainApp from '../layout/MainApp.vue' import MainApp from '../layout/MainApp.vue'
import NavBar from "../layout/NavBar.vue" import NavBar from "../layout/NavBar.vue"
import SideBar from "../layout/SideBar.vue" import SideBar from "../layout/SideBar.vue"
import { settingsStore } from "@/store/modules/settings"
// -- IMPORT --
const useSettingsStore = settingsStore()
// -- REACTIVE OBJECT --
// -- REF OBJECT --
// -- EVENT DEFINITION
const iniPage = () => {
const screenWidth = document.body.clientWidth
if (screenWidth < 1000) {
useSettingsStore.settings.isMobile = true
// isSider.value = false
// isCollaps.value = true
} else if (screenWidth >= 1000 && screenWidth < 1200) {
useSettingsStore.settings.isMobile = false
// isSider.value = false
// isCollaps.value = true
} else {
useSettingsStore.settings.isMobile = false
// isSider.value = true
// isCollaps.value = false
}
}
const openGithub = () => { const openGithub = () => {
let href = 'https://github.com/simple321vip' let href = 'https://github.com/simple321vip'
@@ -42,48 +66,32 @@ const openSiteQuery = () => {
window.open(href, '_blank') window.open(href, '_blank')
} }
/**
* AUTO INVOKE FUNCTION
*/
iniPage()
</script> </script>
<style scoped> <style scoped>
/* .el-header, .el-header {
.el-footer { border-bottom-style: solid;
border-bottom-width: 0.5px;
color: rgb(51, 51, 51); border-bottom-color: rgba(151, 151, 151, 0.3);
text-align: center;
line-height: 60px;
} */
/*
.el-aside {
background-color: #e9eef3;
color: #333;
text-align: left;
line-height: 200px;
} }
.el-main { .el-main {
background-color: #e9eef3; padding: 16px;
color: #333; overflow: hidden;
/* line-height: 450px; */
/* vertical-align: top;
padding: 5px;
} }
body > .el-container { .el-aside {
margin-bottom: 40px; margin-left: -10px;
background: #191a23;
} }
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}
.el-container:nth-child(7) .el-aside {
line-height: 320px;
} */
.el-footer { .el-footer {
margin-top: 20px;
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
} }
+10 -2
View File
@@ -1,11 +1,19 @@
<template> <template>
<section class="section-main" style="height:100%;"> <section class="section-main" v-loading="usesSttingsStore.isLoading" element-loading-text="页面加载中……"
<router-view /> element-loading-background="rgba(0, 0, 0, 0.1)">
<router-view v-if="usesSttingsStore.isRouterAlive" />
</section> </section>
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import { settingsStore } from "@/store/modules/settings"
const usesSttingsStore = settingsStore()
</script> </script>
<style> <style>
.section-main {
height: 100%;
padding: 14px;
}
</style> </style>
+75 -67
View File
@@ -1,88 +1,96 @@
<template> <template>
<div class="nav-main"> <el-row class="pd-0">
<div style="width: 800px;"> <el-col :xs="2" :lg="1" :md="1" :sm="1" :xl="1" style="z-index:100">
<div> <!-- <div class="menu-total" @click="totalCollapse">
当前时间{{ time }} <div v-if="isCollapse" class="gvaIcon gvaIcon-arrow-double-right" />
<div v-else class="gvaIcon gvaIcon-arrow-double-left" />
</div> -->
</el-col>
<el-col :xs="10" :lg="14" :md="14" :sm="9" :xl="14" :pull="1">
<!-- 修改为手机端不显示顶部标签 -->
<!-- <el-breadcrumb v-show="!isMobile" class="breadcrumb">
<el-breadcrumb-item
v-for="item in matched.slice(1,matched.length)"
:key="item.path"
>{{ fmtTitle(item.meta.title,route) }}</el-breadcrumb-item>
</el-breadcrumb> -->
</el-col>
<el-col :xs="12" :lg="9" :md="9" :sm="14" :xl="9">
<div class="right-box">
<el-dropdown>
<div class="dp-flex justify-content-center align-items height-full width-full">
<span class="header-avatar" style="cursor: pointer">
<CustomPic />
<span v-show="!useSettingsStore.isMobile" style="margin-left: 5px">{{
useTenantStore.tenant.account
}}</span>
<el-icon>
<arrow-down />
</el-icon>
</span>
</div> </div>
<div>
期货交易时间&nbsp
<el-icon color="yellow">
<Sunrise />
</el-icon>&nbsp 9:00-11:30 &nbsp
<el-icon color="gold">
<Sunny />
</el-icon>&nbsp 13:30-15:00 &nbsp
<el-icon color="blue">
<Moon />
</el-icon>&nbsp 21:00-23:00
</div>
</div>
<el-dropdown trigger="click" v-show="tenant.account">
<el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png" />
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu class="dropdown-group">
<el-dropdown-item>profile</el-dropdown-item> <el-dropdown-item>
<el-dropdown-item @click="dialogVisible = true">退出</el-dropdown-item> <span style="font-weight: 600;">
当前角色{{ useTenantStore.tenant.account }}
</span>
</el-dropdown-item>
<template v-if="useTenantStore.tenant.account">
<!-- <el-dropdown-item v-for="item in userStore.userInfo.authorities.filter(i=>i.authorityId!==userStore.userInfo.authorityId)" :key="item.authorityId" @click="changeUserAuth(item.authorityId)">
<span>
切换为{{ item.authorityName }}
</span>
</el-dropdown-item> -->
</template>
<el-dropdown-item icon="avatar" @click="toPerson">个人信息</el-dropdown-item>
<el-dropdown-item icon="reading-lamp" @click="useTenantStore.logout"> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
</div> </div>
<el-dialog v-model="dialogVisible" title="提示" width="30%"> </el-col>
<span>确认你的登出操作么</span> </el-row>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="doLogout">确认</el-button>
</span>
</template>
</el-dialog>
<span @click="doLogin" v-show="!tenant.account">登录</span>
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import $moment from "moment"; import CustomPic from "@/components/customPic/index.vue";
import { reactive, ref } from "vue"; import { tenantStore } from '@/store/modules/tenant'
import router from '../../router' import { settingsStore } from '@/store/modules/settings'
import { tenantStore } from '../../store/tenant'
import { logout } from '../../api/user'
// obtain user infomation
const tenant = tenantStore()
tenant.reflush()
const dialogVisible = ref(false)
// show time // -- IMPORT --
let time = ref<String>($moment().format("YYYY年MM月DD日 HH:mm:ss")); const useTenantStore = tenantStore()
setInterval(() => { const useSettingsStore = settingsStore()
time.value = $moment().format("YYYY年MM月DD日 HH:mm:ss");
}, 1000);
// login logout function // -- REACTIVE OBJECT --
const doLogin = () => {
router.push({ // -- REF OBJECT --
path: '/login'
}) // -- EVENT DEFINITION
} const toPerson = () => {
const doLogout = () => {
dialogVisible.value = false
logout(tenant.id).catch(() => { console.log(1) }).finally(() => {
tenant.logout()
router.push({
path: '/login'
})
})
} }
useTenantStore.reflush()
</script> </script>
<style> <style lang="scss" scoped>
.open_side { .right-box {
background: url("../../assets/logo.png") center no-repeat; padding-top: 16px;
background-size: cover; display: flex;
justify-content: flex-end;
align-items: center;
img {
vertical-align: middle;
border: 1px solid #ccc;
border-radius: 6px;
}
} }
.nav-main { .header-avatar {
display: flex; display: flex;
justify-content: center;
align-items: center; align-items: center;
justify-content: space-between;
/* background-color: bisque; */
} }
</style> </style>
+104 -9
View File
@@ -1,27 +1,113 @@
<template> <template>
<div> <div>
<el-menu router class="side_Style" :style="{ height: sideHeight }"> <el-menu router class="el-menu-vertical" :style="{ height: sideHeight }" :background-color="theme.background"
<el-menu-item v-for="(route, index) in current_routes" v-bind:key="index" v-bind:index="route.path" :active-text-color="theme?.background">
v-bind:title="route.meta?.name">{{ route.meta?.name }}</el-menu-item>
<el-menu-item v-for="(route, index) in current_routes" :key="index" :index="route.path" :title="route.meta?.name"
@click="onclick()">
<template #title>
<div class="gva-menu-item">
<el-icon v-if="index == 0" :size="20">
<Sunny />
</el-icon>
<el-icon v-if="index == 1" :size="20">
<EditPen />
</el-icon>
<el-icon v-if="index == 2" :size="20">
<Star />
</el-icon>
<el-icon v-if="index == 3" :size="20">
<Notebook />
</el-icon>
<el-icon v-if="index == 4" :size="20">
<Calendar />
</el-icon>
<el-icon v-if="index == 5" :size="20">
<MostlyCloudy />
</el-icon>
<el-icon v-if="index == 10" :size="20">
<Setting />
</el-icon>
<span>{{ route.meta?.name }}</span>
</div>
</template>
</el-menu-item>
</el-menu> </el-menu>
</div> </div>
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import { RouteRecordRaw } from "vue-router"; import { onBeforeMount, ref } from 'vue'
// import { store } from "../../store/index"; import router from "@/router/index"
import router from "../../router/index"; import { settingsStore } from "@/store/modules/settings"
import { onBeforeMount, ref } from 'vue'; import { Theme } from "@/entity/index";
let current_routes = router.options.routes[0].children;
// -- IMPORT --
const useSettingsStore = settingsStore()
// -- REACTIVE OBJECT --
// -- REF OBJECT --
let sideHeight = ref("") let sideHeight = ref("")
let current_routes = router.options.routes[0].children
const theme = ref<Theme>({
background: '#fff',
activeBackground: '#4D70FF',
activeText: '#fff',
normalText: '#333',
hoverBackground: 'rgba(64, 158, 255, 0.08)',
hoverText: '#333'
})
// -- EVENT DEFINITION
const onclick = () => {
activeBackground.value = '#4D70FF'
}
const getTheme = () => {
switch (useSettingsStore.sideMode) {
// white color
case '#fff':
theme.value = {
background: '#fff',
activeBackground: '#4D70FF',
activeText: '#fff',
normalText: '#333',
hoverBackground: 'rgba(64, 158, 255, 0.08)',
hoverText: '#333',
}
break;
// black color
case '#191a23':
theme.value = {
background: '#191a23',
activeBackground: '#4D70FF',
activeText: '#fff',
normalText: '#fff',
hoverBackground: 'rgba(64, 158, 255, 0.08)',
hoverText: '#fff',
}
break;
}
}
/**
* AUTO INVOKE FUNCTION
*/
getTheme()
onBeforeMount(() => { onBeforeMount(() => {
sideHeight.value = window.innerHeight + 'px' sideHeight.value = window.innerHeight + 'px'
}) })
const activeBackground = ref(theme.value.activeBackground)
onclick()
</script> </script>
<style> <style lang="scss" scoped>
.side_Style { .side_Style {
width: 100px; width: 100px;
} }
@@ -30,6 +116,15 @@ onBeforeMount(() => {
width: 200px; width: 200px;
} }
.gva-menu-item {
color: #fff;
}
.el-menu-item.is-active {
color: v-bind(activeBackground);
background: v-bind(activeBackground);
}
/* .el-menu-item { /* .el-menu-item {
background-color: #01dfd7; background-color: #01dfd7;
} */ } */
+14 -14
View File
@@ -26,7 +26,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import { tenantStore } from '../../store/tenant' import { tenantStore } from '../../store/modules/tenant'
import router from '../../router' import router from '../../router'
import baiduCloudImage from "../../assets/baiducloud.png" import baiduCloudImage from "../../assets/baiducloud.png"
import { config, scan } from '../../const/config' import { config, scan } from '../../const/config'
@@ -34,7 +34,7 @@ import { config, scan } from '../../const/config'
let qrcode = config.BAIDU_CLOUD_URL + config.RESPONSE_TYPE + config.CLIENT_ID + config.REDIRECT_URI + config.SCOPE + config.DEVICE_ID + config.QR_CODE + config.DISPLAY let qrcode = config.BAIDU_CLOUD_URL + config.RESPONSE_TYPE + config.CLIENT_ID + config.REDIRECT_URI + config.SCOPE + config.DEVICE_ID + config.QR_CODE + config.DISPLAY
const tenant = tenantStore() const useTenantStore = tenantStore()
const ruleFormRef = ref<FormInstance>() const ruleFormRef = ref<FormInstance>()
const loginForm = reactive({ const loginForm = reactive({
@@ -66,19 +66,19 @@ const dologin = (form: any) => {
if (!form) if (!form)
return return
form.validate((valid: any) => { // form.validate((valid: any) => {
if (valid) { // if (valid) {
tenant.login(loginForm.user_id, loginForm.password).then(res => { // useTenantStore.login(loginForm.user_id, loginForm.password).then(res => {
console.log(1) // console.log(1)
router.push({ // router.push({
path: '/home' // path: '/home'
}) // })
}) // })
} else { // } else {
return false // return false
} // }
}) // })
} }
const style = reactive({} as any) const style = reactive({} as any)
+23 -1
View File
@@ -1,3 +1,5 @@
import { Tenant } from '@/entity';
import { tenantStore } from '@/store/modules/tenant';
import router from '../router' import router from '../router'
import { setToken, setTenant } from '../utils/auth' import { setToken, setTenant } from '../utils/auth'
@@ -41,7 +43,27 @@ const scan_method: any = {
production: (qrcode: string) => { production: (qrcode: string) => {
window.location.href = qrcode window.location.href = qrcode
}, },
development: () => { development: async () => {
const useTenantStore = tenantStore()
const tenant: Tenant = {
tenant_id: '7788',
account: '小小的测试账户'
}
let token = 'ABCDEFG'
await useTenantStore.login(tenant, token).then(() => {
const { href } = router.resolve({
path: '/'
})
window.open(href, '_self')
}).catch((error) => {
const { href } = router.resolve({
path: '/login'
})
window.open(href, '_self')
})
setToken("token") setToken("token")
setTenant( setTenant(
{ {
+24 -2
View File
@@ -1,6 +1,28 @@
type Tenant = { type Tenant = {
id: string, tenant_id: string,
account: string account: string
} }
export { Tenant } type Theme = {
background: string,
activeBackground: string,
activeText: string,
normalText: string,
hoverBackground: string,
hoverText: string,
}
type DataTimeline = {
from: string,
title: string,
showDayAndMonth: boolean,
message: string,
}
type Event = {
event_date: string,
title: string,
event_info: string,
}
export { Tenant, Theme, DataTimeline, Event }
+7 -117
View File
@@ -2,137 +2,27 @@ import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import ElementPlus from "element-plus"; import ElementPlus from "element-plus";
import 'element-plus/dist/index.css' import 'element-plus/dist/index.css'
import { store } from './store'
import router from './router' import router from './router'
import { createPinia } from 'pinia' import '@/permission'
const store = createPinia()
import * as ElementPlusIconsVue from '@element-plus/icons-vue' import * as ElementPlusIconsVue from '@element-plus/icons-vue'
let app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
import contentmenu from 'v-contextmenu' import contentmenu from 'v-contextmenu'
import 'v-contextmenu/dist/themes/default.css' import 'v-contextmenu/dist/themes/default.css'
app.use(contentmenu)
import { setupMock } from '../mock' import { setupMock } from '../mock'
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
setupMock() setupMock()
} }
let app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(contentmenu)
app.use(router) app.use(router)
app.use(ElementPlus) app.use(ElementPlus)
app.use(store) app.use(store)
import { getToken, getTenant, setToken, setTenant } from './utils/auth'
import { Tenant } from './entity/index'
import Cookies from 'js-cookie'
import { tenantStore } from './store/tenant'
const whiteList = ['/login', '/register', '/sorryPage']
let url = window.location.href
async function checktoken(url: string) {
if (url.search("register") != -1) {
let arr = url.split('?')
let parameters = arr[1].split('&')
let query_data: { [key: string]: any } = {}
parameters.forEach(parameter => {
let parameter_list = parameter.split("=")
query_data[parameter_list[0]] = parameter_list[1]
})
router.push({
path: '/register',
query: query_data
})
return
}
if (url.search("/home/") != -1) {
let arr = url.split('?')
let parameters = arr[1].split('&')
let query_data: { [key: string]: any } = {}
parameters.forEach(parameter => {
let parameter_list = parameter.split("=")
query_data[parameter_list[0]] = parameter_list[1]
})
const tenant = {
tenant_id: query_data.tenantId,
account: query_data.account
}
const tenant_store = tenantStore()
await tenant_store.login(tenant, query_data.token).then(() => {
const { href } = router.resolve({
path: '/'
});
window.open(href, '_self');
}).catch((error) => {
const { href } = router.resolve({
path: '/login'
});
window.open(href, '_self');
})
}
if (url.search("sorryPage") != -1) {
router.push({
path: '/sorryPage',
})
return
}
}
checktoken(url)
/**
* ルーター監視
* 1、トーケン取得
* 2、トーケンなし場合はログイン画面に遷移
* 3、トーケンあり場合:
*  既にログイン場合はデフォルト /  に遷移
*  
*/
router.beforeEach((to, from, next) => {
console.log(from.path, to.path)
const token = getToken()
if (typeof token === 'string') {
switch (to.path) {
case '/login':
next('/')
break;
case '/':
const tenant = <Tenant>(JSON.parse(getTenant() as string))
if (url.search("write") != -1) {
next('/BlogEditer')
} else if (url.search("view") != -1) {
let bid = Cookies.get('bid')
router.push({
path: '/BlogViewer',
query: { bid: bid }
})
} else {
router.push({
path: '/home',
query: tenant
})
}
break;
default:
next()
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next('/login')
}
}
})
app.mount('#app') app.mount('#app')
+89
View File
@@ -0,0 +1,89 @@
import router from './router'
import Cookies from 'js-cookie'
import { getToken, getTenant } from './utils/auth'
import { Tenant } from './entity/index'
import { tenantStore } from './store/modules/tenant'
const whiteList = ['/login', '/register', '/sorryPage']
let url = window.location.href
router.beforeEach((to, from, next) => {
const token = getToken()
if (typeof token === 'string') {
switch (to.path) {
case '/login':
next('/')
break;
case '/':
const tenant = <Tenant>(JSON.parse(getTenant() as string))
if (url.search("write") != -1) {
next('/BlogEditer')
} else if (url.search("view") != -1) {
let bid = Cookies.get('bid')
router.push({
path: '/BlogViewer',
query: { bid: bid }
})
} else {
router.push({
path: '/home',
query: tenant
})
}
break;
default:
next()
}
} else {
if (url.search("register") != -1) {
let arr = url.split('?')
let parameters = arr[1].split('&')
let query_data: { [key: string]: any } = {}
parameters.forEach(parameter => {
let parameter_list = parameter.split("=")
query_data[parameter_list[0]] = parameter_list[1]
})
router.push({
path: '/register',
query: query_data
})
}
if (url.search("/home/") != -1) {
let arr = url.split('?')
let parameters = arr[1].split('&')
let query_data: { [key: string]: any } = {}
parameters.forEach(parameter => {
let parameter_list = parameter.split("=")
query_data[parameter_list[0]] = parameter_list[1]
})
const tenant = {
tenant_id: query_data.tenantId,
account: query_data.account
}
const tenant_store = tenantStore()
tenant_store.login(tenant, query_data.token).then(() => {
const { href } = router.resolve({
path: '/'
});
window.open(href, '_self');
}).catch((error) => {
const { href } = router.resolve({
path: '/login'
});
window.open(href, '_self');
})
}
if (url.search("sorryPage") != -1) {
router.push({
path: '/sorryPage',
})
}
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next('/login')
}
}
})
+28 -27
View File
@@ -1,5 +1,6 @@
import { createRouter, createMemoryHistory, RouteRecordRaw } from 'vue-router' import { createRouter, createMemoryHistory, RouteRecordRaw } from 'vue-router'
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
{ {
path: "/", path: "/",
@@ -14,103 +15,103 @@ const routes: Array<RouteRecordRaw> = [
}, },
{ {
path: '/blog', path: '/blog',
component: () => import('../view/blog/index.vue'), component: resolve => (require('@/view/blog/index.vue'), resolve),
meta: { meta: {
name: '博客' name: '博客'
} }
}, },
{ {
path: '/bookmark', path: '/bookmark',
component: () => import('../view/bookmark/index.vue'), component: resolve => (require('@/view/bookmark/index.vue'), resolve),
meta: { meta: {
name: '书签' name: '书签'
} }
}, },
{ {
path: '/school', path: '/onenote',
component: () => import('../view/school/index.vue'), component: resolve => (require('@/view/onenote/index.vue'), resolve),
meta: { meta: {
name: '学习' name: 'onenote'
} }
}, },
{ {
path: '/thinking', path: '/calendar',
component: () => import('../view/thinking/index.vue'), component: resolve => (require('@/view/calendar/index.vue'), resolve),
meta: { meta: {
name: '感悟人生' name: '日历'
} }
}, },
{ {
path: '/cloud', path: '/cloud',
component: () => import('../view/cloud/index.vue'), component: resolve => (require('@/view/cloud/index.vue'), resolve),
meta: { meta: {
name: '个人云盘' name: '个人云盘'
} }
}, },
{ {
path: '/market', path: '/market',
component: () => import('../view/recommend/index.vue'), component: resolve => (require('@/view/recommend/index.vue'), resolve),
meta: { meta: {
name: '市场行情' name: '市场行情'
} }
}, },
{ {
path: '/capital', path: '/capital',
component: () => import('../view/capital/index.vue'), component: resolve => (require('@/view/capital/index.vue'), resolve),
meta: { meta: {
name: '资金管理' name: '资金管理'
} }
}, },
// {
// path: '/trader',
// component: () => import('../view/trader/index.vue'),
// meta: {
// name: 'trader'
// }
// },
{ {
path: '/strategy', path: '/strategy',
component: () => import('../view/trader/cta_strategy_file.vue'), component: resolve => (require('@/view/trader/cta_strategy_file.vue'), resolve),
meta: { meta: {
name: '策略模板' name: '策略模板'
} }
}, },
{ {
path: '/strategy_instance', path: '/strategy_instance',
component: () => import('../view/trader/cta_strategy.vue'), component: resolve => (require('@/view/trader/cta_strategy.vue'), resolve),
meta: { meta: {
name: 'CTA策略' name: 'CTA策略'
} }
}, },
{
path: '/settings',
component: resolve => (require('@/view/settings/index.vue'), resolve),
meta: {
name: '设置'
}
},
] ]
}, },
{ {
path: "/feedback", path: "/feedback",
component: () => import('../view/blog/mdview.vue') component: resolve => (require('@/view/blog/mdview.vue'), resolve),
}, },
{ {
path: "/BlogEditer", path: "/BlogEditer",
component: () => import('../view/blog/createBlog.vue') component: resolve => (require('@/view/blog/createBlog.vue'), resolve),
}, },
{ {
path: "/BlogViewer", path: "/BlogViewer",
component: () => import('../view/blog/mdview.vue') component: resolve => (require('@/view/blog/mdview.vue'), resolve),
}, },
{ {
path: "/login", path: "/login",
component: () => import('../components/login/login.vue') component: () => import('@/components/login/login.vue')
}, },
{ {
path: "/illustration", path: "/illustration",
component: () => import('../components/illustration/index.vue') component: resolve => (require('../components/illustration/login.vue'), resolve)
}, },
{ {
path: "/register", path: "/register",
component: () => import('../components/login/register.vue') component: resolve => (require('../components/login/register.vue'), resolve)
}, },
{ {
path: "/sorryPage", path: "/sorryPage",
component: () => import('../components/login/sorry.vue') component: resolve => (require('../components/login/sorry.vue'), resolve)
}, },
] ]
+14
View File
@@ -0,0 +1,14 @@
import axios from 'axios'
const service = axios.create()
const get_holiday = () => {
return service({
url: 'https://holidays-jp.github.io/api/v1/2023/date.json',
method: 'GET'
})
}
export {
get_holiday
}
+17
View File
@@ -0,0 +1,17 @@
import axios from 'axios'
const service = axios.create()
export function Commits(page: number) {
return service({
url: 'https://api.github.com/repos/simple321vip/violin-home/commits?page=' + page,
method: 'get'
})
}
export function Members() {
return service({
url: 'https://api.github.com/orgs/FLIPPED-AURORA/members',
method: 'get'
})
}
+30
View File
@@ -0,0 +1,30 @@
import axios from 'axios'
import { ref } from 'vue'
const weatherInfo = ref('对不起主人,我还没有获得天气信息,暂时无法显示哟。')
const amapKey = '8e8baa8a7317586c29ec694895de6e0a'
export const useWeatherInfo = () => {
ip()
return weatherInfo
}
export const ip = async () => {
// key换成你自己的 https://console.amap.com/dev/index
// if (amapKey === '') {
if (amapKey.length == 1) {
return false
}
const res = await axios.get('https://restapi.amap.com/v3/ip?key=' + amapKey)
if (res.data.adcode) {
getWeather(res.data.adcode)
}
}
const getWeather = async (code: string) => {
const response = await axios.get('https://restapi.amap.com/v3/weather/weatherInfo?key=' + amapKey + '&extensions=base&city=' + code)
if (response.data.status === '1') {
const s = response.data.lives[0]
weatherInfo.value = s.city + ' 天气:' + s.weather + ' 温度:' + s.temperature + '摄氏度 风向:' + s.winddirection + ' 风力:' + s.windpower + '级 空气湿度:' + s.humidity
}
}
+3 -67
View File
@@ -1,69 +1,5 @@
import { defineStore, acceptHMRUpdate } from 'pinia' import { createPinia } from "pinia"
import { authorize } from '../api/user'
import { getToken, setToken, resetToken, setTenant, getTenant } from '../utils/auth'
import { Tenant } from '../entity/index'
const store = createPinia()
interface Record { export { store }
isFolder: boolean
path: string
records: Set<Record>
}
/**
* skeleton
*
*
*/
export const tenantStore = defineStore({
id: 'cloud',
state: () => ({
cloud: new Set<Record>(),
}),
getters: {
},
actions: {
logout() {
this.$patch({
})
resetToken()
// we could do other stuff like redirecting the user
},
/**
* Attempt to login a user
* @param {string} user_id
* @param {string} user_password
*/
async login(tenant: any, token: string) {
try {
await authorize({
tenant_id: tenant.tenant_id, token: token
})
setToken(token)
setTenant(tenant)
} catch {
Promise.resolve("authorize error")
}
},
async reflush() {
if (getToken()) {
const tenant = <Tenant>(JSON.parse(getTenant() as string))
const token = <string>getToken()
this.$patch({
})
}
}
},
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(tenantStore, import.meta.hot))
}
+29
View File
@@ -0,0 +1,29 @@
import { acceptHMRUpdate, defineStore } from "pinia"
import { computed, ref } from "vue"
export const settingsStore = defineStore('settings', () => {
const settings = ref({
sideMode: '#191a23',
isMobile: false,
isRouterAlive: true,
isLoading: false
})
const sideMode = computed(() => settings.value.sideMode)
const isMobile = computed(() => settings.value.isMobile)
const isRouterAlive = computed(() => settings.value.isRouterAlive)
const isLoading = computed(() => settings.value.isLoading)
return {
settings,
sideMode,
isMobile,
isRouterAlive,
isLoading,
}
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(settingsStore, import.meta.hot))
}
+51
View File
@@ -0,0 +1,51 @@
import { acceptHMRUpdate, defineStore } from "pinia"
import { reactive } from "vue"
import { get_vt_symbols, get_exchanges, get_subscribe_vt_symbols } from '../../api/capital'
export const strategyStore = defineStore('strategy', () => {
const strategy = reactive({
class_names: [],
vt_symbols: new Map(),
subscribe_vt_symbols: [],
exchanges: []
})
const select_exchanges = async () => {
if (strategy.exchanges.length == 0) {
get_exchanges().then(res => {
strategy.exchanges = res.data.exchanges
})
}
}
const select_vt_symbols = async () => {
if (strategy.vt_symbols.size == 0) {
get_vt_symbols().then(res => {
let keys = Object.keys(res.data.vt_symbols)
keys.forEach(key => {
strategy.vt_symbols.set(key, res.data.vt_symbols[key])
})
})
}
}
const select_subscribe_vt_symbols = async () => {
if (strategy.subscribe_vt_symbols.length == 0) {
get_subscribe_vt_symbols().then(res => {
strategy.subscribe_vt_symbols = res.data.subscribe_vt_symbols
})
}
}
return {
strategy,
select_exchanges,
select_vt_symbols,
select_subscribe_vt_symbols
}
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(strategyStore, import.meta.hot))
}
+56
View File
@@ -0,0 +1,56 @@
import { defineStore, acceptHMRUpdate } from 'pinia'
import { authorize } from '../../api/user'
import { getToken, setToken, resetToken, setTenant, getTenant } from '../../utils/auth'
import { Tenant } from '../../entity/index'
import { ref } from 'vue'
export const tenantStore = defineStore('tenant', () => {
const tenant = ref({
account: '',
token: '',
id: '',
headerImg: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
})
const login = async (tenant: Tenant, token: string) => {
try {
await authorize({
tenant_id: tenant.tenant_id, token: token
})
setToken(token)
setTenant(tenant)
} catch {
Promise.resolve("authorize error")
}
}
const logout = async () => {
tenant.value.account = ''
tenant.value.token = ''
tenant.value.id = ''
resetToken()
window.location.reload()
}
const reflush = async () => {
if (getToken()) {
const tenantStorage = <Tenant>(JSON.parse(getTenant() as string))
const token = <string>getToken()
tenant.value.account = tenantStorage.account
tenant.value.token = token
tenant.value.id = tenantStorage.tenant_id
}
}
return {
tenant,
login,
logout,
reflush
}
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(tenantStore, import.meta.hot))
}
-43
View File
@@ -1,43 +0,0 @@
import { acceptHMRUpdate, defineStore } from "pinia"
import { get_vt_symbols, get_exchanges, get_subscribe_vt_symbols } from '../api/capital'
export const strategyStore = defineStore({
id: 'strategy',
state: () => ({
class_names: [],
vt_symbols: new Map(),
subscribe_vt_symbols: [],
exchanges: []
}),
actions: {
async select_exchanges() {
if (this.exchanges.length == 0) {
get_exchanges().then(res => {
this.$patch({ exchanges: res.data.exchanges })
})
}
},
async select_vt_symbols() {
if (this.vt_symbols.size == 0) {
get_vt_symbols().then(res => {
let keys = Object.keys(res.data.vt_symbols)
keys.forEach(key => {
this.vt_symbols.set(key, res.data.vt_symbols[key])
})
})
}
},
async select_subscribe_vt_symbols() {
if (this.subscribe_vt_symbols.length == 0) {
get_subscribe_vt_symbols().then(res => {
this.$patch({ subscribe_vt_symbols: res.data.vt_symbols })
})
}
}
}
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(strategyStore, import.meta.hot))
}
-75
View File
@@ -1,75 +0,0 @@
import { defineStore, acceptHMRUpdate } from 'pinia'
import { authorize } from '../api/user'
import { getToken, setToken, resetToken, setTenant, getTenant } from '../utils/auth'
import { Tenant } from '../entity/index'
/**
* Simulate a login
* @param {string} a
* @param {string} p
*/
// function apiLogin(a, p) {
// if (a === 'ed' && p === 'ed') return Promise.resolve({ isAdmin: true })
// if (p === 'ed') return Promise.resolve({ isAdmin: false })
// return Promise.reject(new Error('invalid credentials'))
// }
export const tenantStore = defineStore({
id: 'tenant',
state: () => ({
account: '',
token: '',
id: ''
}),
getters: {
},
actions: {
logout() {
this.$patch({
account: '',
token: '',
id: ''
})
resetToken()
// we could do other stuff like redirecting the user
},
/**
* Attempt to login a tenant
* @param {object} tenant
* @param {string} token
*/
async login(tenant: any, token: string) {
try {
await authorize({
tenant_id: tenant.tenant_id, token: token
})
setToken(token)
setTenant(tenant)
} catch {
Promise.resolve("authorize error")
}
},
async reflush() {
if (getToken()) {
const tenant = <Tenant>(JSON.parse(getTenant() as string))
const token = <string>getToken()
this.$patch({
account: tenant.account,
token: token,
id: tenant.id
})
}
}
},
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(tenantStore, import.meta.hot))
}
+1
View File
@@ -0,0 +1 @@
$color: red;
+43
View File
@@ -0,0 +1,43 @@
import { addZero } from "./number"
// 对Date的扩展,将 Date 转化为指定格式的String
// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
// (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
// (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18
// eslint-disable-next-line no-extend-native
const format = (date: Date, fmt: string) => {
var o = {
'M+': date.getMonth() + 1, // 月份
'd+': date.getDate(), // 日
'h+': date.getHours(), // 小时
'm+': date.getMinutes(), // 分
's+': date.getSeconds(), // 秒
'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
'S': date.getMilliseconds() // 毫秒
}
if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) }
for (var k in o) {
// if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) }
}
return fmt
}
const formatTimeToStr = (times: any, pattern: string) => {
if (!pattern) {
pattern = 'yyyy-MM-dd hh:mm:ss'
}
let date = format(new Date(times), pattern)
return date.toLocaleString()
}
// 2023-01-03
const toLocalDate = (date: Date) => {
return date.getFullYear() + '-' + addZero(date.getMonth() + 1) + '-' + addZero(date.getDate())
}
export {
formatTimeToStr,
toLocalDate,
}
+11 -1
View File
@@ -2,4 +2,14 @@ const toPercent = (num: number) => {
return (num * 100).toFixed(2) + "%" return (num * 100).toFixed(2) + "%"
} }
export { toPercent } const addZero = (num: number) => {
if (num > 10) {
return num
}
return '0' + num
}
export {
toPercent,
addZero
}
+1 -1
View File
@@ -53,7 +53,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue'
import { delete_bookmark_type, post_bookmark_type, get_bookmark_type } from '../../api/bookmark_type' import { delete_bookmark_type, post_bookmark_type, get_bookmark_type } from '../../api/bookmark_type'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
const formLabelWidth = '140px' const formLabelWidth = '140px'
+1 -1
View File
@@ -10,7 +10,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue'
import { put_bookmark_type } from '../../api/bookmark_type' import { put_bookmark_type } from '../../api/bookmark_type'
const bookmark_type_name = ref<String>('') const bookmark_type_name = ref<String>('')
+8 -8
View File
@@ -3,12 +3,8 @@
<el-form :model="dialog_form"> <el-form :model="dialog_form">
<el-form-item label="分类" :label-width="formLabelWidth"> <el-form-item label="分类" :label-width="formLabelWidth">
<el-select v-model="dialog_form.bk_type_id" placeholder> <el-select v-model="dialog_form.bk_type_id" placeholder>
<el-option <el-option v-for="(item) in types" :key="item.bk_type_id" :value="item.bk_type_id"
v-for="(item) in types" :label="item.bk_type_name" />
:key="item.bk_type_id"
:value="item.bk_type_id"
:label="item.bk_type_name"
/>
</el-select> </el-select>
<el-form-item label="备注" :label-width="formLabelWidth"> <el-form-item label="备注" :label-width="formLabelWidth">
<el-input v-model="dialog_form.comment" autocomplete="off" /> <el-input v-model="dialog_form.comment" autocomplete="off" />
@@ -28,7 +24,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive } from 'vue'; import { reactive } from 'vue'
import { update_bookmark, put_bookmark } from '../../api/bookmark' import { update_bookmark, put_bookmark } from '../../api/bookmark'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
const formLabelWidth = '140px' const formLabelWidth = '140px'
@@ -69,15 +65,19 @@ const submit = () => {
</script> </script>
<style scoped>.el-button--text { <style scoped>
.el-button--text {
margin-right: 15px; margin-right: 15px;
} }
.el-select { .el-select {
width: 300px; width: 300px;
} }
.el-input { .el-input {
width: 300px; width: 300px;
} }
.dialog-footer button:first-child { .dialog-footer button:first-child {
margin-right: 10px; margin-right: 10px;
} }
+19 -19
View File
@@ -14,12 +14,12 @@
<div class="tag_list"> <div class="tag_list">
<el-tag class="ml-2 click-icon" :type="item.clicked ? 'danger' : 'info'" v-for="(item) in bookmark_types" <el-tag class="ml-2 click-icon" :type="item.clicked ? 'danger' : 'info'" v-for="(item) in bookmark_types"
@click="handleTags(item)">{{ item.bk_type_name }}</el-tag> @click="handleTags(item)">{{ item.bk_type_name }}</el-tag>
<el-button type="primary" @click="handleAddType" v-show="Tenant.account">增加</el-button> <el-button type="primary" @click="handleAddType" v-show="useTenantStore.tenant.account">增加</el-button>
<el-button type="primary" @click="handleManageType" v-show="Tenant.account">自定义</el-button> <el-button type="primary" @click="handleManageType" v-show="useTenantStore.tenant.account">自定义</el-button>
</div> </div>
</el-form> </el-form>
<div class="create_dialog"> <div class="create_dialog">
<el-button type="primary" @click="handleInsert" v-show="Tenant.account">新建</el-button> <el-button type="primary" @click="handleInsert" v-show="useTenantStore.tenant.account">新建</el-button>
</div> </div>
<el-table ref="multipleTableRef" :data="tableData" @selection-change="handleSelectionChange" style="width: 100%"> <el-table ref="multipleTableRef" :data="tableData" @selection-change="handleSelectionChange" style="width: 100%">
@@ -39,9 +39,10 @@
<CopyDocument /> <CopyDocument />
</el-icon> </el-icon>
<el-button class="click-icon" size="small" @click="handleEdit(scope.$index, scope.row)" <el-button class="click-icon" size="small" @click="handleEdit(scope.$index, scope.row)"
v-show="Tenant.account">编辑 v-show="useTenantStore.tenant.account">编辑
</el-button> </el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)" v-show="Tenant.account"> <el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)"
v-show="useTenantStore.tenant.account">
删除 删除
</el-button> </el-button>
</template> </template>
@@ -72,34 +73,33 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref } from 'vue' import { reactive, ref, h } from 'vue'
import { search_bookmark, delete_bookmark } from '../../api/bookmark' import type { ElTable } from 'element-plus'
import { get_bookmark_type } from '../../api/bookmark_type' import { CopyDocument } from "@element-plus/icons-vue"
import { ElNotification } from 'element-plus'
import copy from 'copy-to-clipboard'
import bookmark_dialog from './dialog.vue' import bookmark_dialog from './dialog.vue'
import bookmarkType_dialog from './Typedialog.vue' import bookmarkType_dialog from './Typedialog.vue'
import createType_dialog from './createTypedialog.vue' import createType_dialog from './createTypedialog.vue'
import delete_dialog from '../../components/operate/deleteDialog.vue' import delete_dialog from '../../components/operate/deleteDialog.vue'
import type { ElTable } from 'element-plus' import { tenantStore } from '../../store/modules/tenant'
import { CopyDocument } from "@element-plus/icons-vue"; import { search_bookmark, delete_bookmark } from '../../api/bookmark'
import { ElNotification } from 'element-plus' import { get_bookmark_type } from '../../api/bookmark_type'
import copy from 'copy-to-clipboard';
import { h } from 'vue'
import { tenantStore } from '../../store/tenant'
// obtain user infomation // obtain user infomation
const Tenant = tenantStore() const useTenantStore = tenantStore()
// 定义书签格式 // 定义书签格式
type Bookmark = { type Bookmark = {
bk_id: string, bk_id: string,
bk_type_id: number, bk_type_id: number,
bk_type_name: String, bk_type_name: string,
comment: String, comment: string,
url: String url: string
} }
// 定义 表单格式 // 定义 表单格式
type Searchform = { type Searchform = {
bk_type_ids: number[], bk_type_ids: number[],
comment: String comment: string
} }
// 操作code // 操作code
+150
View File
@@ -0,0 +1,150 @@
<template>
<el-calendar v-model="value" v-if="isComponentActive" v-loading="isComponentLoading"
element-loading-text="主人别着急我在努力加载中^_^">
<template #dateCell="{ data }" lo>
<div class="date-cell-holiday" v-if="Object.keys(holidays).includes(toLocalDate(data.date))">
<span style="color: red;" @click="onclick(data.date)"> {{ data.date.getDate() }}</span><br>
<span>{{ holidays[toLocalDate(data.date)] }}</span>
</div>
<div class="date-cell" v-else>
<span style="color: red;" v-if="data.date.getDay() == 0 || data.date.getDay() == 6" @click="onclick(data.date)">
{{
data.date.getDate()
}}</span>
<span v-else @click="onclick(data.date)"> {{ data.date.getDate() }}</span>
</div>
<div class="date-cell-event" v-if="Object.keys(eventDataObject).includes(toLocalDate(data.date))">
<span>{{ eventDataObject[toLocalDate(data.date)] }}</span>
</div>
</template>
</el-calendar>
<span>待办事项</span>
<el-table :data="eventDataList">
<el-table-column property="event_date" label="Date" width="150" />
<el-table-column property="title" label="title" width="200" />
<el-table-column property="event_info" label="eventInfo" />
</el-table>
<el-dialog v-model="dialogFormVisible" title="待办事项">
<el-form :model="form">
<el-form-item label="日期" :label-width="formLabelWidth">
<span>{{ form.event_date }}</span>
</el-form-item>
<el-form-item label="title" :label-width="formLabelWidth">
<el-input v-model="form.title" autocomplete="off" />
</el-form-item>
<el-form-item label="eventInfo" :label-width="formLabelWidth">
<el-input v-model="form.event_info" autocomplete="off" type="textarea" />
</el-form-item>
<el-form-item label="alert" :label-width="formLabelWidth">
<el-checkbox-group v-model="checkedalerts" :min="0" :max="2">
<el-checkbox v-for="city in alerts" :key="city" :label="city">{{
city
}}</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">Cancel</el-button>
<el-button type="primary" @click="confirm">
Confirm
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { nextTick, onMounted, reactive, ref } from 'vue'
import { get_holiday } from '@/service/calendar'
import { toLocalDate } from '@/utils/date'
import { get_event, put_event } from "@/api/calendar"
import { Event } from "@/entity/index"
import { settingsStore } from "@/store/modules/settings";
// -- IMPORT --
const useSettingsStore = settingsStore()
// -- REACTIVE OBJECT --
const holidays = reactive<any>({})
const eventDataList = reactive<Event[]>([])
const eventDataObject = reactive<any>({})
const form = reactive({
event_date: '',
title: '',
event_info: '',
})
// -- REF OBJECT --
const value = ref(new Date())
const dialogFormVisible = ref(false)
const formLabelWidth = '140px'
const isComponentActive = ref(false)
const isComponentLoading = ref(true)
const checkedalerts = ref([])
const alerts = ['email', 'messageBox', '手机短信', '微信通知']
// -- EVENT DEFINITION
const onclick = (date: Date) => {
form.event_date = toLocalDate(date)
dialogFormVisible.value = true
}
const confirm = async () => {
await put_event(form).then(() => {
})
dialogFormVisible.value = false
handleCommand('refresh')
}
const handleCommand = (command: string) => {
if (command === 'refresh') {
useSettingsStore.settings.isLoading = true
useSettingsStore.settings.isRouterAlive = false
nextTick(() => {
useSettingsStore.settings.isRouterAlive = true
useSettingsStore.settings.isLoading = false
})
} else if (command === 'closeOther') {
// useSettingsStore.closeOther()
} else {
// useSettingsStore.closeLeftOrRight(command)
}
}
/**
* AUTO INVOKE FUNCTION
*/
onMounted(async () => {
await get_holiday().then((resp) => {
Object.keys(resp.data).forEach(element => {
holidays[element] = resp.data[element]
})
})
await get_event().then((response) => {
eventDataList.length = 0
response.data.forEach((record: Event) => {
eventDataList.push(record)
eventDataObject[record.event_date] = record.title
})
})
isComponentActive.value = true
isComponentLoading.value = false
})
</script>
<style lang="scss" scoped>
.date-cell-holiday {
background: #dfd;
}
.date-cell-event {
background: yellow;
}
</style>
+4 -5
View File
@@ -48,7 +48,7 @@
<el-collapse-item title="手动下单" name="2"> <el-collapse-item title="手动下单" name="2">
<el-select v-model="vt_symbol" placeholder="select a vt_symbol" @change="select_contract"> <el-select v-model="vt_symbol" placeholder="select a vt_symbol" @change="select_contract">
<el-option v-for="symbol in store.subscribe_vt_symbols" :value="symbol" /> <el-option v-for="symbol in useStrategyStore.strategy.subscribe_vt_symbols" :value="symbol" />
</el-select> </el-select>
<div style="height: 10px;"></div> <div style="height: 10px;"></div>
<el-row> <el-row>
@@ -134,14 +134,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage, TabsPaneContext } from 'element-plus' import { ElMessage, TabsPaneContext } from 'element-plus'
import { reactive, ref, h, onMounted, onUnmounted } from 'vue' import { reactive, ref, h, onMounted, onUnmounted } from 'vue'
import { get_accounts, send_order, get_tick } from '../../api/capital' import { get_accounts, send_order, get_tick } from '../../api/capital'
import { strategyStore } from '../../store/strategy' import { strategyStore } from '../../store/modules/strategy'
// -- IMPORT -- // -- IMPORT --
const store = strategyStore() const useStrategyStore = strategyStore()
// -- INTERFACE OR TYPE DEFINITION -- // -- INTERFACE OR TYPE DEFINITION --
interface AccountData { interface AccountData {
@@ -272,7 +271,7 @@ const on_order = () => {
*/ */
(() => { (() => {
store.select_subscribe_vt_symbols() useStrategyStore.select_subscribe_vt_symbols()
onMounted(() => { onMounted(() => {
clearInterval(timer.value) clearInterval(timer.value)
timer.value = window.setInterval(on_query_accounts(), 3000); timer.value = window.setInterval(on_query_accounts(), 3000);
@@ -0,0 +1,126 @@
<template>
<div class="dashboard-line-box">
<div class="dashboard-line-title">
访问趋势
</div>
<div ref="echart" class="dashboard-line" />
</div>
</template>
<script setup lang="ts">
import * as echarts from 'echarts'
import { nextTick, onMounted, onUnmounted, ref, shallowRef } from 'vue'
// import 'echarts/theme/macarons'
var dataAxis: Array<string> = []
for (var i = 1; i < 13; i++) {
dataAxis.push(`${i}`)
}
var data = [
220,
182,
191,
234,
290,
330,
310,
123,
442,
321,
90,
149,
]
var yMax = 500
var dataShadow = []
// eslint-disable-next-line no-redeclare
for (var i = 0; i < data.length; i++) {
dataShadow.push(yMax)
}
const chart = shallowRef()
const echart = ref()
const initChart = () => {
chart.value = echarts.init(echart.value /* 'macarons' */)
setOptions()
}
const setOptions = () => {
chart.value.setOption({
grid: {
left: '40',
right: '20',
top: '40',
bottom: '20',
},
xAxis: {
data: dataAxis,
axisTick: {
show: false,
},
axisLine: {
show: false,
},
z: 10,
},
yAxis: {
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
color: '#999',
},
},
dataZoom: [
{
type: 'inside',
},
],
series: [
{
type: 'bar',
barWidth: '40%',
itemStyle: {
borderRadius: [5, 5, 0, 0],
color: '#188df0',
},
emphasis: {
itemStyle: {
color: '#188df0',
},
},
data: data,
},
],
})
}
onMounted(async () => {
await nextTick()
initChart()
})
onUnmounted(() => {
if (!chart.value) {
return
}
chart.value.dispose()
chart.value = null
})
</script>
<style lang="scss" scoped>
.dashboard-line-box {
.dashboard-line {
background-color: #fff;
height: 360px;
width: 100%;
}
.dashboard-line-title {
font-weight: 600;
margin-bottom: 12px;
}
}
</style>
+122
View File
@@ -0,0 +1,122 @@
<template>
<div class="commit-table">
<div class="commit-table-title">
更新日志
</div>
<div class="log">
<div v-for="(item, key) in dataTimeline" :key="key" class="log-item">
<div class="flex-1 flex key-box">
<span class="key" :class="key < 3 && 'top'">{{ key+ 1 }}</span>
</div>
<div class="flex-5 flex message">{{ item.message }}</div>
<div class="flex-3 flex form">{{ item.from }}</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { Commits } from '@/service/github'
import { formatTimeToStr } from '@/utils/date'
import { DataTimeline } from "@/entity/index";
const loading = ref(true)
const dataTimeline = reactive<DataTimeline[]>([])
const loadCommits = () => {
Commits(0).then(({ data }) => {
loading.value = false
data.forEach((element: any, index: any) => {
if (element.commit.message && index < 10) {
dataTimeline.push({
from: formatTimeToStr(element.commit.author.date, 'yyyy-MM-dd'),
title: element.commit.author.name,
showDayAndMonth: true,
message: element.commit.message,
})
}
})
})
}
loadCommits()
</script>
<style lang="scss" scoped>
.commit-table {
background-color: #fff;
height: 400px;
&-title {
font-weight: 600;
margin-bottom: 12px;
}
.log {
&-item {
display: flex;
justify-content: space-between;
margin-top: 14px;
.key-box {
justify-content: center;
}
.key {
&.top {
background: #314659;
color: #FFFFFF;
;
}
display: inline-flex;
justify-content: center;
align-items: center;
width: 20px;
height: 20px;
border-radius: 50%;
background: #F0F2F5;
text-align: center;
color:rgba($color: #000000, $alpha: 0.65)
}
.message {
color: rgba(0, 0, 0, 0.65);
}
.form {
color: rgba(0, 0, 0, 0.65);
margin-left: 12px;
}
.flex {
line-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.flex-1 {
flex: 1;
}
.flex-2 {
flex: 2;
}
.flex-3 {
flex: 3;
}
.flex-4 {
flex: 4;
}
.flex-5 {
flex: 5;
}
}
}
}
</style>
+382 -5
View File
@@ -1,15 +1,392 @@
<template> <template>
<div class="about" ref="bookmarks" id="bookmarks" :style="{ <div class="page">
width: '600px', <div class="gva-card-box">
height: '400px' <div class="gva-card gva-top-card">
}"></div> <div class="gva-top-card-left">
<div class="gva-top-card-left-title">我的主人我们又见面了我有什么能帮助你么^_^</div>
<div class="gva-top-card-left-dot">{{ weatherInfo }}</div>
<div class="gva-top-card-left-rows">
<el-row>
<el-col :span="8" :xs="24" :sm="8">
<div class="flex-center">
<el-icon class="dashboard-icon">
<sort />
</el-icon>
wiki数量 ({{ statistics.wiki_count }})
</div>
</el-col>
<el-col :span="8" :xs="24" :sm="8">
<div class="flex-center">
<el-icon class="dashboard-icon">
<avatar />
</el-icon>
bookmark数量 ({{ statistics.bookmark_count }})
</div>
</el-col>
<!-- <el-col :span="8" :xs="24" :sm="8">
<div class="flex-center">
<el-icon class="dashboard-icon">
<comment />
</el-icon>
好评率 (99%)
</div>
</el-col> -->
</el-row>
</div>
<div class="gva-top-card-left-item">
<div>
{{ time }}
</div>
</div>
<div class="gva-top-card-left-item">
<div>
期货交易时间&nbsp
<el-icon color="yellow">
<Sunrise />
</el-icon>&nbsp 9:00-11:30 &nbsp
<el-icon color="gold">
<Sunny />
</el-icon>&nbsp 13:30-15:00 &nbsp
<el-icon color="blue">
<Moon />
</el-icon>&nbsp 21:00-23:00
</div>
</div>
</div>
<img src="../../assets/dashboard.png" class="gva-top-card-right">
</div>
</div>
<div class="gva-card-box">
<el-card class="gva-card quick-entrance">
<template #header>
<div class="card-header">
<span>快捷入口</span>
</div>
</template>
<el-row :gutter="20">
<el-col v-for="(card, key) in toolCards" :key="key" :span="4" :xs="8" class="quick-entrance-items"
@click="toTarget(card.name)">
<div class="quick-entrance-item">
<div class="quick-entrance-item-icon" :style="{ backgroundColor: card.bg }">
<el-icon>
<component :is="card.icon" :style="{ color: card.color }" />
</el-icon>
</div>
<p>{{ card.label }}</p>
</div>
</el-col>
</el-row>
</el-card>
<!-- <div class="quick-entrance-title"></div> -->
</div>
<div class="gva-card-box">
<div class="gva-card">
<div class="card-header">
<span>数据统计</span>
</div>
<div class="echart-box">
<el-row :gutter="20">
<el-col :xs="24" :sm="18">
<echarts-line />
</el-col>
<el-col :xs="24" :sm="6">
<dashboard-table />
</el-col>
</el-row>
</div>
</div>
</div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'
import $moment from "moment"
import EchartsLine from '@/view/dashboard/dashboardCharts/index.vue'
import DashboardTable from '@/view/dashboard/dashboardTable/index.vue'
import { useRouter } from 'vue-router'
import { useWeatherInfo } from '@/service/weather'
import { countBlogs } from "@/api/blog"
import { bookmarks_count } from "@/api/bookmark"
// -- IMPORT --
const weatherInfo = useWeatherInfo()
const router = useRouter()
// -- REACTIVE OBJECT --
// -- REF OBJECT --
let time = ref<String>($moment().format("YYYY年MM月DD日 HH:mm:ss"));
const statistics = ref({
wiki_count: 0,
bookmark_count: 0,
})
const toolCards = ref([
{
label: 'Jenkins',
icon: 'camera',
name: 'Jenkins',
color: '#ff9c6e',
bg: 'rgba(255, 156, 110,.3)'
},
{
label: 'argo-cd',
icon: 'monitor',
name: 'argocd',
color: '#69c0ff',
bg: 'rgba(92, 219, 211,.3)'
},
{
label: 'wiki',
icon: 'document',
name: 'wiki',
color: '#b37feb',
bg: 'rgba(185, 192, 255,.3)'
},
{
label: '代码生成器',
icon: 'cpu',
name: 'autoCode',
color: '#ffd666',
bg: 'rgba(255, 214, 102,.3)'
},
{
label: '表单生成器',
icon: 'document-checked',
name: 'formCreate',
color: '#ff85c0',
bg: 'rgba(255, 133, 192,.3)'
},
{
label: '关于我们',
icon: 'user',
name: 'about',
color: '#5cdbd3',
bg: 'rgba(92, 219, 211,.3)'
}
])
// -- EVENT DEFINITION
const toTarget = (name: string) => {
// router.push({ name })
switch (name) {
case 'Jenkins':
window.open('https://www.violin-home.cn/jenkins/', '_blank')
break;
case 'argocd':
window.open('https://www.violin-home.cn/argo-cd/', '_blank')
break;
case 'wiki':
window.open('https://www.violin-home.cn/docs#/guan/', '_blank')
break;
}
}
/**
* AUTO INVOKE FUNCTION
*/
setInterval(() => {
time.value = $moment().format("YYYY年MM月DD日 HH:mm:ss")
}, 1000)
countBlogs().then((resp) => {
const data = resp.data
statistics.value.wiki_count = data.count
})
bookmarks_count().then((resp) => {
const data = resp.data
statistics.value.bookmark_count = data.count
})
</script> </script>
<style> <style lang="scss" scoped>
@mixin flex-center {
display: flex;
align-items: center;
}
.page {
background: #f0f2f5;
padding: 0;
.gva-card-box {
padding: 12px 16px;
&+.gva-card-box {
padding-top: 0px;
}
}
.gva-card {
box-sizing: border-box;
background-color: #fff;
border-radius: 2px;
height: auto;
padding: 26px 30px;
overflow: hidden;
box-shadow: 0 0 7px 1px rgba(0, 0, 0, 0.03);
}
.gva-top-card {
height: 260px;
@include flex-center;
justify-content: space-between;
color: #777;
&-left {
height: 100%;
display: flex;
flex-direction: column;
&-title {
font-size: 22px;
color: #343844;
}
&-dot {
font-size: 16px;
color: #6B7687;
margin-top: 24px;
}
&-rows {
// margin-top: 15px;
margin-top: 18px;
color: #6B7687;
width: 600px;
align-items: center;
}
&-item {
+.gva-top-card-left-item {
margin-top: 24px;
}
margin-top: 14px;
}
}
&-right {
height: 600px;
width: 600px;
margin-top: 28px;
}
}
::v-deep(.el-card__header) {
padding: 0;
border-bottom: none;
}
.card-header {
padding-bottom: 20px;
border-bottom: 1px solid #e8e8e8;
}
.quick-entrance-title {
height: 30px;
font-size: 22px;
color: #333;
width: 100%;
border-bottom: 1px solid #eee;
}
.quick-entrance-items {
@include flex-center;
justify-content: center;
text-align: center;
color: #333;
.quick-entrance-item {
padding: 16px 28px;
margin-top: -16px;
margin-bottom: -16px;
border-radius: 4px;
transition: all 0.2s;
&:hover {
box-shadow: 0px 0px 7px 0px rgba(217, 217, 217, 0.55);
}
cursor: pointer;
height: auto;
text-align: center;
// align-items: center;
&-icon {
width: 50px;
height: 50px !important;
border-radius: 8px;
@include flex-center;
justify-content: center;
margin: 0 auto;
i {
font-size: 24px;
}
}
p {
margin-top: 10px;
}
}
}
.echart-box {
padding: 14px;
}
}
.dashboard-icon {
font-size: 20px;
color: rgb(85, 160, 248);
width: 30px;
height: 30px;
margin-right: 10px;
@include flex-center;
}
.flex-center {
@include flex-center;
}
//小屏幕不显示右侧,将登录框居中
@media (max-width: 750px) {
.gva-card {
padding: 20px 10px !important;
.gva-top-card {
height: auto;
&-left {
&-title {
font-size: 20px !important;
}
&-rows {
margin-top: 15px;
align-items: center;
}
}
&-right {
display: none;
}
}
.gva-middle-card {
&-item {
line-height: 20px;
}
}
.dashboard-icon {
font-size: 18px;
}
}
}
</style> </style>
+11 -10
View File
@@ -5,14 +5,14 @@
<el-row> <el-row>
<el-col :span="4"> <el-col :span="4">
<el-select v-model="exchange" placeholder="select a exchange"> <el-select v-model="exchange" placeholder="select a exchange">
<el-option v-for="ex in store.exchanges" :value="ex" /> <el-option v-for="ex in useStrategyStore.strategy.exchanges" :value="ex" />
</el-select> </el-select>
</el-col> </el-col>
<el-col :span="1"> <el-col :span="1">
</el-col> </el-col>
<el-col :span="4"> <el-col :span="4">
<el-select v-model="vt_symbol" placeholder="select a vt_symbol"> <el-select v-model="vt_symbol" placeholder="select a vt_symbol">
<el-option v-for="symbol in store.vt_symbols.get(exchange)" :value="symbol" /> <el-option v-for="symbol in useStrategyStore.strategy.vt_symbols.get(exchange)" :value="symbol" />
</el-select> </el-select>
</el-col> </el-col>
<el-col :span="1"> <el-col :span="1">
@@ -56,15 +56,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, h, onMounted, onUnmounted, reactive, getCurrentInstance } from 'vue' import { ref, h, onMounted, onUnmounted, reactive, getCurrentInstance } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { get_ticks, subscribe, concel_subscribe } from '../../api/capital' import * as echarts from 'echarts'
import { strategyStore } from '../../store/strategy'
import { Contextmenu, ContextmenuItem } from "v-contextmenu" import { Contextmenu, ContextmenuItem } from "v-contextmenu"
import { get_ticks, subscribe, concel_subscribe } from '../../api/capital'
import { strategyStore } from '../../store/modules/strategy'
import { toPercent } from '../../utils/number' import { toPercent } from '../../utils/number'
import * as echarts from 'echarts'
// -- IMPORT -- // -- IMPORT --
const currentInstance = getCurrentInstance() const currentInstance = getCurrentInstance()
const store = strategyStore() const useStrategyStore = strategyStore()
// -- INTERFACE OR TYPE DEFINITION -- // -- INTERFACE OR TYPE DEFINITION --
interface TickData { interface TickData {
@@ -104,7 +105,7 @@ const on_subscribe = () => {
h('i', { style: 'color: green' }, "行情订阅成功"), h('i', { style: 'color: green' }, "行情订阅成功"),
]), ]),
}) })
store.select_subscribe_vt_symbols() useStrategyStore.select_subscribe_vt_symbols()
on_ticks() on_ticks()
}).catch(error => { }).catch(error => {
ElMessage({ ElMessage({
@@ -189,7 +190,7 @@ const on_ticks = () => {
window.clearInterval(timer.value) window.clearInterval(timer.value)
}) })
store.select_subscribe_vt_symbols() useStrategyStore.select_subscribe_vt_symbols()
.catch(error => { .catch(error => {
ElMessage({ ElMessage({
message: h('p', null, [ message: h('p', null, [
@@ -198,7 +199,7 @@ const on_ticks = () => {
}) })
}) })
store.select_vt_symbols() useStrategyStore.select_vt_symbols()
.catch((error) => { .catch((error) => {
ElMessage({ ElMessage({
message: h('p', null, [ message: h('p', null, [
@@ -207,7 +208,7 @@ const on_ticks = () => {
}) })
}) })
store.select_exchanges() useStrategyStore.select_exchanges()
.catch((error) => { .catch((error) => {
ElMessage({ ElMessage({
message: h('p', null, [ message: h('p', null, [
+26
View File
@@ -0,0 +1,26 @@
<template>
<el-button text @click="open">Click to open the Message Box</el-button>
</template>
<script setup lang="ts">
import { ElMessage, ElMessageBox } from 'element-plus'
import type { Action } from 'element-plus'
const open = () => {
ElMessageBox.alert('This is a message', 'Title', {
// if you want to disable its autofocus
// autofocus: false,
confirmButtonText: 'OK',
callback: (action: Action) => {
ElMessage({
type: 'info',
message: `action: ${action}`,
})
},
})
}
</script>
<style>
</style>
-12
View File
@@ -1,12 +0,0 @@
<template>
<div class="dashboard">感悟这个东西暂时没有什么太多想法以后会被删掉把</div>
</template>
<script setup lang="ts">
</script>
<style>
.dashboard {
background-color: beige;
}
</style>
+5 -6
View File
@@ -61,8 +61,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref } from 'vue' import { onMounted, reactive, ref, h } from 'vue'
import { ElMessage } from 'element-plus'
import create_strategy_dialog from './dialog.vue' import create_strategy_dialog from './dialog.vue'
import { strategyStore } from '../../store/modules/strategy'
import { import {
get_strategies, get_strategies,
init_strategy, init_strategy,
@@ -72,9 +74,6 @@ import {
get_strategy_status, get_strategy_status,
get_strategy_load_files, get_strategy_load_files,
} from '../../api/cta_strategy' } from '../../api/cta_strategy'
import { h } from 'vue'
import { ElMessage } from 'element-plus'
import { strategyStore } from '../../store/strategy'
type Strategy = { type Strategy = {
strategy_name: string, strategy_name: string,
@@ -84,7 +83,7 @@ type Strategy = {
status: number, status: number,
} }
const store = strategyStore() const useStrategyStore = strategyStore()
let createStrategyDialogVisible = ref(false) let createStrategyDialogVisible = ref(false)
const strategy_list = reactive<Strategy[]>([]) const strategy_list = reactive<Strategy[]>([])
const dialogRef = ref() const dialogRef = ref()
@@ -133,7 +132,7 @@ const on_status = (row: any) => {
onMounted(() => { onMounted(() => {
on_list() on_list()
get_strategy_load_files().then(res => { get_strategy_load_files().then(res => {
store.$patch({ class_names: res.data.class_names }) useStrategyStore.strategy.class_names = res.data.class_names
}).catch(error => { }).catch(error => {
ElMessage({ ElMessage({
message: h('p', null, [ message: h('p', null, [
+2 -5
View File
@@ -66,17 +66,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref } from 'vue'; import { reactive, ref, h } from 'vue'
import { UploadInstance, UploadProps, UploadRawFile, genFileId, ElMessage, ElButton, ElIcon, ElTable, ElTableColumn, ElUpload } from 'element-plus'
import { import {
get_strategy_files, get_strategy_files,
load_strategy_file, load_strategy_file,
unload_strategy_file, unload_strategy_file,
remove_strategy_file remove_strategy_file
} from '../../api/cta_strategy' } from '../../api/cta_strategy'
import { genFileId } from 'element-plus'
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus'
import { h } from 'vue'
import { ElMessage } from 'element-plus'
// -- INTERFACE OR TYPE DEFINITION -- // -- INTERFACE OR TYPE DEFINITION --
interface StrategyFile { interface StrategyFile {
+4 -4
View File
@@ -7,12 +7,12 @@
</el-form-item> </el-form-item>
<el-form-item label="vt_symbol" prop="vt_symbol"> <el-form-item label="vt_symbol" prop="vt_symbol">
<el-select v-model="ruleForm.vt_symbol" placeholder="select a vt_symbol"> <el-select v-model="ruleForm.vt_symbol" placeholder="select a vt_symbol">
<el-option v-for="vt_symbol in store.subscribe_vt_symbols" :value="vt_symbol" /> <el-option v-for="vt_symbol in useStrategyStore.strategy.subscribe_vt_symbols" :value="vt_symbol" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="class_name" prop="class_name"> <el-form-item label="class_name" prop="class_name">
<el-select v-model="ruleForm.class_name" placeholder="select a class_name"> <el-select v-model="ruleForm.class_name" placeholder="select a class_name">
<el-option v-for="name in store.class_names" :value="name" /> <el-option v-for="name in useStrategyStore.strategy.class_names" :value="name" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="setting:"> <el-form-item label="setting:">
@@ -47,9 +47,9 @@ import type { FormInstance, FormRules } from 'element-plus'
import { create_strategy } from '../../api/cta_strategy' import { create_strategy } from '../../api/cta_strategy'
import { h } from 'vue' import { h } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { strategyStore } from '../../store/strategy' import { strategyStore } from '../../store/modules/strategy'
const store = strategyStore() const useStrategyStore = strategyStore()
const formSize = ref('default') const formSize = ref('default')
const ruleFormRef = ref<FormInstance>() const ruleFormRef = ref<FormInstance>()
+2 -3
View File
@@ -6,10 +6,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive } from 'vue'; import { onMounted, ref, reactive } from 'vue'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { EChartsOption } from 'echarts'; import { EChartsOption } from 'echarts'
import { onMounted, ref } from 'vue'
import { generateOHLC, option } from '../../service/trader' import { generateOHLC, option } from '../../service/trader'
import { get_stock_data } from '../../api/trader' import { get_stock_data } from '../../api/trader'
+6
View File
@@ -1,5 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [
"./src/*"
]
},
"target": "esnext", "target": "esnext",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"module": "esnext", "module": "esnext",
+21 -2
View File
@@ -1,7 +1,8 @@
import { defineConfig, loadEnv } from 'vite' import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import viteCompression from 'vite-plugin-compression'; import viteCompression from 'vite-plugin-compression'
import { viteMockServe } from 'vite-plugin-mock' import { viteMockServe } from 'vite-plugin-mock'
import { resolve } from 'path'
// https://vitejs.cn/guide/env-and-mode.html#env-files // https://vitejs.cn/guide/env-and-mode.html#env-files
export default defineConfig(({ mode }) => { export default defineConfig(({ mode }) => {
@@ -16,6 +17,24 @@ export default defineConfig(({ mode }) => {
localEnabled: true, localEnabled: true,
}), }),
], ],
base: env.VITE_RES_URL pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: []
}
},
base: env.VITE_RES_URL,
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/style/mixin.scss";`
}
}
}
} }
}) })