Browse Source

feat(短信模块): 添加短信管理模块

shizhongming 2 years ago
parent
commit
039a5cc44a
  1. 62
      src/modules/sms/views/SmsChannel/SmartSmsChannelManagerListView.api.ts
  2. 261
      src/modules/sms/views/SmsChannel/SmartSmsChannelManagerListView.config.ts
  3. 166
      src/modules/sms/views/SmsChannel/SmartSmsChannelManagerListView.vue
  4. 91
      src/modules/sms/views/SmsChannel/components/SendTestModal.vue
  5. 35
      src/modules/sms/views/SmsChannel/lang/en_US.ts
  6. 40
      src/modules/sms/views/SmsChannel/lang/zh_CN.ts
  7. 24
      src/modules/sms/views/SmsLog/SmartSmsLogListView.api.ts
  8. 91
      src/modules/sms/views/SmsLog/SmartSmsLogListView.config.ts
  9. 71
      src/modules/sms/views/SmsLog/SmartSmsLogListView.vue
  10. 22
      src/modules/sms/views/SmsLog/lang/zh_CN.ts

62
src/modules/sms/views/SmsChannel/SmartSmsChannelManagerListView.api.ts

@ -0,0 +1,62 @@
import { ApiServiceEnum, defHttp } from '@/utils/http/axios';
enum Api {
list = '/smart/sms/channel/list',
getById = '/smart/sms/channel/getById',
batchSaveUpdate = '/smart/sms/channel/saveUpdateBatch',
delete = '/smart/sms/channel/batchDeleteById',
setDefault = '/smart/sms/channel/setDefault',
sendTest = '/smart/sms/channel/sendTest',
}
export const listApi = (params) => {
return defHttp.post({
service: ApiServiceEnum.SMART_MESSAGE,
url: Api.list,
data: {
...params,
},
});
};
export const batchSaveUpdateApi = (modelList: any[]) => {
return defHttp.post({
service: ApiServiceEnum.SMART_MESSAGE,
url: Api.batchSaveUpdate,
data: modelList,
});
};
export const deleteApi = (removeRecords: Recordable[]) => {
return defHttp.post({
service: ApiServiceEnum.SMART_MESSAGE,
url: Api.delete,
data: removeRecords.map((item) => item.id),
});
};
export const getByIdApi = (id: number) => {
return defHttp.post({
service: ApiServiceEnum.SMART_MESSAGE,
url: Api.getById,
data: id,
});
};
export const setDefaultApi = (id: number) => {
return defHttp.post({
service: ApiServiceEnum.SMART_MESSAGE,
url: Api.setDefault,
data: {
id,
},
});
};
export const sendTestApi = (data: Record<string, any>) => {
return defHttp.post({
service: ApiServiceEnum.SMART_MESSAGE,
url: Api.sendTest,
data,
});
};

261
src/modules/sms/views/SmsChannel/SmartSmsChannelManagerListView.config.ts

@ -0,0 +1,261 @@
import type { SmartColumn, SmartSearchFormSchema } from '@/components/SmartTable';
import type { FormSchema } from '@/components/Form';
import { tableUseYnClass } from '@/components/SmartTable';
/**
*
*/
export const getTableColumns = (): SmartColumn[] => {
return [
{
type: 'checkbox',
width: 60,
align: 'center',
fixed: 'left',
},
{
field: 'channelCode',
fixed: 'left',
title: '{smart.sms.channel.title.channelCode}',
width: 160,
},
{
field: 'channelName',
fixed: 'left',
title: '{smart.sms.channel.title.channelName}',
width: 160,
},
{
field: 'channelType',
sortable: true,
title: '{smart.sms.channel.title.channelType}',
width: 120,
},
{
field: 'isDefault',
title: '{smart.sms.channel.title.isDefault}',
component: 'booleanTag',
width: 120,
},
{
field: 'channelProperties',
visible: false,
title: '{smart.sms.channel.title.channelProperties}',
width: 120,
},
{
field: 'seq',
sortable: true,
title: '{common.table.seq}',
width: 120,
},
{
field: 'remark',
title: '{common.table.remark}',
width: 120,
},
{
field: 'createTime',
sortable: true,
title: '{common.table.createTime}',
width: 170,
},
{
field: 'createBy',
title: '{common.table.createUser}',
width: 120,
},
{
field: 'updateTime',
sortable: true,
title: '{common.table.updateTime}',
width: 170,
},
{
field: 'updateBy',
title: '{common.table.updateUser}',
width: 120,
},
{
...tableUseYnClass(),
},
{
title: '{common.table.operation}',
field: 'operation',
width: 200,
fixed: 'right',
slots: {
default: 'table-operation',
},
},
];
};
/**
*
*/
export const getFormSchemas = (t: Function): FormSchema[] => {
return [
{
field: 'id',
label: '',
component: 'Input',
componentProps: {},
show: false,
},
{
field: 'channelCode',
label: t('smart.sms.channel.title.channelCode'),
component: 'Input',
componentProps: {},
required: true,
},
{
field: 'channelName',
label: t('smart.sms.channel.title.channelName'),
component: 'Input',
componentProps: {},
required: true,
},
{
field: 'channelType',
label: t('smart.sms.channel.title.channelType'),
component: 'SmartApiSelectDict',
componentProps: {
dictCode: 'SMART_SMS_CHANNEL',
},
required: true,
},
{
field: 'seq',
label: t('common.table.seq'),
component: 'InputNumber',
componentProps: {},
required: true,
defaultValue: 1,
},
{
field: 'remark',
label: t('common.table.remark'),
component: 'Input',
componentProps: {},
},
{
field: 'useYn',
label: t('common.table.useYn'),
component: 'Switch',
componentProps: {},
defaultValue: true,
},
...getAliyunFormSchemas(),
...getTencentFormSchemas(),
];
};
export const getSearchFormSchemas = (t: Function): SmartSearchFormSchema[] => {
return [
{
field: 'channelCode',
label: t('smart.sms.channel.title.channelCode'),
component: 'Input',
searchSymbol: 'like',
},
{
field: 'channelName',
label: t('smart.sms.channel.title.channelName'),
component: 'Input',
searchSymbol: 'like',
},
{
field: 'channelType',
label: t('smart.sms.channel.title.channelType'),
searchSymbol: '=',
component: 'SmartApiSelectDict',
componentProps: {
dictCode: 'SMART_SMS_CHANNEL',
style: {
width: '120px',
},
},
},
];
};
const getAliyunFormSchemas = (): FormSchema[] => {
return [
{
field: 'channelProperties.ALIYUN.accessKey',
component: 'Input',
label: 'accessKey',
required: ({ model }) => model.channelType === 'ALIYUN',
show: ({ model }) => {
return model.channelType === 'ALIYUN';
},
},
{
field: 'channelProperties.ALIYUN.accessSecret',
component: 'Input',
label: 'accessSecret',
required: ({ model }) => model.channelType === 'ALIYUN',
show: ({ model }) => {
return model.channelType === 'ALIYUN';
},
},
{
field: 'channelProperties.ALIYUN.endpoint',
component: 'SmartApiSelectDict',
componentProps: {
dictCode: 'SMART_SMS_ALIYUN_ENDPOINT',
},
label: 'endpoint',
required: ({ model }) => model.channelType === 'ALIYUN',
show: ({ model }) => {
return model.channelType === 'ALIYUN';
},
},
];
};
const getTencentFormSchemas = (): FormSchema[] => {
return [
{
field: 'channelProperties.TENCENT.accessKey',
component: 'Input',
label: 'accessKey',
required: ({ model }) => model.channelType === 'TENCENT',
show: ({ model }) => {
return model.channelType === 'TENCENT';
},
},
{
field: 'channelProperties.TENCENT.accessSecret',
component: 'Input',
label: 'accessSecret',
required: ({ model }) => model.channelType === 'TENCENT',
show: ({ model }) => {
return model.channelType === 'TENCENT';
},
},
{
field: 'channelProperties.TENCENT.appid',
component: 'Input',
label: 'appid',
required: ({ model }) => model.channelType === 'TENCENT',
show: ({ model }) => {
return model.channelType === 'TENCENT';
},
},
{
field: 'channelProperties.TENCENT.region',
component: 'SmartApiSelectDict',
componentProps: {
dictCode: 'SMART_SMS_TENCENT_REGION',
},
label: 'Region',
required: ({ model }) => model.channelType === 'TENCENT',
show: ({ model }) => {
return model.channelType === 'TENCENT';
},
},
];
};

166
src/modules/sms/views/SmsChannel/SmartSmsChannelManagerListView.vue

@ -0,0 +1,166 @@
<template>
<div class="full-height page-container">
<SmartTable @register="registerTable" :size="getTableSize">
<template #table-operation="{ row }">
<SmartVxeTableAction :actions="getActions(row)" />
</template>
</SmartTable>
<SendTestModal @register="registerTestSendModal" />
</div>
</template>
<script lang="ts" setup>
import { useI18n } from '@/hooks/web/useI18n';
import { useSizeSetting } from '@/hooks/setting/UseSizeSetting';
import { replace } from 'lodash-es';
import { createConfirm, successMessage } from '@/utils/message/SystemNotice';
import {
ActionItem,
SmartTable,
SmartVxeTableAction,
useSmartTable,
} from '@/components/SmartTable';
import {
getTableColumns,
getFormSchemas,
getSearchFormSchemas,
} from './SmartSmsChannelManagerListView.config';
import {
listApi,
deleteApi,
getByIdApi,
batchSaveUpdateApi,
setDefaultApi,
} from './SmartSmsChannelManagerListView.api';
import SendTestModal from './components/SendTestModal.vue';
import { useModal } from '@/components/Modal';
const { t } = useI18n();
const { getTableSize } = useSizeSetting();
const [registerTestSendModal, { openModal }] = useModal();
const getActions = (row: Recordable): ActionItem[] => {
return [
{
label: t('common.button.edit'),
onClick: () => editByRowModal(row),
auth: 'smart:sms:update',
},
{
label: t('smart.sms.channel.button.setDefault'),
auth: 'smart:sms:update',
disabled: row.isDefault === true,
onClick: () => {
createConfirm({
iconType: 'warning',
content: t('smart.sms.channel.message.setDefault'),
onOk: async () => {
await setDefaultApi(row.id);
successMessage(t('smart.sms.channel.message.setDefaultSuccess'));
await query();
},
});
},
},
{
label: t('smart.sms.channel.button.sendTest'),
onClick: () => {
openModal(true, { channelId: row.id });
},
},
];
};
const [registerTable, { editByRowModal, query }] = useSmartTable({
id: 'smart-sms-smsChannel',
customConfig: { storage: true },
columns: getTableColumns(),
height: 'auto',
showOverflow: 'tooltip',
pagerConfig: true,
border: true,
useSearchForm: true,
sortConfig: {
remote: true,
},
columnConfig: { resizable: true },
searchFormConfig: {
schemas: getSearchFormSchemas(t),
searchWithSymbol: true,
colon: true,
layout: 'inline',
actionColOptions: {
span: undefined,
},
compact: true,
},
addEditConfig: {
formConfig: {
colon: true,
schemas: getFormSchemas(t),
baseColProps: { span: 24 },
labelCol: { span: 6 },
wrapperCol: { span: 17 },
},
},
proxyConfig: {
ajax: {
query: (params) => listApi(params.ajaxParameter),
save: ({ body: { insertRecords, updateRecords } }) => {
const saveDataList = [...insertRecords, ...updateRecords];
const formatDataList = saveDataList.map((item) => {
const result: Record<string, any> = {};
const channelProperties: Recordable = {};
const propertiesKeyPrefix = `channelProperties.${item.channelType}.`;
Object.keys(item).forEach((key) => {
if (!key.startsWith(propertiesKeyPrefix)) {
result[key] = item[key];
} else if (key.startsWith(propertiesKeyPrefix)) {
channelProperties[replace(key, propertiesKeyPrefix, '')] = item[key];
}
});
result.channelProperties = JSON.stringify(channelProperties);
return result;
});
return batchSaveUpdateApi(formatDataList);
},
delete: ({ body: { removeRecords } }) => deleteApi(removeRecords),
getById: async (params) => {
const result = await getByIdApi(params.id);
if (result.channelProperties) {
const channelProperties = JSON.parse(result.channelProperties);
const channelType = result.channelType;
const formatData: Recordable = {};
Object.keys(channelProperties).forEach((item) => {
formatData[`channelProperties.${channelType}.${item}`] = channelProperties[item];
});
console.log(formatData);
return {
...result,
...formatData,
};
}
return result;
},
},
},
toolbarConfig: {
zoom: true,
refresh: true,
column: { columnOrder: true },
buttons: [
{
code: 'ModalAdd',
},
{
code: 'delete',
},
],
},
});
</script>

91
src/modules/sms/views/SmsChannel/components/SendTestModal.vue

@ -0,0 +1,91 @@
<template>
<BasicModal
:title="t('smart.sms.channel.title.sendTest')"
@register="registerModal"
@ok="handleSendTest"
:okText="t('smart.sms.channel.button.sendTest')"
>
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { useModalInner, BasicModal } from '@/components/Modal';
import { useI18n } from '@/hooks/web/useI18n';
import { useForm, BasicForm } from '@/components/Form';
import { useMessage } from '@/hooks/web/useMessage';
import { sendTestApi } from '../SmartSmsChannelManagerListView.api';
const { t } = useI18n();
const { createSuccessModal } = useMessage();
let currentChannelId: number | null = null;
/**
* 发送测试
*/
const handleSendTest = async () => {
try {
changeOkLoading(true);
const formModel = getFieldsValue();
const parameter: Record<string, any> = {
channelId: currentChannelId,
...formModel,
phoneNumberList: formModel.phoneNumberListStr.split(','),
};
if (parameter.templateParameter) {
parameter.templateParameter = JSON.parse(parameter.templateParameter);
}
const sendResult = await sendTestApi(parameter);
createSuccessModal({
content: JSON.stringify(sendResult),
});
closeModal();
} finally {
changeOkLoading(false);
}
};
const [registerModal, { changeOkLoading, closeModal }] = useModalInner((data) => {
currentChannelId = data.channelId;
});
const [registerForm, { getFieldsValue }] = useForm({
showActionButtonGroup: false,
baseColProps: { span: 24 },
colon: true,
labelCol: { span: 4 },
wrapperCol: { span: 19 },
schemas: [
{
field: 'signName',
label: t('smart.sms.channel.title.signName'),
component: 'Input',
required: true,
},
{
field: 'template',
label: t('smart.sms.channel.title.template'),
component: 'Input',
required: true,
},
{
field: 'templateParameter',
label: 'Data',
component: 'InputTextArea',
},
{
field: 'phoneNumberListStr',
label: t('smart.sms.channel.title.phoneNumberList'),
component: 'InputTextArea',
required: true,
componentProps: {
placeholder: t('smart.sms.channel.message.phoneNumberList'),
},
},
],
});
</script>
<style scoped></style>

35
src/modules/sms/views/SmsChannel/lang/en_US.ts

@ -0,0 +1,35 @@
/**
*
*/
export default {
trans: true,
key: 'smart.sms.channel',
data: {
title: {
channelCode: 'Channel code',
channelName: 'Channel name',
channelType: 'Channel type',
isDefault: 'Default',
channelProperties: 'Properties',
},
validate: {
channelCode: 'Please enter channel code',
channelName: 'Please enter channel name',
channelType: 'Please select channel type',
},
rules: {},
search: {
channelCode: 'Please enter channel code',
channelName: 'Please enter channel name',
channelType: 'Please select channel type',
},
button: {
setDefault: 'Set default',
},
message: {
setDefault:
'There can only be one default channel. Are you sure you want to set it as the default?',
setDefaultSuccess: 'Set default successfully',
},
},
};

40
src/modules/sms/views/SmsChannel/lang/zh_CN.ts

@ -0,0 +1,40 @@
/**
*
*/
export default {
trans: true,
key: 'smart.sms.channel',
data: {
title: {
channelCode: '通道编号',
channelName: '通道名称',
channelType: '通道类型',
isDefault: '是否是默认的',
channelProperties: '通道参数',
sendTest: '发送测试',
signName: '签名',
template: '模板',
phoneNumberList: '手机号',
},
validate: {
channelCode: '请输入通道编号',
channelName: '请输入通道名称',
channelType: '请输入通道类型',
},
rules: {},
search: {
channelCode: '请输入通道编号',
channelName: '请输入通道名称',
channelType: '请输入通道类型',
},
button: {
setDefault: '设为默认',
sendTest: '发送测试',
},
message: {
setDefault: '只能有一个默认的通道,确定要设为默认吗?',
setDefaultSuccess: '设置默认成功',
phoneNumberList: '多个手机号以逗号分隔',
},
},
};

24
src/modules/sms/views/SmsLog/SmartSmsLogListView.api.ts

@ -0,0 +1,24 @@
import { ApiServiceEnum, defHttp } from '@/utils/http/axios';
enum Api {
list = '/smart/sms/log/listAll',
getById = '/smart/sms/log/getById',
}
export const listApi = (params) => {
return defHttp.post({
service: ApiServiceEnum.SMART_MESSAGE,
url: Api.list,
data: {
...params,
},
});
};
export const getByIdApi = (id: number) => {
return defHttp.post({
service: ApiServiceEnum.SMART_MESSAGE,
url: Api.getById,
data: id,
});
};

91
src/modules/sms/views/SmsLog/SmartSmsLogListView.config.ts

@ -0,0 +1,91 @@
import type { SmartColumn, SmartSearchFormSchema } from '@/components/SmartTable';
/**
*
*/
export const getTableColumns = (): SmartColumn[] => {
return [
{
type: 'checkbox',
width: 60,
align: 'center',
fixed: 'left',
},
{
field: 'id',
visible: false,
title: '',
width: 120,
},
{
field: 'channelId',
title: '{smart.sms.log.title.channelId}',
width: 120,
},
{
field: 'isSuccess',
title: '{smart.sms.log.title.isSuccess}',
width: 120,
},
{
field: 'sendParameter',
visible: false,
title: '{smart.sms.log.title.sendParameter}',
width: 120,
},
{
field: 'sendResult',
visible: false,
title: '{smart.sms.log.title.sendResult}',
width: 120,
},
{
field: 'errorMessage',
visible: false,
title: '{smart.sms.log.title.errorMessage}',
width: 120,
},
{
field: 'createTime',
title: '{common.table.createTime}',
width: 120,
},
{
field: 'createBy',
title: '{common.table.createUser}',
width: 120,
},
{
title: '{common.table.operation}',
field: 'operation',
width: 120,
fixed: 'right',
slots: {
default: 'table-operation',
},
},
];
};
export const getSearchFormSchemas = (t: Function): SmartSearchFormSchema[] => {
return [
{
field: 'channelId',
label: t('smart.sms.log.title.channelId'),
component: 'Select',
searchSymbol: '=',
},
{
field: 'isSuccess',
label: t('smart.sms.log.title.isSuccess'),
component: 'Select',
searchSymbol: '=',
},
{
field: 'createTime',
label: t('common.table.createTime'),
component: 'DatePicker',
searchSymbol: '=',
},
];
};

71
src/modules/sms/views/SmsLog/SmartSmsLogListView.vue

@ -0,0 +1,71 @@
<template>
<div class="full-height page-container">
<SmartTable @register="registerTable" :size="getTableSize">
<template #table-operation="{ row }">
<SmartVxeTableAction :actions="getActions(row)" />
</template>
</SmartTable>
</div>
</template>
<script lang="ts" setup>
import { useI18n } from '@/hooks/web/useI18n';
import { useSizeSetting } from '@/hooks/setting/UseSizeSetting';
import {
ActionItem,
SmartTable,
SmartVxeTableAction,
useSmartTable,
} from '@/components/SmartTable';
import { getTableColumns, getSearchFormSchemas } from './SmartSmsLogListView.config';
import { listApi, getByIdApi } from './SmartSmsLogListView.api';
const { t } = useI18n();
const { getTableSize } = useSizeSetting();
const getActions = (row: Recordable): ActionItem[] => {
return [
{
label: t('common.button.edit'),
onClick: () => editByRowModal(row),
},
];
};
const [registerTable, { editByRowModal }] = useSmartTable({
id: 'smart-sms-smsLog',
customConfig: { storage: true },
columns: getTableColumns(),
height: 'auto',
border: true,
sortConfig: {
remote: true,
},
pagerConfig: true,
useSearchForm: true,
searchFormConfig: {
schemas: getSearchFormSchemas(t),
searchWithSymbol: true,
colon: true,
compact: true,
layout: 'inline',
actionColOptions: {
span: undefined,
},
},
proxyConfig: {
ajax: {
query: (params) => listApi(params.ajaxParameter),
getById: (params) => getByIdApi(params.id),
},
},
toolbarConfig: {
zoom: true,
refresh: true,
column: { columnOrder: true },
buttons: [],
},
});
</script>

22
src/modules/sms/views/SmsLog/lang/zh_CN.ts

@ -0,0 +1,22 @@
/**
*
*/
export default {
trans: true,
key: 'smart.sms.log',
data: {
title: {
channelId: '短信通道',
isSuccess: '是否发送成功',
sendParameter: '发送参数',
sendResult: '发送结果',
errorMessage: '错误信息',
},
validate: {},
rules: {},
search: {
channelId: '请输入短信通道ID',
isSuccess: '请输入是否发送成功',
},
},
};
Loading…
Cancel
Save