feat: 通知模板页面

This commit is contained in:
JiangQiming 2023-01-16 18:00:45 +08:00
parent ad54e34a93
commit cd146583a1
14 changed files with 996 additions and 3 deletions

View File

@ -0,0 +1,12 @@
import { patch, post, get } from '@/utils/request'
export default {
// 列表
list: (data: any) => post(`/notifier/template/_query`, data),
// 详情
detail: (id: string): any => get(`/notifier/template/${id}`),
// 新增
save: (data: any) => post(`/notifier/template`, data),
// 修改
update: (data: any) => patch(`/notifier/template`, data)
}

View File

@ -0,0 +1,43 @@
import './index.less';
const AliyunSms = () => {
return (
<div class="doc">
<div class="url">
<a
href="https://dysms.console.aliyun.com"
target="_blank"
rel="noopener noreferrer"
>
https://dysms.console.aliyun.com
</a>
</div>
<h1>1. </h1>
<div>
使
</div>
<h1>2.</h1>
<div>
<h2> 1</h2>
<div> 使</div>
<h2> 2</h2>
<div> </div>
<h2> 3</h2>
<div>
{' '}
</div>
<h2> 4</h2>
<div> </div>
<h2> 5</h2>
<div>
</div>
</div>
</div>
);
};
export default AliyunSms;

View File

@ -0,0 +1,43 @@
import './index.less';
const AliyunVoice = () => {
return (
<div class="doc">
<div class="url">
<a href="https://account.console.aliyun.com" target="_blank" rel="noopener noreferrer">
https://account.console.aliyun.com
</a>
</div>
<h1>1. </h1>
<div>
使
</div>
<h1>2.</h1>
<div>
<h2>1</h2>
<div> 使</div>
<h2>2</h2>
<div> </div>
<h2> 3ID</h2>
<div> ID标识</div>
<h2> 4</h2>
<div> </div>
<div>使</div>
<div>使</div>
<h2> 5</h2>
<div> </div>
<h2> 6</h2>
<div> 3</div>
<h2> 7</h2>
<div>
${'{name}'}
便
</div>
</div>
</div>
);
};
export default AliyunVoice;

View File

@ -0,0 +1,54 @@
import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';
const DingTalk = () => {
const agentId = getImage('/notice/doc/template/dingTalk-message/01-Agentid.jpg');
// const userId = getImage('/notice/doc/template/dingTalk-message/02-user-id.jpg');
// const dept = getImage('/notice/doc/template/dingTalk-message/03-dept.jpg');
const a = '{name}';
return (
<div class="doc">
<div class="url">
<a href="https://open-dev.dingtalk.com" target="_blank" rel="noopener noreferrer">
https://open-dev.dingtalk.com
</a>
<br />
<a href="https://www.dingtalk.com" target="_blank" rel="noopener noreferrer">
https://www.dingtalk.com
</a>
</div>
<h1>1. </h1>
<div>
<div>使</div>
</div>
<h1> 2.</h1>
<h2> 1</h2>
<div> 使</div>
<h2> 2Agentid</h2>
<div> </div>
<div> ----</div>
<div class="image">
<Image width="100%" src={agentId} />
</div>
<h2> 3</h2>
<div></div>
{/*<div> 收信人ID获取路径“钉钉管理后台”--“通讯录”--“查看用户”</div>*/}
{/*<div> 收信部门ID获取路径“钉钉管理后台”--“通讯录”--“编辑部门”</div>*/}
{/*<div class="image">*/}
{/* <Image width="100%" src={userId} />*/}
{/* <Image width="100%" src={dept} />*/}
{/*</div>*/}
<h2> 4</h2>
<div>
${a}
便
</div>
</div>
);
};
export default DingTalk;

View File

@ -0,0 +1,35 @@
import './index.less';
const DingTalkRebot = () => {
const b = '{name}';
return (
<div class="doc">
<div class="url">
<a href="https://open-dev.dingtalk.com" target="_blank" rel="noopener noreferrer">
https://open-dev.dingtalk.com
</a>
</div>
<h1>1. </h1>
<div>
</div>
<div>
使
</div>
<h1>2.</h1>
<div>
<h2> 1</h2>
<div> 使</div>
<h2> 2</h2>
<div> textmarkdownlink3种</div>
<h2> 3</h2>
<div>
${b}
便
</div>
</div>
</div>
);
};
export default DingTalkRebot;

View File

@ -0,0 +1,30 @@
import './index.less';
const Email = () => {
const a = '{标题}';
const b = '{name}';
return (
<div class="doc">
<h1>1. </h1>
<div>
</div>
<h1>2.</h1>
<div>
{/* <h2> 1</h2>
<div></div> */}
<h2> 1</h2>
<div>${a}</div>
<h2> 2</h2>
<div> </div>
<h2> 3</h2>
<div>
${b}
便
</div>
</div>
</div>
);
};
export default Email;

View File

@ -0,0 +1,18 @@
import './index.less';
const Webhook = () => {
return (
<div class="doc">
<h1>1. </h1>
<div>
</div>
<h1>2.</h1>
<div>
1 ,
使webhook通知时URL地址POST方式发送
</div>
</div>
);
};
export default Webhook;

View File

@ -0,0 +1,58 @@
import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';
const WeixinApp = () => {
const appId = getImage('/notice/doc/template/weixin-official/02-mini-Program-Appid.png');
return (
<div class="doc">
<div class="url">
<a href="https://work.weixin.qq.com" target="_blank" rel="noopener noreferrer">
https://work.weixin.qq.com
</a>
</div>
<h1>1. </h1>
<div>
</div>
<h1>2.</h1>
<div>
<h2>1</h2>
<div>使</div>
</div>
<div>
<h2>2</h2>
<div></div>
</div>
<div>
<h2>3</h2>
<div></div>
</div>
<div>
<h2>4</h2>
<div></div>
</div>
<div>
<h2>5Appid</h2>
<div></div>
</div>
<div>
<h2>6</h2>
<div></div>
<div class="image">
<Image width="100%" src={appId} />
</div>
</div>
<div>
<h2>7</h2>
<div>
${name}
便
</div>
</div>
</div>
);
};
export default WeixinApp;

View File

@ -0,0 +1,48 @@
import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';
const WeixinCorp = () => {
const agentId = getImage('/notice/doc/template/weixin-corp/01-Agentid.jpg');
const userId = getImage('/notice/doc/template/weixin-corp/02-userID.jpg');
const toDept = getImage('/notice/doc/template/weixin-corp/03-toDept.jpg');
const toTags = getImage('/notice/doc/template/weixin-corp/04-toTags.jpg');
return (
<div class="doc">
<div class="url">
<a href="https://work.weixin.qq.com" target="_blank" rel="noopener noreferrer">
https://work.weixin.qq.com
</a>
</div>
<h1>1. </h1>
<div>
</div>
<h1>2.</h1>
<div>
<h2> 1</h2>
<div> 使</div>
<h2> 2Agentid</h2>
<div> </div>
<div> ------</div>
<div class="image">
<Image width="100%" src={agentId} />
</div>
<h2> 3IDID</h2>
<div>
33
</div>
<div> ID获取路径-{'>'}</div>
<div> ID获取路径-{'>'}ID</div>
<div class="image">
<Image width="100%" src={userId} />
<Image width="100%" src={toDept} />
<Image width="100%" src={toTags} />
</div>
</div>
</div>
);
};
export default WeixinCorp;

View File

@ -0,0 +1,39 @@
.doc {
height: 750px;
padding: 24px;
overflow-y: auto;
color: rgba(#000, 0.8);
font-size: 14px;
background-color: #fafafa;
.url {
padding: 8px 16px;
color: #2f54eb;
background-color: rgba(#a7bdf7, 0.2);
}
h1 {
margin: 16px 0;
color: rgba(#000, 0.85);
font-weight: bold;
font-size: 14px;
&:first-child {
margin-top: 0;
}
}
h2 {
margin: 6px 0;
color: rgba(0, 0, 0, 0.8);
font-size: 14px;
}
span {
color: rgba(0, 0, 0, 0.8);
font-weight: 600;
}
.image {
margin: 16px 0;
}
}

View File

@ -0,0 +1,46 @@
import DingTalk from './DingTalk';
import DingTalkRebot from './DingTalkRebot';
import AliyunSms from './AliyunSms';
import AliyunVoice from './AliyunVoice';
import Email from './Email';
import Webhook from './Webhook';
import WeixinApp from './WeixinApp';
import WeixinCorp from './WeixinCorp';
export default defineComponent({
name: 'Doc',
props: {
docData: {
type: Object,
default: () => ({}),
},
},
setup(props) {
const docMap = {
weixin: {
corpMessage: <WeixinCorp />,
officialMessage: <WeixinApp />,
},
dingTalk: {
dingTalkMessage: <DingTalk />,
dingTalkRobotWebHook: <DingTalkRebot />,
},
voice: {
aliyun: <AliyunVoice />,
},
sms: {
aliyunSms: <AliyunSms />,
},
email: {
embedded: <Email />,
},
webhook: {
http: <Webhook />,
},
};
return () => (
docMap?.[props.docData.type]?.[props.docData.provider]
)
},
});

View File

@ -1,8 +1,433 @@
<!-- 通知模板详情 -->
<template>
<div class="page-container">通知模板详情</div>
<div class="page-container">
<a-card>
<a-row>
<a-col :span="10">
<a-form layout="vertical">
<a-form-item
label="通知方式"
v-bind="validateInfos.type"
>
<a-select
v-model:value="formData.type"
placeholder="请选择通知方式"
>
<a-select-option
v-for="(item, index) in NOTICE_METHOD"
:key="index"
:value="item.value"
>
{{ item.label }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="名称" v-bind="validateInfos.name">
<a-input
v-model:value="formData.name"
placeholder="请输入名称"
/>
</a-form-item>
<a-form-item
label="类型"
v-bind="validateInfos.provider"
v-if="formData.type !== 'email'"
>
<RadioCard
:options="msgType"
v-model="formData.provider"
/>
</a-form-item>
<!-- 钉钉 -->
<template v-if="formData.type === 'dingTalk'">
<template
v-if="formData.provider === 'dingTalkMessage'"
>
<a-form-item
label="AppKey"
v-bind="
validateInfos['configuration.appKey']
"
>
<a-input
v-model:value="
formData.configuration.appKey
"
placeholder="请输入AppKey"
/>
</a-form-item>
<a-form-item
label="AppSecret"
v-bind="
validateInfos['configuration.appSecret']
"
>
<a-input
v-model:value="
formData.configuration.appSecret
"
placeholder="请输入AppSecret"
/>
</a-form-item>
</template>
<template
v-if="
formData.provider === 'dingTalkRobotWebHook'
"
>
<a-form-item
label="webHook"
v-bind="validateInfos['configuration.url']"
>
<a-input
v-model:value="
formData.configuration.url
"
placeholder="请输入webHook"
/>
</a-form-item>
</template>
</template>
<!-- 微信 -->
<template v-if="formData.type === 'weixin'">
<a-form-item
label="corpId"
v-bind="validateInfos['configuration.corpId']"
>
<a-input
v-model:value="
formData.configuration.corpId
"
placeholder="请输入corpId"
/>
</a-form-item>
<a-form-item
label="corpSecret"
v-bind="
validateInfos['configuration.corpSecret']
"
>
<a-input
v-model:value="
formData.configuration.corpSecret
"
placeholder="请输入corpSecret"
/>
</a-form-item>
</template>
<!-- 邮件 -->
<template v-if="formData.type === 'email'">
<a-form-item
label="服务器地址"
v-bind="validateInfos['configuration.host']"
>
<a-space>
<a-input
v-model:value="
formData.configuration.host
"
placeholder="请输入服务器地址"
/>
<a-input-number
v-model:value="
formData.configuration.port
"
/>
<a-checkbox
v-model:value="
formData.configuration.ssl
"
>
开启SSL
</a-checkbox>
</a-space>
</a-form-item>
<a-form-item
label="发件人"
v-bind="validateInfos['configuration.sender']"
>
<a-input
v-model:value="
formData.configuration.sender
"
placeholder="请输入发件人"
/>
</a-form-item>
<a-form-item
label="用户名"
v-bind="validateInfos['configuration.username']"
>
<a-input
v-model:value="
formData.configuration.username
"
placeholder="请输入用户名"
/>
</a-form-item>
<a-form-item
label="密码"
v-bind="validateInfos['configuration.password']"
>
<a-input
v-model:value="
formData.configuration.password
"
placeholder="请输入密码"
/>
</a-form-item>
</template>
<!-- 语音/短信 -->
<template
v-if="
formData.type === 'voice' ||
formData.type === 'sms'
"
>
<a-form-item
label="AccessKeyId"
v-bind="
validateInfos['configuration.accessKeyId']
"
>
<a-input
v-model:value="
formData.configuration.accessKeyId
"
placeholder="请输入AccessKeyId"
/>
</a-form-item>
<a-form-item
label="Secret"
v-bind="validateInfos['configuration.secret']"
>
<a-input
v-model:value="
formData.configuration.secret
"
placeholder="Secret"
/>
</a-form-item>
</template>
<!-- webhook -->
<template v-if="formData.type === 'webhook'">
<a-form-item
label="Webhook"
v-bind="validateInfos['configuration.url']"
>
<a-input
v-model:value="formData.configuration.url"
placeholder="请输入Webhook"
/>
</a-form-item>
<a-form-item label="请求头">
<!-- <EditTable
v-model:headers="
formData.configuration.headers
"
/> -->
</a-form-item>
</template>
<a-form-item label="说明">
<a-textarea
v-model:value="formData.description"
show-count
:maxlength="200"
:rows="5"
placeholder="请输入说明"
/>
</a-form-item>
<a-form-item :wrapper-col="{ offset: 0, span: 3 }">
<a-button
type="primary"
@click="handleSubmit"
:loading="btnLoading"
style="width: 100%"
>
保存
</a-button>
</a-form-item>
</a-form>
</a-col>
<a-col :span="12" :push="2">
<Doc :docData="formData" />
</a-col>
</a-row>
</a-card>
</div>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { getImage } from '@/utils/comm';
import { Form } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { TemplateFormData } from '../types';
import {
NOTICE_METHOD,
CONFIG_FIELD_MAP,
MSG_TYPE,
} from '@/views/notice/const';
// import EditTable from './components/EditTable.vue';
import configApi from '@/api/notice/config';
import Doc from './doc/index';
<style lang="less" scoped></style>
const router = useRouter();
const route = useRoute();
const useForm = Form.useForm;
//
const msgType = ref([
{
label: '钉钉消息',
value: 'dingTalkMessage',
logo: getImage('/notice/dingtalk.png'),
},
{
label: '群机器人消息',
value: 'dingTalkRobotWebHook',
logo: getImage('/notice/dingTalk-rebot.png'),
},
]);
//
const formData = ref<TemplateFormData>({
description: '',
name: '',
provider: '',
type: NOTICE_METHOD[2].value,
template: {
subject: '',
sendTo: [],
attachments: [],
message: '',
text: '',
},
});
//
watch(
() => formData.value.type,
(val) => {
formData.value.configuration = CONFIG_FIELD_MAP[val];
msgType.value = MSG_TYPE[val];
formData.value.provider = msgType.value[0].value;
},
);
//
const formRules = ref({
type: [{ required: true, message: '请选择通知方式' }],
name: [
{ required: true, message: '请输入名称' },
{ max: 64, message: '最多可输入64个字符' },
],
provider: [{ required: true, message: '请选择类型' }],
//
'configuration.appKey': [
{ required: true, message: '请输入AppKey' },
{ max: 64, message: '最多可输入64个字符' },
],
'configuration.appSecret': [
{ required: true, message: '请输入AppSecret' },
{ max: 64, message: '最多可输入64个字符' },
],
// 'configuration.url': [{ required: true, message: 'WebHook' }],
//
'configuration.corpId': [
{ required: true, message: '请输入corpId' },
{ max: 64, message: '最多可输入64个字符' },
],
'configuration.corpSecret': [
{ required: true, message: '请输入corpSecret' },
{ max: 64, message: '最多可输入64个字符' },
],
// /
'configuration.regionId': [
{ required: true, message: '请输入RegionId' },
{ max: 64, message: '最多可输入64个字符' },
],
'configuration.accessKeyId': [
{ required: true, message: '请输入AccessKeyId' },
{ max: 64, message: '最多可输入64个字符' },
],
'configuration.secret': [
{ required: true, message: '请输入Secret' },
{ max: 64, message: '最多可输入64个字符' },
],
//
'configuration.host': [{ required: true, message: '请输入服务器地址' }],
'configuration.sender': [{ required: true, message: '请输入发件人' }],
'configuration.username': [
{ required: true, message: '请输入用户名' },
{ max: 64, message: '最多可输入64个字符' },
],
'configuration.password': [
{ required: true, message: '请输入密码' },
{ max: 64, message: '最多可输入64个字符' },
],
// webhook
'configuration.url': [
{ required: true, message: '请输入Webhook' },
{
pattern:
/^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[a-z]{2,6}\/?/,
message: 'Webhook需要是一个合法的URL',
},
],
description: [{ max: 200, message: '最多可输入200个字符' }],
});
const { resetFields, validate, validateInfos, clearValidate } = useForm(
formData.value,
formRules.value,
);
watch(
() => formData.value.type,
() => {
clearValidate();
},
{ deep: true },
);
const getDetail = async () => {
const res = await configApi.detail(route.params.id as string);
// console.log('res: ', res);
formData.value = res.result;
// console.log('formData.value: ', formData.value);
};
getDetail();
/**
* 表单提交
*/
const btnLoading = ref<boolean>(false);
const handleSubmit = () => {
validate()
.then(async () => {
// console.log('formData.value: ', formData.value);
btnLoading.value = true;
let res;
if (!formData.value.id) {
res = await configApi.save(formData.value);
} else {
res = await configApi.update(formData.value);
}
// console.log('res: ', res);
if (res?.success) {
message.success('保存成功');
router.back();
}
btnLoading.value = false;
})
.catch((err) => {
console.log('err: ', err);
});
};
</script>
<style lang="less" scoped>
.page-container {
background: #f0f2f5;
padding: 24px;
}
</style>

74
src/views/notice/Template/types.d.ts vendored Normal file
View File

@ -0,0 +1,74 @@
export interface IHeaders {
id?: number;
key: string;
value: string;
}
interface IAttachments {
location: string;
name: string;
}
interface IVariableDefinitions {
id: string;
name: string;
type: string;
format: string;
}
export type TemplateFormData = {
name: string;
type: string;
provider: string;
description: string;
id?: string;
creatorId?: string;
createTime?: number;
configId?: string;
template: {
// 钉钉消息
agentId?: string;
message?: string;
// 钉钉机器人
messageType?: string;
markdown?: {
text: string;
title: string;
};
link?: {
title: string;
picUrl: string;
messageUrl: string;
text: string;
};
// 微信
agentId?: string;
// message?: string;
toParty?: string;
toUser?: string;
toTag?: string;
// 邮件
subject?: string;
sendTo?: string[];
attachments?: IAttachments[];
// message?: string;
text?: string;
// 语音
templateType?: string;
templateCode?: string;
ttsCode?: string;
// message?: string;
playTimes?: number;
calledShowNumbers?: string;
calledNumber?: string;
// 短信
code?: string;
// message?: string;
phoneNumber?: string;
signName?: string;
// webhook
contextAsBody?: boolean;
body?: string;
};
variableDefinitions: IVariableDefinitions[];
};

View File

@ -123,4 +123,72 @@ export const CONFIG_FIELD_MAP = {
url: undefined,
headers: [],
},
};
// 模板
export const TEMPLATE_FIELD_MAP = {
dingTalk: {
dingTalkMessage: {
agentId: '',
message: '',
},
dingTalkRobotWebHook: {
message: '',
messageType: '',
markdown: {
text: '',
title: '',
},
link: {
title: '',
picUrl: '',
messageUrl: '',
text: '',
},
}
},
weixin: {
corpMessage: {
agentId: '',
message: '',
toParty: '',
toUser: '',
toTag: '',
},
officialMessage: {},
},
email: {
embedded: {
subject: '',
sendTo: [],
attachments: [],
message: '',
text: '',
}
},
voice: {
aliyun: {
templateType: '',
templateCode: '',
ttsCode: '',
message: '',
playTimes: undefined,
calledShowNumbers: '',
calledNumber: '',
}
},
sms: {
aliyunSms: {
code: '',
message: '',
phoneNumber: '',
signName: '',
}
},
webhook: {
http: {
contextAsBody: false,
body: ''
}
},
};