Browse Source

增加UI界面

tags/v1.0.0
SmallChi 6 years ago
parent
commit
9bef6ed81d
100 changed files with 3218 additions and 0 deletions
  1. +1
    -0
      .gitignore
  2. +5
    -0
      src/JT808.DotNetty.Dashbord.UI/.babelrc
  3. +9
    -0
      src/JT808.DotNetty.Dashbord.UI/.editorconfig
  4. +0
    -0
      src/JT808.DotNetty.Dashbord.UI/.eslintignore
  5. +19
    -0
      src/JT808.DotNetty.Dashbord.UI/.eslintrc.js
  6. +26
    -0
      src/JT808.DotNetty.Dashbord.UI/.gitignore
  7. +5
    -0
      src/JT808.DotNetty.Dashbord.UI/.postcssrc.js
  8. +5
    -0
      src/JT808.DotNetty.Dashbord.UI/.travis.yml
  9. +21
    -0
      src/JT808.DotNetty.Dashbord.UI/LICENSE
  10. +187
    -0
      src/JT808.DotNetty.Dashbord.UI/README.md
  11. BIN
      src/JT808.DotNetty.Dashbord.UI/public/favicon.ico
  12. +17
    -0
      src/JT808.DotNetty.Dashbord.UI/public/index.html
  13. +27
    -0
      src/JT808.DotNetty.Dashbord.UI/src/App.vue
  14. +8
    -0
      src/JT808.DotNetty.Dashbord.UI/src/api/atomicCounter.js
  15. +37
    -0
      src/JT808.DotNetty.Dashbord.UI/src/api/data.js
  16. +11
    -0
      src/JT808.DotNetty.Dashbord.UI/src/api/routers.js
  17. +22
    -0
      src/JT808.DotNetty.Dashbord.UI/src/api/session.js
  18. +32
    -0
      src/JT808.DotNetty.Dashbord.UI/src/api/sourcePackage.js
  19. +32
    -0
      src/JT808.DotNetty.Dashbord.UI/src/api/transmit.js
  20. +8
    -0
      src/JT808.DotNetty.Dashbord.UI/src/api/unificationSend.js
  21. +84
    -0
      src/JT808.DotNetty.Dashbord.UI/src/api/user.js
  22. +37
    -0
      src/JT808.DotNetty.Dashbord.UI/src/assets/icons/iconfont.css
  23. BIN
      src/JT808.DotNetty.Dashbord.UI/src/assets/icons/iconfont.eot
  24. +56
    -0
      src/JT808.DotNetty.Dashbord.UI/src/assets/icons/iconfont.svg
  25. BIN
      src/JT808.DotNetty.Dashbord.UI/src/assets/icons/iconfont.ttf
  26. BIN
      src/JT808.DotNetty.Dashbord.UI/src/assets/icons/iconfont.woff
  27. +1
    -0
      src/JT808.DotNetty.Dashbord.UI/src/assets/images/error-page/error-401.svg
  28. +1
    -0
      src/JT808.DotNetty.Dashbord.UI/src/assets/images/error-page/error-404.svg
  29. +1
    -0
      src/JT808.DotNetty.Dashbord.UI/src/assets/images/error-page/error-500.svg
  30. BIN
      src/JT808.DotNetty.Dashbord.UI/src/assets/images/login-bg.jpg
  31. BIN
      src/JT808.DotNetty.Dashbord.UI/src/assets/images/logo-min.jpg
  32. BIN
      src/JT808.DotNetty.Dashbord.UI/src/assets/images/logo.jpg
  33. BIN
      src/JT808.DotNetty.Dashbord.UI/src/assets/images/qq-group1.jpg
  34. BIN
      src/JT808.DotNetty.Dashbord.UI/src/assets/images/qq-group2.jpg
  35. BIN
      src/JT808.DotNetty.Dashbord.UI/src/assets/images/talkingdata.png
  36. +58
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/charts/bar.vue
  37. +3
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/charts/index.js
  38. +70
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/charts/pie.vue
  39. +42
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/common-icon/common-icon.vue
  40. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/common-icon/index.js
  41. +8
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/common/common.less
  42. +3
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/common/util.js
  43. +174
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/count-to/count-to.vue
  44. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/count-to/index.js
  45. +10
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/count-to/index.less
  46. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/cropper/index.js
  47. +35
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/cropper/index.less
  48. +139
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/cropper/index.vue
  49. +92
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/drag-list/drag-list.vue
  50. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/drag-list/index.js
  51. +75
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/editor/editor.vue
  52. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/editor/index.js
  53. +35
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/icons/icons.vue
  54. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/icons/index.js
  55. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/info-card/index.js
  56. +94
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/info-card/infor-card.vue
  57. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/login-form/index.js
  58. +72
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/login-form/login-form.vue
  59. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/a-back-top/index.js
  60. +90
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/a-back-top/index.vue
  61. +49
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/error-store/error-store.vue
  62. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/error-store/index.js
  63. +84
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/fullscreen/fullscreen.vue
  64. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/fullscreen/index.js
  65. +4
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/custom-bread-crumb/custom-bread-crumb.less
  66. +46
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/custom-bread-crumb/custom-bread-crumb.vue
  67. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/custom-bread-crumb/index.js
  68. +14
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/header-bar.less
  69. +34
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/header-bar.vue
  70. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/index.js
  71. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/sider-trigger/index.js
  72. +21
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/sider-trigger/sider-trigger.less
  73. +27
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/sider-trigger/sider-trigger.vue
  74. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/language/index.js
  75. +51
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/language/language.vue
  76. +51
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/collapsed-menu.vue
  77. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/index.js
  78. +21
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/item-mixin.js
  79. +18
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/mixin.js
  80. +26
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/side-menu-item.vue
  81. +40
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/side-menu.less
  82. +114
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/side-menu.vue
  83. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/tags-nav/index.js
  84. +87
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/tags-nav/tags-nav.less
  85. +209
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/tags-nav/tags-nav.vue
  86. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/user/index.js
  87. +12
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/user/user.less
  88. +59
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/components/user/user.vue
  89. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/index.js
  90. +75
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/main.less
  91. +183
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/main/main.vue
  92. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/markdown/index.js
  93. +76
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/markdown/markdown.vue
  94. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/parent-view/index.js
  95. +20
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/parent-view/parent-view.vue
  96. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/paste-editor/index.js
  97. +26
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/paste-editor/paste-editor.less
  98. +115
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/paste-editor/paste-editor.vue
  99. +58
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/paste-editor/plugins/placeholder.js
  100. +2
    -0
      src/JT808.DotNetty.Dashbord.UI/src/components/split-pane/index.js

+ 1
- 0
.gitignore View File

@@ -328,3 +328,4 @@ ASALocalRun/


# MFractors (Xamarin productivity tool) working folder # MFractors (Xamarin productivity tool) working folder
.mfractor/ .mfractor/
*.json

+ 5
- 0
src/JT808.DotNetty.Dashbord.UI/.babelrc View File

@@ -0,0 +1,5 @@
{
"presets": [
"@vue/app"
]
}

+ 9
- 0
src/JT808.DotNetty.Dashbord.UI/.editorconfig View File

@@ -0,0 +1,9 @@
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

+ 0
- 0
src/JT808.DotNetty.Dashbord.UI/.eslintignore View File


+ 19
- 0
src/JT808.DotNetty.Dashbord.UI/.eslintrc.js View File

@@ -0,0 +1,19 @@
module.exports = {
root: true,
'extends': [
'plugin:vue/essential',
'@vue/standard'
],
rules: {
// allow async-await
'generator-star-spacing': 'off',
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }],
'no-undef': 'off',
'camelcase': 'off'
},
parserOptions: {
parser: 'babel-eslint'
}
}

+ 26
- 0
src/JT808.DotNetty.Dashbord.UI/.gitignore View File

@@ -0,0 +1,26 @@
.DS_Store
node_modules
/dist

/tests/e2e/videos/
/tests/e2e/screenshots/

# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

build/env.js

+ 5
- 0
src/JT808.DotNetty.Dashbord.UI/.postcssrc.js View File

@@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}

+ 5
- 0
src/JT808.DotNetty.Dashbord.UI/.travis.yml View File

@@ -0,0 +1,5 @@
language: node_js
node_js: stable
script: npm run lint
notifications:
email: false

+ 21
- 0
src/JT808.DotNetty.Dashbord.UI/LICENSE View File

@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2017 iView

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 187
- 0
src/JT808.DotNetty.Dashbord.UI/README.md View File

@@ -0,0 +1,187 @@
<p align="center">
<a href="https://www.iviewui.com">
<img width="200" src="https://file.iviewui.com/logo-new.svg">
</a>
</p>

# iView Admin

[![](https://img.shields.io/github/release/iview/iview-admin.svg)](https://github.com/iview/iview-admin/releases)
[![](https://img.shields.io/travis/iview/iview-admin.svg?style=flat-square)](https://travis-ci.org/iview/iview-admin)
[![vue](https://img.shields.io/badge/vue-2.5.10-brightgreen.svg?style=flat-square)](https://github.com/vuejs/vue)
[![iview ui](https://img.shields.io/badge/iview-3.0.0-brightgreen.svg?style=flat-square)](https://github.com/iview/iview)
[![npm](https://img.shields.io/npm/l/express.svg)]()

[更新日志](https://github.com/iview/iview-admin/releases)

[使用文档](https://lison16.github.io/iview-admin-doc/#/)

[在线访问](https://admin.iviewui.com/)

[简化版模板](https://github.com/iview/iview-admin/tree/template)

[教学视频(26课时)](https://segmentfault.com/ls/1650000016221751?utm_source=banner)

`注:在线版本会在开发版本新小版本发布后更新到相应版本,所以如果想体验最新版本iview-admin,请clone完整项目代码到本地运行。`

## Install
```bush
// install dependencies
npm install
```
## Run
### Development
```bush
npm run dev
```
### Production(Build)
```bush
npm run build
```

## 简介
&emsp;&emsp;iView admin是基于Vue.js,搭配使用[iView](https://www.iviewui.com) UI组件库形成的一套后台集成解决方案,由TalkingData前端可视化团队部分成员开发维护。iView admin遵守iView设计和开发约定,风格统一,设计考究,并且更多功能在不停开发中。
如果您想查看iview-admin的更新动态,您可以到[更新日志](https://github.com/iview/iview-admin/releases)查看了解最新更新;如果您是新手,想快速入手iview-admin,您可以到[使用教程](https://github.com/iview/iview-admin/wiki)查看讲解;如果您想在线体验iview-admin,您可以到[在线访问](https://admin.iviewui.com/)体验。如果你只是想要一个清醒爽朗的界面,那你可以下载[简化版模板](https://github.com/iview/iview-admin/tree/template)来做开发。

## 功能

- 登录/登出
- 权限管理
- 列表过滤
- 权限切换
- 多语言切换
- 组件
- 富文本编辑器
- Markdown编辑器
- 城市级联
- 图片预览编辑
- 可拖拽列表
- 文件上传
- 数字渐变
- split-pane
- 表单编辑
- 文章发布
- 工作流
- 表格
- 可拖拽排序
- 可编辑表格
- 行内编辑
- 单元格编辑
- 可搜索表格
- 表格导出数据
- 导出为Csv文件
- 导出为Xls文件
- 表格转图片
- 错误页面
- 403页面
- 404页面
- 500页面
- 高级路由
- 动态路由
- 带参页面
- 换肤
- 收缩侧边栏
- tag标签导航
- 面包屑导航
- 全屏/退出全屏
- 锁屏
- 消息中心
- 个人中心

## 文件结构
```shell
.
├── build 项目构建配置
├── config 开发相关配置
├── public 打包所需静态资源
└── src
├── api AJAX请求
└── assets 项目静态资源
├── icons 自定义图标资源
└── images 图片资源
├── components 业务组件
├── config 项目运行配置
├── directive 自定义指令
├── libs 封装工具函数
├── locale 多语言文件
├── mock mock模拟数据
├── router 路由配置
├── store Vuex配置
├── view 页面文件
└── tests 测试相关
```

## Links

- [TalkingData](https://github.com/TalkingData)
- [iView](https://github.com/iview/iview)
- [Vue](https://github.com/vuejs/vue)
- [Webpack](https://github.com/webpack/webpack)

## 效果展示

- 响应式布局首页
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/home.gif)

- 标签导航
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/page-tags.gif)

- 权限管理
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/access.gif)

- 可拖拽列表
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/dragable-list.gif)

- 图片预览编辑
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/image-editor.gif)

- 文件上传
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/upload.gif)

- 数字渐变
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/count-to.gif)

- split-pane
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/split-pane.gif)

- 文章发布
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/article-publish.gif)

- 工作流
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/workflow.gif)

- 可拖拽表格
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/dragable-table.gif)

- 可编辑表格
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/editable-table.gif)

- 表格导出数据
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/exportable-table.gif)

- 表格转图片
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/table2image.gif)

- 错误页面
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/error-page.gif)

- 锁屏
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/locking.gif)

- 可收缩侧边栏
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/sidebarmenu.gif)

- 主题切换
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/theme.gif)

- 消息中心
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/message.gif)

### 💖💖 If you find this project helpful, maybe you can buy me a coffee. 💖💖
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/code.png)


## License
[MIT](http://opensource.org/licenses/MIT)

Copyright (c) 2016-present, iView

BIN
src/JT808.DotNetty.Dashbord.UI/public/favicon.ico View File

Before After

+ 17
- 0
src/JT808.DotNetty.Dashbord.UI/public/index.html View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title></title>
</head>
<body>
<noscript>
<strong>We're sorry but iview-admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

+ 27
- 0
src/JT808.DotNetty.Dashbord.UI/src/App.vue View File

@@ -0,0 +1,27 @@
<template>
<div id="app">
<router-view/>
</div>
</template>

<script>
export default {
name: 'App'
}
</script>

<style lang="less">
.size{
width: 100%;
height: 100%;
}
html,body{
.size;
overflow: hidden;
margin: 0;
padding: 0;
}
#app {
.size;
}
</style>

+ 8
- 0
src/JT808.DotNetty.Dashbord.UI/src/api/atomicCounter.js View File

@@ -0,0 +1,8 @@
import axios from '@/libs/api.request'

export const GetAtomicCounter = () => {
return axios.request({
url: 'AtomicCounter/GetAtomicCounter',
method: 'get'
})
}

+ 37
- 0
src/JT808.DotNetty.Dashbord.UI/src/api/data.js View File

@@ -0,0 +1,37 @@
import axios from '@/libs/api.request'

export const getTableData = () => {
return axios.request({
url: 'get_table_data',
method: 'get'
})
}

export const getDragList = () => {
return axios.request({
url: 'get_drag_list',
method: 'get'
})
}

export const errorReq = () => {
return axios.request({
url: 'error_url',
method: 'post'
})
}

export const saveErrorLogger = info => {
return axios.request({
url: 'save_error_logger',
data: info,
method: 'post'
})
}

export const uploadImg = formData => {
return axios.request({
url: 'image/upload',
data: formData
})
}

+ 11
- 0
src/JT808.DotNetty.Dashbord.UI/src/api/routers.js View File

@@ -0,0 +1,11 @@
import axios from '@/libs/api.request'

export const getRouterReq = (access) => {
return axios.request({
url: 'get_router',
params: {
access
},
method: 'get'
})
}

+ 22
- 0
src/JT808.DotNetty.Dashbord.UI/src/api/session.js View File

@@ -0,0 +1,22 @@
import axios from '@/libs/api.request'

export const RemoveByTerminalPhoneNo = (terminalPhoneN) => {
return axios.request({
url: 'Session/RemoveByTerminalPhoneNo/' + terminalPhoneN,
method: 'get'
})
}

export const RemoveByChannelId = (channelId) => {
return axios.request({
url: 'Session/RemoveByChannelId/' + channelId,
method: 'get'
})
}

export const GetAll = () => {
return axios.request({
url: 'Session/GetAll',
method: 'get'
})
}

+ 32
- 0
src/JT808.DotNetty.Dashbord.UI/src/api/sourcePackage.js View File

@@ -0,0 +1,32 @@
import axios from '@/libs/api.request'

export const Add = ({ Host, Port }) => {
const data = {
Host,
Port
}
return axios.request({
url: 'SourcePackage/Add',
data,
method: 'post'
})
}

export const Remove = ({ Host, Port }) => {
const data = {
Host,
Port
}
return axios.request({
url: 'SourcePackage/Remove',
data,
method: 'post'
})
}

export const GetAll = () => {
return axios.request({
url: 'SourcePackage/GetAll',
method: 'get'
})
}

+ 32
- 0
src/JT808.DotNetty.Dashbord.UI/src/api/transmit.js View File

@@ -0,0 +1,32 @@
import axios from '@/libs/api.request'

export const Add = ({ Host, Port }) => {
const data = {
Host,
Port
}
return axios.request({
url: 'Transmit/Add',
data,
method: 'post'
})
}

export const Remove = ({ Host, Port }) => {
const data = {
Host,
Port
}
return axios.request({
url: 'Transmit/Remove',
data,
method: 'post'
})
}

export const GetAll = () => {
return axios.request({
url: 'Transmit/GetAll',
method: 'get'
})
}

+ 8
- 0
src/JT808.DotNetty.Dashbord.UI/src/api/unificationSend.js View File

@@ -0,0 +1,8 @@
import axios from '@/libs/api.request'

export const SendText = ({ terminalPhoneNo, text }) => {
return axios.request({
url: 'UnificationSend/SendText/' + terminalPhoneNo + '/' + text,
method: 'get'
})
}

+ 84
- 0
src/JT808.DotNetty.Dashbord.UI/src/api/user.js View File

@@ -0,0 +1,84 @@
import axios from '@/libs/api.request'

export const login = ({ userName, password }) => {
const data = {
userName,
password
}
return axios.request({
url: 'login',
data,
method: 'post'
})
}

export const getUserInfo = (token) => {
return axios.request({
url: 'get_info',
params: {
token
},
method: 'get'
})
}

export const logout = (token) => {
return axios.request({
url: 'logout',
method: 'post'
})
}

export const getUnreadCount = () => {
return axios.request({
url: 'message/count',
method: 'get'
})
}

export const getMessage = () => {
return axios.request({
url: 'message/init',
method: 'get'
})
}

export const getContentByMsgId = msg_id => {
return axios.request({
url: 'message/content',
method: 'get',
params: {
msg_id
}
})
}

export const hasRead = msg_id => {
return axios.request({
url: 'message/has_read',
method: 'post',
data: {
msg_id
}
})
}

export const removeReaded = msg_id => {
return axios.request({
url: 'message/remove_readed',
method: 'post',
data: {
msg_id
}
})
}

export const restoreTrash = msg_id => {
return axios.request({
url: 'message/restore',
method: 'post',
data: {
msg_id
}
})
}

+ 37
- 0
src/JT808.DotNetty.Dashbord.UI/src/assets/icons/iconfont.css View File

@@ -0,0 +1,37 @@

@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1541579316141'); /* IE9*/
src: url('iconfont.eot?t=1541579316141#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAiEAAsAAAAADmgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY8eUnXY21hcAAAAYAAAACjAAACLi+YJuBnbHlmAAACJAAABAgAAAcg4dRWHmhlYWQAAAYsAAAAMQAAADYTL8piaGhlYQAABmAAAAAgAAAAJAfdA4xobXR4AAAGgAAAABQAAAAsLAD//2xvY2EAAAaUAAAAGAAAABgImgpGbWF4cAAABqwAAAAfAAAAIAEcAG5uYW1lAAAGzAAAAUUAAAJtPlT+fXBvc3QAAAgUAAAAbgAAAI54roygeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWCcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByeMTx/ytzwv4EhhrmBoRkozAiSAwDuUwzMeJzlkUEKwkAMRd/YabXFhQvxFF6qPYPrUujGY7jyIr1JoZNjtMnEhag3MOEN5MMk8D9QAoVyVSKEJwGrh6oh6wVN1iM3nc+cVImJVKdOehlklElmWdYVstp+ql8VdIv15a1NLW0zFXsO7Kjz3erH/3+rY37vr6kxnx1LKNWOJZlaxxJNnWOpSu+ot8jgqMvI6KjfyOSo88jsaAbI4tBsig89rQB4nLVUTWwbRRSeNzO767i2g7N/FP9s7MRrE5ON4/V6rSZyU0PiINSSNImES4IUoapWz6hEiqiMBDQqEojkAkiFStyKRC+9VSoFCeUEyqESVUAqEkcu3OAQb3hrJxAXwSGI3X0/szPz5vvm2x0i7O/vf8IJe5VkSJnUyUtklRBQJE1VIjRtUafkmk6pSu2ipleh4+xikkKxSksWTUeo8m8NoagpYtoslTmxrLl37z64e33esuJjU8P5Wd262LxoPVnPZ06Pxfe+C0YjkhSJygPhQCA8ABPOykwuN7NyuRvgUnAgLEnhATkaCQQiUe/7XKUyV6nQz+t2o7l66+rs7NVbq82GXTdrdjxjRGU5amTids2bUDMFtzCsqsMYMqr3IDY6OT05GjsI8Exv/6CSkOWEQigh+y3clxY5QVTcEZFIGtHLxDUJs6WsHR1y9SFKdr1HggCp3V1ICYL36OOpVmvKN9bC1u6R3vZ0qwWtVovgJfqOfUvfIYxIWL+fyETHNVJqSkIT1JTjW8ZWh3yDJDz0ctvsyt51etvrg9/QHhqGlzMM+vbmizPnDWPLMNbW19e7tffvsBzL99aWEfBRY46t+tbe3PypXv/IMDYN43WsQBe9HL2NC33RuxABrPsG+xH3o4bVRE2KgCRqulbWNf8W/UYVHM129aKra24VshZkq+CWD/Oy6Xt8cGYEthgHVlVliCfynAlqjo6oysTKlYUAD4docMI5/1ZioN+GwZNBcTwWUmTdBUqhTwX29QebXzF4An4JJMzwfMl+WQ01+IlQZVR4yhie53ycA16pOI/ODiYNGK4MChdCgXNnX5gIJXPCSYnf2OF850aQ+zJIyOs+u8+mMO8jQdwtg1TIWVRjKAnFcslMi8KfGUPoSUCergUyUk77dMyS69Ms6tijKZKYwUGKbpfdzu+iYeZYAHMFiOVi+MD7h9mb99qC0L7X8c+XatMfTj97KZ5IxJt/pd43tYYQKEjAnXMOB6kQEBrwg+LPjindAPOHNdC3q3ait0I3/ZIunZEARLNYNEUA6czSP3N/7j9wz6ZESdX0VNl1zGNS/szbQaQSIGk4DtVPcZf8AgXpf9A2OyTit5s2syZmand46bhEe2WtodLHkvaoqtTXuXN2/c42WADP9HGfbUcUW7JgqHss4xHtlMys679FqUomdP9VJBQBdnlPABBubpuNwqnmQj6/0HwNQzKxDUJFgKiXurBG6dqFjmeBzsvtRPJgGIZThYa5fdOvsReOticPh6JHHXxsv7ItJpOniYPYsmZ/x0QD/o5P105DeQwF6MH33ogoLi+KQp7zpY3HQV5bFMURzheXeds7gpP+jKNXljjHuYvXHke7cdCxLLZf6YX7B63UcCV4nGNgZGBgAOKAN2ZR8fw2Xxm4WRhA4AbHYRMY/f///1oWBuYGIJeDgQkkCgAvWgs2AAAAeJxjYGRgYG7438AQw8Lw/z8DAwsDA1AEBXADAHXiBHJ4nGNhYGBgYfj/nwVM48cATwECKwAAAAAAjAC6AOgBFAGAAf4CbgLqAzgDkHicY2BkYGDgZkhiYGcAASYg5gJCBob/YD4DABOmAYsAeJxlj01OwzAQhV/6B6QSqqhgh+QFYgEo/RGrblhUavdddN+mTpsqiSPHrdQDcB6OwAk4AtyAO/BIJ5s2lsffvHljTwDc4Acejt8t95E9XDI7cg0XuBeuU38QbpBfhJto41W4Rf1N2MczpsJtdGF5g9e4YvaEd2EPHXwI13CNT+E69S/hBvlbuIk7/Aq30PHqwj7mXle4jUcv9sdWL5xeqeVBxaHJIpM5v4KZXu+Sha3S6pxrW8QmU4OgX0lTnWlb3VPs10PnIhVZk6oJqzpJjMqt2erQBRvn8lGvF4kehCblWGP+tsYCjnEFhSUOjDFCGGSIyujoO1Vm9K+xQ8Jee1Y9zed0WxTU/3OFAQL0z1xTurLSeTpPgT1fG1J1dCtuy56UNJFezUkSskJe1rZUQuoBNmVXjhF6XNGJPyhnSP8ACVpuyAAAAHicbYhdDoIwEAb3a6k/YIIX8VArWewmdJFWJOnpJTG+OQ+TzJCjLy39p4ODR4OAA4444YwWHS7U3IVzn6Voldtb8ksHnvohrlqjjmw1rmzXsvdT7fEbblnCmOfNfJIYStJJfGIL27yb6AOCGR89AAA=') format('woff'),
url('iconfont.ttf?t=1541579316141') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('iconfont.svg?t=1541579316141#iconfont') format('svg'); /* iOS 4.1- */
}

.iconfont {
font-family:"iconfont" !important;
font-size:16px;
font-style:normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

.icon-bear:before { content: "\e600"; }

.icon-resize-vertical:before { content: "\e7c3"; }

.icon-chuizhifanzhuan:before { content: "\e661"; }

.icon-shuipingfanzhuan:before { content: "\e662"; }

.icon-qq:before { content: "\e609"; }

.icon-frown:before { content: "\e77e"; }

.icon-meh:before { content: "\e780"; }

.icon-smile:before { content: "\e783"; }

.icon-man:before { content: "\e7e2"; }

.icon-woman:before { content: "\e7e5"; }


BIN
src/JT808.DotNetty.Dashbord.UI/src/assets/icons/iconfont.eot View File


+ 56
- 0
src/JT808.DotNetty.Dashbord.UI/src/assets/icons/iconfont.svg View File

@@ -0,0 +1,56 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>

<font id="iconfont" horiz-adv-x="1024" >
<font-face
font-family="iconfont"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
ascent="896"
descent="-128"
/>
<missing-glyph />
<glyph glyph-name="bear" unicode="&#58880;" d="M1024 683.008q0-70.656-46.08-121.856 46.08-89.088 46.08-193.536 0-96.256-39.936-181.248t-109.568-147.968-162.816-99.328-199.68-36.352-199.68 36.352-162.304 99.328-109.568 147.968-40.448 181.248q0 104.448 46.08 193.536-46.08 51.2-46.08 121.856 0 37.888 13.824 71.168t37.376 58.368 55.808 39.424 68.096 14.336q43.008 0 78.848-18.432t59.392-50.176q46.08 17.408 96.256 26.624t102.4 9.216 102.4-9.216 96.256-26.624q24.576 31.744 59.904 50.176t78.336 18.432q36.864 0 68.608-14.336t55.296-39.424 37.376-58.368 13.824-71.168zM205.824 268.288q10.24 0 18.944 10.24t15.36 28.672 10.24 42.496 3.584 51.712-3.584 51.712-10.24 41.984-15.36 28.16-18.944 10.24q-9.216 0-17.92-10.24t-15.36-28.16-10.752-41.984-4.096-51.712 4.096-51.712 10.752-42.496 15.36-28.672 17.92-10.24zM512-31.744000000000028q53.248 0 99.84 13.312t81.408 35.84 54.784 52.736 19.968 65.024q0 33.792-19.968 64t-54.784 52.736-81.408 35.84-99.84 13.312-99.84-13.312-81.408-35.84-54.784-52.736-19.968-64q0-34.816 19.968-65.024t54.784-52.736 81.408-35.84 99.84-13.312zM818.176 268.288q10.24 0 18.944 10.24t15.36 28.672 10.24 42.496 3.584 51.712-3.584 51.712-10.24 41.984-15.36 28.16-18.944 10.24q-9.216 0-17.92-10.24t-15.36-28.16-10.752-41.984-4.096-51.712 4.096-51.712 10.752-42.496 15.36-28.672 17.92-10.24zM512 235.51999999999998q39.936 0 68.096-9.728t28.16-24.064-28.16-24.064-68.096-9.728-68.096 9.728-28.16 24.064 28.16 24.064 68.096 9.728z" horiz-adv-x="1024" />

<glyph glyph-name="resize-vertical" unicode="&#59331;" d="M512 896C229.248 896 0 666.752 0 384s229.248-512 512-512 512 229.248 512 512S794.752 896 512 896zM576 192l64 0-128-128-128 128 64 0L448 576l-64 0 128 128 128-128-64 0L576 192z" horiz-adv-x="1024" />

<glyph glyph-name="chuizhifanzhuan" unicode="&#58977;" d="M286.01856 645.08416l472.4224 0 0-146.2784-472.4224 0 0 146.2784ZM87.19872 420.37248l885.80096 0 0-70.87104-885.80096 0 0 70.87104ZM773.55008 268.05248l0-31.0016L270.6688 237.05088l0 31.0016L773.55008 268.05248zM773.55008 121.4208l0-31.0016L270.6688 90.4192l0 31.0016L773.55008 121.4208zM742.54848 240.75776l31.0016 0 0-123.04896-31.0016 0L742.54848 240.75776zM270.70464 240.57856l31.0016 0 0-123.04896-31.0016 0L270.70464 240.57856z" horiz-adv-x="1024" />

<glyph glyph-name="shuipingfanzhuan" unicode="&#58978;" d="M252.76928 596.096l146.2784 0 0-472.42752-146.2784 0 0 472.42752ZM477.48096 810.65472l70.87104 0 0-885.80608-70.87104 0 0 885.80608ZM629.80096 611.2l31.0016 0 0-502.88128-31.0016 0L629.80096 611.2zM776.42752 611.2l31.0016 0 0-502.88128-31.0016 0L776.42752 611.2zM657.09056 580.1984l0 31.0016 123.04896 0 0-31.0016L657.09056 580.1984zM657.27488 108.35456l0 31.0016 123.04896 0 0-31.0016L657.27488 108.35456z" horiz-adv-x="1024" />

<glyph glyph-name="qq" unicode="&#58889;" d="M147.372058 491.394284c-5.28997-13.909921 2.431986-22.698872 0-75.732573-0.682996-14.25092-62.165649-78.762555-86.569511-145.791177-24.192863-66.517625-27.519845-135.978232 9.811944-163.285078 37.419789-27.305846 72.191593 90.879487 76.757567 73.685584 1.961989-7.509958 4.436975-15.317914 7.423958-23.338868a331.945126 331.945126 0 0 1 61.140655-101.162429c5.929967-6.783962-36.009797-19.199892-61.140655-61.99365-25.173858-42.751759 7.209959-120.49032 132.223254-120.49032 161.27909 0 197.288886 56.70368 200.574868 56.447681 12.031932-0.895995 12.841928 0 25.599855 0 15.572912 0 9.129948-1.279993 23.593867 0 7.807956 0.682996 86.186514-67.839617 194.686901-56.447681 184.873956 19.45589 156.586116 81.40754 142.079198 120.48932-15.103915 40.83277-68.692612 59.946662-66.303626 62.549647 44.28775 48.938724 51.285711 79.018554 66.346626 123.9463 6.143965 18.473896 49.066723-101.674426 82.089537-73.685584 13.781922 11.690934 41.301767 60.24566 13.781922 163.285078-27.519845 102.996419-80.767544 126.505286-79.615551 145.791177 2.389987 40.191773 1.023994 68.436614-1.023994 75.732573-9.812945 35.4128-30.378829 27.604844-30.378829 35.4128C858.450044 730.752933 705.10691 896 515.966978 896s-342.398067-165.289067-342.398068-369.192916c0-16.169909-14.378919-4.223976-26.154852-35.4128z" horiz-adv-x="1024" />

<glyph glyph-name="frown" unicode="&#59262;" d="M336 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM688 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM512 832C264.6 832 64 631.4 64 384s200.6-448 448-448 448 200.6 448 448S759.4 832 512 832z m263-711c-34.2-34.2-74-61-118.3-79.8C611 21.8 562.3 12 512 12c-50.3 0-99 9.8-144.8 29.2-44.3 18.7-84.1 45.6-118.3 79.8-34.2 34.2-61 74-79.8 118.3C149.8 285 140 333.7 140 384s9.8 99 29.2 144.8c18.7 44.3 45.6 84.1 79.8 118.3 34.2 34.2 74 61 118.3 79.8C413 746.2 461.7 756 512 756c50.3 0 99-9.8 144.8-29.2 44.3-18.7 84.1-45.6 118.3-79.8 34.2-34.2 61-74 79.8-118.3C874.2 483 884 434.3 884 384s-9.8-99-29.2-144.8c-18.7-44.3-45.6-84.1-79.8-118.2zM512 363c-85.5 0-155.6-67.3-160-151.6-0.2-4.6 3.4-8.4 8-8.4h48.1c4.2 0 7.8 3.2 8.1 7.4C420 259.9 461.5 299 512 299s92.1-39.1 95.8-88.6c0.3-4.2 3.9-7.4 8.1-7.4H664c4.6 0 8.2 3.8 8 8.4-4.4 84.3-74.5 151.6-160 151.6z" horiz-adv-x="1024" />

<glyph glyph-name="meh" unicode="&#59264;" d="M336 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM688 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM512 832C264.6 832 64 631.4 64 384s200.6-448 448-448 448 200.6 448 448S759.4 832 512 832z m263-711c-34.2-34.2-74-61-118.3-79.8C611 21.8 562.3 12 512 12c-50.3 0-99 9.8-144.8 29.2-44.3 18.7-84.1 45.6-118.3 79.8-34.2 34.2-61 74-79.8 118.3C149.8 285 140 333.7 140 384s9.8 99 29.2 144.8c18.7 44.3 45.6 84.1 79.8 118.3 34.2 34.2 74 61 118.3 79.8C413 746.2 461.7 756 512 756c50.3 0 99-9.8 144.8-29.2 44.3-18.7 84.1-45.6 118.3-79.8 34.2-34.2 61-74 79.8-118.3C874.2 483 884 434.3 884 384s-9.8-99-29.2-144.8c-18.7-44.3-45.6-84.1-79.8-118.2zM664 331H360c-4.4 0-8-3.6-8-8v-48c0-4.4 3.6-8 8-8h304c4.4 0 8 3.6 8 8v48c0 4.4-3.6 8-8 8z" horiz-adv-x="1024" />

<glyph glyph-name="smile" unicode="&#59267;" d="M336 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM688 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM512 832C264.6 832 64 631.4 64 384s200.6-448 448-448 448 200.6 448 448S759.4 832 512 832z m263-711c-34.2-34.2-74-61-118.3-79.8C611 21.8 562.3 12 512 12c-50.3 0-99 9.8-144.8 29.2-44.3 18.7-84.1 45.6-118.3 79.8-34.2 34.2-61 74-79.8 118.3C149.8 285 140 333.7 140 384s9.8 99 29.2 144.8c18.7 44.3 45.6 84.1 79.8 118.3 34.2 34.2 74 61 118.3 79.8C413 746.2 461.7 756 512 756c50.3 0 99-9.8 144.8-29.2 44.3-18.7 84.1-45.6 118.3-79.8 34.2-34.2 61-74 79.8-118.3C874.2 483 884 434.3 884 384s-9.8-99-29.2-144.8c-18.7-44.3-45.6-84.1-79.8-118.2zM664 363h-48.1c-4.2 0-7.8-3.2-8.1-7.4C604 306.1 562.5 267 512 267s-92.1 39.1-95.8 88.6c-0.3 4.2-3.9 7.4-8.1 7.4H360c-4.6 0-8.2-3.8-8-8.4 4.4-84.3 74.5-151.6 160-151.6s155.6 67.3 160 151.6c0.2 4.6-3.4 8.4-8 8.4z" horiz-adv-x="1024" />

<glyph glyph-name="man" unicode="&#59362;" d="M874 776H622c-3.3 0-6-2.7-6-6v-56c0-3.3 2.7-6 6-6h160.4L583.1 508.7c-50 38.5-111 59.3-175.1 59.3-76.9 0-149.3-30-203.6-84.4S120 356.9 120 280s30-149.3 84.4-203.6C258.7 22 331.1-8 408-8s149.3 30 203.6 84.4C666 130.7 696 203.1 696 280c0 64.1-20.8 124.9-59.2 174.9L836 654.1V494c0-3.3 2.7-6 6-6h56c3.3 0 6 2.7 6 6V746c0 16.5-13.5 30-30 30zM408 68c-116.9 0-212 95.1-212 212s95.1 212 212 212 212-95.1 212-212-95.1-212-212-212z" horiz-adv-x="1024" />

<glyph glyph-name="woman" unicode="&#59365;" d="M909.7 739.4l-42.2 42.2c-3.1 3.1-8.2 3.1-11.3 0L764 689.4l-84.2 84.2c-3.1 3.1-8.2 3.1-11.3 0l-42.1-42.1c-3.1-3.1-3.1-8.1 0-11.3l84.2-84.2-135.5-135.3c-50 38.5-111 59.3-175.1 59.3-76.9 0-149.3-30-203.6-84.4S112 348.9 112 272s30-149.3 84.4-203.6C250.7 14 323.1-16 400-16s149.3 30 203.6 84.4C658 122.7 688 195.1 688 272c0 64.2-20.9 125.1-59.3 175.1l135.4 135.4 84.2-84.2c3.1-3.1 8.2-3.1 11.3 0l42.1 42.1c3.1 3.1 3.1 8.1 0 11.3l-84.2 84.2 92.2 92.2c3.1 3.1 3.1 8.2 0 11.3zM400 60c-116.9 0-212 95.1-212 212s95.1 212 212 212 212-95.1 212-212-95.1-212-212-212z" horiz-adv-x="1024" />



</font>
</defs></svg>

BIN
src/JT808.DotNetty.Dashbord.UI/src/assets/icons/iconfont.ttf View File


BIN
src/JT808.DotNetty.Dashbord.UI/src/assets/icons/iconfont.woff View File


+ 1
- 0
src/JT808.DotNetty.Dashbord.UI/src/assets/images/error-page/error-401.svg
File diff suppressed because it is too large
View File


+ 1
- 0
src/JT808.DotNetty.Dashbord.UI/src/assets/images/error-page/error-404.svg
File diff suppressed because it is too large
View File


+ 1
- 0
src/JT808.DotNetty.Dashbord.UI/src/assets/images/error-page/error-500.svg
File diff suppressed because it is too large
View File


BIN
src/JT808.DotNetty.Dashbord.UI/src/assets/images/login-bg.jpg View File

Before After
Width: 1920  |  Height: 1080  |  Size: 450 KiB

BIN
src/JT808.DotNetty.Dashbord.UI/src/assets/images/logo-min.jpg View File

Before After
Width: 91  |  Height: 91  |  Size: 726 B

BIN
src/JT808.DotNetty.Dashbord.UI/src/assets/images/logo.jpg View File

Before After
Width: 312  |  Height: 85  |  Size: 3.2 KiB

BIN
src/JT808.DotNetty.Dashbord.UI/src/assets/images/qq-group1.jpg View File

Before After
Width: 540  |  Height: 740  |  Size: 40 KiB

BIN
src/JT808.DotNetty.Dashbord.UI/src/assets/images/qq-group2.jpg View File

Before After
Width: 540  |  Height: 740  |  Size: 40 KiB

BIN
src/JT808.DotNetty.Dashbord.UI/src/assets/images/talkingdata.png View File

Before After
Width: 2480  |  Height: 1204  |  Size: 2.7 MiB

+ 58
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/charts/bar.vue View File

@@ -0,0 +1,58 @@
<template>
<div ref="dom" class="charts chart-bar"></div>
</template>

<script>
import echarts from 'echarts'
import tdTheme from './theme.json'
import { on, off } from '@/libs/tools'
echarts.registerTheme('tdTheme', tdTheme)
export default {
name: 'ChartBar',
props: {
value: Object,
text: String,
subtext: String
},
data () {
return {
dom: null
}
},
methods: {
resize () {
this.dom.resize()
}
},
mounted () {
this.$nextTick(() => {
let xAxisData = Object.keys(this.value)
let seriesData = Object.values(this.value)
let option = {
title: {
text: this.text,
subtext: this.subtext,
x: 'center'
},
xAxis: {
type: 'category',
data: xAxisData
},
yAxis: {
type: 'value'
},
series: [{
data: seriesData,
type: 'bar'
}]
}
this.dom = echarts.init(this.$refs.dom, 'tdTheme')
this.dom.setOption(option)
on(window, 'resize', this.resize)
})
},
beforeDestroy () {
off(window, 'resize', this.resize)
}
}
</script>

+ 3
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/charts/index.js View File

@@ -0,0 +1,3 @@
import ChartPie from './pie.vue'
import ChartBar from './bar.vue'
export { ChartPie, ChartBar }

+ 70
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/charts/pie.vue View File

@@ -0,0 +1,70 @@
<template>
<div ref="dom" class="charts chart-pie"></div>
</template>

<script>
import echarts from 'echarts'
import tdTheme from './theme.json'
import { on, off } from '@/libs/tools'
echarts.registerTheme('tdTheme', tdTheme)
export default {
name: 'ChartPie',
props: {
value: Array,
text: String,
subtext: String
},
data () {
return {
dom: null
}
},
methods: {
resize () {
this.dom.resize()
}
},
mounted () {
this.$nextTick(() => {
let legend = this.value.map(_ => _.name)
let option = {
title: {
text: this.text,
subtext: this.subtext,
x: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
data: legend
},
series: [
{
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: this.value,
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
this.dom = echarts.init(this.$refs.dom, 'tdTheme')
this.dom.setOption(option)
on(window, 'resize', this.resize)
})
},
beforeDestroy () {
off(window, 'resize', this.resize)
}
}
</script>

+ 42
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/common-icon/common-icon.vue View File

@@ -0,0 +1,42 @@
<template>
<component :is="iconType" :type="iconName" :color="iconColor" :size="iconSize"/>
</template>

<script>
import Icons from '_c/icons'
export default {
name: 'CommonIcon',
components: {Icons},
props: {
type: {
type: String,
required: true
},
color: String,
size: Number
},
computed: {
iconType () {
return this.type.indexOf('_') === 0 ? 'Icons' : 'Icon'
},
iconName () {
return this.iconType === 'Icons' ? this.getCustomIconName(this.type) : this.type
},
iconSize () {
return this.size || (this.iconType === 'Icons' ? 12 : undefined)
},
iconColor () {
return this.color || ''
}
},
methods: {
getCustomIconName (iconName) {
return iconName.slice(1)
}
}
}
</script>

<style>

</style>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/common-icon/index.js View File

@@ -0,0 +1,2 @@
import CommonIcon from './common-icon.vue'
export default CommonIcon

+ 8
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/common/common.less View File

@@ -0,0 +1,8 @@
.no-select{
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

+ 3
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/common/util.js View File

@@ -0,0 +1,3 @@
export const showTitle = (item, vm) => {
return vm.$config.useI18n ? vm.$t(item.name) : ((item.meta && item.meta.title) || item.name)
}

+ 174
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/count-to/count-to.vue View File

@@ -0,0 +1,174 @@
<template>
<div class="count-to-wrapper">
<slot name="left"/>
<p class="content-outer"><span :class="['count-to-count-text', countClass]" :id="counterId">{{ init }}</span><i :class="['count-to-unit-text', unitClass]">{{ unitText }}</i></p>
<slot name="right"/>
</div>
</template>

<script>
import CountUp from 'countup'
import './index.less'
export default {
name: 'CountTo',
props: {
init: {
type: Number,
default: 0
},
/**
* @description 起始值,即动画开始前显示的数值
*/
startVal: {
type: Number,
default: 0
},
/**
* @description 结束值,即动画结束后显示的数值
*/
end: {
type: Number,
required: true
},
/**
* @description 保留几位小数
*/
decimals: {
type: Number,
default: 0
},
/**
* @description 分隔整数和小数的符号,默认是小数点
*/
decimal: {
type: String,
default: '.'
},
/**
* @description 动画持续的时间,单位是秒
*/
duration: {
type: Number,
default: 2
},
/**
* @description 动画延迟开始的时间,单位是秒
*/
delay: {
type: Number,
default: 0
},
/**
* @description 是否禁用easing动画效果
*/
uneasing: {
type: Boolean,
default: false
},
/**
* @description 是否使用分组,分组后每三位会用一个符号分隔
*/
usegroup: {
type: Boolean,
default: false
},
/**
* @description 用于分组(usegroup)的符号
*/
separator: {
type: String,
default: ','
},
/**
* @description 是否简化显示,设为true后会使用unit单位来做相关省略
*/
simplify: {
type: Boolean,
default: false
},
/**
* @description 自定义单位,如[3, 'K+'], [6, 'M+']即大于3位数小于6位数的用k+来做省略
* 1000即显示为1K+
*/
unit: {
type: Array,
default () {
return [[3, 'K+'], [6, 'M+'], [9, 'B+']]
}
},
countClass: {
type: String,
default: ''
},
unitClass: {
type: String,
default: ''
}
},
data () {
return {
counter: null,
unitText: ''
}
},
computed: {
counterId () {
return `count_to_${this._uid}`
}
},
methods: {
getHandleVal (val, len) {
return {
endVal: parseInt(val / Math.pow(10, this.unit[len - 1][0])),
unitText: this.unit[len - 1][1]
}
},
transformValue (val) {
let len = this.unit.length
let res = {
endVal: 0,
unitText: ''
}
if (val < Math.pow(10, this.unit[0][0])) res.endVal = val
else {
for (let i = 1; i < len; i++) {
if (val >= Math.pow(10, this.unit[i - 1][0]) && val < Math.pow(10, this.unit[i][0])) res = this.getHandleVal(val, i)
}
}
if (val > Math.pow(10, this.unit[len - 1][0])) res = this.getHandleVal(val, len)
return res
},
getValue (val) {
let res = 0
if (this.simplify) {
let { endVal, unitText } = this.transformValue(val)
this.unitText = unitText
res = endVal
} else {
res = val
}
return res
}
},
mounted () {
this.$nextTick(() => {
let endVal = this.getValue(this.end)
this.counter = new CountUp(this.counterId, this.startVal, endVal, this.decimals, this.duration, {
useEasing: !this.uneasing,
useGrouping: this.useGroup,
separator: this.separator,
decimal: this.decimal
})
setTimeout(() => {
if (!this.counter.error) this.counter.start()
}, this.delay)
})
},
watch: {
end (newVal) {
let endVal = this.getValue(newVal)
this.counter.update(endVal)
}
}
}
</script>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/count-to/index.js View File

@@ -0,0 +1,2 @@
import countTo from './count-to.vue'
export default countTo

+ 10
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/count-to/index.less View File

@@ -0,0 +1,10 @@
@prefix: ~"count-to";

.@{prefix}-wrapper{
.content-outer{
display: inline-block;
.@{prefix}-unit-text{
font-style: normal;
}
}
}

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/cropper/index.js View File

@@ -0,0 +1,2 @@
import Cropper from './index.vue'
export default Cropper

+ 35
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/cropper/index.less View File

@@ -0,0 +1,35 @@
.bg{
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC")
}
.cropper-wrapper{
width: 600px;
height: 340px;
.img-box{
height: 340px;
width: 430px;
border: 1px solid #ebebeb;
display: inline-block;
.bg;
img{
max-width: 100%;
display: block;
}
}
.right-con{
display: inline-block;
width: 170px;
vertical-align: top;
box-sizing: border-box;
padding: 0 10px;
.preview-box{
height: 150px !important;
width: 100% !important;
overflow: hidden;
border: 1px solid #ebebeb;
.bg;
}
.button-box{
padding: 10px 0 0;
}
}
}

+ 139
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/cropper/index.vue View File

@@ -0,0 +1,139 @@
<template>
<div class="cropper-wrapper">
<div class="img-box">
<img class="cropper-image" :id="imgId" alt="">
</div>
<div class="right-con">
<div v-if="preview" class="preview-box" :id="previewId"></div>
<div class="button-box">
<slot>
<Upload action="image/upload" :before-upload="beforeUpload">
<Button style="width: 150px;" type="primary">上传图片</Button>
</Upload>
</slot>
<div v-show="insideSrc">
<Button type="primary" @click="rotate">
<Icon type="md-refresh" :size="18"/>
</Button>
<Button type="primary" @click="shrink">
<Icon type="md-remove" :size="18"/>
</Button>
<Button type="primary" @click="magnify">
<Icon type="md-add" :size="18"/>
</Button>
<Button type="primary" @click="scale('X')">
<Icon custom="iconfont icon-shuipingfanzhuan" :size="18"/>
</Button>
<Button type="primary" @click="scale('Y')">
<Icon custom="iconfont icon-chuizhifanzhuan" :size="18"/>
</Button>
<Button type="primary" @click="move(0, -moveStep)">
<Icon type="md-arrow-round-up" :size="18"/>
</Button>
<Button type="primary" @click="move(-moveStep, 0)">
<Icon type="md-arrow-round-back" :size="18"/>
</Button>
<Button type="primary" @click="move(0, moveStep)">
<Icon type="md-arrow-round-down" :size="18"/>
</Button>
<Button type="primary" @click="move(moveStep, 0)">
<Icon type="md-arrow-round-forward" :size="18"/>
</Button>
<Button style="width: 150px;margin-top: 10px;" type="primary" @click="crop">{{ cropButtonText }}</Button>
</div>
</div>
</div>
</div>
</template>

<script>
import Cropper from 'cropperjs'
import './index.less'
import 'cropperjs/dist/cropper.min.css'
export default {
name: 'Cropper',
props: {
src: {
type: String,
default: ''
},
preview: {
type: Boolean,
default: true
},
moveStep: {
type: Number,
default: 4
},
cropButtonText: {
type: String,
default: '裁剪'
}
},
data () {
return {
cropper: null,
insideSrc: ''
}
},
computed: {
imgId () {
return `cropper${this._uid}`
},
previewId () {
return `cropper_preview${this._uid}`
}
},
watch: {
src (src) {
this.replace(src)
},
insideSrc (src) {
this.replace(src)
}
},
methods: {
beforeUpload (file) {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = (event) => {
this.insideSrc = event.srcElement.result
}
return false
},
replace (src) {
this.cropper.replace(src)
this.insideSrc = src
},
rotate () {
this.cropper.rotate(90)
},
shrink () {
this.cropper.zoom(-0.1)
},
magnify () {
this.cropper.zoom(0.1)
},
scale (d) {
this.cropper[`scale${d}`](-this.cropper.getData()[`scale${d}`])
},
move (...argu) {
this.cropper.move(...argu)
},
crop () {
this.cropper.getCroppedCanvas().toBlob(blob => {
this.$emit('on-crop', blob)
})
}
},
mounted () {
this.$nextTick(() => {
let dom = document.getElementById(this.imgId)
this.cropper = new Cropper(dom, {
preview: `#${this.previewId}`,
checkCrossOrigin: true
})
})
}
}
</script>

+ 92
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/drag-list/drag-list.vue View File

@@ -0,0 +1,92 @@
<template>
<div class="drag-list-wrapper">
<div class="drag-list-con con1">
<slot name="left-title"></slot>
<draggable class="drop-box1" :class="dropConClass.left" :options="options" :value="list1" @input="handleListChange($event, 'left')" @end="handleEnd($event, 'left')">
<div class="drag-list-item" v-for="(itemLeft, index) in list1" :key="`drag_li1_${index}`">
<slot name="left" :itemLeft="itemLeft">{{ itemLeft }}</slot>
</div>
</draggable>
</div>
<div class="drag-list-con con2">
<slot name="right-title"></slot>
<draggable class="drop-box2" :class="dropConClass.right" :options="options" :value="list2" @input="handleListChange($event, 'right')" @end="handleEnd($event, 'right')">
<div class="drag-list-item" v-for="(itemRight, index) in list2" :key="`drag_li2_${index}`">
<slot name="right" :itemRight="itemRight">{{ itemRight }}</slot>
</div>
</draggable>
</div>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: 'DragList',
components: {
draggable
},
props: {
list1: {
type: Array,
required: true
},
list2: {
type: Array,
default: () => []
},
dropConClass: {
type: Object,
default: () => ({})
}
},
data () {
return {
options: { group: 'drag_list' }
}
},
methods: {
handleListChange (value, type) {
if (type === 'left') this.$emit('update:list1', value)
else this.$emit('update:list2', value)
},
handleEnd (event, type) {
const srcClassName = (event.srcElement || event.target).classList[0]
const targetClassName = event.to.classList[0]
let src = ''
let target = ''
if (srcClassName === targetClassName) {
if (type === 'left') {
src = 'left'
target = 'left'
} else {
src = 'right'
target = 'right'
}
} else {
if (type === 'left') {
src = 'left'
target = 'right'
} else {
src = 'right'
target = 'left'
}
}
this.$emit('on-change', {
src: src,
target: target,
oldIndex: event.oldIndex,
newIndex: event.newIndex
})
}
}
}
</script>
<style lang="less">
.drag-list-wrapper{
height: 100%;
.drag-list-con{
width: 50%;
float: left;
}
}
</style>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/drag-list/index.js View File

@@ -0,0 +1,2 @@
import DragList from './drag-list.vue'
export default DragList

+ 75
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/editor/editor.vue View File

@@ -0,0 +1,75 @@
<template>
<div class="editor-wrapper">
<div :id="editorId"></div>
</div>
</template>

<script>
import Editor from 'wangeditor'
import 'wangeditor/release/wangEditor.min.css'
import { oneOf } from '@/libs/tools'
export default {
name: 'Editor',
props: {
value: {
type: String,
default: ''
},
/**
* 绑定的值的类型, enum: ['html', 'text']
*/
valueType: {
type: String,
default: 'html',
validator: (val) => {
return oneOf(val, ['html', 'text'])
}
},
/**
* @description 设置change事件触发时间间隔
*/
changeInterval: {
type: Number,
default: 200
},
/**
* @description 是否开启本地存储
*/
cache: {
type: Boolean,
default: true
}
},
computed: {
editorId () {
return `editor${this._uid}`
}
},
methods: {
setHtml (val) {
this.editor.txt.html(val)
}
},
mounted () {
this.editor = new Editor(`#${this.editorId}`)
this.editor.customConfig.onchange = (html) => {
let text = this.editor.txt.text()
if (this.cache) localStorage.editorCache = html
this.$emit('input', this.valueType === 'html' ? html : text)
this.$emit('on-change', html, text)
}
this.editor.customConfig.onchangeTimeout = this.changeInterval
// create这个方法一定要在所有配置项之后调用
this.editor.create()
// 如果本地有存储加载本地存储内容
let html = this.value || localStorage.editorCache
if (html) this.editor.txt.html(html)
}
}
</script>

<style lang="less">
.editor-wrapper *{
z-index: 100 !important;
}
</style>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/editor/index.js View File

@@ -0,0 +1,2 @@
import Editor from './editor.vue'
export default Editor

+ 35
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/icons/icons.vue View File

@@ -0,0 +1,35 @@
<template>
<i :class="`iconfont icon-${type}`" :style="styles"></i>
</template>

<script>
export default {
name: 'Icons',
props: {
type: {
type: String,
required: true
},
color: {
type: String,
default: '#5c6b77'
},
size: {
type: Number,
default: 16
}
},
computed: {
styles () {
return {
fontSize: `${this.size}px`,
color: this.color
}
}
}
}
</script>

<style>

</style>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/icons/index.js View File

@@ -0,0 +1,2 @@
import Icons from './icons.vue'
export default Icons

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/info-card/index.js View File

@@ -0,0 +1,2 @@
import InforCard from './infor-card.vue'
export default InforCard

+ 94
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/info-card/infor-card.vue View File

@@ -0,0 +1,94 @@
<template>
<Card :shadow="shadow" class="info-card-wrapper" :padding="0">
<div class="content-con">
<div class="left-area" :style="{background: color, width: leftWidth}">
<common-icon class="icon" :type="icon" :size="iconSize" color="#fff"/>
</div>
<div class="right-area" :style="{width: rightWidth}">
<div>
<slot></slot>
</div>
</div>
</div>
</Card>
</template>

<script>
import CommonIcon from '_c/common-icon'
export default {
name: 'InforCard',
components: {
CommonIcon
},
props: {
left: {
type: Number,
default: 36
},
color: {
type: String,
default: '#2d8cf0'
},
icon: {
type: String,
default: ''
},
iconSize: {
type: Number,
default: 20
},
shadow: {
type: Boolean,
default: false
}
},
computed: {
leftWidth () {
return `${this.left}%`
},
rightWidth () {
return `${100 - this.left}%`
}
}
}
</script>

<style lang="less">
.common{
float: left;
height: 100%;
display: table;
text-align: center;
}
.size{
width: 100%;
height: 100%;
}
.middle-center{
display: table-cell;
vertical-align: middle;
}
.info-card-wrapper{
.size;
overflow: hidden;
.ivu-card-body{
.size;
}
.content-con{
.size;
position: relative;
.left-area{
.common;
& > .icon{
.middle-center;
}
}
.right-area{
.common;
& > div{
.middle-center;
}
}
}
}
</style>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/login-form/index.js View File

@@ -0,0 +1,2 @@
import LoginForm from './login-form.vue'
export default LoginForm

+ 72
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/login-form/login-form.vue View File

@@ -0,0 +1,72 @@
<template>
<Form ref="loginForm" :model="form" :rules="rules" @keydown.enter.native="handleSubmit">
<FormItem prop="userName">
<Input v-model="form.userName" placeholder="请输入用户名">
<span slot="prepend">
<Icon :size="16" type="ios-person"></Icon>
</span>
</Input>
</FormItem>
<FormItem prop="password">
<Input type="password" v-model="form.password" placeholder="请输入密码">
<span slot="prepend">
<Icon :size="14" type="md-lock"></Icon>
</span>
</Input>
</FormItem>
<FormItem>
<Button @click="handleSubmit" type="primary" long>登录</Button>
</FormItem>
</Form>
</template>
<script>
export default {
name: 'LoginForm',
props: {
userNameRules: {
type: Array,
default: () => {
return [
{ required: true, message: '账号不能为空', trigger: 'blur' }
]
}
},
passwordRules: {
type: Array,
default: () => {
return [
{ required: true, message: '密码不能为空', trigger: 'blur' }
]
}
}
},
data () {
return {
form: {
userName: 'super_admin',
password: ''
}
}
},
computed: {
rules () {
return {
userName: this.userNameRules,
password: this.passwordRules
}
}
},
methods: {
handleSubmit () {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.$emit('on-success-valid', {
userName: this.form.userName,
password: this.form.password
})
}
})
}
}
}
</script>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/a-back-top/index.js View File

@@ -0,0 +1,2 @@
import ABackTop from './index.vue'
export default ABackTop

+ 90
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/a-back-top/index.vue View File

@@ -0,0 +1,90 @@
<template>
<div :class="classes" :style="styles" @click="back">
<slot>
<div :class="innerClasses">
<i class="ivu-icon ivu-icon-ios-arrow-up"></i>
</div>
</slot>
</div>
</template>
<script>
import { scrollTop } from '@/libs/util'
import { on, off } from '@/libs/tools'
const prefixCls = 'ivu-back-top'

export default {
name: 'ABackTop',
props: {
height: {
type: Number,
default: 400
},
bottom: {
type: Number,
default: 30
},
right: {
type: Number,
default: 30
},
duration: {
type: Number,
default: 1000
},
container: {
type: null,
default: window
}
},
data () {
return {
backTop: false
}
},
mounted () {
// window.addEventListener('scroll', this.handleScroll, false)
// window.addEventListener('resize', this.handleScroll, false)
on(this.containerEle, 'scroll', this.handleScroll)
on(this.containerEle, 'resize', this.handleScroll)
},
beforeDestroy () {
// window.removeEventListener('scroll', this.handleScroll, false)
// window.removeEventListener('resize', this.handleScroll, false)
off(this.containerEle, 'scroll', this.handleScroll)
off(this.containerEle, 'resize', this.handleScroll)
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-show`]: this.backTop
}
]
},
styles () {
return {
bottom: `${this.bottom}px`,
right: `${this.right}px`
}
},
innerClasses () {
return `${prefixCls}-inner`
},
containerEle () {
return this.container === window ? window : document.querySelector(this.container)
}
},
methods: {
handleScroll () {
this.backTop = this.containerEle.scrollTop >= this.height
},
back () {
let target = typeof this.container === 'string' ? this.containerEle : (document.documentElement || document.body)
const sTop = target.scrollTop
scrollTop(this.containerEle, sTop, 0, this.duration)
this.$emit('on-click')
}
}
}
</script>

+ 49
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/error-store/error-store.vue View File

@@ -0,0 +1,49 @@
<template>
<div class="error-store">
<Badge dot :count="countComputed">
<Button type="text" @click="openErrorLoggerPage">
<Icon :size="20" type="ios-bug"/>
</Button>
</Badge>
</div>
</template>

<script>
export default {
name: 'ErrorStore',
props: {
count: {
type: Number,
default: 0
},
hasRead: {
type: Boolean,
default: false
}
},
computed: {
countComputed () {
return this.hasRead ? 0 : this.count
}
},
methods: {
openErrorLoggerPage () {
this.$router.push({
name: 'error_logger_page'
})
}
}
}
</script>

<style lang="less">
.error-store{
margin-right: 12px;
.ivu-badge-dot{
top: 20px;
}
.ivu-btn.ivu-btn-text{
padding: 5px 1px 6px;
}
}
</style>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/error-store/index.js View File

@@ -0,0 +1,2 @@
import ErrorStore from './error-store.vue'
export default ErrorStore

+ 84
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/fullscreen/fullscreen.vue View File

@@ -0,0 +1,84 @@
<template>
<div v-if="showFullScreenBtn" class="full-screen-btn-con">
<Tooltip :content="value ? '退出全屏' : '全屏'" placement="bottom">
<Icon @click.native="handleChange" :type="value ? 'md-contract' : 'md-expand'" :size="23"></Icon>
</Tooltip>
</div>
</template>

<script>
export default {
name: 'Fullscreen',
computed: {
showFullScreenBtn () {
return window.navigator.userAgent.indexOf('MSIE') < 0
}
},
props: {
value: {
type: Boolean,
default: false
}
},
methods: {
handleFullscreen () {
let main = document.body
if (this.value) {
if (document.exitFullscreen) {
document.exitFullscreen()
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen()
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen()
} else if (document.msExitFullscreen) {
document.msExitFullscreen()
}
} else {
if (main.requestFullscreen) {
main.requestFullscreen()
} else if (main.mozRequestFullScreen) {
main.mozRequestFullScreen()
} else if (main.webkitRequestFullScreen) {
main.webkitRequestFullScreen()
} else if (main.msRequestFullscreen) {
main.msRequestFullscreen()
}
}
},
handleChange () {
this.handleFullscreen()
}
},
mounted () {
let isFullscreen = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen
isFullscreen = !!isFullscreen
document.addEventListener('fullscreenchange', () => {
this.$emit('input', !this.value)
this.$emit('on-change', !this.value)
})
document.addEventListener('mozfullscreenchange', () => {
this.$emit('input', !this.value)
this.$emit('on-change', !this.value)
})
document.addEventListener('webkitfullscreenchange', () => {
this.$emit('input', !this.value)
this.$emit('on-change', !this.value)
})
document.addEventListener('msfullscreenchange', () => {
this.$emit('input', !this.value)
this.$emit('on-change', !this.value)
})
this.$emit('input', isFullscreen)
}
}
</script>

<style lang="less">
.full-screen-btn-con .ivu-tooltip-rel{
height: 64px;
line-height: 56px;
i{
cursor: pointer;
}
}
</style>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/fullscreen/index.js View File

@@ -0,0 +1,2 @@
import Fullscreen from './fullscreen.vue'
export default Fullscreen

+ 4
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/custom-bread-crumb/custom-bread-crumb.less View File

@@ -0,0 +1,4 @@
.custom-bread-crumb{
display: inline-block;
vertical-align: top;
}

+ 46
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/custom-bread-crumb/custom-bread-crumb.vue View File

@@ -0,0 +1,46 @@
<template>
<div class="custom-bread-crumb">
<Breadcrumb :style="{fontSize: `${fontSize}px`}">
<BreadcrumbItem v-for="item in list" :to="item.to" :key="`bread-crumb-${item.name}`">
<common-icon style="margin-right: 4px;" :type="item.icon || ''"/>
{{ showTitle(item) }}
</BreadcrumbItem>
</Breadcrumb>
</div>
</template>
<script>
import { showTitle } from '@/libs/util'
import CommonIcon from '_c/common-icon'
import './custom-bread-crumb.less'
export default {
name: 'customBreadCrumb',
components: {
CommonIcon
},
props: {
list: {
type: Array,
default: () => []
},
fontSize: {
type: Number,
default: 14
},
showIcon: {
type: Boolean,
default: false
}
},
methods: {
showTitle (item) {
return showTitle(item, this)
},
isCustomIcon (iconName) {
return iconName.indexOf('_') === 0
},
getCustomIconName (iconName) {
return iconName.slice(1)
}
}
}
</script>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/custom-bread-crumb/index.js View File

@@ -0,0 +1,2 @@
import customBreadCrumb from './custom-bread-crumb.vue'
export default customBreadCrumb

+ 14
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/header-bar.less View File

@@ -0,0 +1,14 @@
.header-bar{
width: 100%;
height: 100%;
position: relative;
.custom-content-con{
float: right;
height: auto;
padding-right: 20px;
line-height: 64px;
& > *{
float: right;
}
}
}

+ 34
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/header-bar.vue View File

@@ -0,0 +1,34 @@
<template>
<div class="header-bar">
<sider-trigger :collapsed="collapsed" icon="md-menu" @on-change="handleCollpasedChange"></sider-trigger>
<custom-bread-crumb show-icon style="margin-left: 30px;" :list="breadCrumbList"></custom-bread-crumb>
<div class="custom-content-con">
<slot></slot>
</div>
</div>
</template>
<script>
import siderTrigger from './sider-trigger'
import customBreadCrumb from './custom-bread-crumb'
import './header-bar.less'
export default {
name: 'HeaderBar',
components: {
siderTrigger,
customBreadCrumb
},
props: {
collapsed: Boolean
},
computed: {
breadCrumbList () {
return this.$store.state.app.breadCrumbList
}
},
methods: {
handleCollpasedChange (state) {
this.$emit('on-coll-change', state)
}
}
}
</script>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/index.js View File

@@ -0,0 +1,2 @@
import HeaderBar from './header-bar'
export default HeaderBar

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/sider-trigger/index.js View File

@@ -0,0 +1,2 @@
import siderTrigger from './sider-trigger.vue'
export default siderTrigger

+ 21
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/sider-trigger/sider-trigger.less View File

@@ -0,0 +1,21 @@
.trans{
transition: transform .2s ease;
}
@size: 40px;
.sider-trigger-a{
padding: 6px;
width: @size;
height: @size;
display: inline-block;
text-align: center;
color: #5c6b77;
margin-top: 12px;
i{
.trans;
vertical-align: top;
}
&.collapsed i{
transform: rotateZ(90deg);
.trans;
}
}

+ 27
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/header-bar/sider-trigger/sider-trigger.vue View File

@@ -0,0 +1,27 @@
<template>
<a @click="handleChange" type="text" :class="['sider-trigger-a', collapsed ? 'collapsed' : '']"><Icon :type="icon" :size="size" /></a>
</template>
<script>
export default {
name: 'siderTrigger',
props: {
collapsed: Boolean,
icon: {
type: String,
default: 'navicon-round'
},
size: {
type: Number,
default: 26
}
},
methods: {
handleChange () {
this.$emit('on-change', !this.collapsed)
}
}
}
</script>
<style lang="less">
@import './sider-trigger.less';
</style>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/language/index.js View File

@@ -0,0 +1,2 @@
import Language from './language.vue'
export default Language

+ 51
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/language/language.vue View File

@@ -0,0 +1,51 @@
<template>
<div>
<Dropdown trigger="click" @on-click="selectLang">
<a href="javascript:void(0)">
{{ title }}
<Icon :size="18" type="md-arrow-dropdown" />
</a>
<DropdownMenu slot="list">
<DropdownItem v-for="(value, key) in localList" :name="key" :key="`lang-${key}`">{{ value }}</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</template>

<script>
export default {
name: 'Language',
props: {
lang: String
},
data () {
return {
langList: {
'zh-CN': '语言',
'zh-TW': '語言',
'en-US': 'Lang'
},
localList: {
'zh-CN': '中文简体',
'zh-TW': '中文繁体',
'en-US': 'English'
}
}
},
watch: {
lang (lang) {
this.$i18n.locale = lang
}
},
computed: {
title () {
return this.langList[this.lang]
}
},
methods: {
selectLang (name) {
this.$emit('on-lang-change', name)
}
}
}
</script>

+ 51
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/collapsed-menu.vue View File

@@ -0,0 +1,51 @@
<template>
<Dropdown ref="dropdown" @on-click="handleClick" :class="hideTitle ? '' : 'collased-menu-dropdown'" :transfer="hideTitle" :placement="placement">
<a class="drop-menu-a" type="text" @mouseover="handleMousemove($event, children)" :style="{textAlign: !hideTitle ? 'left' : ''}"><common-icon :size="rootIconSize" :color="textColor" :type="parentItem.icon"/><span class="menu-title" v-if="!hideTitle">{{ showTitle(parentItem) }}</span><Icon style="float: right;" v-if="!hideTitle" type="ios-arrow-forward" :size="16"/></a>
<DropdownMenu ref="dropdown" slot="list">
<template v-for="child in children">
<collapsed-menu v-if="showChildren(child)" :icon-size="iconSize" :parent-item="child" :key="`drop-${child.name}`"></collapsed-menu>
<DropdownItem v-else :key="`drop-${child.name}`" :name="child.name"><common-icon :size="iconSize" :type="child.icon"/><span class="menu-title">{{ showTitle(child) }}</span></DropdownItem>
</template>
</DropdownMenu>
</Dropdown>
</template>
<script>
import mixin from './mixin'
import itemMixin from './item-mixin'
import { findNodeUpperByClasses } from '@/libs/util'

export default {
name: 'CollapsedMenu',
mixins: [ mixin, itemMixin ],
props: {
hideTitle: {
type: Boolean,
default: false
},
rootIconSize: {
type: Number,
default: 16
}
},
data () {
return {
placement: 'right-end'
}
},
methods: {
handleClick (name) {
this.$emit('on-click', name)
},
handleMousemove (event, children) {
const { pageY } = event
const height = children.length * 38
const isOverflow = pageY + height < window.innerHeight
this.placement = isOverflow ? 'right-start' : 'right-end'
}
},
mounted () {
let dropdown = findNodeUpperByClasses(this.$refs.dropdown.$el, ['ivu-select-dropdown', 'ivu-dropdown-transfer'])
if (dropdown) dropdown.style.overflow = 'visible'
}
}
</script>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/index.js View File

@@ -0,0 +1,2 @@
import SideMenu from './side-menu.vue'
export default SideMenu

+ 21
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/item-mixin.js View File

@@ -0,0 +1,21 @@
export default {
props: {
parentItem: {
type: Object,
default: () => {}
},
theme: String,
iconSize: Number
},
computed: {
parentName () {
return this.parentItem.name
},
children () {
return this.parentItem.children
},
textColor () {
return this.theme === 'dark' ? '#fff' : '#495060'
}
}
}

+ 18
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/mixin.js View File

@@ -0,0 +1,18 @@
import CommonIcon from '_c/common-icon'
import { showTitle } from '@/libs/util'
export default {
components: {
CommonIcon
},
methods: {
showTitle (item) {
return showTitle(item, this)
},
showChildren (item) {
return item.children && (item.children.length > 1 || (item.meta && item.meta.showAlways))
},
getNameOrHref (item, children0) {
return item.href ? `isTurnByHref_${item.href}` : (children0 ? item.children[0].name : item.name)
}
}
}

+ 26
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/side-menu-item.vue View File

@@ -0,0 +1,26 @@
<template>
<Submenu :name="`${parentName}`">
<template slot="title">
<common-icon :type="parentItem.icon || ''"/>
<span>{{ showTitle(parentItem) }}</span>
</template>
<template v-for="item in children">
<template v-if="item.children && item.children.length === 1">
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="getNameOrHref(item, true)" :key="`menu-${item.children[0].name}`"><common-icon :type="item.children[0].icon || ''"/><span>{{ showTitle(item.children[0]) }}</span></menu-item>
</template>
<template v-else>
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="getNameOrHref(item)" :key="`menu-${item.name}`"><common-icon :type="item.icon || ''"/><span>{{ showTitle(item) }}</span></menu-item>
</template>
</template>
</Submenu>
</template>
<script>
import mixin from './mixin'
import itemMixin from './item-mixin'
export default {
name: 'SideMenuItem',
mixins: [ mixin, itemMixin ]
}
</script>

+ 40
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/side-menu.less View File

@@ -0,0 +1,40 @@
.side-menu-wrapper{
user-select: none;
.menu-collapsed{
padding-top: 10px;

.ivu-dropdown{
width: 100%;
.ivu-dropdown-rel a{
width: 100%;
}
}
.ivu-tooltip{
width: 100%;
.ivu-tooltip-rel{
width: 100%;
}
.ivu-tooltip-popper .ivu-tooltip-content{
.ivu-tooltip-arrow{
border-right-color: #fff;
}
.ivu-tooltip-inner{
background: #fff;
color: #495060;
}
}
}


}
a.drop-menu-a{
display: inline-block;
padding: 6px 15px;
width: 100%;
text-align: center;
color: #495060;
}
}
.menu-title{
padding-left: 6px;
}

+ 114
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/side-menu/side-menu.vue View File

@@ -0,0 +1,114 @@
<template>
<div class="side-menu-wrapper">
<slot></slot>
<Menu ref="menu" v-show="!collapsed" :active-name="activeName" :open-names="openedNames" :accordion="accordion" :theme="theme" width="auto" @on-select="handleSelect">
<template v-for="item in menuList">
<template v-if="item.children && item.children.length === 1">
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="getNameOrHref(item, true)" :key="`menu-${item.children[0].name}`"><common-icon :type="item.children[0].icon || ''"/><span>{{ showTitle(item.children[0]) }}</span></menu-item>
</template>
<template v-else>
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="getNameOrHref(item)" :key="`menu-${item.name}`"><common-icon :type="item.icon || ''"/><span>{{ showTitle(item) }}</span></menu-item>
</template>
</template>
</Menu>
<div class="menu-collapsed" v-show="collapsed" :list="menuList">
<template v-for="item in menuList">
<collapsed-menu v-if="item.children && item.children.length > 1" @on-click="handleSelect" hide-title :root-icon-size="rootIconSize" :icon-size="iconSize" :theme="theme" :parent-item="item" :key="`drop-menu-${item.name}`"></collapsed-menu>
<Tooltip transfer v-else :content="showTitle(item.children && item.children[0] ? item.children[0] : item)" placement="right" :key="`drop-menu-${item.name}`">
<a @click="handleSelect(getNameOrHref(item, true))" class="drop-menu-a" :style="{textAlign: 'center'}"><common-icon :size="rootIconSize" :color="textColor" :type="item.icon || (item.children && item.children[0].icon)"/></a>
</Tooltip>
</template>
</div>
</div>
</template>
<script>
import SideMenuItem from './side-menu-item.vue'
import CollapsedMenu from './collapsed-menu.vue'
import { getUnion } from '@/libs/tools'
import mixin from './mixin'

export default {
name: 'SideMenu',
mixins: [ mixin ],
components: {
SideMenuItem,
CollapsedMenu
},
props: {
menuList: {
type: Array,
default () {
return []
}
},
collapsed: {
type: Boolean
},
theme: {
type: String,
default: 'dark'
},
rootIconSize: {
type: Number,
default: 20
},
iconSize: {
type: Number,
default: 16
},
accordion: Boolean,
activeName: {
type: String,
default: ''
},
openNames: {
type: Array,
default: () => []
}
},
data () {
return {
openedNames: []
}
},
methods: {
handleSelect (name) {
this.$emit('on-select', name)
},
getOpenedNamesByActiveName (name) {
return this.$route.matched.map(item => item.name).filter(item => item !== name)
},
updateOpenName (name) {
if (name === this.$config.homeName) this.openedNames = []
else this.openedNames = this.getOpenedNamesByActiveName(name)
}
},
computed: {
textColor () {
return this.theme === 'dark' ? '#fff' : '#495060'
}
},
watch: {
activeName (name) {
if (this.accordion) this.openedNames = this.getOpenedNamesByActiveName(name)
else this.openedNames = getUnion(this.openedNames, this.getOpenedNamesByActiveName(name))
},
openNames (newNames) {
this.openedNames = newNames
},
openedNames () {
this.$nextTick(() => {
this.$refs.menu.updateOpened()
})
}
},
mounted () {
this.openedNames = getUnion(this.openedNames, this.getOpenedNamesByActiveName(name))
}
}
</script>
<style lang="less">
@import './side-menu.less';
</style>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/tags-nav/index.js View File

@@ -0,0 +1,2 @@
import TagsNav from './tags-nav.vue'
export default TagsNav

+ 87
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/tags-nav/tags-nav.less View File

@@ -0,0 +1,87 @@
.no-select{
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.size{
width: 100%;
height: 100%;
}
.tags-nav{
position: relative;
border-top: 1px solid #F0F0F0;
border-bottom: 1px solid #F0F0F0;
.no-select;
.size;
.close-con{
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 32px;
background: #fff;
text-align: center;
z-index: 10;
}
.btn-con{
position: absolute;
top: 0px;
height: 100%;
background: #fff;
padding-top: 3px;
z-index: 10;
button{
padding: 6px 4px;
line-height: 14px;
text-align: center;
}
&.left-btn{
left: 0px;
}
&.right-btn{
right: 32px;
border-right: 1px solid #F0F0F0;
}
}
.scroll-outer{
position: absolute;
left: 28px;
right: 61px;
top: 0;
bottom: 0;
box-shadow: 0px 0 3px 2px rgba(100,100,100,.1) inset;
.scroll-body{
height: ~"calc(100% - 1px)";
display: inline-block;
padding: 1px 4px 0;
position: absolute;
overflow: visible;
white-space: nowrap;
transition: left .3s ease;
.ivu-tag-dot-inner{
transition: background .2s ease;
}
}
}
.contextmenu {
position: absolute;
margin: 0;
padding: 5px 0;
background: #fff;
z-index: 100;
list-style-type: none;
border-radius: 4px;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
li {
margin: 0;
padding: 5px 15px;
cursor: pointer;
&:hover {
background: #eee;
}
}
}
}

+ 209
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/tags-nav/tags-nav.vue View File

@@ -0,0 +1,209 @@
<template>
<div class="tags-nav">
<div class="close-con">
<Dropdown transfer @on-click="handleTagsOption" style="margin-top:7px;">
<Button size="small" type="text">
<Icon :size="18" type="ios-close-circle-outline" />
</Button>
<DropdownMenu slot="list">
<DropdownItem name="close-all">关闭所有</DropdownItem>
<DropdownItem name="close-others">关闭其他</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
<ul v-show="visible" :style="{left: contextMenuLeft + 'px', top: contextMenuTop + 'px'}" class="contextmenu">
<li v-for="(item, key) of menuList" @click="handleTagsOption(key)" :key="key">{{item}}</li>
</ul>
<div class="btn-con left-btn">
<Button type="text" @click="handleScroll(240)">
<Icon :size="18" type="ios-arrow-back" />
</Button>
</div>
<div class="btn-con right-btn">
<Button type="text" @click="handleScroll(-240)">
<Icon :size="18" type="ios-arrow-forward" />
</Button>
</div>
<div class="scroll-outer" ref="scrollOuter" @DOMMouseScroll="handlescroll" @mousewheel="handlescroll">
<div ref="scrollBody" class="scroll-body" :style="{left: tagBodyLeft + 'px'}">
<transition-group name="taglist-moving-animation">
<Tag
type="dot"
v-for="(item, index) in list"
ref="tagsPageOpened"
:key="`tag-nav-${index}`"
:name="item.name"
:data-route-item="item"
@on-close="handleClose(item)"
@click.native="handleClick(item)"
:closable="item.name !== $config.homeName"
:color="isCurrentTag(item) ? 'primary' : 'default'"
@contextmenu.prevent.native="contextMenu(item, $event)"
>{{ showTitleInside(item) }}</Tag>
</transition-group>
</div>
</div>
</div>
</template>

<script>
import { showTitle, routeEqual } from '@/libs/util'
import beforeClose from '@/router/before-close'
export default {
name: 'TagsNav',
props: {
value: Object,
list: {
type: Array,
default () {
return []
}
}
},
data () {
return {
tagBodyLeft: 0,
rightOffset: 40,
outerPadding: 4,
contextMenuLeft: 0,
contextMenuTop: 0,
visible: false,
menuList: {
others: '关闭其他',
all: '关闭所有'
}
}
},
computed: {
currentRouteObj () {
const { name, params, query } = this.value
return { name, params, query }
}
},
methods: {
handlescroll (e) {
var type = e.type
let delta = 0
if (type === 'DOMMouseScroll' || type === 'mousewheel') {
delta = (e.wheelDelta) ? e.wheelDelta : -(e.detail || 0) * 40
}
this.handleScroll(delta)
},
handleScroll (offset) {
const outerWidth = this.$refs.scrollOuter.offsetWidth
const bodyWidth = this.$refs.scrollBody.offsetWidth
if (offset > 0) {
this.tagBodyLeft = Math.min(0, this.tagBodyLeft + offset)
} else {
if (outerWidth < bodyWidth) {
if (this.tagBodyLeft < -(bodyWidth - outerWidth)) {
this.tagBodyLeft = this.tagBodyLeft
} else {
this.tagBodyLeft = Math.max(this.tagBodyLeft + offset, outerWidth - bodyWidth)
}
} else {
this.tagBodyLeft = 0
}
}
},
handleTagsOption (type) {
if (type.includes('all')) {
// 关闭所有,除了home
let res = this.list.filter(item => item.name === this.$config.homeName)
this.$emit('on-close', res, 'all')
} else if (type.includes('others')) {
// 关闭除当前页和home页的其他页
let res = this.list.filter(item => routeEqual(this.currentRouteObj, item) || item.name === this.$config.homeName)
this.$emit('on-close', res, 'others', this.currentRouteObj)
setTimeout(() => {
this.getTagElementByName(this.currentRouteObj.name)
}, 100)
}
},
handleClose (current) {
if (current.meta && current.meta.beforeCloseName && current.meta.beforeCloseName in beforeClose) {
new Promise(beforeClose[current.meta.beforeCloseName]).then(close => {
if (close) {
this.close(current)
}
})
} else {
this.close(current)
}
},
close (route) {
let res = this.list.filter(item => !routeEqual(route, item))
this.$emit('on-close', res, undefined, route)
},
handleClick (item) {
this.$emit('input', item)
},
showTitleInside (item) {
return showTitle(item, this)
},
isCurrentTag (item) {
return routeEqual(this.currentRouteObj, item)
},
moveToView (tag) {
const outerWidth = this.$refs.scrollOuter.offsetWidth
const bodyWidth = this.$refs.scrollBody.offsetWidth
if (bodyWidth < outerWidth) {
this.tagBodyLeft = 0
} else if (tag.offsetLeft < -this.tagBodyLeft) {
// 标签在可视区域左侧
this.tagBodyLeft = -tag.offsetLeft + this.outerPadding
} else if (tag.offsetLeft > -this.tagBodyLeft && tag.offsetLeft + tag.offsetWidth < -this.tagBodyLeft + outerWidth) {
// 标签在可视区域
this.tagBodyLeft = Math.min(0, outerWidth - tag.offsetWidth - tag.offsetLeft - this.outerPadding)
} else {
// 标签在可视区域右侧
this.tagBodyLeft = -(tag.offsetLeft - (outerWidth - this.outerPadding - tag.offsetWidth))
}
},
getTagElementByName (route) {
this.$nextTick(() => {
this.refsTag = this.$refs.tagsPageOpened
this.refsTag.forEach((item, index) => {
if (routeEqual(route, item.$attrs['data-route-item'])) {
let tag = this.refsTag[index].$el
this.moveToView(tag)
}
})
})
},
contextMenu (item, e) {
if (item.name === this.$config.homeName) {
return
}
this.visible = true
const offsetLeft = this.$el.getBoundingClientRect().left
this.contextMenuLeft = e.clientX - offsetLeft + 10
this.contextMenuTop = e.clientY - 64
},
closeMenu () {
this.visible = false
}
},
watch: {
'$route' (to) {
this.getTagElementByName(to)
},
visible (value) {
if (value) {
document.body.addEventListener('click', this.closeMenu)
} else {
document.body.removeEventListener('click', this.closeMenu)
}
}
},
mounted () {
setTimeout(() => {
this.getTagElementByName(this.$route)
}, 200)
}
}
</script>

<style lang="less">
@import './tags-nav.less';
</style>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/user/index.js View File

@@ -0,0 +1,2 @@
import User from './user.vue'
export default User

+ 12
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/user/user.less View File

@@ -0,0 +1,12 @@
.user{
&-avator-dropdown{
cursor: pointer;
display: inline-block;
// height: 64px;
vertical-align: middle;
// line-height: 64px;
.ivu-badge-dot{
top: 16px;
}
}
}

+ 59
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/components/user/user.vue View File

@@ -0,0 +1,59 @@
<template>
<div class="user-avator-dropdown">
<Dropdown @on-click="handleClick">
<Badge :dot="!!messageUnreadCount">
<Avatar :src="userAvator"/>
</Badge>
<Icon :size="18" type="md-arrow-dropdown"></Icon>
<DropdownMenu slot="list">
<DropdownItem name="message">
消息中心<Badge style="margin-left: 10px" :count="messageUnreadCount"></Badge>
</DropdownItem>
<DropdownItem name="logout">退出登录</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</template>

<script>
import './user.less'
import { mapActions } from 'vuex'
export default {
name: 'User',
props: {
userAvator: {
type: String,
default: ''
},
messageUnreadCount: {
type: Number,
default: 0
}
},
methods: {
...mapActions([
'handleLogOut'
]),
logout () {
this.handleLogOut().then(() => {
this.$router.push({
name: 'login'
})
})
},
message () {
this.$router.push({
name: 'message_page'
})
},
handleClick (name) {
switch (name) {
case 'logout': this.logout()
break
case 'message': this.message()
break
}
}
}
}
</script>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/index.js View File

@@ -0,0 +1,2 @@
import Main from './main.vue'
export default Main

+ 75
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/main.less View File

@@ -0,0 +1,75 @@
.main{
.logo-con{
height: 64px;
padding: 10px;
img{
height: 44px;
width: auto;
display: block;
margin: 0 auto;
}
}
.header-con{
background: #fff;
padding: 0 20px;
width: 100%;
}
.main-layout-con{
height: 100%;
overflow: hidden;
}
.main-content-con{
height: ~"calc(100% - 60px)";
overflow: hidden;
}
.tag-nav-wrapper{
padding: 0;
height:40px;
background:#F0F0F0;
}
.content-wrapper{
padding: 18px;
height: ~"calc(100% - 80px)";
overflow: auto;
}
.left-sider{
.ivu-layout-sider-children{
overflow-y: scroll;
margin-right: -18px;
}
}
}
.ivu-menu-item > i{
margin-right: 12px !important;
}
.ivu-menu-submenu > .ivu-menu > .ivu-menu-item > i {
margin-right: 8px !important;
}
.collased-menu-dropdown{
width: 100%;
margin: 0;
line-height: normal;
padding: 7px 0 6px 16px;
clear: both;
font-size: 12px !important;
white-space: nowrap;
list-style: none;
cursor: pointer;
transition: background 0.2s ease-in-out;
&:hover{
background: rgba(100, 100, 100, 0.1);
}
& * {
color: #515a6e;
}
.ivu-menu-item > i{
margin-right: 12px !important;
}
.ivu-menu-submenu > .ivu-menu > .ivu-menu-item > i {
margin-right: 8px !important;
}
}

.ivu-select-dropdown.ivu-dropdown-transfer{
max-height: 400px;
}

+ 183
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/main/main.vue View File

@@ -0,0 +1,183 @@
<template>
<Layout style="height: 100%" class="main">
<Sider hide-trigger collapsible :width="256" :collapsed-width="64" v-model="collapsed" class="left-sider" :style="{overflow: 'hidden'}">
<side-menu accordion ref="sideMenu" :active-name="$route.name" :collapsed="collapsed" @on-select="turnToPage" :menu-list="menuList">
<!-- 需要放在菜单上面的内容,如Logo,写在side-menu标签内部,如下 -->
<div class="logo-con">
<img v-show="!collapsed" :src="maxLogo" key="max-logo" />
<img v-show="collapsed" :src="minLogo" key="min-logo" />
</div>
</side-menu>
</Sider>
<Layout>
<Header class="header-con">
<header-bar :collapsed="collapsed" @on-coll-change="handleCollapsedChange">
<user :message-unread-count="unreadCount" :user-avator="userAvator"/>
<language v-if="$config.useI18n" @on-lang-change="setLocal" style="margin-right: 10px;" :lang="local"/>
<error-store v-if="$config.plugin['error-store'] && $config.plugin['error-store'].showInHeader" :has-read="hasReadErrorPage" :count="errorCount"></error-store>
<fullscreen v-model="isFullscreen" style="margin-right: 10px;"/>
</header-bar>
</Header>
<Content class="main-content-con">
<Layout class="main-layout-con">
<div class="tag-nav-wrapper">
<tags-nav :value="$route" @input="handleClick" :list="tagNavList" @on-close="handleCloseTag"/>
</div>
<Content class="content-wrapper">
<keep-alive :include="cacheList">
<router-view/>
</keep-alive>
<ABackTop :height="100" :bottom="80" :right="50" container=".content-wrapper"></ABackTop>
</Content>
</Layout>
</Content>
</Layout>
</Layout>
</template>
<script>
import SideMenu from './components/side-menu'
import HeaderBar from './components/header-bar'
import TagsNav from './components/tags-nav'
import User from './components/user'
import ABackTop from './components/a-back-top'
import Fullscreen from './components/fullscreen'
import Language from './components/language'
import ErrorStore from './components/error-store'
import { mapMutations, mapActions, mapGetters } from 'vuex'
import { getNewTagList, getNextRoute, routeEqual } from '@/libs/util'
import routers from '@/router/routers'
import minLogo from '@/assets/images/logo-min.jpg'
import maxLogo from '@/assets/images/logo.jpg'
import './main.less'
export default {
name: 'Main',
components: {
SideMenu,
HeaderBar,
Language,
TagsNav,
Fullscreen,
ErrorStore,
User,
ABackTop
},
data () {
return {
collapsed: false,
minLogo,
maxLogo,
isFullscreen: false
}
},
computed: {
...mapGetters([
'errorCount'
]),
tagNavList () {
return this.$store.state.app.tagNavList
},
tagRouter () {
return this.$store.state.app.tagRouter
},
userAvator () {
return this.$store.state.user.avatorImgPath
},
cacheList () {
return ['ParentView', ...this.tagNavList.length ? this.tagNavList.filter(item => !(item.meta && item.meta.notCache)).map(item => item.name) : []]
},
menuList () {
return this.$store.getters.menuList
},
local () {
return this.$store.state.app.local
},
hasReadErrorPage () {
return this.$store.state.app.hasReadErrorPage
},
unreadCount () {
return this.$store.state.user.unreadCount
}
},
methods: {
...mapMutations([
'setBreadCrumb',
'setTagNavList',
'addTag',
'setLocal',
'setHomeRoute'
]),
...mapActions([
'handleLogin',
'getUnreadMessageCount'
]),
turnToPage (route) {
let { name, params, query } = {}
if (typeof route === 'string') name = route
else {
name = route.name
params = route.params
query = route.query
}
if (name.indexOf('isTurnByHref_') > -1) {
window.open(name.split('_')[1])
return
}
this.$router.push({
name,
params,
query
})
},
handleCollapsedChange (state) {
this.collapsed = state
},
handleCloseTag (res, type, route) {
if (type === 'all') {
this.turnToPage(this.$config.homeName)
} else if (routeEqual(this.$route, route)) {
if (type !== 'others') {
const nextRoute = getNextRoute(this.tagNavList, route)
this.$router.push(nextRoute)
}
}
this.setTagNavList(res)
},
handleClick (item) {
this.turnToPage(item)
}
},
watch: {
'$route' (newRoute) {
const { name, query, params, meta } = newRoute
this.addTag({
route: { name, query, params, meta },
type: 'push'
})
this.setBreadCrumb(newRoute)
this.setTagNavList(getNewTagList(this.tagNavList, newRoute))
this.$refs.sideMenu.updateOpenName(newRoute.name)
}
},
mounted () {
/**
* @description 初始化设置面包屑导航和标签导航
*/
this.setTagNavList()
this.setHomeRoute(routers)
this.addTag({
route: this.$store.state.app.homeRoute
})
this.setBreadCrumb(this.$route)
// 设置初始语言
this.setLocal(this.$i18n.locale)
// 如果当前打开页面不在标签栏中,跳到homeName页
if (!this.tagNavList.find(item => item.name === this.$route.name)) {
this.$router.push({
name: this.$config.homeName
})
}
// 获取未读消息条数
this.getUnreadMessageCount()
}
}
</script>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/markdown/index.js View File

@@ -0,0 +1,2 @@
import MarkdownEditor from './markdown.vue'
export default MarkdownEditor

+ 76
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/markdown/markdown.vue View File

@@ -0,0 +1,76 @@
<template>
<div class="markdown-wrapper">
<textarea ref="editor"></textarea>
</div>
</template>

<script>
import Simplemde from 'simplemde'
import 'simplemde/dist/simplemde.min.css'
export default {
name: 'MarkdownEditor',
props: {
value: {
type: String,
default: ''
},
options: {
type: Object,
default: () => {
return {}
}
},
localCache: {
type: Boolean,
default: true
}
},
data () {
return {
editor: null
}
},
methods: {
addEvents () {
this.editor.codemirror.on('change', () => {
let value = this.editor.value()
if (this.localCache) localStorage.markdownContent = value
this.$emit('input', value)
this.$emit('on-change', value)
})
this.editor.codemirror.on('focus', () => {
this.$emit('on-focus', this.editor.value())
})
this.editor.codemirror.on('blur', () => {
this.$emit('on-blur', this.editor.value())
})
}
},
mounted () {
this.editor = new Simplemde(Object.assign(this.options, {
element: this.$refs.editor
}))
/**
* 事件列表为Codemirror编辑器的事件,更多事件类型,请参考:
* https://codemirror.net/doc/manual.html#events
*/
this.addEvents()
let content = localStorage.markdownContent
if (content) this.editor.value(content)
}
}
</script>

<style lang="less">
.markdown-wrapper{
.editor-toolbar.fullscreen{
z-index: 9999;
}
.CodeMirror-fullscreen{
z-index: 9999;
}
.CodeMirror-fullscreen ~ .editor-preview-side{
z-index: 9999;
}
}
</style>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/parent-view/index.js View File

@@ -0,0 +1,2 @@
import ParentView from './parent-view.vue'
export default ParentView

+ 20
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/parent-view/parent-view.vue View File

@@ -0,0 +1,20 @@
<template>
<keep-alive :exclude="notCacheName">
<router-view ref="child"/>
</keep-alive>
</template>
<script>
export default {
name: 'ParentView',
data () {
return {
cacheChaildName: ''
}
},
computed: {
notCacheName () {
return (this.$route.meta && this.$route.meta.notCache) ? this.$route.name : ''
}
}
}
</script>

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/paste-editor/index.js View File

@@ -0,0 +1,2 @@
import PasteEditor from './paste-editor.vue'
export default PasteEditor

+ 26
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/paste-editor/paste-editor.less View File

@@ -0,0 +1,26 @@
.paste-editor-wrapper{
width: 100%;
height: 100%;
border: 1px dashed gainsboro;
textarea.textarea-el{
width: 100%;
height: 100%;
}
.CodeMirror{
height: 100%;
padding: 0;
.CodeMirror-code div .CodeMirror-line > span > span.cm-tab{
&::after{
content: '→';
color: #BFBFBF;
}
}
}
.first-row{
font-weight: 700;
font-size: 14px;
}
.incorrect-row{
background: #F5CBD1;
}
}

+ 115
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/paste-editor/paste-editor.vue View File

@@ -0,0 +1,115 @@
<template>
<div class="paste-editor-wrapper">
<textarea ref="codemirror" class="textarea-el"></textarea>
</div>
</template>
<script>
import CodeMirror from 'codemirror'
import 'codemirror/lib/codemirror.css'
import { forEach } from '@/libs/tools'
import createPlaceholder from './plugins/placeholder'
export default {
name: 'PasteEditor',
props: {
value: Array,
pasteData: {
type: String,
default: ''
},
placeholder: {
type: String,
default: '从网页或其他应用软件复制表格数据,粘贴到这里 。默认第一行是表头,使用回车键添加新行,使用Tab键区分列。'
}
},
data () {
return {
pasteDataArr: [],
rowArrLength: 0,
editor: null
}
},
watch: {
pasteData (val) {
if (val === '') {
this.editor.setValue('')
}
}
},
computed: {
rowNum () {
return this.pasteDataArr.length
},
colNum () {
return this.pasteDataArr[0] ? this.pasteDataArr[0].length : 0
}
},
methods: {
handleKeyup (e) {
this.handleAreaData()
},
/**
* @description 处理粘贴操作
*/
handleContentChanged (content) {
let pasteData = content.trim()
this.$emit('on-content-change', pasteData)
let rows = pasteData.split((/[\n\u0085\u2028\u2029]|\r\n?/g)).map(row => {
return row.split('\t')
})
if (content === '') rows = []
this.pasteDataArr = rows
this.clearLineClass()
this.checkColNumInEveryRow()
this.$emit('input', this.pasteDataArr)
},
/**
* @description 检查除第一行的每一行列数是否与第一行相同
*/
checkColNumInEveryRow () {
let i = 0
const len = this.rowNum
if (len === 0) return
while (++i < len) {
let item = this.pasteDataArr[i]
if (item.length !== this.colNum && (!(i === len - 1 && item.length === 1 && item[0] === '') || i !== len - 1)) {
this.markIncorrectRow(i)
this.$emit('on-error', i)
return false
}
}
this.$emit('on-success', this.pasteDataArr)
return true
},
/**
* @description 标记不符合格式的一行
*/
markIncorrectRow (index) {
this.editor.addLineClass(index, 'text', 'incorrect-row')
},
/**
* @description 标记不符合格式的一行
*/
clearLineClass () {
forEach(this.pasteDataArr, (item, index) => {
this.editor.removeLineClass(index, 'text', 'incorrect-row')
})
}
},
mounted () {
createPlaceholder(CodeMirror)
this.editor = CodeMirror.fromTextArea(this.$refs.codemirror, {
lineNumbers: true,
tabSize: 1,
lineWrapping: true,
placeholder: this.placeholder
})
this.editor.on('change', (editor) => {
this.handleContentChanged(editor.getValue())
})
this.editor.addLineClass(0, 'text', 'first-row')
}
}
</script>
<style lang="less">
@import './paste-editor.less';
</style>

+ 58
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/paste-editor/plugins/placeholder.js View File

@@ -0,0 +1,58 @@
export default (codemirror) => {
(function (mod) {
mod(codemirror)
})(function (CodeMirror) {
CodeMirror.defineOption('placeholder', '', function (cm, val, old) {
var prev = old && old !== CodeMirror.Init
if (val && !prev) {
cm.on('blur', onBlur)
cm.on('change', onChange)
cm.on('swapDoc', onChange)
onChange(cm)
} else if (!val && prev) {
cm.off('blur', onBlur)
cm.off('change', onChange)
cm.off('swapDoc', onChange)
clearPlaceholder(cm)
var wrapper = cm.getWrapperElement()
wrapper.className = wrapper.className.replace(' CodeMirror-empty', '')
}

if (val && !cm.hasFocus()) onBlur(cm)
})

function clearPlaceholder (cm) {
if (cm.state.placeholder) {
cm.state.placeholder.parentNode.removeChild(cm.state.placeholder)
cm.state.placeholder = null
}
}
function setPlaceholder (cm) {
clearPlaceholder(cm)
var elt = cm.state.placeholder = document.createElement('pre')
elt.style.cssText = 'height: 0; overflow: visible; color: #80848f;'
elt.style.direction = cm.getOption('direction')
elt.className = 'CodeMirror-placeholder'
var placeHolder = cm.getOption('placeholder')
if (typeof placeHolder === 'string') placeHolder = document.createTextNode(placeHolder)
elt.appendChild(placeHolder)
cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild)
}

function onBlur (cm) {
if (isEmpty(cm)) setPlaceholder(cm)
}
function onChange (cm) {
let wrapper = cm.getWrapperElement()
let empty = isEmpty(cm)
wrapper.className = wrapper.className.replace(' CodeMirror-empty', '') + (empty ? ' CodeMirror-empty' : '')

if (empty) setPlaceholder(cm)
else clearPlaceholder(cm)
}

function isEmpty (cm) {
return (cm.lineCount() === 1) && (cm.getLine(0) === '')
}
})
}

+ 2
- 0
src/JT808.DotNetty.Dashbord.UI/src/components/split-pane/index.js View File

@@ -0,0 +1,2 @@
import Split from './split.vue'
export default Split

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save