mirror of https://github.com/veops/cmdb.git
feat:触发器
This commit is contained in:
parent
36a24d4a68
commit
788fefd9a1
|
@ -205,3 +205,11 @@ export function ciTypeFilterPermissions(type_id) {
|
|||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
export function getAllDagsName(params) {
|
||||
return axios({
|
||||
url: '/v1/dag/all_names',
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getCIHistory (ciId) {
|
||||
export function getCIHistory(ciId) {
|
||||
return axios({
|
||||
url: `/v0.1/history/ci/${ciId}`,
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
export function getCIHistoryTable (params) {
|
||||
export function getCIHistoryTable(params) {
|
||||
return axios({
|
||||
url: `/v0.1/history/records/attribute`,
|
||||
method: 'GET',
|
||||
|
@ -15,7 +15,7 @@ export function getCIHistoryTable (params) {
|
|||
})
|
||||
}
|
||||
|
||||
export function getRelationTable (params) {
|
||||
export function getRelationTable(params) {
|
||||
return axios({
|
||||
url: `/v0.1/history/records/relation`,
|
||||
method: 'GET',
|
||||
|
@ -23,7 +23,7 @@ export function getRelationTable (params) {
|
|||
})
|
||||
}
|
||||
|
||||
export function getCITypesTable (params) {
|
||||
export function getCITypesTable(params) {
|
||||
return axios({
|
||||
url: `/v0.1/history/ci_types`,
|
||||
method: 'GET',
|
||||
|
@ -31,10 +31,26 @@ export function getCITypesTable (params) {
|
|||
})
|
||||
}
|
||||
|
||||
export function getUsers (params) {
|
||||
export function getUsers(params) {
|
||||
return axios({
|
||||
url: `/v1/acl/users/employee`,
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function getCiTriggers(params) {
|
||||
return axios({
|
||||
url: `/v0.1/history/ci_triggers`,
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function getCiTriggersByCiId(ci_id, params) {
|
||||
return axios({
|
||||
url: `/v0.1/history/ci_triggers/${ci_id}`,
|
||||
method: 'GET',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
import NoticeContent from './index.vue'
|
||||
export default NoticeContent
|
|
@ -0,0 +1,199 @@
|
|||
<template>
|
||||
<div class="notice-content">
|
||||
<div class="notice-content-main">
|
||||
<Toolbar
|
||||
:editor="editor"
|
||||
:defaultConfig="{
|
||||
excludeKeys: [
|
||||
'emotion',
|
||||
'group-image',
|
||||
'group-video',
|
||||
'insertTable',
|
||||
'codeBlock',
|
||||
'blockquote',
|
||||
'fullScreen',
|
||||
],
|
||||
}"
|
||||
mode="default"
|
||||
/>
|
||||
<Editor class="notice-content-editor" :defaultConfig="editorConfig" mode="simple" @onCreated="onCreated" />
|
||||
<div class="notice-content-sidebar">
|
||||
<template v-if="needOld">
|
||||
<div class="notice-content-sidebar-divider">变更前</div>
|
||||
<div
|
||||
@dblclick="dblclickSidebar(`old_${attr.name}`, attr.alias || attr.name)"
|
||||
class="notice-content-sidebar-item"
|
||||
v-for="attr in attrList"
|
||||
:key="`old_${attr.id}`"
|
||||
:title="attr.alias || attr.name"
|
||||
>
|
||||
{{ attr.alias || attr.name }}
|
||||
</div>
|
||||
<div class="notice-content-sidebar-divider">变更后</div>
|
||||
</template>
|
||||
<div
|
||||
@dblclick="dblclickSidebar(attr.name, attr.alias || attr.name)"
|
||||
class="notice-content-sidebar-item"
|
||||
v-for="attr in attrList"
|
||||
:key="attr.id"
|
||||
:title="attr.alias || attr.name"
|
||||
>
|
||||
{{ attr.alias || attr.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import '@wangeditor/editor/dist/css/style.css'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
export default {
|
||||
name: 'NoticeContent',
|
||||
components: { Editor, Toolbar },
|
||||
props: {
|
||||
attrList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
needOld: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
editorConfig: { placeholder: '请输入通知内容', readOnly: this.readOnly },
|
||||
content: '',
|
||||
defaultParams: [],
|
||||
value2LabelMap: {},
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
const editor = this.editor
|
||||
if (editor == null) return
|
||||
editor.destroy() // 组件销毁时,及时销毁编辑器
|
||||
},
|
||||
methods: {
|
||||
onCreated(editor) {
|
||||
this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
|
||||
},
|
||||
getContent() {
|
||||
const html = _.cloneDeep(this.editor.getHtml())
|
||||
const _html = html.replace(
|
||||
/<span data-w-e-type="attachment" data-w-e-is-void data-w-e-is-inline.*?<\/span>/gm,
|
||||
(value) => {
|
||||
const _match = value.match(/(?<=data-attachmentValue=").*?(?=")/)
|
||||
return `{{${_match}}}`
|
||||
}
|
||||
)
|
||||
return { body_html: html, body: _html }
|
||||
},
|
||||
setContent(html) {
|
||||
this.editor.setHtml(html)
|
||||
},
|
||||
dblclickSidebar(value, label) {
|
||||
if (!this.readOnly) {
|
||||
this.editor.restoreSelection()
|
||||
|
||||
const node = {
|
||||
type: 'attachment',
|
||||
attachmentValue: value,
|
||||
attachmentLabel: `${label}`,
|
||||
children: [{ text: '' }],
|
||||
}
|
||||
this.editor.insertNode(node)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '~@/style/static.less';
|
||||
.notice-content {
|
||||
width: 100%;
|
||||
& &-main {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
position: relative;
|
||||
.notice-content-editor {
|
||||
height: 300px;
|
||||
width: 75%;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-top: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
.notice-content-sidebar {
|
||||
width: 25%;
|
||||
position: absolute;
|
||||
height: 300px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
overflow: auto;
|
||||
.notice-content-sidebar-divider {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
color: #afafaf;
|
||||
background-color: #fff;
|
||||
line-height: 20px;
|
||||
padding-left: 12px;
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-top: 1px solid #d1d1d1;
|
||||
top: 50%;
|
||||
transition: translateY(-50%);
|
||||
}
|
||||
&::before {
|
||||
left: 3px;
|
||||
width: 5px;
|
||||
}
|
||||
&::after {
|
||||
right: 3px;
|
||||
width: 78px;
|
||||
}
|
||||
}
|
||||
.notice-content-sidebar-item:first-child {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.notice-content-sidebar-item {
|
||||
line-height: 1.5;
|
||||
padding: 4px 12px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
&:hover {
|
||||
background-color: #custom_colors[color_2];
|
||||
color: #custom_colors[color_1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
@import '~@/style/static.less';
|
||||
|
||||
.notice-content {
|
||||
.w-e-bar {
|
||||
background-color: #custom_colors[color_2];
|
||||
}
|
||||
.w-e-text-placeholder {
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,144 @@
|
|||
<template>
|
||||
<div class="authorization-wrapper">
|
||||
<div class="authorization-header">
|
||||
<a-space>
|
||||
<span>Authorization Type</span>
|
||||
<a-select size="small" v-model="authorizationType" style="width: 200px" :showSearch="true">
|
||||
<a-select-option value="none">
|
||||
None
|
||||
</a-select-option>
|
||||
<a-select-option value="BasicAuth">
|
||||
Basic Auth
|
||||
</a-select-option>
|
||||
<a-select-option value="Bearer">
|
||||
Bearer
|
||||
</a-select-option>
|
||||
<a-select-option value="APIKey">
|
||||
APIKey
|
||||
</a-select-option>
|
||||
<a-select-option value="OAuth2.0">
|
||||
OAuth2.0
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-space>
|
||||
</div>
|
||||
<div style="margin-top:10px">
|
||||
<table v-if="authorizationType === 'BasicAuth'">
|
||||
<tr>
|
||||
<td><a-input class="authorization-input" v-model="BasicAuth.username" placeholder="用户名" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a-input class="authorization-input" v-model="BasicAuth.password" placeholder="密码" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table v-else-if="authorizationType === 'Bearer'">
|
||||
<tr>
|
||||
<td><a-input class="authorization-input" v-model="Bearer.token" placeholder="token" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table v-else-if="authorizationType === 'APIKey'">
|
||||
<tr>
|
||||
<td><a-input class="authorization-input" v-model="APIKey.key" placeholder="key" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a-input class="authorization-input" v-model="APIKey.value" placeholder="value" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table v-else-if="authorizationType === 'OAuth2.0'">
|
||||
<tr>
|
||||
<td><a-input class="authorization-input" v-model="OAuth2.client_id" placeholder="client_id" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a-input class="authorization-input" v-model="OAuth2.client_secret" placeholder="client_secret" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a-input
|
||||
class="authorization-input"
|
||||
v-model="OAuth2.authorization_base_url"
|
||||
placeholder="authorization_base_url"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a-input class="authorization-input" v-model="OAuth2.token_url" placeholder="token_url" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a-input class="authorization-input" v-model="OAuth2.redirect_url" placeholder="redirect_url" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a-input class="authorization-input" v-model="OAuth2.scope" placeholder="scope" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<a-empty
|
||||
v-else
|
||||
:image-style="{
|
||||
height: '60px',
|
||||
}"
|
||||
>
|
||||
<img slot="image" :src="require('@/assets/data_empty.png')" />
|
||||
<span slot="description"> 暂无请求认证 </span>
|
||||
</a-empty>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Authorization',
|
||||
data() {
|
||||
return {
|
||||
authorizationType: 'none',
|
||||
BasicAuth: {
|
||||
username: '',
|
||||
password: '',
|
||||
},
|
||||
Bearer: {
|
||||
token: '',
|
||||
},
|
||||
APIKey: {
|
||||
key: '',
|
||||
value: '',
|
||||
},
|
||||
OAuth2: {
|
||||
client_id: '',
|
||||
client_secret: '',
|
||||
authorization_base_url: '',
|
||||
token_url: '',
|
||||
redirect_url: '',
|
||||
scope: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.authorization-wrapper {
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table,
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #f3f4f6;
|
||||
}
|
||||
.authorization-input {
|
||||
border: none;
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,79 @@
|
|||
<template>
|
||||
<div class="body-wrapper">
|
||||
<div class="body-header">
|
||||
<!-- <a-space>
|
||||
<span>Content Type</span>
|
||||
<a-select size="small" v-model="contentType" style="width: 200px" :showSearch="true">
|
||||
<a-select-option value="none">
|
||||
None
|
||||
</a-select-option>
|
||||
<a-select-opt-group v-for="item in segmentedContentTypes" :key="item.title" :label="item.title">
|
||||
<a-select-option v-for="ele in item.contentTypes" :key="ele" :value="ele">
|
||||
{{ ele }}
|
||||
</a-select-option>
|
||||
</a-select-opt-group>
|
||||
</a-select>
|
||||
</a-space> -->
|
||||
</div>
|
||||
<div style="margin-top:10px">
|
||||
<vue-json-editor v-model="jsonData" :showBtns="false" :mode="'text'" />
|
||||
<!-- <a-empty
|
||||
v-else
|
||||
:image-style="{
|
||||
height: '60px',
|
||||
}"
|
||||
>
|
||||
<img slot="image" :src="require('@/assets/data_empty.png')" />
|
||||
<span slot="description"> 暂无请求体 </span>
|
||||
</a-empty> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import vueJsonEditor from 'vue-json-editor'
|
||||
|
||||
export default {
|
||||
name: 'Body',
|
||||
components: { vueJsonEditor },
|
||||
data() {
|
||||
const segmentedContentTypes = [
|
||||
{
|
||||
title: 'text',
|
||||
contentTypes: [
|
||||
'application/json',
|
||||
'application/ld+json',
|
||||
'application/hal+json',
|
||||
'application/vnd.api+json',
|
||||
'application/xml',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'structured',
|
||||
contentTypes: ['application/x-www-form-urlencoded', 'multipart/form-data'],
|
||||
},
|
||||
{
|
||||
title: 'others',
|
||||
contentTypes: ['text/html', 'text/plain'],
|
||||
},
|
||||
]
|
||||
return {
|
||||
segmentedContentTypes,
|
||||
// contentType: 'none',
|
||||
jsonData: {},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
<style lang="less">
|
||||
.body-wrapper {
|
||||
div.jsoneditor-menu {
|
||||
display: none;
|
||||
}
|
||||
div.jsoneditor {
|
||||
border-color: #f3f4f6;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,101 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="headers-header">
|
||||
<span>请求参数</span>
|
||||
<a-space>
|
||||
<a-tooltip title="清空">
|
||||
<ops-icon
|
||||
type="icon-xianxing-delete"
|
||||
@click="
|
||||
() => {
|
||||
headers = [
|
||||
{
|
||||
id: uuidv4(),
|
||||
key: '',
|
||||
value: '',
|
||||
},
|
||||
]
|
||||
}
|
||||
"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="新增">
|
||||
<a-icon type="plus" @click="add" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="headers-box">
|
||||
<table>
|
||||
<tr v-for="(item, index) in headers" :key="item.id">
|
||||
<td><a-input class="headers-input" v-model="item.key" :placeholder="`参数${index + 1}`" /></td>
|
||||
<td><a-input class="headers-input" v-model="item.value" :placeholder="`值${index + 1}`" /></td>
|
||||
<td>
|
||||
<a style="color:red">
|
||||
<ops-icon type="icon-xianxing-delete" @click="deleteParam(index)" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export default {
|
||||
name: 'Header',
|
||||
data() {
|
||||
return {
|
||||
headers: [
|
||||
{
|
||||
id: uuidv4(),
|
||||
key: '',
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
uuidv4,
|
||||
add() {
|
||||
this.headers.push({
|
||||
id: uuidv4(),
|
||||
key: '',
|
||||
value: '',
|
||||
})
|
||||
},
|
||||
deleteParam(index) {
|
||||
this.headers.splice(index, 1)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.headers-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.headers-box {
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table,
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #f3f4f6;
|
||||
}
|
||||
.headers-input {
|
||||
border: none;
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,2 @@
|
|||
import Webhook from './index.vue'
|
||||
export default Webhook
|
|
@ -0,0 +1,140 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-input-group compact>
|
||||
<treeselect
|
||||
:disable-branch-nodes="true"
|
||||
class="custom-treeselect custom-treeselect-bgcAndBorder"
|
||||
:style="{
|
||||
'--custom-height': '30px',
|
||||
lineHeight: '30px',
|
||||
'--custom-bg-color': '#fff',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
display: 'inline-block',
|
||||
width: '100px',
|
||||
}"
|
||||
v-model="method"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="methodList"
|
||||
value-consists-of="LEAF_PRIORITY"
|
||||
placeholder="请选择方式"
|
||||
>
|
||||
</treeselect>
|
||||
<a-input :style="{ display: 'inline-block', width: 'calc(100% - 100px)' }" v-model="url" />
|
||||
</a-input-group>
|
||||
<a-tabs>
|
||||
<a-tab-pane key="Parameters" tab="Parameters">
|
||||
<Parameters ref="Parameters" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="Body" tab="Body" force-render>
|
||||
<Body ref="Body" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="Headers" tab="Headers" force-render>
|
||||
<Header ref="Header" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="Authorization" tab="Authorization" force-render>
|
||||
<Authorization ref="Authorization" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import Parameters from './paramaters.vue'
|
||||
import Body from './body.vue'
|
||||
import Header from './header.vue'
|
||||
import Authorization from './authorization.vue'
|
||||
export default {
|
||||
name: 'Webhook',
|
||||
components: { Parameters, Body, Header, Authorization },
|
||||
data() {
|
||||
const methodList = [
|
||||
{
|
||||
id: 'GET',
|
||||
label: 'GET',
|
||||
},
|
||||
{
|
||||
id: 'POST',
|
||||
label: 'POST',
|
||||
},
|
||||
{
|
||||
id: 'PUT',
|
||||
label: 'PUT',
|
||||
},
|
||||
{
|
||||
id: 'DELETE',
|
||||
label: 'DELETE',
|
||||
},
|
||||
]
|
||||
return {
|
||||
methodList,
|
||||
method: 'GET',
|
||||
url: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getParams() {
|
||||
const parameters = {}
|
||||
this.$refs.Parameters.parameters.forEach((item) => {
|
||||
parameters[item.key] = item.value
|
||||
})
|
||||
const body = this.$refs.Body.jsonData
|
||||
const headers = {}
|
||||
this.$refs.Header.headers.forEach((item) => {
|
||||
headers[item.key] = item.value
|
||||
})
|
||||
let authorization = {}
|
||||
const type = this.$refs.Authorization.authorizationType
|
||||
if (type !== 'none') {
|
||||
if (type === 'OAuth2.0') {
|
||||
authorization = { ...this.$refs.Authorization['OAuth2'], type }
|
||||
} else {
|
||||
authorization = { ...this.$refs.Authorization[type], type }
|
||||
}
|
||||
}
|
||||
const { method, url } = this
|
||||
return { method, url, parameters, body, headers, authorization }
|
||||
},
|
||||
setParams(params) {
|
||||
console.log(2222, params)
|
||||
const { method, url, parameters, body, headers, authorization = {} } = params ?? {}
|
||||
this.method = method
|
||||
this.url = url
|
||||
this.$refs.Parameters.parameters =
|
||||
Object.keys(parameters).map((key) => {
|
||||
return {
|
||||
id: uuidv4(),
|
||||
key: key,
|
||||
value: parameters[key],
|
||||
}
|
||||
}) || []
|
||||
this.$refs.Body.jsonData = body
|
||||
this.$refs.Header.headers =
|
||||
Object.keys(headers).map((key) => {
|
||||
return {
|
||||
id: uuidv4(),
|
||||
key: key,
|
||||
value: headers[key],
|
||||
}
|
||||
}) || []
|
||||
const { type = 'none' } = authorization
|
||||
console.log(type)
|
||||
this.$refs.Authorization.authorizationType = type
|
||||
if (type !== 'none') {
|
||||
const _authorization = _.cloneDeep(authorization)
|
||||
delete _authorization.type
|
||||
if (type === 'OAuth2.0') {
|
||||
this.$refs.Authorization.OAuth2 = _authorization
|
||||
} else {
|
||||
this.$refs.Authorization[type] = _authorization
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
|
@ -0,0 +1,100 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="parameters-header">
|
||||
<span>请求参数</span>
|
||||
<a-space>
|
||||
<a-tooltip title="清空">
|
||||
<ops-icon
|
||||
type="icon-xianxing-delete"
|
||||
@click="
|
||||
() => {
|
||||
parameters = []
|
||||
}
|
||||
"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="新增">
|
||||
<a-icon type="plus" @click="add" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="parameters-box" v-if="parameters && parameters.length">
|
||||
<table>
|
||||
<tr v-for="(item, index) in parameters" :key="item.id">
|
||||
<td><a-input class="parameters-input" v-model="item.key" :placeholder="`参数${index + 1}`" /></td>
|
||||
<td><a-input class="parameters-input" v-model="item.value" :placeholder="`值${index + 1}`" /></td>
|
||||
<td>
|
||||
<a style="color:red">
|
||||
<ops-icon type="icon-xianxing-delete" @click="deleteParam(index)" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<a-empty
|
||||
v-else
|
||||
:image-style="{
|
||||
height: '60px',
|
||||
}"
|
||||
>
|
||||
<img slot="image" :src="require('@/assets/data_empty.png')" />
|
||||
<span slot="description"> 暂无请求参数 </span>
|
||||
<a-button @click="add" type="primary" size="small" icon="plus" class="ops-button-primary">
|
||||
添加
|
||||
</a-button>
|
||||
</a-empty>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export default {
|
||||
name: 'Parameters',
|
||||
data() {
|
||||
return {
|
||||
parameters: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add() {
|
||||
this.parameters.push({
|
||||
id: uuidv4(),
|
||||
key: '',
|
||||
value: '',
|
||||
})
|
||||
},
|
||||
deleteParam(index) {
|
||||
this.parameters.splice(index, 1)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.parameters-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.parameters-box {
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table,
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #f3f4f6;
|
||||
}
|
||||
.parameters-input {
|
||||
border: none;
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -86,6 +86,12 @@
|
|||
</vxe-table>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="tab_4">
|
||||
<span slot="tab"><ops-icon type="itsm_auto_trigger" />触发历史</span>
|
||||
<div :style="{ padding: '24px', height: 'calc(100vh - 44px)' }">
|
||||
<TriggerTable :ci_id="ci._id" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</CustomDrawer>
|
||||
</template>
|
||||
|
@ -97,6 +103,7 @@ import { getCIHistory } from '@/modules/cmdb/api/history'
|
|||
import { getCIById } from '@/modules/cmdb/api/ci'
|
||||
import CiDetailAttrContent from './ciDetailAttrContent.vue'
|
||||
import CiDetailRelation from './ciDetailRelation.vue'
|
||||
import TriggerTable from '../../operation_history/modules/triggerTable.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -104,6 +111,7 @@ export default {
|
|||
ElDescriptionsItem: DescriptionsItem,
|
||||
CiDetailAttrContent,
|
||||
CiDetailRelation,
|
||||
TriggerTable,
|
||||
},
|
||||
props: {
|
||||
typeId: {
|
||||
|
|
|
@ -1,59 +1,209 @@
|
|||
<template>
|
||||
<a-modal :title="title" :visible="visible" @cancel="handleCancel" @ok="handleOk">
|
||||
<a-space slot="footer">
|
||||
<CustomDrawer
|
||||
wrapClassName="trigger-form"
|
||||
:width="700"
|
||||
:title="title"
|
||||
:visible="visible"
|
||||
@close="handleCancel"
|
||||
@ok="handleOk"
|
||||
>
|
||||
<div class="custom-drawer-bottom-action">
|
||||
<a-button type="primary" ghost @click="handleCancel">取消</a-button>
|
||||
<a-button v-if="triggerId" type="danger" @click="handleDetele">删除</a-button>
|
||||
<a-button @click="handleOk" type="primary">确定</a-button>
|
||||
</a-space>
|
||||
<a-form-model ref="triggerForm" :model="form" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
|
||||
<a-form-model-item label="属性" prop="attr_id" :hidden="!isCreateFromTriggerTable || triggerId">
|
||||
<a-select v-model="form.attr_id">
|
||||
</div>
|
||||
<a-form-model ref="triggerForm" :model="form" :rules="rules" :label-col="{ span: 3 }" :wrapper-col="{ span: 18 }">
|
||||
<p><strong>基本信息</strong></p>
|
||||
<a-form-model-item label="名称" prop="name">
|
||||
<a-input v-model="form.name" placeholder="请输入名称" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="类型">
|
||||
<a-radio-group v-model="category">
|
||||
<a-radio-button :value="1">
|
||||
数据变更
|
||||
</a-radio-button>
|
||||
<a-radio-button :value="2">
|
||||
日期属性
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="备注" prop="description">
|
||||
<a-input v-model="form.description" placeholder="请输入备注" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="开启" prop="enable">
|
||||
<a-switch v-model="form.enable" />
|
||||
</a-form-model-item>
|
||||
<template v-if="category === 1">
|
||||
<p><strong>触发条件</strong></p>
|
||||
<a-form-model-item label="事件" prop="action">
|
||||
<a-radio-group v-model="form.action">
|
||||
<a-radio value="0">
|
||||
新增实例
|
||||
</a-radio>
|
||||
<a-radio value="1">
|
||||
删除实例
|
||||
</a-radio>
|
||||
<a-radio value="2">
|
||||
实例变更
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item v-if="form.action === '2'" label="属性" prop="attr_ids">
|
||||
<a-select v-model="form.attr_ids" show-search mode="multiple" placeholder="请选择属性(多选)">
|
||||
<a-select-option v-for="attr in attrList" :key="attr.id" :value="attr.id">{{
|
||||
attr.alias || attr.name
|
||||
}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="筛选" class="trigger-form-filter">
|
||||
<FilterComp
|
||||
ref="filterComp"
|
||||
:isDropdown="false"
|
||||
:canSearchPreferenceAttrList="attrList"
|
||||
@setExpFromFilter="setExpFromFilter"
|
||||
:expression="filterExp ? `q=${filterExp}` : ''"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
</template>
|
||||
</a-form-model>
|
||||
<template v-if="category === 2">
|
||||
<p><strong>触发条件</strong></p>
|
||||
<a-form-model
|
||||
ref="dateForm"
|
||||
:model="dateForm"
|
||||
:rules="dateFormRules"
|
||||
:label-col="{ span: 3 }"
|
||||
:wrapper-col="{ span: 18 }"
|
||||
>
|
||||
<a-form-model-item label="属性" prop="attr_id">
|
||||
<a-select v-model="dateForm.attr_id" placeholder="请选择属性(单选)">
|
||||
<a-select-option v-for="attr in canAddTriggerAttr" :key="attr.id" :value="attr.id">{{
|
||||
attr.alias || attr.name
|
||||
}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="主题" prop="subject">
|
||||
<a-input v-model="form.subject" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="内容" prop="body">
|
||||
<a-textarea v-model="form.body" :rows="3" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="微信通知" prop="wx_to">
|
||||
<a-select
|
||||
mode="tags"
|
||||
v-model="form.wx_to"
|
||||
placeholder="选择微信通知人"
|
||||
showSearch
|
||||
:filter-option="false"
|
||||
@search="filterChange"
|
||||
>
|
||||
<a-select-option v-for="item in filterWxUsers" :value="item['wx_id']" :key="item.id">
|
||||
<span>{{ item['nickname'] }}</span>
|
||||
<a-divider type="vertical" />
|
||||
<span>{{ item['wx_id'].length > 12 ? item['wx_id'].slice(0, 10) + '...' : item['wx_id'] }}</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="邮箱通知" prop="mail_to">
|
||||
<a-textarea v-model="form.mail_to" :rows="3" placeholder="多个邮箱用逗号分隔" />
|
||||
<a-form-model-item label="筛选" class="trigger-form-filter">
|
||||
<FilterComp
|
||||
ref="filterComp"
|
||||
:isDropdown="false"
|
||||
:canSearchPreferenceAttrList="attrList"
|
||||
@setExpFromFilter="setExpFromFilter"
|
||||
:expression="filterExp ? `q=${filterExp}` : ''"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="提前" prop="before_days">
|
||||
<a-input-number v-model="form.before_days" :min="0" />
|
||||
<a-input-number v-model="dateForm.before_days" :min="0" />
|
||||
天
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="发送时间" prop="notify_at">
|
||||
<a-time-picker v-model="form.notify_at" format="HH:mm" valueFormat="HH:mm" />
|
||||
<a-time-picker v-model="dateForm.notify_at" format="HH:mm" valueFormat="HH:mm" />
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</a-modal>
|
||||
</template>
|
||||
<p><strong>触发动作</strong></p>
|
||||
<a-radio-group
|
||||
v-model="triggerAction"
|
||||
:style="{ width: '100%', display: 'flex', justifyContent: 'space-around', marginBottom: '10px' }"
|
||||
>
|
||||
<a-radio value="1">
|
||||
通知
|
||||
</a-radio>
|
||||
<a-radio value="2">
|
||||
Webhook
|
||||
</a-radio>
|
||||
<!-- <a-radio value="3">
|
||||
DAG
|
||||
</a-radio> -->
|
||||
</a-radio-group>
|
||||
<a-form-model
|
||||
ref="notifiesForm"
|
||||
:model="notifies"
|
||||
:rules="notifiesRules"
|
||||
:label-col="{ span: 3 }"
|
||||
:wrapper-col="{ span: 18 }"
|
||||
v-if="triggerAction === '1'"
|
||||
>
|
||||
<a-form-model-item label=" " :colon="false">
|
||||
<span class="trigger-tips">{{ tips }}</span>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="收件人" prop="employee_ids" class="trigger-form-employee">
|
||||
<EmployeeTreeSelect multiple v-model="notifies.employee_ids" />
|
||||
<div class="trigger-form-custom-email">
|
||||
<a-textarea
|
||||
v-if="showCustomEmail"
|
||||
v-model="notifies.custom_email"
|
||||
placeholder="请输入邮箱,多个邮箱用;分隔"
|
||||
:rows="1"
|
||||
/>
|
||||
<a-button
|
||||
@click="
|
||||
() => {
|
||||
showCustomEmail = !showCustomEmail
|
||||
}
|
||||
"
|
||||
type="primary"
|
||||
size="small"
|
||||
class="ops-button-primary"
|
||||
>{{ `${showCustomEmail ? '删除' : '添加'}自定义收件人` }}</a-button
|
||||
>
|
||||
</div>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="通知标题" prop="subject">
|
||||
<a-input v-model="notifies.subject" placeholder="请输入通知标题" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="内容" prop="body" :wrapper-col="{ span: 21 }">
|
||||
<NoticeContent :needOld="category === 1 && form.action === '2'" :attrList="attrList" ref="noticeContent" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="通知方式" prop="method">
|
||||
<a-checkbox-group v-model="notifies.method">
|
||||
<a-checkbox value="wechatApp">
|
||||
微信
|
||||
</a-checkbox>
|
||||
<a-checkbox value="email">
|
||||
邮件
|
||||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
<div class="auto-complete-wrapper" v-if="triggerAction === '3'">
|
||||
<a-input
|
||||
id="auto-complete-wrapper-input"
|
||||
ref="input"
|
||||
v-model="searchValue"
|
||||
@focus="focusOnInput"
|
||||
@blur="handleBlurInput"
|
||||
allowClear
|
||||
>
|
||||
</a-input>
|
||||
<div id="auto-complete-wrapper-popover" class="auto-complete-wrapper-popover" v-if="isShow">
|
||||
<div
|
||||
class="auto-complete-wrapper-popover-item"
|
||||
@click="handleClickSelect(item)"
|
||||
v-for="item in filterList"
|
||||
:key="item.id"
|
||||
:title="item.label"
|
||||
>
|
||||
{{ item.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Webhook ref="webhook" style="margin-top:10px" v-if="triggerAction === '2'" />
|
||||
</CustomDrawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { getWX } from '../../api/perm'
|
||||
import { addTrigger, updateTrigger, deleteTrigger } from '../../api/CIType'
|
||||
import { addTrigger, updateTrigger, deleteTrigger, getAllDagsName } from '../../api/CIType'
|
||||
import FilterComp from '@/components/CMDBFilterComp'
|
||||
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
|
||||
import Webhook from '../../components/webhook'
|
||||
import NoticeContent from '../../components/noticeContent'
|
||||
import { getNoticeByEmployeeIds } from '@/api/employee'
|
||||
|
||||
export default {
|
||||
name: 'TriggerForm',
|
||||
components: { FilterComp, Webhook, EmployeeTreeSelect, NoticeContent },
|
||||
props: {
|
||||
CITypeId: {
|
||||
type: Number,
|
||||
|
@ -61,21 +211,54 @@ export default {
|
|||
},
|
||||
},
|
||||
data() {
|
||||
const defaultForm = {
|
||||
name: '',
|
||||
description: '',
|
||||
enable: true,
|
||||
action: '0',
|
||||
attr_ids: [],
|
||||
}
|
||||
const defaultDateForm = {
|
||||
attr_id: undefined,
|
||||
before_days: 0,
|
||||
notify_at: '08:00',
|
||||
}
|
||||
const defaultNotify = {
|
||||
employee_ids: undefined,
|
||||
custom_email: '',
|
||||
subject: '',
|
||||
body: '',
|
||||
method: ['wechatApp'],
|
||||
}
|
||||
return {
|
||||
defaultForm,
|
||||
defaultDateForm,
|
||||
defaultNotify,
|
||||
tips: '标题、内容可以引用该模型的属性值,引用方法为: {{ attr_name }}',
|
||||
visible: false,
|
||||
form: { attr_id: '', subject: '', body: '', wx_to: [], mail_to: '', before_days: 0, notify_at: '08:00' },
|
||||
category: 1,
|
||||
form: _.cloneDeep(defaultForm),
|
||||
rules: {
|
||||
attr_id: [{ required: true, message: '请选择属性' }],
|
||||
subject: [{ required: true, message: '请填写主题' }],
|
||||
body: [{ required: true, message: '请填写内容' }],
|
||||
name: [{ required: true, message: '请填写名称' }],
|
||||
},
|
||||
dateForm: _.cloneDeep(defaultDateForm),
|
||||
dateFormRules: {
|
||||
attr_id: [{ required: true, message: '请选择属性' }],
|
||||
},
|
||||
notifies: _.cloneDeep(defaultNotify),
|
||||
notifiesRules: {},
|
||||
WxUsers: [],
|
||||
filterValue: '',
|
||||
triggerId: null,
|
||||
attr_id: null,
|
||||
canAddTriggerAttr: [],
|
||||
isCreateFromTriggerTable: false,
|
||||
title: '新增触发器',
|
||||
attrList: [],
|
||||
filterExp: '',
|
||||
triggerAction: '1',
|
||||
searchValue: '',
|
||||
dags: [],
|
||||
isShow: false,
|
||||
dag_id: null,
|
||||
showCustomEmail: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -89,6 +272,15 @@ export default {
|
|||
user.username.toLowerCase().indexOf(this.filterValue.toLowerCase()) >= 0
|
||||
)
|
||||
},
|
||||
canAddTriggerAttr() {
|
||||
return this.attrList.filter((attr) => attr.value_type === '3' || attr.value_type === '4')
|
||||
},
|
||||
filterList() {
|
||||
if (this.searchValue) {
|
||||
return this.dags.filter((item) => item.label.toLowerCase().includes(this.searchValue.toLowerCase()))
|
||||
}
|
||||
return this.dags
|
||||
},
|
||||
},
|
||||
inject: {
|
||||
refresh: {
|
||||
|
@ -96,53 +288,107 @@ export default {
|
|||
default: null,
|
||||
},
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
createFromTriggerTable(canAddTriggerAttr) {
|
||||
this.visible = true
|
||||
this.getWxList()
|
||||
this.canAddTriggerAttr = canAddTriggerAttr
|
||||
this.triggerId = null
|
||||
this.isCreateFromTriggerTable = true
|
||||
this.title = '新增触发器'
|
||||
this.form = {
|
||||
attr_id: '',
|
||||
subject: '',
|
||||
body: '',
|
||||
wx_to: [],
|
||||
mail_to: '',
|
||||
before_days: 0,
|
||||
notify_at: '08:00',
|
||||
}
|
||||
async getDags() {
|
||||
await getAllDagsName().then((res) => {
|
||||
this.dags = res.map((dag) => ({ id: dag[1], label: dag[0] }))
|
||||
})
|
||||
},
|
||||
open(property) {
|
||||
createFromTriggerTable(attrList) {
|
||||
this.visible = true
|
||||
this.getWxList()
|
||||
this.getDags()
|
||||
this.attrList = attrList
|
||||
this.triggerId = null
|
||||
this.title = '新增触发器'
|
||||
this.form = _.cloneDeep(this.defaultForm)
|
||||
this.dateForm = _.cloneDeep(this.defaultDateForm)
|
||||
this.notifies = _.cloneDeep(this.defaultNotify)
|
||||
this.category = 1
|
||||
this.triggerAction = '1'
|
||||
this.filterExp = ''
|
||||
this.$nextTick(() => {
|
||||
this.$refs.filterComp.visibleChange(true, false)
|
||||
setTimeout(() => {
|
||||
this.$refs.noticeContent.setContent('')
|
||||
}, 100)
|
||||
})
|
||||
},
|
||||
async open(property, attrList) {
|
||||
this.visible = true
|
||||
this.getWxList()
|
||||
await this.getDags()
|
||||
this.attrList = attrList
|
||||
if (property.has_trigger) {
|
||||
this.triggerId = property.trigger.id
|
||||
this.title = `编辑触发器 ${property.alias || property.name}`
|
||||
this.form = {
|
||||
...property.trigger.notify,
|
||||
attr_id: property.id,
|
||||
mail_to: property.trigger.notify.mail_to ? property.trigger.notify.mail_to.join(',') : '',
|
||||
const { name, description, enable, action = '0', attr_ids, filter = '' } = property?.trigger?.option ?? {}
|
||||
this.filterExp = filter
|
||||
this.$nextTick(() => {
|
||||
this.$refs.filterComp.visibleChange(true, false)
|
||||
})
|
||||
this.form = { name, description, enable, action, attr_ids }
|
||||
const { attr_id } = property?.trigger ?? {}
|
||||
if (attr_id) {
|
||||
this.category = 2
|
||||
const { before_days, notify_at } = property?.trigger?.option?.notifies ?? {}
|
||||
this.dateForm = {
|
||||
attr_id,
|
||||
before_days,
|
||||
notify_at,
|
||||
}
|
||||
} else {
|
||||
this.category = 1
|
||||
}
|
||||
const { notifies = undefined, webhooks = undefined, dag_id = undefined } = property?.trigger?.option ?? {}
|
||||
if (webhooks) {
|
||||
this.triggerAction = '2'
|
||||
this.$nextTick(() => {
|
||||
this.$refs.webhook.setParams(webhooks)
|
||||
})
|
||||
} else if (dag_id) {
|
||||
this.triggerAction = '3'
|
||||
this.dag_id = dag_id
|
||||
const _find = this.dags.find((item) => item.id === dag_id)
|
||||
this.searchValue = _find?.label
|
||||
} else if (notifies) {
|
||||
this.triggerAction = '1'
|
||||
const { tos = [], subject = '', body_html = '', method = ['wechatApp'] } =
|
||||
property?.trigger?.option?.notifies ?? {}
|
||||
const employee_ids = property?.trigger?.option?.employee_ids ?? undefined
|
||||
const custom_email =
|
||||
tos
|
||||
.filter((t) => !t.employee_id)
|
||||
.map((t) => t.email)
|
||||
.join(';') ?? ''
|
||||
|
||||
if (custom_email) {
|
||||
this.showCustomEmail = true
|
||||
}
|
||||
if (body_html) {
|
||||
setTimeout(() => {
|
||||
this.$refs.noticeContent.setContent(body_html)
|
||||
}, 100)
|
||||
}
|
||||
this.notifies = { employee_ids, custom_email, subject, method }
|
||||
}
|
||||
} else {
|
||||
this.title = `新增触发器 ${property.alias || property.name}`
|
||||
this.triggerId = null
|
||||
this.form = {
|
||||
attr_id: property.id,
|
||||
subject: '',
|
||||
body: '',
|
||||
wx_to: [],
|
||||
mail_to: '',
|
||||
before_days: 0,
|
||||
notify_at: '08:00',
|
||||
}
|
||||
this.form = _.cloneDeep(this.defaultForm)
|
||||
}
|
||||
},
|
||||
handleCancel() {
|
||||
this.$refs.triggerForm.clearValidate()
|
||||
this.$refs.triggerForm.resetFields()
|
||||
this.filterValue = ''
|
||||
this.form = _.cloneDeep(this.defaultForm)
|
||||
this.dateForm = _.cloneDeep(this.defaultDateForm)
|
||||
this.notifies = _.cloneDeep(this.defaultNotify)
|
||||
this.category = 1
|
||||
this.triggerAction = '1'
|
||||
this.filterExp = ''
|
||||
this.visible = false
|
||||
},
|
||||
getWxList() {
|
||||
|
@ -156,12 +402,67 @@ export default {
|
|||
handleOk() {
|
||||
this.$refs.triggerForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
const { mail_to, attr_id } = this.form
|
||||
this.$refs.filterComp.handleSubmit()
|
||||
const { name, description, enable, action, attr_ids } = this.form
|
||||
const params = {
|
||||
attr_id,
|
||||
notify: { ...this.form, mail_to: mail_to ? mail_to.split(',') : undefined },
|
||||
attr_id: '',
|
||||
option: {
|
||||
filter: this.filterExp,
|
||||
name,
|
||||
description,
|
||||
enable,
|
||||
},
|
||||
}
|
||||
switch (this.triggerAction) {
|
||||
case '1':
|
||||
const { employee_ids, custom_email, subject, method } = this.notifies
|
||||
const { body, body_html } = this.$refs.noticeContent.getContent()
|
||||
let tos = []
|
||||
if (employee_ids && employee_ids.length) {
|
||||
await getNoticeByEmployeeIds({ employee_ids: employee_ids.map((item) => item.split('-')[1]) }).then(
|
||||
(res) => {
|
||||
tos = tos.concat(res)
|
||||
}
|
||||
)
|
||||
params.option.employee_ids = employee_ids
|
||||
}
|
||||
if (this.showCustomEmail) {
|
||||
custom_email.split(';').forEach((email) => {
|
||||
tos.push({ email })
|
||||
})
|
||||
}
|
||||
if (this.category === 2) {
|
||||
const { before_days, notify_at } = this.dateForm
|
||||
params.option.notifies = { tos, subject, body, body_html, method, before_days, notify_at }
|
||||
} else {
|
||||
params.option.notifies = { tos, subject, body, body_html, method }
|
||||
}
|
||||
break
|
||||
case '2':
|
||||
const webhooks = this.$refs.webhook.getParams()
|
||||
params.option.webhooks = webhooks
|
||||
break
|
||||
case '3':
|
||||
params.option.dag_id = this.dag_id
|
||||
break
|
||||
}
|
||||
if (this.category === 1) {
|
||||
params.option.action = action
|
||||
if (action === '2') {
|
||||
params.option.attr_ids = attr_ids
|
||||
}
|
||||
}
|
||||
if (this.category === 2) {
|
||||
this.$refs.dateForm.validate((valid) => {
|
||||
if (valid) {
|
||||
const { attr_id, before_days, notify_at } = this.dateForm
|
||||
params.attr_id = attr_id
|
||||
params.option.notifies = { ..._.cloneDeep(params.option.notifies), before_days, notify_at }
|
||||
} else {
|
||||
throw Error()
|
||||
}
|
||||
})
|
||||
}
|
||||
delete params.notify.attr_id
|
||||
if (this.triggerId) {
|
||||
await updateTrigger(this.CITypeId, this.triggerId, params)
|
||||
} else {
|
||||
|
@ -190,8 +491,85 @@ export default {
|
|||
},
|
||||
})
|
||||
},
|
||||
setExpFromFilter(filterExp) {
|
||||
if (filterExp) {
|
||||
this.filterExp = `${filterExp}`
|
||||
} else {
|
||||
this.filterExp = ''
|
||||
}
|
||||
},
|
||||
handleBlurInput() {
|
||||
setTimeout(() => {
|
||||
this.isShow = false
|
||||
}, 100)
|
||||
},
|
||||
focusOnInput() {
|
||||
this.isShow = true
|
||||
},
|
||||
handleClickSelect(item) {
|
||||
this.searchValue = item.label
|
||||
this.dag_id = item.id
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<style lang="less">
|
||||
.trigger-form {
|
||||
.ant-form-item {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.trigger-form-employee,
|
||||
.trigger-form-filter {
|
||||
.ant-form-item-control {
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
.trigger-form-filter {
|
||||
.table-filter-add {
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '~@/style/static.less';
|
||||
|
||||
.auto-complete-wrapper {
|
||||
position: relative;
|
||||
margin-left: 25px;
|
||||
width: 250px;
|
||||
margin-top: 20px;
|
||||
.auto-complete-wrapper-popover {
|
||||
position: fixed;
|
||||
width: 250px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
background-color: #fff;
|
||||
z-index: 10;
|
||||
box-shadow: 0 2px 8px #00000026;
|
||||
.auto-complete-wrapper-popover-item {
|
||||
.ops_popover_item();
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trigger-form-custom-email {
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.trigger-tips {
|
||||
border: 1px solid #d4380d;
|
||||
background-color: #fff2e8;
|
||||
padding: 2px 10px;
|
||||
border-radius: 4px;
|
||||
color: #d4380d;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
icon="plus"
|
||||
>新增触发器</a-button
|
||||
>
|
||||
<span class="trigger-tips">{{ tips }}</span>
|
||||
</div>
|
||||
<vxe-table
|
||||
stripe
|
||||
|
@ -21,26 +20,40 @@
|
|||
:max-height="windowHeight - 180"
|
||||
class="ops-stripe-table"
|
||||
>
|
||||
<vxe-column field="attr_name" title="属性名"></vxe-column>
|
||||
<vxe-column field="notify.subject" title="主题"></vxe-column>
|
||||
<vxe-column field="notify.body" title="内容"></vxe-column>
|
||||
<vxe-column field="notify.wx_to" title="微信通知">
|
||||
<vxe-column field="option.name" title="名称"></vxe-column>
|
||||
<vxe-column field="option.description" title="备注"></vxe-column>
|
||||
<vxe-column field="type" title="类型">
|
||||
<template #default="{ row }">
|
||||
<span v-for="(person, index) in row.notify.wx_to" :key="person + index">[{{ person }}]</span>
|
||||
<span v-if="row.attr_id">日期属性</span>
|
||||
<span v-else>数据变更</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="notify.mail_to" title="邮件通知">
|
||||
<vxe-column field="option.enable" title="开启">
|
||||
<template #default="{ row }">
|
||||
<span v-for="(email, index) in row.notify.mail_to" :key="email + index">[{{ email }}]</span>
|
||||
<a-switch :checked="row.option.enable" @click="changeEnable(row)"></a-switch>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="notify.before_days" title="提前">
|
||||
|
||||
<!-- <vxe-column field="attr_name" title="属性名"></vxe-column>
|
||||
<vxe-column field="option.subject" title="主题"></vxe-column>
|
||||
<vxe-column field="option.body" title="内容"></vxe-column>
|
||||
<vxe-column field="option.wx_to" title="微信通知">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.notify.before_days">{{ row.notify.before_days }}天</span>
|
||||
<span v-for="(person, index) in row.option.wx_to" :key="person + index">[{{ person }}]</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="notify.notify_at" title="发送时间"></vxe-column>
|
||||
<vxe-column field="operation" title="操作" width="200px" align="center">
|
||||
<vxe-column field="option.mail_to" title="邮件通知">
|
||||
<template #default="{ row }">
|
||||
<span v-for="(email, index) in row.option.mail_to" :key="email + index">[{{ email }}]</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="option.before_days" title="提前">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.option.before_days">{{ row.option.before_days }}天</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="option.notify_at" title="发送时间"></vxe-column> -->
|
||||
<vxe-column field="operation" title="操作" width="80px" align="center">
|
||||
<template #default="{ row }">
|
||||
<a-space>
|
||||
<a @click="handleEdit(row)"><a-icon type="edit"/></a>
|
||||
|
@ -54,9 +67,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { getTriggerList, deleteTrigger } from '../../api/CIType'
|
||||
import _ from 'lodash'
|
||||
import { getTriggerList, deleteTrigger, updateTrigger } from '../../api/CIType'
|
||||
import { getCITypeAttributesById } from '../../api/CITypeAttr'
|
||||
import TriggerForm from './triggerForm.vue'
|
||||
import { getAllDepAndEmployee } from '@/api/company'
|
||||
|
||||
export default {
|
||||
name: 'TriggerTable',
|
||||
components: { TriggerForm },
|
||||
|
@ -68,24 +84,33 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
tips: '主题、内容、微信通知和邮件通知都可以引用该模型的属性值,引用方法为: {{ attr_name }}',
|
||||
tableData: [],
|
||||
attrList: [],
|
||||
allTreeDepAndEmp: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
windowHeight() {
|
||||
return this.$store.state.windowHeight
|
||||
},
|
||||
canAddTriggerAttr() {
|
||||
return this.attrList.filter((attr) => attr.value_type === '3' || attr.value_type === '4')
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return { refresh: this.getTableData }
|
||||
return {
|
||||
refresh: this.getTableData,
|
||||
provide_allTreeDepAndEmp: () => {
|
||||
return this.allTreeDepAndEmp
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getAllDepAndEmployee()
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
getAllDepAndEmployee() {
|
||||
getAllDepAndEmployee({ block: 0 }).then((res) => {
|
||||
this.allTreeDepAndEmp = res
|
||||
})
|
||||
},
|
||||
async getTableData() {
|
||||
const [triggerList, attrList] = await Promise.all([
|
||||
getTriggerList(this.CITypeId),
|
||||
|
@ -101,7 +126,7 @@ export default {
|
|||
this.attrList = attrList.attributes
|
||||
},
|
||||
handleAddTrigger() {
|
||||
this.$refs.triggerForm.createFromTriggerTable(this.canAddTriggerAttr)
|
||||
this.$refs.triggerForm.createFromTriggerTable(this.attrList)
|
||||
},
|
||||
handleDetele(id) {
|
||||
const that = this
|
||||
|
@ -117,12 +142,23 @@ export default {
|
|||
})
|
||||
},
|
||||
handleEdit(row) {
|
||||
const _find = this.attrList.find((attr) => attr.id === row.attr_id)
|
||||
this.$refs.triggerForm.open({
|
||||
this.$refs.triggerForm.open(
|
||||
{
|
||||
id: row.attr_id,
|
||||
alias: _find ? _find.alias || _find.name : '',
|
||||
trigger: { id: row.id, notify: row.notify },
|
||||
alias: row?.option?.name ?? '',
|
||||
trigger: { id: row.id, attr_id: row.attr_id, option: row.option },
|
||||
has_trigger: true,
|
||||
},
|
||||
this.attrList
|
||||
)
|
||||
},
|
||||
changeEnable(row) {
|
||||
const _row = _.cloneDeep(row)
|
||||
delete _row.id
|
||||
const enable = row?.option?.enable ?? true
|
||||
_row.option.enable = !enable
|
||||
updateTrigger(this.CITypeId, row.id, _row).then(() => {
|
||||
this.getTableData()
|
||||
})
|
||||
},
|
||||
},
|
||||
|
@ -132,13 +168,5 @@ export default {
|
|||
<style lang="less" scoped>
|
||||
.ci-types-triggers {
|
||||
padding: 16px 24px 24px;
|
||||
.trigger-tips {
|
||||
border: 1px solid #d4380d;
|
||||
background-color: #fff2e8;
|
||||
padding: 2px 10px;
|
||||
border-radius: 4px;
|
||||
color: #d4380d;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
<a-tab-pane key="3" tab="模型变更">
|
||||
<type-table></type-table>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="4" tab="触发历史">
|
||||
<TriggerTable></TriggerTable>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-card>
|
||||
</div>
|
||||
|
@ -20,18 +23,20 @@
|
|||
import CiTable from './modules/ciTable.vue'
|
||||
import RelationTable from './modules/relation.vue'
|
||||
import TypeTable from './modules/typeTable.vue'
|
||||
import TriggerTable from './modules/triggerTable.vue'
|
||||
export default {
|
||||
name: 'Index',
|
||||
data() {
|
||||
return {
|
||||
userList: []
|
||||
}
|
||||
},
|
||||
name: 'OperationHistory',
|
||||
components: {
|
||||
CiTable,
|
||||
RelationTable,
|
||||
TypeTable
|
||||
TypeTable,
|
||||
TriggerTable,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
userList: [],
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
:max-height="`${windowHeight - windowHeightMinus}px`"
|
||||
:span-method="mergeRowMethod"
|
||||
:scroll-y="{enabled: false}"
|
||||
class="ops-unstripe-table"
|
||||
>
|
||||
<vxe-column field="created_at" width="159px" title="操作时间"></vxe-column>
|
||||
<vxe-column field="user" width="100px" title="用户">
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
<vxe-table
|
||||
ref="xTable"
|
||||
:loading="loading"
|
||||
border
|
||||
size="small"
|
||||
show-overflow="tooltip"
|
||||
show-header-overflow="tooltip"
|
||||
|
@ -19,6 +18,8 @@
|
|||
row-id="_XID"
|
||||
:scroll-y="{ enabled: false }"
|
||||
:span-method="mergeRowMethod"
|
||||
stripe
|
||||
class="ops-stripe-table"
|
||||
>
|
||||
<vxe-column field="created_at" width="159px" title="操作时间"></vxe-column>
|
||||
<vxe-column field="user" width="100px" title="用户">
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<div>
|
||||
<vxe-table
|
||||
show-overflow
|
||||
show-header-overflow
|
||||
stripe
|
||||
size="small"
|
||||
class="ops-stripe-table"
|
||||
:data="tableData"
|
||||
v-bind="ci_id ? { maxHeight: `${windowHeight - 94}px` } : { height: `${windowHeight - 225}px` }"
|
||||
>
|
||||
<vxe-column field="trigger_name" title="触发器名称"> </vxe-column>
|
||||
<vxe-column field="type" title="类型">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.trigger && row.trigger.attr_id">日期属性</span>
|
||||
<span v-else-if="row.trigger && !row.trigger.attr_id">数据变更</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column title="事件">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.operate_type === '0'">新增实例</span>
|
||||
<span v-else-if="row.operate_type === '1'">删除实例</span>
|
||||
<span v-else-if="row.operate_type === '2'">实例变更</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column title="动作">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.webhook">Webhook</span>
|
||||
<span v-else-if="row.notify">通知</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column title="触发时间">
|
||||
<template #default="{row}">
|
||||
{{ row.updated_at || row.created_at }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
<div :style="{ textAlign: 'right' }" v-if="!ci_id">
|
||||
<a-pagination
|
||||
size="small"
|
||||
show-size-changer
|
||||
show-quick-jumper
|
||||
:page-size-options="pageSizeOptions"
|
||||
:current="tablePage.currentPage"
|
||||
:total="tablePage.totalResult"
|
||||
:show-total="(total, range) => `共 ${total} 条记录`"
|
||||
:page-size="tablePage.pageSize"
|
||||
:default-current="1"
|
||||
@change="pageOrSizeChange"
|
||||
@showSizeChange="pageOrSizeChange"
|
||||
>
|
||||
</a-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCiTriggers, getCiTriggersByCiId } from '../../../api/history'
|
||||
export default {
|
||||
name: 'TriggerTable',
|
||||
props: {
|
||||
ci_id: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
tablePage: {
|
||||
currentPage: 1,
|
||||
pageSize: 50,
|
||||
totalResult: 0,
|
||||
},
|
||||
pageSizeOptions: ['50', '100', '200'],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
windowHeight() {
|
||||
return this.$store.state.windowHeight
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.updateTableData()
|
||||
},
|
||||
methods: {
|
||||
updateTableData(currentPage = 1, pageSize = this.tablePage.pageSize) {
|
||||
const params = { page: currentPage, page_size: pageSize }
|
||||
if (this.ci_id) {
|
||||
getCiTriggersByCiId(this.ci_id, params).then((res) => {
|
||||
this.tableData = res.items.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
trigger: res.id2trigger[item.trigger_id],
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
getCiTriggers(params).then((res) => {
|
||||
this.tableData = res?.result || []
|
||||
this.tablePage = {
|
||||
...this.tablePage,
|
||||
currentPage: res.page,
|
||||
pageSize: res.page_size,
|
||||
totalResult: res.numfound,
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
pageOrSizeChange(currentPage, pageSize) {
|
||||
this.updateTableData(currentPage, pageSize)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
|
@ -9,13 +9,14 @@
|
|||
<vxe-table
|
||||
ref="xTable"
|
||||
:loading="loading"
|
||||
border
|
||||
resizable
|
||||
:data="tableData"
|
||||
:max-height="`${windowHeight - windowHeightMinus}px`"
|
||||
row-id="_XID"
|
||||
size="small"
|
||||
:row-config="{isHover: true}"
|
||||
stripe
|
||||
class="ops-stripe-table"
|
||||
>
|
||||
<vxe-column field="created_at" width="159px" title="操作时间"></vxe-column>
|
||||
<vxe-column field="user" width="116px" title="用户"></vxe-column>
|
||||
|
@ -60,7 +61,7 @@
|
|||
</vxe-column>
|
||||
<vxe-column field="type_id" title="模型" width="150px">
|
||||
<template #default="{ row }">
|
||||
{{ row.operate_type === '删除模型' ? row.change.alias : row.type_id }}
|
||||
{{ row.operate_type === '删除模型' ? row.change.alias : row.type_id}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="changeDescription" title="描述">
|
||||
|
@ -194,7 +195,7 @@ export default {
|
|||
return this.$store.state.windowHeight
|
||||
},
|
||||
windowHeightMinus() {
|
||||
return this.isExpand ? 396 : 331
|
||||
return this.isExpand ? 396 : 335
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
@ -377,15 +378,15 @@ export default {
|
|||
}
|
||||
// 新增触发器
|
||||
case '6': {
|
||||
item.changeDescription = `属性ID:${item.change.attr_id},提前:${item.change.notify.before_days}天,主题:${item.change.notify.subject}\n内容:${item.change.notify.body}\n通知时间:${item.change.notify.notify_at}`
|
||||
item.changeDescription = `属性ID:${item.change.attr_id},提前:${item.change.option.before_days}天,主题:${item.change.option.subject}\n内容:${item.change.option.body}\n通知时间:${item.change.option.notify_at}`
|
||||
break
|
||||
}
|
||||
// 修改触发器
|
||||
case '7': {
|
||||
item.changeArr = []
|
||||
for (const key in item.change.old.notify) {
|
||||
const newVal = item.change.new.notify[key]
|
||||
const oldVal = item.change.old.notify[key]
|
||||
for (const key in item.change.old.option) {
|
||||
const newVal = item.change.new.option[key]
|
||||
const oldVal = item.change.old.option[key]
|
||||
if (!_.isEqual(newVal, oldVal) && key !== 'updated_at') {
|
||||
const str = ` [ ${key} : 由 ${oldVal} 改为 ${newVal} ] `
|
||||
item.changeDescription += str
|
||||
|
@ -397,7 +398,7 @@ export default {
|
|||
}
|
||||
// 删除触发器
|
||||
case '8': {
|
||||
item.changeDescription = `属性ID:${item.change.attr_id},提前:${item.change.notify.before_days}天,主题:${item.change.notify.subject}\n内容:${item.change.notify.body}\n通知时间:${item.change.notify.notify_at}`
|
||||
item.changeDescription = `属性ID:${item.change.attr_id},提前:${item.change.option.before_days}天,主题:${item.change.option.subject}\n内容:${item.change.option.body}\n通知时间:${item.change.option.notify_at}`
|
||||
break
|
||||
}
|
||||
// 新增联合唯一
|
||||
|
|
Loading…
Reference in New Issue