feat: 赛事1.09

This commit is contained in:
cst
2026-11-06 16:36:10 +08:00
parent 03ec325318
commit 1f356a1112
6 changed files with 42 additions and 36 deletions
+4 -4
View File
@@ -52,10 +52,10 @@ system.roles.add # 角色权限-新增(筛选栏中的"新增
system.roles.edit # 角色权限-编辑(表格操作列中的"编辑"按钮)
system.roles.delete # 角色权限-删除(表格操作列中的"删除"按钮,红色危险样式)
system.notices.view # 公告管理-查看(页面默认查看权限)
system.notices.add # 公告管理-新增(筛选栏中的"新增"按钮)
system.notices.edit # 公告管理-编辑(表格操作列中的"修改"按钮)
system.notices.delete # 公告管理-删除(表格操作列中的"删除"按钮,红色危险样式)
system.announcement.view # 公告管理-查看(页面默认查看权限)
system.announcement.save # 公告管理-新增(筛选栏中的"新增"按钮)
system.announcement.edit # 公告管理-编辑(表格操作列中的"修改"按钮)
system.announcement.del # 公告管理-删除(表格操作列中的"删除"按钮,红色危险样式)
system.logs.view # 操作日志-查看(页面默认查看权限,无操作列按钮)
+2 -2
View File
@@ -48,7 +48,7 @@ export function updateNotice(params: NoticeSaveParams): Promise<ApiResult<null>>
return post(ANNOUNCEMENT_UPDATE, params);
}
/** 删除公告(物理删除,单个)— POST /admin/sys/announcement/delete?id=xxx */
/** 删除公告(物理删除,单个)— POST /admin/sys/announcement/delete(id 放在请求体) */
export function deleteNotice(id: number): Promise<ApiResult<null>> {
return post(ANNOUNCEMENT_DELETE, undefined, { id: String(id) });
return post(ANNOUNCEMENT_DELETE, { id });
}
+1 -3
View File
@@ -165,15 +165,13 @@ export const FALLBACK_MENU_NODES: MenuNode[] = [
permission: 'system.roles.view',
},
{
// 公告管理:后端 /op/permission 尚未下发 system.notices.view 权限码,
// 暂不挂 permission(否则整条菜单会被权限过滤掉);后端配置后可在下方补回
// permission: 'system.notices.view',
id: 'system_notices',
name: '公告管理',
path: 'system/notices',
component: 'system/notices',
componentName: 'SystemNotices',
icon: 'AiOutlineSound',
permission: 'system.announcement.view',
},
{
id: 'system_params',
@@ -7,6 +7,7 @@ import { getNoticeList, type NoticeVO } from '../model/services';
import {
TITLE_MAX,
CONTENT_MAX,
DISPLAY_TIME_FORMAT,
SUBMIT_TIME_FORMAT,
typeRules,
timeRangeRules,
@@ -56,10 +57,11 @@ export default defineComponent({
Object.assign(formData, getDefaultForm());
return;
}
const startTime = record.startTime ? dayjs(record.startTime) : null;
const endTime = record.endTime ? dayjs(record.endTime) : null;
// 编辑回填时秒统一归零,界面只精确到分钟
const startTime = record.startTime ? dayjs(record.startTime).second(0) : null;
const endTime = record.endTime ? dayjs(record.endTime).second(0) : null;
Object.assign(formData, {
type: record.type || 1,
type: Number(record.type) || 1,
timeRange: startTime?.isValid() && endTime?.isValid() ? [startTime, endTime] : null,
title: record.title || '',
context: record.context || '',
@@ -81,7 +83,11 @@ export default defineComponent({
const validateTimeRange = async (startTime: Dayjs, endTime: Dayjs): Promise<boolean> => {
timeRangeChecking.value = true;
try {
const res = await getNoticeList({ page: '1', limit: '999', type: formData.type });
const res = await getNoticeList({
page: '1',
limit: '999',
type: Number(formData.type) || 1,
});
if (res.code != 200) return true;
const editingId = props.record ? String(props.record.id) : '';
@@ -119,7 +125,7 @@ export default defineComponent({
if (!isTimeRangeValid) return;
props.onSave({
type: formData.type,
type: Number(formData.type) || 1,
title: formData.title.trim(),
context: formData.context.trim(),
startTime: startTime.format(SUBMIT_TIME_FORMAT),
@@ -146,24 +152,28 @@ export default defineComponent({
{/* 类型 */}
<Form.Item label="类型" name="type" rules={typeRules}>
<Select
value={formData.type}
value={Number(formData.type)}
options={NOTICE_TYPE_OPTIONS as any}
placeholder="请选择"
onUpdate:value={(val: any) => (formData.type = val as number)}
onUpdate:value={(val: any) => (formData.type = Number(val) || 1)}
/>
</Form.Item>
{/* 生效时间 */}
<Form.Item label="生效时间" name="timeRange" rules={timeRangeRules}>
<RangePicker
showTime
format={SUBMIT_TIME_FORMAT}
showTime={{ format: 'HH:mm' }}
format={DISPLAY_TIME_FORMAT}
value={formData.timeRange as any}
placeholder={['开始时间', '结束时间']}
style={{ width: '100%' }}
allowClear
onUpdate:value={(val: any) => {
formData.timeRange = (val as [Dayjs, Dayjs]) || null;
const range =
Array.isArray(val) && val.length === 2
? ([val[0].second(0), val[1].second(0)] as [Dayjs, Dayjs])
: null;
formData.timeRange = range;
formRef.value?.clearValidate('timeRange');
}}
/>
+9 -9
View File
@@ -63,7 +63,7 @@ function confirmDelete({ record, onOk }: { record: any; onOk: (record: any) => v
Modal.confirm({
title: '删除确认',
class: 'deleteConfirmModal',
content: `确认要删除“${record.title}”吗?`,
content: `确认删除“${record.title}”吗?`,
okText: '确认',
cancelText: '取消',
okButtonProps: { danger: true } as ModalProps['okButtonProps'],
@@ -100,17 +100,17 @@ export default defineComponent({
const { hasPermission, hasAnyPermission } = usePermissionStore();
// 公告模块的权限码后端尚未配置时(/op/permission 未下发),按钮默认放行,
// 待后端下发任意一个 system.notices.* 权限码后自动切换为按码控制
// 待后端下发任意一个 system.announcement.* 权限码后自动切换为按码控制
const noticePermsConfigured = hasAnyPermission([
'system.notices.view',
'system.notices.add',
'system.notices.edit',
'system.notices.delete',
'system.announcement.view',
'system.announcement.save',
'system.announcement.edit',
'system.announcement.del',
]);
const canAdd = !noticePermsConfigured || hasPermission('system.notices.add');
const canAdd = !noticePermsConfigured || hasPermission('system.announcement.save');
const operationPermissions = {
canEdit: !noticePermsConfigured || hasPermission('system.notices.edit'),
canDelete: !noticePermsConfigured || hasPermission('system.notices.delete'),
canEdit: !noticePermsConfigured || hasPermission('system.announcement.edit'),
canDelete: !noticePermsConfigured || hasPermission('system.announcement.del'),
};
const triggerDelete = (record: any) => confirmDelete({ record, onOk: handleDelete });
@@ -19,11 +19,8 @@ import {
// 常量
// ============================================================
/** 公告类型选项(筛选 / 表单共用) */
export const NOTICE_TYPE_OPTIONS = [
{ value: 1, label: '首页公告提示' },
{ value: 2, label: '产品更新公告' },
] as const;
/** 公告类型选项(筛选 / 表单共用):目前仅开放「首页公告提示」 */
export const NOTICE_TYPE_OPTIONS = [{ value: 1, label: '首页公告提示' }] as const;
/** 公告类型文案映射(兜底,后端 typeName 为空时使用) */
export const NOTICE_TYPE_MAP: Record<number, string> = {
@@ -67,9 +64,10 @@ export function useNoticeModel() {
formatResult: (res) => res,
});
const dataSource = computed(() => {
const res = data.value;
return res ? (res as any).data?.list || [] : [];
const dataSource = computed<NoticeVO[]>(() => {
const list = (data.value as any)?.data?.list || [];
// 后端 type 可能返回字符串,统一归一化为数字,保证编辑弹窗 Select 能匹配选项
return list.map((item: NoticeVO) => ({ ...item, type: Number(item.type) }));
});
useEffect(() => {