From 81af5b5bdf1e45f7dd57bb33dd4a2da2d0e910b7 Mon Sep 17 00:00:00 2001 From: sunlei Date: Wed, 10 Jun 2026 13:21:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E7=AB=99=E5=86=85=E4=BF=A1=E9=A1=B5=E9=9D=A2=E4=B8=8E=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E8=81=94=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-antdv-next/src/api/core/menu.ts | 4 + apps/web-antdv-next/src/api/system/index.ts | 1 + apps/web-antdv-next/src/api/system/notice.ts | 104 ++++++++ .../src/locales/langs/en-US/system.json | 25 ++ .../src/locales/langs/zh-CN/system.json | 25 ++ .../src/router/routes/modules/system.ts | 9 + .../src/views/system/notice/data.ts | 156 +++++++++++ .../src/views/system/notice/list.vue | 243 ++++++++++++++++++ .../src/views/system/notice/modules/form.vue | 95 +++++++ 9 files changed, 662 insertions(+) create mode 100644 apps/web-antdv-next/src/api/system/notice.ts create mode 100644 apps/web-antdv-next/src/views/system/notice/data.ts create mode 100644 apps/web-antdv-next/src/views/system/notice/list.vue create mode 100644 apps/web-antdv-next/src/views/system/notice/modules/form.vue diff --git a/apps/web-antdv-next/src/api/core/menu.ts b/apps/web-antdv-next/src/api/core/menu.ts index 5c0bb6d..4d8c738 100644 --- a/apps/web-antdv-next/src/api/core/menu.ts +++ b/apps/web-antdv-next/src/api/core/menu.ts @@ -69,6 +69,10 @@ const SUPPORTED_ADMIN_MENU_NAMES = new Set([ 'SystemMenuCreate', 'SystemMenuDelete', 'SystemMenuEdit', + 'SystemNotice', + 'SystemNoticeCreate', + 'SystemNoticeDelete', + 'SystemNoticeEdit', 'SystemRole', 'SystemRoleCreate', 'SystemRoleDelete', diff --git a/apps/web-antdv-next/src/api/system/index.ts b/apps/web-antdv-next/src/api/system/index.ts index 828ddbb..dd4e162 100644 --- a/apps/web-antdv-next/src/api/system/index.ts +++ b/apps/web-antdv-next/src/api/system/index.ts @@ -3,4 +3,5 @@ export * from './dict'; export * from './log'; export * from './menu'; export * from './role'; +export * from './notice'; export * from './user'; diff --git a/apps/web-antdv-next/src/api/system/notice.ts b/apps/web-antdv-next/src/api/system/notice.ts new file mode 100644 index 0000000..3281448 --- /dev/null +++ b/apps/web-antdv-next/src/api/system/notice.ts @@ -0,0 +1,104 @@ +import type { Recordable } from '@vben/types'; + +import { requestClient } from '#/api/request'; + +export namespace SystemNoticeApi { + export interface NoticeItem { + [key: string]: any; + content: string; + createTime?: string; + createdBy?: string; + id: string; + isDeleted: boolean; + isTop: boolean; + level: number; + notifyUsers?: string; + status: 0 | 1; + summary?: string; + title: string; + updateTime?: string; + } + + export interface NoticeInput { + content: string; + id?: string; + isTop?: boolean; + level?: number; + notifyUsers?: string; + status?: 0 | 1; + summary?: string; + title: string; + } + + export interface NoticeQuery { + [key: string]: any; + isTop?: boolean | number | string; + keyword?: string; + level?: number | string; + notifyUsers?: string; + page?: number; + pageNo?: number; + pageSize?: number; + status?: 0 | 1 | number | string; + } + + export interface PageResult { + items: T[]; + total: number; + } +} + +async function getNoticeList(params: Recordable) { + return requestClient.get>( + '/system/notice/list', + { params }, + ); +} + +async function getNoticeDetail(id: string) { + return requestClient.get(`/system/notice/detail/${id}`); +} + +async function createNotice(data: SystemNoticeApi.NoticeInput) { + return requestClient.post('/system/notice/save', data); +} + +async function updateNotice(id: string, data: SystemNoticeApi.NoticeInput) { + return requestClient.post('/system/notice/update', { + ...data, + id, + }); +} + +async function deleteNotice(id: string) { + return requestClient.delete(`/system/notice/${id}`); +} + +async function toggleNoticeStatus(id: string, status: SystemNoticeApi.NoticeItem['status']) { + return requestClient.post('/system/notice/toggle', undefined, { + params: { + id, + status, + }, + }); +} + +async function toggleNoticeTop(id: string, isTop: boolean) { + return requestClient.post('/system/notice/top', undefined, { + params: { + id, + isTop: isTop ? 1 : 0, + }, + }); +} + +export { + createNotice, + deleteNotice, + getNoticeDetail, + getNoticeList, + toggleNoticeStatus, + toggleNoticeTop, + updateNotice, +}; + diff --git a/apps/web-antdv-next/src/locales/langs/en-US/system.json b/apps/web-antdv-next/src/locales/langs/en-US/system.json index 7a1574f..b0fd710 100644 --- a/apps/web-antdv-next/src/locales/langs/en-US/system.json +++ b/apps/web-antdv-next/src/locales/langs/en-US/system.json @@ -111,6 +111,31 @@ "total": "Total", "unconfigured": "Loki Not Configured" }, + "notice": { + "content": "Content", + "createTime": "Create Time", + "deleteConfirm": "Confirm deleting \"{0}\"?", + "disableSuccess": "Notice disabled", + "enableSuccess": "Notice enabled", + "isTop": "Top", + "keyword": "Keyword", + "level": "Level", + "levelText": "Level {level}", + "name": "Notice", + "notifyUsers": "Notify Users", + "status": "Status", + "summary": "Summary", + "title": "Notices", + "top": "Pinned", + "topNo": "No", + "topYes": "Yes", + "topSuccess": "Pinned", + "cancelTopSuccess": "Unpinned", + "toggle": "Toggle", + "toggleStatusConfirm": "Confirm toggling \"{0}\" status?", + "topToggle": "Pin Toggle", + "toggleTopConfirm": "Confirm toggling \"{0}\" pin state?" + }, "role": { "title": "Role Management", "list": "Role List", diff --git a/apps/web-antdv-next/src/locales/langs/zh-CN/system.json b/apps/web-antdv-next/src/locales/langs/zh-CN/system.json index 993f1e6..6674f61 100644 --- a/apps/web-antdv-next/src/locales/langs/zh-CN/system.json +++ b/apps/web-antdv-next/src/locales/langs/zh-CN/system.json @@ -113,6 +113,31 @@ "total": "总量", "unconfigured": "Loki 未配置" }, + "notice": { + "content": "内容", + "createTime": "创建时间", + "deleteConfirm": "确认删除「{0}」吗?", + "disableSuccess": "已停用站内信", + "enableSuccess": "已启用站内信", + "isTop": "置顶", + "keyword": "关键字", + "level": "等级", + "levelText": "等级 {level}", + "name": "站内信", + "notifyUsers": "通知用户", + "status": "状态", + "summary": "摘要", + "title": "站内信管理", + "top": "置顶", + "topNo": "否", + "topYes": "是", + "topSuccess": "置顶成功", + "cancelTopSuccess": "取消置顶成功", + "toggle": "启停", + "toggleStatusConfirm": "确认切换「{0}」启停状态吗?", + "topToggle": "置顶切换", + "toggleTopConfirm": "确认切换「{0}」置顶状态吗?" + }, "role": { "title": "角色管理", "list": "角色列表", diff --git a/apps/web-antdv-next/src/router/routes/modules/system.ts b/apps/web-antdv-next/src/router/routes/modules/system.ts index 22de24e..742e7f8 100644 --- a/apps/web-antdv-next/src/router/routes/modules/system.ts +++ b/apps/web-antdv-next/src/router/routes/modules/system.ts @@ -66,6 +66,15 @@ const routes: RouteRecordRaw[] = [ }, component: () => import('#/views/system/log/list.vue'), }, + { + path: '/system/notice', + name: 'SystemNotice', + meta: { + icon: 'mdi:bell-outline', + title: $t('system.notice.title'), + }, + component: () => import('#/views/system/notice/list.vue'), + }, { path: '/system/ktTableDemo', name: 'SystemKtTableDemo', diff --git a/apps/web-antdv-next/src/views/system/notice/data.ts b/apps/web-antdv-next/src/views/system/notice/data.ts new file mode 100644 index 0000000..d263099 --- /dev/null +++ b/apps/web-antdv-next/src/views/system/notice/data.ts @@ -0,0 +1,156 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { z } from '#/adapter/form'; +import { $t } from '#/locales'; + +export const NOTICE_LEVEL_OPTIONS = [ + { label: '普通', value: 1 }, + { label: '重要', value: 2 }, + { label: '紧急', value: 3 }, +]; + +export const NOTICE_STATUS_OPTIONS = [ + { color: 'success', label: $t('common.enabled'), value: 1 }, + { color: 'default', label: $t('common.disabled'), value: 0 }, +]; + +export function getNoticeStatusOptions() { + return NOTICE_STATUS_OPTIONS; +} + +export function useSearchSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + componentProps: { + allowClear: true, + }, + fieldName: 'keyword', + label: $t('system.notice.keyword'), + }, + { + component: 'Select', + componentProps: { + allowClear: true, + options: NOTICE_LEVEL_OPTIONS, + }, + fieldName: 'level', + label: $t('system.notice.level'), + }, + { + component: 'Select', + componentProps: { + allowClear: true, + options: NOTICE_STATUS_OPTIONS, + }, + fieldName: 'status', + label: $t('system.notice.status'), + }, + { + component: 'Select', + componentProps: { + allowClear: true, + options: [ + { label: $t('common.no'), value: 0 }, + { label: $t('common.yes'), value: 1 }, + ], + }, + fieldName: 'isTop', + label: $t('system.notice.top'), + }, + ]; +} + +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '如 系统公告', + }, + fieldName: 'title', + label: $t('system.notice.title'), + rules: z + .string() + .min(1, $t('ui.formRules.required', [$t('system.notice.title')])) + .max(120, $t('ui.formRules.maxLength', [$t('system.notice.title'), 120])), + }, + { + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '如 站内信内容概要', + }, + fieldName: 'summary', + label: $t('system.notice.summary'), + rules: z + .string() + .max(200, $t('ui.formRules.maxLength', [$t('system.notice.summary'), 200])), + }, + { + component: 'TextArea', + componentProps: { + autoSize: { + maxRows: 10, + minRows: 4, + }, + allowClear: true, + placeholder: '请输入完整内容', + }, + fieldName: 'content', + label: $t('system.notice.content'), + rules: z + .string() + .min(1, $t('ui.formRules.required', [$t('system.notice.content')])), + }, + { + component: 'Select', + componentProps: { + allowClear: true, + options: NOTICE_LEVEL_OPTIONS, + }, + fieldName: 'level', + label: $t('system.notice.level'), + defaultValue: 1, + }, + { + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '逗号分隔,如:10001,10002,留空表示全部用户可见', + }, + fieldName: 'notifyUsers', + label: $t('system.notice.notifyUsers'), + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: [ + { + label: $t('system.notice.topNo'), + value: false, + }, + { + label: $t('system.notice.topYes'), + value: true, + }, + ], + }, + defaultValue: false, + fieldName: 'isTop', + label: $t('system.notice.top'), + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: NOTICE_STATUS_OPTIONS, + }, + defaultValue: 1, + fieldName: 'status', + label: $t('system.notice.status'), + }, + ]; +} diff --git a/apps/web-antdv-next/src/views/system/notice/list.vue b/apps/web-antdv-next/src/views/system/notice/list.vue new file mode 100644 index 0000000..f1f62d6 --- /dev/null +++ b/apps/web-antdv-next/src/views/system/notice/list.vue @@ -0,0 +1,243 @@ + + + diff --git a/apps/web-antdv-next/src/views/system/notice/modules/form.vue b/apps/web-antdv-next/src/views/system/notice/modules/form.vue new file mode 100644 index 0000000..f25a29d --- /dev/null +++ b/apps/web-antdv-next/src/views/system/notice/modules/form.vue @@ -0,0 +1,95 @@ + + + +