Commit 1f92bf9a by 温丽香

ci/cd

1 parent 19a0d507
Pipeline #3290 failed
in 1 minute 54 seconds
src/client/*
\ No newline at end of file
src/client/*
src/components/select/*
\ No newline at end of file
stages:
- build-dev
variables:
CONFIG_DEV: ./public/config.js
# 开发构建
build-dev:
stage: build-dev
script:
- docker-compose build && docker-compose -p device_manage up -d
only:
- develop
tags:
- develop
FROM node:lts-alpine3.12 as build
# RUN npm config set registry https://registry.npm.taobao.org
WORKDIR /tmp/cache
COPY package.json .
COPY package-lock.json .
RUN npm install
COPY . .
RUN npm run build
FROM socialengine/nginx-spa:latest as nginx
COPY --from=build /tmp/cache/dist /app
version: "3.7"
services:
aiot_control:
container_name: device_manage
restart: always
image: device_manage:${CI_COMMIT_REF_NAME}
build:
dockerfile: Dockerfile
context: .
volumes:
- /ssl:/app/ssl
ports:
- 9998:80
\ No newline at end of file
......@@ -58,7 +58,11 @@
"@vue/eslint-config-standard": "^4.0.0",
"@vue/eslint-config-typescript": "^4.0.0",
"babel-eslint": "^10.1.0",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-vue-jsx": "^3.7.0",
"babel-preset-env": "^1.7.0",
"echarts": "^4.8.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
......
window.g = {
baseURL: 'http://218.94.122.141:8089/api',
baseURL: 'http://hzdev.seetatech.com:9997',
defaultLang: 'zh-CN', // 目前支持 zh-CN / ja-JP
}
\ No newline at end of file
import axios from './http'
const GET = 'get'
const POST = 'post'
const FRONT = '/front'
const USER = '/user'
export const loginApi = data => axios(POST, FRONT + USER + '/login', data)
const PARAMETER = '/parameter'
export const getParameter = data => axios(GET, FRONT + PARAMETER + '/query_list', data)
const TABLE = '/table'
export const getTable = data => axios(GET, FRONT + TABLE + '/query_list', data)
......@@ -23,6 +23,7 @@ import seetaTable from './seeta-ui/seeta-table'
// import seetaTooltip from './seeta-ui/seeta-tooltip.vue'
import seetaTabs from './seeta-ui/seeta-tabs.vue'
import seetaSelect from './seeta-ui/seeta-select.vue'
import packSelect from './select'
import seetaMenu from './seeta-ui/seeta-menu.vue'
import seetaCrumb from './seeta-ui/seeta-crumb.vue'
// import dateRange from './list-filter/date-range.vue'
......@@ -64,6 +65,7 @@ Vue.component('popup', popup)
// Vue.component('st-tooltip', seetaTooltip)
Vue.component('st-tabs', seetaTabs)
Vue.component('st-select', seetaSelect)
Vue.component('pack-select', packSelect)
Vue.component('st-menu', seetaMenu)
Vue.component('st-crumb', seetaCrumb)
Vue.component('puzzle-validator', puzzlevalidator)
import Select from './src/select';
/* istanbul ignore next */
Select.install = function(Vue) {
Vue.component(Select.name, Select);
};
export default Select;
export default {
data() {
return {
hoverOption: -1
};
},
computed: {
optionsAllDisabled() {
return this.options.filter(option => option.visible).every(option => option.disabled);
}
},
watch: {
hoverIndex(val) {
if (typeof val === 'number' && val > -1) {
this.hoverOption = this.options[val] || {};
}
this.options.forEach(option => {
option.hover = this.hoverOption === option;
});
}
},
methods: {
navigateOptions(direction) {
if (!this.visible) {
this.visible = true;
return;
}
if (this.options.length === 0 || this.filteredOptionsCount === 0) return;
if (!this.optionsAllDisabled) {
if (direction === 'next') {
this.hoverIndex++;
if (this.hoverIndex === this.options.length) {
this.hoverIndex = 0;
}
} else if (direction === 'prev') {
this.hoverIndex--;
if (this.hoverIndex < 0) {
this.hoverIndex = this.options.length - 1;
}
}
const option = this.options[this.hoverIndex];
if (option.disabled === true ||
option.groupDisabled === true ||
!option.visible) {
this.navigateOptions(direction);
}
this.$nextTick(() => this.scrollToOption(this.hoverOption));
}
}
}
};
<template>
<ul class="el-select-group__wrap" v-show="visible">
<li class="el-select-group__title">{{ label }}</li>
<li>
<ul class="el-select-group">
<slot></slot>
</ul>
</li>
</ul>
</template>
<script type="text/babel">
import Emitter from 'element-ui/src/mixins/emitter';
export default {
mixins: [Emitter],
name: 'ElOptionGroup',
componentName: 'ElOptionGroup',
props: {
label: String,
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
visible: true
};
},
watch: {
disabled(val) {
this.broadcast('ElOption', 'handleGroupDisabled', val);
}
},
methods: {
queryChange() {
this.visible = this.$children &&
Array.isArray(this.$children) &&
this.$children.some(option => option.visible === true);
}
},
created() {
this.$on('queryChange', this.queryChange);
},
mounted() {
if (this.disabled) {
this.broadcast('ElOption', 'handleGroupDisabled', this.disabled);
}
}
};
</script>
<template>
<li
@mouseenter="hoverItem"
@click.stop="selectOptionClick"
class="el-select-dropdown__item"
v-show="visible"
:class="{
'selected': itemSelected,
'is-disabled': disabled || groupDisabled || limitReached,
'hover': hover
}">
<slot>
<span>{{ currentLabel }}</span>
</slot>
</li>
</template>
<script type="text/babel">
import Emitter from 'element-ui/src/mixins/emitter';
import { getValueByPath, escapeRegexpString } from 'element-ui/src/utils/util';
export default {
mixins: [Emitter],
name: 'ElOption',
componentName: 'ElOption',
inject: ['select'],
props: {
value: {
required: true
},
label: [String, Number],
created: Boolean,
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
index: -1,
groupDisabled: false,
visible: true,
hitState: false,
hover: false
};
},
computed: {
isObject() {
return Object.prototype.toString.call(this.value).toLowerCase() === '[object object]';
},
currentLabel() {
return this.label || (this.isObject ? '' : this.value);
},
currentValue() {
return this.value || this.label || '';
},
itemSelected() {
if (!this.select.multiple) {
return this.isEqual(this.value, this.select.value);
} else {
return this.contains(this.select.value, this.value);
}
},
limitReached() {
if (this.select.multiple) {
return !this.itemSelected &&
(this.select.value || []).length >= this.select.multipleLimit &&
this.select.multipleLimit > 0;
} else {
return false;
}
}
},
watch: {
currentLabel() {
if (!this.created && !this.select.remote) this.dispatch('ElSelect', 'setSelected');
},
value(val, oldVal) {
const { remote, valueKey } = this.select;
if (!this.created && !remote) {
if (valueKey && typeof val === 'object' && typeof oldVal === 'object' && val[valueKey] === oldVal[valueKey]) {
return;
}
this.dispatch('ElSelect', 'setSelected');
}
}
},
methods: {
isEqual(a, b) {
if (!this.isObject) {
return a === b;
} else {
const valueKey = this.select.valueKey;
return getValueByPath(a, valueKey) === getValueByPath(b, valueKey);
}
},
contains(arr = [], target) {
if (!this.isObject) {
return arr && arr.indexOf(target) > -1;
} else {
const valueKey = this.select.valueKey;
return arr && arr.some(item => {
return getValueByPath(item, valueKey) === getValueByPath(target, valueKey);
});
}
},
handleGroupDisabled(val) {
this.groupDisabled = val;
},
hoverItem() {
if (!this.disabled && !this.groupDisabled) {
this.select.hoverIndex = this.select.options.indexOf(this);
}
},
selectOptionClick() {
if (this.disabled !== true && this.groupDisabled !== true) {
this.dispatch('ElSelect', 'handleOptionClick', [this, true]);
}
},
queryChange(query) {
this.visible = new RegExp(escapeRegexpString(query), 'i').test(this.currentLabel) || this.created;
if (!this.visible) {
this.select.filteredOptionsCount--;
}
}
},
created() {
this.select.options.push(this);
this.select.cachedOptions.push(this);
this.select.optionsCount++;
this.select.filteredOptionsCount++;
this.$on('queryChange', this.queryChange);
this.$on('handleGroupDisabled', this.handleGroupDisabled);
},
beforeDestroy() {
const { selected, multiple } = this.select;
let selectedOptions = multiple ? selected : [selected];
let index = this.select.cachedOptions.indexOf(this);
let selectedIndex = selectedOptions.indexOf(this);
// if option is not selected, remove it from cache
if (index > -1 && selectedIndex < 0) {
this.select.cachedOptions.splice(index, 1);
}
this.select.onOptionDestroy(this.select.options.indexOf(this));
}
};
</script>
<template>
<div
class="el-select-dropdown el-popper"
:class="[{ 'is-multiple': $parent.multiple }, popperClass]"
:style="{ minWidth: minWidth }">
<slot></slot>
</div>
</template>
<script type="text/babel">
import Popper from 'element-ui/src/utils/vue-popper';
export default {
name: 'ElSelectDropdown',
componentName: 'ElSelectDropdown',
mixins: [Popper],
props: {
placement: {
default: 'bottom-start'
},
boundariesPadding: {
default: 0
},
popperOptions: {
default() {
return {
gpuAcceleration: false
};
}
},
visibleArrow: {
default: true
},
appendToBody: {
type: Boolean,
default: true
}
},
data() {
return {
minWidth: ''
};
},
computed: {
popperClass() {
return this.$parent.popperClass;
}
},
watch: {
'$parent.inputWidth'() {
this.minWidth = this.$parent.$el.getBoundingClientRect().width + 'px';
}
},
mounted() {
this.referenceElm = this.$parent.$refs.reference.$el;
this.$parent.popperElm = this.popperElm = this.$el;
this.$on('updatePopper', () => {
if (this.$parent.visible) this.updatePopper();
});
this.$on('destroyPopper', this.destroyPopper);
}
};
</script>
'use strict'
import Axios from 'axios'
import { getToken } from '@/utils/user'
// import * as api from '../src/client'
import { baseUrl } from '../src/config'
import browserStorage from '@/services/local-storage'
import store from '@/store'
import { Notification } from 'element-ui'
import Router from '@/router/index.js'
// 配置默认进度条事件
Axios.defaults.onUploadProgress = p => {
const obj = document.querySelector('.uploadForm .el-icon-circle-check')
if (obj) {
const percent = ((p.loaded / p.total) * 100) | 0
obj.setAttribute('data-text', percent + '%')
store.dispatch('setChangeUploadPercent', percent)
}
}
import { baseUrl } from '@/config'
function notificationError(title: any, msg: any, duration: number = 1500) {
Notification.error({
......@@ -27,56 +15,19 @@ function notificationError(title: any, msg: any, duration: number = 1500) {
})
}
interface resData { code: string, text: string, msg: string }
interface optionsType {
allow_throw: boolean,
valid_code: boolean,
error_page: boolean,
must_fields?: [],
routerCancel?: boolean,
cancelToken?: any,
}
const m = new Map()
// 获取请求头
function getHeaders() {
const token = getToken()
return token ? { Authorization: token } : null
}
// 全局错误提示
function throwErr(resData: resData) {
vm.$notify.error({
title: resData.text,
message: resData.msg,
})
}
function validBackendCode(resData: resData, options: optionsType) {
if (resData.code === 'Success') return true
if (options.error_page) {
if (resData.code === 'NoAuthority') vm.$router.replace('/403')
if (resData.code === 'RecordNotFoundError') vm.$router.replace('/404')
}
Axios.defaults.baseURL = baseUrl
if (options.allow_throw) {
const code = resData.code
const d = new Date()
if (m.has(code)) {
if (d.getTime() - m.get(code) > 2000) {
throwErr(resData)
m.set(code, d.getTime())
}
} else {
throwErr(resData)
m.set(code, d.getTime())
}
export default (method, url, data, config) => {
method = method.toLowerCase()
switch (method) {
case 'get':
return Axios({ method: 'get', url, headers: { 'Token': browserStorage.getItem('token') }, params: data })
case 'post':
return Axios({ method: 'post', url, headers: { 'Token': browserStorage.getItem('token') }, data })
default:
return false
}
return false
}
// 添加请求拦截器
Axios.interceptors.request.use(function(config) {
const urlBase = config.url.split('?')[0]
......@@ -131,13 +82,3 @@ Axios.interceptors.response.use(res => {
// })
}
})
// const configuration = new api.Configuration({
// // basePath: process.env.API_URL,
// basePath: baseUrl,
// apiKey() {
// // TODO: 从localStorage中获取Token,Organization-Id
// return browserStorage.getItem('id')
// }
// })
// export const usersApi = new api.UsersApi(configuration)
......@@ -6,8 +6,8 @@
<div class="page-nav">
<div class="search-fields">
<span class="fields">用户名:</span>
<!-- <st-input width="240px" class="align contents" id="align-input" v-model="deviceName"></st-input> -->
<input type="text" id="raw-input" autocomplete="off" class="align contents" :style="{borderColor: borderColor}" placeholder="请输入" @blur="getData" v-model.trim="deviceName" @keyup.enter="getData" @input="checkSearchContent">
<st-input width="240px" class="align contents" id="align-input" v-model="deviceName"></st-input>
<!-- <input type="text" id="raw-input" autocomplete="off" class="align contents" :style="{borderColor: borderColor}" placeholder="请输入" @blur="getData" v-model.trim="deviceName" @keyup.enter="getData" @input="checkSearchContent"> -->
<span class="fields">用户角色:</span>
<st-select
v-model="logLevel"
......
......@@ -42,6 +42,15 @@
<el-table-column v-for="(column, index) in tableData[0].columns" :key="index" :label="`列${index + 1}`">
<template slot-scope="scope">
<div class="column-box">
<!-- <div style="display: flex"> -->
<!-- <pack-select v-model="scope.row.columns[index].type" @blur="handleBlur" placeholder="请选择" class="none-display" style="margin-right: 10px">
<el-option
v-for="item in typeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</pack-select> -->
<input type="text" v-model="scope.row.columns[index].value" @blur="handleBlur">
<el-select v-model="scope.row.columns[index].type" placeholder="请选择">
<el-option
......@@ -145,12 +154,33 @@ export default {
this.setCurrent()
event.target && event.target.classList && event.target.classList.remove('write')
},
// handleBlur(event) {
// console.log(event.target, event.target.classList)
// if (this.selectBlur && this.inputBlur) {
// this.setCurrent()
// event.path.map(item => {
// if (item.classList && Array.from(item.classList).includes('cell')) {
// item.getElementsByClassName('el-select')[0].classList.add('none-display')
// item.getElementsByTagName('input')[0].classList.remove('write')
// item.getElementsByTagName('input')[1].classList.remove('write')
// }
// })
// }
// },
cellClick(row, column, cell, event) {
if (cell.getElementsByTagName('input')[0]) {
cell.getElementsByTagName('input')[0].classList.add('write')
cell.getElementsByTagName('input')[0].focus()
}
},
// cellClick(row, column, cell, event) {
// cell.getElementsByClassName('el-select')[0].classList.remove('none-display')
// if (cell.getElementsByTagName('input')[0] && cell.getElementsByTagName('input')[1]) {
// cell.getElementsByTagName('input')[0].classList.add('write')
// cell.getElementsByTagName('input')[1].classList.add('write')
// cell.getElementsByTagName('input')[1].focus()
// }
// },
rowClick(row, column, event) {
this.setCurrent(row)
},
......@@ -185,6 +215,7 @@ export default {
width: 70px
.el-input__inner
width: 50px
color: #999
::v-deep.el-icon-arrow-up
&::before
content: '\e78f'
......@@ -298,4 +329,6 @@ export default {
color: #666
&:disabled
color: #C0C4CC
.none-display
display: none
</style>
......@@ -62,14 +62,14 @@
</el-table-column>
<el-table-column label="类型">
<template slot-scope="scope">
<el-select v-model="scope.row.type" placeholder="请选择" @change="handleChange(scope.row)">
<pack-select v-model="scope.row.type" placeholder="请选择" @blur="handleBlur" @visible-change="handleVisibleChange" @change="handleChange(scope.row)">
<el-option
v-for="item in typeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</pack-select>
</template>
</el-table-column>
<el-table-column label="值">
......@@ -81,14 +81,14 @@
<span class="file-btn" v-if="scope.row.value">{{scope.row.value}}<i class="iconfont icon-guanbi" @click="deleteFile(scope.row)"></i></span>
</div>
<div v-if="scope.row.type === 'table'">
<el-select v-model="scope.row.value" placeholder="请选择" @blur="handleBlur">
<pack-select v-model="scope.row.value" placeholder="请选择" @blur="handleBlur" @visible-change="handleVisibleChange">
<el-option
v-for="item in tableOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</pack-select>
</div>
</template>
</el-table-column>
......@@ -201,6 +201,11 @@ export default {
row.file = ''
row.value = ''
},
handleVisibleChange(val) {
if (!val) {
this.setCurrent()
}
},
handleBlur(event) {
this.setCurrent()
event.target && event.target.classList && event.target.classList.remove('write')
......@@ -228,9 +233,9 @@ export default {
.page-content
text-align: left
position: relative
::v-deep.el-form-item
// height: 32px
// line-height: 32px
::v-deep.el-form-item, ::v-deep.el-form-item__label, ::v-deep.el-form-item__content
height: 32px
line-height: 32px
margin-bottom: 24px
::v-deep.el-form-item.textarea
height: 100px
......@@ -287,7 +292,7 @@ export default {
height: calc(100% - 60px)
overflow: hidden
.flex-item
height: calc(100% - 380px)
height: calc(100% - 370px)
.delete-btn
margin-left: 12px
color: #F56C6C
......
......@@ -122,7 +122,7 @@ import crypto from 'crypto-js'
import browserStorage from '@/services/local-storage'
import seetaPopover from '@/components/seeta-ui/seeta-popover'
import { saveUser } from '@/utils/user'
// import { usersApi } from '../../http'
import { loginApi } from '@/axios'
import { langList } from '@/i18n/utils'
export default {
......@@ -271,7 +271,7 @@ export default {
async prelogin(val) {
},
async login() {
const res = await usersApi.login({
const res = await loginApi({
username: this.userName,
password: this.userPassWord
})
......
......@@ -40,6 +40,16 @@ module.exports = {
happyPackMode: false
})
.end()
config.module
.rule('thejs')
.test(/\.js$/)
.include
.add(path.resolve('src'))
.add(path.resolve('node_modules/element-ui/packages'))
.end()
.use('babel-loader')
.loader('babel-loader')
.end()
},
// devServer: {
// // development server port 8000
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!