Commit fc6732c7 by 应超杰

init

parents
> 1%
last 2 versions
not IE <= 11
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
BASE_URL=/
ENTRY=./src/main.ts
VUE_APP_ROUTE_BASE=/
VUE_APP_APP_NAME=roms-cnc
VUE_APP_APP_THEME=theme_light
VUE_APP_BASE_URL=https://www.machplat.com/roms-server-cnc
VUE_APP_ROUTE_MODE=history
# 测试用url
VUE_APP_TEST_BASE_URL=https://www.machplat.com/roms-server-cnc
#VUE_APP_TEST_BASE_URL=http://192.168.0.105:10100/roms-server-cnc
VUE_APP_HTML_TITLE=LKT-ROMS
VUE_APP_LOGIN_TITLE1=设备运维管理云平台
VUE_APP_LOGIN_TITLE2=LKT-ROMS
VUE_APP_TITLE_LG=https://roms-cnc.oss-cn-hangzhou.aliyuncs.com/logos/logow1.png
VUE_APP_LOGIN_LG=https://roms-cnc.oss-cn-hangzhou.aliyuncs.com/logos/lkt_logo2.png
VUE_APP_LOGIN_SCHEMA=cnc
NODE_ENV=production
ENTRY=./src/main.ts
BASE_URL=./
VUE_APP_ROUTE_BASE=/
VUE_APP_APP_NAME=roms-cnc
VUE_APP_APP_THEME=theme_light
VUE_APP_BASE_URL=https://www.machplat.com/roms-server-cnc
VUE_APP_ROUTE_MODE=hash
VUE_APP_DEPLOY_MODE=normal
VUE_APP_HTML_TITLE=LKT-ROMS
VUE_APP_LOGIN_TITLE1=设备运维管理云平台
VUE_APP_LOGIN_TITLE2=LKT-ROMS
VUE_APP_TITLE_LG=https://roms-cnc.oss-cn-hangzhou.aliyuncs.com/logos/logow1.png
VUE_APP_LOGIN_LG=https://roms-cnc.oss-cn-hangzhou.aliyuncs.com/logos/lkt_logo2.png
VUE_APP_LOGIN_SCHEMA=cnc
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
weekly.*
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
package-lock.json
.history
# TODO 组件
-[ ] gateway部分中dialog的进一步抽象,不能将原始对象传入。
# BUG
-[ ] io设备切换时,reg_name, reg_type要清空。
-[ ] remote ip网段的限制(有几个ip不能用);
-[ ] 首页界面修改、定位信息的问题
## DONE-BUG
-[x] 远程连接配置时,串口只能选择一次的问题。
-[x] 登录后或切换登录后的首页显示问题。
-[x] 整体的高有问题,下拉. - 工银插件问题
-[x] IPInput 如果输入. 那么就要调到下一个方块了
-[x] IPInput 如果输入的是非法格式?
## DONE-FUN
-[x] ip输入的组件,4个分隔开,并在前一个输完后可以调到后一个。
-[x] 十进制、八进制、十六进制输入组件。
module.exports = {
presets: [
'@vue/app'
]
}
{
"name": "roms_web",
"version": "2.0.0",
"private": true,
"scripts": {
"dev.demo": "vue-cli-service serve --mode dev.demo",
"build.demo": "vue-cli-service build --modern --mode pro.demo",
"lint": "vue-cli-service lint",
"rm_cache": "rm -rf ./node_modules/.cache",
"core": "yarn add git+ssh://git@git.linkortech.com:10022/yingchaojie/web-toolkit.git"
},
"lint-staged": {
"*.ts": [
"vue-cli-service lint",
"git add"
]
},
"dependencies": {
"element-ui": "^2.13.0",
"web-toolkit": "git+ssh://git@git.linkortech.com:10022/yingchaojie/web-toolkit.git"
},
"devDependencies": {
"@babel/core": "7.7.4",
"@types/echarts": "^4.4.1",
"@types/qs": "^6.9.0",
"@vue/babel-preset-app": "^4.2.3",
"@vue/cli-plugin-babel": "^4.2.3",
"@vue/cli-plugin-typescript": "^4.2.3",
"@vue/cli-service": "^4.2.3",
"babel-loader": "^8.0.6",
"core-js": "^3.4.2",
"node-sass": "^4.13.0",
"sass-loader": "^7.3.1",
"typescript": "^3.8.3",
"vue-template-compiler": "^2.6.10"
}
}
module.exports = {
plugins: {
autoprefixer: {}
}
}
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566177859925" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1464" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M532.1 918.9c-9.1 11-25.5 12.6-36.5 3.4-1.2-1-2.4-2.2-3.4-3.4-154.6-186.6-245.7-319-273.2-397.3-12-34.3-18.2-70.4-18.1-106.7 0-174.9 139.4-316.7 311.3-316.7S823.4 240 823.4 414.9c0 37.7-6.5 73.9-18.4 107.4-27.5 78.1-118.6 210.3-272.9 396.6z m-20-398.4c57.3 0 103.8-47.3 103.8-105.6s-46.4-105.5-103.8-105.5-103.8 47.2-103.8 105.5 46.5 105.6 103.8 105.6z" p-id="1465" fill="#515151"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566177859925" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1464" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M532.1 918.9c-9.1 11-25.5 12.6-36.5 3.4-1.2-1-2.4-2.2-3.4-3.4-154.6-186.6-245.7-319-273.2-397.3-12-34.3-18.2-70.4-18.1-106.7 0-174.9 139.4-316.7 311.3-316.7S823.4 240 823.4 414.9c0 37.7-6.5 73.9-18.4 107.4-27.5 78.1-118.6 210.3-272.9 396.6z m-20-398.4c57.3 0 103.8-47.3 103.8-105.6s-46.4-105.5-103.8-105.5-103.8 47.2-103.8 105.5 46.5 105.6 103.8 105.6z" p-id="1465" fill="#d81e06"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566177859925" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1464" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M532.1 918.9c-9.1 11-25.5 12.6-36.5 3.4-1.2-1-2.4-2.2-3.4-3.4-154.6-186.6-245.7-319-273.2-397.3-12-34.3-18.2-70.4-18.1-106.7 0-174.9 139.4-316.7 311.3-316.7S823.4 240 823.4 414.9c0 37.7-6.5 73.9-18.4 107.4-27.5 78.1-118.6 210.3-272.9 396.6z m-20-398.4c57.3 0 103.8-47.3 103.8-105.6s-46.4-105.5-103.8-105.5-103.8 47.2-103.8 105.5 46.5 105.6 103.8 105.6z" p-id="1465" fill="#515151"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1576662003743" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2223" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M514.878 89.234c-177.908 0-322.133 142.358-322.133 317.969s322.133 522.38 322.133 522.38 322.133-346.77 322.133-522.38-144.225-317.969-322.133-317.969zM514.878 565.431c-85.087 0-154.064-68.977-154.064-154.064s68.978-154.064 154.064-154.064 154.064 68.977 154.064 154.064c0 85.087-68.978 154.064-154.064 154.064z" p-id="2224" fill="#09dc17"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1577968201688" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4489" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M514.878 89.234c-177.908 0-322.133 142.358-322.133 317.969s322.133 522.38 322.133 522.38 322.133-346.77 322.133-522.38-144.225-317.969-322.133-317.969zM514.878 565.431c-85.087 0-154.064-68.977-154.064-154.064s68.978-154.064 154.064-154.064 154.064 68.977 154.064 154.064c0 85.087-68.978 154.064-154.064 154.064z" p-id="4490" fill="#f78105"></path></svg>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
<title><%= process.env.VUE_APP_HTML_TITLE %></title>
<script>
if (!!window.ActiveXObject || "ActiveXObject" in window) {
alert("当前浏览器不支持,请使用360浏览器极速模式或谷歌浏览器等现代浏览器");
}
</script>
<script src="https://webapi.amap.com/maps?v=1.4.15&key=00e717e8ef5d85403d95f8fade2920bd&plugin=AMap.Geocoder,AMap.DistrictSearch,AMap.Weather"></script>
<link rel="stylesheet" href="https://at.alicdn.com/t/font_248093_unlsz3fwy.css
"/>
</head>
<body>
<div id="app"></div>
</body>
</html>
<template>
<router-view/>
</template>
<script>
export default {
name: 'app',
};
</script>
<template>
<div class="flex">
<div v-for="(item, index) in addrArray" class="el-input el-input--small" :key="index"
:class="setWrap()" style="display:flex; min-width: 60px;"
onpaste="return false">
<input
class="el-input__inner"
v-model="addrArray[index]" :key="index" style="text-align: center;"
maxlength="3"
:class="setGroup()"
:disabled="!available"
@blur="blur1($event, index)"
@keypress="$event.returnValue=limitCharacter($event, index)"
@keydown="lastFocus($event, index)"
@keyup="nextFocus($event, index)"/>
<div class="point" v-if="index<3">&bull;</div>
</div>
</div>
</template>
<script lang="ts">
import {ref, createComponent, onMounted, watch} from '@vue/composition-api';
import {isNil} from 'web-toolkit/src/utils';
export default createComponent({
name: 'IPInput',
props: {
value: {
type: String,
default: null,
},
available: {
type: Boolean,
default: true,
},
},
setup(props: Record<string, any>, ctx) {
const addrArray = ref<any>(isNil(props.value) ? ['', '', '', ''] : props.value.split('.'));
const addrResult = ref<any>('');
// 独立性区分groupId --- 根据毫秒时间
const groupId = ref<any>(guid());
watch(() => props.value, () => {
addrArray.value = isNil(props.value) ? ['', '', '', ''] : props.value.split('.');
});
// 禁止输入不是数字的字符
const limitCharacter = (event: any, index: number) => {
return !event.key.match(/[^0-9]/);
};
const nextFocus = (event: any, index: number) => {
const dom = document.getElementsByClassName(`${groupId.value}`);
const currInput: any = dom[index];
const nextInput: any = dom[index + 1];
// 输入长度为3聚焦到下一个输入框
if (!(event.keyCode === 46 || event.keyCode === 8) && ![37, 38, 39, 40].includes(event.keyCode)) {
addrArray.value[index] = addrArray.value[index].replace(/[^0-9]/g, '');
event.target.value = addrArray.value[index];
if (currInput.value.length === 3 && nextInput && nextInput.value.length < 3) {
nextInput.focus();
}
}
// 输入为'.'聚焦到下一个输入框
if (event.key === '.' && nextInput) {
nextInput.focus();
}
// 输入大于255自动转为255
if (event.target.value > 255) {
addrArray.value[index] = '255';
event.target.value = '255';
}
// 将ip转为字符串并更新
addrResult.value = addrArray.value.join('.');
ctx.emit('input', addrResult.value);
};
// backspace | delete 删除整个输入框聚焦到上一个输入框
const lastFocus = (event: any, index: number) => {
const dom = document.getElementsByClassName(`${groupId.value}`);
const currInput: any = dom[index];
const lastInput: any = dom[index - 1];
if (event.keyCode === 46 || event.keyCode === 8) {
if (currInput.value.length === 0 && lastInput) {
lastInput.focus();
}
}
};
const blur1 = (event: any, index: number) => {
addrArray.value[index] = addrArray.value[index].replace(/[^0-9]/g, '');
event.target.value = addrArray.value[index];
addrResult.value = addrArray.value.join('.');
ctx.emit('input', addrResult.value);
};
const setWrap = () => {
const obj: any = {};
obj['is-disabled'] = !props.available;
return obj;
};
function guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
const setGroup = () => {
const obj: any = {};
obj[`${groupId.value}`] = true;
return obj;
};
return {
addrArray,
limitCharacter,
nextFocus, lastFocus,
blur1,
setWrap, setGroup,
};
},
});
</script>
<style scoped lang="scss">
.point {
text-align: center;
width: 10px;
}
.is-valid {
border-color: #F56C6C;
-webkit-animation: mymove 2s infinite; /* Chrome, Safari, Opera */
animation: mymove 2s infinite;
}
/* Chrome, Safari, Opera */
@-webkit-keyframes chcolor {
50% {
border-color: #F56C6C;
}
}
/* Standard syntax */
@keyframes chcolor {
50% {
border-color: #F56C6C;
}
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button{
-webkit-appearance: none !important;
margin: 0;
}
</style>
<template>
<div class="out">
<i class="iconfont icon-404"></i>
<div class="info">{{ $route.params.msg === undefined?'当前页面找不到':$route.params.msg }}</div>
<br />
<el-button type="primary" @click="$router.back()">返 回</el-button>
<el-button type="primary" @click="logout()" style="margin-left: 10px">返回至登录页</el-button>
</div>
</template>
<script>
export default {
name: 'page404',
methods: {
logout() {
this.$store.commit('removeToken');
this.$router.push({name: 'login'});
},
},
};
</script>
<style scoped lang="scss">
.out{
text-align: center;
padding: 70px 0;
}
img{
height: 200px;
margin-bottom: 10px;
}
i{
color: $active-text;
font-size: 16rem;
}
.info{
color: $active-text;
margin-bottom: 10px;
font-size: 1.5rem;
}
</style>
// 主菜单
export const mainMenuTitles = [
// 如果没有子导航,直接在这指定CName一样的值
'Demo1', 'icon-monitor',
'Demo2', 'icon-dashboard',
'Demo3', 'icon-dashboard',
];
export const loginTitle1 = process.env.VUE_APP_LOGIN_TITLE1;
export const loginTitle2 = process.env.VUE_APP_LOGIN_TITLE2;
export const loginLogo = process.env.VUE_APP_LOGIN_LG;
export const titleLogo = process.env.VUE_APP_TITLE_LG;
export const schema = process.env.VUE_APP_LOGIN_SCHEMA;
import {postService} from 'web-toolkit/src/case-main/index';
import {Province} from '@/types/beans';
import {postService} from 'web-toolkit/src/case-main/index';
export const Login = async (param: any) => {
// const { data } = await postService('/rest/user/login', param);
// 无接口时的模拟数据
const data = {
user: {
id: 1,
username: 'test',
name: 'test',
role: {
id: 1,
department: {
id: 1,
},
},
phone: '',
extend: {},
},
token: 'test',
};
return data;
};
// export const LoginOut = async () => {
// await postService(urlMap.logout.url);
// };
//
// export const UserUpdatePwd = async (params: any) => {
// await postService(urlMap.pwd_update.url, params);
// };
//
// export const UserUpdateInfo = async (params: any) => {
// await postService(urlMap.user_update_info.url, params);
// };
import Vue from 'vue';
import App from './App.vue';
// 加载所用组件,含core
import './plugin';
import { axiosIntercept, routeIntercept, buildMenu} from 'web-toolkit/src/case-main';
import { mainMenuTitles } from './config';
import './scss/common.scss';
import { routes } from './router/routes';
import { genRouter } from 'web-toolkit/src/case-main/router';
export const router = genRouter(routes);
buildMenu(routes, mainMenuTitles);
routeIntercept(router);
axiosIntercept(router);
const vm = new Vue({
router,
render: (h) => h(App),
});
// 可用于延迟加载
vm.$mount('#app');
import 'echarts/lib/chart/bar';
import 'echarts/lib/chart/pie';
import 'echarts/lib/chart/custom';
import 'echarts/lib/chart/line';
import 'echarts/lib/chart/scatter';
import 'echarts/lib/component/title';
import 'echarts/lib/component/tooltip';
import 'echarts/lib/component/dataZoom';
import 'echarts/lib/component/toolbox';
import 'echarts/lib/component/legend';
import 'echarts/lib/component/visualMap';
// import 'echarts/lib/component/markLine';
// import 'echarts/lib/component/markPoint';
import Vue from 'vue';
import { debounce } from 'web-toolkit/src/utils';
function adapt() {
const screenWidth = window.innerWidth;
let size = '';
if (screenWidth < 940) {
size = 'mini';
} else if (screenWidth < 1000) {
size = 'small';
} else {
size = 'medium';
}
Vue.prototype.$ELEMENT = { size };
}
window.addEventListener('resize', debounce(adapt));
adapt();
import Vue from 'vue';
import { isNumber, formatDateTime, formatDate } from 'web-toolkit/src/utils';
Vue.filter('datetime', (value?: number | Date) => {
if (isNumber(value)) {
const date = new Date(value);
if (date.toString().includes('Invalid')) {
return '- -';
} else {
return formatDateTime(date);
}
} else if (value instanceof Date) {
return formatDateTime(value);
} else {
return '- -';
}
});
Vue.filter('date', (value?: number | Date) => {
if (isNumber(value)) {
const date = new Date(value);
if (date.toString().includes('Invalid')) {
return '- -';
} else {
return formatDate(date);
}
} else if (value instanceof Date) {
return formatDate(value);
} else {
return '- -';
}
});
// 注意放第一个
import './web-toolkit';
import './echarts';
import './filter';
import './element';
import Vue from 'vue';
// scss
import 'web-toolkit/src/scss/common.scss';
import 'web-toolkit/src/scss/vivify.scss';
// 注意加载顺序
import 'web-toolkit/src/plugins';
import 'web-toolkit/src/case-main';
import 'web-toolkit/src/filter/date-time';
// components
import lktTable from 'web-toolkit/src/components/lkt-table.vue';
Vue.component('lkt-table', lktTable);
import lktSelect from 'web-toolkit/src/components/lkt-select.vue';
Vue.component('lkt-select', lktSelect);
import LktDatePicker from 'web-toolkit/src/components/lkt-date-picker.vue';
Vue.component('lkt-date-picker', LktDatePicker);
import LktCheck from 'web-toolkit/src/components/lkt-check.vue';
Vue.component('lkt-check', LktCheck);
import KitDialogSimple from 'web-toolkit/src/components/kit-dialog-simple.vue';
Vue.component('kit-dialog-simple', KitDialogSimple);
import KitErrChannel from 'web-toolkit/src/components/kit-err-channel.vue';
Vue.component('kit-err-channel', KitErrChannel);
import { PRIVILEGE } from '@/types/privilege';
const demo1 = {
path: '/demo1',
name: 'index-page',
component: () => import('../views/demo1/demo1.vue'),
meta: {
// privileges: [PRIVILEGE.USER_MNG, PRIVILEGE.USER_LIST],
CName: 'Demo1-subtitle',
parentCName: 'Demo1',
},
};
const demo2 = {
path: '/demo2',
name: 'demo2',
component: () => import('../views/demo2/demo2.vue'),
meta: {
CName: 'Demo2-subtitle',
parentCName: 'Demo2',
},
};
// 按顺序 用于菜单的排列
const indexChildren = [
demo1,
demo2,
];
export const routes = [
{
path: '/login',
name: 'login',
component: () => import('../views/login.vue'),
meta: { authDisabled: true },
},
{
path: '/',
name: 'index',
redirect: 'index-page',
component: () => import('../views/main/index.vue'),
children: [
...indexChildren,
{
path: '*',
name: '404_child',
component: () => import('../component/page404.vue'),
meta: { authDisabled: true },
},
],
},
{
path: '*',
name: '404',
component: () => import('../component/page404.vue'),
meta: { authDisabled: true },
},
];
.router-link {
color: #0ae;
text-decoration: none;
font-size: 1rem;
&:hover {
text-decoration: underline;
}
}
.full-screen {
position: fixed;
z-index: 2000;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.card {
box-shadow: 0 0 5px 0 rgba(0,0,0,.2);
border: 1px solid #EBEEF5;
background-color: #fff;
border-radius: 5px;
color: #303133;
&.card__transition {
transition: .3s;
}
}
.card-header {
padding: 10px 20px;
line-height: 1;
font-size: 1.1rem;
}
.card-body {
padding: 10px 20px;
}
.no-data {
width: 100%;
margin-top: 10rem;
color: #b0bebf;
font-weight: bold;
font-size: 3rem;
text-align: center;
}
.lkt-select--all {
padding: 5px 20px;
}
.main {
height: 100%;
box-sizing: border-box;
}
.el-card__header {
padding: 5px 20px;
}
.el-button + .el-button {
margin-left: 0;
}
.little-space > * {
margin: 5px!important;
&:first-child:not(.align) {
margin-left: 0 !important;
}
}
.el-table {
//border: 1px solid #d9d9d9;
color: rgba(0,0,0,.65);
thead{
color: rgba(0,0,0,.68);
}
}
.lkt-message {
padding: 15px 30px;
i {
font-size: 1.7rem;
}
> * {
font-size: 1.2rem;
}
}
import Vue, { VNode } from 'vue';
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any;
}
}
}
declare module '*.vue' {
global {
class QWebChannel {
[index: string]: any;
constructor(transport: any, cb: (channel: QWebChannel) => any);
}
namespace qt {
const webChannelTransport: any;
}
// const bridge: any;
namespace AMap {
function plugin(name: string, cb: () => any): void;
type Status = 'complete' | 'no_data';
class EventEmitter {
on(event: string, handler: (e: any) => any): void;
}
class Map extends EventEmitter {
constructor(id: string, opts?: {
resizeEnable?: boolean;
zoom?: number;
center?: LngLat;
});
add(elm: any): void;
remove(elm: any): void;
getZoom(): number;
getCenter(): LngLat;
setFitView(elm: any): void;
setFeatures(features: string[]): void;
}
class Geocoder {
constructor(opts?: {
city?: string;
radius?: number;
});
getAddress(
lngLat: LngLat,
cb: (status: Status, result: any) => any,
): void;
getLocation(
address: string,
cb: (status: Status, result: any) => any,
): void;
}
export class DistrictSearch {
constructor(opts?: {
subdistrict?: number;
extensions?: string;
level?: string;
});
setLevel(level: string): void;
search(value: string, cb: (status: Status, result: any) => any): void;
}
export namespace DistrictLayer {
class Province {
constructor(opts: {
zIndex?: number;
adcode?: number[];
depth?: number;
styles?: {
'fill'?: string;
'province-stroke'?: string;
'city-stroke'?: string;
'county-stroke'?: string;
};
});
setMap(map: Map | null): void;
}
}
type LngLat = [number, number];
type Anchor = 'top-left' | 'top-center' | 'top-right' | 'middle-left' | 'center' | 'middle-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
class InfoWindow {
constructor(opts: {
content?: string | HTMLElement;
anchor?: Anchor;
offset?: Pixel;
position?: LngLat;
});
getIsOpen(): boolean;
open(map: Map, position?: LngLat): void;
close(): void;
setAnchor(anchor: Anchor): void;
setContent(content: string | HTMLElement): void;
setOffset(offset: Pixel): void;
setPosition(position: LngLat): void;
setSize(size: number[]): void;
}
interface LiveWeatherData {
city: string;
weather: string;
temperature: string; // 温度
windDirection: string; // 风向
windPower: string; // 风力
humidity: string; // 湿度
reportTime: string; // 发布时间
}
class Weather {
getLive(district: string, cb: (err: Error | null, data: LiveWeatherData) => void): void;
}
class Polygon {
constructor(opts: {
strokeWeight?: number;
path?: number[];
fillOpacity?: number;
fillColor?: string;
strokeColor?: string;
});
}
class Pixel {
x: number;
y: number;
constructor(x: number, y: number);
}
class Marker extends EventEmitter {
constructor(opts?: {
map?: Map;
icon?: string | Icon;
position?: LngLat;
offset?: Pixel;
anchor?: Anchor;
});
setPosition(lngLat: LngLat): void;
setMap(map: Map): void;
setOffset(offset: Pixel): void;
setAnchor(anchor: Anchor): void;
setContent(content: string | HTMLElement): void;
}
class Size {
constructor(width: number, height: number);
}
class Icon {
constructor(opts?: {
image: string;
size?: Size;
imageOffset?: Pixel;
imageSize?: Size;
});
}
}
}
import Vue from 'vue';
export default Vue;
}
import { IDepartment, IRole, IUser } from 'web-toolkit/src/case-main/types/beans';
export * from 'web-toolkit/src/case-main/types/beans';
export interface IAlarmMsg {
id: string;
deviceType: string;
type: string;
msg: string;
}
export interface IAlarmNoticeUser {
id: number;
name: string;
phone: string;
off: boolean;
createDt: string;
extend: Record<string, any>;
}
export interface IAlarmRule {
id: string;
title: string;
type: string;
description: string;
rules: object;
noticeUserIds: number[];
noticeUsers: IAlarmNoticeUser[];
off: boolean;
}
export interface IBaseClock {
device: IDevice;
part: IProductPart;
clock: number;
extend: Record<string, any>;
createDt: string;
}
export interface ICallRecord {
id: number;
fromUser: IUser;
toUser: IUser;
type: ICallType;
extend: Record<string, any>;
status: number;
createDt: string;
}
export interface ICallType {
id: number;
name: string;
extend: Record<string, any>;
}
export interface IClockAnalysisDaily {
deviceId: string;
device: IDevice;
data: object;
dt: string;
startDt: string;
endDt: string;
createDt: string;
}
export interface IClockRecord {
id: number;
device: IDevice;
dt: string;
duration: number;
durationWork: number;
extend: Record<string, any>;
createDt: string;
}
export interface ICollector {
id: number;
ip: string;
no: string;
mac: string;
pwd: string;
createDt: string;
off: boolean;
extend: Record<string, any>;
}
export interface ICollectorWarn {
id: number;
collector: ICollector;
info: string;
createDt: string;
}
export interface IDepartmentRelation {
department: IDepartment;
users: number[];
products: number[];
callUsers: number[];
userList: IUser[];
productList: IProduct[];
callUserList: IUser[];
}
export interface IDevice {
id: string;
collector: ICollector;
name: string;
type: string;
createDt: string;
off: boolean;
extend: Record<string, any>;
deviceType: IDeviceType;
}
export interface IDeviceAlarmRecord {
id: number;
device: IDevice;
createDt: string;
type: string;
no: string;
msg: string;
duration: number;
extend: Record<string, any>;
deviceId: string;
category: string;
}
export interface IDeviceMaintenance {
id: number;
device: IDevice;
operator: string;
description: string;
position: string;
reason: string;
occurDt: string;
treatment: string;
restorationDt: string;
extend: Record<string, any>;
createDt: string;
}
export interface IDeviceType {
id: string;
name: string;
img: string;
extend: Record<string, any>;
}
export interface IDeviceValue {
id: number;
device: IDevice;
createDt: string;
dt: string;
type: string;
data: object;
}
export interface IDeviceValueAlarmsDTO {
id: number;
alarms: object[];
createDt: string;
}
export interface IDeviceWorkStatusRecord {
id: number;
device: IDevice;
dt: string;
status: string;
duration: number;
extend: Record<string, any>;
createDt: string;
}
export interface IEmpAttendance {
id: number;
employee: IUser;
dt: string;
extend: Record<string, any>;
}
export interface IFactorySetting {
id: number;
name: string;
workshopName: string;
workshopImg: string;
workshopImgApp: string;
company: object;
}
export interface ILine {
id: string;
name: string;
createDt: string;
extend: Record<string, any>;
off: boolean;
}
export interface ILineDeviceConfig {
device: IDevice;
line: ILine;
stageId: number;
stageName: string;
}
export interface ILineProductRecord {
id: number;
no: string;
line: ILine;
product: IProduct;
startDt: string;
endDt: string;
extend: Record<string, any>;
createDt: string;
}
export interface ILineRelation {
line: ILine;
users: number[];
products: object[];
callUsers: number[];
shifts: number[];
userList: IUser[];
shiftList: IShiftSetting[];
callUserList: IUser[];
}
export interface ILineStatusRecord {
id: number;
line: ILine;
dt: string;
status: string;
duration: number;
extend: Record<string, any>;
createDt: string;
}
export interface ILineWorkOrder {
id: number;
plan: IProductionPlan;
no: string;
product: IProduct;
line: ILine;
shift: IShiftSetting;
startDt: string;
endDt: string;
status: number;
extend: Record<string, any>;
createDt: string;
}
export interface ILogisticsRecord {
id: number;
product: IProduct;
device: IDevice;
no: string;
plan: IProductionPlan;
operator: IUser;
address: string;
extend: Record<string, any>;
createDt: string;
}
export interface IMaterial {
id: number;
no: string;
name: string;
content: string;
extend: Record<string, any>;
off: boolean;
createDt: string;
}
export interface IMaterialForm {
id: number;
plan: IProductionPlan;
no: string;
name: string;
extend: Record<string, any>;
off: boolean;
createDt: string;
materialList: IMaterial[];
}
export interface IMonitorGraph {
id: number;
type: string;
device: IDevice;
style: object;
}
export interface IParamConfig {
available: boolean;
id: number;
key: string;
keyFull: string;
modify: boolean;
remoteId: string;
nameSimple: string;
nameFull: string;
ruleMap: object;
showAnalysis: boolean;
showTogether: string[];
unit: string;
influxEx: boolean;
freq: number;
benchmarkMin: number;
benchmarkMax: number;
value: string;
}
export interface IPartDeviceRelation {
device: IDevice;
part: IProductPart;
extend: Record<string, any>;
}
export interface IPartOptRecord {
id: number;
no: string;
part: IProductPart;
product: IProduct;
device: IDevice;
plan: IProductionPlan;
operator: IUser;
startDt: string;
endDt: string;
duration: number;
extend: Record<string, any>;
createDt: string;
}
export interface IProduceSetting {
id: number;
startTimeInDay: string;
endTimeInDay: string;
shifts: string;
shiftsList: string[][];
}
export interface IProduct {
id: number;
no: string;
name: string;
description: string;
process: IProductProcess;
extend: Record<string, any>;
off: boolean;
createDt: string;
}
export interface IProductionPlan {
id: number;
product: IProduct;
no: string;
name: string;
startDt: string;
endDt: string;
status: number;
extend: Record<string, any>;
off: boolean;
createDt: string;
}
export interface IProductOrder {
id: number;
no: string;
name: string;
content: string;
startDt: string;
endDt: string;
status: number;
target: object;
extend: Record<string, any>;
createDt: string;
off: number;
}
export interface IProductPart {
id: number;
process: IProductProcess;
no: string;
name: string;
content: string;
sort: number;
extend: Record<string, any>;
off: boolean;
createDt: string;
deviceList: IDevice[];
}
export interface IProductProcess {
id: number;
no: string;
name: string;
content: string;
extend: Record<string, any>;
off: boolean;
createDt: string;
partList: IProductPart[];
}
export interface IProductWasteReason {
id: number;
name: string;
off: boolean;
extend: Record<string, any>;
}
export interface IProductWasteReasonGroup {
id: number;
name: string;
reasons: number[];
products: number[];
reasonList: IUser[];
productList: IProduct[];
extend: Record<string, any>;
}
export interface IQualityManualRecord {
id: number;
no: string;
plan: IProductionPlan;
product: IProduct;
device: IDevice;
part: IProductPart;
operator: IUser;
dt: string;
createDt: string;
extend: Record<string, any>;
line: ILine;
startDt: string;
endDt: string;
}
export interface IQualityRecord {
id: number;
device: IDevice;
product: IProduct;
operator: IUser;
category: string;
reason: string;
dt: string;
extend: Record<string, any>;
line: ILine;
}
export interface IShiftSetting {
id: number;
name: string;
startTime: string;
endTime: string;
start: string;
end: string;
}
export interface IStationConfig {
id: string;
name: string;
mac: string;
config: Record<string, any>;
dt: string;
}
export interface Administration {
code: string;
name: string;
}
export interface Province extends Administration {
cities: Administration[];
}
export interface IROMSCustomer {
id: number;
name: string;
tel: string;
province: Province;
city: Administration;
area: Administration;
address: string;
extend: Record<string, any>;
createDt: string;
off: boolean;
}
export interface IStaffAreaRelation {
id: number;
staff: IUser;
province: Province;
city: Administration;
area: Administration;
}
export interface IROMSMaintenOrderResult {
id: number;
name: string;
}
export interface IROMSMaintenMaterial {
id: number;
no: string;
name: string;
usage?: string;
}
export interface IROMSMaintenOrder {
id: number;
no: string;
customer: IROMSCustomer;
devices: string[];
staff: IUser;
content: string;
address: string;
dt: string | Date;
startDt: string | Date;
completeDt: string | Date;
feedback: string;
materials: IROMSMaintenMaterial[];
pics: string[];
result: string;
score: number;
scoreContent: string;
createDt: string;
off: boolean;
extend: Record<string, any>;
deviceList: IDevice[];
}
import Vue from 'vue';
interface Result { valid: boolean; message: string; }
export interface Filter extends Vue {
conditions: Record<string, string>;
validate(): Result;
load(): Promise<void>;
selectDevice(deviceId: string, customerId?: number): void;
}
export const PRIVILEGE = {
USER_LIST : 'userList',
USER_MNG : 'userMng',
ROLE_MNG : 'roleMng',
DEVICE_MONITOR : 'deviceMonitor',
PRODUCT_LIST : 'productList',
PRODUCT_MNG : 'productMng',
PLAN_LIST : 'planList',
PLAN_MNG : 'planMng',
QUALITY_LIST : 'qualityList',
QUALITY_ADD : 'qualityAdd',
QUALITY_MNG : 'qualityMng',
CALL_RECORD_LIST : 'callRecordList',
CALL_MNG : 'callMng',
// 质保提醒管理
QUALITY_DT_MNG: 'qualityDtMng',
// 远程协助-参数
// REMOTE_PARAM: 'remoteParam',
// 远程协助-程序
// REMOTE_PROGRAM: 'remoteProgram',
REMOTE: 'remote',
// 派单查询
MAINTENT_ORDER_LIST: 'maintenOrderList',
// 派单管理
MAINTENT_ORDER_MNG: 'maintenOrderMng',
// 采集参数配置
COLLECT_PARAMS_MNG: 'collectParamsMng',
// 维护人员管理
MAINTENT_STAFF_MNG: 'maintenStaffMng',
// 消耗物料管理
MAINTENT_MATERIAL_MNG: 'maintenMaterialMng',
// 派单结果管理
MAINTENT_ORDER_RESULT_MNG: 'maintenOrderResultMng',
// 客户资料管理
CUSTOMER_MNG: 'customerMng',
DEVICE_LIST : 'deviceList',
DEVICE_MNG : 'deviceMng',
// 边缘网关配置
ADAPTER_DEVICE_MNG: 'AdapterDeviceMng',
EMP_LIST : 'empList',
EMP_MNG : 'empMng',
PART_OPT_RECORD_LIST : 'partOptRecordList',
PART_OPT_RECORD_MNG : 'partOptRecordMng',
DEVICE_ANALYSIS : 'deviceAnalysis',
DEPARTMENT_MNG : 'departmentMng',
PRODUCE_SETTING : 'produceSetting',
ANALYSIS_SETTING : 'analysisSetting',
ALARM_RULE_SETTING : 'alarmRuleSetting',
EMP_REPORT_WORK : 'empReportWork',
STATION_SETTING : 'stationSetting',
DEV : 'dev',
// 临时权限: 工单模式
MODE_ORDER: 'mode_order',
MODE_DEVICE: 'mode_device',
};
export default PRIVILEGE;
import {debounce, isNil, deepClone} from 'web-toolkit/src/utils';
export const isProvinceZoom = (zoom: number) => {
return zoom > 3 && zoom < 8;
};
export const isCityZoom = (zoom: number) => {
return zoom >= 8 && zoom < 11;
};
export const getDistrictLevel = (zoom: number) => {
if (zoom < 8) {
return 'province';
} else if (zoom >= 8 && zoom < 9) {
return 'city';
} else {
return 'district';
}
};
let infoWindow: AMap.InfoWindow;
export function setInfoWindow(opts: {
map: AMap.Map;
content: string | HTMLElement;
anchor?: AMap.Anchor;
offset?: AMap.Pixel;
position?: AMap.LngLat;
}) {
if (!infoWindow) {
infoWindow = new AMap.InfoWindow(opts);
} else {
infoWindow.setContent(opts.content);
opts.anchor && infoWindow.setAnchor(opts.anchor);
opts.offset && infoWindow.setOffset(opts.offset);
opts.position && infoWindow.setPosition(opts.position);
}
infoWindow.open(opts.map);
}
const weather = new AMap.Weather();
export const getLiveWeather = async (district: string) => {
return new Promise((resolve) => {
weather.getLive(district, (err, data) => {
resolve(err ? undefined : data);
});
});
};
let marker: AMap.Marker;
export const mark = async (lnglat: AMap.LngLat, map: AMap.Map) => {
if (!marker) {
marker = new AMap.Marker();
}
marker.setPosition(lnglat);
marker.setMap(map);
};
const geocoder = new AMap.Geocoder();
export const getAddress = async (lnglat: AMap.LngLat) => {
return new Promise<any>((resolve) => {
geocoder.getAddress(lnglat, (status, result) => {
if (status === 'complete' && result.regeocode) {
resolve(result.regeocode);
} else {
resolve();
}
});
});
};
export const getLocation = async (address: string) => {
return new Promise<any>((resolve) => {
geocoder.getLocation(address, (status, result) => {
if (status === 'complete' && result.geocodes.length) {
resolve(result.geocodes);
} else {
resolve();
}
});
});
};
const districtSearch = new AMap.DistrictSearch({
level: 'province',
extensions: 'all',
subdistrict: 0,
});
export const getDistrictBounds = (level: 'province' | 'city' | 'district', value: string) => {
return new Promise<{ bounds: number[][], name: string }>((resolve) => {
districtSearch.setLevel(level);
districtSearch.search(value, (status, result) => {
if (status === 'complete' && result.districtList && result.districtList[0]) {
resolve({
bounds: result.districtList[0].boundaries,
name: result.districtList[0].name,
});
} else {
resolve({
bounds: [],
name: '',
});
}
});
});
};
let polygons: AMap.Polygon[];
let districtName: string;
export const highLightDistrict = (name: string, bounds: number[][], map: AMap.Map) => {
if (districtName === name) {
return;
}
districtName = name;
if (polygons) {
map.remove(polygons);
}
polygons = bounds.map((bound) => new AMap.Polygon({
strokeWeight: 1,
path: bound,
fillOpacity: 0.4,
fillColor: '#80d8ff',
strokeColor: '#0091ea',
}));
map.add(polygons);
};
import {IDevice} from '@/types/beans';
export const changeXY = (deviceList: IDevice[]) => {
let x = 0;
let y = 0;
deviceList.sort( function( a: IDevice, b: IDevice) {
if (isNil(a.extend.lon) || isNil(b.extend.lon)) { return 0; }
return (a.extend.lon - b.extend.lon);
});
for (const device of deviceList) {
if (isNil(device.extend.lon) || isNil(device.extend.lat)) { continue; }
if (device.extend.lon === x && device.extend.lat === y) {
device.extend.lon = parseFloat((0.0005 * Math.random()).toFixed(6)) + device.extend.lon;
device.extend.lat = parseFloat((0.0005 * Math.random()).toFixed(6)) + device.extend.lat;
} else {
x = device.extend.lon;
y = device.extend.lat;
}
}
return deviceList;
};
interface StatusMap {
[key: string]: {
arrName: string;
color: string;
tag: string;
};
}
const $statusMap: StatusMap = {
mdc_offline: {
arrName: 'MDC离线',
color: '#ff4949',
tag: 'danger',
},
offline: {
arrName: '离线',
color: '#3d3d3d',
tag: 'info',
},
close: {
arrName: '关机',
color: '#8e8e8e',
tag: 'info',
},
idle: {
arrName: '空闲',
color: '#d7d400',
tag: 'warning',
},
working: {
arrName: '运行',
color: '#26b229',
tag: 'success',
},
run_gap: {
arrName: '加工间隔',
color: '#9add6f',
tag: 'success',
},
editing: {
arrName: '设置',
color: '#1082ff',
tag: 'primary',
},
emergency: {
arrName: '急停',
color: '#ff4949',
tag: 'danger',
},
pause: {
arrName: '暂停',
color: '#00b9ff',
tag: 'success',
},
overhaul: {
arrName: '维护',
color: '#ff9b00',
tag: 'warning',
},
collect_err: {
arrName: '采集异常',
color: '#ff4949',
tag: 'danger',
},
debug: {
arrName: '手动',
color: '#00b9ff',
tag: 'primary',
},
};
/**
* 一个map对象, 根据status获取对应的arrName, color, tag
* @example statusMap(some_status)
*/
export const statusMap = (status: string) => {
const obj = $statusMap[status];
return obj || $statusMap.offline;
};
/**
* 判断一个状态是否属于正常的状态
* @param status
*/
export function isNormal(status: string) {
const normalStatus = ['working', 'run_gap', 'pause', 'debug', 'idle', 'editing'];
return normalStatus.includes(status);
}
/**
* 判断一个状态是否属于不正常的状态
* @param status
*/
export function isAbnormal(status: string) {
const abnormalStatus = ['emergency', 'collect_err'];
return abnormalStatus.includes(status);
}
/**
* 判断一个状态是否属于离线状态
* @param status
*/
export function isOffline(status: string) {
const offlineStatus = ['close', 'offline', 'overhaul'];
return offlineStatus.includes(status);
}
/**
* 将提供的时间数据填充进日期对象
* @param dt 日期对象
* @param base 填充的时间数据
*/
export function fillBaseTime(dt: Date, base: any = {
hh: 0,
mm: 0,
ss: 0,
}) {
dt.setHours(base.hh);
dt.setMinutes(base.mm);
dt.setSeconds(base.ss);
dt.setMilliseconds(0);
}
/**
* 用于默认的时间范围, 若提供的开始时间晚于当前时间则将开始日期提前一天
*/
export function getDefaultDatetimePickerRanger(startTime?: any) {
const startBase = new Date();
fillBaseTime(startBase, startTime);
let ret: Date[] = [];
const end = new Date();
const start = new Date();
if (start.getTime() < startBase.getTime()) {
start.setDate(start.getDate() - 1);
}
fillBaseTime(start, startTime);
ret = [start, end];
return ret;
}
/**
* 根据oee返回对应的状态
* @param num oee数值
*/
export function oeeProgressStatus(num: number) {
if (num > 80) {
return 'success';
} else if (num < 40) {
return 'exception';
} else {
return undefined;
}
}
/**
* 根据oee返回对应的颜色值
* @param num oee数值
*/
export function oeeProgressColor(num: number) {
if (num > 80) {
return '#26b229';
} else if (num < 40) {
return '#FF4949';
} else {
return '#00afff';
}
}
/**
* 一个对象, 用于获取全局的echart样式
* todo 用echarts-helper中的getColor
*/
export const echartStyle = {
titleColor: '#ffffff',
textColor: 'white',
contentColor: '#3398DB',
};
export type AnyFunction = (...args: any[]) => any;
export const debounce = (fn: AnyFunction, freq: number = 300): AnyFunction => {
let timer = 0;
const dFn: () => void = function(this: any) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, [...arguments]);
}, freq);
};
return dFn;
};
export const offsetWindow = (elm: HTMLElement) => {
const offsetParent = elm.offsetParent;
let offsetLeft = elm.offsetLeft;
let offsetTop = elm.offsetTop;
if (offsetParent) {
const offset = offsetWindow(offsetParent as HTMLElement);
offsetLeft += offset.left;
offsetTop += offset.top;
}
return {
left: offsetLeft,
top: offsetTop,
};
};
export const transformMiiliseconds = (milliseconds: number) => {
const oneHour = 3600000;
const oneMinute = 60000;
const oneSecond = 1000;
const hours = Math.floor(milliseconds / oneHour);
milliseconds %= oneHour;
const minutes = Math.floor(milliseconds / oneMinute);
milliseconds %= oneMinute;
const seconds = Math.floor(milliseconds / oneSecond);
return `${hours}:${minutes}:${seconds}`;
};
<template>
<div v-loading="loading">
</div>
</template>
<script lang="ts">
import { ref, onMounted } from '@vue/composition-api';
export default {
setup() {
const loading = ref<boolean>(false);
return{
loading,
};
},
};
</script>
<template>
<div v-loading="loading">
</div>
</template>
<script lang="ts">
import { ref, onMounted } from '@vue/composition-api';
export default {
setup() {
const loading = ref<boolean>(false);
return{
loading,
};
},
};
</script>
<template>
<div class="flex column center login" v-loading="loading">
<div class="login-body vivify popIn flex center column">
<div class="login-head flex column center align-center">
<div class="title1">{{loginTitle1}}</div>
<div v-if="loginTitle2" class="title2">{{loginTitle2}}</div>
</div>
<div class="inner flex center align-center around">
<div class="left">
<img src="../assets/u90.png">
</div>
<div class="flex column right">
<div class="flex center">
<img :src="loginLogo"/>
</div>
<el-form
label-position="left" label-width="70px"
class="flex column around" ref="formRef" :model="form" :rules="rule">
<el-form-item prop="username" label="账户:">
<el-input v-model="form.username" autofocus autocomplete="off" placeholder="用户名" @keyup.enter.native="login($refs.formRef)" />
</el-form-item>
<el-form-item prop="pwd" label="密码:">
<el-input type="password" autocomplete="off" v-model="form.pwd" placeholder="密 码" @keyup.enter.native="login($refs.formRef)" />
</el-form-item>
<kit-err-channel id="login" style="margin-bottom: 5px" />
<el-form-item>
<el-button type="primary" @click="login($refs.formRef)">登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
<div class="login-support">技术支持:杭州领克信息科技有限公司</div>
</div>
</template>
<script lang="ts">
import { ref, createComponent, Ref, onMounted } from '@vue/composition-api';
import { ElForm } from 'element-ui/types/form';
import { useLoading } from 'web-toolkit/src/service';
import { postService, storeUserInfo, updateStoreUserInfo, submitErrChanel, pushMsgErr } from 'web-toolkit/src/case-main';
import {loginTitle1, loginTitle2, loginLogo, schema} from '@/config';
import {router} from '@/main';
import { Route } from 'vue-router';
import {assert} from 'web-toolkit/src/utils/index';
import {Login} from '@/dao/userDao';
export default createComponent({
setup() {
const loading = ref(false);
const form = ref({ username: '', pwd: '' });
const formRef: Ref<ElForm|null> = ref(null);
const rule = {
username: {
required: true,
message: '请填写用户名',
trigger: 'none',
},
pwd: [{
required: true,
message: '请填写密码',
trigger: 'none',
}, {
type: 'string',
min: 6,
message: '密码长度不能小于6位',
trigger: 'none',
}],
};
async function login() {
const valid = await (formRef.value as ElForm).validate();
assert(valid);
submitErrChanel('login');
const data = await Login({
...form.value,
schema,
});
updateStoreUserInfo(data as any);
// 设置登录后回到登录前页面
// @ts-ignore
const redirect: Route = {
name: 'index',
query: {},
};
if (
storeUserInfo.redirect &&
storeUserInfo.redirect.name &&
storeUserInfo.redirect.name !== 'notFound' &&
storeUserInfo.redirect.name !== 'login'
) {
redirect.name = storeUserInfo.redirect.name;
redirect.query = storeUserInfo.redirect.query;
}
// @ts-ignore
storeUserInfo.redirect = redirect;
router.push(redirect as any);
}
return {
formRef, loginTitle1, loginTitle2, loginLogo,
loading, form, rule, login: useLoading(loading, login),
};
},
});
</script>
<style lang="scss" scoped>
.login{
background: url("../assets/bg.jpg") no-repeat;
background-size: 100% 100%;
width: 100vw;
height: 100vh;
min-height: 600px;
}
.login-body {
width: 90vw;
height: 90vh;
min-height: 400px;
.login-head{
color: white;
.title1{
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 10px;
}
.title2{
font-size: 1.8rem;
margin-bottom: 2rem;
}
}
.inner{
width: 100%;
max-height: 600px;
max-width: 1000px;
background: white;
padding: 2rem 2rem;
box-shadow: 3px 3px 10px rgba($color: #000, $alpha: .5);
border-radius: 10px;
font-size: 1rem;
.left{
img{
height: 40vh;
}
}
.right{
width: 300px;
img{
height: 3.8rem;
width: auto;
margin-bottom: 3rem;
}
.el-input{
width: 230px;
}
.el-button{
width: 230px;
font-weight: 600;
margin: 0 auto;
}
}
}
}
.login-support{
/*margin-top: 3vh;*/
color: #fff;
width: 100%;
font-weight: 700;
font-size: 1.1rem;
text-align: center;
}
</style>
<template>
<el-menu
activeTextColor="#00d0FF"
textColor="white"
:unique-opened="true"
:backgroundColor="bgColor"
mode="vertical"
:default-active="active"
@select="routeTo">
<template v-for="(item, index) of storePageMenu">
<el-menu-item
v-if="item.name && contains(item.privileges)"
:key="index"
:index="item.name">
<i :class="'iconfont ' + item.icon"/>
<span class="title">{{ item.title }}</span>
</el-menu-item>
<el-submenu
v-if="!item.name && item.children && item.children.length > 0 && contains(item.privileges)"
popper-class="my-el-menu-popper"
:key="index"
:index="item.title">
<template slot="title">
<i :class="'iconfont '+item.icon"/>
<span class="title">{{ item.title }}</span>
</template>
<el-menu-item
class="item"
v-for="(child, i) of menuItemFilter(item.children)"
:key="i"
:index="child.name"
v-text="child.cTitle"/>
</el-submenu>
</template>
</el-menu>
</template>
<script lang="ts">
import { ref, onMounted, computed, watch } from '@vue/composition-api';
import {checkPrivilege, storePageMenu, storeUserInfo, storeCurrentRoute} from 'web-toolkit/src/case-main';
import {router} from '@/main';
export default {
props: {
bgColor: {
type: String,
default: 'center',
},
},
setup() {
const active = computed(() => {
if (storeCurrentRoute.meta) {
return (storeCurrentRoute.meta as any).parentName || storeCurrentRoute.name;
} else if (storeCurrentRoute.name) {
return storeCurrentRoute.name;
}
return undefined;
});
function menuItemFilter(itemChildren: any[]) {
return itemChildren.filter((child) => contains(child.privileges));
}
function routeTo(name: string) {
router.push({ name });
}
function contains(privileges: any[]) {
if (!storeUserInfo.user) { return false; }
const all = storeUserInfo.user.role.privileges;
return checkPrivilege(all, privileges);
}
return{
storePageMenu, storeCurrentRoute,
menuItemFilter,
routeTo,
contains,
active,
};
},
};
</script>
<style scoped lang="scss">
.el-menu {
border-right: 0;
height: 100%;
i{
margin-right: 10px;
}
}
</style>
<template>
<div class="header theme flex between">
<div class="brand flex align-center center" style="color: white">
<!-- <img :src="titleLogo" style="height: 20px; margin-right: 5px" /> -->
<span>{{loginTitle2}}</span>
<!-- <span>设备运维管理云平台</span> -->
</div>
<div class="header-menu-wrapper flex center">
</div>
<lkt-header-side class="header-side"/>
</div>
</template>
<script lang="ts">
import LktHeaderSide from './header-side.vue';
import LktMenu from './Menu.vue';
import {loginTitle1, loginTitle2, loginLogo, titleLogo} from '@/config';
export default {
components: {
LktHeaderSide, LktMenu,
},
setup() {
return{
loginTitle2, loginTitle1, loginLogo, titleLogo,
};
},
};
</script>
<style scoped lang="scss">
.brand{
height: 60px;
width: 200px;
color: $header-title-font;
font-weight: 600;
cursor: default;
// background-color: $header-title;
background-color: #23479C;
&.vertical {
border-color: transparent;
}
span{
font-size: 1.2rem;
}
}
.header {
background-color: $background-main;
min-width: 800px;
height: 60px;
&.horizontal > * {
// background-color: $header-title;
background-color: #23479C;
border: none;
}
.header-menu-wrapper{
border-bottom: 1px solid $grey-4;
width: calc(100vw - 200px - 30vw);
background-color: white;
}
.header-side{
width: 30vw;
border-bottom: 1px solid $grey-4;
background-color: white;
}
}
</style>
<template>
<div class="layout">
<lkt-navbar class="header"/>
<lkt-menu class="vertical-menu" bgColor="#23479C"/>
<div class="main">
<el-breadcrumb separator-class="el-icon-arrow-right" class="bread">
<el-breadcrumb-item v-for="(item, index) of path" :key="item.cname">
<span v-if="!item.isLink" :class="index" class="bread-title">{{ item.cname }}</span>
<span v-else-if="index === path.length - 1" class="bread-title bread-title--last">{{ item.cname }}</span>
<router-link v-else class="bread-title bread-title--link" :to="item.route.path">{{ item.cname }}</router-link>
</el-breadcrumb-item>
</el-breadcrumb>
<router-view class="page--inner"/>
</div>
<!-- <div class="page flex" :style="{ height: 'calc(100% - 60px)'}">-->
<!-- <lkt-menu class="vertical-menu" bgColor="#02213F"/>-->
<!-- <div class="main" :style="{ width: 'calc(100% - 200px)'}">-->
<!-- <el-breadcrumb separator-class="el-icon-arrow-right" class="bread">-->
<!-- <el-breadcrumb-item v-for="(item, index) of path" :key="item.cname">-->
<!-- <span v-if="!item.isLink" :class="index" class="bread-title">{{ item.cname }}</span>-->
<!-- <span v-else-if="index === path.length - 1" class="bread-title bread-title&#45;&#45;last">{{ item.cname }}</span>-->
<!-- <router-link v-else class="bread-title bread-title&#45;&#45;link" :to="item.route.path">{{ item.cname }}</router-link>-->
<!-- </el-breadcrumb-item>-->
<!-- </el-breadcrumb>-->
<!-- <lkt-scrollbar class="page&#45;&#45;outer">-->
<!-- <router-view class="page&#45;&#45;inner"/>-->
<!-- </lkt-scrollbar>-->
<!-- </div>-->
<!-- </div>-->
</div>
</template>
<script lang="ts">
import LktMenu from './Menu.vue';
import LktNavbar from './Navbar.vue';
import { ref, Ref } from '@vue/composition-api';
import {router} from '@/main';
import {routes} from '@/router/routes';
export default {
components: { LktMenu, LktNavbar },
setup() {
const getRoute = (cname: string, searchedRoutes: any = routes): any => {
for (const route of searchedRoutes) {
if (route.meta && route.meta.CName === cname) {
return route;
}
if (route.children) {
const target = getRoute(cname, route.children);
if (target) {
return target;
}
}
}
};
const resolveRouteMeta = (cname: string | void): PathItem[] => {
if (!cname) {
return [];
}
const route = getRoute(cname);
return [...resolveRouteMeta(route ? route.meta.parentCName : undefined), {
cname,
isLink: !!(route && !route.children),
route,
}];
};
const path: Ref<PathItem[]> = ref(resolveRouteMeta(router.currentRoute.meta.CName));
router.afterEach((to, from) => {
path.value = resolveRouteMeta(to.meta.CName);
});
return { path };
},
};
interface PathItem {
cname: string;
isLink: boolean;
route?: any;
}
</script>
<style lang="scss" scoped>
.header{
position: fixed;
top: 0;
width: 100vw;
z-index: 2000;
}
.vertical-menu {
/*margin-top: 60px;*/
width: 200px;
height: calc(100% - 60px);
position: fixed;
left: 0;
top: 60px;
bottom: 0;
z-index: 1500;
overflow-y: scroll;
// 滚动条消失
&::-webkit-scrollbar { width: 0 !important }
-ms-overflow-style: none;
overflow: -moz-scrollbars-none;
}
.main {
margin: 60px 0 0 200px;
padding: 10px;
width: calc(100% - 200px);
.bread{
padding-top: 5px;
padding-bottom: 10px;
height: 30px;
.bread-title {
color: $normal-text;
cursor: default;
}
.bread-title--link {
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
.bread-title--last {
color: $active;
}
}
/*> .page--outer {*/
/* height: calc(100% - 30px);*/
/*}*/
}
.layout {
height: calc(100% - 60px);
background-color: $background-main;
}
.page--inner > *:not(*[class^=el-]):not(*[class^=lkt-]) {
min-width: 1040px;
}
html{
height: 100%;
}
</style>
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
// todo
"skipLibCheck": true,
// "importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"sourceMap": true,
"baseUrl": ".",
"noEmit": true,
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"es5",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules/**",
"src/views/temp/**"
]
}
{
"defaultSeverity": "warning",
"extends": [
"tslint:recommended"
],
"linterOptions": {
"exclude": [
"node_modules/**",
"src/views/temp/**"
],
"include": [
"src/**"
]
},
"rules": {
"quotemark": [true, "single"],
"indent": [true, "spaces", 2],
"object-literal-sort-keys": false,
"radix": false,
"ordered-imports": false,
"only-arrow-function": false,
"max-line-length": false,
"no-namespace": false,
"only-arrow-functions": false,
"member-access": false,
"no-empty": false,
"max-classes-per-file": false,
"interface-name": false,
"arrow-parens": false,
"no-shadowed-variable": false,
"ban-types": false,
"no-unused-expression": false,
"no-bitwise": false
}
}
// 这里是在编译时执行
const theme = process.env.VUE_APP_APP_THEME || 'theme_dark';
const webpackExternals = process.env.NODE_ENV==='production' ? {
'vue': 'Vue',
'element-ui': 'ELEMENT',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios',
'qs': 'Qs',
} : {};
module.exports = {
publicPath: process.env.BASE_URL,
productionSourceMap: false,
configureWebpack: {
entry: process.env.ENTRY,
externals: {},
},
devServer:{
port: 8888
},
css: {
loaderOptions: {
sass: {
data: `
@import "node_modules/web-toolkit/src/scss/color.scss";
@import "node_modules/web-toolkit/src/scss/device.scss";
@import "node_modules/web-toolkit/src/scss/${theme}.scss";
`
}
}
},
// for vue-echarts
transpileDependencies: [
'vue-echarts',
'resize-detector'
],
// web-bundle-analyzer: https://github.com/webpack-contrib/webpack-bundle-analyzer#options-for-plugin
pluginOptions: {
// webpackBundleAnalyzer: {
// openAnalyzer: false,
// // server(dev), static(product), disabled。需要时用server
// analyzerMode: 'disabled',
// }
}
// pages:{
// index: 'src/main.js',
// }
}
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment