├── public ├── resources │ ├── aliplayer-min.css │ └── tween.min.js ├── favicon.ico ├── draco │ ├── draco_decoder.wasm │ ├── gltf │ │ └── draco_decoder.wasm │ └── README.md └── index.html ├── .npmrc ├── .eslintignore ├── examples └── dancing.fbx ├── guide └── assets │ ├── bg │ ├── bg.gif │ ├── head.png │ ├── pageBg.png │ ├── bg_title.png │ ├── bg_title1.png │ ├── bg_warning.png │ ├── experience-back.png │ ├── experience-box-bg.png │ ├── experience-left.png │ └── experience-left-hover.png │ └── images │ └── topo │ └── screen │ └── card.png ├── src ├── assets │ ├── bg │ │ ├── bg.gif │ │ ├── card.png │ │ ├── avator.png │ │ ├── pageBg.png │ │ ├── bg_title.png │ │ ├── bg_title1.png │ │ ├── pageBg1.png │ │ ├── realtime.png │ │ ├── background.jpg │ │ ├── bg_warning.png │ │ ├── screen_select.png │ │ ├── experience-back.png │ │ ├── experience-left.png │ │ ├── screen_unselect.png │ │ ├── experience-box-bg.png │ │ └── experience-left-hover.png │ ├── static │ │ ├── card.png │ │ └── background.jpg │ └── 404_images │ │ ├── 404.png │ │ └── 404_cloud.png ├── components │ ├── model3d │ │ ├── draco │ │ │ ├── draco_decoder.wasm │ │ │ ├── gltf │ │ │ │ └── draco_decoder.wasm │ │ │ └── README.md │ │ ├── utils │ │ │ └── load.js │ │ ├── dgiot-model-demo2.vue │ │ └── dgiot-model-fbx.vue │ ├── Hamburger │ │ └── index.vue │ ├── SvgIcon │ │ └── index.vue │ ├── Amis │ │ └── index.vue │ └── Breadcrumb │ │ └── index.vue ├── views │ ├── nested │ │ ├── menu2 │ │ │ └── index.vue │ │ └── menu1 │ │ │ ├── menu1-3 │ │ │ └── index.vue │ │ │ ├── index.vue │ │ │ ├── menu1-2 │ │ │ ├── menu1-2-1 │ │ │ │ └── index.vue │ │ │ ├── menu1-2-2 │ │ │ │ └── index.vue │ │ │ └── index.vue │ │ │ └── menu1-1 │ │ │ └── index.vue │ ├── amis │ │ ├── index.vue │ │ └── form.json │ ├── dashboard │ │ └── component │ │ │ ├── profile │ │ │ └── DgiotSwitch1.vue │ │ │ ├── commom │ │ │ ├── RealTime.vue │ │ │ └── RealCard.vue │ │ │ ├── ScreenSgmap.vue │ │ │ ├── TopoCard.vue │ │ │ ├── notification │ │ │ └── DgiotNotification1.vue │ │ │ └── TopoPie.vue │ ├── CloudOc │ │ └── AmisPage │ │ │ └── style │ │ │ └── themeBlack.css │ ├── tree │ │ └── index.vue │ └── Device │ │ ├── DeviceKonva │ │ └── index.vue │ │ └── DeviceRealCard │ │ └── index.vue ├── layout │ ├── components │ │ ├── index.js │ │ ├── Sidebar │ │ │ ├── FixiOSBug.js │ │ │ ├── Link.vue │ │ │ ├── Item.vue │ │ │ ├── Logo.vue │ │ │ ├── index.vue │ │ │ └── SidebarItem.vue │ │ └── AppMain.vue │ ├── mixin │ │ └── ResizeHandler.js │ └── index.vue ├── App.vue ├── api │ ├── Navigation │ │ └── index.js │ ├── table.js │ ├── Maintenance │ │ └── index.js │ ├── Upload │ │ └── index.js │ ├── Opc │ │ └── index.js │ ├── Relation │ │ └── index.js │ ├── Product │ │ └── index.js │ ├── Notification │ │ └── index.js │ ├── user.js │ ├── View │ │ └── index.js │ ├── Device │ │ └── index.js │ ├── Evidence │ │ └── index.js │ ├── Dashboard │ │ └── index.js │ └── User │ │ └── index.js ├── icons │ ├── svg │ │ ├── link.svg │ │ ├── user.svg │ │ ├── example.svg │ │ ├── table.svg │ │ ├── password.svg │ │ ├── stop.svg │ │ ├── deptchange.svg │ │ ├── nested.svg │ │ ├── eye.svg │ │ ├── code.svg │ │ ├── run.svg │ │ ├── eye-open.svg │ │ ├── throw.svg │ │ ├── phone.svg │ │ ├── shooting.svg │ │ ├── material.svg │ │ ├── tree.svg │ │ ├── organode.svg │ │ ├── dashboard.svg │ │ ├── organization.svg │ │ ├── fight.svg │ │ ├── form.svg │ │ └── jump.svg │ ├── index.js │ └── svgo.yml ├── utils │ ├── get-page-title.js │ ├── auth.js │ ├── validate.js │ ├── request.js │ ├── instance.js │ └── index.js ├── settings.js ├── store │ ├── index.js │ ├── getters.js │ └── modules │ │ ├── settings.js │ │ ├── dashboard.js │ │ └── app.js ├── styles │ ├── mixin.scss │ ├── variables.scss │ ├── element-ui.scss │ ├── transition.scss │ ├── index.scss │ └── sidebar.scss ├── i18n │ └── index.js ├── main.js ├── router │ └── index.js └── permission.js ├── tests └── unit │ ├── .eslintrc.js │ ├── utils │ ├── param2Obj.spec.js │ ├── validate.spec.js │ ├── formatTime.spec.js │ └── parseTime.spec.js │ └── components │ ├── Hamburger.spec.js │ ├── SvgIcon.spec.js │ └── Breadcrumb.spec.js ├── .travis.yml ├── .env.production ├── .env.staging ├── .env.development ├── jsconfig.json ├── postcss.config.js ├── .gitignore ├── .editorconfig ├── babel.config.js ├── mock ├── utils.js ├── table.js ├── index.js ├── user.js └── mock-server.js ├── jest.config.js ├── LICENSE ├── README.md ├── README-zh.md ├── package.json ├── .eslintrc.js └── vue.config.js /public/resources/aliplayer-min.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmmirror.com/ 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | src/assets 3 | public 4 | dist 5 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /examples/dancing.fbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/examples/dancing.fbx -------------------------------------------------------------------------------- /guide/assets/bg/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/guide/assets/bg/bg.gif -------------------------------------------------------------------------------- /src/assets/bg/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/bg.gif -------------------------------------------------------------------------------- /src/assets/bg/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/card.png -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /guide/assets/bg/head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/guide/assets/bg/head.png -------------------------------------------------------------------------------- /src/assets/bg/avator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/avator.png -------------------------------------------------------------------------------- /src/assets/bg/pageBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/pageBg.png -------------------------------------------------------------------------------- /guide/assets/bg/pageBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/guide/assets/bg/pageBg.png -------------------------------------------------------------------------------- /src/assets/bg/bg_title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/bg_title.png -------------------------------------------------------------------------------- /src/assets/bg/bg_title1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/bg_title1.png -------------------------------------------------------------------------------- /src/assets/bg/pageBg1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/pageBg1.png -------------------------------------------------------------------------------- /src/assets/bg/realtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/realtime.png -------------------------------------------------------------------------------- /src/assets/static/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/static/card.png -------------------------------------------------------------------------------- /guide/assets/bg/bg_title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/guide/assets/bg/bg_title.png -------------------------------------------------------------------------------- /guide/assets/bg/bg_title1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/guide/assets/bg/bg_title1.png -------------------------------------------------------------------------------- /src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/404_images/404.png -------------------------------------------------------------------------------- /src/assets/bg/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/background.jpg -------------------------------------------------------------------------------- /src/assets/bg/bg_warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/bg_warning.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 10 3 | script: npm run test 4 | notifications: 5 | email: false 6 | -------------------------------------------------------------------------------- /guide/assets/bg/bg_warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/guide/assets/bg/bg_warning.png -------------------------------------------------------------------------------- /public/draco/draco_decoder.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/public/draco/draco_decoder.wasm -------------------------------------------------------------------------------- /src/assets/bg/screen_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/screen_select.png -------------------------------------------------------------------------------- /src/assets/static/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/static/background.jpg -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'production' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/iotapi' 6 | 7 | -------------------------------------------------------------------------------- /src/assets/bg/experience-back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/experience-back.png -------------------------------------------------------------------------------- /src/assets/bg/experience-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/experience-left.png -------------------------------------------------------------------------------- /src/assets/bg/screen_unselect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/screen_unselect.png -------------------------------------------------------------------------------- /guide/assets/bg/experience-back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/guide/assets/bg/experience-back.png -------------------------------------------------------------------------------- /guide/assets/bg/experience-box-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/guide/assets/bg/experience-box-bg.png -------------------------------------------------------------------------------- /guide/assets/bg/experience-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/guide/assets/bg/experience-left.png -------------------------------------------------------------------------------- /public/draco/gltf/draco_decoder.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/public/draco/gltf/draco_decoder.wasm -------------------------------------------------------------------------------- /src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /src/assets/bg/experience-box-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/experience-box-bg.png -------------------------------------------------------------------------------- /src/assets/bg/experience-left-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/assets/bg/experience-left-hover.png -------------------------------------------------------------------------------- /guide/assets/bg/experience-left-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/guide/assets/bg/experience-left-hover.png -------------------------------------------------------------------------------- /guide/assets/images/topo/screen/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/guide/assets/images/topo/screen/card.png -------------------------------------------------------------------------------- /.env.staging: -------------------------------------------------------------------------------- 1 | NODE_ENV = production 2 | 3 | # just a flag 4 | ENV = 'staging' 5 | 6 | # base api 7 | VUE_APP_BASE_API = '/iotapi' 8 | 9 | -------------------------------------------------------------------------------- /src/components/model3d/draco/draco_decoder.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/components/model3d/draco/draco_decoder.wasm -------------------------------------------------------------------------------- /src/components/model3d/draco/gltf/draco_decoder.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgiot/iotView/HEAD/src/components/model3d/draco/gltf/draco_decoder.wasm -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'development' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/iotapi' 6 | VUE_APP_URL = 'http://dev.dgiotcloud.cn' 7 | -------------------------------------------------------------------------------- /src/views/nested/menu2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/layout/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Navbar } from './Navbar' 2 | export { default as Sidebar } from './Sidebar' 3 | export { default as AppMain } from './AppMain' 4 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/views/amis/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "@/*": ["src/*"] 6 | } 7 | }, 8 | "exclude": ["node_modules", "dist"] 9 | } 10 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-3/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/nested/menu1/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/menu1-2-1/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/menu1-2-2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/api/Navigation/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getNavigation() { 4 | return request({ 5 | url: `/classes/Navigation`, 6 | method: 'get', 7 | }) 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-1/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/api/table.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getList(params) { 4 | return request({ 5 | url: '/vue-admin-template/table/list', 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | 'plugins': { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | 'autoprefixer': {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/api/Maintenance/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getMaintenance(params) { 4 | return request({ 5 | url: `/classes/Maintenance`, 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | tests/**/coverage/ 8 | *.zip 9 | *.rar 10 | release 11 | # Editor directories and files 12 | .idea 13 | .vscode 14 | *.suo 15 | *.ntvs* 16 | *.njsproj 17 | *.sln 18 | -------------------------------------------------------------------------------- /src/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/get-page-title.js: -------------------------------------------------------------------------------- 1 | import defaultSettings from '@/settings' 2 | 3 | const title = defaultSettings.title || 'Amis Vue Template' 4 | 5 | export default function getPageTitle(pageTitle) { 6 | if (pageTitle) { 7 | return `${pageTitle} - ${title}` 8 | } 9 | return `${title}` 10 | } 11 | -------------------------------------------------------------------------------- /src/api/Upload/index.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export async function uppyUpload( 4 | url, 5 | params, 6 | headers = { 7 | Accept: 'application/json', 8 | 'Content-Type': 'application/json; charset=utf-8', 9 | } 10 | ) { 11 | return axios.post(url, params, headers) 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | insert_final_newline = false 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /src/api/Opc/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export async function postHead(data) { 4 | return request({ 5 | url: '/head', 6 | method: 'post', 7 | headers: { 8 | accept: 'application/json', 9 | 'Content-Type': 'application/json', 10 | }, 11 | data: data, 12 | }) 13 | } -------------------------------------------------------------------------------- /src/icons/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import SvgIcon from '@/components/SvgIcon'// svg component 3 | 4 | // register globally 5 | Vue.component('svg-icon', SvgIcon) 6 | 7 | const req = require.context('./svg', false, /\.svg$/) 8 | const requireAll = requireContext => requireContext.keys().map(requireContext) 9 | requireAll(req) 10 | -------------------------------------------------------------------------------- /src/api/Relation/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function queryRelation(params) { 4 | return request({ 5 | url: `/relation`, 6 | method: 'get', 7 | headers: { 8 | accept: 'application/json', 9 | 'Content-Type': 'application/json', 10 | }, 11 | params: params, 12 | }) 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/settings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | title: '物联网平台', 4 | 5 | /** 6 | * @type {boolean} true | false 7 | * @description Whether fix the header 8 | */ 9 | fixedHeader: false, 10 | 11 | /** 12 | * @type {boolean} true | false 13 | * @description Whether show the logo in sidebar 14 | */ 15 | sidebarLogo: false 16 | } 17 | -------------------------------------------------------------------------------- /src/icons/svgo.yml: -------------------------------------------------------------------------------- 1 | # replace default config 2 | 3 | # multipass: true 4 | # full: true 5 | 6 | plugins: 7 | 8 | # - name 9 | # 10 | # or: 11 | # - name: false 12 | # - name: true 13 | # 14 | # or: 15 | # - name: 16 | # param1: 1 17 | # param2: 2 18 | 19 | - removeAttrs: 20 | attrs: 21 | - 'fill' 22 | - 'fill-rule' 23 | -------------------------------------------------------------------------------- /src/api/Product/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function queryProduct(params) { 4 | return request({ 5 | url: `/classes/Product`, 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | 11 | // export function getDevice(objectId) { 12 | // return request({ 13 | // url: `/classes/Device/${objectId}`, 14 | // method: 'get', 15 | // }) 16 | // } 17 | -------------------------------------------------------------------------------- /src/api/Notification/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function queryNotification(params) { 4 | return request({ 5 | url: `/amis/Notification`, 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | 11 | export function getNotification(params) { 12 | return request({ 13 | url: `/classes/Notification`, 14 | method: 'get', 15 | params 16 | }) 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/amis/form.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "表单", 3 | "body": { 4 | "mode": "horizontal", 5 | "type": "form", 6 | "body": [ 7 | { 8 | "type": "input-text", 9 | "label": "文本框", 10 | "name": "text", 11 | "size": "md" 12 | }, 13 | { 14 | "type": "input-password", 15 | "label": "密码", 16 | "name": "password", 17 | "size": "md" 18 | } 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/unit/utils/param2Obj.spec.js: -------------------------------------------------------------------------------- 1 | import { param2Obj } from '@/utils/index.js' 2 | describe('Utils:param2Obj', () => { 3 | const url = 'https://github.com/PanJiaChen/vue-element-admin?name=bill&age=29&sex=1&field=dGVzdA==&key=%E6%B5%8B%E8%AF%95' 4 | 5 | it('param2Obj test', () => { 6 | expect(param2Obj(url)).toEqual({ 7 | name: 'bill', 8 | age: '29', 9 | sex: '1', 10 | field: window.btoa('test'), 11 | key: '测试' 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import getters from './getters' 4 | import app from './modules/app' 5 | import settings from './modules/settings' 6 | import user from './modules/user' 7 | import dashboard from './modules/dashboard' 8 | 9 | Vue.use(Vuex) 10 | 11 | const store = new Vuex.Store({ 12 | modules: { 13 | app, 14 | settings, 15 | user, 16 | dashboard, 17 | }, 18 | getters 19 | }) 20 | 21 | export default store 22 | -------------------------------------------------------------------------------- /src/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/auth.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const TokenKey = 'sessionToken' 4 | 5 | export function getToken() { 6 | // return Cookies.get(TokenKey) 7 | return localStorage.getItem(TokenKey) 8 | } 9 | 10 | export function setToken(token) { 11 | // return Cookies.set(TokenKey, token) 12 | return localStorage.setItem(TokenKey, token) 13 | } 14 | 15 | export function removeToken() { 16 | // return Cookies.remove(TokenKey) 17 | return localStorage.removeItem(TokenKey) 18 | } 19 | -------------------------------------------------------------------------------- /src/styles/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix { 2 | &:after { 3 | content: ""; 4 | display: table; 5 | clear: both; 6 | } 7 | } 8 | 9 | @mixin scrollBar { 10 | &::-webkit-scrollbar-track-piece { 11 | background: #d3dce6; 12 | } 13 | 14 | &::-webkit-scrollbar { 15 | width: 6px; 16 | } 17 | 18 | &::-webkit-scrollbar-thumb { 19 | background: #99a9bf; 20 | border-radius: 20px; 21 | } 22 | } 23 | 24 | @mixin relative { 25 | position: relative; 26 | width: 100%; 27 | height: 100%; 28 | } 29 | -------------------------------------------------------------------------------- /src/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/api/user.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function login(data) { 4 | return request({ 5 | url: '/login', 6 | method: 'post', 7 | data, 8 | headers:{ 9 | "Content-Type": "text/plain" 10 | } 11 | }) 12 | } 13 | 14 | // export function getInfo(token) { 15 | // return request({ 16 | // url: '/vue-admin-template/user/info', 17 | // method: 'get', 18 | // params: { token } 19 | // }) 20 | // } 21 | 22 | export function logout() { 23 | return request({ 24 | url: '/vue-admin-template/user/logout', 25 | method: 'post' 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /src/views/dashboard/component/profile/DgiotSwitch1.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 24 | 28 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app 4 | '@vue/cli-plugin-babel/preset' 5 | ], 6 | 'env': { 7 | 'development': { 8 | // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). 9 | // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. 10 | // https://panjiachen.github.io/vue-element-admin-site/guide/advanced/lazy-loading.html 11 | 'plugins': ['dynamic-import-node'] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /mock/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} url 3 | * @returns {Object} 4 | */ 5 | function param2Obj(url) { 6 | const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') 7 | if (!search) { 8 | return {} 9 | } 10 | const obj = {} 11 | const searchArr = search.split('&') 12 | searchArr.forEach(v => { 13 | const index = v.indexOf('=') 14 | if (index !== -1) { 15 | const name = v.substring(0, index) 16 | const val = v.substring(index + 1, v.length) 17 | obj[name] = val 18 | } 19 | }) 20 | return obj 21 | } 22 | 23 | module.exports = { 24 | param2Obj 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/validate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by PanJiaChen on 16/11/18. 3 | */ 4 | 5 | /** 6 | * @param {string} path 7 | * @returns {Boolean} 8 | */ 9 | export function isExternal(path) { 10 | return /^(https?:|mailto:|tel:)/.test(path) 11 | } 12 | 13 | /** 14 | * @param {string} str 15 | * @returns {Boolean} 16 | */ 17 | export function validUsername(str) { 18 | const valid_map = ['admin', 'editor'] 19 | return valid_map.indexOf(str.trim()) >= 0 20 | } 21 | 22 | export function isvalidPhone(phone) { 23 | const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/ 24 | return reg.test(phone) 25 | } -------------------------------------------------------------------------------- /src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/stop.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mock/table.js: -------------------------------------------------------------------------------- 1 | const Mock = require('mockjs') 2 | 3 | const data = Mock.mock({ 4 | 'items|30': [{ 5 | id: '@id', 6 | title: '@sentence(10, 20)', 7 | 'status|1': ['published', 'draft', 'deleted'], 8 | author: 'name', 9 | display_time: '@datetime', 10 | pageviews: '@integer(300, 5000)' 11 | }] 12 | }) 13 | 14 | module.exports = [ 15 | { 16 | url: '/vue-admin-template/table/list', 17 | type: 'get', 18 | response: config => { 19 | const items = data.items 20 | return { 21 | code: 20000, 22 | data: { 23 | total: items.length, 24 | items: items 25 | } 26 | } 27 | } 28 | } 29 | ] 30 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | sidebar: state => state.app.sidebar, 3 | device: state => state.app.device, 4 | token: state => state.user.token, 5 | objectId: state => state.objectId, 6 | avatar: state => state.user.avatar, 7 | name: state => state.user.name, 8 | departmentToken: state => state.departmentToken, 9 | _product_count: state => state.dashboard._product_count, //产品总数 10 | _dev_count: state => state.dashboard._dev_count, //设备总数 11 | _dev_online_count: state => state.dashboard._dev_online_count, //在线 12 | _dev_off_count: state => state.dashboard._dev_off_count, //离线 13 | treeFlag: state => state.settings.treeFlag, //部门树展开关闭 14 | } 15 | export default getters 16 | -------------------------------------------------------------------------------- /src/icons/svg/deptchange.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | // sidebar 2 | $menuText:#bfcbd9; 3 | $menuActiveText:#409EFF; 4 | $subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951 5 | 6 | $menuBg:#304156; 7 | $menuHover:#263445; 8 | 9 | $subMenuBg:#1f2d3d; 10 | $subMenuHover:#001528; 11 | 12 | $sideBarWidth: 210px; 13 | 14 | // the :export directive is the magic sauce for webpack 15 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 16 | :export { 17 | menuText: $menuText; 18 | menuActiveText: $menuActiveText; 19 | subMenuActiveText: $subMenuActiveText; 20 | menuBg: $menuBg; 21 | menuHover: $menuHover; 22 | subMenuBg: $subMenuBg; 23 | subMenuHover: $subMenuHover; 24 | sideBarWidth: $sideBarWidth; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unit/components/Hamburger.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import Hamburger from '@/components/Hamburger/index.vue' 3 | describe('Hamburger.vue', () => { 4 | it('toggle click', () => { 5 | const wrapper = shallowMount(Hamburger) 6 | const mockFn = jest.fn() 7 | wrapper.vm.$on('toggleClick', mockFn) 8 | wrapper.find('.hamburger').trigger('click') 9 | expect(mockFn).toBeCalled() 10 | }) 11 | it('prop isActive', () => { 12 | const wrapper = shallowMount(Hamburger) 13 | wrapper.setProps({ isActive: true }) 14 | expect(wrapper.contains('.is-active')).toBe(true) 15 | wrapper.setProps({ isActive: false }) 16 | expect(wrapper.contains('.is-active')).toBe(false) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /tests/unit/components/SvgIcon.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import SvgIcon from '@/components/SvgIcon/index.vue' 3 | describe('SvgIcon.vue', () => { 4 | it('iconClass', () => { 5 | const wrapper = shallowMount(SvgIcon, { 6 | propsData: { 7 | iconClass: 'test' 8 | } 9 | }) 10 | expect(wrapper.find('use').attributes().href).toBe('#icon-test') 11 | }) 12 | it('className', () => { 13 | const wrapper = shallowMount(SvgIcon, { 14 | propsData: { 15 | iconClass: 'test' 16 | } 17 | }) 18 | expect(wrapper.classes().length).toBe(1) 19 | wrapper.setProps({ className: 'test' }) 20 | expect(wrapper.classes().includes('test')).toBe(true) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /src/i18n/index.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | import VueI18n from 'vue-i18n' 3 | import elEn from 'element-ui/lib/locale/lang/en' 4 | import elZh from 'element-ui/lib/locale/lang/zh-CN' 5 | import elJp from 'element-ui/lib/locale/lang/ja' 6 | import en from './en' 7 | import zh from './zh' 8 | import jp from './jp' 9 | Vue.use(VueI18n) 10 | 11 | const messages = { 12 | en: { 13 | ...en, 14 | ...elEn, 15 | }, 16 | zh: { 17 | ...zh, 18 | ...elZh, 19 | }, 20 | jp: { 21 | ...jp, 22 | ...elJp, 23 | }, 24 | } 25 | function getLanguage() { 26 | return store.getters['settings/language'] || 'zh' 27 | } 28 | 29 | const i18n = new VueI18n({ 30 | locale: getLanguage(), 31 | messages, 32 | }) 33 | 34 | export default i18n 35 | -------------------------------------------------------------------------------- /src/store/modules/settings.js: -------------------------------------------------------------------------------- 1 | import defaultSettings from '@/settings' 2 | 3 | const { showSettings, fixedHeader, sidebarLogo } = defaultSettings 4 | 5 | const state = { 6 | treeFlag:false, 7 | showSettings: showSettings, 8 | fixedHeader: fixedHeader, 9 | sidebarLogo: sidebarLogo 10 | } 11 | 12 | const mutations = { 13 | CHANGE_SETTING: (state, { key, value }) => { 14 | // eslint-disable-next-line no-prototype-builtins 15 | if (state.hasOwnProperty(key)) { 16 | state[key] = value 17 | } 18 | } 19 | } 20 | 21 | const actions = { 22 | changeSetting({ commit }, data) { 23 | commit('CHANGE_SETTING', data) 24 | } 25 | } 26 | 27 | export default { 28 | namespaced: true, 29 | state, 30 | mutations, 31 | actions 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/FixiOSBug.js: -------------------------------------------------------------------------------- 1 | export default { 2 | computed: { 3 | device() { 4 | return this.$store.state.app.device 5 | } 6 | }, 7 | mounted() { 8 | // In order to fix the click on menu on the ios device will trigger the mouseleave bug 9 | // https://github.com/PanJiaChen/vue-element-admin/issues/1135 10 | this.fixBugIniOS() 11 | }, 12 | methods: { 13 | fixBugIniOS() { 14 | const $subMenu = this.$refs.subMenu 15 | if ($subMenu) { 16 | const handleMouseleave = $subMenu.handleMouseleave 17 | $subMenu.handleMouseleave = (e) => { 18 | if (this.device === 'mobile') { 19 | return 20 | } 21 | handleMouseleave(e) 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/unit/utils/validate.spec.js: -------------------------------------------------------------------------------- 1 | import { validUsername, isExternal } from '@/utils/validate.js' 2 | 3 | describe('Utils:validate', () => { 4 | it('validUsername', () => { 5 | expect(validUsername('admin')).toBe(true) 6 | expect(validUsername('editor')).toBe(true) 7 | expect(validUsername('xxxx')).toBe(false) 8 | }) 9 | it('isExternal', () => { 10 | expect(isExternal('https://github.com/PanJiaChen/vue-element-admin')).toBe(true) 11 | expect(isExternal('http://github.com/PanJiaChen/vue-element-admin')).toBe(true) 12 | expect(isExternal('github.com/PanJiaChen/vue-element-admin')).toBe(false) 13 | expect(isExternal('/dashboard')).toBe(false) 14 | expect(isExternal('./dashboard')).toBe(false) 15 | expect(isExternal('dashboard')).toBe(false) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /src/views/dashboard/component/commom/RealTime.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | 38 | -------------------------------------------------------------------------------- /src/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/api/View/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function queryView(params) { 4 | return request({ 5 | url: `classes/View`, 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | export function getView(objectId, params = {}) { 11 | return request({ 12 | url: `amis/View/${objectId}`, 13 | method: 'get', 14 | params 15 | }) 16 | } 17 | export function getTopo(params) { 18 | return request({ 19 | url: `/topo`, 20 | method: 'get', 21 | params 22 | }) 23 | } 24 | export function postAmis(params, data) { 25 | return request({ 26 | url: `/amis`, 27 | method: 'post', 28 | params, 29 | data 30 | }) 31 | } 32 | 33 | export function postBatchAmis(data) { 34 | return request({ 35 | url: `/batch_amis`, 36 | method: 'post', 37 | data 38 | }) 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/api/Device/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function querycompanyDevice(params) { 4 | return request({ 5 | url: `/classes/Device`, 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | 11 | export function getDevice(objectId) { 12 | return request({ 13 | url: `/classes/Device/${objectId}`, 14 | method: 'get', 15 | }) 16 | } 17 | export function putDevice(objectId, data) { 18 | return request({ 19 | url: `/classes/Device/${objectId}`, 20 | method: 'put', 21 | data 22 | }) 23 | } 24 | export function getDeviceRealCard(objectId) { 25 | return request({ 26 | url: `/devicecard/${objectId}`, 27 | method: 'get', 28 | }) 29 | } 30 | 31 | export function getEchart(objectId,params) { 32 | return request({ 33 | url: `/echart/${objectId}`, 34 | method: 'get', 35 | params 36 | }) 37 | } -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest', 5 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 6 | 'jest-transform-stub', 7 | '^.+\\.jsx?$': 'babel-jest' 8 | }, 9 | moduleNameMapper: { 10 | '^@/(.*)$': '/src/$1' 11 | }, 12 | snapshotSerializers: ['jest-serializer-vue'], 13 | testMatch: [ 14 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 15 | ], 16 | collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'], 17 | coverageDirectory: '/tests/unit/coverage', 18 | // 'collectCoverage': true, 19 | 'coverageReporters': [ 20 | 'lcov', 21 | 'text-summary' 22 | ], 23 | testURL: 'http://localhost/' 24 | } 25 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 44 | -------------------------------------------------------------------------------- /src/styles/element-ui.scss: -------------------------------------------------------------------------------- 1 | // cover some element-ui styles 2 | 3 | .el-breadcrumb__inner, 4 | .el-breadcrumb__inner a { 5 | font-weight: 400 !important; 6 | } 7 | 8 | .el-upload { 9 | input[type="file"] { 10 | display: none !important; 11 | } 12 | } 13 | 14 | .el-upload__input { 15 | display: none; 16 | } 17 | 18 | 19 | // to fixed https://github.com/ElemeFE/element/issues/2461 20 | .el-dialog { 21 | transform: none; 22 | left: 0; 23 | position: relative; 24 | margin: 0 auto; 25 | } 26 | 27 | // refine element ui upload 28 | .upload-container { 29 | .el-upload { 30 | width: 100%; 31 | 32 | .el-upload-dragger { 33 | width: 100%; 34 | height: 200px; 35 | } 36 | } 37 | } 38 | 39 | // dropdown 40 | .el-dropdown-menu { 41 | a { 42 | display: block 43 | } 44 | } 45 | 46 | // to fix el-date-picker css style 47 | .el-range-separator { 48 | box-sizing: content-box; 49 | } 50 | -------------------------------------------------------------------------------- /src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/transition.scss: -------------------------------------------------------------------------------- 1 | // global transition css 2 | 3 | /* fade */ 4 | .fade-enter-active, 5 | .fade-leave-active { 6 | transition: opacity 0.28s; 7 | } 8 | 9 | .fade-enter, 10 | .fade-leave-active { 11 | opacity: 0; 12 | } 13 | 14 | /* fade-transform */ 15 | .fade-transform-leave-active, 16 | .fade-transform-enter-active { 17 | transition: all .5s; 18 | } 19 | 20 | .fade-transform-enter { 21 | opacity: 0; 22 | transform: translateX(-30px); 23 | } 24 | 25 | .fade-transform-leave-to { 26 | opacity: 0; 27 | transform: translateX(30px); 28 | } 29 | 30 | /* breadcrumb transition */ 31 | .breadcrumb-enter-active, 32 | .breadcrumb-leave-active { 33 | transition: all .5s; 34 | } 35 | 36 | .breadcrumb-enter, 37 | .breadcrumb-leave-active { 38 | opacity: 0; 39 | transform: translateX(20px); 40 | } 41 | 42 | .breadcrumb-move { 43 | transition: all .5s; 44 | } 45 | 46 | .breadcrumb-leave-active { 47 | position: absolute; 48 | } 49 | -------------------------------------------------------------------------------- /src/icons/svg/code.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/run.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <%= webpackConfig.name %> 11 | 12 | 13 | 14 | 15 | 19 |
20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/store/modules/dashboard.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const state = { 4 | _product_count:Cookies.get('_product_count') || 0, //产品总数 5 | _dev_count: Cookies.get('_dev_count') || 0, //设备总数 6 | _dev_online_count: Cookies.get('_dev_online_count') || 0, //在线 7 | _dev_off_count: Cookies.get('_dev_off_count') || 0, //离线 8 | } 9 | 10 | const mutations = { 11 | TOGGLE_DEVICE: (state, device) => { 12 | state.device = device 13 | }, 14 | set_product_count: (state, _product_count) => { 15 | state._product_count = _product_count 16 | }, 17 | set_dev_count: (state, _dev_count) => { 18 | state._dev_count = _dev_count 19 | }, 20 | set_dev_online_count: (state, _dev_online_count) => { 21 | state._dev_online_count = _dev_online_count 22 | }, 23 | set_dev_off_count: (state, _dev_off_count) => { 24 | state._dev_off_count = _dev_off_count 25 | }, 26 | } 27 | 28 | const actions = { 29 | toggleDevice({ commit }, device) { 30 | commit('TOGGLE_DEVICE', device) 31 | }, 32 | } 33 | 34 | export default { 35 | namespaced: true, 36 | state, 37 | mutations, 38 | actions 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present PanJiaChen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/unit/utils/formatTime.spec.js: -------------------------------------------------------------------------------- 1 | import { formatTime } from '@/utils/index.js' 2 | 3 | describe('Utils:formatTime', () => { 4 | const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" 5 | const retrofit = 5 * 1000 6 | 7 | it('ten digits timestamp', () => { 8 | expect(formatTime((d / 1000).toFixed(0))).toBe('7月13日17时54分') 9 | }) 10 | it('test now', () => { 11 | expect(formatTime(+new Date() - 1)).toBe('刚刚') 12 | }) 13 | it('less two minute', () => { 14 | expect(formatTime(+new Date() - 60 * 2 * 1000 + retrofit)).toBe('2分钟前') 15 | }) 16 | it('less two hour', () => { 17 | expect(formatTime(+new Date() - 60 * 60 * 2 * 1000 + retrofit)).toBe('2小时前') 18 | }) 19 | it('less one day', () => { 20 | expect(formatTime(+new Date() - 60 * 60 * 24 * 1 * 1000)).toBe('1天前') 21 | }) 22 | it('more than one day', () => { 23 | expect(formatTime(d)).toBe('7月13日17时54分') 24 | }) 25 | it('format', () => { 26 | expect(formatTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') 27 | expect(formatTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') 28 | expect(formatTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /src/api/Evidence/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | 4 | export function queryEvidence(params) { 5 | return request({ 6 | url: `/classes/Evidence`, 7 | method: 'get', 8 | params 9 | }) 10 | } 11 | export function postBatch(data) { 12 | return request({ 13 | url: `/batch`, 14 | method: 'post', 15 | data 16 | }) 17 | } 18 | export async function postEvidence(id, params) { 19 | return request({ 20 | url: `/evidence?id=${id}`, 21 | method: 'post', 22 | data: params, 23 | }) 24 | } 25 | export async function generatereport(id) { 26 | return request({ 27 | url: `/generatereport?id=${id}`, 28 | method: 'post', 29 | }) 30 | } 31 | export async function delEvidence(ObjectId) { 32 | return request({ 33 | url: `/classes/Evidence/${ObjectId}`, 34 | method: 'delete', 35 | }) 36 | } 37 | export async function putEvidence(ObjectId, data) { 38 | return request({ 39 | url: `/classes/Evidence/${ObjectId}`, 40 | method: 'put', 41 | data 42 | }) 43 | } 44 | export async function postDrawxnqx(params) { 45 | return request({ 46 | url: '/drawxnqx', 47 | method: 'post', 48 | data: params, 49 | }) 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | @import './mixin.scss'; 3 | @import './transition.scss'; 4 | @import './element-ui.scss'; 5 | @import './sidebar.scss'; 6 | 7 | body { 8 | height: 100%; 9 | -moz-osx-font-smoothing: grayscale; 10 | -webkit-font-smoothing: antialiased; 11 | text-rendering: optimizeLegibility; 12 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; 13 | } 14 | 15 | label { 16 | font-weight: 700; 17 | } 18 | 19 | html { 20 | height: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | #app { 25 | height: 100%; 26 | } 27 | 28 | *, 29 | *:before, 30 | *:after { 31 | box-sizing: inherit; 32 | } 33 | 34 | a:focus, 35 | a:active { 36 | outline: none; 37 | } 38 | 39 | a, 40 | a:focus, 41 | a:hover { 42 | cursor: pointer; 43 | // color: inherit; 44 | text-decoration: none; 45 | } 46 | 47 | div:focus { 48 | outline: none; 49 | } 50 | 51 | .clearfix { 52 | &:after { 53 | visibility: hidden; 54 | display: block; 55 | font-size: 0; 56 | content: " "; 57 | clear: both; 58 | height: 0; 59 | } 60 | } 61 | 62 | // main-container global css 63 | .app-container { 64 | padding: 20px; 65 | } 66 | -------------------------------------------------------------------------------- /src/icons/svg/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/throw.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/utils/parseTime.spec.js: -------------------------------------------------------------------------------- 1 | import { parseTime } from '@/utils/index.js' 2 | 3 | describe('Utils:parseTime', () => { 4 | const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" 5 | it('timestamp', () => { 6 | expect(parseTime(d)).toBe('2018-07-13 17:54:01') 7 | }) 8 | it('timestamp string', () => { 9 | expect(parseTime((d + ''))).toBe('2018-07-13 17:54:01') 10 | }) 11 | it('ten digits timestamp', () => { 12 | expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01') 13 | }) 14 | it('new Date', () => { 15 | expect(parseTime(new Date(d))).toBe('2018-07-13 17:54:01') 16 | }) 17 | it('format', () => { 18 | expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') 19 | expect(parseTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') 20 | expect(parseTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') 21 | }) 22 | it('get the day of the week', () => { 23 | expect(parseTime(d, '{a}')).toBe('五') // 星期五 24 | }) 25 | it('get the day of the week', () => { 26 | expect(parseTime(+d + 1000 * 60 * 60 * 24 * 2, '{a}')).toBe('日') // 星期日 27 | }) 28 | it('empty argument', () => { 29 | expect(parseTime()).toBeNull() 30 | }) 31 | 32 | it('null', () => { 33 | expect(parseTime(null)).toBeNull() 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /src/components/model3d/utils/load.js: -------------------------------------------------------------------------------- 1 | import { OBJLoader, MTLLoader } from "three-obj-mtl-loader"; 2 | import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'; 3 | import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; 4 | 5 | 6 | // 加载obj,mtl文件 7 | export function objloader(path) { 8 | return new Promise(resolve => { 9 | var mtlLoader = new MTLLoader(); 10 | // 初始化obj 11 | var objLoader = new OBJLoader(); 12 | // 加载mtl文件 13 | mtlLoader.load(`/static/${path}.mtl`, (mtl) => { 14 | // 初始化 15 | mtl.preload(); 16 | // 加载贴图 17 | objLoader.setMaterials(mtl); 18 | objLoader.load(`/static/${path}.obj`, (obj) => { 19 | resolve({ mtl, obj }) 20 | }) 21 | }) 22 | }); 23 | } 24 | 25 | // 加载fbx文件 26 | export function fbxloader(path) { 27 | return new Promise(resolve => { 28 | var loader = new FBXLoader(); 29 | loader.load(`${path}`, (fbx) => { 30 | resolve(fbx) 31 | }) 32 | }); 33 | } 34 | // 加载gtlf文件 35 | export function gltfloader(path) { 36 | return new Promise(resolve => { 37 | var loader = new GLTFLoader(); 38 | loader.load(`${path}`, (gltf) => { 39 | resolve(gltf) 40 | }) 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /src/views/dashboard/component/ScreenSgmap.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 55 | -------------------------------------------------------------------------------- /src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 45 | -------------------------------------------------------------------------------- /src/icons/svg/phone.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/store/modules/app.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const state = { 4 | sidebar: { 5 | opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, 6 | withoutAnimation: false 7 | }, 8 | device: 'desktop' 9 | } 10 | 11 | const mutations = { 12 | TOGGLE_SIDEBAR: state => { 13 | state.sidebar.opened = !state.sidebar.opened 14 | state.sidebar.withoutAnimation = false 15 | if (state.sidebar.opened) { 16 | Cookies.set('sidebarStatus', 1) 17 | } else { 18 | Cookies.set('sidebarStatus', 0) 19 | } 20 | }, 21 | CLOSE_SIDEBAR: (state, withoutAnimation) => { 22 | Cookies.set('sidebarStatus', 0) 23 | state.sidebar.opened = false 24 | state.sidebar.withoutAnimation = withoutAnimation 25 | }, 26 | TOGGLE_DEVICE: (state, device) => { 27 | state.device = device 28 | } 29 | } 30 | 31 | const actions = { 32 | toggleSideBar({ commit }) { 33 | commit('TOGGLE_SIDEBAR') 34 | }, 35 | closeSideBar({ commit }, { withoutAnimation }) { 36 | commit('CLOSE_SIDEBAR', withoutAnimation) 37 | }, 38 | toggleDevice({ commit }, device) { 39 | commit('TOGGLE_DEVICE', device) 40 | }, 41 | // updateRealCard(state, payload) { 42 | // state.realCard = payload 43 | // }, 44 | } 45 | 46 | export default { 47 | namespaced: true, 48 | state, 49 | mutations, 50 | actions 51 | } 52 | -------------------------------------------------------------------------------- /src/icons/svg/shooting.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/mixin/ResizeHandler.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | const { body } = document 4 | const WIDTH = 992 // refer to Bootstrap's responsive design 5 | 6 | export default { 7 | watch: { 8 | $route(route) { 9 | if (this.device === 'mobile' && this.sidebar.opened) { 10 | store.dispatch('app/closeSideBar', { withoutAnimation: false }) 11 | } 12 | } 13 | }, 14 | beforeMount() { 15 | window.addEventListener('resize', this.$_resizeHandler) 16 | }, 17 | beforeDestroy() { 18 | window.removeEventListener('resize', this.$_resizeHandler) 19 | }, 20 | mounted() { 21 | const isMobile = this.$_isMobile() 22 | if (isMobile) { 23 | store.dispatch('app/toggleDevice', 'mobile') 24 | store.dispatch('app/closeSideBar', { withoutAnimation: true }) 25 | } 26 | }, 27 | methods: { 28 | // use $_ for mixins properties 29 | // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential 30 | $_isMobile() { 31 | const rect = body.getBoundingClientRect() 32 | return rect.width - 1 < WIDTH 33 | }, 34 | $_resizeHandler() { 35 | if (!document.hidden) { 36 | const isMobile = this.$_isMobile() 37 | store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') 38 | 39 | if (isMobile) { 40 | store.dispatch('app/closeSideBar', { withoutAnimation: true }) 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/views/CloudOc/AmisPage/style/themeBlack.css: -------------------------------------------------------------------------------- 1 | /* *{ 2 | background-color: #000 !important; 3 | } */ 4 | *{ 5 | --themeColor:#0f266b; 6 | --componentColor:#1d386f; 7 | --headColor:#0c6ed3; 8 | --basicColor:#2850a7; 9 | --whiteColor:#fff; 10 | } 11 | .antd-Page-body{ 12 | background-color: var(--themeColor) !important; 13 | } 14 | .antd-Form-label{ 15 | color: var(--whiteColor) !important; 16 | } 17 | .antd-Panel-body{ 18 | background-color: var(--componentColor) !important; 19 | } 20 | .antd-Panel-footer{ 21 | background-color:var(--componentColor) !important; 22 | } 23 | /* 表格 */ 24 | .antd-Table-table{ 25 | background-color: var(--componentColor) !important; 26 | color: var(--whiteColor) !important; 27 | } 28 | .antd-Table-table > thead > tr > th{ 29 | background-color: var(--headColor) !important; 30 | color: var(--whiteColor) !important; 31 | border: #000 1px solid !important; 32 | } 33 | /* 下拉框 */ 34 | .antd-Select{ 35 | background-color: var(--basicColor) !important; 36 | color: var(--whiteColor) !important; 37 | border: 0 !important; 38 | } 39 | 40 | /* .antd-Select-option{ 41 | background-color: #1d386f !important; 42 | color: #fff !important; 43 | } */ 44 | /* 输入框 */ 45 | .antd-TextControl-input{ 46 | background-color: var(--componentColor) !important; 47 | } 48 | .antd-TextControl-input input{ 49 | color: var(--whiteColor) !important; 50 | } 51 | /* tree 树 */ 52 | .antd-Tree-itemText{ 53 | color: var(--whiteColor) !important; 54 | } -------------------------------------------------------------------------------- /src/icons/svg/material.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/api/Dashboard/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import axios from 'axios' 3 | import { getToken } from '@/utils/auth' 4 | 5 | 6 | const server = 'http://dev.dgiotcloud.cn' 7 | 8 | export function getDlinkJson(data) { 9 | return request({ 10 | url: `/dlinkjson`, 11 | method: 'get', 12 | params: { 13 | type: data 14 | } 15 | }) 16 | } 17 | export async function Startdashboard(dashboardId, data) { 18 | // console.log('window', window.location.origin); 19 | let ip = window.location.origin 20 | if (ip.indexOf('localhost') >= 0) { 21 | ip = server 22 | } 23 | return axios({ 24 | url: `${ip}/iotapi/dashboard`, 25 | method: 'post', 26 | data: data, 27 | params: { 28 | dashboardId: dashboardId 29 | }, 30 | headers: { 31 | "sessionToken": getToken() 32 | } 33 | }) 34 | } 35 | // export async function sendTopic(data) { 36 | // // console.log('window', window.location.origin); 37 | // let ip = window.location.origin 38 | // if (ip.indexOf('localhost') >= 0) { 39 | // ip = server 40 | // } 41 | // return axios({ 42 | // url: `${ip}/iotapi/topic`, 43 | // method: 'post', 44 | // data: data, 45 | // headers: { 46 | // "sessionToken": getToken() 47 | // } 48 | // }) 49 | // } 50 | export function sendTopic(data) { 51 | return request({ 52 | url: `/topic`, 53 | method: 'post', 54 | data 55 | }) 56 | } 57 | export function postCookie(data) { 58 | return request({ 59 | url: `/cookie`, 60 | method: 'post', 61 | data 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /public/draco/README.md: -------------------------------------------------------------------------------- 1 | # Draco 3D Data Compression 2 | 3 | Draco is an open-source library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics. 4 | 5 | [Website](https://google.github.io/draco/) | [GitHub](https://github.com/google/draco) 6 | 7 | ## Contents 8 | 9 | This folder contains three utilities: 10 | 11 | * `draco_decoder.js` — Emscripten-compiled decoder, compatible with any modern browser. 12 | * `draco_decoder.wasm` — WebAssembly decoder, compatible with newer browsers and devices. 13 | * `draco_wasm_wrapper.js` — JavaScript wrapper for the WASM decoder. 14 | 15 | Each file is provided in two variations: 16 | 17 | * **Default:** Latest stable builds, tracking the project's [master branch](https://github.com/google/draco). 18 | * **glTF:** Builds targeted by the [glTF mesh compression extension](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression), tracking the [corresponding Draco branch](https://github.com/google/draco/tree/gltf_2.0_draco_extension). 19 | 20 | Either variation may be used with `THREE.DRACOLoader`: 21 | 22 | ```js 23 | var dracoLoader = new THREE.DRACOLoader(); 24 | dracoLoader.setDecoderPath('path/to/decoders/'); 25 | dracoLoader.setDecoderConfig({type: 'js'}); // (Optional) Override detection of WASM support. 26 | ``` 27 | 28 | Further [documentation on GitHub](https://github.com/google/draco/tree/master/javascript/example#static-loading-javascript-decoder). 29 | 30 | ## License 31 | 32 | [Apache License 2.0](https://github.com/google/draco/blob/master/LICENSE) 33 | -------------------------------------------------------------------------------- /mock/index.js: -------------------------------------------------------------------------------- 1 | const Mock = require('mockjs') 2 | const { param2Obj } = require('./utils') 3 | 4 | const user = require('./user') 5 | const table = require('./table') 6 | 7 | const mocks = [ 8 | ...user, 9 | ...table 10 | ] 11 | 12 | // for front mock 13 | // please use it cautiously, it will redefine XMLHttpRequest, 14 | // which will cause many of your third-party libraries to be invalidated(like progress event). 15 | function mockXHR() { 16 | // mock patch 17 | // https://github.com/nuysoft/Mock/issues/300 18 | Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send 19 | Mock.XHR.prototype.send = function() { 20 | if (this.custom.xhr) { 21 | this.custom.xhr.withCredentials = this.withCredentials || false 22 | 23 | if (this.responseType) { 24 | this.custom.xhr.responseType = this.responseType 25 | } 26 | } 27 | this.proxy_send(...arguments) 28 | } 29 | 30 | function XHR2ExpressReqWrap(respond) { 31 | return function(options) { 32 | let result = null 33 | if (respond instanceof Function) { 34 | const { body, type, url } = options 35 | // https://expressjs.com/en/4x/api.html#req 36 | result = respond({ 37 | method: type, 38 | body: JSON.parse(body), 39 | query: param2Obj(url) 40 | }) 41 | } else { 42 | result = respond 43 | } 44 | return Mock.mock(result) 45 | } 46 | } 47 | 48 | for (const i of mocks) { 49 | Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response)) 50 | } 51 | } 52 | 53 | module.exports = { 54 | mocks, 55 | mockXHR 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/components/model3d/draco/README.md: -------------------------------------------------------------------------------- 1 | # Draco 3D Data Compression 2 | 3 | Draco is an open-source library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics. 4 | 5 | [Website](https://google.github.io/draco/) | [GitHub](https://github.com/google/draco) 6 | 7 | ## Contents 8 | 9 | This folder contains three utilities: 10 | 11 | * `draco_decoder.js` — Emscripten-compiled decoder, compatible with any modern browser. 12 | * `draco_decoder.wasm` — WebAssembly decoder, compatible with newer browsers and devices. 13 | * `draco_wasm_wrapper.js` — JavaScript wrapper for the WASM decoder. 14 | 15 | Each file is provided in two variations: 16 | 17 | * **Default:** Latest stable builds, tracking the project's [master branch](https://github.com/google/draco). 18 | * **glTF:** Builds targeted by the [glTF mesh compression extension](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression), tracking the [corresponding Draco branch](https://github.com/google/draco/tree/gltf_2.0_draco_extension). 19 | 20 | Either variation may be used with `THREE.DRACOLoader`: 21 | 22 | ```js 23 | var dracoLoader = new THREE.DRACOLoader(); 24 | dracoLoader.setDecoderPath('path/to/decoders/'); 25 | dracoLoader.setDecoderConfig({type: 'js'}); // (Optional) Override detection of WASM support. 26 | ``` 27 | 28 | Further [documentation on GitHub](https://github.com/google/draco/tree/master/javascript/example#static-loading-javascript-decoder). 29 | 30 | ## License 31 | 32 | [Apache License 2.0](https://github.com/google/draco/blob/master/LICENSE) 33 | -------------------------------------------------------------------------------- /src/api/User/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 获取验证码 4 | export function postCode(account) { 5 | return request({ 6 | url: '/smscode?account=' + account, 7 | method: 'post' 8 | // data, 9 | // params 10 | // headers: { 11 | // "Content-Type": "text/plain" 12 | // } 13 | }) 14 | } 15 | // 验证码登录 16 | export function verifyCode(phone, code, username, password) { 17 | return request({ 18 | url: `/verify_code?account=${phone}&code=${code}&username=${username}&password=${password}`, 19 | method: 'post', 20 | data: {} 21 | // params 22 | // headers: { 23 | // "Content-Type": "text/plain" 24 | // } 25 | }) 26 | } 27 | 28 | // 登录 29 | export function login(data) { 30 | return request({ 31 | url: '/login', 32 | method: 'post', 33 | data, 34 | headers: { 35 | 'Content-Type': 'text/plain' 36 | 37 | } 38 | }) 39 | } 40 | export function Roletree() { 41 | return request({ 42 | url: '/roletree', 43 | method: 'get' 44 | }) 45 | } 46 | export function getToken(params) { 47 | return request({ 48 | url: '/token', 49 | method: 'get', 50 | params 51 | }) 52 | } 53 | 54 | export function putUserInfo(objectId, data) { 55 | return request({ 56 | url: `/amis/_User/${objectId}`, 57 | method: 'put', 58 | data 59 | }) 60 | } 61 | 62 | export function getInfo(token) { 63 | return request({ 64 | url: '/vue-admin-template/user/info', 65 | method: 'get', 66 | params: { token } 67 | }) 68 | } 69 | 70 | export function logout() { 71 | return request({ 72 | url: '/vue-admin-template/user/logout', 73 | method: 'post' 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /src/components/SvgIcon/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 51 | 52 | 67 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Item.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 64 | -------------------------------------------------------------------------------- /src/icons/svg/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/tree/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 78 | 79 | -------------------------------------------------------------------------------- /src/icons/svg/organode.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mock/user.js: -------------------------------------------------------------------------------- 1 | 2 | const tokens = { 3 | admin: { 4 | token: 'admin-token' 5 | }, 6 | editor: { 7 | token: 'editor-token' 8 | } 9 | } 10 | 11 | const users = { 12 | 'admin-token': { 13 | roles: ['admin'], 14 | introduction: 'I am a super administrator', 15 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', 16 | name: 'Super Admin' 17 | }, 18 | 'editor-token': { 19 | roles: ['editor'], 20 | introduction: 'I am an editor', 21 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', 22 | name: 'Normal Editor' 23 | } 24 | } 25 | 26 | module.exports = [ 27 | // user login 28 | { 29 | url: '/vue-admin-template/user/login', 30 | type: 'post', 31 | response: config => { 32 | const { username } = config.body 33 | const token = tokens[username] 34 | 35 | // mock error 36 | if (!token) { 37 | return { 38 | code: 60204, 39 | message: 'Account and password are incorrect.' 40 | } 41 | } 42 | 43 | return { 44 | code: 20000, 45 | data: token 46 | } 47 | } 48 | }, 49 | 50 | // get user info 51 | { 52 | url: '/vue-admin-template/user/info\.*', 53 | type: 'get', 54 | response: config => { 55 | const { token } = config.query 56 | const info = users[token] 57 | 58 | // mock error 59 | if (!info) { 60 | return { 61 | code: 50008, 62 | message: 'Login failed, unable to get user details.' 63 | } 64 | } 65 | 66 | return { 67 | code: 20000, 68 | data: info 69 | } 70 | } 71 | }, 72 | 73 | // user logout 74 | { 75 | url: '/vue-admin-template/user/logout', 76 | type: 'post', 77 | response: _ => { 78 | return { 79 | code: 20000, 80 | data: 'success' 81 | } 82 | } 83 | } 84 | ] 85 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Cookies from 'js-cookie' 3 | import { Base64 } from "js-base64"; 4 | import md5 from "js-md5"; 5 | import 'normalize.css/normalize.css' // A modern alternative to CSS resets 6 | 7 | import ElementUI from 'element-ui' 8 | import 'element-ui/lib/theme-chalk/index.css' 9 | // import locale from 'element-ui/lib/locale/lang/en' // lang i18n 10 | 11 | import '@/styles/index.scss' // global css 12 | 13 | import App from './App' 14 | import store from './store' 15 | import router from './router' 16 | 17 | import VCharts from 'v-charts' 18 | 19 | import '@/icons' // icon 20 | import '@/permission' // permission control 21 | 22 | import '@dgiot/dgiot-component' 23 | 24 | // import 'amis/lib/themes/cxd.css' 25 | // import 'amis/lib/themes/ang.css' 26 | // import 'amis/lib/themes/default.css' 27 | // import 'amis/lib/helper.css' 28 | // import 'amis/sdk/sdk.css' 29 | // import "amis/lib/themes/antd.css"; 30 | 31 | // import 'amis-editor/dist/style.css' 32 | /** 33 | * If you don't want to use mock-server 34 | * you want to use MockJs for mock api 35 | * you can execute: mockXHR() 36 | * 37 | * Currently MockJs will be used in the production environment, 38 | * please remove it before going online ! ! ! 39 | */ 40 | // if (process.env.NODE_ENV === 'production') { 41 | // const { mockXHR } = require('../mock') 42 | // mockXHR() 43 | // } 44 | Vue.prototype.$md5 = md5 45 | Vue.prototype.$_ = require("lodash") 46 | Vue.prototype.$Cookies = Cookies 47 | Vue.prototype.$Base64 = Base64 48 | window.Base64 = Vue.prototype.$Base64 49 | Vue.prototype.$dgiotBus = new Vue(); 50 | window.dgiotBus = Vue.prototype.$dgiotBus 51 | Vue.prototype.$FileServe = '' 52 | window.Vue = Vue 53 | 54 | // set ElementUI lang to EN 55 | // Vue.use(ElementUI, { locale }) 56 | // 如果想要中文版 element-ui,按如下方式声明 57 | Vue.use(ElementUI) 58 | Vue.use(VCharts) 59 | 60 | Vue.config.productionTip = false 61 | 62 | new Vue({ 63 | el: '#app', 64 | router, 65 | store, 66 | render: h => h(App) 67 | }) 68 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Logo.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 33 | 34 | 83 | -------------------------------------------------------------------------------- /src/components/Amis/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 16 | 81 | 91 | -------------------------------------------------------------------------------- /src/icons/svg/dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 52 | 53 | 94 | -------------------------------------------------------------------------------- /src/icons/svg/organization.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/fight.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/form.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iotView 轻量级工业物联网平台 2 |

3 | ommit-activity 4 | package 6 | release 7 | 8 | 9 | 10 | ## DGIoT平台简介 11 | DGIoT是国内首款轻量级工业物联网开源平台,我们致力于为5类客户提供物联网解决方案: 12 | + **国企/研究院**:平台代码开源,无版权产权困扰,国产无“卡脖”之忧 13 | + **系统集成商**:通用设备海量接入、定制设备二次开发、30分钟一键式私有化快速部署,低成本(降90%成本) 14 | + **工业设备制造商**:海量设备上线运维,不受公有云限制,低成本,短周期自建平台,私有化部署,数据安全 15 | + **开源平台开发者**:一键式开发环境,集成和兼容各种最优开源工具,快速承接物联网项目 16 | + **垂直领域物联网平台**:快速部署私有化平台,千万级承载,运营级底座,全开放扩展 17 | 18 | ## 平台快速体验与技术交流微信群 19 | | 微信技术支持群 |官网 https://www.dgiotcloud.cn/| [QQ技术支持群](https://jq.qq.com/?_wv=1027&k=LipWZvDe): 346566935 | 20 | |:---:|:---:|:---:| 21 | | |**平台体验网址**

https://prod.dgiotcloud.cn



| | 22 | 23 | ## 特色功能 24 | + 低代码快速开发 25 | 26 | 27 | ![微服务架构图_02.png](https://dgiot-1253666439.cos.ap-shanghai-fsi.myqcloud.com/shuwa_tech/zh/frontend/web/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E5%9B%BE_02.png) 28 | 29 | dgiot 前端框架 30 | 31 | 32 | 33 | ## 相关源码地址项目源码 34 | 35 | | 源码平台 | 源码地址 | 36 | | -------- | --------------------------------------------------------------------------------------------- | 37 | | github | [https://github.com/dgiot/iotView](https://github.com/dgiot/iotView?from=git) | 38 | 39 | 40 | ## 安装使用 41 | 42 | - 获取项目代码 43 | 44 | ```bash 45 | git clone -b master https://github.com/dgiot/iotView.git 46 | ``` 47 | 48 | - 安装依赖 49 | 50 | ```bash 51 | 52 | npm install 53 | 54 | ``` 55 | 56 | - 运行 57 | 58 | ```bash 59 | 60 | npm run dev 61 | 62 | ``` 63 | 64 | - 效果展示 65 |
66 | 67 | 68 | 69 | - 登录 70 | 71 | | 用户名 | 密码 | 72 | | ----------- | ----------- | 73 | | dgiot_dev | dgiot_dev | 74 | 75 | - 打包 76 | 77 | ```bash 78 | npm run build:prod 79 | ``` 80 | 81 | 82 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # iotView 轻量级工业物联网平台 2 |

3 | ommit-activity 4 | package 6 | release 7 | 8 | 9 | 10 | ## DGIoT平台简介 11 | DGIoT是国内首款轻量级工业物联网开源平台,我们致力于为5类客户提供物联网解决方案: 12 | + **国企/研究院**:平台代码开源,无版权产权困扰,国产无“卡脖”之忧 13 | + **系统集成商**:通用设备海量接入、定制设备二次开发、30分钟一键式私有化快速部署,低成本(降90%成本) 14 | + **工业设备制造商**:海量设备上线运维,不受公有云限制,低成本,短周期自建平台,私有化部署,数据安全 15 | + **开源平台开发者**:一键式开发环境,集成和兼容各种最优开源工具,快速承接物联网项目 16 | + **垂直领域物联网平台**:快速部署私有化平台,千万级承载,运营级底座,全开放扩展 17 | 18 | ## 平台快速体验与技术交流微信群 19 | | 微信技术支持群 |官网 https://www.dgiotcloud.cn/| [QQ技术支持群](https://jq.qq.com/?_wv=1027&k=LipWZvDe): 346566935 | 20 | |:---:|:---:|:---:| 21 | | |**平台体验网址**

https://prod.dgiotcloud.cn



| | 22 | 23 | ## 特色功能 24 | + 低代码快速开发 25 | 26 | 27 | ![微服务架构图_02.png](https://dgiot-1253666439.cos.ap-shanghai-fsi.myqcloud.com/shuwa_tech/zh/frontend/web/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E5%9B%BE_02.png) 28 | 29 | dgiot 前端框架 30 | 31 | 32 | 33 | ## 相关源码地址项目源码 34 | 35 | | 源码平台 | 源码地址 | 36 | | -------- | --------------------------------------------------------------------------------------------- | 37 | | github | [https://github.com/dgiot/iotView](https://github.com/dgiot/iotView?from=git) | 38 | 39 | 40 | ## 安装使用 41 | 42 | - 获取项目代码 43 | 44 | ```bash 45 | git clone -b master https://github.com/dgiot/iotView.git 46 | ``` 47 | 48 | - 安装依赖 49 | 50 | ```bash 51 | 52 | npm install 53 | 54 | ``` 55 | 56 | - 运行 57 | 58 | ```bash 59 | 60 | npm run dev 61 | 62 | ``` 63 | 64 | - 效果展示 65 |
66 | 67 | 68 | 69 | - 登录 70 | 71 | | 用户名 | 密码 | 72 | | ----------- | ----------- | 73 | | dgiot_dev | dgiot_dev | 74 | 75 | - 打包 76 | 77 | ```bash 78 | npm run build:prod 79 | ``` 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/components/Breadcrumb/index.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 79 | 80 | 93 | -------------------------------------------------------------------------------- /mock/mock-server.js: -------------------------------------------------------------------------------- 1 | const chokidar = require('chokidar') 2 | const bodyParser = require('body-parser') 3 | const chalk = require('chalk') 4 | const path = require('path') 5 | const Mock = require('mockjs') 6 | 7 | const mockDir = path.join(process.cwd(), 'mock') 8 | 9 | function registerRoutes(app) { 10 | let mockLastIndex 11 | const { mocks } = require('./index.js') 12 | const mocksForServer = mocks.map(route => { 13 | return responseFake(route.url, route.type, route.response) 14 | }) 15 | for (const mock of mocksForServer) { 16 | app[mock.type](mock.url, mock.response) 17 | mockLastIndex = app._router.stack.length 18 | } 19 | const mockRoutesLength = Object.keys(mocksForServer).length 20 | return { 21 | mockRoutesLength: mockRoutesLength, 22 | mockStartIndex: mockLastIndex - mockRoutesLength 23 | } 24 | } 25 | 26 | function unregisterRoutes() { 27 | Object.keys(require.cache).forEach(i => { 28 | if (i.includes(mockDir)) { 29 | delete require.cache[require.resolve(i)] 30 | } 31 | }) 32 | } 33 | 34 | // for mock server 35 | const responseFake = (url, type, respond) => { 36 | return { 37 | url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`), 38 | type: type || 'get', 39 | response(req, res) { 40 | console.log('request invoke:' + req.path) 41 | res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)) 42 | } 43 | } 44 | } 45 | 46 | module.exports = app => { 47 | // parse app.body 48 | // https://expressjs.com/en/4x/api.html#req.body 49 | app.use(bodyParser.json()) 50 | app.use(bodyParser.urlencoded({ 51 | extended: true 52 | })) 53 | 54 | const mockRoutes = registerRoutes(app) 55 | var mockRoutesLength = mockRoutes.mockRoutesLength 56 | var mockStartIndex = mockRoutes.mockStartIndex 57 | 58 | // watch files, hot reload mock server 59 | chokidar.watch(mockDir, { 60 | ignored: /mock-server/, 61 | ignoreInitial: true 62 | }).on('all', (event, path) => { 63 | if (event === 'change' || event === 'add') { 64 | try { 65 | // remove mock routes stack 66 | app._router.stack.splice(mockStartIndex, mockRoutesLength) 67 | 68 | // clear routes cache 69 | unregisterRoutes() 70 | 71 | const mockRoutes = registerRoutes(app) 72 | mockRoutesLength = mockRoutes.mockRoutesLength 73 | mockStartIndex = mockRoutes.mockStartIndex 74 | 75 | console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`)) 76 | } catch (error) { 77 | console.log(chalk.redBright(error)) 78 | } 79 | } 80 | }) 81 | } 82 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 90 | -------------------------------------------------------------------------------- /src/views/dashboard/component/commom/RealCard.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 70 | 108 | -------------------------------------------------------------------------------- /src/icons/svg/jump.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/Device/DeviceKonva/index.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 79 | 122 | -------------------------------------------------------------------------------- /tests/unit/components/Breadcrumb.spec.js: -------------------------------------------------------------------------------- 1 | import { mount, createLocalVue } from '@vue/test-utils' 2 | import VueRouter from 'vue-router' 3 | import ElementUI from 'element-ui' 4 | import Breadcrumb from '@/components/Breadcrumb/index.vue' 5 | 6 | const localVue = createLocalVue() 7 | localVue.use(VueRouter) 8 | localVue.use(ElementUI) 9 | 10 | const routes = [ 11 | { 12 | path: '/', 13 | name: 'home', 14 | children: [{ 15 | path: 'dashboard', 16 | name: 'dashboard' 17 | }] 18 | }, 19 | { 20 | path: '/menu', 21 | name: 'menu', 22 | children: [{ 23 | path: 'menu1', 24 | name: 'menu1', 25 | meta: { title: 'menu1' }, 26 | children: [{ 27 | path: 'menu1-1', 28 | name: 'menu1-1', 29 | meta: { title: 'menu1-1' } 30 | }, 31 | { 32 | path: 'menu1-2', 33 | name: 'menu1-2', 34 | redirect: 'noredirect', 35 | meta: { title: 'menu1-2' }, 36 | children: [{ 37 | path: 'menu1-2-1', 38 | name: 'menu1-2-1', 39 | meta: { title: 'menu1-2-1' } 40 | }, 41 | { 42 | path: 'menu1-2-2', 43 | name: 'menu1-2-2' 44 | }] 45 | }] 46 | }] 47 | }] 48 | 49 | const router = new VueRouter({ 50 | routes 51 | }) 52 | 53 | describe('Breadcrumb.vue', () => { 54 | const wrapper = mount(Breadcrumb, { 55 | localVue, 56 | router 57 | }) 58 | it('dashboard', () => { 59 | router.push('/dashboard') 60 | const len = wrapper.findAll('.el-breadcrumb__inner').length 61 | expect(len).toBe(1) 62 | }) 63 | it('normal route', () => { 64 | router.push('/menu/menu1') 65 | const len = wrapper.findAll('.el-breadcrumb__inner').length 66 | expect(len).toBe(2) 67 | }) 68 | it('nested route', () => { 69 | router.push('/menu/menu1/menu1-2/menu1-2-1') 70 | const len = wrapper.findAll('.el-breadcrumb__inner').length 71 | expect(len).toBe(4) 72 | }) 73 | it('no meta.title', () => { 74 | router.push('/menu/menu1/menu1-2/menu1-2-2') 75 | const len = wrapper.findAll('.el-breadcrumb__inner').length 76 | expect(len).toBe(3) 77 | }) 78 | // it('click link', () => { 79 | // router.push('/menu/menu1/menu1-2/menu1-2-2') 80 | // const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') 81 | // const second = breadcrumbArray.at(1) 82 | // console.log(breadcrumbArray) 83 | // const href = second.find('a').attributes().href 84 | // expect(href).toBe('#/menu/menu1') 85 | // }) 86 | // it('noRedirect', () => { 87 | // router.push('/menu/menu1/menu1-2/menu1-2-1') 88 | // const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') 89 | // const redirectBreadcrumb = breadcrumbArray.at(2) 90 | // expect(redirectBreadcrumb.contains('a')).toBe(false) 91 | // }) 92 | it('last breadcrumb', () => { 93 | router.push('/menu/menu1/menu1-2/menu1-2-1') 94 | const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') 95 | const redirectBreadcrumb = breadcrumbArray.at(3) 96 | expect(redirectBreadcrumb.contains('a')).toBe(false) 97 | }) 98 | }) 99 | -------------------------------------------------------------------------------- /src/views/dashboard/component/TopoCard.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 101 | 117 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | Vue.use(Router) 5 | 6 | /* Layout */ 7 | import Layout from '@/layout' 8 | 9 | /** 10 | * Note: sub-menu only appear when route children.length >= 1 11 | * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html 12 | * 13 | * hidden: true if set true, item will not show in the sidebar(default is false) 14 | * alwaysShow: true if set true, will always show the root menu 15 | * if not set alwaysShow, when item has more than one children route, 16 | * it will becomes nested mode, otherwise not show the root menu 17 | * redirect: noRedirect if set noRedirect will no redirect in the breadcrumb 18 | * name:'router-name' the name is used by (must set!!!) 19 | * meta : { 20 | roles: ['admin','editor'] control the page roles (you can set multiple roles) 21 | title: 'title' the name show in sidebar and breadcrumb (recommend set) 22 | icon: 'svg-name'/'el-icon-x' the icon show in the sidebar 23 | breadcrumb: false if set false, the item will hidden in breadcrumb(default is true) 24 | activeMenu: '/example/list' if set path, the sidebar will highlight the path you set 25 | } 26 | */ 27 | 28 | /** 29 | * constantRoutes 30 | * a base page that does not have permission requirements 31 | * all roles can be accessed 32 | */ 33 | export const constantRoutes = [ 34 | { 35 | path: '/login', 36 | component: () => import('@/views/login/index'), 37 | hidden: true 38 | }, 39 | { 40 | path: '/404', 41 | component: () => import('@/views/404'), 42 | hidden: true 43 | }, 44 | 45 | { 46 | path: '/', 47 | component: Layout, 48 | redirect: '/dashboard', 49 | meta: { 50 | title: '首页', icon: 'dashboard', hidden: false, 51 | }, 52 | children: [{ 53 | path: 'dashboard', 54 | name: 'Dashboard', 55 | component: () => import('@/views/dashboard/index'), 56 | meta: { title: '首页', icon: 'dashboard', hidden: false, } 57 | }] 58 | }, 59 | { 60 | path: '/amis', 61 | component: Layout, 62 | redirect: '/amis/theme', 63 | name: 'Amis', 64 | children: [ 65 | { 66 | path: '/amis/View/:id', 67 | name: 'tabs', 68 | component: () => import('@/views/CloudOc/AmisPage/index'), 69 | meta: { title: 'amis', icon: 'form' } 70 | }, 71 | { 72 | path: '/amis/Device/konva', 73 | name: 'konva', 74 | component: () => import('@/views/Device/DeviceKonva/index'), 75 | meta: { title: 'amis', icon: 'form' } 76 | }, 77 | { 78 | path: '/amis/Device/realcard', 79 | name: 'realcard', 80 | component: () => import('@/views/Device/DeviceRealCard/index'), 81 | meta: { title: 'amis', icon: 'form' } 82 | }, 83 | ] 84 | }, 85 | // 404 page must be placed at the end !!! 86 | { path: '*', redirect: '/404', hidden: true } 87 | ] 88 | 89 | const createRouter = () => new Router({ 90 | // mode: 'history', // require service support 91 | scrollBehavior: () => ({ y: 0 }), 92 | routes: constantRoutes 93 | }) 94 | 95 | const router = createRouter() 96 | 97 | // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 98 | export function resetRouter() { 99 | const newRouter = createRouter() 100 | router.matcher = newRouter.matcher // reset router 101 | } 102 | 103 | export default router 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dashboard-amis", 3 | "version": "4.8.2", 4 | "description": "Amis Vue Template with Element UI & axios & iconfont & permission control & lint", 5 | "author": "dgiot <34489690@qq.com>", 6 | "scripts": { 7 | "dev": "vue-cli-service serve", 8 | "build:prod": "vue-cli-service build", 9 | "build:stage": "vue-cli-service build --mode staging", 10 | "preview": "node build/index.js --preview", 11 | "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml", 12 | "lint": "eslint --ext .js,.vue src", 13 | "test:unit": "jest --clearCache && vue-cli-service test:unit", 14 | "test:ci": "npm run lint && npm run test:unit" 15 | }, 16 | "dependencies": { 17 | "@dgiot/dgiot-component": "^0.0.3", 18 | "axios": "^0.26.1", 19 | "core-js": "3.23.5", 20 | "echarts": "^4.9.0", 21 | "element-ui": "2.13.2", 22 | "gsap": "^3.11.4", 23 | "js-base64": "^3.7.2", 24 | "js-cookie": "2.2.0", 25 | "js-md5": "^0.7.3", 26 | "mqtt": "^4.3.7", 27 | "normalize.css": "7.0.0", 28 | "nprogress": "0.2.0", 29 | "path-to-regexp": "2.4.0", 30 | "postcss-custom-properties": "^8.0.11", 31 | "postcss-pxtorem": "^5.1.1", 32 | "qs": "^6.11.0", 33 | "three": "^0.147.0", 34 | "three-obj-mtl-loader": "^1.0.3", 35 | "v-charts": "^1.19.0", 36 | "vue": "2.6.10", 37 | "vue-3d-model": "^1.4.1", 38 | "vue-aliplayer-v2": "^1.3.0", 39 | "vue-amis-sdk": "1.10.3", 40 | "vue-baidu-map": "^0.21.22", 41 | "vue-router": "3.0.6", 42 | "vuera": "^0.2.7", 43 | "vuex": "3.1.0", 44 | 45 | "@fortawesome/fontawesome-free": "^5.15.3", 46 | "amis": "3.4.0", 47 | "amis-core": "3.4.0", 48 | "amis-editor": "5.5.0", 49 | "amis-editor-core": "5.5.0", 50 | "amis-formula": "3.4.0", 51 | "amis-ui": "3.4.0", 52 | 53 | "copy-to-clipboard": "^3.2.0", 54 | "mobx": "4.15.7", 55 | "mobx-react": "6.3.1", 56 | "mobx-state-tree": "3.17.3", 57 | "react": "^16.14.0", 58 | "react-dom": "^16.14.0", 59 | "react-hook-form": "^7.39.3", 60 | "react-router": "5.0.1", 61 | "react-router-dom": "5.0.1" 62 | }, 63 | "devDependencies": { 64 | "@vue/cli-plugin-babel": "4.4.4", 65 | "@vue/cli-plugin-eslint": "4.4.4", 66 | "@vue/cli-plugin-unit-jest": "4.4.4", 67 | "@vue/cli-service": "4.4.4", 68 | "@vue/test-utils": "1.0.0-beta.29", 69 | "autoprefixer": "9.5.1", 70 | "babel-eslint": "10.1.0", 71 | "babel-jest": "23.6.0", 72 | "babel-plugin-dynamic-import-node": "2.3.3", 73 | "chalk": "2.4.2", 74 | "connect": "3.6.6", 75 | "copy-to-clipboard": "^3.3.1", 76 | "eslint": "6.7.2", 77 | "eslint-plugin-vue": "6.2.2", 78 | "html-webpack-plugin": "3.2.0", 79 | "runjs": "4.3.2", 80 | "sass": "1.26.8", 81 | "sass-loader": "8.0.2", 82 | "script-ext-html-webpack-plugin": "2.1.3", 83 | "serve-static": "1.13.2", 84 | "svg-sprite-loader": "4.1.3", 85 | "svgo": "1.2.2", 86 | "vue-template-compiler": "2.6.10", 87 | 88 | "@types/lodash": "^4.14.123", 89 | "@types/node": "^11.13.8", 90 | "@types/qs": "^6.5.3", 91 | "@types/react": "^16.9.0", 92 | "@types/react-dom": "^16.9.0", 93 | "@types/react-router": "5.0.1", 94 | "@types/react-router-dom": "^4.3.2", 95 | "acorn": "^8.8.2", 96 | "amis-widget-cli": "^3.2.0", 97 | "monaco-editor-webpack-plugin": "6.0.0", 98 | "prettier": "^2.2.1", 99 | "typescript": "^4.5.5" 100 | }, 101 | "browserslist": [ 102 | "> 1%", 103 | "last 2 versions" 104 | ], 105 | "engines": { 106 | "node": ">=8.9", 107 | "npm": ">= 3.0.0" 108 | }, 109 | "license": "MIT" 110 | } 111 | -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { MessageBox, Message } from 'element-ui' 3 | import store from '@/store' 4 | import { getToken, setToken } from '@/utils/auth' 5 | import route from '../router/index' 6 | // import router from '../router/index' 7 | 8 | // create an axios instance 9 | const service = axios.create({ 10 | baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url 11 | // withCredentials: true, // send cookies when cross-domain requests 12 | timeout: 1000 * 60 * 3 // request timeout 13 | }) 14 | 15 | // request interceptor 16 | service.interceptors.request.use( 17 | config => { 18 | // do something before request is sent 19 | 20 | if (store.getters.token) { 21 | // let each request carry token 22 | // ['X-Token'] is a custom headers key 23 | // please modify it according to the actual situation 24 | config.headers['sessionToken'] = getToken() 25 | } 26 | return config 27 | }, 28 | error => { 29 | // do something with request error 30 | // console.log('错误', error) // for debug 31 | return Promise.reject(error) 32 | } 33 | ) 34 | 35 | // response interceptor 36 | service.interceptors.response.use( 37 | /** 38 | * If you want to get http information such as headers or status 39 | * Please return response => response 40 | */ 41 | 42 | /** 43 | * Determine the request status by custom code 44 | * Here is just an example 45 | * You can also judge the status by HTTP Status Code 46 | */ 47 | response => { 48 | const res = response 49 | 50 | if (res.status != 200) { 51 | // Message({ 52 | // message: res.statusText || 'Error', 53 | // type: 'error', 54 | // duration: 5 * 1000 55 | // }) 56 | 57 | // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired; 58 | if (res.status === 403) { 59 | // to re-login 60 | Message({ 61 | message: '暂无权限', 62 | type: 'error', 63 | duration: 5 * 1000 64 | }) 65 | } else if (res.status === 401) { 66 | Message({ 67 | message: '登录失效', 68 | type: 'error', 69 | duration: 5 * 1000 70 | }) 71 | 72 | // MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', { 73 | // confirmButtonText: 'Re-Login', 74 | // cancelButtonText: 'Cancel', 75 | // type: 'warning' 76 | // }).then(() => { 77 | // store.dispatch('user/resetToken').then(() => { 78 | // location.reload() 79 | // }) 80 | // }) 81 | } 82 | return Promise.reject(new Error(res.message || 'Error')) 83 | } else { 84 | return res.data 85 | } 86 | }, 87 | error => { 88 | console.log('错误', error) 89 | const info = error.msg || error.message 90 | Message({ 91 | message: info, 92 | type: 'error', 93 | duration: 5 * 1000 94 | }) 95 | if (error.message.indexOf('401') >= 0) { 96 | // console.log('异常错误', error.message) // for debug 97 | setToken() 98 | store.dispatch('user/resetToken').then(() => { 99 | location.reload() 100 | }) 101 | // router.push({ 102 | // path: '/login', 103 | // replace: true, 104 | // }) 105 | // window.location.href = 'login' 106 | // history.push("/login"); 107 | // router.push({ path: '/login' }) 108 | // route.replace({ path: "/login" }); 109 | // router.push({ path: "/login" }); 110 | } 111 | return Promise.reject(error) 112 | } 113 | ) 114 | 115 | export default service 116 | -------------------------------------------------------------------------------- /src/utils/instance.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { getToken } from '@/utils/auth' 3 | // import { MessageBox, Message } from "element-ui"; 4 | // import store from "@/store"; 5 | // import router from '@/router' 6 | // import { getToken,removeToken } from "@/utils/auth"; 7 | // import { Toast } from "vant"; 8 | // axios.post 9 | // create an axios instance 10 | const service = axios.create({ 11 | // baseURL: "https://test.hgjzt.com", //https://test.hgjzt.com //"https://prod.dgiotcloud.com", //https://shuukgold.ga //http://45.40.194.29:28019 //process.env.VUE_APP_BASE_API, // url = base url + request url 12 | // withCredentials: true, // send cookies when cross-domain requests 13 | timeout: 360000 // request timeout 14 | }); 15 | 16 | // request interceptor 17 | service.interceptors.request.use( 18 | config => { 19 | // console.log("222",config.headers['Content-Type']); 20 | if(!config.headers['Content-Type']){ 21 | // console.log("111"); 22 | config.headers['Content-Type'] = 'application/json'; 23 | } 24 | 25 | config.headers["sessionToken"] = getToken() || '' 26 | // } 27 | return config; 28 | }, 29 | error => { 30 | console.log("111"); 31 | // do something with request error 32 | // console.log("Hi, this is the ERROR in Request!!", error); // for debug 33 | return Promise.reject(error); 34 | } 35 | ); 36 | 37 | // response interceptor 38 | service.interceptors.response.use( 39 | response => { 40 | // console.log("response",response); 41 | const code = response.status 42 | if (code < 200 || (code > 300&&code<400)) { 43 | // Notification.error({ 44 | // title: response.message 45 | // }) 46 | return Promise.reject('error') 47 | } else { 48 | return response.data 49 | } 50 | }, 51 | error => { 52 | // console.log("error",error.response); 53 | let code = 0 54 | try { 55 | code = error.response.status 56 | console.log("code",code); 57 | if(code==401){ 58 | // console.log("111"); 59 | // Toast.fail("登录已失效"); 60 | // setTimeout(()=>{ 61 | // router.push({ path: '/login' }) 62 | // removeToken(); 63 | // },2000) 64 | 65 | // store.dispatch('/manage/login').then(() => { 66 | // location.reload(true) 67 | // }) 68 | } 69 | } catch (e) { 70 | if (error.toString().indexOf('Error: timeout') !== -1) { 71 | // Notification.error({ 72 | // title: '网络请求超时', 73 | // duration: 360000 74 | // }) 75 | return Promise.reject(error) 76 | } 77 | } 78 | if (code) { 79 | // if(code==401 &&res.data.code==209) 80 | if (code === 401) { 81 | // removeToken(); 82 | // router.push(`/login?redirect=${this.$route.fullPath}`); 83 | // removeToken() 84 | // store.dispatch('/login').then(() => { 85 | // // 用户登录界面提示 86 | // // Cookies.set('point', 401) 87 | // location.reload() 88 | // }) 89 | } else if (code === 403) { 90 | // router.push({ path: '/401' }) 91 | } else { 92 | const errorMsg = error.response.data.message 93 | if (errorMsg !== undefined) { 94 | // Notification.error({ 95 | // title: errorMsg, 96 | // duration: 360000 97 | // }) 98 | } 99 | } 100 | } else { 101 | // Notification.error({ 102 | // title: '接口请求失败', 103 | // duration: 360000 104 | // }) 105 | } 106 | // console.log("测试",error); 107 | return Promise.reject(error) 108 | } 109 | ) 110 | 111 | export default service; 112 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by PanJiaChen on 16/11/18. 3 | */ 4 | 5 | /** 6 | * Parse the time to string 7 | * @param {(Object|string|number)} time 8 | * @param {string} cFormat 9 | * @returns {string | null} 10 | */ 11 | export function parseTime(time, cFormat) { 12 | if (arguments.length === 0 || !time) { 13 | return null 14 | } 15 | const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' 16 | let date 17 | if (typeof time === 'object') { 18 | date = time 19 | } else { 20 | if ((typeof time === 'string')) { 21 | if ((/^[0-9]+$/.test(time))) { 22 | // support "1548221490638" 23 | time = parseInt(time) 24 | } else { 25 | // support safari 26 | // https://stackoverflow.com/questions/4310953/invalid-date-in-safari 27 | time = time.replace(new RegExp(/-/gm), '/') 28 | } 29 | } 30 | 31 | if ((typeof time === 'number') && (time.toString().length === 10)) { 32 | time = time * 1000 33 | } 34 | date = new Date(time) 35 | } 36 | const formatObj = { 37 | y: date.getFullYear(), 38 | m: date.getMonth() + 1, 39 | d: date.getDate(), 40 | h: date.getHours(), 41 | i: date.getMinutes(), 42 | s: date.getSeconds(), 43 | a: date.getDay() 44 | } 45 | const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => { 46 | const value = formatObj[key] 47 | // Note: getDay() returns 0 on Sunday 48 | if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] } 49 | return value.toString().padStart(2, '0') 50 | }) 51 | return time_str 52 | } 53 | 54 | /** 55 | * @param {number} time 56 | * @param {string} option 57 | * @returns {string} 58 | */ 59 | export function formatTime(time, option) { 60 | if (('' + time).length === 10) { 61 | time = parseInt(time) * 1000 62 | } else { 63 | time = +time 64 | } 65 | const d = new Date(time) 66 | const now = Date.now() 67 | 68 | const diff = (now - d) / 1000 69 | 70 | if (diff < 30) { 71 | return '刚刚' 72 | } else if (diff < 3600) { 73 | // less 1 hour 74 | return Math.ceil(diff / 60) + '分钟前' 75 | } else if (diff < 3600 * 24) { 76 | return Math.ceil(diff / 3600) + '小时前' 77 | } else if (diff < 3600 * 24 * 2) { 78 | return '1天前' 79 | } 80 | if (option) { 81 | return parseTime(time, option) 82 | } else { 83 | return ( 84 | d.getMonth() + 85 | 1 + 86 | '月' + 87 | d.getDate() + 88 | '日' + 89 | d.getHours() + 90 | '时' + 91 | d.getMinutes() + 92 | '分' 93 | ) 94 | } 95 | } 96 | 97 | /** 98 | * @param {string} url 99 | * @returns {Object} 100 | */ 101 | export function param2Obj(url) { 102 | const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') 103 | if (!search) { 104 | return {} 105 | } 106 | const obj = {} 107 | const searchArr = search.split('&') 108 | searchArr.forEach(v => { 109 | const index = v.indexOf('=') 110 | if (index !== -1) { 111 | const name = v.substring(0, index) 112 | const val = v.substring(index + 1, v.length) 113 | obj[name] = val 114 | } 115 | }) 116 | return obj 117 | } 118 | export function utc2beijing(utc_datetime) { 119 | // 转为正常的时间格式 年-月-日 时:分:秒 120 | var date = new Date(+new Date(utc_datetime) + 8 * 3600 * 1000) 121 | .toISOString() 122 | .replace(/T/g, ' ') 123 | .replace(/\.[\d]{3}Z/, '') 124 | return date // 2017-03-31 16:02:06 125 | } 126 | export function isPC() { 127 | var userAgentInfo = navigator.userAgent 128 | var Agents = new Array( 129 | 'Android', 130 | 'iPhone', 131 | 'SymbianOS', 132 | 'Windows Phone', 133 | 'iPad', 134 | 'iPod' 135 | ) 136 | var flag = true 137 | for (var v = 0; v < Agents.length; v++) { 138 | if (userAgentInfo.indexOf(Agents[v]) > 0) { 139 | flag = false 140 | break 141 | } 142 | } 143 | return flag 144 | } 145 | function FormatTime(t) { 146 | if (t < 10) return "0" + t; 147 | return t; 148 | } 149 | export function getTime() { 150 | var myDate = new Date(); 151 | let yy = myDate.getFullYear(); // 获取完整的年份(4位,1970-????) 152 | let mm = myDate.getMonth() + 1; // 获取当前月份(0-11,0代表1月) 153 | mm = FormatTime(mm); 154 | let dd = myDate.getDate(); // 获取当前日(1-31) 155 | dd = FormatTime(dd); 156 | let hh = myDate.getHours(); // 获取当前小时数(0-23) 157 | hh = FormatTime(hh); 158 | let mt = myDate.getMinutes(); // 获取当前分钟数(0-59) 159 | mt = FormatTime(mt); 160 | let ss = myDate.getSeconds(); // 获取当前秒数(0-59) 161 | ss = FormatTime(ss); 162 | // let ms = myDate.getMilliseconds(); 163 | //2021-10-22T10:13:39.229Z 164 | let currentTime = 165 | yy + "-" + mm + "-" + dd + " " + hh + ":" + mt + ":" + ss; 166 | return currentTime; 167 | } -------------------------------------------------------------------------------- /public/resources/tween.min.js: -------------------------------------------------------------------------------- 1 | // tween.js - http://github.com/sole/tween.js 2 | 'use strict';var TWEEN=TWEEN||function(){var a=[];return{REVISION:"7",getAll:function(){return a},removeAll:function(){a=[]},add:function(c){a.push(c)},remove:function(c){c=a.indexOf(c);-1!==c&&a.splice(c,1)},update:function(c){if(0===a.length)return!1;for(var b=0,d=a.length,c=void 0!==c?c:Date.now();b(a*=2)?0.5*a*a:-0.5*(--a*(a-2)-1)}},Cubic:{In:function(a){return a*a*a},Out:function(a){return--a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a:0.5*((a-=2)*a*a+2)}},Quartic:{In:function(a){return a*a*a*a},Out:function(a){return 1- --a*a*a*a},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)}},Quintic:{In:function(a){return a*a*a* 7 | a*a},Out:function(a){return--a*a*a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)}},Sinusoidal:{In:function(a){return 1-Math.cos(a*Math.PI/2)},Out:function(a){return Math.sin(a*Math.PI/2)},InOut:function(a){return 0.5*(1-Math.cos(Math.PI*a))}},Exponential:{In:function(a){return 0===a?0:Math.pow(1024,a-1)},Out:function(a){return 1===a?1:1-Math.pow(2,-10*a)},InOut:function(a){return 0===a?0:1===a?1:1>(a*=2)?0.5*Math.pow(1024,a-1):0.5*(-Math.pow(2,-10*(a-1))+2)}},Circular:{In:function(a){return 1- 8 | Math.sqrt(1-a*a)},Out:function(a){return Math.sqrt(1- --a*a)},InOut:function(a){return 1>(a*=2)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)}},Elastic:{In:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return-(b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4))},Out:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return b*Math.pow(2,-10*a)*Math.sin((a-c)* 9 | 2*Math.PI/0.4)+1},InOut:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return 1>(a*=2)?-0.5*b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4):0.5*b*Math.pow(2,-10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4)+1}},Back:{In:function(a){return a*a*(2.70158*a-1.70158)},Out:function(a){return--a*a*(2.70158*a+1.70158)+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*(3.5949095*a-2.5949095):0.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)}},Bounce:{In:function(a){return 1- 10 | TWEEN.Easing.Bounce.Out(1-a)},Out:function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375},InOut:function(a){return 0.5>a?0.5*TWEEN.Easing.Bounce.In(2*a):0.5*TWEEN.Easing.Bounce.Out(2*a-1)+0.5}}}; 11 | TWEEN.Interpolation={Linear:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.Linear;return 0>c?f(a[0],a[1],d):1b?b:e+1],d-e)},Bezier:function(a,c){var b=0,d=a.length-1,e=Math.pow,f=TWEEN.Interpolation.Utils.Bernstein,h;for(h=0;h<=d;h++)b+=e(1-c,d-h)*e(c,h)*a[h]*f(d,h);return b},CatmullRom:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.CatmullRom;return a[0]===a[b]?(0>c&&(e=Math.floor(d=b*(1+c))),f(a[(e- 12 | 1+b)%b],a[e],a[(e+1)%b],a[(e+2)%b],d-e)):0>c?a[0]-(f(a[0],a[0],a[1],a[1],-d)-a[0]):1 2 |

3 |
8 |
9 |
14 |
15 | 报警信息:  {{ item.content.alarm_message }} 18 |
19 |
20 | 报警时间:   {{ utc2beijing(item.createdAt) }} 21 |
22 |
23 |
24 |
25 |
26 | 27 | 28 | 122 | 143 | 167 | -------------------------------------------------------------------------------- /src/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 119 | 120 | 156 | 157 | 165 | -------------------------------------------------------------------------------- /src/permission.js: -------------------------------------------------------------------------------- 1 | import router from './router' 2 | import store from './store' 3 | import { Message } from 'element-ui' 4 | import NProgress from 'nprogress' // progress bar 5 | import 'nprogress/nprogress.css' // progress bar style 6 | import { getToken } from '@/utils/auth' // get token from cookie 7 | import getPageTitle from '@/utils/get-page-title' 8 | import Cookies from 'js-cookie' 9 | import { getNavigation } from '@/api/Navigation' 10 | import { Roletree } from '@/api/User/index' 11 | import { postCookie } from '@/api/Dashboard/index' 12 | import VueRouter from 'vue-router' 13 | 14 | NProgress.configure({ showSpinner: false }) // NProgress Configuration 15 | 16 | const whiteList = ['/login'] // no redirect whitelist 17 | 18 | router.beforeEach(async(to, from, next) => { 19 | // start progress bar 20 | NProgress.start() 21 | 22 | // set page title 23 | document.title = getPageTitle(to.meta.title) 24 | 25 | // determine whether the user has logged in 26 | // console.log('路由拦截',hasToken); 27 | console.error('to.query', to.query) 28 | if (to.query.username) { 29 | // http://192.168.2.132:3333/#/dashboard?username=ailiandata&password=fJYjEFg59H 30 | // https://work.dgiotcloud.com/#/dashboard?username=ailiandata&password=fJYjEFg59H 31 | // https://work.dgiotcloud.com/#/amis/View/f864c8f234?username=ailiandata&password=fJYjEFg59H 32 | await store.dispatch('user/login', { 33 | username: to.query.username || '', 34 | password: to.query.password || '' 35 | }) 36 | .then(async() => { 37 | Vue.prototype.$FileServe = Cookies.get('fileServer') || '' 38 | const res = await getNavigation() 39 | console.log('路由', res) 40 | const list = [] 41 | const item = { 42 | path: '/', 43 | url: '/', 44 | // component: Layout, 45 | redirect: '/dashboard', 46 | meta: { 47 | title: '首页', 48 | icon: 'dashboard', 49 | hidden: false 50 | }, 51 | children: [ 52 | { 53 | path: '/dashboard', 54 | url: '/dashboard', 55 | name: 'Dashboard', 56 | // component: () => import("@/views/dashboard/index"), 57 | meta: { title: '首页', icon: 'el-icon-s-home', hidden: false } 58 | } 59 | ] 60 | } 61 | list.push(item) 62 | res.results.forEach((item) => { 63 | if (item.url != '/' && item.url != '/roles') { 64 | list.push(item) 65 | } 66 | }) 67 | store.dispatch('user/initRoutes', list).then(res => { 68 | const routelist = [] 69 | // 过滤admin 中的路由 70 | res.forEach((element, index) => { 71 | if (element.children) { 72 | if ( 73 | element.children[0].url.indexOf('amis') >= 0 || 74 | element.children[0].name.indexOf('Dashboard') >= 0 75 | ) { 76 | routelist.push(element) 77 | } 78 | } else { 79 | routelist.push(element) 80 | } 81 | }) 82 | localStorage.setItem('routes', JSON.stringify(routelist)) // 路由列表 83 | }) 84 | const roletree = await Roletree() 85 | localStorage.setItem( 86 | 'dgiotroletree', 87 | JSON.stringify(roletree.results) 88 | ) 89 | // 设置使用什么地图 90 | const data = { 91 | UserSession: getToken() || '', 92 | cookie: { 93 | mapType: 'baidu' 94 | } 95 | } 96 | await postCookie(data) 97 | }) 98 | } 99 | const hasToken = getToken() 100 | if (hasToken) { 101 | if (to.path === '/login') { 102 | // if is logged in, redirect to the home page 103 | next({ path: '/' }) 104 | NProgress.done() 105 | } else { 106 | const hasGetUserInfo = store.getters.name 107 | if (hasGetUserInfo) { 108 | next() 109 | } else { 110 | try { 111 | // get user info 112 | // await store.dispatch('user/getInfo') 113 | 114 | next() 115 | } catch (error) { 116 | // remove token and go to login page to re-login 117 | await store.dispatch('user/resetToken') 118 | Message.error(error || 'Has Error') 119 | next(`/login?redirect=${to.path}`) 120 | NProgress.done() 121 | } 122 | } 123 | } 124 | } else { 125 | /* has no token*/ 126 | // next(`/login?redirect=${to.path}`) 127 | if (whiteList.indexOf(to.path) !== -1) { 128 | // in the free login whitelist, go directly 129 | next() 130 | } else { 131 | // other pages that do not have permission to access are redirected to the login page. 132 | next(`/login?redirect=${to.path}`) 133 | NProgress.done() 134 | } 135 | } 136 | }) 137 | 138 | router.afterEach(() => { 139 | // finish progress bar 140 | NProgress.done() 141 | }) 142 | -------------------------------------------------------------------------------- /src/components/model3d/dgiot-model-demo2.vue: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 145 | 151 | -------------------------------------------------------------------------------- /src/views/Device/DeviceRealCard/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 123 | 129 | 170 | -------------------------------------------------------------------------------- /src/styles/sidebar.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | 3 | .main-container { 4 | min-height: 100%; 5 | transition: margin-left .28s; 6 | margin-left: $sideBarWidth; 7 | position: relative; 8 | } 9 | 10 | .sidebar-container { 11 | transition: width 0.28s; 12 | width: $sideBarWidth !important; 13 | background-color: $menuBg; 14 | height: 100%; 15 | position: fixed; 16 | font-size: 0px; 17 | top: 0; 18 | bottom: 0; 19 | left: 0; 20 | z-index: 1001; 21 | overflow: hidden; 22 | 23 | // reset element-ui css 24 | .horizontal-collapse-transition { 25 | transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; 26 | } 27 | 28 | .scrollbar-wrapper { 29 | overflow-x: hidden !important; 30 | } 31 | 32 | .el-scrollbar__bar.is-vertical { 33 | right: 0px; 34 | } 35 | 36 | .el-scrollbar { 37 | height: 100%; 38 | } 39 | 40 | &.has-logo { 41 | .el-scrollbar { 42 | height: calc(100% - 50px); 43 | } 44 | } 45 | 46 | .is-horizontal { 47 | display: none; 48 | } 49 | 50 | a { 51 | display: inline-block; 52 | width: 100%; 53 | overflow: hidden; 54 | } 55 | 56 | .svg-icon { 57 | margin-right: 16px; 58 | } 59 | 60 | .sub-el-icon { 61 | margin-right: 12px; 62 | margin-left: -2px; 63 | } 64 | 65 | .el-menu { 66 | border: none; 67 | height: 100%; 68 | width: 100% !important; 69 | } 70 | 71 | // menu hover 72 | .submenu-title-noDropdown, 73 | .el-submenu__title { 74 | &:hover { 75 | background-color: $menuHover !important; 76 | } 77 | } 78 | 79 | .is-active>.el-submenu__title { 80 | color: $subMenuActiveText !important; 81 | } 82 | 83 | & .nest-menu .el-submenu>.el-submenu__title, 84 | & .el-submenu .el-menu-item { 85 | min-width: $sideBarWidth !important; 86 | background-color: $subMenuBg !important; 87 | 88 | &:hover { 89 | background-color: $subMenuHover !important; 90 | } 91 | } 92 | } 93 | 94 | .hideSidebar { 95 | .sidebar-container { 96 | width: 54px !important; 97 | } 98 | 99 | .main-container { 100 | margin-left: 54px; 101 | } 102 | 103 | .submenu-title-noDropdown { 104 | padding: 0 !important; 105 | position: relative; 106 | 107 | .el-tooltip { 108 | padding: 0 !important; 109 | 110 | .svg-icon { 111 | margin-left: 20px; 112 | } 113 | 114 | .sub-el-icon { 115 | margin-left: 19px; 116 | } 117 | } 118 | } 119 | 120 | .el-submenu { 121 | overflow: hidden; 122 | 123 | &>.el-submenu__title { 124 | padding: 0 !important; 125 | 126 | .svg-icon { 127 | margin-left: 20px; 128 | } 129 | 130 | .sub-el-icon { 131 | margin-left: 19px; 132 | } 133 | 134 | .el-submenu__icon-arrow { 135 | display: none; 136 | } 137 | } 138 | } 139 | 140 | .el-menu--collapse { 141 | .el-submenu { 142 | &>.el-submenu__title { 143 | &>span { 144 | height: 0; 145 | width: 0; 146 | overflow: hidden; 147 | visibility: hidden; 148 | display: inline-block; 149 | } 150 | } 151 | } 152 | } 153 | } 154 | 155 | .el-menu--collapse .el-menu .el-submenu { 156 | min-width: $sideBarWidth !important; 157 | } 158 | 159 | // mobile responsive 160 | .mobile { 161 | .main-container { 162 | margin-left: 0px; 163 | } 164 | 165 | .sidebar-container { 166 | transition: transform .28s; 167 | width: $sideBarWidth !important; 168 | } 169 | 170 | &.hideSidebar { 171 | .sidebar-container { 172 | pointer-events: none; 173 | transition-duration: 0.3s; 174 | transform: translate3d(-$sideBarWidth, 0, 0); 175 | } 176 | } 177 | } 178 | 179 | .withoutAnimation { 180 | 181 | .main-container, 182 | .sidebar-container { 183 | transition: none; 184 | } 185 | } 186 | } 187 | 188 | // when menu collapsed 189 | .el-menu--vertical { 190 | &>.el-menu { 191 | .svg-icon { 192 | margin-right: 16px; 193 | } 194 | .sub-el-icon { 195 | margin-right: 12px; 196 | margin-left: -2px; 197 | } 198 | } 199 | 200 | .nest-menu .el-submenu>.el-submenu__title, 201 | .el-menu-item { 202 | &:hover { 203 | // you can use $subMenuHover 204 | background-color: $menuHover !important; 205 | } 206 | } 207 | 208 | // the scroll bar appears when the subMenu is too long 209 | >.el-menu--popup { 210 | max-height: 100vh; 211 | overflow-y: auto; 212 | 213 | &::-webkit-scrollbar-track-piece { 214 | background: #d3dce6; 215 | } 216 | 217 | &::-webkit-scrollbar { 218 | width: 6px; 219 | } 220 | 221 | &::-webkit-scrollbar-thumb { 222 | background: #99a9bf; 223 | border-radius: 20px; 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/SidebarItem.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 160 | -------------------------------------------------------------------------------- /src/components/model3d/dgiot-model-fbx.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | 191 | 197 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | parser: 'babel-eslint', 5 | sourceType: 'module' 6 | }, 7 | env: { 8 | browser: true, 9 | node: true, 10 | es6: true, 11 | }, 12 | extends: ['plugin:vue/recommended', 'eslint:recommended'], 13 | 14 | // add your custom rules here 15 | //it is base on https://github.com/vuejs/eslint-config-vue 16 | rules: { 17 | "vue/max-attributes-per-line": [2, { 18 | "singleline": 10, 19 | "multiline": { 20 | "max": 1, 21 | "allowFirstLine": false 22 | } 23 | }], 24 | "vue/singleline-html-element-content-newline": "off", 25 | "vue/multiline-html-element-content-newline":"off", 26 | "vue/name-property-casing": ["error", "PascalCase"], 27 | "vue/no-v-html": "off", 28 | 'accessor-pairs': 2, 29 | 'arrow-spacing': [2, { 30 | 'before': true, 31 | 'after': true 32 | }], 33 | 'block-spacing': [2, 'always'], 34 | 'brace-style': [2, '1tbs', { 35 | 'allowSingleLine': true 36 | }], 37 | 'camelcase': [0, { 38 | 'properties': 'always' 39 | }], 40 | 'comma-dangle': [2, 'never'], 41 | 'comma-spacing': [2, { 42 | 'before': false, 43 | 'after': true 44 | }], 45 | 'comma-style': [2, 'last'], 46 | 'constructor-super': 2, 47 | 'curly': [2, 'multi-line'], 48 | 'dot-location': [2, 'property'], 49 | 'eol-last': 2, 50 | 'eqeqeq': ["error", "always", {"null": "ignore"}], 51 | 'generator-star-spacing': [2, { 52 | 'before': true, 53 | 'after': true 54 | }], 55 | 'handle-callback-err': [2, '^(err|error)$'], 56 | 'indent': [2, 2, { 57 | 'SwitchCase': 1 58 | }], 59 | 'jsx-quotes': [2, 'prefer-single'], 60 | 'key-spacing': [2, { 61 | 'beforeColon': false, 62 | 'afterColon': true 63 | }], 64 | 'keyword-spacing': [2, { 65 | 'before': true, 66 | 'after': true 67 | }], 68 | 'new-cap': [2, { 69 | 'newIsCap': true, 70 | 'capIsNew': false 71 | }], 72 | 'new-parens': 2, 73 | 'no-array-constructor': 2, 74 | 'no-caller': 2, 75 | 'no-console': 'off', 76 | 'no-class-assign': 2, 77 | 'no-cond-assign': 2, 78 | 'no-const-assign': 2, 79 | 'no-control-regex': 0, 80 | 'no-delete-var': 2, 81 | 'no-dupe-args': 2, 82 | 'no-dupe-class-members': 2, 83 | 'no-dupe-keys': 2, 84 | 'no-duplicate-case': 2, 85 | 'no-empty-character-class': 2, 86 | 'no-empty-pattern': 2, 87 | 'no-eval': 2, 88 | 'no-ex-assign': 2, 89 | 'no-extend-native': 2, 90 | 'no-extra-bind': 2, 91 | 'no-extra-boolean-cast': 2, 92 | 'no-extra-parens': [2, 'functions'], 93 | 'no-fallthrough': 2, 94 | 'no-floating-decimal': 2, 95 | 'no-func-assign': 2, 96 | 'no-implied-eval': 2, 97 | 'no-inner-declarations': [2, 'functions'], 98 | 'no-invalid-regexp': 2, 99 | 'no-irregular-whitespace': 2, 100 | 'no-iterator': 2, 101 | 'no-label-var': 2, 102 | 'no-labels': [2, { 103 | 'allowLoop': false, 104 | 'allowSwitch': false 105 | }], 106 | 'no-lone-blocks': 2, 107 | 'no-mixed-spaces-and-tabs': 2, 108 | 'no-multi-spaces': 2, 109 | 'no-multi-str': 2, 110 | 'no-multiple-empty-lines': [2, { 111 | 'max': 1 112 | }], 113 | 'no-native-reassign': 2, 114 | 'no-negated-in-lhs': 2, 115 | 'no-new-object': 2, 116 | 'no-new-require': 2, 117 | 'no-new-symbol': 2, 118 | 'no-new-wrappers': 2, 119 | 'no-obj-calls': 2, 120 | 'no-octal': 2, 121 | 'no-octal-escape': 2, 122 | 'no-path-concat': 2, 123 | 'no-proto': 2, 124 | 'no-redeclare': 2, 125 | 'no-regex-spaces': 2, 126 | 'no-return-assign': [2, 'except-parens'], 127 | 'no-self-assign': 2, 128 | 'no-self-compare': 2, 129 | 'no-sequences': 2, 130 | 'no-shadow-restricted-names': 2, 131 | 'no-spaced-func': 2, 132 | 'no-sparse-arrays': 2, 133 | 'no-this-before-super': 2, 134 | 'no-throw-literal': 2, 135 | 'no-trailing-spaces': 2, 136 | 'no-undef': 2, 137 | 'no-undef-init': 2, 138 | 'no-unexpected-multiline': 2, 139 | 'no-unmodified-loop-condition': 2, 140 | 'no-unneeded-ternary': [2, { 141 | 'defaultAssignment': false 142 | }], 143 | 'no-unreachable': 2, 144 | 'no-unsafe-finally': 2, 145 | 'no-unused-vars': [2, { 146 | 'vars': 'all', 147 | 'args': 'none' 148 | }], 149 | 'no-useless-call': 2, 150 | 'no-useless-computed-key': 2, 151 | 'no-useless-constructor': 2, 152 | 'no-useless-escape': 0, 153 | 'no-whitespace-before-property': 2, 154 | 'no-with': 2, 155 | 'one-var': [2, { 156 | 'initialized': 'never' 157 | }], 158 | 'operator-linebreak': [2, 'after', { 159 | 'overrides': { 160 | '?': 'before', 161 | ':': 'before' 162 | } 163 | }], 164 | 'padded-blocks': [2, 'never'], 165 | 'quotes': [2, 'single', { 166 | 'avoidEscape': true, 167 | 'allowTemplateLiterals': true 168 | }], 169 | 'semi': [2, 'never'], 170 | 'semi-spacing': [2, { 171 | 'before': false, 172 | 'after': true 173 | }], 174 | 'space-before-blocks': [2, 'always'], 175 | 'space-before-function-paren': [2, 'never'], 176 | 'space-in-parens': [2, 'never'], 177 | 'space-infix-ops': 2, 178 | 'space-unary-ops': [2, { 179 | 'words': true, 180 | 'nonwords': false 181 | }], 182 | 'spaced-comment': [2, 'always', { 183 | 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] 184 | }], 185 | 'template-curly-spacing': [2, 'never'], 186 | 'use-isnan': 2, 187 | 'valid-typeof': 2, 188 | 'wrap-iife': [2, 'any'], 189 | 'yield-star-spacing': [2, 'both'], 190 | 'yoda': [2, 'never'], 191 | 'prefer-const': 2, 192 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 193 | 'object-curly-spacing': [2, 'always', { 194 | objectsInObjects: false 195 | }], 196 | 'array-bracket-spacing': [2, 'never'] 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const defaultSettings = require('./src/settings.js') 4 | 5 | function resolve(dir) { 6 | return path.join(__dirname, dir) 7 | } 8 | 9 | const name = defaultSettings.title || 'Amis Vue Template' // page title 10 | 11 | // If your port is set to 80, 12 | // use administrator privileges to execute the command line. 13 | // For example, Mac: sudo npm run 14 | // You can change the port by the following methods: 15 | // port = 9528 npm run dev OR npm run dev --port = 9528 16 | const port = process.env.port || process.env.npm_config_port || 3333 // dev port 17 | // All configuration item explanations can be find in https://cli.vuejs.org/config/ 18 | module.exports = { 19 | /** 20 | * You will need to set publicPath if you plan to deploy your site under a sub path, 21 | * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/, 22 | * then publicPath should be set to "/bar/". 23 | * In most cases please use '/' !!! 24 | * Detail: https://cli.vuejs.org/config/#publicpath 25 | */ 26 | publicPath: './', 27 | outputDir: 'dist', 28 | assetsDir: 'static', 29 | lintOnSave: process.env.NODE_ENV === 'development', 30 | productionSourceMap: false, 31 | devServer: { 32 | port: port, 33 | open: true, 34 | overlay: { 35 | warnings: false, 36 | errors: true 37 | }, 38 | proxy: { 39 | '/iotapi': { 40 | // 代理api 41 | target: 'http://192.168.1.201', // 代理接口(注意只要域名就够了) 42 | secure: 'true', 43 | changeOrigin: true, // 是否跨域 44 | ws: true, // proxy websockets 45 | pathRewrite: { 46 | // 重写路径 47 | '^/iotapi': '/iotapi' // 代理路径 48 | } 49 | }, 50 | '/dgiot_dashboard': { 51 | // 代理api 52 | target: 'http://192.168.1.201', // 代理接口(注意只要域名就够了) 53 | secure: 'true', 54 | changeOrigin: true, // 是否跨域 55 | ws: true, // proxy websockets 56 | pathRewrite: { 57 | // 重写路径 58 | '^/dgiot_dashboard': '/dgiot_dashboard' // 代理路径 59 | } 60 | }, 61 | '/dgiot_file': { 62 | // 代理api 63 | target: 'http://192.168.1.201', // 代理接口(注意只要域名就够了) 64 | secure: 'true', 65 | changeOrigin: true, // 是否跨域 66 | ws: true, // proxy websockets 67 | pathRewrite: { 68 | // 重写路径 69 | '^/dgiot_file': '/dgiot_file' // 代理路径 70 | } 71 | }, 72 | '/upload': { 73 | // 代理api 74 | target: 'http://192.168.1.201', // 代理接口(注意只要域名就够了) 75 | secure: 'true', 76 | changeOrigin: true, // 是否跨域 77 | ws: true, // proxy websockets 78 | pathRewrite: { 79 | // 重写路径 80 | '^/upload': '/upload' // 代理路径 81 | } 82 | } 83 | } 84 | // before: require('./mock/mock-server.js') 85 | }, 86 | configureWebpack: { 87 | // provide the app's title in webpack's name field, so that 88 | // it can be accessed in index.html to inject the correct title. 89 | name: name, 90 | resolve: { 91 | alias: { 92 | '@': resolve('src') 93 | } 94 | } 95 | }, 96 | chainWebpack(config) { 97 | // it can improve the speed of the first screen, it is recommended to turn on preload 98 | config.plugin('preload').tap(() => [ 99 | { 100 | rel: 'preload', 101 | // to ignore runtime.js 102 | // https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171 103 | fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/], 104 | include: 'initial' 105 | } 106 | ]) 107 | 108 | // when there are many pages, it will cause too many meaningless requests 109 | config.plugins.delete('prefetch') 110 | 111 | // set svg-sprite-loader 112 | config.module 113 | .rule('svg') 114 | .exclude.add(resolve('src/icons')) 115 | .end() 116 | config.module 117 | .rule('icons') 118 | .test(/\.svg$/) 119 | .include.add(resolve('src/icons')) 120 | .end() 121 | .use('svg-sprite-loader') 122 | .loader('svg-sprite-loader') 123 | .options({ 124 | symbolId: 'icon-[name]' 125 | }) 126 | .end() 127 | 128 | config 129 | .when(process.env.NODE_ENV !== 'development', 130 | config => { 131 | config 132 | .plugin('ScriptExtHtmlWebpackPlugin') 133 | .after('html') 134 | .use('script-ext-html-webpack-plugin', [{ 135 | // `runtime` must same as runtimeChunk name. default is `runtime` 136 | inline: /runtime\..*\.js$/ 137 | }]) 138 | .end() 139 | config 140 | .optimization.splitChunks({ 141 | chunks: 'all', 142 | cacheGroups: { 143 | libs: { 144 | name: 'chunk-libs', 145 | test: /[\\/]node_modules[\\/]/, 146 | priority: 10, 147 | chunks: 'initial' // only package third parties that are initially dependent 148 | }, 149 | elementUI: { 150 | name: 'chunk-elementUI', // split elementUI into a single package 151 | priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app 152 | test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm 153 | }, 154 | commons: { 155 | name: 'chunk-commons', 156 | test: resolve('src/components'), // can customize your rules 157 | minChunks: 3, // minimum common number 158 | priority: 5, 159 | reuseExistingChunk: true 160 | } 161 | } 162 | }) 163 | // https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk 164 | config.optimization.runtimeChunk('single') 165 | } 166 | ) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/views/dashboard/component/TopoPie.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 199 | 215 | --------------------------------------------------------------------------------