From 6b194bb95f48043fd53050b222b7f311adc9c7d7 Mon Sep 17 00:00:00 2001 From: YunaiV <zhijiantianya@gmail.com> Date: Fri, 24 Mar 2023 23:07:51 +0800 Subject: [PATCH] =?UTF-8?q?Vue3=20=E9=87=8D=E6=9E=84=EF=BC=9AREVIEW=20?= =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/system/sms/smsChannel/index.ts | 2 +- src/api/system/sms/smsLog/index.ts | 23 +- src/components/RightToolbar/index.ts | 9 - src/components/RightToolbar/src/index.vue | 104 ----- src/components/index.ts | 2 - src/locales/zh-CN.ts | 9 +- src/types/auto-components.d.ts | 6 +- src/types/auto-imports.d.ts | 2 +- src/utils/dict.ts | 8 + src/utils/formatTime.ts | 12 +- .../sms/{smsChannel => channel}/form.vue | 274 ++++++------- .../sms/{smsChannel => channel}/index.vue | 0 src/views/system/sms/log/SmsLogDetail.vue | 87 ++++ src/views/system/sms/log/index.vue | 263 ++++++++++++ src/views/system/sms/smsLog/index.vue | 382 ------------------ .../sms/{smsTemplate => template}/form.vue | 0 .../sms/{smsTemplate => template}/index.vue | 2 +- 17 files changed, 516 insertions(+), 669 deletions(-) delete mode 100644 src/components/RightToolbar/index.ts delete mode 100644 src/components/RightToolbar/src/index.vue rename src/views/system/sms/{smsChannel => channel}/form.vue (97%) rename src/views/system/sms/{smsChannel => channel}/index.vue (100%) create mode 100644 src/views/system/sms/log/SmsLogDetail.vue create mode 100644 src/views/system/sms/log/index.vue delete mode 100644 src/views/system/sms/smsLog/index.vue rename src/views/system/sms/{smsTemplate => template}/form.vue (100%) rename src/views/system/sms/{smsTemplate => template}/index.vue (99%) diff --git a/src/api/system/sms/smsChannel/index.ts b/src/api/system/sms/smsChannel/index.ts index 7c8ccea9..ee0e6167 100644 --- a/src/api/system/sms/smsChannel/index.ts +++ b/src/api/system/sms/smsChannel/index.ts @@ -31,7 +31,7 @@ export const getSmsChannelPageApi = (params: SmsChannelPageReqVO) => { } // 获得短信渠道精简列表 -export function getSimpleSmsChannels() { +export function getSimpleSmsChannelList() { return request.get({ url: '/system/sms-channel/list-all-simple' }) } diff --git a/src/api/system/sms/smsLog/index.ts b/src/api/system/sms/smsLog/index.ts index 269b609d..3d54fac1 100644 --- a/src/api/system/sms/smsLog/index.ts +++ b/src/api/system/sms/smsLog/index.ts @@ -28,31 +28,12 @@ export interface SmsLogVO { createTime: Date | null } -export interface SmsLogPageReqVO extends PageParam { - channelId?: number | null - templateId?: number | null - mobile?: string - sendStatus?: number | null - sendTime?: Date[] - receiveStatus?: number | null - receiveTime?: Date[] -} -export interface SmsLogExportReqVO { - channelId?: number - templateId?: number - mobile?: string - sendStatus?: number - sendTime?: Date[] - receiveStatus?: number - receiveTime?: Date[] -} - // 查询短信日志列表 -export const getSmsLogPageApi = (params: SmsLogPageReqVO) => { +export const getSmsLogPage = (params: PageParam) => { return request.get({ url: '/system/sms-log/page', params }) } // 导出短信日志 -export const exportSmsLogApi = (params: SmsLogExportReqVO) => { +export const exportSmsLog = (params) => { return request.download({ url: '/system/sms-log/export-excel', params }) } diff --git a/src/components/RightToolbar/index.ts b/src/components/RightToolbar/index.ts deleted file mode 100644 index eb9d1112..00000000 --- a/src/components/RightToolbar/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import RightToolbar from './src/index.vue' - -export interface columnsType { - key?: number - label?: string - visible?: boolean -} - -export { RightToolbar } diff --git a/src/components/RightToolbar/src/index.vue b/src/components/RightToolbar/src/index.vue deleted file mode 100644 index 11e021dc..00000000 --- a/src/components/RightToolbar/src/index.vue +++ /dev/null @@ -1,104 +0,0 @@ -<template> - <div :style="style"> - <el-row justify="end"> - <el-tooltip - class="item" - effect="dark" - :content="showSearch ? '隐藏搜索' : '显示搜索'" - placement="top" - v-if="search" - > - <el-button circle @click="toggleSearch()"> - <Icon icon="ep:search" /> - </el-button> - </el-tooltip> - <el-tooltip class="item" effect="dark" content="刷新" placement="top"> - <el-button circle @click="refresh()"> - <Icon icon="ep:refresh" /> - </el-button> - </el-tooltip> - <el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="isColumns"> - <el-button circle @click="showColumn()"> - <Icon icon="ep:menu" /> - </el-button> - </el-tooltip> - </el-row> - <el-dialog :title="title" v-model="open" append-to-body> - <el-transfer - :titles="['显示', '隐藏']" - v-model="value" - :data="columns" - @change="dataChange" - /> - </el-dialog> - </div> -</template> -<script lang="ts" setup name="RightToolbar"> -import type { CSSProperties } from 'vue' -import type { columnsType } from '@/components/RightToolbar' -import { propTypes } from '@/utils/propTypes' -// 显隐数据 -const value = ref<number[]>([]) -// 弹出层标题 -const title = ref('显示/隐藏') -// 是否显示弹出层 -const open = ref(false) - -const props = defineProps({ - showSearch: propTypes.bool.def(true), - columns: { - type: Array as PropType<columnsType[]>, - default: () => [] - }, - search: propTypes.bool.def(true), - gutter: propTypes.number.def(10) -}) -const isColumns = computed(() => props.columns?.length > 0) -const style = computed((): CSSProperties => { - const ret: CSSProperties = {} - if (props.gutter) { - ret.marginRight = `${props.gutter / 2}px` - } - return ret -}) -const emit = defineEmits(['update:showSearch', 'queryTable']) -// 搜索 -const toggleSearch = () => { - emit('update:showSearch', !props.showSearch) -} -// 刷新 -const refresh = () => { - emit('queryTable') -} -// 右侧列表元素变化 -const dataChange = (data: number[]) => { - props.columns.forEach((item) => { - const key: number = item.key! - item.visible = !data.includes(key) - }) -} -// 打开显隐列dialog -const showColumn = () => { - open.value = true -} -// 显隐列初始默认隐藏列 -const init = () => { - props.columns.forEach((item, index) => { - if (item.visible === false) { - value.value.push(index) - } - }) -} -init() -</script> -<style lang="scss" scoped> -:deep(.el-transfer__button) { - border-radius: 50%; - padding: 12px; - display: block; - margin-left: 0px; -} -:deep(.el-transfer__button:first-child) { - margin-bottom: 10px; -} -</style> diff --git a/src/components/index.ts b/src/components/index.ts index 97c2b4b0..19b2aac6 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -9,7 +9,6 @@ import { XButton, XTextButton } from '@/components/XButton' import { DictTag } from '@/components/DictTag' import { ContentWrap } from '@/components/ContentWrap' import { Descriptions } from '@/components/Descriptions' -import { RightToolbar } from '@/components/RightToolbar' export const setupGlobCom = (app: App<Element>): void => { app.component('Icon', Icon) @@ -23,5 +22,4 @@ export const setupGlobCom = (app: App<Element>): void => { app.component('DictTag', DictTag) app.component('ContentWrap', ContentWrap) app.component('Descriptions', Descriptions) - app.component('RightToolbar', RightToolbar) } diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 0a1e9e19..6f46f1ab 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -303,14 +303,7 @@ export default { dialog: { dialog: '弹窗', open: '打开', - close: '关闭', - sms: { - template: { - addTitle: '添加短信模板', - updtaeTitle: '修改短信模板', - sendSms: '发送短信' - } - } + close: '关闭' }, sys: { api: { diff --git a/src/types/auto-components.d.ts b/src/types/auto-components.d.ts index 5c679fa9..2b199f1a 100644 --- a/src/types/auto-components.d.ts +++ b/src/types/auto-components.d.ts @@ -1,5 +1,7 @@ -// generated by unplugin-vue-components -// We suggest you to commit this file into source control +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// Generated by unplugin-vue-components // Read more: https://github.com/vuejs/core/pull/3399 import '@vue/runtime-core' diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts index 2c68c6ce..75cf16d9 100644 --- a/src/types/auto-imports.d.ts +++ b/src/types/auto-imports.d.ts @@ -72,5 +72,5 @@ declare global { // for type re-export declare global { // @ts-ignore - export type { Component,ComponentPublicInstance,ComputedRef,InjectionKey,PropType,Ref,VNode } from 'vue' + export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue' } diff --git a/src/utils/dict.ts b/src/utils/dict.ts index 4f5d63fb..395f7157 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -69,6 +69,13 @@ export const getDictObj = (dictType: string, value: any) => { } }) } + +/** + * 获得字典数据的文本展示 + * + * @param dictType 字典类型 + * @param value 字典数据的值 + */ export const getDictLabel = (dictType: string, value: any) => { const dictOptions: DictDataType[] = getDictOptions(dictType) const dictLabel = ref('') @@ -79,6 +86,7 @@ export const getDictLabel = (dictType: string, value: any) => { }) return dictLabel.value } + export enum DICT_TYPE { USER_TYPE = 'user_type', COMMON_STATUS = 'common_status', diff --git a/src/utils/formatTime.ts b/src/utils/formatTime.ts index 35c783a6..ec7f3744 100644 --- a/src/utils/formatTime.ts +++ b/src/utils/formatTime.ts @@ -11,9 +11,19 @@ import dayjs from 'dayjs' * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ" * @returns 返回拼接后的时间字符串 */ -export function formatDate(date: Date, format: string): string { +export function formatDate(date: Date, format?: string): string { + // 日期不存在,则返回空 + if (!date) { + return '' + } + // 日期存在,则进行格式化 + if (format === undefined) { + format = 'YYYY-MM-DD HH:mm:ss' + } return dayjs(date).format(format) } + +// TODO 芋艿:稍后去掉 // 日期格式化 export function parseTime(time: any, pattern?: string) { if (arguments.length === 0 || !time) { diff --git a/src/views/system/sms/smsChannel/form.vue b/src/views/system/sms/channel/form.vue similarity index 97% rename from src/views/system/sms/smsChannel/form.vue rename to src/views/system/sms/channel/form.vue index 7c20a90d..9c3881d8 100644 --- a/src/views/system/sms/smsChannel/form.vue +++ b/src/views/system/sms/channel/form.vue @@ -1,137 +1,137 @@ -<template> - <Dialog :title="modelTitle" v-model="modelVisible"> - <el-form ref="formRef" :model="form" :rules="rules" label-width="130px" v-loading="formLoading"> - <el-form-item label="短信签名" prop="signature"> - <el-input v-model="form.signature" placeholder="请输入短信签名" /> - </el-form-item> - <el-form-item label="渠道编码" prop="code"> - <el-select v-model="form.code" placeholder="请选择渠道编码" clearable> - <el-option - v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE)" - :key="dict.value" - :label="dict.label" - :value="dict.value" - /> - </el-select> - </el-form-item> - <el-form-item label="启用状态"> - <el-radio-group v-model="form.status"> - <el-radio - v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)" - :key="dict.value" - :label="parseInt(dict.value)" - >{{ dict.label }}</el-radio - > - </el-radio-group> - </el-form-item> - <el-form-item label="备注" prop="remark"> - <el-input v-model="form.remark" placeholder="请输入备注" /> - </el-form-item> - <el-form-item label="短信 API 的账号" prop="apiKey"> - <el-input v-model="form.apiKey" placeholder="请输入短信 API 的账号" /> - </el-form-item> - <el-form-item label="短信 API 的密钥" prop="apiSecret"> - <el-input v-model="form.apiSecret" placeholder="请输入短信 API 的密钥" /> - </el-form-item> - <el-form-item label="短信发送回调 URL" prop="callbackUrl"> - <el-input v-model="form.callbackUrl" placeholder="请输入短信发送回调 URL" /> - </el-form-item> - </el-form> - <template #footer> - <div class="dialog-footer"> - <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> - </div> - </template> - </Dialog> -</template> -<script setup lang="ts"> -import { DICT_TYPE, getDictOptions } from '@/utils/dict' -import * as SmsChannelApi from '@/api/system/sms/smsChannel' - -const { t } = useI18n() // 国际化 -const message = useMessage() // 消息弹窗 - -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 -const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 -const formType = ref('') // 表单的类型:create - 新增;update - 修改 -const form = ref({ - id: undefined, - signature: '', - code: '', - status: '', - remark: '', - apiKey: '', - apiSecret: '', - callbackUrl: '' -}) -const rules = reactive({ - signature: [{ required: true, message: '短信签名不能为空', trigger: 'blur' }], - code: [{ required: true, message: '渠道编码不能为空', trigger: 'blur' }], - status: [{ required: true, message: '启用状态不能为空', trigger: 'blur' }], - apiKey: [{ required: true, message: '短信 API 的账号不能为空', trigger: 'blur' }] -}) -const formRef = ref() // 表单 Ref - -/** 打开弹窗 */ -const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) - formType.value = type - resetForm() - // 修改时,设置数据 - if (id) { - formLoading.value = true - try { - form.value = await SmsChannelApi.getSmsChannelApi(id) - console.log(form) - } finally { - formLoading.value = false - } - } -} - -defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗 - -/** 提交表单 */ -const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 -const submitForm = async () => { - // 校验表单 - if (!formRef) return - const valid = await formRef.value.validate() - if (!valid) return - // 提交请求 - formLoading.value = true - try { - const data = unref(formRef)?.formModel as SmsChannelApi.SmsChannelVO - if (formType.value === 'create') { - await SmsChannelApi.createSmsChannelApi(data) - message.success(t('common.createSuccess')) - } else { - await SmsChannelApi.updateSmsChannelApi(data) - message.success(t('common.updateSuccess')) - } - modelVisible.value = false - // 发送操作成功的事件 - emit('success') - } finally { - formLoading.value = false - } -} - -/** 重置表单 */ -const resetForm = () => { - form.value = { - id: undefined, - signature: '', - code: '', - status: '', - remark: '', - apiKey: '', - apiSecret: '', - callbackUrl: '' - } - formRef.value?.resetFields() -} -</script> +<template> + <Dialog :title="modelTitle" v-model="modelVisible"> + <el-form ref="formRef" :model="form" :rules="rules" label-width="130px" v-loading="formLoading"> + <el-form-item label="短信签名" prop="signature"> + <el-input v-model="form.signature" placeholder="请输入短信签名" /> + </el-form-item> + <el-form-item label="渠道编码" prop="code"> + <el-select v-model="form.code" placeholder="请选择渠道编码" clearable> + <el-option + v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="启用状态"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="parseInt(dict.value)" + >{{ dict.label }}</el-radio + > + </el-radio-group> + </el-form-item> + <el-form-item label="备注" prop="remark"> + <el-input v-model="form.remark" placeholder="请输入备注" /> + </el-form-item> + <el-form-item label="短信 API 的账号" prop="apiKey"> + <el-input v-model="form.apiKey" placeholder="请输入短信 API 的账号" /> + </el-form-item> + <el-form-item label="短信 API 的密钥" prop="apiSecret"> + <el-input v-model="form.apiSecret" placeholder="请输入短信 API 的密钥" /> + </el-form-item> + <el-form-item label="短信发送回调 URL" prop="callbackUrl"> + <el-input v-model="form.callbackUrl" placeholder="请输入短信发送回调 URL" /> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="modelVisible = false">取 消</el-button> + </div> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { DICT_TYPE, getDictOptions } from '@/utils/dict' +import * as SmsChannelApi from '@/api/system/sms/smsChannel' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const modelVisible = ref(false) // 弹窗的是否展示 +const modelTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const form = ref({ + id: undefined, + signature: '', + code: '', + status: '', + remark: '', + apiKey: '', + apiSecret: '', + callbackUrl: '' +}) +const rules = reactive({ + signature: [{ required: true, message: '短信签名不能为空', trigger: 'blur' }], + code: [{ required: true, message: '渠道编码不能为空', trigger: 'blur' }], + status: [{ required: true, message: '启用状态不能为空', trigger: 'blur' }], + apiKey: [{ required: true, message: '短信 API 的账号不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const openModal = async (type: string, id?: number) => { + modelVisible.value = true + modelTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + form.value = await SmsChannelApi.getSmsChannelApi(id) + console.log(form) + } finally { + formLoading.value = false + } + } +} + +defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + if (!formRef) return + const valid = await formRef.value.validate() + if (!valid) return + // 提交请求 + formLoading.value = true + try { + const data = unref(formRef)?.formModel as SmsChannelApi.SmsChannelVO + if (formType.value === 'create') { + await SmsChannelApi.createSmsChannelApi(data) + message.success(t('common.createSuccess')) + } else { + await SmsChannelApi.updateSmsChannelApi(data) + message.success(t('common.updateSuccess')) + } + modelVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + form.value = { + id: undefined, + signature: '', + code: '', + status: '', + remark: '', + apiKey: '', + apiSecret: '', + callbackUrl: '' + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/system/sms/smsChannel/index.vue b/src/views/system/sms/channel/index.vue similarity index 100% rename from src/views/system/sms/smsChannel/index.vue rename to src/views/system/sms/channel/index.vue diff --git a/src/views/system/sms/log/SmsLogDetail.vue b/src/views/system/sms/log/SmsLogDetail.vue new file mode 100644 index 00000000..736d0b8e --- /dev/null +++ b/src/views/system/sms/log/SmsLogDetail.vue @@ -0,0 +1,87 @@ +<template> + <Dialog title="详情" v-model="modelVisible" :scroll="true" :max-height="500" width="800"> + <el-descriptions border :column="1"> + <el-descriptions-item label="日志主键" min-width="120"> + {{ detailData.id }} + </el-descriptions-item> + <el-descriptions-item label="短信渠道"> + {{ channelList.find((channel) => channel.id === detailData.channelId)?.signature }} + <dict-tag :type="DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE" :value="detailData.channelCode" /> + </el-descriptions-item> + <el-descriptions-item label="短信模板"> + {{ detailData.templateId }} | {{ detailData.templateCode }} + <dict-tag :type="DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE" :value="detailData.templateType" /> + </el-descriptions-item> + <el-descriptions-item label="API 的模板编号"> + {{ detailData.apiTemplateId }} + </el-descriptions-item> + <el-descriptions-item label="用户信息"> + {{ detailData.mobile }} + <span v-if="detailData.userType && detailData.userId"> + <dict-tag :type="DICT_TYPE.USER_TYPE" :value="detailData.userType" /> + ({{ detailData.userId }}) + </span> + </el-descriptions-item> + <el-descriptions-item label="短信内容"> + {{ detailData.templateContent }} + </el-descriptions-item> + <el-descriptions-item label="短信参数"> + {{ detailData.templateParams }} + </el-descriptions-item> + <el-descriptions-item label="创建时间"> + {{ formatDate(detailData.createTime) }} + </el-descriptions-item> + <el-descriptions-item label="发送状态"> + <dict-tag :type="DICT_TYPE.SYSTEM_SMS_SEND_STATUS" :value="detailData.sendStatus" /> + </el-descriptions-item> + <el-descriptions-item label="发送时间"> + {{ formatDate(detailData.sendTime) }} + </el-descriptions-item> + <el-descriptions-item label="发送结果"> + {{ detailData.sendCode }} | {{ detailData.sendMsg }} + </el-descriptions-item> + <el-descriptions-item label="API 发送结果"> + {{ detailData.apiSendCode }} | {{ detailData.apiSendMsg }} + </el-descriptions-item> + <el-descriptions-item label="API 短信编号"> + {{ detailData.apiSerialNo }} + </el-descriptions-item> + <el-descriptions-item label="API 请求编号"> + {{ detailData.apiRequestId }} + </el-descriptions-item> + <el-descriptions-item label="API 接收状态"> + <dict-tag :type="DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS" :value="detailData.receiveStatus" /> + {{ formatDate(detailData.receiveTime) }} + </el-descriptions-item> + <el-descriptions-item label="API 接收结果"> + {{ detailData.apiReceiveCode }} | {{ detailData.apiReceiveMsg }} + </el-descriptions-item> + </el-descriptions> + </Dialog> +</template> +<script setup lang="ts"> +import { DICT_TYPE } from '@/utils/dict' +import { formatDate } from '@/utils/formatTime' +import * as SmsLogApi from '@/api/system/sms/smsLog' +import * as SmsChannelApi from '@/api/system/sms/smsChannel' + +const modelVisible = ref(false) // 弹窗的是否展示 +const detailLoading = ref(false) // 表单的加载中 +const detailData = ref() // 详情数据 +const channelList = ref([]) // 短信渠道列表 + +/** 打开弹窗 */ +const open = async (data: SmsLogApi.SmsLogVO) => { + modelVisible.value = true + // 设置数据 + detailLoading.value = true + try { + detailData.value = data + } finally { + detailLoading.value = false + } + // 加载渠道列表 + channelList.value = await SmsChannelApi.getSimpleSmsChannelList() +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 +</script> diff --git a/src/views/system/sms/log/index.vue b/src/views/system/sms/log/index.vue new file mode 100644 index 00000000..a0acdfaa --- /dev/null +++ b/src/views/system/sms/log/index.vue @@ -0,0 +1,263 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="100px" + > + <el-form-item label="手机号" prop="mobile"> + <el-input + v-model="queryParams.mobile" + placeholder="请输入手机号" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="短信渠道" prop="channelId"> + <el-select + v-model="queryParams.channelId" + placeholder="请选择短信渠道" + clearable + class="!w-240px" + > + <el-option + v-for="channel in channelList" + :key="channel.id" + :value="channel.id" + :label=" + channel.signature + + `【 ${getDictLabel(DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE, channel.code)}】` + " + /> + </el-select> + </el-form-item> + <el-form-item label="模板编号" prop="templateId"> + <el-input + v-model="queryParams.templateId" + placeholder="请输入模板编号" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="发送状态" prop="sendStatus"> + <el-select + v-model="queryParams.sendStatus" + placeholder="请选择发送状态" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_SMS_SEND_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="发送时间" prop="sendTime"> + <el-date-picker + v-model="queryParams.sendTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="接收状态" prop="receiveStatus"> + <el-select + v-model="queryParams.receiveStatus" + placeholder="请选择接收状态" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="接收时间" prop="receiveTime"> + <el-date-picker + v-model="queryParams.receiveTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="success" + plain + @click="handleExport" + :loading="exportLoading" + v-hasPermi="['infra:config:export']" + > + <Icon icon="ep:download" class="mr-5px" /> 导出 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list"> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + width="180" + :formatter="dateFormatter" + /> + <el-table-column label="手机号" align="center" prop="mobile" width="120"> + <template #default="scope"> + <div>{{ scope.row.mobile }}</div> + <div v-if="scope.row.userType && scope.row.userId"> + <dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType" /> + {{ '(' + scope.row.userId + ')' }} + </div> + </template> + </el-table-column> + <el-table-column label="短信内容" align="center" prop="templateContent" width="300" /> + <el-table-column label="发送状态" align="center" width="180"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.SYSTEM_SMS_SEND_STATUS" :value="scope.row.sendStatus" /> + <div>{{ formatDate(scope.row.sendTime) }}</div> + </template> + </el-table-column> + <el-table-column label="接收状态" align="center" width="180"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS" :value="scope.row.receiveStatus" /> + <div>{{ formatDate(scope.row.receiveTime) }}</div> + </template> + </el-table-column> + <el-table-column label="短信渠道" align="center" width="120"> + <template #default="scope"> + <div> + {{ channelList.find((channel) => channel.id === scope.row.channelId)?.signature }} + </div> + <dict-tag :type="DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE" :value="scope.row.channelCode" /> + </template> + </el-table-column> + <el-table-column label="模板编号" align="center" prop="templateId" /> + <el-table-column label="短信类型" align="center" prop="templateType"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE" :value="scope.row.templateType" /> + </template> + </el-table-column> + <el-table-column label="操作" align="center" fixed="right" class-name="fixed-width"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openDetail(scope.row)" + v-hasPermi="['system:sms-log:query']" + > + 详情 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:详情 --> + <SmsLogDetail ref="detailRef" /> +</template> +<script setup lang="ts" name="smsLog"> +import { DICT_TYPE, getIntDictOptions, getDictLabel } from '@/utils/dict' +import { dateFormatter, formatDate } from '@/utils/formatTime' +import download from '@/utils/download' +import * as SmsChannelApi from '@/api/system/sms/smsChannel' +import * as SmsLogApi from '@/api/system/sms/smsLog' +import SmsLogDetail from './SmsLogDetail.vue' +const message = useMessage() // 消息弹窗 + +const loading = ref(false) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数据 +const queryFormRef = ref() // 搜索的表单 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + channelId: null, + templateId: null, + mobile: '', + sendStatus: null, + receiveStatus: null, + sendTime: [], + receiveTime: [] +}) +const exportLoading = ref(false) // 导出的加载中 +const channelList = ref([]) // 短信渠道列表 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await SmsLogApi.getSmsLogPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 导出按钮操作 */ +const handleExport = async () => { + try { + // 导出的二次确认 + await message.exportConfirm() + // 发起导出 + exportLoading.value = true + const data = await SmsLogApi.exportSmsLog(queryParams) + download.excel(data, '短信日志.xls') + } catch { + } finally { + exportLoading.value = false + } +} + +/** 详情操作 */ +const detailRef = ref() +const openDetail = (data: SmsLogApi.SmsLogVO) => { + detailRef.value.open(data) +} + +/** 初始化 **/ +onMounted(async () => { + await getList() + // 加载渠道列表 + channelList.value = await SmsChannelApi.getSimpleSmsChannelList() +}) +</script> diff --git a/src/views/system/sms/smsLog/index.vue b/src/views/system/sms/smsLog/index.vue deleted file mode 100644 index f4a0ca9f..00000000 --- a/src/views/system/sms/smsLog/index.vue +++ /dev/null @@ -1,382 +0,0 @@ -<template> - <content-wrap> - <!-- 搜索工作栏 --> - <el-form - :model="queryParams" - ref="queryForm" - :inline="true" - v-show="showSearch" - label-width="100px" - > - <el-form-item label="手机号" prop="mobile"> - <el-input - v-model="queryParams.mobile" - placeholder="请输入手机号" - clearable - @keyup.enter="handleQuery" - /> - </el-form-item> - <el-form-item label="短信渠道" prop="channelId"> - <el-select v-model="queryParams.channelId" placeholder="请选择短信渠道" clearable> - <el-option - v-for="channel in channelOptions" - :key="channel.id" - :value="channel.id" - :label=" - channel.signature + optionLabel(DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE, channel.code) - " - /> - </el-select> - </el-form-item> - <el-form-item label="模板编号" prop="templateId"> - <el-input - v-model="queryParams.templateId" - placeholder="请输入模板编号" - clearable - @keyup.enter="handleQuery" - /> - </el-form-item> - <el-form-item label="发送状态" prop="sendStatus"> - <el-select v-model="queryParams.sendStatus" placeholder="请选择发送状态" clearable> - <el-option - v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_SMS_SEND_STATUS)" - :key="dict.value" - :label="dict.label" - :value="dict.value" - /> - </el-select> - </el-form-item> - <el-form-item label="发送时间" prop="sendTime"> - <el-date-picker - v-model="queryParams.sendTime" - style="width: 240px" - value-format="YYYY-MM-DD HH:mm:ss" - type="daterange" - range-separator="-" - start-placeholder="开始日期" - end-placeholder="结束日期" - /> - </el-form-item> - <el-form-item label="接收状态" prop="receiveStatus"> - <el-select v-model="queryParams.receiveStatus" placeholder="请选择接收状态" clearable> - <el-option - v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS)" - :key="dict.value" - :label="dict.label" - :value="dict.value" - /> - </el-select> - </el-form-item> - <el-form-item label="接收时间" prop="receiveTime"> - <el-date-picker - v-model="queryParams.receiveTime" - style="width: 240px" - value-format="YYYY-MM-DD HH:mm:ss" - type="daterange" - range-separator="-" - start-placeholder="开始日期" - end-placeholder="结束日期" - /> - </el-form-item> - <el-form-item> - <el-button type="primary" @click="handleQuery" - ><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button - > - <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> - </el-form-item> - </el-form> - <!-- 操作工具栏 --> - <el-row class="mb-10px"> - <el-col :span="12"> - <el-row :gutter="10"> - <el-col :span="1.5"> - <el-button - type="success" - plain - @click="handleExport" - :loading="exportLoading" - v-hasPermi="['system:sms-log:export']" - > - <Icon icon="ep:download" class="mr-5px" /> 导出 - </el-button> - </el-col> - </el-row> - </el-col> - <el-col :span="12"> - <right-toolbar v-model:showSearch="showSearch" @query-table="getList" /> - </el-col> - </el-row> - <!-- 列表 --> - <el-table v-loading="loading" :data="smsLoglist"> - <el-table-column label="编号" align="center" prop="id" /> - <el-table-column label="创建时间" align="center" prop="createTime" width="180"> - <template #default="scope"> - <span>{{ parseTime(scope.row.createTime) }}</span> - </template> - </el-table-column> - <el-table-column label="手机号" align="center" prop="mobile" width="120"> - <template #default="scope"> - <div>{{ scope.row.mobile }}</div> - <div v-if="scope.row.userType && scope.row.userId"> - <dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType" />{{ - '(' + scope.row.userId + ')' - }} - </div> - </template> - </el-table-column> - <el-table-column label="短信内容" align="center" prop="templateContent" width="300" /> - <el-table-column label="发送状态" align="center" width="180"> - <template #default="scope"> - <dict-tag :type="DICT_TYPE.SYSTEM_SMS_SEND_STATUS" :value="scope.row.sendStatus" /> - <div>{{ parseTime(scope.row.sendTime) }}</div> - </template> - </el-table-column> - <el-table-column label="接收状态" align="center" width="180"> - <template #default="scope"> - <dict-tag :type="DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS" :value="scope.row.receiveStatus" /> - <div>{{ parseTime(scope.row.receiveTime) }}</div> - </template> - </el-table-column> - <el-table-column label="短信渠道" align="center" width="120"> - <template #default="scope"> - <div>{{ formatChannelSignature(scope.row.channelId) }}</div> - <dict-tag :type="DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE" :value="scope.row.channelCode" /> - </template> - </el-table-column> - <el-table-column label="模板编号" align="center" prop="templateId" /> - <el-table-column label="短信类型" align="center" prop="templateType"> - <template #default="scope"> - <dict-tag :type="DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE" :value="scope.row.templateType" /> - </template> - </el-table-column> - <el-table-column - label="操作" - align="center" - fixed="right" - class-name="small-padding fixed-width" - > - <template #default="scope"> - <el-button - link - type="primary" - @click="handleView(scope.row)" - v-hasPermi="['system:sms-log:query']" - ><Icon icon="ep:view" class="mr-3px" />详情</el-button - > - </template> - </el-table-column> - </el-table> - <!-- 分页 --> - <Pagination - :total="total" - v-model:page="queryParams.pageNo" - v-model:limit="queryParams.pageSize" - @pagination="getList" - /> - <!-- 短信日志详细 --> - <Dialog title="短信日志详情" v-model="open"> - <el-descriptions border :column="1"> - <el-descriptions-item label-align="right" width="50px" label="日志主键:"> - {{ formData.id }} - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="短信渠道:"> - {{ formatChannelSignature(formData.channelId) }} - <dict-tag :type="DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE" :value="formData.channelCode" /> - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="短信模板:"> - {{ formData.templateId }} | {{ formData.templateCode }} - <dict-tag :type="DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE" :value="formData.templateType" /> - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="API 的模板编号:"> - {{ formData.apiTemplateId }} - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="用户信息:"> - {{ formData.mobile }} - <span v-if="formData.userType && formData.userId"> - <dict-tag :type="DICT_TYPE.USER_TYPE" :value="formData.userType" /> - ({{ formData.userId }}) - </span> - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="短信内容:"> - {{ formData.templateContent }} - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="短信参数:"> - {{ formData.templateParams }} - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="创建时间:"> - {{ parseTime(formData.createTime) }} - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="发送状态:"> - <dict-tag :type="DICT_TYPE.SYSTEM_SMS_SEND_STATUS" :value="formData.sendStatus" /> - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="发送时间:"> - {{ parseTime(formData.sendTime) }} - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="发送结果:"> - {{ formData.sendCode }} | {{ formData.sendMsg }} - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="API 发送结果:"> - {{ formData.apiSendCode }} | {{ formData.apiSendMsg }} - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="API 短信编号:"> - {{ formData.apiSerialNo }} - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="API 请求编号:"> - {{ formData.apiRequestId }} - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="接收状态:"> - <dict-tag :type="DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS" :value="formData.receiveStatus" /> - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="接收时间:"> - {{ parseTime(formData.receiveTime) }} - </el-descriptions-item> - <el-descriptions-item label-align="right" width="50px" label="API 接收结果:"> - {{ formData.apiReceiveCode }} | {{ formData.apiReceiveMsg }} - </el-descriptions-item> - </el-descriptions> - <template #footer> - <el-button @click="open = false">关 闭</el-button> - </template> - </Dialog> - </content-wrap> -</template> -<script setup lang="ts" name="smsLog"> -import { DICT_TYPE, getDictOptions, getDictLabel } from '@/utils/dict' -import download from '@/utils/download' -import { parseTime } from '@/utils/formatTime' -import * as SmsChannelApi from '@/api/system/sms/smsChannel' -import * as SmsLogApi from '@/api/system/sms/smsLog' - -const message = useMessage() // 消息弹窗 - -// ================= 表单相关================ -const showSearch = ref(true) // 显示搜索条件 -const queryForm = ref() // queryFormRef -// 查询参数 -const queryParams = ref<SmsLogApi.SmsLogPageReqVO>({ - pageNo: 1, - pageSize: 10, - channelId: null, - templateId: null, - mobile: '', - sendStatus: null, - receiveStatus: null, - sendTime: [], - receiveTime: [] -}) -// 短信渠道列表 -const channelOptions = ref<SmsChannelApi.SmsChannelListVO[]>([]) -onMounted(() => { - SmsChannelApi.getSimpleSmsChannels().then((res) => { - channelOptions.value = res - }) -}) -const optionLabel = computed( - () => (type: string, code: string) => `【${getDictLabel(type, code)}】` -) -/** 格式化短信渠道 */ -const formatChannelSignature = (channelId: number | null) => { - channelOptions.value.forEach((item) => { - if (item.id === channelId) { - return item.signature - } - }) - return '找不到签名:' + channelId -} -// ================= 表单相关操作================ -/** 搜索按钮操作 */ -const handleQuery = () => { - queryParams.value.pageNo = 1 - getList() -} -/** 重置按钮操作 */ -const resetQuery = () => { - resetForm() - handleQuery() -} -/** 重置搜索表单 */ -const resetForm = () => { - queryParams.value = { - pageNo: 1, - pageSize: 10, - channelId: null, - templateId: null, - mobile: '', - sendStatus: null, - receiveStatus: null, - sendTime: [], - receiveTime: [] - } - queryForm.value?.resetFields() -} -// 导出遮罩层 -const exportLoading = ref(false) -/** 导出按钮操作 */ -const handleExport = () => { - // 处理查询参数 - let params = queryParams.value as SmsLogApi.SmsLogExportReqVO - // 执行导出 - message - .confirm('是否确认导出所有短信日志数据项?') - .then(() => { - exportLoading.value = true - return SmsLogApi.exportSmsLogApi(params) - }) - .then((response) => { - download.excel(response, '短信日志.xls') - exportLoading.value = false - }) - .catch(() => {}) -} -// ================== 表格 ==================== -// 总条数 -const total = ref(0) -// 短信日志列表 -const smsLoglist = ref([]) -const loading = ref(false) -/** 查询列表 */ -const getList = () => { - loading.value = true - // 执行查询 - SmsLogApi.getSmsLogPageApi(queryParams.value).then((response) => { - smsLoglist.value = response.list - total.value = response.total - loading.value = false - }) -} -// ================== 详情 ==================== -const open = ref(false) -const formData = ref<SmsLogApi.SmsLogVO>({ - id: null, - channelId: null, - channelCode: '', - templateId: null, - templateCode: '', - templateType: null, - templateContent: '', - templateParams: null, - apiTemplateId: '', - mobile: '', - userId: null, - userType: null, - sendStatus: null, - sendTime: null, - sendCode: null, - sendMsg: '', - apiSendCode: '', - apiSendMsg: '', - apiRequestId: '', - apiSerialNo: '', - receiveStatus: null, - receiveTime: null, - apiReceiveCode: '', - apiReceiveMsg: '', - createTime: null -}) -/** 详细按钮操作 */ -const handleView = (row: SmsLogApi.SmsLogVO) => { - formData.value = row - open.value = true -} -getList() -</script> diff --git a/src/views/system/sms/smsTemplate/form.vue b/src/views/system/sms/template/form.vue similarity index 100% rename from src/views/system/sms/smsTemplate/form.vue rename to src/views/system/sms/template/form.vue diff --git a/src/views/system/sms/smsTemplate/index.vue b/src/views/system/sms/template/index.vue similarity index 99% rename from src/views/system/sms/smsTemplate/index.vue rename to src/views/system/sms/template/index.vue index ed934c0f..ffdd4240 100644 --- a/src/views/system/sms/smsTemplate/index.vue +++ b/src/views/system/sms/template/index.vue @@ -275,7 +275,7 @@ const resetForm = () => { // 短信渠道 const channelOptions = ref<SmsChannelApi.SmsChannelListVO[]>([]) onMounted(() => { - SmsChannelApi.getSimpleSmsChannels().then((res) => { + SmsChannelApi.getSimpleSmsChannelList().then((res) => { channelOptions.value = res }) })