feat: UI更新 触发器 (#179)

* feat:新增api&适配

* feat:触发器

* add packages & 注释代码

* feat: webhook tips
This commit is contained in:
wang-liang0615 2023-09-26 18:25:04 +08:00 committed by GitHub
parent c2066b53f1
commit ab26033cea
23 changed files with 3895 additions and 2553 deletions

View File

@ -17,6 +17,8 @@
"@babel/plugin-syntax-import-meta": "^7.10.4",
"@riophae/vue-treeselect": "^0.4.0",
"@vue/composition-api": "^1.7.1",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^1.0.0",
"ant-design-vue": "^1.6.5",
"axios": "0.18.0",
"babel-eslint": "^8.2.2",

View File

@ -117,3 +117,11 @@ export function getEmployeeListByFilter(data) {
data
})
}
export function getNoticeByEmployeeIds(data) {
return axios({
url: '/common-setting/v1/employee/get_notice_by_ids',
method: 'post',
data
})
}

View File

@ -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
})
}

View File

@ -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
})
}

View File

@ -0,0 +1,2 @@
import NoticeContent from './index.vue'
export default NoticeContent

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,2 @@
import Webhook from './index.vue'
export default Webhook

View File

@ -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>

View File

@ -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>

View File

@ -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: {

View File

@ -1,59 +1,210 @@
<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">
<a-select-option v-for="attr in canAddTriggerAttr" :key="attr.id" :value="attr.id">{{
attr.alias || attr.name
}}</a-select-option>
</a-select>
</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="主题" prop="subject">
<a-input v-model="form.subject" />
<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="body">
<a-textarea v-model="form.body" :rows="3" />
<a-form-model-item label="备注" prop="description">
<a-input v-model="form.description" placeholder="请输入备注" />
</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 label="开启" prop="enable">
<a-switch v-model="form.enable" />
</a-form-model-item>
<a-form-model-item label="邮箱通知" prop="mail_to">
<a-textarea v-model="form.mail_to" :rows="3" placeholder="多个邮箱用逗号分隔" />
<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="筛选" 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="dateForm.before_days" :min="0" />
</a-form-model-item>
<a-form-model-item label="发送时间" prop="notify_at">
<a-time-picker v-model="dateForm.notify_at" format="HH:mm" valueFormat="HH:mm" />
</a-form-model-item>
</a-form-model>
</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="before_days">
<a-input-number v-model="form.before_days" :min="0" />
<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="notify_at">
<a-time-picker v-model="form.notify_at" format="HH:mm" valueFormat="HH:mm" />
<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>
</a-modal>
<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>
<span v-if="triggerAction === '2'" class="trigger-tips">{{ webhookTips }}</span>
<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 +212,55 @@ 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 }}',
webhookTips: '请求参数可以引用该模型的属性值,引用方法为: {{ 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 +274,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 +290,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 +404,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 +493,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>

View File

@ -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({
id: row.attr_id,
alias: _find ? _find.alias || _find.name : '',
trigger: { id: row.id, notify: row.notify },
has_trigger: true,
this.$refs.triggerForm.open(
{
id: row.attr_id,
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>

View File

@ -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>

View File

@ -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="用户">

View File

@ -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="用户">

View File

@ -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>

View File

@ -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
}
// 新增联合唯一

View File

@ -94,7 +94,7 @@
<ops-icon type="ops-setting-notice-wx" />
</div>
<div @click="handleBindWx" class="setting-person-bind-button">
{{ form.wx_id ? '重新绑定' : '绑定' }}
{{ form.notice_info.wechatApp ? '重新绑定' : '绑定' }}
</div>
</a-space>
</a-form-model-item>