Pre Merge pull request !156 from xingyu/dev

pull/156/MERGE
xingyu 2025-06-24 12:07:22 +00:00 committed by Gitee
commit 42df728de0
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
24 changed files with 512 additions and 288 deletions

View File

@ -2,6 +2,8 @@ import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemAreaApi } from '#/api/system/area'; import type { SystemAreaApi } from '#/api/system/area';
import { z } from '#/adapter/form';
/** 查询 IP 的表单 */ /** 查询 IP 的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
return [ return [
@ -12,7 +14,7 @@ export function useFormSchema(): VbenFormSchema[] {
componentProps: { componentProps: {
placeholder: '请输入 IP 地址', placeholder: '请输入 IP 地址',
}, },
rules: 'required', rules: z.string().ip({ message: '请输入正确的 IP 地址' }),
}, },
{ {
fieldName: 'result', fieldName: 'result',

View File

@ -1,6 +1,12 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue';
import { formatDateTime } from '@vben/utils';
import { DictTag } from '#/components/dict-tag';
import { DICT_TYPE, getRangePickerDefaultProps } from '#/utils'; import { DICT_TYPE, getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */ /** 列表的搜索表单 */
@ -84,3 +90,50 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}, },
]; ];
} }
/** 详情页的字段 */
export function useDetailSchema(): DescriptionItemSchema[] {
return [
{
field: 'id',
label: '日志编号',
},
{
field: 'logType',
label: '操作类型',
content: (data) => {
return h(DictTag, {
type: DICT_TYPE.SYSTEM_LOGIN_TYPE,
value: data?.logType,
});
},
},
{
field: 'username',
label: '用户名称',
},
{
field: 'userIp',
label: '登录地址',
},
{
field: 'userAgent',
label: '浏览器',
},
{
field: 'result',
label: '登录结果',
content: (data) => {
return h(DictTag, {
type: DICT_TYPE.SYSTEM_LOGIN_RESULT,
value: data?.result,
});
},
},
{
field: 'createTime',
label: '登录日期',
content: (data) => formatDateTime(data?.createTime || '') as string,
},
];
}

View File

@ -4,15 +4,22 @@ import type { SystemLoginLogApi } from '#/api/system/login-log';
import { ref } from 'vue'; import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { formatDateTime } from '@vben/utils';
import { Descriptions } from 'ant-design-vue'; import { useDescription } from '#/components/description';
import { DictTag } from '#/components/dict-tag'; import { useDetailSchema } from '../data';
import { DICT_TYPE } from '#/utils';
const formData = ref<SystemLoginLogApi.LoginLog>(); const formData = ref<SystemLoginLogApi.LoginLog>();
const [Descriptions] = useDescription({
componentProps: {
bordered: true,
column: 1,
class: 'mx-4',
},
schema: useDetailSchema(),
});
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
async onOpenChange(isOpen: boolean) { async onOpenChange(isOpen: boolean) {
if (!isOpen) { if (!isOpen) {
@ -41,40 +48,6 @@ const [Modal, modalApi] = useVbenModal({
:show-cancel-button="false" :show-cancel-button="false"
:show-confirm-button="false" :show-confirm-button="false"
> >
<Descriptions <Descriptions :data="formData" />
bordered
:column="1"
size="middle"
class="mx-4"
:label-style="{ width: '110px' }"
>
<Descriptions.Item label="日志编号">
{{ formData?.id }}
</Descriptions.Item>
<Descriptions.Item label="操作类型">
<DictTag
:type="DICT_TYPE.SYSTEM_LOGIN_TYPE"
:value="formData?.logType"
/>
</Descriptions.Item>
<Descriptions.Item label="用户名称">
{{ formData?.username }}
</Descriptions.Item>
<Descriptions.Item label="登录地址">
{{ formData?.userIp }}
</Descriptions.Item>
<Descriptions.Item label="浏览器">
{{ formData?.userAgent }}
</Descriptions.Item>
<Descriptions.Item label="登录结果">
<DictTag
:type="DICT_TYPE.SYSTEM_LOGIN_RESULT"
:value="formData?.result"
/>
</Descriptions.Item>
<Descriptions.Item label="登录日期">
{{ formatDateTime(formData?.createTime || '') }}
</Descriptions.Item>
</Descriptions>
</Modal> </Modal>
</template> </template>

View File

@ -31,7 +31,7 @@ const [Form, formApi] = useVbenForm({
class: 'w-full', class: 'w-full',
}, },
formItemClass: 'col-span-2', formItemClass: 'col-span-2',
labelWidth: 80, labelWidth: 140,
}, },
layout: 'horizontal', layout: 'horizontal',
schema: useFormSchema(), schema: useFormSchema(),
@ -83,7 +83,7 @@ const [Modal, modalApi] = useVbenModal({
</script> </script>
<template> <template>
<Modal :title="getTitle"> <Modal :title="getTitle" class="w-1/3">
<Form class="mx-4" /> <Form class="mx-4" />
</Modal> </Modal>
</template> </template>

View File

@ -1,7 +1,13 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue';
import { formatDateTime } from '@vben/utils';
import { getSimpleMailAccountList } from '#/api/system/mail/account'; import { getSimpleMailAccountList } from '#/api/system/mail/account';
import { DictTag } from '#/components/dict-tag';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils'; import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */ /** 列表的搜索表单 */
@ -117,3 +123,79 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}, },
]; ];
} }
/** 详情页的字段 */
export function useDetailSchema(): DescriptionItemSchema[] {
return [
{
field: 'id',
label: '编号',
},
{
field: 'createTime',
label: '创建时间',
content: (data) => formatDateTime(data?.createTime || '') as string,
},
{
field: 'toMail',
label: '收件邮箱',
},
{
field: 'fromMail',
label: '发送邮箱',
},
{
field: 'userId',
label: '用户编号',
},
{
field: 'userType',
label: '用户类型',
},
{
field: 'templateId',
label: '模板编号',
},
{
field: 'templateCode',
label: '模板编码',
},
{
field: 'templateTitle',
label: '邮件标题',
},
{
field: 'templateContent',
label: '邮件内容',
content: (data) => {
// 渲染HTML内容
return h('div', {
innerHTML: data?.templateContent || '',
});
},
},
{
field: 'sendStatus',
label: '发送状态',
content: (data) => {
return h(DictTag, {
type: DICT_TYPE.SYSTEM_MAIL_SEND_STATUS,
value: data?.sendStatus,
});
},
},
{
field: 'sendTime',
label: '发送时间',
content: (data) => formatDateTime(data?.sendTime || '') as string,
},
{
field: 'sendMessageId',
label: '发送消息编号',
},
{
field: 'sendException',
label: '发送异常',
},
];
}

View File

@ -4,15 +4,22 @@ import type { SystemMailLogApi } from '#/api/system/mail/log';
import { ref } from 'vue'; import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { formatDateTime } from '@vben/utils';
import { Descriptions } from 'ant-design-vue'; import { useDescription } from '#/components/description';
import { DictTag } from '#/components/dict-tag'; import { useDetailSchema } from '../data';
import { DICT_TYPE } from '#/utils';
const formData = ref<SystemMailLogApi.MailLog>(); const formData = ref<SystemMailLogApi.MailLog>();
const [Descriptions] = useDescription({
componentProps: {
bordered: true,
column: 2,
class: 'mx-4',
},
schema: useDetailSchema(),
});
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
async onOpenChange(isOpen: boolean) { async onOpenChange(isOpen: boolean) {
if (!isOpen) { if (!isOpen) {
@ -41,53 +48,6 @@ const [Modal, modalApi] = useVbenModal({
:show-cancel-button="false" :show-cancel-button="false"
:show-confirm-button="false" :show-confirm-button="false"
> >
<div class="p-4"> <Descriptions :data="formData" />
<Descriptions :column="2" bordered :label-style="{ width: '140px' }">
<Descriptions.Item label="编号">{{ formData?.id }}</Descriptions.Item>
<Descriptions.Item label="创建时间">
{{ formatDateTime(formData?.createTime || '') }}
</Descriptions.Item>
<Descriptions.Item label="收件邮箱">
{{ formData?.toMail }}
</Descriptions.Item>
<Descriptions.Item label="发送邮箱">
{{ formData?.fromMail }}
</Descriptions.Item>
<Descriptions.Item label="用户编号">
{{ formData?.userId }}
</Descriptions.Item>
<Descriptions.Item label="用户类型">
{{ formData?.userType }}
</Descriptions.Item>
<Descriptions.Item label="模板编号">
{{ formData?.templateId }}
</Descriptions.Item>
<Descriptions.Item label="模板编码">
{{ formData?.templateCode }}
</Descriptions.Item>
<Descriptions.Item label="邮件标题" :span="2">
{{ formData?.templateTitle }}
</Descriptions.Item>
<Descriptions.Item label="邮件内容" :span="2">
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-html="formData?.templateContent"></div>
</Descriptions.Item>
<Descriptions.Item label="发送状态">
<DictTag
:type="DICT_TYPE.SYSTEM_MAIL_SEND_STATUS"
:value="formData?.sendStatus"
/>
</Descriptions.Item>
<Descriptions.Item label="发送时间">
{{ formatDateTime(formData?.sendTime || '') }}
</Descriptions.Item>
<Descriptions.Item label="发送消息编号">
{{ formData?.sendMessageId }}
</Descriptions.Item>
<Descriptions.Item label="发送异常">
{{ formData?.sendException }}
</Descriptions.Item>
</Descriptions>
</div>
</Modal> </Modal>
</template> </template>

View File

@ -83,7 +83,7 @@ const [Modal, modalApi] = useVbenModal({
</script> </script>
<template> <template>
<Modal :title="getTitle"> <Modal :title="getTitle" class="w-1/3">
<Form class="mx-4" /> <Form class="mx-4" />
</Modal> </Modal>
</template> </template>

View File

@ -87,7 +87,7 @@ async function handleDeleteBatch() {
/** 推送公告 */ /** 推送公告 */
async function handlePush(row: SystemNoticeApi.Notice) { async function handlePush(row: SystemNoticeApi.Notice) {
const hideLoading = message.loading({ const hideLoading = message.loading({
content: $t('ui.actionMessage.processing', ['推送']), content: '正在推送中',
key: 'action_process_msg', key: 'action_process_msg',
}); });
try { try {

View File

@ -1,6 +1,12 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue';
import { formatDateTime } from '@vben/utils';
import { DictTag } from '#/components/dict-tag';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils'; import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */ /** 列表的搜索表单 */
@ -135,3 +141,84 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}, },
]; ];
} }
/** 详情页的字段 */
export function useDetailSchema(): DescriptionItemSchema[] {
return [
{
field: 'id',
label: '编号',
},
{
field: 'userType',
label: '用户类型',
content: (data) => {
return h(DictTag, {
type: DICT_TYPE.USER_TYPE,
value: data?.userType,
});
},
},
{
field: 'userId',
label: '用户编号',
},
{
field: 'templateId',
label: '模版编号',
},
{
field: 'templateCode',
label: '模板编码',
},
{
field: 'templateNickname',
label: '发送人名称',
},
{
field: 'templateContent',
label: '模版内容',
},
{
field: 'templateParams',
label: '模版参数',
content: (data) => {
try {
return JSON.stringify(data?.templateParams);
} catch {
return '';
}
},
},
{
field: 'templateType',
label: '模版类型',
content: (data) => {
return h(DictTag, {
type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE,
value: data?.templateType,
});
},
},
{
field: 'readStatus',
label: '是否已读',
content: (data) => {
return h(DictTag, {
type: DICT_TYPE.INFRA_BOOLEAN_STRING,
value: data?.readStatus,
});
},
},
{
field: 'readTime',
label: '阅读时间',
content: (data) => formatDateTime(data?.readTime || '') as string,
},
{
field: 'createTime',
label: '创建时间',
content: (data) => formatDateTime(data?.createTime || '') as string,
},
];
}

View File

@ -4,15 +4,22 @@ import type { SystemNotifyMessageApi } from '#/api/system/notify/message';
import { ref } from 'vue'; import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { formatDateTime } from '@vben/utils';
import { Descriptions } from 'ant-design-vue'; import { useDescription } from '#/components/description';
import { DictTag } from '#/components/dict-tag'; import { useDetailSchema } from '../data';
import { DICT_TYPE } from '#/utils';
const formData = ref<SystemNotifyMessageApi.NotifyMessage>(); const formData = ref<SystemNotifyMessageApi.NotifyMessage>();
const [Descriptions] = useDescription({
componentProps: {
bordered: true,
column: 1,
class: 'mx-4',
},
schema: useDetailSchema(),
});
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
async onOpenChange(isOpen: boolean) { async onOpenChange(isOpen: boolean) {
if (!isOpen) { if (!isOpen) {
@ -37,51 +44,10 @@ const [Modal, modalApi] = useVbenModal({
<template> <template>
<Modal <Modal
title="站内信详情" title="站内信详情"
class="w-1/2" class="w-1/3"
:show-cancel-button="false" :show-cancel-button="false"
:show-confirm-button="false" :show-confirm-button="false"
> >
<Descriptions bordered :column="1" size="middle" class="mx-4"> <Descriptions :data="formData" />
<Descriptions.Item label="编号">{{ formData?.id }}</Descriptions.Item>
<Descriptions.Item label="用户类型">
<DictTag :type="DICT_TYPE.USER_TYPE" :value="formData?.userType" />
</Descriptions.Item>
<Descriptions.Item label="用户编号">
{{ formData?.userId }}
</Descriptions.Item>
<Descriptions.Item label="模版编号">
{{ formData?.templateId }}
</Descriptions.Item>
<Descriptions.Item label="模板编码">
{{ formData?.templateCode }}
</Descriptions.Item>
<Descriptions.Item label="发送人名称">
{{ formData?.templateNickname }}
</Descriptions.Item>
<Descriptions.Item label="模版内容">
{{ formData?.templateContent }}
</Descriptions.Item>
<Descriptions.Item label="模版参数">
{{ formData?.templateParams }}
</Descriptions.Item>
<Descriptions.Item label="模版类型">
<DictTag
:type="DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE"
:value="formData?.templateType"
/>
</Descriptions.Item>
<Descriptions.Item label="是否已读">
<DictTag
:type="DICT_TYPE.INFRA_BOOLEAN_STRING"
:value="formData?.readStatus"
/>
</Descriptions.Item>
<Descriptions.Item label="阅读时间">
{{ formatDateTime(formData?.readTime || '') }}
</Descriptions.Item>
<Descriptions.Item label="创建时间">
{{ formatDateTime(formData?.createTime || '') }}
</Descriptions.Item>
</Descriptions>
</Modal> </Modal>
</template> </template>

View File

@ -11,7 +11,6 @@ const [Description, descApi] = useDescription({
componentProps: { componentProps: {
bordered: true, bordered: true,
column: 1, column: 1,
size: 'middle',
class: 'mx-4', class: 'mx-4',
}, },
schema: useDetailSchema(), schema: useDetailSchema(),
@ -40,6 +39,7 @@ const [Modal, modalApi] = useVbenModal({
<template> <template>
<Modal <Modal
title="消息详情" title="消息详情"
class="w-1/3"
:show-cancel-button="false" :show-cancel-button="false"
:show-confirm-button="false" :show-confirm-button="false"
> >

View File

@ -116,7 +116,17 @@ export function useFormSchema(): VbenFormSchema[] {
componentProps: { componentProps: {
placeholder: '请输入自动授权范围', placeholder: '请输入自动授权范围',
mode: 'multiple', mode: 'multiple',
// TODO @芋艿:根据权限,自动授权范围 },
dependencies: {
triggerFields: ['scopes'],
componentProps: (values) => ({
options: values.scopes
? values.scopes.map((scope: string) => ({
label: scope,
value: scope,
}))
: [],
}),
}, },
}, },
{ {

View File

@ -30,9 +30,10 @@ const [Form, formApi] = useVbenForm({
componentProps: { componentProps: {
class: 'w-full', class: 'w-full',
}, },
formItemClass: 'col-span-2',
labelWidth: 140, labelWidth: 140,
}, },
// 2
wrapperClass: 'grid-cols-2',
layout: 'horizontal', layout: 'horizontal',
schema: useFormSchema(), schema: useFormSchema(),
showDefaultActions: false, showDefaultActions: false,
@ -83,7 +84,7 @@ const [Modal, modalApi] = useVbenModal({
</script> </script>
<template> <template>
<Modal class="w-2/5" :title="getTitle"> <Modal class="w-1/2" :title="getTitle">
<Form class="mx-4" /> <Form class="mx-4" />
</Modal> </Modal>
</template> </template>

View File

@ -88,7 +88,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
icon: ACTION_ICON.DELETE, icon: ACTION_ICON.DELETE,
auth: ['system:oauth2-token:delete'], auth: ['system:oauth2-token:delete'],
popConfirm: { popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]), title: `确定要强退令牌吗?`,
confirm: handleDelete.bind(null, row), confirm: handleDelete.bind(null, row),
}, },
}, },

View File

@ -1,5 +1,8 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { DescriptionItemSchema } from '#/components/description';
import { formatDateTime } from '@vben/utils';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils'; import { getRangePickerDefaultProps } from '#/utils';
@ -113,3 +116,68 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}, },
]; ];
} }
/** 详情页的字段 */
export function useDetailSchema(): DescriptionItemSchema[] {
return [
{
field: 'id',
label: '日志编号',
},
{
field: 'traceId',
label: '链路追踪',
content: (data) => data?.traceId || '',
},
{
field: 'userId',
label: '操作人编号',
},
{
field: 'userName',
label: '操作人名字',
},
{
field: 'userIp',
label: '操作人IP',
},
{
field: 'userAgent',
label: '操作人UA',
},
{
field: 'type',
label: '操作模块',
},
{
field: 'subType',
label: '操作名',
},
{
field: 'action',
label: '操作内容',
},
{
field: 'extra',
label: '操作拓展参数',
},
{
field: 'requestUrl',
label: '请求URL',
content: (data) => {
const method = data?.requestMethod || '';
const url = data?.requestUrl || '';
return `${method} ${url}`.trim();
},
},
{
field: 'createTime',
label: '操作时间',
content: (data) => formatDateTime(data?.createTime || '') as string,
},
{
field: 'bizId',
label: '业务编号',
},
];
}

View File

@ -4,12 +4,22 @@ import type { SystemOperateLogApi } from '#/api/system/operate-log';
import { ref } from 'vue'; import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { formatDateTime } from '@vben/utils';
import { Descriptions } from 'ant-design-vue'; import { useDescription } from '#/components/description';
import { useDetailSchema } from '../data';
const formData = ref<SystemOperateLogApi.OperateLog>(); const formData = ref<SystemOperateLogApi.OperateLog>();
const [Descriptions] = useDescription({
componentProps: {
bordered: true,
column: 1,
class: 'mx-4',
},
schema: useDetailSchema(),
});
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
async onOpenChange(isOpen: boolean) { async onOpenChange(isOpen: boolean) {
if (!isOpen) { if (!isOpen) {
@ -38,52 +48,6 @@ const [Modal, modalApi] = useVbenModal({
:show-cancel-button="false" :show-cancel-button="false"
:show-confirm-button="false" :show-confirm-button="false"
> >
<Descriptions <Descriptions :data="formData" />
bordered
:column="1"
size="middle"
class="mx-4"
:label-style="{ width: '110px' }"
>
<Descriptions.Item label="日志编号">
{{ formData?.id }}
</Descriptions.Item>
<Descriptions.Item label="链路追踪" v-if="formData?.traceId">
{{ formData?.traceId }}
</Descriptions.Item>
<Descriptions.Item label="操作人编号">
{{ formData?.userId }}
</Descriptions.Item>
<Descriptions.Item label="操作人名字">
{{ formData?.userName }}
</Descriptions.Item>
<Descriptions.Item label="操作人IP">
{{ formData?.userIp }}
</Descriptions.Item>
<Descriptions.Item label="操作人UA">
{{ formData?.userAgent }}
</Descriptions.Item>
<Descriptions.Item label="操作模块">
{{ formData?.type }}
</Descriptions.Item>
<Descriptions.Item label="操作名">
{{ formData?.subType }}
</Descriptions.Item>
<Descriptions.Item label="操作内容">
{{ formData?.action }}
</Descriptions.Item>
<Descriptions.Item v-if="formData?.extra" label="操作拓展参数">
{{ formData?.extra }}
</Descriptions.Item>
<Descriptions.Item label="请求URL">
{{ formData?.requestMethod }} {{ formData?.requestUrl }}
</Descriptions.Item>
<Descriptions.Item label="操作时间">
{{ formatDateTime(formData?.createTime || '') }}
</Descriptions.Item>
<Descriptions.Item label="业务编号">
{{ formData?.bizId }}
</Descriptions.Item>
</Descriptions>
</Modal> </Modal>
</template> </template>

View File

@ -1,7 +1,13 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue';
import { formatDateTime } from '@vben/utils';
import { getSimpleSmsChannelList } from '#/api/system/sms/channel'; import { getSimpleSmsChannelList } from '#/api/system/sms/channel';
import { DictTag } from '#/components/dict-tag';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils'; import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */ /** 列表的搜索表单 */
@ -153,3 +159,94 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}, },
]; ];
} }
/** 详情页的字段 */
export function useDetailSchema(): DescriptionItemSchema[] {
return [
{
field: 'createTime',
label: '创建时间',
content: (data) => formatDateTime(data?.createTime || '') as string,
},
{
field: 'mobile',
label: '手机号',
},
{
field: 'channelCode',
label: '短信渠道',
},
{
field: 'templateId',
label: '模板编号',
},
{
field: 'templateType',
label: '模板类型',
content: (data) => {
return h(DictTag, {
type: DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE,
value: data?.templateType,
});
},
},
{
field: 'templateContent',
label: '短信内容',
},
{
field: 'sendStatus',
label: '发送状态',
content: (data) => {
return h(DictTag, {
type: DICT_TYPE.SYSTEM_SMS_SEND_STATUS,
value: data?.sendStatus,
});
},
},
{
field: 'sendTime',
label: '发送时间',
content: (data) => formatDateTime(data?.sendTime || '') as string,
},
{
field: 'apiSendCode',
label: 'API 发送编码',
},
{
field: 'apiSendMsg',
label: 'API 发送消息',
},
{
field: 'receiveStatus',
label: '接收状态',
content: (data) => {
return h(DictTag, {
type: DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS,
value: data?.receiveStatus,
});
},
},
{
field: 'receiveTime',
label: '接收时间',
content: (data) => formatDateTime(data?.receiveTime || '') as string,
},
{
field: 'apiReceiveCode',
label: 'API 接收编码',
},
{
field: 'apiReceiveMsg',
label: 'API 接收消息',
},
{
field: 'apiRequestId',
label: 'API 请求 ID',
},
{
field: 'apiSerialNo',
label: 'API 序列号',
},
];
}

View File

@ -4,15 +4,22 @@ import type { SystemSmsLogApi } from '#/api/system/sms/log';
import { ref } from 'vue'; import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { formatDateTime } from '@vben/utils';
import { Descriptions } from 'ant-design-vue'; import { useDescription } from '#/components/description';
import { DictTag } from '#/components/dict-tag'; import { useDetailSchema } from '../data';
import { DICT_TYPE } from '#/utils';
const formData = ref<SystemSmsLogApi.SmsLog>(); const formData = ref<SystemSmsLogApi.SmsLog>();
const [Descriptions] = useDescription({
componentProps: {
bordered: true,
column: 2,
class: 'mx-4',
},
schema: useDetailSchema(),
});
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
async onOpenChange(isOpen: boolean) { async onOpenChange(isOpen: boolean) {
if (!isOpen) { if (!isOpen) {
@ -41,70 +48,6 @@ const [Modal, modalApi] = useVbenModal({
:show-cancel-button="false" :show-cancel-button="false"
:show-confirm-button="false" :show-confirm-button="false"
> >
<Descriptions <Descriptions :data="formData" />
bordered
:column="2"
size="middle"
class="mx-4"
:label-style="{ width: '140px' }"
>
<Descriptions.Item label="创建时间">
{{ formatDateTime(formData?.createTime || '') }}
</Descriptions.Item>
<Descriptions.Item label="手机号">
{{ formData?.mobile }}
</Descriptions.Item>
<Descriptions.Item label="短信渠道">
{{ formData?.channelCode }}
</Descriptions.Item>
<Descriptions.Item label="模板编号">
{{ formData?.templateId }}
</Descriptions.Item>
<Descriptions.Item label="模板类型">
<DictTag
:type="DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE"
:value="formData?.templateType"
/>
</Descriptions.Item>
<Descriptions.Item label="短信内容">
{{ formData?.templateContent }}
</Descriptions.Item>
<Descriptions.Item label="发送状态">
<DictTag
:type="DICT_TYPE.SYSTEM_SMS_SEND_STATUS"
:value="formData?.sendStatus"
/>
</Descriptions.Item>
<Descriptions.Item label="发送时间">
{{ formatDateTime(formData?.sendTime || '') }}
</Descriptions.Item>
<Descriptions.Item label="API 发送编码">
{{ formData?.apiSendCode }}
</Descriptions.Item>
<Descriptions.Item label="API 发送消息">
{{ formData?.apiSendMsg }}
</Descriptions.Item>
<Descriptions.Item label="接收状态">
<DictTag
:type="DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS"
:value="formData?.receiveStatus"
/>
</Descriptions.Item>
<Descriptions.Item label="接收时间">
{{ formatDateTime(formData?.receiveTime || '') }}
</Descriptions.Item>
<Descriptions.Item label="API 接收编码">
{{ formData?.apiReceiveCode }}
</Descriptions.Item>
<Descriptions.Item label="API 接收消息" :span="2">
{{ formData?.apiReceiveMsg }}
</Descriptions.Item>
<Descriptions.Item label="API 请求 ID">
{{ formData?.apiRequestId }}
</Descriptions.Item>
<Descriptions.Item label="API 序列号">
{{ formData?.apiSerialNo }}
</Descriptions.Item>
</Descriptions>
</Modal> </Modal>
</template> </template>

View File

@ -108,6 +108,8 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
gridOptions: { gridOptions: {
columns: useGridColumns(getPackageName), columns: useGridColumns(getPackageName),
height: 'auto',
keepSource: true,
proxyConfig: { proxyConfig: {
ajax: { ajax: {
query: async ({ page }, formValues) => { query: async ({ page }, formValues) => {

View File

@ -26,9 +26,9 @@ const [Form, formApi] = useVbenForm({
componentProps: { componentProps: {
class: 'w-full', class: 'w-full',
}, },
formItemClass: 'col-span-2',
labelWidth: 80,
}, },
// 2
wrapperClass: 'grid-cols-2',
layout: 'horizontal', layout: 'horizontal',
schema: useFormSchema(), schema: useFormSchema(),
showDefaultActions: false, showDefaultActions: false,
@ -75,7 +75,7 @@ const [Modal, modalApi] = useVbenModal({
}); });
</script> </script>
<template> <template>
<Modal :title="getTitle"> <Modal :title="getTitle" class="w-1/3">
<Form class="mx-4" /> <Form class="mx-4" />
</Modal> </Modal>
</template> </template>

View File

@ -8,7 +8,7 @@ import { ref } from 'vue';
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui'; import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { downloadFileFromBlobPart, isEmpty } from '@vben/utils'; import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
import { message } from 'ant-design-vue'; import { Card, message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { import {
@ -215,9 +215,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
<div class="flex h-full w-full"> <div class="flex h-full w-full">
<!-- 左侧部门树 --> <!-- 左侧部门树 -->
<div class="h-full w-1/6 pr-4"> <Card class="mr-4 h-full w-1/6">
<DeptTree @select="handleDeptSelect" /> <DeptTree @select="handleDeptSelect" />
</div> </Card>
<!-- 右侧用户列表 --> <!-- 右侧用户列表 -->
<div class="w-5/6"> <div class="w-5/6">
<Grid table-title=""> <Grid table-title="">

View File

@ -26,9 +26,9 @@ const [Form, formApi] = useVbenForm({
componentProps: { componentProps: {
class: 'w-full', class: 'w-full',
}, },
formItemClass: 'col-span-2',
labelWidth: 80,
}, },
// 2
wrapperClass: 'grid-cols-2',
layout: 'horizontal', layout: 'horizontal',
schema: useFormSchema(), schema: useFormSchema(),
showDefaultActions: false, showDefaultActions: false,
@ -76,7 +76,7 @@ const [Modal, modalApi] = useVbenModal({
</script> </script>
<template> <template>
<Modal :title="getTitle"> <Modal :title="getTitle" class="w-1/3">
<Form class="mx-4" /> <Form class="mx-4" />
</Modal> </Modal>
</template> </template>

View File

@ -1,10 +1,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { DocAlertProps } from './types'; import type { DocAlertProps } from './types';
import { ref } from 'vue';
import { isDocAlertEnable } from '@vben/hooks'; import { isDocAlertEnable } from '@vben/hooks';
import { VbenIcon } from '@vben-core/shadcn-ui'; import { VbenIcon } from '@vben-core/shadcn-ui';
import { cn, openWindow } from '@vben-core/shared/utils'; import { openWindow } from '@vben-core/shared/utils';
defineOptions({ defineOptions({
name: 'DocAlert', name: 'DocAlert',
@ -12,28 +14,42 @@ defineOptions({
const props = defineProps<DocAlertProps>(); const props = defineProps<DocAlertProps>();
/** 控制组件显示状态 */
const isVisible = ref(true);
function goToUrl() { function goToUrl() {
openWindow(props.url); openWindow(props.url);
} }
function close() {
isVisible.value = false;
}
</script> </script>
<template> <template>
<!-- Alert Component -->
<!-- TODO @xingyu是不是左右边距 + 和搜索的对齐会好看一丢丢的 -->
<div <div
role="alert" role="alert"
v-if="isDocAlertEnable()" v-if="isDocAlertEnable() && isVisible"
:class=" class="border-primary bg-primary/10 relative my-2 flex h-8 w-full items-center gap-2 rounded-md border p-2"
cn(
'border-primary bg-primary/10 relative m-1 flex w-full items-center gap-5 rounded-md border p-1',
)
"
> >
<span class="grid shrink-0 place-items-center"> <span class="grid shrink-0 place-items-center">
<VbenIcon icon="mdi:information-outline" class="text-primary size-5" /> <VbenIcon icon="mdi:information-outline" class="text-primary size-5" />
</span> </span>
<div class="text-primary w-full font-sans text-sm leading-none"> <div class="text-primary min-w-0 flex-1 font-sans text-sm leading-none">
{{ title }}文档地址 <span class="inline-block">{{ title }}</span>
<a class="hover:text-primary" @click="goToUrl">{{ url }}</a> <a
class="hover:text-success cursor-pointer break-all"
@click="goToUrl"
:title="url"
>
文档地址{{ url }}
</a>
</div> </div>
<span class="grid shrink-0 cursor-pointer place-items-center">
<VbenIcon
icon="mdi:close"
class="text-primary size-5 hover:text-red-500"
@click="close"
/>
</span>
</div> </div>
</template> </template>

View File

@ -63,7 +63,7 @@ onMounted(() => {
ref="docRef" ref="docRef"
:class=" :class="
cn( cn(
'bg-card border-border relative flex items-start rounded-md border-b p-1', 'bg-card border-border relative mx-4 flex items-start rounded-md border-b',
) )
" "
> >