feat: 删除前端工程中meterSphere组件

This commit is contained in:
fit2cloud-chenyw 2021-03-16 17:53:43 +08:00
parent 5781da135a
commit 46d24a17bc
112 changed files with 129 additions and 133539 deletions

View File

@ -9,15 +9,15 @@
<!--<i v-if="asideHidden" class="el-icon-arrow-right"/>-->
<!--</div>-->
<slot />
<ms-horizontal-drag-bar />
<de-horizontal-drag-bar />
</el-aside>
</template>
<script>
import MsHorizontalDragBar from './dragbar/MsLeft2RightDragBar'
import DeHorizontalDragBar from './dragbar/DeLeft2RightDragBar'
export default {
name: 'MsAsideContainer',
components: { MsHorizontalDragBar },
name: 'DeAsideContainer',
components: { DeHorizontalDragBar },
props: {
width: {
type: String,

View File

@ -8,7 +8,7 @@
<script>
export default {
name: 'MsContainer'
name: 'DeContainer'
}
</script>

View File

@ -6,7 +6,7 @@
<script>
export default {
name: 'MsMainContainer'
name: 'DeMainContainer'
}
</script>

View File

@ -1,11 +1,11 @@
<template>
<div class="drag-bar" v-bottom-to-top-drag/>
<div v-bottom-to-top-drag class="drag-bar" />
</template>
<script>
export default {
name: "MsBottom2TopDragBar"
}
export default {
name: 'DeBottom2TopDragBar'
}
</script>
<style scoped>

View File

@ -4,7 +4,7 @@
<script>
export default {
name: 'MsLeft2RightDragBar'
name: 'DeLeft2RightDragBar'
}
</script>

View File

@ -1,11 +1,11 @@
<template>
<div class="drag-bar" v-right-to-left-drag/>
<div v-right-to-left-drag class="drag-bar" />
</template>
<script>
export default {
name: "MsRight2LeftDragBar"
}
export default {
name: 'DeRight2LeftDragBar'
}
</script>
<style scoped>

View File

@ -1,8 +1,71 @@
import permission from '@/directive/Permission'
export const left2RightDrag = {
inserted(el, binding) {
el.onmousedown = function(e) {
const init = e.clientX
const parent = el.parentNode
const initWidth = parent.offsetWidth
document.onmousemove = function(e) {
const end = e.clientX
const newWidth = end - init + initWidth
if (newWidth < document.body.clientWidth - 10 && newWidth > 10) {
parent.style.width = newWidth + 'px'
}
}
document.onmouseup = function() {
document.onmousemove = document.onmouseup = null
}
}
}
}
export const right2LeftDrag = {
inserted(el, binding) {
el.onmousedown = function(e) {
const init = e.clientX
const parent = el.parentNode
const initWidth = parent.offsetWidth
document.onmousemove = function(e) {
const end = e.clientX
const newWidth = initWidth - (end - init)
if (newWidth < document.body.clientWidth - 10 && newWidth > 10) {
parent.style.width = newWidth + 'px'
}
}
document.onmouseup = function() {
document.onmousemove = document.onmouseup = null
}
}
}
}
export const bottom2TopDrag = {
inserted(el, binding) {
el.onmousedown = function(e) {
const init = e.clientY
const parent = el.parentNode
const initHeight = parent.offsetHeight
document.onmousemove = function(e) {
const end = e.clientY
const newHeight = initHeight - (end - init)
if (newHeight < document.body.clientHeight - 10 && newHeight > 10) {
parent.style.height = newHeight + 'px'
}
}
document.onmouseup = function() {
document.onmousemove = document.onmouseup = null
}
}
}
}
export default {
install(Vue) {
Vue.directive('permission', permission)
Vue.directive('left-to-right-drag', left2RightDrag)
Vue.directive('right-to-left-drag', right2LeftDrag)
Vue.directive('bottom-to-top-drag', bottom2TopDrag)
}
}

View File

@ -3,10 +3,8 @@ import Cookies from 'js-cookie'
import '@/styles/index.scss' // global css
import ElementUI from 'element-ui'
import Fit2CloudUI from 'fit2cloud-ui'
// import 'element-ui/lib/theme-chalk/index.css'
// import './styles/variables.scss'
import i18n from './lang' // internationalization
import i18n from './lang' // internationalization
import App from './App'
import store from './store'
import router from './router'
@ -15,8 +13,6 @@ import '@/icons' // icon
import '@/permission' // permission control
import api from '@/api/index.js'
import filter from '@/filter/filter'
import message from '@/metersphere/common/js/message'
import { left2RightDrag, bottom2TopDrag, right2LeftDrag } from '@/metersphere/common/js/directive'
import directives from './directive'
import './styles/vdrr/common-temp.scss'
@ -58,13 +54,8 @@ Vue.use(Fit2CloudUI, {
i18n: (key, value) => i18n.t(key, value)
})
Vue.use(filter)
Vue.use(message)
Vue.use(directives)
Vue.config.productionTip = false
// 支持左右拖拽
Vue.directive('left-to-right-drag', left2RightDrag)
Vue.directive('right-to-left-drag', right2LeftDrag)
Vue.directive('bottom-to-top-drag', bottom2TopDrag)
new Vue({
el: '#app',

View File

@ -1,49 +0,0 @@
<template>
<chart
:init-options="defaultInitOptions"
:options="options"
:theme="theme"
:group="group"
@click="onClick"
:watch-shallow="watchShallow"
:manual-update="manualUpdate"
:autoresize="autoresize"/>
</template>
<script>
export default {
name: "MsChart",
props: {
options: Object,
theme: [String, Object],
initOptions: Object,
group: String,
autoresize: Boolean,
watchShallow: Boolean,
manualUpdate: Boolean
},
data() {
return {
defaultInitOptions: this.initOptions
}
},
mounted() {
this.defaultInitOptions = this.defaultInitOptions || {};
// svg
// BUG: svg legend
// if (!this.defaultInitOptions.renderer) {
// this.defaultInitOptions.renderer = 'svg';
// }
},
methods: {
onClick(params){
this.$emit('onClick', params.data)
},
}
}
</script>
<style scoped>
</style>

View File

@ -1,26 +0,0 @@
<template>
<span class="box">
<el-tooltip class="item" effect="dark" :content="tips" placement="right">
<el-button type="primary" size="mini" circle @click="exec()">
<font-awesome-icon :icon="['fas', 'plus']" />
</el-button>
</el-tooltip>
</span>
</template>
<script>
export default {
name: 'MsCreateBox',
props: {
tips: String,
exec: Function
}
}
</script>
<style scoped>
.box {
margin-left: 5px;
margin-right: 5px;
}
</style>

View File

@ -1,136 +0,0 @@
<template>
<ms-aside-container :enable-aside-hidden="false" :width="width + 'px'">
<div class="title-bar" :style="{'height': titleBarHeight + 'px'}">
<slot name="title">
<span :style="{'line-height': titleBarHeight - 10 + 'px'}" class="title-left">
{{title}}
</span>
<span :style="{'line-height': titleBarHeight - 10 + 'px'}" class="title-right">
<i class="el-icon-plus" @click="addFuc"/>
</span>
</slot>
</div>
<div :style="{'height': itemBarHeight + 'px'}" v-for="(item, index) in data" :key="index" class="item-bar" @click="itemSelected(index, item)" :class="{'item-selected' : index == selectIndex}">
<input class="item-input" :style="{'height': itemBarHeight - 12 + 'px', 'line-height': itemBarHeight - 12 + 'px', 'width': width - 90 + 'px'}" v-model="item.name" :placeholder="$t('commons.input_content')"/>
<span :style="{'line-height': itemBarHeight - 10 + 'px'}" class="item-right">
<i v-for="(operator, operatorIndex) in itemOperators" :key="operatorIndex" :class="operator.icon" @click.stop="operator.func(item, index)"/>
</span>
</div>
</ms-aside-container>
</template>
<script>
import MsAsideContainer from "./MsAsideContainer";
export default {
name: "MsAsideItem",
components: {MsAsideContainer},
data() {
return {
selectIndex: -1
}
},
props: {
width: {
type: Number,
default: 200
},
titleBarHeight: {
type: Number,
default: 40
},
itemBarHeight: {
type: Number,
default: 35
},
title: String,
data: Array,
deleteFuc: Function,
addFuc: Function,
itemOperators: {
type: Array,
default() {
return [
{
icon: 'el-icon-delete',
func: this.deleteFuc
}
];
}
},
enableAsideHidden: {
type: Boolean,
default: true
},
},
methods: {
itemSelected(index, item) {
this.selectIndex = index;
this.$emit('itemSelected', item);
}
}
}
</script>
<style scoped>
.ms-aside-container {
padding: 0;
}
.title-bar {
width: 100%;
background: #e9ebef;
padding: 5px 10px;
box-sizing: border-box;
}
.item-bar {
width: 100%;
background: #F9F9F9;
padding: 5px 10px;
box-sizing: border-box;
border: solid 1px #e6e6e6;
}
.item-bar:hover .item-right {
visibility: visible;
}
.title-right,.item-right {
float: right;
}
.item-right {
visibility: hidden;
}
.item-right i {
margin: 5px;
}
i:hover {
color: #409EFF;
font-size: large;
}
.item-selected {
background: #ECF5FF;
border-left: solid #409EFF 5px;
}
.item-selected .item-right {
visibility: visible;
}
.item-input {
border: hidden;
display: inline;
background-color:rgba(0,0,0,0);
}
.item-input:focus{
outline:none;
}
</style>

View File

@ -1,112 +0,0 @@
<template>
<editor v-model="formatData" :lang="mode" @init="editorInit" :theme="theme" :height="height"/>
</template>
<script>
import {formatHtml, formatJson, formatXml} from "../../../../common/js/format-utils";
import toDiffableHtml from 'diffable-html';
export default {
name: "MsCodeEdit",
components: { editor: require('vue2-ace-editor')},
data() {
return {
formatData: ''
}
},
props: {
height: [String, Number],
data: {
type: String
},
theme: {
type: String,
default() {
return 'chrome'
}
},
init: {
type: Function
},
enableFormat: {
type: Boolean,
default() {
return true;
}
},
readOnly: {
type: Boolean,
default() {
return false;
}
},
mode: {
type: String,
default() {
return 'text';
}
},
modes: {
type: Array,
default() {
return ['text', 'json', 'xml', 'html'];
}
}
},
mounted() {
if (!this.data) {
this.formatData = "";
}
this.format();
},
watch: {
formatData() {
this.$emit('update:data', this.formatData);
},
mode() {
this.format();
}
},
methods: {
editorInit: function (editor) {
require('brace/ext/language_tools') //language extension prerequsite...
this.modes.forEach(mode => {
require('brace/mode/' + mode); //language
});
require('brace/theme/' + this.theme)
require('brace/snippets/javascript') //snippet
if (this.readOnly) {
editor.setReadOnly(true);
}
if (this.init) {
this.init(editor);
}
},
format() {
if (this.enableFormat) {
if (this.data) {
switch (this.mode) {
case 'json':
this.formatData = formatJson(this.data);
break;
case 'html':
this.formatData = toDiffableHtml(this.data);
break;
case 'xml':
this.formatData = formatXml(this.data);
break;
default:
this.formatData = this.data;
}
}
} else {
this.formatData = this.data;
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,101 +0,0 @@
<template>
<el-dialog
:close-on-click-modal="false"
:title="title"
:visible.sync="dialogVisible"
class="delete-confirm"
>
<el-row>
<el-col>
<span>{{ $t('commons.delete_confirm') }}</span>
<span class="delete-tip"> DELETE-{{ record.name || record.title }}</span>
<br>
</el-col>
</el-row>
<el-row v-if="withTip" class="tip">
<span>
<slot class="tip" />
</span>
</el-row>
<el-row>
<el-col :span="15">
<el-input v-model="value" :placeholder="$t('commons.input_content')" />
</el-col>
</el-row>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">{{ $t('commons.cancel') }}</el-button>
<el-button type="primary" @click="confirm">{{ $t('commons.confirm') }}</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
name: 'MsDeleteConfirm',
props: {
title: {
type: String,
default() {
return this.$t('commons.title')
}
},
withTip: {
type: Boolean,
default() {
return false
}
}
},
data() {
return {
dialogVisible: false,
value: '',
record: {}
}
},
methods: {
open(record) {
this.dialogVisible = true
this.value = ''
this.record = record
},
confirm() {
if (this.value.trim() !== 'DELETE-' + (this.record.name || this.record.title)) {
this.$warning(this.$t('commons.incorrect_input'))
return
}
this.$emit('delete', this.record)
this.dialogVisible = false
}
}
}
</script>
<style scoped>
.delete-confirm >>> .el-dialog {
width: 500px;
}
.delete-confirm .el-dialog:first-child {
margin-bottom: 10px;
}
.delete-confirm .el-row:first-child {
margin-bottom: 20px;
}
.delete-tip {
font-style: italic;
font-weight: bold;
}
.tip {
margin-bottom: 20px;
color: red;
}
</style>

View File

@ -1,52 +0,0 @@
<template>
<div class="dialog-footer">
<el-button @click="cancel">{{ $t('commons.cancel') }}</el-button>
<el-button v-if="isShowValidate" type="warning" @click="validate" @keydown.enter.native.prevent>{{ $t('commons.validate') }}</el-button>
<el-button type="primary" :disabled="disabled" @click="confirm" @keydown.enter.native.prevent>{{ $t('commons.confirm') }}</el-button>
<el-button v-if="isShow" type="primary" @click="saveAsEdit" @keydown.enter.native.prevent>{{ title }}</el-button>
</div>
</template>
<script>
export default {
name: 'MsDialogFooter',
props: {
isShow: {
type: Boolean,
default: false
},
isShowValidate: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
title: {
type: String,
default: null
}
},
methods: {
cancel() {
this.$emit('cancel')
},
validate() {
this.$emit('validate')
},
confirm() {
this.$emit('confirm')
},
saveAsEdit() {
this.$emit('saveAsEdit')
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,223 +0,0 @@
<template>
<div v-if="visible" id="ms-drawer" class="ms-drawer" :class="directionStyle" :style="{width: w + 'px', height: h + 'px'}" ref="msDrawer">
<ms-bottom2-top-drag-bar v-if="direction == 'bottom'"/>
<ms-right2-left-drag-bar v-if="direction == 'right'"/>
<div class="ms-drawer-header">
<slot name="header"></slot>
<i v-if="isShowClose" class="el-icon-close" @click="close"/>
<font-awesome-icon v-if="!isFullScreen && showFullScreen" class="alt-ico" :icon="['fa', 'expand-alt']" size="lg" @click="fullScreen"/>
<font-awesome-icon v-if="isFullScreen && showFullScreen" class="alt-ico" :icon="['fa', 'compress-alt']" size="lg" @click="unFullScreen"/>
</div>
<div class="ms-drawer-body">
<slot></slot>
</div>
<ms-left2-right-drag-bar v-if="direction == 'left'"/>
</div>
</template>
<script>
import MsRight2LeftDragBar from "./dragbar/MsRight2LeftDragBar";
import MsLeft2RightDragBar from "./dragbar/MsLeft2RightDragBar";
import MsBottom2TopDragBar from "./dragbar/MsBottom2TopDragBar";
export default {
name: "MsDrawer",
components: {MsBottom2TopDragBar, MsLeft2RightDragBar, MsRight2LeftDragBar},
data() {
return {
x: 0,
y: 0,
w: 100,
h: 100,
directionStyle: 'left-style',
dragBarDirection: 'vertical',
isFullScreen: false,
originalW: 100,
originalH: 100,
}
},
props: {
direction: {
type: String,
default() {
return "left";
}
},
visible: {
type: Boolean,
default() {
return true;
}
},
size: {
type: Number,
default() {
return 40;
}
},
showFullScreen: {
type: Boolean,
default() {
return true;
}
},
isShowClose: {
type: Boolean,
default() {
return true;
}
}
},
mounted() {
this.init();
},
methods: {
init() {
// todo
switch (this.direction) {
case 'left':
this.w = this.getWidthPercentage(this.size);
this.h = this.getHeightPercentage(100);
this.x = 0;
this.y = 0;
this.directionStyle = 'left-style';
this.dragBarDirection = 'horizontal';
break;
case 'right':
this.w = this.getWidthPercentage(this.size);
this.h = this.getHeightPercentage(100);
this.x = document.body.clientWidth - this.w;
this.y = 0;
this.directionStyle = 'right-style';
this.dragBarDirection = 'horizontal';
break;
case 'top':
this.w = this.getWidthPercentage(100);
this.h = this.getHeightPercentage(this.size);
this.x = 0;
this.y = 0;
this.directionStyle = 'top-style';
this.dragBarDirection = 'vertical';
break;
case 'bottom':
this.w = this.getWidthPercentage(100);
this.h = this.getHeightPercentage(this.size);
this.x = 0;
this.y = document.body.clientHeight - this.h;
this.directionStyle = 'bottom-style';
this.dragBarDirection = 'vertical';
break;
}
},
getWidthPercentage(per) {
return document.body.clientWidth * per / 100.0;
},
getHeightPercentage(per) {
return document.body.clientHeight * per / 100.0;
},
fullScreen() {
this.originalW = this.w;
this.originalH = this.h;
this.w = document.body.clientWidth;
this.h = document.body.clientHeight;
this.isFullScreen = true;
},
unFullScreen() {
this.w = this.originalW;
this.h = this.originalH;
this.isFullScreen = false;
},
close() {
this.$emit('close')
}
}
}
</script>
<style scoped>
.ms-drawer {
background-color: white;
border: 1px #DCDFE6 solid;
-webkit-box-shadow: 0 8px 10px -5px rgba(0,0,0,.2), 0 16px 24px 2px rgba(0,0,0,.14), 0 6px 30px 5px rgba(0,0,0,.12);
box-shadow: 0 8px 10px -5px rgba(0,0,0,.2), 0 16px 24px 2px rgba(0,0,0,.14), 0 6px 30px 5px rgba(0,0,0,.12);
z-index: 999 !important;
position: fixed;
overflow: auto;
}
.left-style {
top: 0;
left: 0;
}
.right-style {
top: 0;
right: 0;
}
.top-style {
top: 0;
left: 0;
}
.bottom-style {
bottom: 0;
left: 0;
border-top: 5px;
}
.ms-drawer-body {
overflow: scroll;
}
.ms-drawer-header {
z-index: 999;
width: 100%;
}
.bottom-style .ms-drawer-header {
position: fixed;
}
.el-icon-close {
position: absolute;
font-size: 20px;
right: 10px;
top: 10px;
color: gray;
}
.bottom-style .el-icon-close {
right: 10px;
top: 13px;
}
.right-style .el-icon-close {
position: fixed;
right: 10px;
top: 10px;
}
.el-icon-close:hover {
color: red;
}
.alt-ico {
position: absolute;
font-size: 15px;
right: 40px;
top: 15px;
color: #8c939d;
}
.alt-ico:hover {
color: black;
font-size: 18px;
}
</style>

View File

@ -1,60 +0,0 @@
<template>
<el-dropdown @command="handleCommand" class="ms-dropdown">
<slot>
<span class="el-dropdown-link">
{{currentCommand}}
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
</slot>
<el-dropdown-menu slot="dropdown" chang>
<el-dropdown-item v-for="(command, index) in commands" :key="index" :command="command">
{{command}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
name: "MsDropdown",
data() {
return {
currentCommand: ''
}
},
props: {
commands: {
type: Array
},
defaultCommand: {
type: String
}
},
created() {
if (this.defaultCommand) {
this.currentCommand = this.defaultCommand;
} else if (this.commands && this.commands.length > 0) {
this.currentCommand = this.commands [0];
}
},
methods: {
handleCommand(command) {
this.currentCommand = command;
this.$emit('command', command);
}
}
}
</script>
<style scoped>
.el-dropdown-link {
cursor: pointer;
color: #409EFF;
}
.el-icon-arrow-down {
font-size: 12px;
}
</style>

View File

@ -1,53 +0,0 @@
<template>
<el-tooltip class="instructions-icon" :effect="effect" :placement="placement">
<template v-slot:content>
{{content}}
</template>
<i :style="{'font-size': size + 'px'}" class="el-icon-info"></i>
</el-tooltip>
</template>
<script>
export default {
name: "MsInstructionsIcon",
props: {
content: String,
icon: {
type: String,
default: 'el-icon-question'
},
placement: {
type: String,
default: 'top-start'
},
type: {
type: String,
default: null
},
effect: {
type: String,
default: 'dark'
},
size: {
type: String,
default: '16'
},
isTesterPermission: {
type: Boolean,
default: false
}
},
}
</script>
<style scoped>
.el-icon-info {
color: #606266;
}
.el-icon-info:hover {
color: #409EFF;
}
</style>

View File

@ -1,165 +0,0 @@
<template>
<div>
<div class="jsoneditor-vue" style="height: 400px"></div>
</div>
</template>
<script>
import 'jsoneditor/dist/jsoneditor.css';
import JsonEditor from 'jsoneditor'
export default {
props: {
value: [String, Number, Object, Array],
expandedOnStart: {
type: Boolean,
default: false
},
mode: {
type: String,
default: "code"
},
modes: {
type: Array,
default: function () {
return ["code"];
}
}
},
watch: {
value: {
immediate: true,
async handler(val) {
if (!this.internalChange) {
await this.setEditor(val);
this.expandAll();
}
},
deep: true
}
},
data() {
return {
editor: null,
error: false,
json: JSON.parse(this.value ? this.value : "{}"),
internalChange: false,
expandedModes: ["tree", "view", "form"],
};
},
mounted() {
let self = this;
let options = {
mode: this.mode,
modes: this.modes, // allowed modes
onChange() {
try {
let json = self.editor.get();
self.json = json;
self.$emit("json-change", json);
self.internalChange = true;
self.$emit("input", json);
self.$nextTick(function () {
self.internalChange = false;
});
} catch (e) {
self.$emit("has-error", e);
}
},
onModeChange() {
self.expandAll();
},
onError(error) {
self.$emit("onError", error);
}
};
this.editor = new JsonEditor(
this.$el.querySelector(".jsoneditor-vue"),
options,
this.json
);
},
methods: {
expandAll() {
if (
this.expandedOnStart &&
this.expandedModes.includes(this.editor.getMode())
) {
this.editor.expandAll();
}
},
async setEditor(value) {
if (this.editor) this.editor.set(value);
}
}
};
</script>
<style scoped>
.ace_line_group {
text-align: left;
}
.json-editor-container {
display: flex;
width: 100%;
}
.json-editor-container .tree-mode {
width: 50%;
}
.json-editor-container .code-mode {
flex-grow: 1;
}
.jsoneditor-btns {
text-align: center;
margin-top: 10px;
}
.jsoneditor-vue .jsoneditor-outer {
height: 300px;
}
.jsoneditor-vue div.jsoneditor-tree {
min-height: 350px;
}
.json-save-btn {
background-color: #20A0FF;
border: none;
color: #fff;
padding: 5px 10px;
border-radius: 5px;
}
.json-save-btn:focus {
outline: none;
}
.json-save-btn[disabled] {
background-color: #1D8CE0;
}
code {
background-color: #f5f5f5;
}
/deep/ .jsoneditor-poweredBy {
visibility: hidden;
}
/deep/ .jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected {
background-color: #1E9FFB;
}
/deep/ jsoneditor-tree {
overflow: auto;
}
/deep/ .jsoneditor {
border-color: #1E9FFB;
border-radius: 3px;
}
</style>

View File

@ -1,124 +0,0 @@
<template>
<div>
<ms-chart :options="options" @onClick="onClick">
</ms-chart>
</div>
</template>
<script>
import MsChart from "@/business/components/common/chart/MsChart";
export default {
name: "MsPieChart",
components: {MsChart},
mounted() {
this.getDataNamesByData();
},
watch: {
data() {
this.getDataNamesByData();
}
},
data() {
return {
options: {
title: {
text: this.text,
subtext: this.subtext,
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>({d}%)'
},
legend: {
orient: 'vertical',
left: 20,
data: this.dataNames
},
series : [
{
name: this.name,
type: 'pie',
radius: ['40%', '70%'],
// roseType: 'angle',
data: this.data,
animation: false,
label: {
normal: {
show: true,
position: 'outside',
formatter: '{b}:{c}'
}
}
}
]
},
dataNames: ['示例1','示例2','示例3']
}
},
props: {
text: {
type: String,
default: '图表名称'
},
name: {
type: String,
default: '数据名称'
},
subtext: {
type: String,
default: ''
},
data: {
type: Array,
default() {
return [
{
value:235, name:'示例1',
itemStyle: {
color: '#67C23A'
}
},
{
value:274, name:'示例2',
itemStyle: {
color: '#E6A23C'
}
},
{
value:274, name:'示例3',
itemStyle: {
color: '#F56C6C'
}
}
];
}
}
},
methods: {
getDataNamesByData() {
let itemNames = [];
this.data.forEach(item => {
itemNames.push(item.name);
});
this.dataNames = itemNames;
},
onClick(params){
this.$emit('onClick', params);
},
}
}
</script>
<style scoped>
.echarts {
margin: 0 auto;
}
</style>

View File

@ -1,57 +0,0 @@
<template>
<span class="previous-next-button">
<span class="head-right-tip" v-if="index + 1 === list.length">
{{ $t('test_track.plan_view.pre_case') }} : {{list[index - 1] ? list[index - 1].name : ''}}
</span>
<span class="head-right-tip" v-if="index + 1 !== list.length">
{{ $t('test_track.plan_view.next_case') }} : {{list[index + 1] ? list[index + 1].name : ''}}
</span>
<el-button plain size="mini" icon="el-icon-arrow-up" :disabled="index + 1 <= 1" @click="handlePre()"/>
<span>
{{ index + 1 }}/{{ list.length }}
</span>
<el-button plain size="mini" icon="el-icon-arrow-down" :disabled="index + 1 >= list.length" @click="handleNext()"/>
</span>
</template>
<script>
export default {
name: "MsPreviousNextButton",
data() {
return {
}
},
props: {
list: {
type: Array,
default() {
return []
}
},
index: {
type: Number,
default() {
return 0
}
}
},
methods: {
handlePre() {
this.$emit('pre');
},
handleNext() {
this.$emit('next');
}
}
}
</script>
<style scoped>
.head-right-tip {
color: darkgrey;
}
</style>

View File

@ -1,35 +0,0 @@
<template>
<div>
<ms-tag v-for="(role, index) in roles"
:key="index"
:effect="effect"
:type="type"
:content="$t('role.' + role.id)"/>
</div>
</template>
<script>
import MsTag from "./MsTag";
export default {
name: "MsRolesTag",
components: {MsTag},
props: {
type: {
type: String,
default: 'primary',
},
roles: {
type: Array
},
effect: {
type: String,
default: 'dark',
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,132 +0,0 @@
<template>
<div class="schedule-config">
<div>
<span class="cron-ico" @click="scheduleEdit">
<i class="el-icon-date" size="small"></i>
<span class="character">SCHEDULER</span>
</span>
<el-switch :disabled="!schedule.value || isReadOnly" v-model="schedule.enable" @change="scheduleChange"/>
<ms-schedule-edit :is-read-only="isReadOnly" :schedule="schedule" :test-id="testId" :save="save"
:custom-validate="customValidate"
ref="scheduleEdit"/>
</div>
<div>
<span>
{{ $t('schedule.next_execution_time') }}
<span :class="{'disable-character': !schedule.enable}"
v-if="!schedule.enable">{{ $t('schedule.not_set') }}</span>
<crontab-result v-if="schedule.enable" :enable-simple-mode="true" :ex="schedule.value" ref="crontabResult"/>
</span>
</div>
</div>
</template>
<script>
import MsScheduleEdit from "./MsScheduleEdit";
import CrontabResult from "../cron/CrontabResult";
function defaultCustomValidate() {
return {pass: true};
}
export default {
name: "MsScheduleConfig",
components: {CrontabResult, MsScheduleEdit},
data() {
return {
recentList: [],
refreshScheduler: null,
}
},
props: {
testId: String,
save: Function,
schedule: {},
checkOpen: {
type: Function,
default() {
return {
checkOpen() {
return true;
}
}
}
},
customValidate: {
type: Function,
default: defaultCustomValidate
},
isReadOnly: {
type: Boolean,
default: false
}
},
methods: {
scheduleEdit() {
if (!this.checkOpen()) {
return;
}
this.$refs.scheduleEdit.open();
},
scheduleChange() {
this.$emit('scheduleChange');
},
flashResultList() {
if (this.$refs.crontabResult) {
this.$refs.crontabResult.expressionChange();
}
},
cancelRefresh() {
if (this.refreshScheduler) {
clearInterval(this.refreshScheduler);
}
}
},
beforeDestroy() {
this.cancelRefresh();
},
watch: {
schedule() {
if (this.schedule.enable) {
this.refreshScheduler = setInterval(this.flashResultList, 2000);
} else {
this.cancelRefresh();
}
}
}
}
</script>
<style scoped>
.schedule-config {
float: right;
width: 250px;
height: 15px;
line-height: 25px;
}
.el-icon-date {
font-size: 20px;
margin-left: 5px;
}
.character {
font-weight: bold;
margin: 0 5px;
}
.disable-character {
color: #cccccc;
}
.el-switch {
margin: 0 5px;
}
.cron-ico {
cursor: pointer;
}
</style>

View File

@ -1,216 +0,0 @@
<template>
<el-dialog :close-on-click-modal="false" width="60%" class="schedule-edit" :visible.sync="dialogVisible"
@close="close">
<template>
<div>
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('schedule.edit_timer_task')" name="first">
<el-form :model="form" :rules="rules" ref="from">
<el-form-item
prop="cronValue">
<el-input :disabled="isReadOnly" v-model="form.cronValue" class="inp"
:placeholder="$t('schedule.please_input_cron_expression')"/>
<!-- <el-button type="primary" @click="showCronDialog">{{$t('schedule.generate_expression')}}</el-button>-->
<el-button :disabled="isReadOnly" type="primary" @click="saveCron">{{
$t('commons.save')
}}
</el-button>
</el-form-item>
<el-form-item>
<el-link :disabled="isReadOnly" type="primary" @click="showCronDialog">
{{ $t('schedule.generate_expression') }}
</el-link>
</el-form-item>
<crontab-result :ex="form.cronValue" ref="crontabResult"/>
</el-form>
<el-dialog width="60%" :title="$t('schedule.generate_expression')" :visible.sync="showCron"
:modal="false">
<crontab @hide="showCron=false" @fill="crontabFill" :expression="schedule.value"
ref="crontab"/>
</el-dialog>
</el-tab-pane>
<el-tab-pane :label="$t('schedule.task_notification')" name="second">
<schedule-task-notification :is-tester-permission="isTesterPermission" :test-id="testId"
:schedule-receiver-options="scheduleReceiverOptions"/>
</el-tab-pane>
</el-tabs>
</div>
</template>
</el-dialog>
</template>
<script>
import {checkoutTestManagerOrTestUser, getCurrentUser, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import Crontab from "../cron/Crontab";
import CrontabResult from "../cron/CrontabResult";
import {cronValidate} from "@/common/js/cron";
import ScheduleTaskNotification from "../../settings/organization/components/ScheduleTaskNotification";
function defaultCustomValidate() {
return {pass: true};
}
export default {
name: "MsScheduleEdit",
components: {CrontabResult, Crontab, ScheduleTaskNotification},
props: {
testId: String,
save: Function,
schedule: {},
customValidate: {
type: Function,
default: defaultCustomValidate
},
isReadOnly: {
type: Boolean,
default: false
},
},
watch: {
'schedule.value'() {
this.form.cronValue = this.schedule.value;
}
},
data() {
const validateCron = (rule, cronValue, callback) => {
let customValidate = this.customValidate(this.getIntervalTime());
if (!cronValue) {
callback(new Error(this.$t('commons.input_content')));
} else if (!cronValidate(cronValue)) {
callback(new Error(this.$t('schedule.cron_expression_format_error')));
}
// else if(!this.intervalShortValidate()) {
// callback(new Error(this.$t('schedule.cron_expression_interval_short_error')));
// }
else if (!customValidate.pass) {
callback(new Error(customValidate.info));
} else {
callback();
}
};
return {
scheduleReceiverOptions: [],
operation: true,
dialogVisible: false,
showCron: false,
form: {
cronValue: ""
},
activeName: 'first',
rules: {
cronValue: [{required: true, validator: validateCron, trigger: 'blur'}],
}
}
},
methods: {
currentUser: () => {
return getCurrentUser();
},
initUserList() {
let param = {
name: '',
organizationId: this.currentUser().lastOrganizationId
};
if (this.isTesterPermission) {
this.result = this.$post('user/org/member/list/all', param, response => {
this.scheduleReceiverOptions = response.data
});
}
},
/* handleClick() {
if (this.activeName === "second") {
this.result = this.$get('/notice/search/message/'+this.testId, response => {
this.scheduleTask = response.data;
})
}
},*/
buildParam() {
let param = {};
param.notices = this.tableData
param.testId = this.testId
return param;
},
open() {
this.initUserList();
this.dialogVisible = true;
this.form.cronValue = this.schedule.value;
listenGoBack(this.close);
this.activeName = 'first'
},
crontabFill(value, resultList) {
//
this.form.cronValue = value;
this.$refs.crontabResult.resultList = resultList;
this.$refs['from'].validate();
},
showCronDialog() {
this.showCron = true;
},
saveCron() {
this.$refs['from'].validate((valid) => {
if (valid) {
this.intervalShortValidate();
this.save(this.form.cronValue);
this.dialogVisible = false;
} else {
return false;
}
});
},
saveNotice() {
let param = this.buildParam();
this.result = this.$post("notice/save", param, () => {
this.$success(this.$t('commons.save_success'));
})
},
close() {
this.dialogVisible = false;
this.form.cronValue = '';
this.$refs['from'].resetFields();
if (!this.schedule.value) {
this.$refs.crontabResult.resultList = [];
}
removeGoBackListener(this.close);
},
intervalShortValidate() {
if (this.getIntervalTime() < 3 * 60 * 1000) {
// return false;
this.$info(this.$t('schedule.cron_expression_interval_short_error'));
}
return true;
},
resultListChange() {
this.$refs['from'].validate();
},
getIntervalTime() {
let resultList = this.$refs.crontabResult.resultList;
let time1 = new Date(resultList[0]);
let time2 = new Date(resultList[1]);
return time2 - time1;
},
},
computed: {
isTesterPermission() {
return checkoutTestManagerOrTestUser();
}
}
}
</script>
<style scoped>
.inp {
width: 50%;
margin-right: 20px;
}
.el-form-item {
margin-bottom: 10px;
}
</style>

View File

@ -1,58 +0,0 @@
<template>
<el-button :disabled="disabled" plain :type="type" :icon="icon" :size="size" @click="exec()">
{{ content }}
</el-button>
</template>
<script>
import { checkoutTestManagerOrTestUser } from '@/metersphere/common/js/utils'
export default {
name: 'MsTableButton',
props: {
content: String,
icon: {
type: String,
default: 'el-icon-question'
},
type: {
type: String,
default: null
},
effect: {
type: String,
default: 'dark'
},
size: {
type: String,
default: 'mini'
},
isTesterPermission: {
type: Boolean,
default: false
}
},
data() {
return {
disabled: false
}
},
mounted() {
if (this.isTesterPermission && !checkoutTestManagerOrTestUser()) {
this.disabled = true
}
},
methods: {
exec() {
this.$emit('click')
}
}
}
</script>
<style scoped>
.el-button {
padding: 8px 10px;
}
</style>

View File

@ -1,148 +0,0 @@
<template>
<div>
<el-row v-if="title" class="table-title" type="flex" justify="space-between" align="middle">
<slot name="title">
{{ title }}
</slot>
</el-row>
<el-row type="flex" justify="space-between" align="middle">
<span class="operate-button">
<ms-table-button
v-if="showCreate"
v-permission="permission.add"
:is-tester-permission="isTesterPermission"
icon="el-icon-circle-plus-outline"
:content="createTip"
@click="create"
/>
<ms-table-button
v-if="showRun"
:is-tester-permission="isTesterPermission"
icon="el-icon-video-play"
type="primary"
:content="runTip"
@click="runTest"
/>
<ms-table-button
v-if="showRun"
:is-tester-permission="isTesterPermission"
icon="el-icon-circle-plus-outline"
content="转场景测试"
@click="historicalDataUpgrade"
/>
<slot name="button" />
</span>
<span>
<slot name="searchBarBefore" />
<ms-table-search-bar :condition.sync="condition" class="search-bar" :tip="tip" @change="search" />
<ms-table-adv-search-bar v-if="isCombine" :condition.sync="condition" @search="search" />
</span>
</el-row>
</div>
</template>
<script>
import MsTableSearchBar from './MsTableSearchBar'
import MsTableButton from './MsTableButton'
import MsTableAdvSearchBar from './search/MsTableAdvSearchBar'
export default {
name: 'MsTableHeader',
components: { MsTableAdvSearchBar, MsTableSearchBar, MsTableButton },
props: {
title: {
type: String,
default() {
return this.$t('commons.name')
}
},
showCreate: {
type: Boolean,
default: true
},
showRun: {
type: Boolean,
default: false
},
condition: {
type: Object,
default: null
},
createTip: {
type: String,
default() {
return this.$t('commons.create')
}
},
runTip: {
type: String,
default: null
},
isTesterPermission: {
type: Boolean,
default: false
},
tip: {
type: String,
default() {
return this.$t('commons.search_by_name')
}
},
permission: {
type: Object,
default() {
return {
add: []
}
}
}
},
computed: {
isCombine() {
return this.condition.components !== undefined && this.condition.components.length > 0
}
},
methods: {
search(value) {
this.$emit('update:condition', this.condition)
this.$emit('search', value)
},
create() {
this.$emit('create')
},
runTest() {
this.$emit('runTest')
},
historicalDataUpgrade() {
this.$emit('historicalDataUpgrade')
}
}
}
</script>
<style>
.table-title {
height: 40px;
font-weight: bold;
font-size: 18px;
}
</style>
<style scoped>
.operate-button {
margin-bottom: -5px;
}
.search-bar {
width: 240px
}
</style>

View File

@ -1,64 +0,0 @@
<template>
<span>
<slot name="front" />
<ms-table-operator-button v-permission="permission.edit" :is-tester-permission="isTesterPermission" :tip="tip1" icon="el-icon-edit" @exec="editClick" @click.stop="editClickStop" />
<slot name="middle" />
<ms-table-operator-button v-permission="permission.del" :is-tester-permission="isTesterPermission" :tip="tip2" icon="el-icon-delete" type="danger" @exec="deleteClick" @click.stop="deleteClickStop" />
<slot name="behind" />
</span>
</template>
<script>
import MsTableOperatorButton from './MsTableOperatorButton'
export default {
name: 'MsTableOperator',
components: { MsTableOperatorButton },
props: {
tip1: {
type: String,
default() {
return this.$t('commons.edit')
}
},
tip2: {
type: String,
default() {
return this.$t('commons.delete')
}
},
isTesterPermission: {
type: Boolean,
default: false
},
permission: {
type: Object,
default() {
return {
edit: [],
del: []
}
}
}
},
methods: {
editClick() {
this.$emit('editClick')
},
editClickStop() {
this.$emit('editClickStop')
},
deleteClick() {
this.$emit('deleteClick')
},
deleteClickStop() {
this.$emit('deleteClickStop')
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,64 +0,0 @@
<template>
<ms-tip-button
:disabled="disabled || isReadOnly"
:type="type"
:tip="tip"
:icon="icon"
size="mini"
circle
@click="exec"
@clickStop="clickStop"
/>
</template>
<script>
import MsTipButton from './MsTipButton'
import { checkoutTestManagerOrTestUser } from '@/metersphere/common/js/utils'
export default {
name: 'MsTableOperatorButton',
components: { MsTipButton },
props: {
icon: {
type: String,
default: 'el-icon-question'
},
type: {
type: String,
default: 'primary'
},
tip: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
},
isTesterPermission: {
type: Boolean,
default: false
}
},
data() {
return {
isReadOnly: false
}
},
mounted() {
if (this.isTesterPermission && !checkoutTestManagerOrTestUser()) {
this.isReadOnly = true
}
},
methods: {
exec() {
this.$emit('exec')
},
clickStop() {
this.$emit('clickStop')
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,43 +0,0 @@
<template>
<span>
<ms-table-operator-button v-for="(btn, index) in buttons" :key="index" :isTesterPermission="isTesterPermission(btn)"
:tip="btn.tip" :icon="btn.icon" :type="btn.type"
@exec="click(btn)" @click.stop="clickStop(btn)"/>
</span>
</template>
<script>
import MsTableOperatorButton from "./MsTableOperatorButton";
export default {
name: "MsTableOperators",
components: {MsTableOperatorButton},
props: {
row: Object,
buttons: Array
},
methods: {
click(btn) {
if (btn.exec instanceof Function) {
btn.exec(this.row);
}
},
clickStop(btn) {
if (btn.stop instanceof Function) {
btn.stop(this.row);
}
},
},
computed: {
isTesterPermission() {
return function (btn) {
return btn.isTesterPermission !== false;
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,43 +0,0 @@
<template>
<el-input
v-model="condition.name"
class="search"
type="text"
size="small"
:placeholder="tip"
prefix-icon="el-icon-search"
maxlength="60"
clearable
@change="search"
/>
</template>
<script>
export default {
name: 'MsTableSearchBar',
props: {
condition: {
type: Object,
default: null
},
tip: {
type: String,
default() {
return this.$t('commons.search_by_name')
}
}
},
methods: {
search() {
this.$emit('update:condition', this.condition)
this.$emit('change')
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,30 +0,0 @@
<template>
<el-tag :type="type" :effect="effect" :color="color" size="mini">{{content}}</el-tag>
</template>
<script>
export default {
name: "MsTag",
props: {
type: {
type: String,
default: 'primary',
},
color: {
type: String,
default: '',
},
content: {
type: String
},
effect: {
type: String,
default: 'dark',
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,67 +0,0 @@
<template>
<el-card>
<template v-slot:header>
<span class="title">{{$t('commons.calendar_heatmap')}}</span>
</template>
<calendar-heatmap :end-date="endDate" :values="values" :locale="locale"
:tooltip-unit="unit"
:range-color="colorRange"/>
</el-card>
</template>
<script>
export default {
name: "MsTestHeatmap",
props: ['values'],
data() {
return {
endDate: (new Date().getTime() + 7*24*60*60*1000),
colorRange: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'],
}
},
computed: {
locale() {
return {
months: [
this.$t('commons.months_1'),
this.$t('commons.months_2'),
this.$t('commons.months_3'),
this.$t('commons.months_4'),
this.$t('commons.months_5'),
this.$t('commons.months_6'),
this.$t('commons.months_7'),
this.$t('commons.months_8'),
this.$t('commons.months_9'),
this.$t('commons.months_10'),
this.$t('commons.months_11'),
this.$t('commons.months_12')
],
days: [
this.$t('commons.weeks_0'),
this.$t('commons.weeks_1'),
this.$t('commons.weeks_2'),
this.$t('commons.weeks_3'),
this.$t('commons.weeks_4'),
this.$t('commons.weeks_5'),
this.$t('commons.weeks_6')
],
No: 'No',
on: ',',
less: 'Less',
more: 'More'
}
},
unit() {
return this.$t('commons.test_unit')
}
},
}
</script>
<style scoped>
.el-card {
height: 370px;
}
</style>

View File

@ -1,62 +0,0 @@
<template>
<el-tooltip
:content="tip"
placement="bottom"
:enterable="false"
:effect="effect"
>
<el-button
circle
:disabled="disabled"
:type="type"
:icon="icon"
:size="size"
@click="exec()"
@click.stop="clickStop"
@keydown.enter.native.prevent
/>
</el-tooltip>
</template>
<script>
export default {
name: 'MsTipButton',
props: {
tip: String,
icon: {
type: String,
default: 'el-icon-question'
},
type: {
type: String,
default: null
},
effect: {
type: String,
default: 'dark'
},
size: {
type: String,
default: 'mini'
},
disabled: {
type: Boolean,
default: false
}
},
methods: {
exec() {
this.$emit('click')
},
clickStop() {
this.$emit('clickStop')
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,31 +0,0 @@
<template>
<div class="report-export">
<ms-report-title :title="title" :type="type"/>
<slot></slot>
</div>
</template>
<script>
import MsReportTitle from "./MsReportTitle";
export default {
name: "MsReportExportTemplate",
components: {MsReportTitle},
props: {title: String, type: String},
}
</script>
<style scoped>
.report-export {
padding: 30px;
}
.report-title {
margin-bottom: 30px;
}
.report-export {
background: white;
}
</style>

View File

@ -1,61 +0,0 @@
<template>
<div class="clearfix report-title">
<div class="report-left">
<div class="title">
{{type}}- {{title}}
</div>
<div>
<span class="time">{{new Date() | timestampFormatDate}}</span>
</div>
</div>
<div class="report-right">
<img class="logo" src="@/assets/logo-MeterSphere.png">
</div>
</div>
</template>
<script>
export default {
name: "MsReportTitle",
props: {title: String, type: String},
data() {
return {
}
}
}
</script>
<style scoped>
.report-left {
float: left;
}
.report-right {
float: right;
}
.logo {
height: 30px;
line-height: 30px;
vertical-align: middle
}
.report-left {
font-size: 15px;
}
.time {
margin-left: 10px;
}
.title {
margin-bottom: 5px;
}
</style>

View File

@ -1,168 +0,0 @@
<template>
<span class="adv-search-bar">
<el-link v-if="showLink" type="primary" @click="open">{{ $t('commons.adv_search.title') }}</el-link>
<el-dialog
:title="$t('commons.adv_search.combine')"
:visible.sync="visible"
custom-class="adv-dialog"
:append-to-body="true"
>
<div>
<div class="search-items">
<component
:is="component.name"
v-for="(component, index) in config.components"
:key="index"
class="search-item"
:component="component"
/>
</div>
</div>
<template v-slot:footer>
<div class="dialog-footer">
<el-button @click="reset">{{ $t('commons.adv_search.reset') }}</el-button>
<el-button type="primary" @click="search">{{ $t('commons.adv_search.search') }}</el-button>
</div>
</template>
</el-dialog>
</span>
</template>
<script>
import components from './search-components'
import { cloneDeep } from 'lodash'
export default {
components: { ...components },
name: 'MsTableAdvSearchBar',
props: {
condition: Object,
showLink: {
type: Boolean,
default: true
}
},
data() {
return {
visible: false,
config: this.init()
}
},
methods: {
init() {
const config = cloneDeep(this.condition)
config.components.forEach(component => {
const operator = component.operator.value
component.operator.value = operator === undefined ? component.operator.options[0].value : operator
})
return config
},
search() {
const condition = {}
this.config.components.forEach(component => {
const operator = component.operator.value
const value = component.value
if (Array.isArray(value)) {
if (value.length > 0) {
condition[component.key] = {
operator: operator,
value: value
}
}
} else {
if (value !== undefined && value !== null && value !== '') {
condition[component.key] = {
operator: operator,
value: value
}
}
}
})
// name
if (this.condition.name) this.condition.name = undefined
//
this.condition.combine = condition
this.$emit('update:condition', this.condition)
this.$emit('search', condition)
this.visible = false
},
reset() {
const source = this.condition.components
this.config.components.forEach((component, index) => {
if (component.operator.value !== undefined) {
const operator = source[index].operator.value
component.operator.value = operator === undefined ? component.operator.options[0].value : operator
}
if (component.value !== undefined) {
component.value = source[index].value
}
})
this.condition.combine = undefined
this.$emit('update:condition', this.condition)
this.$emit('search')
},
open() {
this.visible = true
}
}
}
</script>
<style>
@media only screen and (min-width: 1870px) {
.el-dialog.adv-dialog {
width: 70%;
}
}
@media only screen and (min-width: 1650px) and (max-width: 1869px) {
.el-dialog.adv-dialog {
width: 80%;
}
}
@media only screen and (min-width: 1470px) and (max-width: 1649px) {
.el-dialog.adv-dialog {
width: 90%;
}
}
@media only screen and (max-width: 1469px) {
.el-dialog.adv-dialog {
width: 70%;
min-width: 695px;
}
}
</style>
<style scoped>
.adv-search-bar {
margin-left: 5px;
}
.dialog-footer {
text-align: center;
}
.search-items {
width: 100%;
}
@media only screen and (max-width: 1469px) {
.search-item {
width: 100%;
}
}
@media only screen and (min-width: 1470px) {
.search-item {
width: 50%;
}
}
.search-item {
display: inline-block;
margin-top: 10px;
}
</style>

View File

@ -1,73 +0,0 @@
<template>
<div>
<div class="search-label">{{ $t(component.label) }}</div>
<el-select
v-model="component.operator.value"
class="search-operator"
:placeholder="$t('commons.please_select')"
size="small"
v-bind="component.operator.props"
@change="change"
@input="input"
>
<el-option v-for="o in operators" :key="o.value" :label="$t(o.label)" :value="o.value" />
</el-select>
<div v-if="showContent" class="search-content">
<slot :component="component" />
</div>
</div>
</template>
<script>
export default {
name: 'MsTableSearchComponent',
props: ['component'],
data() {
return {
operators: this.component.operator.options || []
}
},
computed: {
showContent() {
if (this.component.isShow) {
return this.component.isShow(this.component.operator.value)
}
return true
}
},
methods: {
change(value) {
if (this.component.operator.change) {
this.component.operator.change(this.component, value)
}
this.$emit('change', value)
},
input(value) {
this.$emit('input', value)
}
}
}
</script>
<style scoped>
.search-label {
display: inline-block;
width: 120px;
box-sizing: border-box;
padding-left: 5px;
}
.search-operator {
display: inline-block;
width: 120px;
}
.search-content {
display: inline-block;
padding: 0 5px 0 10px;
width: calc(100% - 240px);
box-sizing: border-box;
}
</style>

View File

@ -1,56 +0,0 @@
<template>
<ms-table-search-component v-model="component.operator.value" :component="component">
<template v-slot="scope">
<el-date-picker
:key="type"
v-model="scope.component.value"
v-bind="scope.component.props"
:placeholder="$t('commons.date.select_date')"
size="small"
:type="type"
value-format="timestamp"
:range-separator="$t('commons.date.range_separator')"
:start-placeholder="$t('commons.date.start_date')"
:end-placeholder="$t('commons.date.end_date')"
/>
</template>
</ms-table-search-component>
</template>
<script>
import MsTableSearchComponent from './MsTableSearchComponet'
import { OPERATORS } from './search-components'
export default {
name: 'MsTableSearchDatePicker',
components: { MsTableSearchComponent },
props: ['component'],
computed: {
type() {
if (this.component.operator.value === OPERATORS.BETWEEN.value) {
return 'daterange'
} else {
return 'date'
}
}
},
methods: {
change(value) {
if (value === OPERATORS.BETWEEN.value) {
if (!Array.isArray(this.component.value)) {
this.component.value = []
}
} else {
if (Array.isArray(this.component.value)) {
this.component.value = ''
}
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,56 +0,0 @@
<template>
<ms-table-search-component v-model="component.operator.value" :component="component" @change="change">
<template v-slot="scope">
<el-date-picker
:key="type"
v-model="scope.component.value"
v-bind="scope.component.props"
:placeholder="$t('commons.date.select_date_time')"
size="small"
:type="type"
value-format="timestamp"
:range-separator="$t('commons.date.range_separator')"
:start-placeholder="$t('commons.date.start_date_time')"
:end-placeholder="$t('commons.date.end_date_time')"
/>
</template>
</ms-table-search-component>
</template>
<script>
import MsTableSearchComponent from './MsTableSearchComponet'
import { OPERATORS } from './search-components'
export default {
name: 'MsTableSearchDateTimePicker',
components: { MsTableSearchComponent },
props: ['component'],
computed: {
type() {
if (this.component.operator.value === OPERATORS.BETWEEN.value) {
return 'datetimerange'
} else {
return 'datetime'
}
}
},
methods: {
change(value) {
if (value === OPERATORS.BETWEEN.value) {
if (!Array.isArray(this.component.value)) {
this.component.value = []
}
} else {
if (Array.isArray(this.component.value)) {
this.component.value = ''
}
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,35 +0,0 @@
<template>
<ms-table-search-component v-model="component.operator.value" :component="component">
<template v-slot="scope">
<el-input
v-model="scope.component.value"
v-bind="props"
:placeholder="$t('commons.input_content')"
size="small"
/>
</template>
</ms-table-search-component>
</template>
<script>
import MsTableSearchComponent from './MsTableSearchComponet'
export default {
name: 'MsTableSearchInput',
components: { MsTableSearchComponent },
props: ['component'],
data() {
return {
props: {
maxlength: '60',
showWordLimit: true,
...this.component.props
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,62 +0,0 @@
<template>
<ms-table-search-component v-model="component.operator.value" :component="component">
<template v-slot="scope">
<el-select
v-model="scope.component.value"
:placeholder="$t('commons.please_select')"
size="small"
filterable
v-bind="scope.component.props"
class="search-select"
>
<el-option v-for="op in options" :key="op.value" :label="label(op)" :value="op.value" />
</el-select>
</template>
</ms-table-search-component>
</template>
<script>
import MsTableSearchComponent from './MsTableSearchComponet'
export default {
name: 'MsTableSearchSelect',
components: { MsTableSearchComponent },
props: ['component'],
data() {
return {
options: !(this.component.options instanceof Array) ? [] : this.component.options || []
}
},
computed: {
label() {
return op => {
if (this.component.options.showLabel) {
return this.component.options.showLabel(op)
}
return op.label.indexOf('.') !== -1 ? this.$t(op.label) : op.label
}
}
},
created() {
if (!(this.component.options instanceof Array) && this.component.options.url) {
this.$get(this.component.options.url, response => {
if (response.data) {
response.data.forEach(item => {
this.options.push({
label: item[this.component.options.labelKey],
value: item[this.component.options.valueKey]
})
})
}
})
}
}
}
</script>
<style scoped>
.search-select {
display: inline-block;
width: 100%;
}
</style>

View File

@ -1,463 +0,0 @@
import MsTableSearchInput from './MsTableSearchInput'
import MsTableSearchDateTimePicker from './MsTableSearchDateTimePicker'
import MsTableSearchDatePicker from './MsTableSearchDatePicker'
import MsTableSearchSelect from './MsTableSearchSelect'
export default {
MsTableSearchInput, MsTableSearchDatePicker, MsTableSearchDateTimePicker, MsTableSearchSelect
}
export const OPERATORS = {
LIKE: {
label: 'commons.adv_search.operators.like',
value: 'like'
},
NOT_LIKE: {
label: 'commons.adv_search.operators.not_like',
value: 'not like'
},
IN: {
label: 'commons.adv_search.operators.in',
value: 'in'
},
NOT_IN: {
label: 'commons.adv_search.operators.not_in',
value: 'not in'
},
GT: {
label: 'commons.adv_search.operators.gt',
value: 'gt'
},
GE: {
label: 'commons.adv_search.operators.ge',
value: 'ge'
},
LT: {
label: 'commons.adv_search.operators.lt',
value: 'lt'
},
LE: {
label: 'commons.adv_search.operators.le',
value: 'le'
},
EQ: {
label: 'commons.adv_search.operators.equals',
value: 'eq'
},
BETWEEN: {
label: 'commons.adv_search.operators.between',
value: 'between'
},
CURRENT_USER: {
label: 'commons.adv_search.operators.current_user',
value: 'current user'
}
}
export const NAME = {
key: 'name', // 返回结果Map的key
name: 'MsTableSearchInput', // Vue控件名称
label: 'commons.name', // 显示名称
operator: { // 运算符设置
value: OPERATORS.LIKE.value, // 如果未设置value初始值则value初始值为options[0]
options: [OPERATORS.LIKE, OPERATORS.NOT_LIKE] // 运算符候选项
}
}
export const UPDATE_TIME = {
key: 'updateTime',
name: 'MsTableSearchDateTimePicker',
label: 'commons.update_time',
operator: {
options: [OPERATORS.BETWEEN, OPERATORS.GT, OPERATORS.GE, OPERATORS.LT, OPERATORS.LE, OPERATORS.EQ]
}
}
export const PROJECT_NAME = {
key: 'projectName',
name: 'MsTableSearchInput',
label: 'commons.adv_search.project',
operator: {
options: [OPERATORS.LIKE, OPERATORS.NOT_LIKE]
}
}
export const TEST_NAME = {
key: 'testName',
name: 'MsTableSearchInput',
label: 'commons.adv_search.test',
operator: {
options: [OPERATORS.LIKE, OPERATORS.NOT_LIKE]
}
}
export const TEST_PLAN_NAME = {
key: 'testPlanName',
name: 'MsTableSearchInput',
label: 'test_track.report.list.test_plan',
operator: {
options: [OPERATORS.LIKE, OPERATORS.NOT_LIKE]
}
}
export const CREATE_TIME = {
key: 'createTime',
name: 'MsTableSearchDateTimePicker',
label: 'commons.create_time',
operator: {
options: [OPERATORS.BETWEEN, OPERATORS.GT, OPERATORS.GE, OPERATORS.LT, OPERATORS.LE, OPERATORS.EQ]
}
}
export const STATUS = {
key: 'status',
name: 'MsTableSearchSelect',
label: 'commons.status',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ label: 'Saved', value: 'Saved' }, { label: 'Starting', value: 'Starting' },
{ label: 'Running', value: 'Running' }, { label: 'Reporting', value: 'Reporting' },
{ label: 'Completed', value: 'Completed' }, { label: 'Error', value: 'Error' },
{ label: 'Success', value: 'Success' }
],
props: { // 尾部控件的props一般为element ui控件的props
multiple: true
}
}
export const API_STATUS = {
key: 'status',
name: 'MsTableSearchSelect',
label: 'commons.status',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ value: 'Prepare', label: '未开始' },
{ value: 'Underway', label: '进行中' },
{ value: 'Completed', label: '已完成' }
],
props: { // 尾部控件的props一般为element ui控件的props
multiple: true
}
}
export const API_CASE_PRIORITY = {
key: 'priority',
name: 'MsTableSearchSelect',
label: 'test_track.case.priority',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ value: 'P0', label: 'P0' },
{ value: 'P1', label: 'P1' },
{ value: 'P2', label: 'P2' },
{ value: 'P3', label: 'P3' }
],
props: { // 尾部控件的props一般为element ui控件的props
multiple: true
}
}
export const API_CASE_RESULT = {
key: 'status',
name: 'MsTableSearchSelect',
label: 'test_track.plan_view.execute_result',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ value: 'success', label: 'api_test.automation.success' },
{ value: 'error', label: 'api_test.automation.fail' }
],
props: { // 尾部控件的props一般为element ui控件的props
multiple: true
}
}
export const API_SCENARIO_RESULT = {
key: 'status',
name: 'MsTableSearchSelect',
label: 'test_track.plan_view.execute_result',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ value: 'Success', label: 'api_test.automation.success' },
{ value: 'Fail', label: 'api_test.automation.fail' }
],
props: { // 尾部控件的props一般为element ui控件的props
multiple: true
}
}
export const API_METHOD = {
key: 'method',
name: 'MsTableSearchSelect',
label: 'api_test.definition.api_type',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ value: 'GET', label: 'GET' },
{ value: 'POST', label: 'POST' },
{ value: 'PUT', label: 'PUT' },
{ value: 'PATCH', label: 'PATCH' },
{ value: 'DELETE', label: 'DELETE' },
{ value: 'OPTIONS', label: 'OPTIONS' },
{ value: 'HEAD', label: 'HEAD' },
{ value: 'CONNECT', label: 'CONNECT' },
{ value: 'DUBBO', label: 'DUBBO' },
{ value: 'dubbo://', label: 'dubbo://' },
{ value: 'SQL', label: 'SQL' },
{ value: 'TCP', label: 'TCP' }
],
props: { // 尾部控件的props一般为element ui控件的props
multiple: true
}
}
export const API_PATH = {
key: 'path', // 返回结果Map的key
name: 'MsTableSearchInput', // Vue控件名称
label: 'api_test.definition.api_path', // 显示名称
operator: { // 运算符设置
value: OPERATORS.LIKE.value, // 如果未设置value初始值则value初始值为options[0]
options: [OPERATORS.LIKE, OPERATORS.NOT_LIKE] // 运算符候选项
}
}
export const API_TAGS = {
key: 'tags', // 返回结果Map的key
name: 'MsTableSearchInput', // Vue控件名称
label: 'commons.tag', // 显示名称
operator: { // 运算符设置
value: OPERATORS.LIKE.value, // 如果未设置value初始值则value初始值为options[0]
options: [OPERATORS.LIKE, OPERATORS.NOT_LIKE] // 运算符候选项
}
}
export const CREATOR = {
key: 'creator',
name: 'MsTableSearchSelect',
label: 'api_test.creator',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN, OPERATORS.CURRENT_USER],
change: function(component, value) { // 运算符change事件
if (value === OPERATORS.CURRENT_USER.value) {
component.value = value
}
}
},
options: { // 异步获取候选项
url: '/user/list',
labelKey: 'name',
valueKey: 'id',
showLabel: option => {
return option.label + '(' + option.value + ')'
}
},
props: {
multiple: true
},
isShow: operator => {
return operator !== OPERATORS.CURRENT_USER.value
}
}
export const EXECUTOR = {
key: 'executor',
name: 'MsTableSearchSelect',
label: 'test_track.plan_view.executor',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN, OPERATORS.CURRENT_USER],
change: function(component, value) { // 运算符change事件
if (value === OPERATORS.CURRENT_USER.value) {
component.value = value
}
}
},
options: { // 异步获取候选项
url: '/user/list',
labelKey: 'name',
valueKey: 'id',
showLabel: option => {
return option.label + '(' + option.value + ')'
}
},
props: {
multiple: true
},
isShow: operator => {
return operator !== OPERATORS.CURRENT_USER.value
}
}
export const TRIGGER_MODE = {
key: 'triggerMode',
name: 'MsTableSearchSelect',
label: 'commons.trigger_mode.name',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ label: 'commons.trigger_mode.manual', value: 'MANUAL' },
{ label: 'commons.trigger_mode.schedule', value: 'SCHEDULE' },
{ label: 'commons.trigger_mode.api', value: 'API' }
],
props: {
multiple: true
}
}
export const PRIORITY = {
key: 'priority',
name: 'MsTableSearchSelect',
label: 'test_track.case.priority',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ label: 'P0', value: 'P0' },
{ label: 'P1', value: 'P1' },
{ label: 'P2', value: 'P2' },
{ label: 'P3', value: 'P3' }
],
props: {
multiple: true
}
}
export const TYPE = {
key: 'type',
name: 'MsTableSearchSelect',
label: 'test_track.case.type',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ label: 'commons.functional', value: 'functional' },
{ label: 'commons.performance', value: 'performance' },
{ label: 'commons.api', value: 'api' }
],
props: {
multiple: true
}
}
export const METHOD = {
key: 'method',
name: 'MsTableSearchSelect',
label: 'test_track.case.method',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ label: 'test_track.case.manual', value: 'manual' },
{ label: 'test_track.case.auto', value: 'auto' }
],
props: {
multiple: true
}
}
export const MODULE = {
key: 'module',
name: 'MsTableSearchInput',
label: 'test_track.case.module',
operator: {
value: OPERATORS.LIKE.value,
options: [OPERATORS.LIKE, OPERATORS.NOT_LIKE]
}
}
export const PRINCIPAL = {
key: 'principal',
name: 'MsTableSearchSelect',
label: 'test_track.plan.plan_principal',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN, OPERATORS.CURRENT_USER],
change: function(component, value) { // 运算符change事件
if (value === OPERATORS.CURRENT_USER.value) {
component.value = value
}
}
},
options: { // 异步获取候选项
url: '/user/list',
labelKey: 'name',
valueKey: 'id',
showLabel: option => {
return option.label + '(' + option.value + ')'
}
},
props: {
multiple: true
},
isShow: operator => {
return operator !== OPERATORS.CURRENT_USER.value
}
}
export const STAGE = {
key: 'stage',
name: 'MsTableSearchSelect',
label: 'test_track.plan.plan_stage',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ label: 'test_track.plan.smoke_test', value: 'smoke' },
{ label: 'test_track.plan.regression_test', value: 'regression' },
{ label: 'test_track.plan.system_test', value: 'system' }
],
props: {
multiple: true
}
}
export const TEST_PLAN_STATUS = {
key: 'status',
name: 'MsTableSearchSelect',
label: 'test_track.plan.plan_status',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ label: 'test_track.plan.plan_status_prepare', value: 'Prepare' },
{ label: 'test_track.plan.plan_status_running', value: 'Underway' },
{ label: 'test_track.plan.plan_status_completed', value: 'Completed' }
],
props: {
multiple: true
}
}
export const TEST_PLAN_TRIGGER_MODE = {
key: 'triggerMode',
name: 'MsTableSearchSelect',
label: 'test_track.report.list.trigger_mode',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{ label: 'test_track.report.trigger_mode.manual', value: 'manual' },
{ label: 'test_track.report.trigger_mode.automation', value: 'automation' }
],
props: {
multiple: true
}
}
export const TEST_CONFIGS = [NAME, UPDATE_TIME, CREATE_TIME, STATUS, CREATOR]
export const REPORT_CONFIGS = [NAME, TEST_NAME, CREATE_TIME, STATUS, CREATOR, TRIGGER_MODE]
export const TEST_CASE_CONFIGS = [NAME, API_TAGS, MODULE, PRIORITY, CREATE_TIME, TYPE, UPDATE_TIME, METHOD, CREATOR, EXECUTOR]
export const TEST_PLAN_CONFIGS = [NAME, UPDATE_TIME, CREATE_TIME, PRINCIPAL, TEST_PLAN_STATUS, STAGE]
export const API_DEFINITION_CONFIGS = [NAME, API_METHOD, API_PATH, API_STATUS, API_TAGS, UPDATE_TIME, CREATE_TIME, CREATOR]
export const API_CASE_CONFIGS = [NAME, API_CASE_PRIORITY, API_TAGS, API_CASE_RESULT, UPDATE_TIME, CREATE_TIME, CREATOR]
export const API_SCENARIO_CONFIGS = [NAME, API_CASE_PRIORITY, API_TAGS, API_SCENARIO_RESULT, UPDATE_TIME, CREATE_TIME, CREATOR]
export const TEST_PLAN_REPORT_CONFIGS = [NAME, TEST_PLAN_NAME, CREATOR, CREATE_TIME, TEST_PLAN_TRIGGER_MODE, TEST_PLAN_STATUS]

View File

@ -1,25 +0,0 @@
<template>
<el-table-column width="1" :resizable="false" align="center">
<el-popover slot="header" placement="right" trigger="click" style="margin-right: 0px;">
<el-link @click.native.stop="$emit('selectAll')">{{$t('api_test.batch_menus.select_all_data',[total])}}</el-link>
<br/>
<el-link @click.native.stop="$emit('selectPageAll')">{{$t('api_test.batch_menus.select_show_data',[pageSize])}}</el-link>
<i class="el-icon-arrow-down" slot="reference"></i>
</el-popover>
</el-table-column>
</template>
<script>
export default {
name: "MsTableSelectAll",
props: ['total', 'pageSize'],
data() {
return {
};
}
}
</script>
<style scoped>
</style>

View File

@ -1,28 +0,0 @@
<template>
<el-table-column width="1" :resizable="false" align="center">
<el-dropdown slot="header">
<span class="el-dropdown-link">
<i class="el-icon-arrow-down"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native.stop="$emit('selectAll')">
{{$t('api_test.batch_menus.select_all_data',[total])}}
</el-dropdown-item>
<el-dropdown-item @click.native.stop="$emit('selectPageAll')">
{{$t('api_test.batch_menus.select_show_data',[pageSize])}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-table-column>
</template>
<script>
export default {
name: "MsTableSelectAll",
props: ['total', 'pageSize']
}
</script>
<style scoped>
</style>

View File

@ -1,429 +0,0 @@
<template>
<div>
<el-tabs type="border-card">
<el-tab-pane :label="$t('schedule.cron.seconds')" v-if="shouldHide('second')">
<crontab-second
@update="updateContabValue"
:check="checkNumber"
ref="cronsecond"
/>
</el-tab-pane>
<el-tab-pane :label="$t('schedule.cron.minutes')" v-if="shouldHide('min')">
<crontab-min
@update="updateContabValue"
:check="checkNumber"
:cron="contabValueObj"
ref="cronmin"
/>
</el-tab-pane>
<el-tab-pane :label="$t('schedule.cron.hours')" v-if="shouldHide('hour')">
<crontab-hour
@update="updateContabValue"
:check="checkNumber"
:cron="contabValueObj"
ref="cronhour"
/>
</el-tab-pane>
<el-tab-pane :label="$t('schedule.cron.day')" v-if="shouldHide('day')">
<crontab-day
@update="updateContabValue"
:check="checkNumber"
:cron="contabValueObj"
ref="cronday"
/>
</el-tab-pane>
<el-tab-pane :label="$t('schedule.cron.month')" v-if="shouldHide('mouth')">
<crontab-mouth
@update="updateContabValue"
:check="checkNumber"
:cron="contabValueObj"
ref="cronmouth"
/>
</el-tab-pane>
<el-tab-pane :label="$t('schedule.cron.weeks')" v-if="shouldHide('week')">
<crontab-week
@update="updateContabValue"
:check="checkNumber"
:cron="contabValueObj"
ref="cronweek"
/>
</el-tab-pane>
<el-tab-pane :label="$t('schedule.cron.years')" v-if="shouldHide('year')">
<crontab-year @update="updateContabValue"
:check="checkNumber"
:cron="contabValueObj"
ref="cronyear"/>
</el-tab-pane>
</el-tabs>
<div class="popup-main">
<div class="popup-result-container">
<p class="title">{{$t('schedule.cron.time_expression')}}</p>
<table>
<thead>
<th v-for="item of tabTitles" width="40" :key="item">{{item}}</th>
<th>{{$t('schedule.cron.complete_expression')}}</th>
</thead>
<tbody>
<td>
<span>{{contabValueObj.second}}</span>
</td>
<td>
<span>{{contabValueObj.min}}</span>
</td>
<td>
<span>{{contabValueObj.hour}}</span>
</td>
<td>
<span>{{contabValueObj.day}}</span>
</td>
<td>
<span>{{contabValueObj.mouth}}</span>
</td>
<td>
<span>{{contabValueObj.week}}</span>
</td>
<td>
<span>{{contabValueObj.year}}</span>
</td>
<td>
<span>{{contabValueString}}</span>
</td>
</tbody>
</table>
</div>
<crontab-result :ex="contabValueString" ref="crontabResult"/>
<div class="pop_btn">
<el-button size="small" type="primary" @click="submitFill">{{$t('commons.confirm')}}</el-button>
<el-button size="small" type="warning" @click="clearCron">{{$t('api_test.reset')}}</el-button>
<el-button size="small" @click="hidePopup">{{$t('commons.cancel')}}</el-button>
</div>
</div>
</div>
</template>
<script>
import CrontabSecond from "./CrontabSecond.vue";
import CrontabMin from "./CrontabMin.vue";
import CrontabHour from "./CrontabHour.vue";
import CrontabDay from "./CrontabDay.vue";
import CrontabMouth from "./CrontabMouth.vue";
import CrontabWeek from "./CrontabWeek.vue";
import CrontabYear from "./CrontabYear.vue";
import CrontabResult from "./CrontabResult.vue";
export default {
name: "Crontab",
data() {
return {
tabTitles: [
this.$t('schedule.cron.seconds'),
this.$t('schedule.cron.minutes'),
this.$t('schedule.cron.hours'),
this.$t('schedule.cron.day'),
this.$t('schedule.cron.month'),
this.$t('schedule.cron.weeks'),
this.$t('schedule.cron.years')],
tabActive: 0,
myindex: 0,
contabValueObj: {
second: "*",
min: "*",
hour: "*",
day: "*",
mouth: "*",
week: "?",
year: "",
},
};
},
props: ["expression", "hideComponent"],
methods: {
shouldHide(key) {
if (this.hideComponent && this.hideComponent.includes(key)) return false;
return true;
},
resolveExp() {
//
if (this.expression) {
let arr = this.expression.split(" ");
if (arr.length >= 6) {
//6
let obj = {
second: arr[0],
min: arr[1],
hour: arr[2],
day: arr[3],
mouth: arr[4],
week: arr[5],
year: arr[6] ? arr[6] : "",
};
this.contabValueObj = {
...obj,
};
for (let i in obj) {
if (obj[i]) this.changeRadio(i, obj[i]);
}
}
} else {
//
this.clearCron();
}
},
// tab
tabCheck(index) {
this.tabActive = index;
},
//
updateContabValue(name, value, from) {
"updateContabValue", name, value, from;
this.contabValueObj[name] = value;
if (from && from !== name) {
// console.log(` ${from} ${name} ${value}`);
this.changeRadio(name, value);
}
},
//
changeRadio(name, value) {
let arr = ["second", "min", "hour", "mouth"],
refName = "cron" + name,
insVlaue;
if (!this.$refs[refName]) return;
if (arr.includes(name)) {
if (value === "*") {
insVlaue = 1;
} else if (value.indexOf("-") > -1) {
let indexArr = value.split("-");
isNaN(indexArr[0])
? (this.$refs[refName].cycle01 = 0)
: (this.$refs[refName].cycle01 = indexArr[0]);
this.$refs[refName].cycle02 = indexArr[1];
insVlaue = 2;
} else if (value.indexOf("/") > -1) {
let indexArr = value.split("/");
isNaN(indexArr[0])
? (this.$refs[refName].average01 = 0)
: (this.$refs[refName].average01 = indexArr[0]);
this.$refs[refName].average02 = indexArr[1];
insVlaue = 3;
} else {
insVlaue = 4;
this.$refs[refName].checkboxList = value.split(",");
}
} else if (name == "day") {
if (value === "*") {
insVlaue = 1;
} else if (value == "?") {
insVlaue = 2;
} else if (value.indexOf("-") > -1) {
let indexArr = value.split("-");
isNaN(indexArr[0])
? (this.$refs[refName].cycle01 = 0)
: (this.$refs[refName].cycle01 = indexArr[0]);
this.$refs[refName].cycle02 = indexArr[1];
insVlaue = 3;
} else if (value.indexOf("/") > -1) {
let indexArr = value.split("/");
isNaN(indexArr[0])
? (this.$refs[refName].average01 = 0)
: (this.$refs[refName].average01 = indexArr[0]);
this.$refs[refName].average02 = indexArr[1];
insVlaue = 4;
} else if (value.indexOf("W") > -1) {
let indexArr = value.split("W");
isNaN(indexArr[0])
? (this.$refs[refName].workday = 0)
: (this.$refs[refName].workday = indexArr[0]);
insVlaue = 5;
} else if (value === "L") {
insVlaue = 6;
} else {
this.$refs[refName].checkboxList = value.split(",");
insVlaue = 7;
}
} else if (name == "week") {
if (value === "*") {
insVlaue = 1;
} else if (value == "?") {
insVlaue = 2;
} else if (value.indexOf("-") > -1) {
let indexArr = value.split("-");
isNaN(indexArr[0])
? (this.$refs[refName].cycle01 = 0)
: (this.$refs[refName].cycle01 = indexArr[0]);
this.$refs[refName].cycle02 = indexArr[1];
insVlaue = 3;
} else if (value.indexOf("#") > -1) {
let indexArr = value.split("#");
isNaN(indexArr[0])
? (this.$refs[refName].average01 = 1)
: (this.$refs[refName].average01 = indexArr[0]);
this.$refs[refName].average02 = indexArr[1];
insVlaue = 4;
} else if (value.indexOf("L") > -1) {
let indexArr = value.split("L");
isNaN(indexArr[0])
? (this.$refs[refName].weekday = 1)
: (this.$refs[refName].weekday = indexArr[0]);
insVlaue = 5;
} else {
this.$refs[refName].checkboxList = value.split(",");
insVlaue = 7;
}
} else if (name == "year") {
if (value == "") {
insVlaue = 1;
} else if (value == "*") {
insVlaue = 2;
} else if (value.indexOf("-") > -1) {
insVlaue = 3;
} else if (value.indexOf("/") > -1) {
insVlaue = 4;
} else {
this.$refs[refName].checkboxList = value.split(",");
insVlaue = 5;
}
}
this.$refs[refName].radioValue = insVlaue;
},
// -props
checkNumber(value, minLimit, maxLimit) {
//
value = Math.floor(value);
if (value < minLimit) {
value = minLimit;
} else if (value > maxLimit) {
value = maxLimit;
}
return value;
},
//
hidePopup() {
this.$emit("hide");
},
//
submitFill() {
this.$emit("fill", this.contabValueString, this.$refs.crontabResult.resultList);
this.hidePopup();
},
clearCron() {
//
// ("");
this.contabValueObj = {
second: "*",
min: "*",
hour: "*",
day: "*",
mouth: "*",
week: "?",
year: "",
};
for (let j in this.contabValueObj) {
this.changeRadio(j, this.contabValueObj[j]);
}
},
},
computed: {
contabValueString: function() {
let obj = this.contabValueObj;
let str =
obj.second +
" " +
obj.min +
" " +
obj.hour +
" " +
obj.day +
" " +
obj.mouth +
" " +
obj.week +
(obj.year == "" ? "" : " " + obj.year);
return str;
},
},
components: {
CrontabSecond,
CrontabMin,
CrontabHour,
CrontabDay,
CrontabMouth,
CrontabWeek,
CrontabYear,
CrontabResult,
},
watch: {
expression: "resolveExp",
hideComponent(value) {
//
},
},
mounted: function() {
this.resolveExp();
},
};
</script>
<style scoped>
.pop_btn {
text-align: center;
margin-top: 20px;
}
.popup-main {
position: relative;
margin: 10px auto;
background: #fff;
border-radius: 5px;
font-size: 12px;
overflow: hidden;
}
.popup-title {
overflow: hidden;
line-height: 34px;
padding-top: 6px;
background: #f2f2f2;
}
.popup-result-container {
box-sizing: border-box;
line-height: 24px;
margin: 25px auto;
padding: 15px 10px 10px;
border: 1px solid #ccc;
position: relative;
}
.popup-result-container .title {
position: absolute;
top: -28px;
left: 50%;
width: 140px;
font-size: 14px;
margin-left: -70px;
text-align: center;
line-height: 30px;
background: #fff;
}
.popup-result-container table {
text-align: center;
width: 100%;
margin: 0 auto;
}
.popup-result-container table span {
display: block;
width: 100%;
font-family: arial;
line-height: 30px;
height: 30px;
white-space: nowrap;
overflow: hidden;
border: 1px solid #e8e8e8;
}
</style>

View File

@ -1,179 +0,0 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio v-model='radioValue' :label="1">
{{$t('schedule.cron.day')}}{{$t('schedule.cron.day_allowed_wildcards')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="2">
{{$t('schedule.cron.not_specify')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="3">
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
<el-input-number v-model='cycle01' :min="0" :max="31" /> -
<el-input-number v-model='cycle02' :min="0" :max="31" /> {{$t('schedule.cron.day')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="4">
{{$t('schedule.cron.from')}}
<el-input-number v-model='average01' :min="0" :max="31" /> {{$t('schedule.cron.day_unit')}}{{$t('schedule.cron.start')}}{{$t('schedule.cron.every')}}
<el-input-number v-model='average02' :min="0" :max="31" /> {{$t('schedule.cron.day')}}{{$t('schedule.cron.execute_once')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="5">
{{$t('schedule.cron.every')}}{{$t('schedule.cron.month')}}
<el-input-number v-model='workday' :min="0" :max="31" /> {{$t('schedule.cron.day_unit')}}{{$t('schedule.cron.last_working_day')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="6">
{{$t('schedule.cron.last_working_day')}}{{$t('schedule.cron.last_day_of_the_month')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="7">
{{$t('schedule.cron.specify')}}
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
<el-option v-for="item in 31" :key="item" :value="item">{{item}}</el-option>
</el-select>
</el-radio>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
return {
radioValue: 1,
workday: 1,
cycle01: 1,
cycle02: 2,
average01: 1,
average02: 1,
checkboxList: [],
checkNum: this.$options.propsData.check
}
},
name: 'CrontabDay',
props: ['check', 'cron'],
methods: {
//
radioChange() {
('day rachange');
if (this.radioValue === 1) {
this.$emit('update', 'day', '*', 'day');
this.$emit('update', 'week', '?', 'day');
this.$emit('update', 'mouth', '*', 'day');
} else {
if (this.cron.hour === '*') {
this.$emit('update', 'hour', '0', 'day');
}
if (this.cron.min === '*') {
this.$emit('update', 'min', '0', 'day');
}
if (this.cron.second === '*') {
this.$emit('update', 'second', '0', 'day');
}
}
switch (this.radioValue) {
case 2:
this.$emit('update', 'day', '?');
break;
case 3:
this.$emit('update', 'day', this.cycle01 + '-' + this.cycle02);
break;
case 4:
this.$emit('update', 'day', this.average01 + '/' + this.average02);
break;
case 5:
this.$emit('update', 'day', this.workday + 'W');
break;
case 6:
this.$emit('update', 'day', 'L');
break;
case 7:
this.$emit('update', 'day', this.checkboxString);
break;
}
('day rachange end');
},
//
cycleChange() {
if (this.radioValue == '3') {
this.$emit('update', 'day', this.cycleTotal);
}
},
//
averageChange() {
if (this.radioValue == '4') {
this.$emit('update', 'day', this.averageTotal);
}
},
//
workdayChange() {
if (this.radioValue == '5') {
this.$emit('update', 'day', this.workday + 'W');
}
},
// checkbox
checkboxChange() {
if (this.radioValue == '7') {
this.$emit('update', 'day', this.checkboxString);
}
},
// week
weekChange() {
//weekday?
if (this.cron.week == '?' && this.radioValue == '2') {
this.radioValue = '1';
} else if (this.cron.week !== '?' && this.radioValue != '2') {
this.radioValue = '2';
}
},
},
watch: {
"radioValue": "radioChange",
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'workdayCheck': 'workdayChange',
'checkboxString': 'checkboxChange',
},
computed: {
//
cycleTotal: function () {
this.checkNum(this.cycle01, 1, 31)
this.checkNum(this.cycle02, 1, 31)
return this.cycle01 + '-' + this.cycle02;
},
//
averageTotal: function () {
this.checkNum(this.average01, 1, 31)
this.checkNum(this.average02, 1, 31)
return this.average01 + '/' + this.average02;
},
//
workdayCheck: function () {
this.checkNum(this.workday, 1, 31)
return this.workday;
},
// checkbox
checkboxString: function () {
let str = this.checkboxList.join();
return str == '' ? '*' : str;
}
}
}
</script>

View File

@ -1,122 +0,0 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio v-model='radioValue' :label="1">
{{$t('schedule.cron.hours')}}{{$t('schedule.cron.allowed_wildcards')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="2">
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
<el-input-number v-model='cycle01' :min="0" :max="24" /> -
<el-input-number v-model='cycle02' :min="0" :max="24" /> 小时
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="3">
{{$t('schedule.cron.from')}}
<el-input-number v-model='average01' :min="0" :max="24" /> {{$t('schedule.cron.hours')}}{{$t('schedule.cron.start')}}{{$t('schedule.cron.every')}}
<el-input-number v-model='average02' :min="0" :max="24" /> {{$t('schedule.cron.hours')}}{{$t('schedule.cron.execute_once')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="4">
{{$t('schedule.cron.specify')}}
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
<el-option v-for="item in 24" :key="item" :value="item-1">{{item-1}}</el-option>
</el-select>
</el-radio>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
return {
radioValue: 1,
cycle01: 0,
cycle02: 1,
average01: 0,
average02: 1,
checkboxList: [],
checkNum: this.$options.propsData.check
}
},
name: 'CrontabHour',
props: ['check', 'cron'],
methods: {
//
radioChange() {
if (this.radioValue === 1) {
this.$emit('update', 'hour', '*', 'hour');
this.$emit('update', 'day', '*', 'hour');
} else {
if (this.cron.min === '*') {
this.$emit('update', 'min', '0', 'hour');
}
if (this.cron.second === '*') {
this.$emit('update', 'second', '0', 'hour');
}
}
switch (this.radioValue) {
case 2:
this.$emit('update', 'hour', this.cycle01 + '-' + this.cycle02);
break;
case 3:
this.$emit('update', 'hour', this.average01 + '/' + this.average02);
break;
case 4:
this.$emit('update', 'hour', this.checkboxString);
break;
}
},
//
cycleChange() {
if (this.radioValue == '2') {
this.$emit('update', 'hour', this.cycleTotal);
}
},
//
averageChange() {
if (this.radioValue == '3') {
this.$emit('update', 'hour', this.averageTotal);
}
},
// checkbox
checkboxChange() {
if (this.radioValue == '4') {
this.$emit('update', 'hour', this.checkboxString);
}
}
},
watch: {
"radioValue": "radioChange",
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'checkboxString': 'checkboxChange'
},
computed: {
//
cycleTotal: function () {
this.checkNum(this.cycle01, 0, 23)
this.checkNum(this.cycle02, 0, 23)
return this.cycle01 + '-' + this.cycle02;
},
//
averageTotal: function () {
this.checkNum(this.average01, 0, 23)
this.checkNum(this.average02, 1, 23)
return this.average01 + '/' + this.average02;
},
// checkbox
checkboxString: function () {
let str = this.checkboxList.join();
return str == '' ? '*' : str;
}
}
}
</script>

View File

@ -1,120 +0,0 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio v-model='radioValue' :label="1">
{{$t('schedule.cron.minutes')}}{{$t('schedule.cron.allowed_wildcards')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="2">
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
<el-input-number v-model='cycle01' :min="0" :max="59" /> -
<el-input-number v-model='cycle02' :min="0" :max="59" /> {{$t('schedule.cron.minutes')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="3">
{{$t('schedule.cron.from')}}
<el-input-number v-model='average01' :min="0" :max="59" /> {{$t('schedule.cron.minutes')}}{{$t('schedule.cron.start')}}{{$t('schedule.cron.every')}}
<el-input-number v-model='average02' :min="0" :max="60" /> {{$t('schedule.cron.minutes')}}{{$t('schedule.cron.execute_once')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="4">
{{$t('schedule.cron.specify')}}
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
<el-option v-for="item in 60" :key="item" :value="item-1">{{item-1}}</el-option>
</el-select>
</el-radio>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
return {
radioValue: 1,
cycle01: 1,
cycle02: 2,
average01: 0,
average02: 1,
checkboxList: [],
checkNum: this.$options.propsData.check
}
},
name: 'CrontabMin',
props: ['check', 'cron'],
methods: {
//
radioChange() {
if (this.radioValue !== 1 && this.cron.second === '*') {
this.$emit('update', 'second', '0', 'min');
}
switch (this.radioValue) {
case 1:
this.$emit('update', 'min', '*', 'min');
this.$emit('update', 'hour', '*', 'min');
break;
case 2:
this.$emit('update', 'min', this.cycle01 + '-' + this.cycle02, 'min');
break;
case 3:
this.$emit('update', 'min', this.average01 + '/' + this.average02, 'min');
break;
case 4:
this.$emit('update', 'min', this.checkboxString, 'min');
break;
}
},
//
cycleChange() {
if (this.radioValue == '2') {
this.$emit('update', 'min', this.cycleTotal, 'min');
}
},
//
averageChange() {
if (this.radioValue == '3') {
this.$emit('update', 'min', this.averageTotal, 'min');
}
},
// checkbox
checkboxChange() {
if (this.radioValue == '4') {
this.$emit('update', 'min', this.checkboxString, 'min');
}
},
},
watch: {
"radioValue": "radioChange",
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'checkboxString': 'checkboxChange',
},
computed: {
//
cycleTotal: function () {
this.checkNum(this.cycle01, 0, 59)
this.checkNum(this.cycle02, 0, 59)
return this.cycle01 + '-' + this.cycle02;
},
//
averageTotal: function () {
this.checkNum(this.average01, 0, 59)
this.checkNum(this.average02, 1, 59)
return this.average01 + '/' + this.average02;
},
// checkbox
checkboxString: function () {
let str = this.checkboxList.join();
return str == '' ? '*' : str;
}
}
}
</script>

View File

@ -1,128 +0,0 @@
<template>
<el-form size='small'>
<el-form-item>
<el-radio v-model='radioValue' :label="1">
{{$t('schedule.cron.month')}}{{$t('schedule.cron.allowed_wildcards')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="2">
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
<el-input-number v-model='cycle01' :min="1" :max="12" /> -
<el-input-number v-model='cycle02' :min="1" :max="12" /> {{$t('schedule.cron.month')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="3">
{{$t('schedule.cron.from')}}
<el-input-number v-model='average01' :min="1" :max="12" /> {{$t('schedule.cron.month')}}{{$t('schedule.cron.start')}}{{$t('schedule.cron.every')}}
<el-input-number v-model='average02' :min="1" :max="12" /> {{$t('schedule.cron.month')}}{{$t('schedule.cron.execute_once')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="4">
{{$t('schedule.cron.specify')}}
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
<el-option v-for="item in 12" :key="item" :value="item">{{item}}</el-option>
</el-select>
</el-radio>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
return {
radioValue: 1,
cycle01: 1,
cycle02: 2,
average01: 1,
average02: 1,
checkboxList: [],
checkNum: this.check
}
},
name: 'CrontabMouth',
props: ['check', 'cron'],
methods: {
//
radioChange() {
if (this.radioValue === 1) {
this.$emit('update', 'mouth', '*');
this.$emit('update', 'year', '*');
} else {
if (this.cron.day === '*') {
this.$emit('update', 'day', '1', 'mouth');
}
if (this.cron.hour === '*') {
this.$emit('update', 'hour', '0', 'mouth');
}
if (this.cron.min === '*') {
this.$emit('update', 'min', '0', 'mouth');
}
if (this.cron.second === '*') {
this.$emit('update', 'second', '0', 'mouth');
}
}
switch (this.radioValue) {
case 2:
this.$emit('update', 'mouth', this.cycle01 + '-' + this.cycle02);
break;
case 3:
this.$emit('update', 'mouth', this.average01 + '/' + this.average02);
break;
case 4:
this.$emit('update', 'mouth', this.checkboxString);
break;
}
},
//
cycleChange() {
if (this.radioValue == '2') {
this.$emit('update', 'mouth', this.cycleTotal);
}
},
//
averageChange() {
if (this.radioValue == '3') {
this.$emit('update', 'mouth', this.averageTotal);
}
},
// checkbox
checkboxChange() {
if (this.radioValue == '4') {
this.$emit('update', 'mouth', this.checkboxString);
}
}
},
watch: {
"radioValue": "radioChange",
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'checkboxString': 'checkboxChange'
},
computed: {
//
cycleTotal: function () {
this.checkNum(this.cycle01, 1, 12)
this.checkNum(this.cycle02, 1, 12)
return this.cycle01 + '-' + this.cycle02;
},
//
averageTotal: function () {
this.checkNum(this.average01, 1, 12)
this.checkNum(this.average02, 1, 12)
return this.average01 + '/' + this.average02;
},
// checkbox
checkboxString: function () {
let str = this.checkboxList.join();
return str == '' ? '*' : str;
}
}
}
</script>

View File

@ -1,604 +0,0 @@
<template>
<span>
<span v-if="enableSimpleMode">{{resultList && resultList.length > 0 ? resultList[0] : ''}}</span>
<div v-if="!enableSimpleMode" class="popup-result">
<p class="title">{{$t('schedule.cron.recent_run_time')}}</p>
<ul class="popup-result-scroll">
<template>
<li v-for='item in resultList' :key="item">{{item}}</li>
</template>
</ul>
</div>
</span>
</template>
<script>
import {cronValidate} from "../../../../common/js/cron";
export default {
name: 'CrontabResult',
data() {
return {
dayRule: '',
dayRuleSup: '',
dateArr: [],
resultList: [],
isShow: false
}
},
watch: {
'ex': 'expressionChange'
},
props: {
ex: String,
enableSimpleMode: {
type: Boolean,
default: false
}
},
mounted: function () {
//
this.expressionChange();
},
methods: {
//
expressionChange() {
// -
this.isShow = false;
if (!cronValidate(this.ex)) {
this.resultList = [];
this.$emit("resultListChange", this.resultList);
return;
}
// [0123456]
let ruleArr = this.$options.propsData.ex.split(' ');
//
let nums = 0;
//
let resultArr = [];
// []
let nTime = new Date();
let nYear = nTime.getFullYear();
let nMouth = nTime.getMonth() + 1;
let nDay = nTime.getDate();
let nHour = nTime.getHours();
let nMin = nTime.getMinutes();
let nSecond = nTime.getSeconds();
// 100
this.getSecondArr(ruleArr[0]);
this.getMinArr(ruleArr[1]);
this.getHourArr(ruleArr[2]);
this.getDayArr(ruleArr[3]);
this.getMouthArr(ruleArr[4]);
this.getWeekArr(ruleArr[5]);
this.getYearArr(ruleArr[6], nYear);
// -便使
let sDate = this.dateArr[0];
let mDate = this.dateArr[1];
let hDate = this.dateArr[2];
let DDate = this.dateArr[3];
let MDate = this.dateArr[4];
let YDate = this.dateArr[5];
//
let sIdx = this.getIndex(sDate, nSecond);
let mIdx = this.getIndex(mDate, nMin);
let hIdx = this.getIndex(hDate, nHour);
let DIdx = this.getIndex(DDate, nDay);
let MIdx = this.getIndex(MDate, nMouth);
let YIdx = this.getIndex(YDate, nYear);
// ()
const resetSecond = function () {
sIdx = 0;
nSecond = sDate[sIdx]
}
const resetMin = function () {
mIdx = 0;
nMin = mDate[mIdx]
resetSecond();
}
const resetHour = function () {
hIdx = 0;
nHour = hDate[hIdx]
resetMin();
}
const resetDay = function () {
DIdx = 0;
nDay = DDate[DIdx]
resetHour();
}
const resetMouth = function () {
MIdx = 0;
nMouth = MDate[MIdx]
resetDay();
}
//
if (nYear !== YDate[YIdx]) {
resetMouth();
}
//
if (nMouth !== MDate[MIdx]) {
resetDay();
}
//
if (nDay !== DDate[DIdx]) {
resetHour();
}
//
if (nHour !== hDate[hIdx]) {
resetMin();
}
//
if (nMin !== mDate[mIdx]) {
resetSecond();
}
//
goYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) {
let YY = YDate[Yi];
//
if (nMouth > MDate[MDate.length - 1]) {
resetMouth();
continue;
}
//
goMouth: for (let Mi = MIdx; Mi < MDate.length; Mi++) {
// 便
let MM = MDate[Mi];
MM = MM < 10 ? '0' + MM : MM;
//
if (nDay > DDate[DDate.length - 1]) {
resetDay();
if (Mi == MDate.length - 1) {
resetMouth();
continue goYear;
}
continue;
}
//
goDay: for (let Di = DIdx; Di < DDate.length; Di++) {
// 便
let DD = DDate[Di];
let thisDD = DD < 10 ? '0' + DD : DD;
//
if (nHour > hDate[hDate.length - 1]) {
resetHour();
if (Di == DDate.length - 1) {
resetDay();
if (Mi == MDate.length - 1) {
resetMouth();
continue goYear;
}
continue goMouth;
}
continue;
}
//
if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true && this.dayRule !== 'workDay' && this.dayRule !== 'lastWeek' && this.dayRule !== 'lastDay') {
resetDay();
continue goMouth;
}
//
if (this.dayRule == 'lastDay') {
//
if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
while (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
DD--;
thisDD = DD < 10 ? '0' + DD : DD;
}
}
} else if (this.dayRule == 'workDay') {
//230
if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
while (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
DD--;
thisDD = DD < 10 ? '0' + DD : DD;
}
}
// X
let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
//
if (thisWeek == 0) {
//
DD++;
thisDD = DD < 10 ? '0' + DD : DD;
//
if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
DD -= 3;
}
} else if (thisWeek == 6) {
//61
if (this.dayRuleSup !== 1) {
DD--;
} else {
DD += 2;
}
}
} else if (this.dayRule == 'weekDay') {
//
//
let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
//dayRuleSup
if (Array.indexOf(this.dayRuleSup, thisWeek) < 0) {
//
if (Di == DDate.length - 1) {
resetDay();
if (Mi == MDate.length - 1) {
resetMouth();
continue goYear;
}
continue goMouth;
}
continue;
}
} else if (this.dayRule == 'assWeek') {
//
//1
let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
if (this.dayRuleSup[1] >= thisWeek) {
DD = (this.dayRuleSup[0] - 1) * 7 + this.dayRuleSup[1] - thisWeek + 1;
} else {
DD = this.dayRuleSup[0] * 7 + this.dayRuleSup[1] - thisWeek + 1;
}
} else if (this.dayRule == 'lastWeek') {
//
//230
if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
while (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
DD--;
thisDD = DD < 10 ? '0' + DD : DD;
}
}
//
let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
//
if (this.dayRuleSup < thisWeek) {
DD -= thisWeek - this.dayRuleSup;
} else if (this.dayRuleSup > thisWeek) {
DD -= 7 - (this.dayRuleSup - thisWeek)
}
}
// 1005
DD = DD < 10 ? '0' + DD : DD;
//
goHour: for (let hi = hIdx; hi < hDate.length; hi++) {
let hh = hDate[hi] < 10 ? '0' + hDate[hi] : hDate[hi]
//
if (nMin > mDate[mDate.length - 1]) {
resetMin();
if (hi == hDate.length - 1) {
resetHour();
if (Di == DDate.length - 1) {
resetDay();
if (Mi == MDate.length - 1) {
resetMouth();
continue goYear;
}
continue goMouth;
}
continue goDay;
}
continue;
}
// ""
goMin: for (let mi = mIdx; mi < mDate.length; mi++) {
let mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi];
//
if (nSecond > sDate[sDate.length - 1]) {
resetSecond();
if (mi == mDate.length - 1) {
resetMin();
if (hi == hDate.length - 1) {
resetHour();
if (Di == DDate.length - 1) {
resetDay();
if (Mi == MDate.length - 1) {
resetMouth();
continue goYear;
}
continue goMouth;
}
continue goDay;
}
continue goHour;
}
continue;
}
// ""
goSecond: for (let si = sIdx; si <= sDate.length - 1; si++) {
let ss = sDate[si] < 10 ? '0' + sDate[si] : sDate[si];
//
if (MM !== '00' && DD !== '00') {
resultArr.push(YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss)
nums++;
}
//退
if (nums == 5) break goYear;
//
if (si == sDate.length - 1) {
resetSecond();
if (mi == mDate.length - 1) {
resetMin();
if (hi == hDate.length - 1) {
resetHour();
if (Di == DDate.length - 1) {
resetDay();
if (Mi == MDate.length - 1) {
resetMouth();
continue goYear;
}
continue goMouth;
}
continue goDay;
}
continue goHour;
}
continue goMin;
}
} //goSecond
} //goMin
}//goHour
}//goDay
}//goMouth
}
// 100
if (resultArr.length == 0) {
this.resultList = [this.$t('schedule.cron.no_qualifying_results')];
} else {
this.resultList = resultArr;
// if (resultArr.length !== 5) {
// this.resultList.push('100' + resultArr.length + '')
// }
}
this.$emit("resultListChange", this.resultList);
// -
this.isShow = true;
},
//
getIndex(arr, value) {
if (value <= arr[0] || value > arr[arr.length - 1]) {
return 0;
} else {
for (let i = 0; i < arr.length - 1; i++) {
if (value > arr[i] && value <= arr[i + 1]) {
return i + 1;
}
}
}
},
// ""
getYearArr(rule, year) {
this.dateArr[5] = this.getOrderArr(year, year + 100);
if (rule !== undefined) {
if (rule.indexOf('-') >= 0) {
this.dateArr[5] = this.getCycleArr(rule, year + 100, false)
} else if (rule.indexOf('/') >= 0) {
this.dateArr[5] = this.getAverageArr(rule, year + 100)
} else if (rule !== '*') {
this.dateArr[5] = this.getAssignArr(rule)
}
}
},
// ""
getMouthArr(rule) {
this.dateArr[4] = this.getOrderArr(1, 12);
if (rule.indexOf('-') >= 0) {
this.dateArr[4] = this.getCycleArr(rule, 12, false)
} else if (rule.indexOf('/') >= 0) {
this.dateArr[4] = this.getAverageArr(rule, 12)
} else if (rule !== '*') {
this.dateArr[4] = this.getAssignArr(rule)
}
},
// ""-
getWeekArr(rule) {
//
if (this.dayRule == '' && this.dayRuleSup == '') {
if (rule.indexOf('-') >= 0) {
this.dayRule = 'weekDay';
this.dayRuleSup = this.getCycleArr(rule, 7, false)
} else if (rule.indexOf('#') >= 0) {
this.dayRule = 'assWeek';
let matchRule = rule.match(/[0-9]{1}/g);
this.dayRuleSup = [Number(matchRule[0]), Number(matchRule[1])];
this.dateArr[3] = [1];
if (this.dayRuleSup[1] == 7) {
this.dayRuleSup[1] = 0;
}
} else if (rule.indexOf('L') >= 0) {
this.dayRule = 'lastWeek';
this.dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0]);
this.dateArr[3] = [31];
if (this.dayRuleSup == 7) {
this.dayRuleSup = 0;
}
} else if (rule !== '*' && rule !== '?') {
this.dayRule = 'weekDay';
this.dayRuleSup = this.getAssignArr(rule)
}
//weekDay70week0
if (this.dayRule == 'weekDay') {
for (let i = 0; i < this.dayRuleSup.length; i++) {
if (this.dayRuleSup[i] == 7) {
this.dayRuleSup[i] = 0;
}
}
}
}
},
// ""-
getDayArr(rule) {
this.dateArr[3] = this.getOrderArr(1, 31);
this.dayRule = '';
this.dayRuleSup = '';
if (rule.indexOf('-') >= 0) {
this.dateArr[3] = this.getCycleArr(rule, 31, false)
this.dayRuleSup = 'null';
} else if (rule.indexOf('/') >= 0) {
this.dateArr[3] = this.getAverageArr(rule, 31)
this.dayRuleSup = 'null';
} else if (rule.indexOf('W') >= 0) {
this.dayRule = 'workDay';
this.dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0]);
this.dateArr[3] = [this.dayRuleSup];
} else if (rule.indexOf('L') >= 0) {
this.dayRule = 'lastDay';
this.dayRuleSup = 'null';
this.dateArr[3] = [31];
} else if (rule !== '*' && rule !== '?') {
this.dateArr[3] = this.getAssignArr(rule)
this.dayRuleSup = 'null';
} else if (rule == '*') {
this.dayRuleSup = 'null';
}
},
// ""
getHourArr(rule) {
this.dateArr[2] = this.getOrderArr(0, 23);
if (rule.indexOf('-') >= 0) {
this.dateArr[2] = this.getCycleArr(rule, 24, true)
} else if (rule.indexOf('/') >= 0) {
this.dateArr[2] = this.getAverageArr(rule, 23)
} else if (rule !== '*') {
this.dateArr[2] = this.getAssignArr(rule)
}
},
// ""
getMinArr(rule) {
this.dateArr[1] = this.getOrderArr(0, 59);
if (rule.indexOf('-') >= 0) {
this.dateArr[1] = this.getCycleArr(rule, 60, true)
} else if (rule.indexOf('/') >= 0) {
this.dateArr[1] = this.getAverageArr(rule, 59)
} else if (rule !== '*') {
this.dateArr[1] = this.getAssignArr(rule)
}
},
// ""
getSecondArr(rule) {
this.dateArr[0] = this.getOrderArr(0, 59);
if (rule.indexOf('-') >= 0) {
this.dateArr[0] = this.getCycleArr(rule, 60, true)
} else if (rule.indexOf('/') >= 0) {
this.dateArr[0] = this.getAverageArr(rule, 59)
} else if (rule !== '*') {
this.dateArr[0] = this.getAssignArr(rule)
}
},
// min-max
getOrderArr(min, max) {
let arr = [];
for (let i = min; i <= max; i++) {
arr.push(i);
}
return arr;
},
//
getAssignArr(rule) {
let arr = [];
let assiginArr = rule.split(',');
for (let i = 0; i < assiginArr.length; i++) {
arr[i] = Number(assiginArr[i])
}
arr.sort(this.compare)
return arr;
},
//
getAverageArr(rule, limit) {
let arr = [];
let agArr = rule.split('/');
let min = Number(agArr[0]);
let step = Number(agArr[1]);
while (min <= limit) {
arr.push(min);
min += step;
}
return arr;
},
//
getCycleArr(rule, limit, status) {
//status--01
let arr = [];
let cycleArr = rule.split('-');
let min = Number(cycleArr[0]);
let max = Number(cycleArr[1]);
if (min > max) {
max += limit;
}
for (let i = min; i <= max; i++) {
let add = 0;
if (status == false && i % limit == 0) {
add = limit;
}
arr.push(Math.round(i % limit + add))
}
arr.sort(this.compare)
return arr;
},
//Array.sort
compare(value1, value2) {
if (value2 - value1 > 0) {
return -1;
} else {
return 1;
}
},
// 2017-9-19 18:04:33
formatDate(value, type) {
//
let time = typeof value == 'number' ? new Date(value) : value;
let Y = time.getFullYear();
let M = time.getMonth() + 1;
let D = time.getDate();
let h = time.getHours();
let m = time.getMinutes();
let s = time.getSeconds();
let week = time.getDay();
// type
if (type == undefined) {
return Y + '-' + (M < 10 ? '0' + M : M) + '-' + (D < 10 ? '0' + D : D) + ' ' + (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
} else if (type == 'week') {
return week;
}
},
//
checkDate(value) {
let time = new Date(value);
let format = this.formatDate(time)
return value == format ? true : false;
}
}
}
</script>
<style>
.title {
margin: 0;
}
.popup-result-scroll {
font-size: 12px;
line-height: 24px;
height: 10em;
overflow-y: auto;
}
.popup-result {
box-sizing: border-box;
line-height: 24px;
margin: 10px auto;
padding: 15px 10px 10px;
border: 1px solid #ccc;
position: relative;
}
</style>

View File

@ -1,133 +0,0 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio v-model='radioValue' :label="1">
{{$t('schedule.cron.seconds')}}{{$t('schedule.cron.allowed_wildcards')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="2">
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
<el-input-number v-model='cycle01' :min="0" :max="59" /> -
<el-input-number v-model='cycle02' :min="0" :max="59" /> {{$t('schedule.cron.seconds')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="3">
{{$t('schedule.cron.from')}}
<el-input-number v-model='average01' :min="0" :max="59" /> {{$t('schedule.cron.seconds')}}{{$t('schedule.cron.start')}}{{$t('schedule.cron.every')}}
<el-input-number v-model='average02' :min="0" :max="60" /> {{$t('schedule.cron.seconds')}}{{$t('schedule.cron.execute_once')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="4">
{{$t('schedule.cron.specify')}}
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
<el-option v-for="item in 60" :key="item" :value="item-1">{{item-1}}</el-option>
</el-select>
</el-radio>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
return {
radioValue: 1,
cycle01: 1,
cycle02: 2,
average01: 0,
average02: 1,
checkboxList: [],
checkNum: this.$options.propsData.check
}
},
name: 'CrontabSecond',
props: ['check', 'radioParent'],
methods: {
//
radioChange() {
switch (this.radioValue) {
case 1:
this.$emit('update', 'second', '*', 'second');
this.$emit('update', 'min', '*', 'second');
break;
case 2:
this.$emit('update', 'second', this.cycle01 + '-' + this.cycle02);
break;
case 3:
this.$emit('update', 'second', this.average01 + '/' + this.average02);
break;
case 4:
this.$emit('update', 'second', this.checkboxString);
break;
}
},
//
cycleChange() {
if (this.radioValue == '2') {
this.$emit('update', 'second', this.cycleTotal);
}
},
//
averageChange() {
if (this.radioValue == '3') {
this.$emit('update', 'second', this.averageTotal);
}
},
// checkbox
checkboxChange() {
if (this.radioValue == '4') {
this.$emit('update', 'second', this.checkboxString);
}
},
othChange() {
//
let ins = this.cron.second
// (' second', ins);
if (ins === '*') {
this.radioValue = 1;
} else if (ins.indexOf('-') > -1) {
this.radioValue = 2
} else if (ins.indexOf('/') > -1) {
this.radioValue = 3
} else {
this.radioValue = 4
this.checkboxList = ins.split(',')
}
}
},
watch: {
"radioValue": "radioChange",
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'checkboxString': 'checkboxChange',
radioParent() {
this.radioValue = this.radioParent
}
},
computed: {
//
cycleTotal: function () {
this.checkNum(this.cycle01, 0, 59);
this.checkNum(this.cycle02, 0, 59);
return this.cycle01 + '-' + this.cycle02;
},
//
averageTotal: function () {
this.checkNum(this.average01, 0, 59)
this.checkNum(this.average02, 1, 59)
return this.average01 + '/' + this.average02;
},
// checkbox
checkboxString: function () {
let str = this.checkboxList.join();
return str == '' ? '*' : str;
}
}
}
</script>

View File

@ -1,174 +0,0 @@
<template>
<el-form size='small'>
<el-form-item>
<el-radio v-model='radioValue' :label="1">
{{$t('schedule.cron.weeks')}}{{$t('schedule.cron.weeks_allowed_wildcards')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="2">
{{$t('schedule.cron.not_specify')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="3">
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}{{$t('schedule.cron.week')}}
<el-input-number v-model='cycle01' :min="1" :max="7" /> -
<el-input-number v-model='cycle02' :min="1" :max="7" />
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="4">
{{$t('schedule.cron.num')}}
<el-input-number v-model='average01' :min="1" :max="4" /> {{$t('schedule.cron.week_of_weeks')}}
<el-input-number v-model='average02' :min="1" :max="7" />
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="5">
{{$t('schedule.cron.last_week_of_the_month')}}
<el-input-number v-model='weekday' :min="1" :max="7" />
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model='radioValue' :label="6">
{{$t('schedule.cron.specify')}}
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
<el-option v-for="(item,index) of weekList" :key="index" :value="index+1">{{item}}</el-option>
</el-select>
</el-radio>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
return {
radioValue: 2,
weekday: 1,
cycle01: 1,
cycle02: 2,
average01: 1,
average02: 1,
checkboxList: [],
weekList: [this.$t('commons.weeks_1'),
this.$t('commons.weeks_2'),
this.$t('commons.weeks_3'),
this.$t('commons.weeks_4'),
this.$t('commons.weeks_5'),
this.$t('commons.weeks_6'),
this.$t('commons.weeks_0'),
],
checkNum: this.$options.propsData.check
}
},
name: 'CrontabWeek',
props: ['check', 'cron'],
methods: {
//
radioChange() {
if (this.radioValue === 1) {
this.$emit('update', 'week', '*');
this.$emit('update', 'year', '*');
} else {
if (this.cron.mouth === '*') {
this.$emit('update', 'mouth', '1', 'week');
}
if (this.cron.day === '*') {
this.$emit('update', 'day', '1', 'week');
}
if (this.cron.hour === '*') {
this.$emit('update', 'hour', '0', 'week');
}
if (this.cron.min === '*') {
this.$emit('update', 'min', '0', 'week');
}
if (this.cron.second === '*') {
this.$emit('update', 'second', '0', 'week');
}
}
switch (this.radioValue) {
case 2:
this.$emit('update', 'week', '?');
break;
case 3:
this.$emit('update', 'week', this.cycle01 + '-' + this.cycle02);
break;
case 4:
this.$emit('update', 'week', this.average01 + '#' + this.average02);
break;
case 5:
this.$emit('update', 'week', this.weekday + 'L');
break;
case 6:
this.$emit('update', 'week', this.checkboxString);
break;
}
},
// radio
//
cycleChange() {
if (this.radioValue == '3') {
this.$emit('update', 'week', this.cycleTotal);
}
},
//
averageChange() {
if (this.radioValue == '4') {
this.$emit('update', 'week', this.averageTotal);
}
},
//
weekdayChange() {
if (this.radioValue == '5') {
this.$emit('update', 'week', this.weekday + 'L');
}
},
// checkbox
checkboxChange() {
if (this.radioValue == '6') {
this.$emit('update', 'week', this.checkboxString);
}
},
},
watch: {
"radioValue": "radioChange",
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'weekdayCheck': 'weekdayChange',
'checkboxString': 'checkboxChange',
},
computed: {
//
cycleTotal: function () {
this.checkNum(this.cycle01, 1, 7)
this.checkNum(this.cycle02, 1, 7)
return this.cycle01 + '-' + this.cycle02;
},
//
averageTotal: function () {
this.checkNum(this.average01, 1, 4)
this.checkNum(this.average02, 1, 7)
return this.average01 + '#' + this.average02;
},
//
weekdayCheck: function () {
this.checkNum(this.weekday, 1, 7)
return this.weekday;
},
// checkbox
checkboxString: function () {
let str = this.checkboxList.join();
return str == '' ? '?' : str;
}
}
}
</script>

View File

@ -1,144 +0,0 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio :label="1" v-model='radioValue'>
{{$t('schedule.cron.not_fill')}}{{$t('schedule.cron.allowed_wildcards')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio :label="2" v-model='radioValue'>
{{$t('schedule.cron.every')}}{{$t('schedule.cron.years')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio :label="3" v-model='radioValue'>
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
<el-input-number v-model='cycle01' :min='fullYear' /> -
<el-input-number v-model='cycle02' :min='fullYear' />
</el-radio>
</el-form-item>
<el-form-item>
<el-radio :label="4" v-model='radioValue'>
{{$t('schedule.cron.from')}}
<el-input-number v-model='average01' :min='fullYear' /> {{$t('schedule.cron.years')}}{{$t('schedule.cron.start')}}{{$t('schedule.cron.every')}}
<el-input-number v-model='average02' :min='fullYear' /> {{$t('schedule.cron.years')}}{{$t('schedule.cron.execute_once')}}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio :label="5" v-model='radioValue'>
指定
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple>
<el-option v-for="item in 9" :key="item" :value="item - 1 + fullYear" :label="item -1 + fullYear" />
</el-select>
</el-radio>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
return {
fullYear: 0,
radioValue: 1,
cycle01: 0,
cycle02: 0,
average01: 0,
average02: 1,
checkboxList: [],
checkNum: this.$options.propsData.check
}
},
name: 'CrontabYear',
props: ['check', 'mouth', 'cron'],
methods: {
//
radioChange() {
if (this.cron.mouth === '*') {
this.$emit('update', 'mouth', '1', 'year');
}
if (this.cron.day === '*') {
this.$emit('update', 'day', '1', 'year');
}
if (this.cron.hour === '*') {
this.$emit('update', 'hour', '0', 'year');
}
if (this.cron.min === '*') {
this.$emit('update', 'min', '0', 'year');
}
if (this.cron.second === '*') {
this.$emit('update', 'second', '0', 'year');
}
switch (this.radioValue) {
case 1:
this.$emit('update', 'year', '');
break;
case 2:
this.$emit('update', 'year', '*');
break;
case 3:
this.$emit('update', 'year', this.cycle01 + '-' + this.cycle02);
break;
case 4:
this.$emit('update', 'year', this.average01 + '/' + this.average02);
break;
case 5:
this.$emit('update', 'year', this.checkboxString);
break;
}
},
//
cycleChange() {
if (this.radioValue == '3') {
this.$emit('update', 'year', this.cycleTotal);
}
},
//
averageChange() {
if (this.radioValue == '4') {
this.$emit('update', 'year', this.averageTotal);
}
},
// checkbox
checkboxChange() {
if (this.radioValue == '5') {
this.$emit('update', 'year', this.checkboxString);
}
}
},
watch: {
"radioValue": "radioChange",
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'checkboxString': 'checkboxChange'
},
computed: {
//
cycleTotal: function () {
this.checkNum(this.cycle01, this.fullYear, this.fullYear + 100)
this.checkNum(this.cycle02, this.fullYear + 1, this.fullYear + 101)
return this.cycle01 + '-' + this.cycle02;
},
//
averageTotal: function () {
this.checkNum(this.average01, this.fullYear, this.fullYear + 100)
this.checkNum(this.average02, 1, 10)
return this.average01 + '/' + this.average02;
},
// checkbox
checkboxString: function () {
let str = this.checkboxList.join();
return str;
}
},
mounted: function () {
//
this.fullYear = Number(new Date().getFullYear());
}
}
</script>

View File

@ -1,3 +0,0 @@
@import './main.css';
/* @import './menu-header.css';
@import '../theme/index.css'; */

View File

@ -1,204 +0,0 @@
.container {
padding: 15px;
width: 100%;
height: 100%;
box-sizing: border-box;
}
.main-content {
margin: 0 auto;
width: 100%;
}
/* body {
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
font-size: 14px;
margin: 0;
} */
/* 解决 document.body.clientHeight 为0 */
html,body {
height:100%
}
.main-content span.title {
font-size: 16px;
font-weight: 500;
margin-top: 0;
text-overflow: ellipsis;
overflow: hidden;
word-wrap: break-word;
white-space: nowrap;
}
/* 解决高度塌陷和边距重叠 */
.clearfix:before, .clearfix:after {
content: "";
display: table;
clear: both;
}
/* 解决富文本框中link显示问题 */
.ck-rounded-corners .ck.ck-balloon-panel, .ck.ck-balloon-panel.ck-rounded-corners {
z-index: 10055 !important;
}
.table-card > .el-card__body {
padding-top: 0;
}
/* Safari 表格不错位 */
.el-table__body {
width: 100%;
table-layout: fixed !important;
}
/* <-- 表格拖拽表头调整宽度,在 t-bable 上添加 border 属性,并添加 adjust-table 类名 */
.adjust-table td {
border-right: 0;
}
.adjust-table th {
border-right-color: white;
}
.adjust-table {
border-color: white;
}
.adjust-table:after {
background-color: white;
}
.adjust-table th:not([class*="el-table-column--selection"]):hover:after {
content: '';
position: absolute;
top: 25%;
right: 0;
height: 50%;
width: 2px;
background-color: #EBEEF5;
}
.adjust-table tr:hover td {
border-right: 0;
}
/* 表格拖拽表头调整宽度 --> */
/* <-- 表格 input 编辑效果 */
.table-edit-input .el-textarea__inner {
border-style: hidden;
}
.table-edit-input.is-disabled .el-textarea__inner {
background-color: white;
color: #606266;
}
.el-table .current-row .table-edit-input {
border: 1px solid #DCDFE6;
border-radius: 5px;
}
.table-edit-input .el-textarea__inner:focus {
border: 1px solid #409EFF;
}
/* 表格 input 编辑效果 --> */
.ms-border {
padding: 10px;
border: #DCDFE6 solid 1px;
margin: 5px 0;
border-radius: 5px;
}
/* 修复带长度限制的文本框,内容太长造成的无法查看内容的问题 */
.el-input__inner[maxlength] {
padding-right: 60px;
}
/* 滚动条样式 */
::-webkit-scrollbar{
width: 10px;
height: 10px;
position: fixed;
}
::-webkit-scrollbar-thumb{
border-radius: 1em;
background-color: rgba(50,50,50,.3);
position: fixed;
}
::-webkit-scrollbar-track{
border-radius: 1em;
background-color: transparent;
position: fixed;
}
/* <-- 表格全选样式 */
.ms-select-all th:first-child.el-table-column--selection {
border: 1px solid #DCDFE6;
border-radius:5px;
padding: 0px;
margin-top: 10px;
display: inline-block;
/*margin-top: 25px;*/
width: 50px;
}
.ms-select-all th:nth-child(2) {
overflow: visible;
}
.ms-select-all th:first-child.el-table-column--selection>.cell {
padding: 5px;
width: 35px;
}
.ms-select-all th:nth-child(2)>.cell {
overflow: visible;
}
.ms-select-all th:nth-child(2) .el-icon-arrow-down {
position: absolute;
display: inline-block;
top: -7px;
left: -38px;
width: 30px;
}
/* 表格全选样式 --> */
/* <-- 表格全选样式 (列固定表格样式) */
.ms-select-all-fixed th:first-child.el-table-column--selection {
border: 1px solid #DCDFE6;
border-radius:5px;
padding: 0px;
margin-top: 10px;
display: inline-block;
/*margin-top: 25px;*/
width: 50px;
}
.ms-select-all-fixed th:nth-child(2) {
overflow: visible;
}
.ms-select-all-fixed th:first-child.el-table-column--selection>.cell {
padding: 5px;
width: 35px;
}
.ms-select-all-fixed th:nth-child(2)>.cell {
overflow: visible;
}
.ms-select-all-fixed th:nth-child(2) .el-icon-arrow-down {
position: absolute;
display: inline-block;
top: -9px;
left: -30px;
width: 30px;
}
/* 表格全选样式 --> */

View File

@ -1,22 +0,0 @@
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(./material.woff2) format('woff2');
}
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-feature-settings: 'liga';
-webkit-font-smoothing: antialiased;
}

View File

@ -1,38 +0,0 @@
.header-menu.el-menu--horizontal > li {
height: 39px;
line-height: 40px;
color: dimgray;
}
.header-menu.el-menu--horizontal > li.el-submenu > * {
height: 39px;
line-height: 40px;
color: dimgray;
}
.header-user-menu.el-menu--horizontal > li.el-submenu > * {
height: 40px;
line-height: 40px;
color: inherit;
}
.header-top-menus.el-menu--horizontal > li {
height: 40px;
line-height: 40px;
color: inherit;
}
.header-top-menus.el-menu--horizontal > li.el-submenu > * {
height: 39px;
line-height: 40px;
color: inherit;
}
.header-top-menus.el-menu--horizontal > li.is-active {
background: #595591 !important;
}
.el-menu.el-menu--horizontal {
border-bottom: none;
}

View File

@ -1,61 +0,0 @@
const ReportStyle = `.body{
font-size: 14px;
color: #7B0274;
}
.container {
height: 100vh;
background: #F5F5F5;
}
.preview {
position: relative;
}
/* <-- 表格拖拽表头调整宽度,在 t-bable 上添加 border 属性,并添加 adjust-table 类名*/
.adjust-table td {
border-right-color: white;
}
.adjust-table th {
border-right-color: white;
}
.adjust-table {
border-color: white;
}
.adjust-table:after {
background-color: white;
}
.adjust-table th:hover:after {
content: '';
position: absolute;
top: 25%;
right: 0;
height: 50%;
width: 3px;
background-color: #EBEEF5;
}
span {
margin-right: 5px;
display: inline-block;
}
.el-col span:first-child {
font-weight: bold;
width: 100px;
}
.el-row {
height: 60px;
}
.select-time span {
display: inline-block;
}
.el-date-editor {
width: 150px;
}
`
export default ReportStyle;

View File

@ -1,108 +0,0 @@
<template>
<el-dialog
:title="$t('commons.about_us')"
:visible.sync="dialogVisible" class="about-us">
<el-row>
<el-col>
<el-link :underline="false" :href="websiteUrl" target="_blank">
<img class="logo" src="../../../../assets/favicon-彩色.png"/>
</el-link>
<el-link class="url" :href="websiteUrl" target="_blank">
<span>{{websiteUrl}}</span>
</el-link>
</el-col>
</el-row>
<el-row>
<el-col>
<div class="github">
<el-link :underline="false" :href="githubUrl" target="_blank">
<font-awesome-icon class="github-icon" :icon="['fab', 'github-square']"/>
</el-link>
</div>
<el-link class="url" :href="githubUrl" target="_blank">
<span>{{githubUrl}}</span>
</el-link>
</el-col>
</el-row>
<el-row>
<el-col class="version">
<span>版本:</span> &nbsp;
<span>{{version}}</span>
</el-col>
</el-row>
</el-dialog>
</template>
<script>
export default {
name: "AboutUs",
data() {
return {
dialogVisible: false,
githubUrl: 'https://github.com/metersphere/metersphere',
websiteUrl: 'https://metersphere.io',
version: '1.0.1'
}
},
created() {
this.getVersion();
},
methods: {
open() {
this.dialogVisible = true;
},
getVersion() {
// this.$get('/system/version', response => {
// this.version = response.data;
// });
this.version = "1.0"
}
}
}
</script>
<style scoped>
.logo {
height: 30px;
line-height: 30px;
vertical-align: middle
}
.version {
height: 30px;
line-height: 30px;
margin-left: 5px;
}
.github-icon {
font-size: 20px;
margin-left: 5px;
}
.github {
height: 30px;
width: 30px;
line-height: 30px;
display: inline-block;
}
.el-row {
margin-bottom: 3%;
}
.url {
margin-left: 5px;
}
.about-us >>> .el-dialog {
width: 500px;
}
</style>

View File

@ -1,22 +0,0 @@
<template>
<el-menu-item :index="this.index">
<el-button type="text" class="create-button">{{this.title}}</el-button>
</el-menu-item>
</template>
<script>
export default {
name: "MsCreateButton",
props: {
index: String,
title: String,
click: Function
}
}
</script>
<style scoped>
.create-button {
padding-left: 18px;
}
</style>

View File

@ -1,32 +0,0 @@
<template>
<router-link class="create-test" :to="this.to" v-permission="this.permission">
<el-button type="primary" size="small">{{this.title}}</el-button>
</router-link>
</template>
<script>
export default {
name: "MsCreateTest",
props: {
to: [String, Object],
title: {
type: String,
default: function () {
return this.$t('load_test.create');
}
},
permission: {
type: Array,
default: function () {
return ['test_user', 'test_manager'];
}
}
}
}
</script>
<style scoped>
.create-test {
line-height: 38px;
}
</style>

View File

@ -1,232 +0,0 @@
<template>
<el-menu
:unique-opened="true"
mode="horizontal"
router
class="header-user-menu align-right"
background-color="#2c2a48"
active-text-color="#fff"
default-active="1"
text-color="#fff"
>
<el-menu-item v-show="false" index="1">Placeholder</el-menu-item>
<el-submenu
v-roles="['org_admin', 'test_manager', 'test_user', 'test_viewer']"
index="1"
popper-class="org-ws-submenu"
>
<template v-slot:title>{{ $t('commons.organization') }}: {{ currentOrganizationName }}</template>
<el-input
v-model="searchOrg"
:placeholder="$t('project.search_by_name')"
prefix-icon="el-icon-search"
clearable
class="search-input"
size="small"
/>
<div class="org-ws-menu">
<el-menu-item v-for="(item,index) in organizationList" :key="index" @click="changeOrg(item)">
{{ item.name }}
<i
v-if="item.id === currentUserInfo.lastOrganizationId"
class="el-icon-check"
/>
</el-menu-item>
</div>
</el-submenu>
<el-submenu v-roles="['test_manager', 'test_user', 'test_viewer']" index="2" popper-class="submenu">
<template v-slot:title>{{ $t('commons.workspace') }}: {{ currentWorkspaceName }}</template>
<el-input
v-model="searchWs"
:placeholder="$t('project.search_by_name')"
prefix-icon="el-icon-search"
clearable
class="search-input"
size="small"
/>
<div class="org-ws-menu">
<el-menu-item v-for="(item,index) in workspaceList" :key="index" @click="changeWs(item)">
{{ item.name }}
<i v-if="item.id === currentUserInfo.lastWorkspaceId" class="el-icon-check" />
</el-menu-item>
</div>
</el-submenu>
</el-menu>
</template>
<script>
import {
PROJECT_ID,
ROLE_ORG_ADMIN,
ROLE_TEST_MANAGER,
ROLE_TEST_USER,
ROLE_TEST_VIEWER,
WORKSPACE_ID
} from '@/metersphere/common/js/constants'
import { getCurrentUser, hasRoles, saveLocalStorage } from '@/metersphere/common/js/utils'
export default {
name: 'MsHeaderOrgWs',
data() {
return {
organizationList: [
{ name: this.$t('organization.none') }
],
workspaceList: [
{ name: this.$t('workspace.none') }
],
currentUserInfo: {},
currentUserId: getCurrentUser().id,
workspaceIds: [],
currentOrganizationName: '',
currentWorkspaceName: '',
searchOrg: '',
searchWs: '',
orgListCopy: [{ name: this.$t('organization.none') }],
wsListCopy: [{ name: this.$t('workspace.none') }]
}
},
computed: {
currentUser: () => {
return getCurrentUser()
}
},
watch: {
searchOrg(val) {
this.query('org', val)
},
searchWs(val) {
this.query('ws', val)
}
},
created() {
this.initMenuData()
this.getCurrentUserInfo()
},
methods: {
initMenuData() {
if (hasRoles(ROLE_ORG_ADMIN, ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
this.$get('/organization/list/userorg/' + encodeURIComponent(this.currentUserId), response => {
const data = response.data
this.organizationList = data
this.orgListCopy = data
const org = data.filter(r => r.id === this.currentUser.lastOrganizationId)
if (org.length > 0) {
this.currentOrganizationName = org[0].name
}
})
}
if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
if (!this.currentUser.lastOrganizationId) {
return false
}
this.$get('/workspace/list/orgworkspace/', response => {
const data = response.data
if (data.length === 0) {
this.workspaceList = [{ name: this.$t('workspace.none') }]
} else {
this.workspaceList = data
this.wsListCopy = data
const workspace = data.filter(r => r.id === this.currentUser.lastWorkspaceId)
if (workspace.length > 0) {
this.currentWorkspaceName = workspace[0].name
localStorage.setItem(WORKSPACE_ID, workspace[0].id)
}
}
})
}
},
getCurrentUserInfo() {
this.$get('/user/info/' + encodeURIComponent(this.currentUserId), response => {
this.currentUserInfo = response.data
})
},
changeOrg(data) {
const orgId = data.id
if (!orgId) {
return false
}
this.$post('/user/switch/source/org/' + orgId, {}, response => {
saveLocalStorage(response)
if (response.data.workspaceId) {
localStorage.setItem('workspace_id', response.data.workspaceId)
}
localStorage.removeItem(PROJECT_ID)
this.$router.push('/').then(() => {
window.location.reload()
}).catch(err => err)
})
},
changeWs(data) {
const workspaceId = data.id
if (!workspaceId) {
return false
}
this.$post('/user/switch/source/ws/' + workspaceId, {}, response => {
saveLocalStorage(response)
localStorage.setItem('workspace_id', workspaceId)
localStorage.removeItem(PROJECT_ID)
this.$router.push('/').then(() => {
window.location.reload()
}).catch(err => err)
})
},
query(sign, queryString) {
if (sign === 'org') {
this.organizationList = queryString ? this.orgListCopy.filter(this.createFilter(queryString)) : this.orgListCopy
}
if (sign === 'ws') {
this.workspaceList = queryString ? this.wsListCopy.filter(this.createFilter(queryString)) : this.wsListCopy
}
},
createFilter(queryString) {
return item => {
return (item.name.toLowerCase().indexOf(queryString.toLowerCase()) !== -1)
}
}
}
}
</script>
<style scoped>
.el-icon-check {
color: #44b349;
margin-left: 10px;
}
::-webkit-scrollbar {
width: 10px;
height: 10px;
position: fixed;
}
::-webkit-scrollbar-thumb {
border-radius: 1em;
background-color: #595591;
position: fixed;
}
::-webkit-scrollbar-track {
border-radius: 1em;
background-color: transparent;
position: fixed;
}
.org-ws-menu {
height: 180px;
overflow: auto;
}
.search-input {
padding: 0;
margin-top: -4px;
background-color: #595591;
}
.search-input >>> .el-input__inner {
border-radius: 0;
background-color: #2d2a49;
color: #d2ced8;
border-color: #b4aebe;
}
</style>

View File

@ -1,71 +0,0 @@
<template>
<el-menu mode="horizontal" menu-trigger="click"
background-color="#2c2a48"
class="header-top-menus"
text-color="#F2F2F2"
active-text-color="#fff"
:default-active="activeIndex"
@select="handleSelect"
router>
<!-- <el-menu-item index="/track" v-permission="['test_manager','test_user','test_viewer']">
{{ $t('test_track.test_track') }}
</el-menu-item> -->
<el-menu-item index="/chart" @click="active()" >
{{ $t('commons.api') }}
</el-menu-item>
<el-menu-item index="/dataset" onselectstart="return false"
>
{{ $t('commons.dataset') }}
</el-menu-item>
<el-menu-item index="/setting" onselectstart="return false">
{{ $t('commons.system_setting') }}
</el-menu-item>
</el-menu>
</template>
<script>
export default {
name: "MsTopMenus",
data() {
return {
activeIndex: '/'
}
},
watch: {
'$route'(to) {
if (to.matched.length > 0) {
this.activeIndex = to.matched[0].path;
}
this.handleSelect(this.activeIndex);
}
},
mounted() {
if (this.$route.matched.length > 0) {
this.activeIndex = this.$route.matched[0].path;
}
},
methods: {
handleSelect(index) {
this.activeIndex = index
},
active() {
if (this.activeIndex === '/api') {
if (this.$store.state.switch.value == 'new') {
window.location.href = "/#/api/home";
} else if (this.$store.state.switch.value == 'old') {
window.location.href = "/#/api/home_obsolete";
}
}
}
}
}
</script>
<style scoped>
.el-menu >>> .el-menu-item {
box-sizing: border-box;
height: 40px;
}
</style>

View File

@ -1,111 +0,0 @@
<template>
<el-dropdown size="medium" @command="handleCommand" class="align-right">
<span class="dropdown-link">
{{ currentUser.username }}<i class="el-icon-caret-bottom el-icon--right"/>
</span>
<template v-slot:dropdown>
<el-dropdown-menu>
<el-dropdown-item command="personal">{{ $t('commons.personal_information') }}</el-dropdown-item>
<el-dropdown-item command="about">{{ $t('commons.about_us') }} <i class="el-icon-info"/></el-dropdown-item>
<el-dropdown-item command="help">{{ $t('commons.help_documentation') }}</el-dropdown-item>
<el-dropdown-item command="ApiHelp">{{ $t('commons.api_help_documentation') }}</el-dropdown-item>
<el-dropdown-item command="old" v-show=isReadOnly @click.native="changeBar('old')">
{{ $t('commons.cut_back_old_version') }}
</el-dropdown-item>
<el-dropdown-item command="new" v-show=!isReadOnly @click.native="changeBar('new')">
{{ $t('commons.cut_back_new_version') }}
</el-dropdown-item>
<el-dropdown-item command="logout">{{ $t('commons.exit_system') }}</el-dropdown-item>
</el-dropdown-menu>
</template>
<about-us ref="aboutUs"/>
</el-dropdown>
</template>
<script>
import {getUserInfo} from "@/common/js/utils";
import AboutUs from "./AboutUs";
import axios from "axios";
import {removeToken} from '@/utils/auth'
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const auth = requireComponent.keys().length > 0 ? requireComponent("./auth/Auth.vue") : {};
export default {
name: "MsUser",
components: {AboutUs},
data() {
return {
isReadOnly: this.$store.state.isReadOnly.flag
}
},
computed: {
currentUser: () => {
return getUserInfo();
}
},
methods: {
logout: function () {
axios.get("/signout").then(response => {
if (response.data.success) {
localStorage.clear();
removeToken();
window.location.href = "/login";
}
}).catch(error => {
localStorage.clear();
window.location.href = "/login";
});
},
handleCommand(command) {
switch (command) {
case "personal":
// TODO
this.$router.push('/setting/personsetting').catch(error => error);
break;
case "logout":
this.logout();
break;
case "about":
this.$refs.aboutUs.open();
break;
case "help":
window.location.href = "https://metersphere.io/docs/index.html";
break;
case "ApiHelp":
window.open('/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config', "_blank");
break;
default:
break;
}
},
changeBar(item) {
this.isReadOnly = !this.isReadOnly
this.$store.commit('setFlag', this.isReadOnly);
this.$store.commit('setValue', item);
if (item == "old") {
window.location.href = "/#/api/home_obsolete";
} else {
window.location.href = "/#/api/home";
}
}
}
}
</script>
<style scoped>
.dropdown-link {
cursor: pointer;
font-size: 12px;
color: rgb(245, 245, 245);
line-height: 40px;
}
.align-right {
float: right;
}
</style>

View File

@ -1,102 +0,0 @@
<template>
<el-menu
:unique-opened="true"
class="header-user-menu align-right"
mode="horizontal"
background-color="#2c2a48"
text-color="#fff"
active-text-color="#fff"
>
<el-submenu index="1">
<template slot="title">
<font-awesome-icon class="icon global" :icon="['fas', 'globe']" />
<span>{{ language }}</span>
</template>
<el-menu-item v-for="(value, key) in languageMap" :key="key" @click="changeLanguage(key)">
{{ value }} <i v-if="language === value" class="el-icon-check" />
</el-menu-item>
</el-submenu>
</el-menu>
</template>
<script>
import { DEFAULT_LANGUAGE, EN_US, TokenKey, ZH_CN, ZH_TW } from '@/metersphere/common/js/constants'
import { getCurrentUser } from '@/metersphere/common/js/utils'
export default {
name: 'MsLanguageSwitch',
data() {
return {
currentUserInfo: {},
language: '',
languageMap: {
[ZH_CN]: '简体中文',
[EN_US]: 'English',
[ZH_TW]: '繁體中文'
}
}
},
created() {
let lang = this.currentUser().language
this.currentUserInfo = this.currentUser()
if (!lang) {
lang = localStorage.getItem(DEFAULT_LANGUAGE)
}
this.checkLanguage(lang)
},
methods: {
checkLanguage(lang) {
if (!lang) return
this.$setLang(lang)
switch (lang) {
case ZH_CN:
this.language = this.languageMap[ZH_CN]
break
case ZH_TW:
this.language = this.languageMap[ZH_TW]
break
case EN_US:
this.language = this.languageMap[EN_US]
break
default:
this.language = this.languageMap[ZH_CN]
break
}
},
currentUser: () => {
return getCurrentUser()
},
changeLanguage(language) {
const user = {
id: this.currentUser().id,
language: language
}
this.checkLanguage(language)
this.result = this.$post('/user/update/current', user, response => {
localStorage.setItem(TokenKey, JSON.stringify(response.data))
window.location.reload()
})
}
}
}
</script>
<style scoped>
.el-icon-check {
color: #44b349;
margin-left: 10px;
}
.align-right {
float: right;
}
.icon {
width: 24px;
}
.global {
color: #fff;
}
</style>

View File

@ -1,7 +0,0 @@
import Vue from 'vue'
export const LIST_CHANGE = 'LIST_CHANGE';
export let ApiEvent = new Vue();
export let TrackEvent = new Vue();
export let PerformanceEvent = new Vue();

View File

@ -1,60 +0,0 @@
<template>
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" default-active="1" router>
<!-- 不激活项目路由-->
<el-menu-item v-show="false" index="1">Placeholder</el-menu-item>
<el-submenu v-permission="['test_manager','test_user','test_viewer']" index="2" popper-class="submenu">
<template v-slot:title>
<span class="project-name" :title="currentProject">
{{ $t('commons.project') }}: {{ currentProject }}
</span>
</template>
<search-list :current-project.sync="currentProject" />
<el-divider />
<el-menu-item :index="'/setting/project/create'">
<font-awesome-icon :icon="['fa', 'plus']" />
<span style="padding-left: 7px;">{{ $t("project.create") }}</span>
</el-menu-item>
<el-menu-item :index="'/setting/project/all'">
<font-awesome-icon :icon="['fa', 'list-ul']" />
<span style="padding-left: 7px;">{{ $t('commons.show_all') }}</span>
</el-menu-item>
</el-submenu>
</el-menu>
</template>
<script>
import SearchList from '@/metersphere/components/common/head/SearchList'
import { PROJECT_NAME } from '@/metersphere/common/js/constants'
export default {
name: 'ProjectSwitch',
components: { SearchList },
props: {
projectName: String
},
data() {
return {
currentProject: this.projectName
}
},
watch: {
currentProject() {
localStorage.setItem(PROJECT_NAME, this.currentProject)
}
}
}
</script>
<style scoped>
.project-name {
display: inline-block;
width: 130px;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
.el-divider--horizontal {
margin: 0;
}
</style>

View File

@ -1,101 +0,0 @@
<template>
<div v-loading="result.loading">
<div class="recent-text">
<i class="el-icon-time" />
<span>{{ options.title }}</span>
<i class="el-icon-refresh" @click="recent" />
</div>
<el-menu-item v-for="i in items" :key="i.id" :index="getIndex(i)" :route="getRouter(i)">
<template slot="title">
<div class="title">{{ i.name }}</div>
<div v-if="options.showTime && i.updateTime" class="time">{{ i.updateTime | timestampFormatDate }}</div>
</template>
</el-menu-item>
</div>
</template>
<script>
import { hasRoles } from '@/metersphere/common/js/utils'
import { ROLE_TEST_MANAGER, ROLE_TEST_USER, ROLE_TEST_VIEWER } from '@/metersphere/common/js/constants'
export default {
name: 'MsRecentList',
props: {
options: Object
},
data() {
return {
result: {},
items: []
}
},
computed: {
getIndex: function() {
return function(item) {
return this.options.index(item)
}
},
getRouter: function() {
return function(item) {
if (this.options.router) {
return this.options.router(item)
}
}
}
},
mounted() {
this.recent()
},
methods: {
recent: function() {
if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
this.result = this.$get(this.options.url, (response) => {
this.items = response.data
})
}
}
}
}
</script>
<style scoped>
.recent-text {
padding: 0 10px;
margin-top: -5px;
line-height: 36px;
color: #777777;
background-color: #F5F5F5;
}
.recent-text span {
padding-left: 6px;
line-height: 36px;
}
.recent-text .el-icon-refresh {
cursor: pointer;
float: right;
line-height: 36px;
}
.recent-text .el-icon-refresh:hover {
color: #BBBBBB;
}
.title {
display: inline-block;
padding-left: 20px;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.time {
color: #C0C4CC;
display: inline-block;
padding-left: 20px;
float: right;
}
</style>

View File

@ -1,157 +0,0 @@
<template>
<div v-loading="result.loading">
<el-input :placeholder="$t('project.search_by_name')"
prefix-icon="el-icon-search"
v-model="searchString"
clearable
class="search-input"
size="small"/>
<div v-if="items.length === 0" style="text-align: center; margin: 15px 0">
<span style="font-size: 15px; color: #8a8b8d;">
{{ $t('project.no_data') }}
</span>
</div>
<div v-else style="height: 150px;overflow: auto">
<el-menu-item :key="i.id" v-for="i in items" @click="change(i.id)">
<template slot="title">
<div class="title">
{{ i.name }}
</div>
<i class="el-icon-check" v-if="i.id === currentProjectId"></i>
</template>
</el-menu-item>
</div>
</div>
</template>
<script>
import {getCurrentProjectID, getCurrentUser, hasRoles} from "@/common/js/utils";
import {PROJECT_ID, ROLE_TEST_MANAGER, ROLE_TEST_USER, ROLE_TEST_VIEWER} from "@/common/js/constants";
export default {
name: "SearchList",
props: {
options: Object,
currentProject: String
},
created() {
this.init();
},
computed: {
currentProjectId() {
return localStorage.getItem(PROJECT_ID)
}
},
data() {
return {
result: {},
items: [],
searchArray: [],
searchString: '',
userId: getCurrentUser().id,
}
},
watch: {
searchString(val) {
this.query(val)
}
},
methods: {
init: function () {
if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
this.result = this.$get("/project/listAll", response => {
this.items = response.data;
this.searchArray = response.data;
let userLastProjectId = getCurrentUser().lastProjectId;
if (userLastProjectId) {
// id
if (this.searchArray.length > 0 && this.searchArray.map(p => p.id).indexOf(userLastProjectId) !== -1) {
localStorage.setItem(PROJECT_ID, userLastProjectId);
}
}
let projectId = getCurrentProjectID();
if (projectId) {
// projectId ;
if (this.searchArray.length > 0 && this.searchArray.map(p => p.id).indexOf(projectId) === -1) {
this.change(this.items[0].id);
}
} else {
if (this.items.length > 0) {
this.change(this.items[0].id);
}
}
this.changeProjectName(projectId);
})
}
},
query(queryString) {
this.items = queryString ? this.searchArray.filter(this.createFilter(queryString)) : this.searchArray;
},
createFilter(queryString) {
return item => {
return (item.name.toLowerCase().indexOf(queryString.toLowerCase()) !== -1);
};
},
change(projectId) {
let currentProjectId = getCurrentProjectID();
if (projectId === currentProjectId) {
return;
}
this.$post("/user/update/current", {id: this.userId, lastProjectId: projectId}, () => {
localStorage.setItem(PROJECT_ID, projectId);
let path = this.$route.matched[0].path ? this.$route.matched[0].path : '/';
if (path === '/api') {
if (this.$store.state.switch.value === 'new') {
path = "/api/home";
} else if (this.$store.state.switch.value === 'old') {
path = "/api/home_obsolete";
} else {
path = '/';
}
}
this.$router.push(path).then(() => {
window.location.reload()
}).catch(err => err);
this.changeProjectName(projectId);
});
},
changeProjectName(projectId) {
if (projectId) {
let project = this.searchArray.filter(p => p.id === projectId);
if (project.length > 0) {
this.$emit("update:currentProject", project[0].name);
}
} else {
this.$emit("update:currentProject", this.$t('project.select'));
}
}
}
}
</script>
<style scoped>
.search-input {
padding: 0;
margin-top: -5px;
}
.search-input >>> .el-input__inner {
border-radius: 0;
}
.title {
display: inline-block;
padding-left: 15px;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.el-icon-check {
color: #773888;
margin-left: 10px;
}
</style>

View File

@ -1,29 +0,0 @@
<template>
<el-menu-item :index="this.index" @click="changeRoute">
<font-awesome-icon :icon="['fa', 'list-ul']"/>
<span>{{ $t('commons.show_all') }}</span>
</el-menu-item>
</template>
<script>
export default {
name: "MsShowAll",
props: {
index: String
},
methods: {
changeRoute() {
//
if (this.$route.path === this.index) {
this.$router.replace({path: this.index, query: {type: 'all'}});
}
}
}
}
</script>
<style scoped>
svg + span {
padding-left: 5px;
}
</style>

View File

@ -1,159 +0,0 @@
export const TokenKey = 'Authorization'
export const LicenseKey = 'License'
export const DEFAULT_LANGUAGE = 'default_language'
export const ROLE_ADMIN = 'admin'
export const ROLE_ORG_ADMIN = 'org_admin'
export const ROLE_TEST_MANAGER = 'test_manager'
export const ROLE_TEST_USER = 'test_user'
export const ROLE_TEST_VIEWER = 'test_viewer'
export const WORKSPACE_ID = 'workspace_id'
export const CURRENT_PROJECT = 'current_project'
export const PROJECT_ID = 'project_id'
export const PROJECT_NAME = 'project_name'
export const REFRESH_SESSION_USER_URL = 'user/refresh'
export const WORKSPACE = 'workspace'
export const ORGANIZATION = 'organization'
export const MENU = 'menu'
export const DEFAULT = 'default'
export const ZH_CN = 'zh_CN'
export const ZH_TW = 'zh_TW'
export const EN_US = 'en_US'
export const TAPD = 'Tapd'
export const JIRA = 'Jira'
export const ZEN_TAO = 'Zentao'
export const SCHEDULE_TYPE = {
API_TEST: 'API_TEST',
PERFORMANCE_TEST: 'PERFORMANCE_TEST'
}
export const REQUEST_HEADERS = [
{ value: 'Accept' },
{ value: 'Accept-Charset' },
{ value: 'Accept-Language' },
{ value: 'Accept-Datetime' },
{ value: 'Authorization' },
{ value: 'Cache-Control' },
{ value: 'Connection' },
{ value: 'Cookie' },
{ value: 'Content-Length' },
{ value: 'Content-MD5' },
{ value: 'Content-Type' },
{ value: 'Date' },
{ value: 'Expect' },
{ value: 'From' },
{ value: 'Host' },
{ value: 'If-Match' },
{ value: 'If-Modified-Since' },
{ value: 'If-None-Match' },
{ value: 'If-Range' },
{ value: 'If-Unmodified-Since' },
{ value: 'Max-Forwards' },
{ value: 'Origin' },
{ value: 'Pragma' },
{ value: 'Proxy-Authorization' },
{ value: 'Range' },
{ value: 'Referer' },
{ value: 'TE' },
{ value: 'User-Agent' },
{ value: 'Upgrade' },
{ value: 'Via' },
{ value: 'Warning' }
]
export const MOCKJS_FUNC = [
{ name: '@boolean' },
{ name: '@natural' },
{ name: '@integer' },
{ name: '@float' },
{ name: '@character' },
{ name: '@string' },
{ name: '@range' },
{ name: '@date' },
{ name: '@time' },
{ name: '@datetime' },
{ name: '@now' },
{ name: '@img' },
{ name: '@dataImage' },
{ name: '@color' },
{ name: '@hex' },
{ name: '@rgb' },
{ name: '@rgba' },
{ name: '@hsl' },
{ name: '@paragraph' },
{ name: '@sentence' },
{ name: '@word' },
{ name: '@title' },
{ name: '@cparagraph' },
{ name: '@csentence' },
{ name: '@cword' },
{ name: '@ctitle' },
{ name: '@first' },
{ name: '@last' },
{ name: '@name' },
{ name: '@cfirst' },
{ name: '@clast' },
{ name: '@cname' },
{ name: '@url' },
{ name: '@domain' },
{ name: '@protocol' },
{ name: '@tld' },
{ name: '@email' },
{ name: '@ip' },
{ name: '@region' },
{ name: '@province' },
{ name: '@city' },
{ name: '@county' },
{ name: '@zip' },
{ name: '@capitalize' },
{ name: '@upper' },
{ name: '@lower' },
{ name: '@pick' },
{ name: '@shuffle' },
{ name: '@guid' },
{ name: '@id' },
{ name: '@increment' }
]
export const JMETER_FUNC = [
{ type: 'Information', name: '${__threadNum}', description: 'get thread number' },
{ type: 'Information', name: '${__samplerName}', description: 'get the sampler name (label)' },
{ type: 'Information', name: '${__machineIP}', description: 'get the local machine IP address' },
{ type: 'Information', name: '${__machineName}', description: 'get the local machine name' },
{ type: 'Information', name: '${__time}', description: 'return current time in various formats' },
{ type: 'Information', name: '${__log}', description: 'log (or display) a message (and return the value)' },
{ type: 'Information', name: '${__logn}', description: 'log (or display) a message (empty return value)' },
{ type: 'Input', name: '${__StringFromFile}', description: 'read a line from a file' },
{ type: 'Input', name: '${__FileToString}', description: 'read an entire file' },
{ type: 'Input', name: '${__CSVRead}', description: 'read from CSV definitioned file' },
{ type: 'Input', name: '${__XPath}', description: 'Use an XPath expression to read from a file' },
{ type: 'Calculation', name: '${__counter}', description: 'generate an incrementing number' },
{ type: 'Calculation', name: '${__intSum}', description: 'add int numbers' },
{ type: 'Calculation', name: '${__longSum}', description: 'add long numbers' },
{ type: 'Calculation', name: '${__Random}', description: 'generate a random number' },
{ type: 'Calculation', name: '${__RandomString}', description: 'generate a random string' },
{ type: 'Calculation', name: '${__UUID}', description: 'generate a random type 4 UUID' },
{ type: 'Scripting', name: '${__BeanShell}', description: 'run a BeanShell script' },
{ type: 'Scripting', name: '${__javaScript}', description: 'process JavaScript (Mozilla Rhino)' },
{ type: 'Scripting', name: '${__jexl}', description: 'evaluate a Commons Jexl expression' },
{ type: 'Scripting', name: '${__jexl2}', description: 'evaluate a Commons Jexl expression' },
{ type: 'Properties', name: '${__property}', description: 'read a property' },
{ type: 'Properties', name: '${__P}', description: 'read a property (shorthand method)' },
{ type: 'Properties', name: '${__setProperty}', description: 'set a JMeter property' },
{ type: 'Variables', name: '${__split}', description: 'Split a string into variables' },
{ type: 'Variables', name: '${__V}', description: 'evaluate a variable name' },
{ type: 'Variables', name: '${__eval}', description: 'evaluate a variable expression' },
{ type: 'Variables', name: '${__evalVar}', description: 'evaluate an expression stored in a variable' },
{ type: 'String', name: '${__regexFunction}', description: 'parse previous response using a regular expression' },
{ type: 'String', name: '${__escapeOroRegexpChars}', description: 'quote meta chars used by ORO regular expression' },
{ type: 'String', name: '${__char}', description: 'generate Unicode char values from a list of numbers' },
{ type: 'String', name: '${__unescape}', description: 'Process strings containing Java escapes (e.g. & )' },
{ type: 'String', name: '${__unescapeHtml}', description: 'Decode HTML-encoded strings' },
{ type: 'String', name: '${__escapeHtml}', description: 'Encode strings using HTML encoding' },
{ type: 'String', name: '${__TestPlanName}', description: 'Return name of current test plan' }
]

View File

@ -1,59 +0,0 @@
export const left2RightDrag = {
inserted(el, binding) {
el.onmousedown = function(e) {
const init = e.clientX
const parent = el.parentNode
const initWidth = parent.offsetWidth
document.onmousemove = function(e) {
const end = e.clientX
const newWidth = end - init + initWidth
if (newWidth < document.body.clientWidth - 10 && newWidth > 10) {
parent.style.width = newWidth + 'px'
}
}
document.onmouseup = function() {
document.onmousemove = document.onmouseup = null
}
}
}
}
export const right2LeftDrag = {
inserted(el, binding) {
el.onmousedown = function(e) {
const init = e.clientX
const parent = el.parentNode
const initWidth = parent.offsetWidth
document.onmousemove = function(e) {
const end = e.clientX
const newWidth = initWidth - (end - init)
if (newWidth < document.body.clientWidth - 10 && newWidth > 10) {
parent.style.width = newWidth + 'px'
}
}
document.onmouseup = function() {
document.onmousemove = document.onmouseup = null
}
}
}
}
export const bottom2TopDrag = {
inserted(el, binding) {
el.onmousedown = function(e) {
const init = e.clientY
const parent = el.parentNode
const initHeight = parent.offsetHeight
document.onmousemove = function(e) {
const end = e.clientY
const newHeight = initHeight - (end - init)
if (newHeight < document.body.clientHeight - 10 && newHeight > 10) {
parent.style.height = newHeight + 'px'
}
}
document.onmouseup = function() {
document.onmousemove = document.onmouseup = null
}
}
}
}

View File

@ -1,46 +0,0 @@
import { Message } from 'element-ui'
export default {
install(Vue) {
if (!Message) {
window.console.error('You have to install Message of ElementUI')
return
}
Vue.prototype.$success = function(message) {
Message.success({
message: message,
type: 'success',
showClose: true,
duration: 1500
})
}
Vue.prototype.$info = function(message, duration) {
Message.info({
message: message,
type: 'info',
showClose: true,
duration: duration || 3000
})
}
Vue.prototype.$warning = function(message) {
Message.warning({
message: message,
type: 'warning',
showClose: true,
duration: 5000
})
}
Vue.prototype.$error = function(message, duration) {
Message.error({
message: message,
type: 'error',
showClose: true,
duration: duration || 10000
})
}
}
}

View File

@ -1 +0,0 @@
export const PHONE_REGEX = '^1(3|4|5|6|7|8|9)\\d{9}$'

View File

@ -1,292 +0,0 @@
import {
LicenseKey,
PROJECT_ID,
REFRESH_SESSION_USER_URL,
ROLE_ADMIN,
ROLE_ORG_ADMIN,
ROLE_TEST_MANAGER,
ROLE_TEST_USER,
ROLE_TEST_VIEWER
} from './constants'
import axios from 'axios'
export function hasRole(role) {
const user = getCurrentUser()
const roles = user.roles.map(r => r.id)
return roles.indexOf(role) > -1
}
// 是否含有某个角色
export function hasRoles(...roles) {
const user = getCurrentUser()
const rs = user.roles.map(r => r.id)
for (const item of roles) {
if (rs.indexOf(item) > -1) {
return true
}
}
return false
}
export function hasRolePermission(role) {
const user = getCurrentUser()
for (const ur of user.userRoles) {
if (role === ur.roleId) {
if (ur.roleId === ROLE_ADMIN) {
return true
} else if (ur.roleId === ROLE_ORG_ADMIN && user.lastOrganizationId === ur.sourceId) {
return true
} else if (user.lastWorkspaceId === ur.sourceId) {
return true
}
}
}
return false
}
export function hasLicense() {
const v = localStorage.getItem(LicenseKey)
return v === 'valid'
}
// 是否含有对应组织或工作空间的角色
export function hasRolePermissions(...roles) {
for (const role of roles) {
if (hasRolePermission(role)) {
return true
}
}
return false
}
export function checkoutCurrentOrganization() {
// 查看当前用户是否是 lastOrganizationId 的组织管理员
return hasRolePermissions(ROLE_ORG_ADMIN)
}
export function checkoutCurrentWorkspace() {
// 查看当前用户是否是 lastWorkspaceId 的工作空间用户
return hasRolePermissions(ROLE_TEST_MANAGER, ROLE_TEST_USER, ROLE_TEST_VIEWER)
}
export function checkoutTestManagerOrTestUser() {
return hasRolePermissions(ROLE_TEST_MANAGER, ROLE_TEST_USER)
}
export function getCurrentOrganizationId() {
const user = getCurrentUser()
return user.lastOrganizationId
}
export function getCurrentWorkspaceId() {
const user = getCurrentUser()
return user.lastWorkspaceId
}
export function getCurrentUser() {
// return JSON.parse(localStorage.getItem(TokenKey));
return getUserInfo()
}
export function getCurrentProjectID() {
return localStorage.getItem(PROJECT_ID)
}
// export function saveLocalStorage(response) {
// // 登录信息保存 cookie
// //localStorage.setItem(TokenKey, JSON.stringify(response.data));
// let rolesArray = response.data.roles;
// let roles = rolesArray.map(r => r.id);
// // 保存角色
// localStorage.setItem("roles", roles);
// }
export function saveLocalStorage(response) {
// 登录信息保存 cookie
// localStorage.setItem(TokenKey, JSON.stringify(response.data));
const rolesArray = response.data.roles
const roles = rolesArray.map(r => r.id)
// 保存角色
localStorage.setItem('roles', roles)
}
export function saveLicense(data) {
// 保存License
localStorage.setItem(LicenseKey, data)
}
export function refreshSessionAndCookies(sign, sourceId) {
axios.post(REFRESH_SESSION_USER_URL + '/' + sign + '/' + sourceId).then(r => {
saveLocalStorage(r.data)
window.location.reload()
})
}
export function jsonToMap(jsonStr) {
const obj = JSON.parse(jsonStr)
const strMap = new Map()
for (const k of Object.keys(obj)) {
strMap.set(k, obj[k])
}
return strMap
}
export function mapToJson(strMap) {
const obj = Object.create(null)
for (const [k, v] of strMap) {
obj[k] = v
}
return JSON.stringify(obj)
}
// 驼峰转换下划线
export function humpToLine(name) {
return name.replace(/([A-Z])/g, '_$1').toLowerCase()
}
export function downloadFile(name, content) {
const blob = new Blob([content])
if ('download' in document.createElement('a')) {
// 非IE下载
// chrome/firefox
const aTag = document.createElement('a')
aTag.download = name
aTag.href = URL.createObjectURL(blob)
aTag.click()
URL.revokeObjectURL(aTag.href)
} else {
// IE10+下载
navigator.msSaveBlob(blob, name)
}
}
export function listenGoBack(callback) {
// 监听浏览器返回操作,关闭该对话框
if (window.history && window.history.pushState) {
history.pushState(null, null, document.URL)
window.addEventListener('popstate', callback)
}
}
export function removeGoBackListener(callback) {
window.removeEventListener('popstate', callback)
}
export const uuid = function() {
let d = new Date().getTime()
let d2 = (performance && performance.now && (performance.now() * 1000)) || 0
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
let r = Math.random() * 16
if (d > 0) {
r = (d + r) % 16 | 0
d = Math.floor(d / 16)
} else {
r = (d2 + r) % 16 | 0
d2 = Math.floor(d2 / 16)
}
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
})
}
export function getUUID() {
function S4() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
}
return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4())
}
export function windowPrint(id, zoom) {
// 根据div标签ID拿到div中的局部内容
const bdhtml = window.document.body.innerHTML
const el = document.getElementById(id)
var jubuData = el.innerHTML
document.getElementsByTagName('body')[0].style.zoom = zoom
// 把获取的 局部div内容赋给body标签, 相当于重置了 body里的内容
window.document.body.innerHTML = jubuData
// 调用打印功能
window.print()
window.document.body.innerHTML = bdhtml// 重新给页面内容赋值;
return false
}
export function getBodyUploadFiles(obj, runData) {
const bodyUploadFiles = []
obj.bodyUploadIds = []
if (runData) {
if (runData instanceof Array) {
runData.forEach(request => {
_getBodyUploadFiles(request, bodyUploadFiles, obj)
})
} else {
_getBodyUploadFiles(runData, bodyUploadFiles, obj)
}
}
return bodyUploadFiles
}
export function _getBodyUploadFiles(request, bodyUploadFiles, obj) {
let body = null
if (request.hashTree && request.hashTree.length > 0 && request.hashTree[0].body) {
body = request.hashTree[0].body
} else if (request.body) {
body = request.body
}
if (body) {
if (body.kvs) {
body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
if (!item.id) {
const fileId = getUUID().substring(0, 12)
item.name = item.file.name
item.id = fileId
}
obj.bodyUploadIds.push(item.id)
bodyUploadFiles.push(item.file)
}
})
}
})
}
if (body.binary) {
body.binary.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
if (!item.id) {
const fileId = getUUID().substring(0, 12)
item.name = item.file.name
item.id = fileId
}
obj.bodyUploadIds.push(item.id)
bodyUploadFiles.push(item.file)
}
})
}
})
}
}
}
export function handleCtrlSEvent(event, func) {
if (event.keyCode === 83 && event.ctrlKey) {
// console.log('拦截到 ctrl + s');//ctrl+s
func()
event.preventDefault()
event.returnValue = false
return false
}
}
// ---------------- dataEase新增
export function setUserInfo(userInfo) {
localStorage.setItem('userInfo', JSON.stringify(userInfo))
}
export function getUserInfo() {
return JSON.parse(localStorage.getItem('userInfo'))
}
export function getRoles() {
const uinfo = getUserInfo()
return uinfo && uinfo['roles']
}

View File

@ -1,90 +0,0 @@
<template>
<div id="app" v-loading="loading">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane :label="$t('organization.message.template')" name="apiTemplate">
<el-button type="primary" size="mini" style="margin: 10px 10px 0px" @click="openOneClickOperation">导入</el-button>
<div style="min-height: 200px">
<json-schema-editor class="schema" :value="schema" lang="zh_CN" custom/>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('schema.preview')" name="preview">
<div style="min-height: 200px">
<pre>{{this.preview}}</pre>
</div>
</el-tab-pane>
</el-tabs>
<ms-import-json ref="importJson" @jsonData="jsonData"/>
</div>
</template>
<script>
import {schemaToJson} from './common';
import MsImportJson from './import/ImportJson';
const GenerateSchema = require('generate-schema/src/schemas/json.js');
export default {
name: 'App',
components: {MsImportJson},
props: {
body: {},
},
created() {
if (!this.body.jsonSchema && this.body.raw && this.checkIsJson(this.body.raw)) {
let obj = {"root": GenerateSchema(JSON.parse(this.body.raw))}
this.schema = obj;
}
else if (this.body.jsonSchema) {
this.schema = {"root": this.body.jsonSchema};
}
this.body.jsonSchema = this.schema.root;
},
data() {
return {
schema:
{
"root": {
"type": "object",
"properties": {},
}
},
loading: false,
preview: null,
activeName: "apiTemplate",
}
},
methods: {
handleClick() {
if (this.activeName === 'preview') {
//
//this.preview = schemaToJson(this.schema.root);
this.loading = true;
//
this.$post('/api/definition/preview', this.schema.root, response => {
this.preview = response.data;
this.loading = false;
});
}
},
openOneClickOperation() {
this.$refs.importJson.openOneClickOperation();
},
checkIsJson(json) {
try {
JSON.parse(json);
return true;
} catch (e) {
return false;
}
},
jsonData(data) {
let obj = {"root": data}
this.schema = obj;
}
}
}
</script>
<style>
</style>

View File

@ -1,32 +0,0 @@
const Mock = require('mockjs');
const jsf = require('json-schema-faker');
jsf.extend('mock', function () {
return {
mock: function (xx) {
if (xx && xx.startsWith("@")) {
return Mock.mock(xx);
}
return xx;
}
};
});
const defaultOptions = {
failOnInvalidTypes: false,
failOnInvalidFormat: false
};
export const schemaToJson = (schema, options = {}) => {
Object.assign(options, defaultOptions);
jsf.option(options);
let result;
try {
result = jsf.generate(schema);
} catch (err) {
result = err.message;
}
jsf.option(defaultOptions);
return result;
};

View File

@ -1,117 +0,0 @@
<template>
<el-dialog
title="导入"
:visible.sync="importVisible"
width="50%"
append-to-body
show-close
:close-on-click-modal="false"
@closed="handleClose">
<el-tabs v-model="activeName">
<el-tab-pane label="JSON" name="JSON">
<div style="height: 400px">
<ms-code-edit :mode="mode"
:data.sync="json" theme="eclipse" :modes="[]"
ref="codeEdit"/>
</div>
</el-tab-pane>
<el-tab-pane label="JSON-SCHEMA" name="JSON-SCHEMA">
<div style="height: 400px">
<ms-code-edit :mode="mode"
:data.sync="jsonSchema" theme="eclipse" :modes="[]"
ref="codeEdit"/>
</div>
</el-tab-pane>
</el-tabs>
<span slot="footer" class="dialog-footer">
<ms-dialog-footer
@cancel="importVisible = false"
@confirm="saveConfirm"/>
</span>
</el-dialog>
</template>
<script>
import MsDialogFooter from '../../../common/components/MsDialogFooter'
import MsCodeEdit from "../../../common/components/MsCodeEdit";
import json5 from 'json5';
const GenerateSchema = require('generate-schema/src/schemas/json.js');
export default {
name: "MsImportJson",
components: {MsDialogFooter, MsCodeEdit},
data() {
return {
importVisible: false,
activeName: "JSON",
mode: "json",
json: "",
jsonSchema: "",
};
},
watch: {},
props: {},
methods: {
openOneClickOperation() {
this.importVisible = true;
},
checkIsJson(json) {
try {
json5.parse(json);
return true;
} catch (e) {
return false;
}
},
checkIsJsonSchema(json) {
try {
json = json5.parse(json);
if (json.properties && typeof json.properties === 'object' && !json.type) {
json.type = 'object';
}
if (json.items && typeof json.items === 'object' && !json.type) {
json.type = 'array';
}
if (!json.type) {
return false;
}
json.type = json.type.toLowerCase();
let types = ['object', 'string', 'number', 'array', 'boolean', 'integer'];
if (types.indexOf(json.type) === -1) {
return false;
}
return JSON.stringify(json);
} catch (e) {
return false;
}
},
saveConfirm() {
if (this.activeName === 'JSON') {
if (!this.checkIsJson(this.json)) {
this.$error("导入的数据非JSON格式");
return;
}
let jsonData = GenerateSchema(json5.parse(this.json));
this.$emit('jsonData', jsonData);
} else {
if (!this.checkIsJsonSchema(this.jsonSchema)) {
this.$error("导入的数据非JSON-SCHEMA 格式");
return;
}
let obj = json5.parse(this.jsonSchema);
this.$emit('jsonData', obj);
}
this.importVisible = false;
},
handleClose() {
this.importVisible = false;
},
}
}
</script>
<style scoped>
</style>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,7 +0,0 @@
import JsonSchemaEditor from './main.vue'
JsonSchemaEditor.install = function (Vue) {
Vue.component(JsonSchemaEditor.name, JsonSchemaEditor)
}
export default JsonSchemaEditor

View File

@ -1,400 +0,0 @@
<template>
<div class="json-schema-editor">
<el-row class="row" :gutter="20">
<el-col :span="8" class="ms-col-name">
<div :style="{marginLeft:`${10*deep}px`}" class="ms-col-name-c"/>
<span v-if="pickValue.type==='object'" :class="hidden? 'el-icon-caret-left ms-transform':
'el-icon-caret-bottom'" @click="hidden = !hidden"/>
<span v-else style="width:10px;display:inline-block"></span>
<input class="el-input el-input__inner" style="height: 32px" :disabled="disabled || root" :value="pickKey" @blur="onInputName" size="small"/>
<el-tooltip v-if="root" :content="$t('schema.checked_all')" placement="top">
<input type="checkbox" :disabled="!isObject && !isArray" class="ms-col-name-required" @change="onRootCheck"/>
</el-tooltip>
<el-tooltip v-else :content="$t('schema.required')" placement="top">
<input type="checkbox" :disabled="isItem" :checked="checked" class="ms-col-name-required" @change="onCheck"/>
</el-tooltip>
</el-col>
<el-col :span="4">
<el-select v-model="pickValue.type" :disabled="disabledType" class="ms-col-type" @change="onChangeType" size="small">
<el-option :key="t" :value="t" :label="t" v-for="t in TYPE_NAME"/>
</el-select>
</el-col>
<el-col :span="4">
<ms-mock :disabled="pickValue.type==='object'" :schema="pickValue"/>
</el-col>
<el-col :span="4">
<el-input v-model="pickValue.description" class="ms-col-title" :placeholder="$t('schema.description')" size="small"/>
</el-col>
<el-col :span="4" class="col-item-setting">
<el-tooltip class="item" effect="dark" :content="$t('schema.adv_setting')" placement="top">
<i class="el-icon-setting" @click="onSetting"/>
</el-tooltip>
<el-tooltip v-if="isObject" :content="$t('schema.add_child_node')" placement="top">
<i class="el-icon-plus" @click="addChild" style="margin-left: 10px"/>
</el-tooltip>
<el-tooltip v-if="!root && !isItem" :content="$t('schema.remove_node')" placement="top">
<i class="el-icon-close" @click="removeNode" style="margin-left: 10px"/>
</el-tooltip>
</el-col>
</el-row>
<template v-if="!hidden&&pickValue.properties && !isArray">
<json-schema-editor v-for="(item,key,index) in pickValue.properties" :value="{[key]:item}" :parent="pickValue" :key="index" :deep="deep+1" :root="false" class="children" :lang="lang" :custom="custom"/>
</template>
<template v-if="isArray">
<json-schema-editor :value="{items:pickValue.items}" :deep="deep+1" disabled isItem :root="false" class="children" :lang="lang" :custom="custom"/>
</template>
<!-- 高级设置-->
<el-dialog append-to-body :close-on-click-modal="false" :title="$t('schema.adv_setting')" :visible.sync="modalVisible" :destroy-on-close="true"
@close="handleClose">
<p class="tip">基础设置 </p>
<el-form :inline="true" v-model="advancedValue" class="ms-advanced-search-form">
<el-row :gutter="6">
<el-col :span="8" v-for="(item,key) in advancedValue" :key="key" style="float: right">
<el-form-item>
<div>{{ $t('schema.'+key)}}</div>
<el-input-number v-model="advancedValue[key]" v-if="advancedAttr[key].type === 'integer'" style="width:100%" :placeholder="key" size="small"/>
<el-input-number v-model="advancedValue[key]" v-else-if="advancedAttr[key].type === 'number'" style="width:100%" :placeholder="key" size="small"/>
<span v-else-if="advancedAttr[key].type === 'boolean'" style="display:inline-block;width:100%">
<el-switch v-model="advancedValue[key]"/>
</span>
<el-select v-else-if="advancedAttr[key].type === 'array'" v-model="advancedValue[key]" style="width:100%" size="small">
<el-option value="" :label="$t('schema.nothing')"></el-option>
<el-option :key="t" :value="t" :label="t" v-for="t in advancedAttr[key].enums"/>
</el-select>
<el-input v-model="advancedValue[key]" v-else style="width:100%;" :placeholder="key" size="small"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!--<h3 v-text="$t('schema.add_custom')" v-show="custom">添加自定义属性</h3>
<el-form class="ms-advanced-search-form" v-show="custom">
<el-row :gutter="6">
<el-col :span="8" v-for="item in customProps" :key="item.key">
<el-form-item :label="item.key">
<el-input v-model="item.value" style="width:calc(100% - 30px)" size="small"/>
<el-button icon="close" type="link" @click="customProps.splice(customProps.indexOf(item),1)" size="small"/>
</el-form-item>
</el-col>
<el-col :span="8" v-show="addProp.key != undefined">
<el-form-item>
<el-input slot="label" v-model="addProp.key" size="small"/>
<el-input v-model="addProp.value" size="small"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item>
<el-button icon="check" type="link" @click="confirmAddCustomNode" v-if="customing"/>
<el-tooltip content="$t('schema.add_custom')" v-else>
<el-button icon="el-icon-plus" type="link" @click="addCustomNode"/>
</el-tooltip>
</el-form-item>
</el-col>
</el-row>
</el-form>-->
<p class="tip">{{$t('schema.preview')}} </p>
<pre style="width:100%">{{completeNodeValue}}</pre>
<span slot="footer" class="dialog-footer">
<ms-dialog-footer
@cancel="modalVisible = false"
@confirm="handleOk"/>
</span>
</el-dialog>
</div>
</template>
<script>
import {isNull} from './util'
import {TYPE_NAME, TYPE} from './type/type'
import MsMock from './mock/MockComplete'
import MsDialogFooter from '../../../components/MsDialogFooter'
import {getUUID} from "@/common/js/utils";
export default {
name: 'JsonSchemaEditor',
components: {MsMock, MsDialogFooter},
props: {
value: {
type: Object,
required: true
},
disabled: { //namename,name
type: Boolean,
default: false
},
disabledType: { //
type: Boolean,
default: false
},
isItem: { //
type: Boolean,
default: false
},
deep: { // deep=0
type: Number,
default: 0
},
root: { //root
type: Boolean,
default: true
},
parent: { //
type: Object,
default: null
},
custom: { //enable custom properties
type: Boolean,
default: false
},
lang: { // i18n language
type: String,
default: 'zh_CN'
}
},
computed: {
pickValue() {
return Object.values(this.value)[0]
},
pickKey() {
return Object.keys(this.value)[0]
},
isObject() {
return this.pickValue.type === 'object'
},
isArray() {
return this.pickValue.type === 'array'
},
checked() {
return this.parent && this.parent.required && this.parent.required.indexOf(this.pickKey) >= 0
},
advanced() {
return TYPE[this.pickValue.type]
},
advancedAttr() {
return TYPE[this.pickValue.type].attr
},
advancedNotEmptyValue() {
const jsonNode = Object.assign({}, this.advancedValue);
for (let key in jsonNode) {
isNull(jsonNode[key]) && delete jsonNode[key]
}
return jsonNode
},
completeNodeValue() {
const t = {}
for (const item of this.customProps) {
t[item.key] = item.value
}
return Object.assign({}, this.pickValue, this.advancedNotEmptyValue, t)
}
},
data() {
return {
TYPE_NAME,
hidden: false,
countAdd: 1,
modalVisible: false,
advancedValue: {},
addProp: {},//
customProps: [],
customing: false
}
},
methods: {
onInputName(e) {
const val = e.target.value
const p = {};
for (let key in this.parent.properties) {
if (key != this.pickKey) {
p[key] = this.parent.properties[key]
} else {
p[val] = this.parent.properties[key]
delete this.parent.properties[key]
}
}
this.$set(this.parent, 'properties', p)
},
onChangeType() {
this.$delete(this.pickValue, 'properties')
this.$delete(this.pickValue, 'items')
this.$delete(this.pickValue, 'required')
this.$delete(this.pickValue, 'mock')
if (this.isArray) {
this.$set(this.pickValue, 'items', {type: 'string', mock: {mock: ""}})
}
},
onCheck(e) {
this._checked(e.target.checked, this.parent)
},
onRootCheck(e) {
const checked = e.target.checked
this._deepCheck(checked, this.pickValue)
},
_deepCheck(checked, node) {
if (node.type === 'object' && node.properties) {
checked ? this.$set(node, 'required', Object.keys(node.properties)) : this.$delete(node, 'required')
Object.keys(node.properties).forEach(key => this._deepCheck(checked, node.properties[key]))
} else if (node.type === 'array' && node.items.type === 'object') {
checked ? this.$set(node.items, 'required', Object.keys(node.items.properties)) : this.$delete(node.items, 'required')
Object.keys(node.items.properties).forEach(key => this._deepCheck(checked, node.items.properties[key]))
}
},
_checked(checked, parent) {
let required = parent.required
if (checked) {
required || this.$set(this.parent, 'required', [])
required = this.parent.required
required.indexOf(this.pickKey) === -1 && required.push(this.pickKey)
} else {
const pos = required.indexOf(this.pickKey)
pos >= 0 && required.splice(pos, 1)
}
required.length === 0 && this.$delete(parent, 'required')
},
addChild() {
const name = this._joinName()
const type = 'string'
const node = this.pickValue
node.properties || this.$set(node, 'properties', {})
const props = node.properties
this.$set(props, name, {type: type, mock: {mock: ""}})
},
addCustomNode() {
this.$set(this.addProp, 'key', this._joinName())
this.$set(this.addProp, 'value', '')
this.customing = true
},
confirmAddCustomNode() {
this.customProps.push(this.addProp)
this.addProp = {}
this.customing = false
},
removeNode() {
const {properties, required} = this.parent
this.$delete(properties, this.pickKey)
if (required) {
const pos = required.indexOf(this.pickKey)
pos >= 0 && required.splice(pos, 1)
required.length === 0 && this.$delete(this.parent, 'required')
}
},
_joinName() {
return `feild_${this.deep}_${this.countAdd++}_${getUUID().substring(0, 5)}`
},
onSetting() {
this.modalVisible = true;
this.advancedValue = this.advanced.value
for (const k in this.advancedValue) {
if (this.pickValue[k]) this.advancedValue[k] = this.pickValue[k]
}
},
handleClose() {
this.modalVisible = false;
},
handleOk() {
this.modalVisible = false
for (const key in this.advancedValue) {
if (isNull(this.advancedValue[key])) {
this.$delete(this.pickValue, key)
} else {
this.$set(this.pickValue, key, this.advancedValue[key])
}
}
for (const item of this.customProps) {
this.$set(this.pickValue, item.key, item.value)
}
}
}
}
</script>
<style scoped>
.json-schema-editor .row {
display: flex;
margin: 12px;
}
.json-schema-editor .row .ms-col-name {
display: flex;
align-items: center;
}
.json-schema-editor .row .ms-col-name .ms-col-name-c {
display: flex;
align-items: center;
}
.json-schema-editor .row .ms-col-name .ms-col-name-required {
flex: 0 0 30px;
text-align: center;
}
.json-schema-editor .row .ms-col-type {
width: 100%;
}
.json-schema-editor .row .ms-col-setting {
display: inline-block;
}
.json-schema-editor .row .setting-icon {
color: rgba(0, 0, 0, 0.45);
border: none;
}
.json-schema-editor .row .plus-icon {
border: none;
}
.json-schema-editor .row .close-icon {
color: #888;
border: none;
}
.json-schema-editor-advanced-modal {
color: rgba(0, 0, 0, 0.65);
min-width: 600px;
}
.json-schema-editor-advanced-modal pre {
font-family: monospace;
height: 100%;
overflow-y: auto;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 4px;
padding: 12px;
width: 50%;
}
.json-schema-editor-advanced-modal h3 {
display: block;
border-left: 3px solid #1890ff;
padding: 0 8px;
}
.json-schema-editor-advanced-modal .ms-advanced-search-form {
display: flex;
}
.json-schema-editor-advanced-modal .ms-advanced-search-form .ms-form-item .ms-form-item-control-wrapper {
flex: 1;
}
.col-item-setting {
padding-top: 8px;
cursor: pointer;
}
.tip {
padding: 3px 5px;
font-size: 16px;
border-radius: 4px;
border-left: 4px solid #783887;
margin: 0px 0px 10px;
}
.ms-transform {
transform: rotate(-180deg);
transition: 0ms;
}
</style>

View File

@ -1,236 +0,0 @@
<template>
<el-dialog :title="$t('api_test.request.parameters_advance')"
:visible.sync="itemValueVisible"
class="advanced-item-value"
width="70%">
<el-tabs tab-position="top" style="height: 50vh;" @tab-click="selectTab">
<el-tab-pane :label="$t('api_test.request.parameters_advance_mock')">
<el-row type="flex" :gutter="20">
<el-col :span="6" class="col-height">
<div>
<el-input size="small" v-model="filterText"
:placeholder="$t('api_test.request.parameters_mock_filter_tips')"/>
<el-tree class="filter-tree" ref="tree" :data="mockFuncs" :props="treeProps"
default-expand-all @node-click="selectVariable"
:filter-node-method="filterNode"></el-tree>
</div>
</el-col>
<el-col :span="6" v-for="(itemFunc, itemIndex) in mockVariableFuncs" :key="itemIndex">
<div v-for="(func, funcIndex) in funcs"
:key="`${itemIndex}-${funcIndex}`">
<el-row>
<el-col :span="12">
<el-radio size="mini" v-model="itemFunc.name" :label="func.name"
@change="methodChange(itemFunc, func)"/>
</el-col>
<el-col :span="12" v-if="itemFunc.name === func.name">
<div v-for="(p, pIndex) in itemFunc.params" :key="`${itemIndex}-${funcIndex}-${pIndex}`">
<el-input :placeholder="p.name" size="mini" v-model="p.value" @change="showPreview"/>
</div>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.variable')">
<el-row>
<el-col :span="6" class="col-height">
<div v-if="preRequestParams">
<p>{{ $t('api_test.request.parameters_pre_request') }}</p>
<el-tree :data="preRequestParams" :props="treeProps" @node-click="selectVariable"></el-tree>
</div>
</el-col>
<el-col :span="18" class="col-height">
<div>
<h1>{{ $t('api_test.request.jmeter_func') }}</h1>
<el-table border :data="jmeterFuncs" class="adjust-table table-content" height="400">
<el-table-column prop="type" label="Type" width="150"/>
<el-table-column prop="name" label="Functions" width="250"/>
<el-table-column prop="description" label="Description"/>
</el-table>
</div>
</el-col>
</el-row>
</el-tab-pane>
</el-tabs>
<el-form>
<el-form-item>
<el-input :placeholder="valueText" size="small"
v-model="itemValue"/>
</el-form-item>
</el-form>
<div style="padding-top: 10px;">
<el-row type="flex" align="middle">
<el-col :span="12">
<el-button size="small" type="primary" plain @click="saveAdvanced()">
{{ $t('commons.save') }}
</el-button>
<el-button size="small" type="info" plain @click="addFunc()" v-if="currentTab === 0">
{{ $t('api_test.request.parameters_advance_add_func') }}
</el-button>
<el-button size="small" type="success" plain @click="showPreview()" v-if="currentTab === 0">
{{ $t('api_test.request.parameters_preview') }}
</el-button>
</el-col>
<el-col>
<div> {{ itemValuePreview }}</div>
</el-col>
</el-row>
</div>
</el-dialog>
</template>
<script>
import {calculate} from "./calculate";
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
export default {
name: "MsApiVariableAdvance",
props: {
parameters: Array,
currentItem: Object,
},
data() {
return {
itemValueVisible: false,
filterText: '',
environmentParams: [],
scenarioParams: [],
Scenario: {},
preRequests: [],
preRequestParams: [],
treeProps: {children: 'children', label: 'name'},
currentTab: 0,
itemValue: null,
itemValuePreview: null,
funcs: [
{name: "md5"},
{name: "base64"},
{name: "unbase64"},
{
name: "substr",
params: [{name: "start"}, {name: "length"}]
},
{
name: "concat",
params: [{name: "suffix"}]
},
{name: "lconcat", params: [{name: "prefix"}]},
{name: "sha1"},
{name: "sha224"},
{name: "sha256"},
{name: "sha384"},
{name: "sha512"},
{name: "lower"},
{name: "upper"},
{name: "length"},
{name: "number"}
],
mockFuncs: MOCKJS_FUNC.map(f => {
return {name: f.name, value: f.name}
}),
jmeterFuncs: JMETER_FUNC,
mockVariableFuncs: [],
jmeterVariableFuncs: [],
}
},
computed: {
valueText() {
return this.valuePlaceholder || this.$t("api_test.value");
}
},
mounted() {
this.prepareData();
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
},
},
methods: {
open() {
this.itemValueVisible = true;
},
prepareData() {
},
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
selectVariable(node) {
this.itemValue = node.value;
},
selectTab(tab) {
this.currentTab = +tab.index;
this.itemValue = null;
this.itemValuePreview = null;
},
showPreview() {
//
if (!this.itemValue) {
return;
}
let index = this.itemValue.indexOf("|");
if (index > -1) {
this.itemValue = this.itemValue.substring(0, index).trim();
}
this.mockVariableFuncs.forEach(f => {
if (!f.name) {
return;
}
this.itemValue += "|" + f.name;
if (f.params) {
this.itemValue += ":" + f.params.map(p => p.value).join(",");
}
});
this.itemValuePreview = calculate(this.itemValue);
},
methodChange(itemFunc, func) {
let index = this.mockVariableFuncs.indexOf(itemFunc);
this.mockVariableFuncs = this.mockVariableFuncs.slice(0, index);
// deep copy
this.mockVariableFuncs.push(JSON.parse(JSON.stringify(func)));
this.showPreview();
},
addFunc() {
if (this.mockVariableFuncs.length > 4) {
this.$info(this.$t('api_test.request.parameters_advance_add_func_limit'));
return;
}
if (this.mockVariableFuncs.length > 0) {
let func = this.mockVariableFuncs[this.mockVariableFuncs.length - 1];
if (!func.name) {
this.$warning(this.$t('api_test.request.parameters_advance_add_func_error'));
return;
}
if (func.params) {
for (let j = 0; j < func.params.length; j++) {
if (!func.params[j].value) {
this.$warning(this.$t('api_test.request.parameters_advance_add_param_error'));
return;
}
}
}
}
this.mockVariableFuncs.push({name: '', params: []});
},
saveAdvanced() {
this.currentItem.mock = this.itemValue;
this.itemValueVisible = false;
this.mockVariableFuncs = [];
this.$emit('advancedRefresh', this.itemValue);
}
}
}
</script>
<style scoped>
.col-height {
height: 40vh;
overflow: auto;
}
</style>

View File

@ -1,95 +0,0 @@
<template>
<div>
<el-autocomplete
size="small"
class="input-with-autocomplete"
v-model="mock.mock"
:fetch-suggestions="funcSearch"
:disabled="disabled"
:placeholder="$t('api_test.value')"
value-key="name"
highlight-first-item
@select="change">
<i slot="suffix" class="el-input__icon el-icon-edit pointer" @click="advanced()"></i>
</el-autocomplete>
<ms-advance ref="variableAdvance" :current-item="mock"/>
</div>
</template>
<script>
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
import MsAdvance from "./Advance";
export default {
name: 'MsMock',
components: {MsAdvance},
props: {
schema: {
type: Object,
default: () => {
}
},
disabled: Boolean,
},
data() {
return {
mock: {mock: ""}
}
},
created() {
if (this.schema.mock && Object.prototype.toString.call(this.schema.mock).match(/\[object (\w+)\]/)[1].toLowerCase() === 'object') {
this.mock = this.schema.mock;
} else {
this.schema.mock = this.mock;
}
if (this.schema.type === 'object') {
this.$delete(this.schema, 'mock')
}
},
mounted() {
},
methods: {
funcSearch(queryString, cb) {
let funcs = MOCKJS_FUNC.concat(JMETER_FUNC);
let results = queryString ? funcs.filter(this.funcFilter(queryString)) : funcs;
// callback
cb(results);
},
funcFilter(queryString) {
return (func) => {
return (func.name.toLowerCase().indexOf(queryString.toLowerCase()) > -1);
};
},
change: function () {
},
advanced() {
this.$refs.variableAdvance.open();
},
showEdit() {
this.$emit('showEdit')
},
handleChange(e) {
this.$emit('change', e)
},
querySearchAsync(queryString, cb) {
const arr = this.mock || []
const results = queryString
? arr.filter(this.createStateFilter(queryString))
: arr
cb(results)
},
createStateFilter(queryString) {
return state => {
return (
state.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
)
}
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -1,31 +0,0 @@
import Mock from "mockjs";
import {funcFilters} from "@/common/js/func-filter";
export const calculate = function (itemValue) {
if (!itemValue) {
return;
}
try {
if (itemValue.trim().startsWith("${")) {
// jmeter 内置函数不做处理
return itemValue;
}
let funcs = itemValue.split("|");
let value = Mock.mock(funcs[0].trim());
if (funcs.length === 1) {
return value;
}
for (let i = 1; i < funcs.length; i++) {
let func = funcs[i].trim();
let args = func.split(":");
let strings = [];
if (args[1]) {
strings = args[1].split(",");
}
value = funcFilters[args[0].trim()](value, ...strings);
}
return value;
} catch (e) {
return itemValue;
}
}

View File

@ -1,26 +0,0 @@
const value = {
description: null,
minItems:null,
maxItems:null,
uniqueItems:false
}
const attr = {
description: {
name: '描述',
type: 'string'
},
maxItems:{
name: '最大元素个数',
type: 'integer'
},
minItems:{
name: '最小元素个数',
type: 'integer'
},
uniqueItems:{
name:'元素不可重复',
type: 'boolean'
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -1,11 +0,0 @@
const value = {
description: null
}
const attr = {
description: {
name: '描述',
type: 'string'
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -1,31 +0,0 @@
const value = {
description: null,
maximum: null,
minimum: null,
exclusiveMaximum:null,
exclusiveMinimum:null
}
const attr = {
description: {
name: '描述',
type: 'string',
},
maximum:{
name:'最大值',
type:'integer'
},
minimum:{
name:'最小值',
type:'integer'
},
exclusiveMaximum:{
name:'不包含最大值',
type:'boolean'
},
exclusiveMinimum:{
name:'不包含最小值',
type:'boolean'
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -1,31 +0,0 @@
const value = {
description: null,
maximum: null,
minimum: null,
exclusiveMaximum:null,
exclusiveMinimum:null
}
const attr = {
description: {
name: '描述',
type: 'string',
},
maximum:{
name:'最大值',
type:'number'
},
minimum:{
name:'最小值',
type:'number'
},
exclusiveMaximum:{
name:'不包含最大值',
type:'boolean'
},
exclusiveMinimum:{
name:'不包含最小值',
type:'boolean'
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -1,21 +0,0 @@
const value = {
description: null,
maxProperties: null,
minProperties: null
}
const attr = {
description: {
name: '描述',
type: 'string',
},
maxProperties:{
name:'最大元素个数',
type:'integer'
},
minProperties:{
name:'最小元素个数',
type:'integer'
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -1,32 +0,0 @@
const value = {
description: null,
maxLength: null,
minLength: null,
pattern: null,
format:null
}
const attr = {
description: {
name: '描述',
type: 'string',
},
maxLength:{
name:'最大字符数',
type:'integer'
},
minLength:{
name:'最小字符数',
type:'integer'
},
pattern: {
name: '正则表达式',
type:'string'
},
format: {
name:'格式',
type:'array',
enums:['date','date-time','email','hostname','ipv4','ipv6','uri']
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -1,17 +0,0 @@
import _object from './object'
import _string from './string'
import _array from './array'
import _boolean from './boolean'
import _integer from './integer'
import _number from './number'
const TYPE_NAME = ['string', 'number', 'integer','object', 'array', 'boolean']
const TYPE = {
'object': _object,
'string': _string,
'array': _array,
'boolean': _boolean,
'integer': _integer,
'number': _number
}
export {TYPE ,TYPE_NAME}

View File

@ -1,24 +0,0 @@
export function clearAttr(obj) {
for(let key in obj){
delete obj[key]
}
}
/**
* 快速拷贝两个对象的属性值
* @param {*} source
* @param {*} target
*/
export function copyAttr(source, target){
Object.keys(target).forEach(key=>{target[key]=source[key]})
}
export function isNull(ele){
if(typeof ele==='undefined'){
return true;
}else if(ele==null){
return true;
}else if(ele==''){
return true;
}
return false;
}

View File

@ -1,25 +0,0 @@
import JsonSchemaEditor from './editor/index'
const components = [
JsonSchemaEditor
]
// 定义 install 方法
const install = function (Vue) {
if (install.installed) return
install.installed = true
// 遍历并注册全局组件
components.map(component => {
Vue.component(component.name, component)
})
}
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
export default {
// 导出的对象必须具备一个 install 方法
install,
// 组件列表
...components
}

View File

@ -1,59 +0,0 @@
<template>
<el-row type="flex" justify="end">
<div class="table-page">
<el-pagination
:current-page="currentPage"
:page-sizes="pageSizes"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-row>
</template>
<script>
export default {
name: 'MsTablePagination',
props: {
page: Object,
currentPage: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 5
},
pageSizes: {
type: Array,
default: function() {
return [5, 10, 20, 50, 100]
}
},
total: {
type: Number,
default: 0
},
change: Function
},
methods: {
handleSizeChange: function(size) {
this.$emit('update:pageSize', size)
this.change()
},
handleCurrentChange(current) {
this.$emit('update:currentPage', current)
this.change()
}
}
}
</script>
<style scoped>
.table-page {
padding-top: 20px;
}
</style>

View File

@ -1,15 +0,0 @@
<template>
<div>
adfdsaf
</div>
</template>
<script>
export default {
name: "RouterSidebar"
}
</script>
<style scoped>
</style>

View File

@ -1,33 +0,0 @@
<template>
<div id="body">
<router-view name="sidebar" class="sidebar"/>
<router-view name="content" class="content"/>
</div>
</template>
<script>
export default {
name: "MsView"
}
</script>
<style scoped>
#body {
width: 100%;
height: calc(100vh - 56px);
/*background-color: #F5F5F5;*/
}
.sidebar {
position: relative;
width: 330px;
height: 100%;
max-width: 750px;
min-width: 250px;
}
.content {
width: 100%;
height: 100%;
}
</style>

View File

@ -1,57 +0,0 @@
import Vue from "vue";
import VueRouter from 'vue-router'
import RouterSidebar from "./RouterSidebar";
import Setting from "@/business/components/settings/router";
import Chart from "@/business/components/chart/router";
// import API from "@/business/components/api/router";
// import Performance from "@/business/components/performance/router";
import DataSet from "@/business/components/dataset/router";
import Track from "@/business/components/track/router";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{path: "/", redirect: '/setting/personsetting'},
{
path: "/sidebar",
components: {
sidebar: RouterSidebar
}
},
Setting,
Chart,
// Performance,
DataSet,
Track,
]
});
router.beforeEach((to, from, next) => {
//redirectLoginPath(to.fullPath);
next()
});
//重复点击导航路由报错
const routerPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
return routerPush.call(this, location).catch(error => error)
}
// 登入后跳转至原路径
// function redirectLoginPath(originPath) {
// let redirectUrl = sessionStorage.getItem('redirectUrl');
// let loginSuccess = sessionStorage.getItem('loginSuccess');
// sessionStorage.setItem('redirectUrl', originPath);
// if (redirectUrl && loginSuccess) {
// sessionStorage.removeItem('loginSuccess');
// router.push(redirectUrl);
// }
// sessionStorage.removeItem('loginSuccess');
// }
export default router

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