Compare commits
10 Commits
849ff71393
...
0f98692b25
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f98692b25 | ||
|
|
4264ef36b7 | ||
|
|
f5b06cb9b9 | ||
|
|
0fa1451e27 | ||
|
|
ed949fbdb3 | ||
|
|
7b322a3da5 | ||
|
|
1b2191fc2f | ||
|
|
dc2162d36e | ||
|
|
d84ec11a32 | ||
|
|
b430f4ef1e |
331
CLAUDE.md
331
CLAUDE.md
@ -368,54 +368,6 @@ coiMsgBox('确定要删除吗?', '删除确认').then(() => {
|
||||
- ❌ 图标与功能语义不匹配
|
||||
- ❌ 在表格列表页面使用圆角按钮(`round` 属性)
|
||||
|
||||
### 示例:完整的页面按钮实现
|
||||
```vue
|
||||
<template>
|
||||
<div class="page-actions">
|
||||
<!-- 主要操作按钮 -->
|
||||
<NButton v-permission="PERMISSIONS.USER.ADD" type="primary" @click="handleAdd">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:plus /></NIcon>
|
||||
</template>
|
||||
新增
|
||||
</NButton>
|
||||
|
||||
<!-- 批量操作按钮 -->
|
||||
<NButton type="error" :disabled="selectedRows.length === 0" @click="handleBatchDelete">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:delete /></NIcon>
|
||||
</template>
|
||||
删除
|
||||
</NButton>
|
||||
|
||||
<!-- 功能按钮 -->
|
||||
<NButton @click="handleExport">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:download /></NIcon>
|
||||
</template>
|
||||
导出
|
||||
</NButton>
|
||||
|
||||
<!-- 搜索重置按钮 -->
|
||||
<NButton type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:search /></NIcon>
|
||||
</template>
|
||||
搜索
|
||||
</NButton>
|
||||
|
||||
<NButton @click="handleReset">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
</template>
|
||||
重置
|
||||
</NButton>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
**遵循此规范可确保项目界面的一致性和专业性!**
|
||||
|
||||
### 🚀 render函数中图标渲染规范(强制要求)
|
||||
|
||||
**在DataTable等组件的render函数中使用图标时,必须遵循以下规范**
|
||||
@ -528,41 +480,6 @@ const columns: DataTableColumns<DataType> = [
|
||||
default: () => '编辑',
|
||||
}))
|
||||
}
|
||||
|
||||
// 删除按钮(带确认)
|
||||
if (hasPermission('delete')) {
|
||||
buttons.push(h(NPopconfirm, {
|
||||
onPositiveClick: () => handleDelete(row.id),
|
||||
negativeText: '取消',
|
||||
positiveText: '确定',
|
||||
}, {
|
||||
default: () => '确定删除此项吗?',
|
||||
trigger: () => h(NButton, {
|
||||
type: 'error',
|
||||
size: 'small',
|
||||
}, {
|
||||
icon: () => h(NIcon, { size: 14, style: 'transform: translateY(-1px)' }, {
|
||||
default: () => h(IconParkOutlineDelete)
|
||||
}),
|
||||
default: () => '删除',
|
||||
}),
|
||||
}))
|
||||
}
|
||||
|
||||
// 设置按钮
|
||||
if (hasPermission('setting')) {
|
||||
buttons.push(h(NButton, {
|
||||
type: 'warning',
|
||||
size: 'small',
|
||||
onClick: () => handleSetting(row),
|
||||
}, {
|
||||
icon: () => h(NIcon, { size: 14, style: 'transform: translateY(-1px)' }, {
|
||||
default: () => h(IconParkOutlineSetting)
|
||||
}),
|
||||
default: () => '设置',
|
||||
}))
|
||||
}
|
||||
|
||||
return h('div', { class: 'flex items-center justify-center gap-2' }, buttons)
|
||||
},
|
||||
},
|
||||
@ -780,40 +697,6 @@ const deleteDialogRef = ref()
|
||||
</div>
|
||||
</template>
|
||||
</CoiDialog>
|
||||
|
||||
<!-- 仅查看类弹框示例(无确认按钮) -->
|
||||
<CoiDialog
|
||||
ref="viewDialogRef"
|
||||
title="查看详情"
|
||||
:width="600"
|
||||
height="auto"
|
||||
cancel-text="关闭"
|
||||
:show-confirm="false"
|
||||
@coi-cancel="handleClose"
|
||||
>
|
||||
<template #content>
|
||||
<div class="p-3">
|
||||
<!-- 查看内容 -->
|
||||
</div>
|
||||
</template>
|
||||
</CoiDialog>
|
||||
|
||||
<!-- 禁用ESC键关闭的弹框示例 -->
|
||||
<CoiDialog
|
||||
ref="criticalDialogRef"
|
||||
title="重要操作确认"
|
||||
:width="600"
|
||||
:close-on-esc="false"
|
||||
:mask-closable="false"
|
||||
@coi-confirm="handleCriticalConfirm"
|
||||
@coi-cancel="handleCriticalCancel"
|
||||
>
|
||||
<template #content>
|
||||
<div class="p-3">
|
||||
<p class="text-red-600">此操作不可逆,请谨慎操作!</p>
|
||||
</div>
|
||||
</template>
|
||||
</CoiDialog>
|
||||
</template>
|
||||
```
|
||||
|
||||
@ -1069,20 +952,6 @@ var(--info-color) /* 信息色 */
|
||||
</template>
|
||||
新增
|
||||
</NButton>
|
||||
|
||||
<NButton type="error" @click="handleDelete">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:delete /></NIcon>
|
||||
</template>
|
||||
删除
|
||||
</NButton>
|
||||
|
||||
<NButton @click="handleExport">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:download /></NIcon>
|
||||
</template>
|
||||
导出
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 - 表头不加粗,使用默认样式 -->
|
||||
@ -1349,62 +1218,6 @@ import { coiMsgError, coiMsgSuccess, coiMsgWarning } from '@/utils/coi'
|
||||
</template>
|
||||
新增
|
||||
</NButton>
|
||||
|
||||
<NButton type="error" @click="handleDelete">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:delete /></NIcon>
|
||||
</template>
|
||||
删除
|
||||
</NButton>
|
||||
|
||||
<NButton @click="handleExport">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:download /></NIcon>
|
||||
</template>
|
||||
导出
|
||||
</NButton>
|
||||
|
||||
<NButton type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:search /></NIcon>
|
||||
</template>
|
||||
搜索
|
||||
</NButton>
|
||||
|
||||
<NButton @click="handleReset">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
</template>
|
||||
重置
|
||||
</NButton>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 错误示例(严格禁止)
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="page-actions">
|
||||
<!-- ❌ 错误:使用圆角按钮 -->
|
||||
<NButton type="primary" round @click="handleAdd">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:plus /></NIcon>
|
||||
</template>
|
||||
新增
|
||||
</NButton>
|
||||
|
||||
<!-- ❌ 错误:只有文字没有图标 -->
|
||||
<NButton type="primary" @click="handleAdd">
|
||||
新增
|
||||
</NButton>
|
||||
|
||||
<!-- ❌ 错误:只有图标没有文字 -->
|
||||
<NButton type="primary" @click="handleAdd">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:plus /></NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
@ -1750,3 +1563,147 @@ import { coiMsgError, coiMsgSuccess, coiMsgWarning } from '@/utils/coi'
|
||||
- Node.js 21.x
|
||||
- pnpm 10.x
|
||||
- 现代浏览器支持ES6+
|
||||
|
||||
## 🔢 雪花ID和大数字处理规范(强制要求)
|
||||
|
||||
**项目使用雪花ID算法生成唯一标识,必须严格按照以下规范处理大数字精度问题**
|
||||
|
||||
### 问题背景
|
||||
|
||||
**JavaScript数字精度限制**:
|
||||
- 雪花ID长度:19位数字(如:`194285920715864064`)
|
||||
- JavaScript安全整数范围:`Number.MAX_SAFE_INTEGER = 9007199254740991`(16位数字)
|
||||
- **超过安全整数范围的数字会发生精度丢失**
|
||||
|
||||
**典型问题示例**:
|
||||
```text
|
||||
// 精度丢失示例
|
||||
194285920715864064 变成 194285920715864060 // 末尾数字被修改
|
||||
```
|
||||
|
||||
### 核心原则
|
||||
|
||||
1. **全链路字符串保持**:从数据库到前端,再到API调用,都保持字符串格式
|
||||
2. **后端安全转换**:只在后端业务逻辑中进行字符串到Long的转换
|
||||
3. **前端零转换**:前端严禁将大数字ID转换为Number类型
|
||||
4. **向后兼容**:同时支持短ID和长ID的处理
|
||||
|
||||
### 强制实施规范
|
||||
|
||||
#### 1. **前端API接口设计**
|
||||
|
||||
**✅ 正确的API设计**:
|
||||
```typescript
|
||||
// 接收和发送都使用字符串格式
|
||||
export function saveRoleMenuPermission(roleId: string, menuIds: string[]) {
|
||||
const menuIdsStr = menuIds.length > 0 ? menuIds : ['-1']
|
||||
return request.Post<Service.ResponseResult<string>>('/coder/sysMenu/saveRoleMenu', {
|
||||
roleId, // 字符串格式
|
||||
menuIds: menuIdsStr // 字符串数组格式
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**❌ 错误的API设计**:
|
||||
```typescript
|
||||
// 不要在前端进行数字转换
|
||||
export function saveRoleMenuPermission(roleId: string, menuIds: string[]) {
|
||||
const menuIdsLong = menuIds.map(id => Number(id)) // ❌ 会造成精度丢失
|
||||
return request.Post('/api/save', {
|
||||
roleId: Number(roleId), // ❌ 会造成精度丢失
|
||||
menuIds: menuIdsLong // ❌ 会造成精度丢失
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. **前端类型定义规范**
|
||||
|
||||
**✅ 正确的类型定义**:
|
||||
```typescript
|
||||
// 所有ID相关字段使用字符串类型
|
||||
export interface RoleMenuPermissionBo {
|
||||
roleId: string // 字符串格式避免精度丢失
|
||||
menuIds: string[] // 字符串数组格式避免精度丢失
|
||||
}
|
||||
|
||||
export interface UserVo {
|
||||
userId: string // 字符串格式
|
||||
roleId: string // 字符串格式
|
||||
menuIds: string[] // 字符串数组格式
|
||||
}
|
||||
```
|
||||
|
||||
**❌ 错误的类型定义**:
|
||||
```typescript
|
||||
// 不要使用number类型处理大数字ID
|
||||
export interface RoleMenuPermissionBo {
|
||||
roleId: number // ❌ 会造成精度丢失
|
||||
menuIds: number[] // ❌ 会造成精度丢失
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. **前端数据处理规范**
|
||||
|
||||
**✅ 正确的数据处理**:
|
||||
```typescript
|
||||
// 保持字符串格式进行比较和处理
|
||||
function handleRowSelectionChange(rowKeys: (string | number)[]) {
|
||||
const stringKeys = rowKeys.map(key => String(key))
|
||||
selectedRows.value = tableData.value.filter(row =>
|
||||
stringKeys.includes(String(row.roleId))
|
||||
)
|
||||
}
|
||||
|
||||
// 发送API请求时保持字符串格式
|
||||
const response = await saveRoleMenuPermission(
|
||||
String(currentRole.roleId),
|
||||
selectedMenuIds // 已经是字符串数组
|
||||
)
|
||||
```
|
||||
|
||||
**❌ 错误的数据处理**:
|
||||
```typescript
|
||||
// 不要将字符串ID转换为数字
|
||||
function handleRowSelectionChange(rowKeys: (string | number)[]) {
|
||||
const numericKeys = rowKeys.map(key => Number(key)) // ❌ 精度丢失
|
||||
selectedRows.value = tableData.value.filter(row =>
|
||||
numericKeys.includes(Number(row.roleId)) // ❌ 精度丢失
|
||||
)
|
||||
}
|
||||
|
||||
// 不要在发送前进行数字转换
|
||||
const menuIds = selectedMenuIds.map(id => Number(id)) // ❌ 精度丢失
|
||||
const response = await saveRoleMenuPermission(
|
||||
Number(currentRole.roleId), // ❌ 精度丢失
|
||||
menuIds
|
||||
)
|
||||
```
|
||||
|
||||
### 检查清单
|
||||
|
||||
**开发新功能时,必须确保**:
|
||||
- [ ] 前端类型定义中所有ID字段使用string类型
|
||||
- [ ] 前端API调用保持字符串格式,不进行Number转换
|
||||
- [ ] 前端数据处理中避免parseInt()、Number()等数字转换
|
||||
- [ ] 后端BO类提供字符串接收和Long转换的安全方法
|
||||
- [ ] 后端控制器使用安全转换方法
|
||||
- [ ] 后端服务层对无效ID进行过滤处理
|
||||
|
||||
### 常见问题排查
|
||||
|
||||
**当遇到大数字ID相关问题时,按以下步骤排查**:
|
||||
1. **检查前端是否进行了数字转换**:搜索`Number(`、`parseInt(`、`parseFloat(`
|
||||
2. **检查API调用参数格式**:确认发送的是字符串而非数字
|
||||
3. **检查后端BO类设计**:确认使用字符串接收和安全转换
|
||||
4. **检查数据库表字段类型**:确认使用`bigint`类型存储
|
||||
5. **检查序列化配置**:确认JSON序列化时大数字转为字符串
|
||||
|
||||
### 历史修复案例
|
||||
|
||||
**案例:菜单权限分配失败**
|
||||
- **问题**:雪花ID在前端Number转换时精度丢失
|
||||
- **症状**:部门管理等新菜单权限无法保存
|
||||
- **解决**:全链路改为字符串格式,后端安全转换
|
||||
- **文件**:`SysRoleMenuBo.java`、`saveRoleMenuPermission API`、角色管理页面
|
||||
|
||||
**遵循此规范可彻底避免大数字精度丢失问题,确保系统稳定运行!**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "nova-admin",
|
||||
"name": "coi-admin",
|
||||
"type": "module",
|
||||
"version": "0.9.15",
|
||||
"private": true,
|
||||
|
||||
@ -5,12 +5,12 @@
|
||||
<div class="coi-empty__icon-wrapper">
|
||||
<div class="coi-empty__icon-bg" />
|
||||
<NIcon class="coi-empty__icon" :size="iconSize" :color="iconColor">
|
||||
<icon-park-outline:inbox v-if="currentType === 'default'" />
|
||||
<icon-park-outline:search v-else-if="currentType === 'search'" />
|
||||
<icon-park-outline:folder-close v-else-if="currentType === 'network'" />
|
||||
<icon-park-outline:lock v-else-if="currentType === 'permission'" />
|
||||
<icon-park-outline:folder-close v-else-if="currentType === 'custom'" />
|
||||
<icon-park-outline:inbox v-else />
|
||||
<icon-park-outline-inbox v-if="currentType === 'default'" />
|
||||
<icon-park-outline-search v-else-if="currentType === 'search'" />
|
||||
<icon-park-outline-folder-close v-else-if="currentType === 'network'" />
|
||||
<icon-park-outline-lock v-else-if="currentType === 'permission'" />
|
||||
<icon-park-outline-folder-close v-else-if="currentType === 'custom'" />
|
||||
<icon-park-outline-inbox v-else />
|
||||
</NIcon>
|
||||
</div>
|
||||
|
||||
@ -36,8 +36,8 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="coi-empty__action-icon">
|
||||
<icon-park-outline:refresh v-if="currentType === 'search'" />
|
||||
<icon-park-outline:plus v-else />
|
||||
<icon-park-outline-refresh v-if="currentType === 'search'" />
|
||||
<icon-park-outline-plus v-else />
|
||||
</NIcon>
|
||||
</template>
|
||||
{{ actionText }}
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
@click="handlePageChange(1)"
|
||||
>
|
||||
<NIcon size="14">
|
||||
<icon-park-outline:go-start />
|
||||
<icon-park-outline-go-start />
|
||||
</NIcon>
|
||||
</button>
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
@click="handlePageChange(currentPage - 1)"
|
||||
>
|
||||
<NIcon size="14">
|
||||
<icon-park-outline:left />
|
||||
<icon-park-outline-left />
|
||||
</NIcon>
|
||||
</button>
|
||||
|
||||
@ -74,7 +74,7 @@
|
||||
@click="handlePageChange(currentPage + 1)"
|
||||
>
|
||||
<NIcon size="14">
|
||||
<icon-park-outline:right />
|
||||
<icon-park-outline-right />
|
||||
</NIcon>
|
||||
</button>
|
||||
|
||||
@ -85,7 +85,7 @@
|
||||
@click="handlePageChange(totalPages)"
|
||||
>
|
||||
<NIcon size="14">
|
||||
<icon-park-outline:go-end />
|
||||
<icon-park-outline-go-end />
|
||||
</NIcon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -54,7 +54,7 @@ const MassageData = ref<Entity.Message[]>([
|
||||
id: 0,
|
||||
type: 0,
|
||||
title: 'Admin 已经完成40%了!',
|
||||
icon: 'icon-park-outline:tips-one',
|
||||
icon: 'icon-park-outline-tips-one',
|
||||
tagTitle: '未开始',
|
||||
tagType: 'info',
|
||||
description: '项目稳定推进中,很快就能看到正式版了',
|
||||
@ -64,7 +64,7 @@ const MassageData = ref<Entity.Message[]>([
|
||||
id: 1,
|
||||
type: 0,
|
||||
title: 'Admin 已经添加通知功能!',
|
||||
icon: 'icon-park-outline:comment-one',
|
||||
icon: 'icon-park-outline-comment-one',
|
||||
tagTitle: '未开始',
|
||||
tagType: 'success',
|
||||
date: '2022-2-2 12:22',
|
||||
@ -73,7 +73,7 @@ const MassageData = ref<Entity.Message[]>([
|
||||
id: 2,
|
||||
type: 0,
|
||||
title: 'Admin 已经添加路由功能!',
|
||||
icon: 'icon-park-outline:message-emoji',
|
||||
icon: 'icon-park-outline-message-emoji',
|
||||
tagTitle: '未开始',
|
||||
tagType: 'warning',
|
||||
description: '项目稳定推进中...',
|
||||
@ -84,7 +84,7 @@ const MassageData = ref<Entity.Message[]>([
|
||||
type: 0,
|
||||
title:
|
||||
'Admin 已经添加菜单导航功能!Admin 已经添加菜单导航功能!Admin 已经添加菜单导航功能!Admin 已经添加菜单导航功能!',
|
||||
icon: 'icon-park-outline:tips-one',
|
||||
icon: 'icon-park-outline-tips-one',
|
||||
tagTitle: '未开始',
|
||||
tagType: 'error',
|
||||
description:
|
||||
@ -95,7 +95,7 @@ const MassageData = ref<Entity.Message[]>([
|
||||
id: 4,
|
||||
type: 0,
|
||||
title: 'Admin开始启动了!',
|
||||
icon: 'icon-park-outline:tips-one',
|
||||
icon: 'icon-park-outline-tips-one',
|
||||
tagTitle: '未开始',
|
||||
description: '项目稳定推进中...',
|
||||
date: '2022-2-5 18:32',
|
||||
@ -104,7 +104,7 @@ const MassageData = ref<Entity.Message[]>([
|
||||
id: 5,
|
||||
type: 1,
|
||||
title: '相见恨晚??',
|
||||
icon: 'icon-park-outline:comment',
|
||||
icon: 'icon-park-outline-comment',
|
||||
description: '项目稳定推进中,很快就能看到正式版了',
|
||||
date: '2022-2-2 12:22',
|
||||
},
|
||||
@ -112,7 +112,7 @@ const MassageData = ref<Entity.Message[]>([
|
||||
id: 6,
|
||||
type: 1,
|
||||
title: '动态路由已完成!',
|
||||
icon: 'icon-park-outline:comment',
|
||||
icon: 'icon-park-outline-comment',
|
||||
description: '项目稳定推进中,很快就能看到正式版了',
|
||||
date: '2022-2-25 12:22',
|
||||
},
|
||||
@ -120,7 +120,7 @@ const MassageData = ref<Entity.Message[]>([
|
||||
id: 7,
|
||||
type: 2,
|
||||
title: '接下来需要完善一些',
|
||||
icon: 'icon-park-outline:beach-umbrella',
|
||||
icon: 'icon-park-outline-beach-umbrella',
|
||||
tagTitle: '未开始',
|
||||
description: '项目稳定推进中,很快就能看到正式版了',
|
||||
date: '2022-2-2 12:22',
|
||||
|
||||
@ -16,6 +16,7 @@ export type {
|
||||
MenuQueryBo,
|
||||
MenuRouterBo,
|
||||
MenuVo,
|
||||
RoleMenuPermissionBo,
|
||||
} from './types'
|
||||
|
||||
// 兼容性类型别名
|
||||
@ -120,8 +121,14 @@ export function getMenuIdsByRoleId(roleId: string) {
|
||||
* 保存角色和菜单权限之间的关系
|
||||
*/
|
||||
export function saveRoleMenuPermission(roleId: string, menuIds: string[]) {
|
||||
const menuIdsStr = menuIds.length > 0 ? menuIds.join(',') : '-1'
|
||||
return request.Post<Service.ResponseResult<string>>(`/coder/sysMenu/saveRoleMenu/${roleId}/${menuIdsStr}`)
|
||||
// 空数组时传递 ["-1"] 表示取消所有权限
|
||||
// 保持字符串格式避免精度丢失
|
||||
const menuIdsStr = menuIds.length > 0 ? menuIds : ['-1']
|
||||
|
||||
return request.Post<Service.ResponseResult<string>>('/coder/sysMenu/saveRoleMenu', {
|
||||
roleId,
|
||||
menuIds: menuIdsStr,
|
||||
})
|
||||
}
|
||||
|
||||
// 兼容性导出 - 保持原有函数名以确保向后兼容
|
||||
|
||||
@ -95,3 +95,9 @@ export interface MenuNormalResponse {
|
||||
menuList: MenuVo[]
|
||||
spreadList: string[] // 改为字符串数组避免大整数精度丢失
|
||||
}
|
||||
|
||||
// 角色菜单权限分配请求参数
|
||||
export interface RoleMenuPermissionBo {
|
||||
roleId: string // 字符串格式避免精度丢失
|
||||
menuIds: string[] // 字符串格式避免精度丢失
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { local } from '@/utils'
|
||||
import { coiMsgWarning } from '@/utils/coi'
|
||||
import { coiMsgError } from '@/utils/coi'
|
||||
import { createAlova } from 'alova'
|
||||
import { createServerTokenAuthentication } from 'alova/client'
|
||||
import adapterFetch from 'alova/fetch'
|
||||
@ -105,7 +105,7 @@ export function createAlovaInstance(
|
||||
userMessage = '文件大小超出限制,请选择较小的文件'
|
||||
}
|
||||
|
||||
coiMsgWarning(userMessage)
|
||||
coiMsgError(userMessage)
|
||||
},
|
||||
|
||||
onComplete: async (_method) => {
|
||||
|
||||
@ -110,7 +110,7 @@ const rules = {
|
||||
},
|
||||
}
|
||||
const formValue = ref({
|
||||
account: 'admin',
|
||||
account: 'yuadmin',
|
||||
pwd: '000000',
|
||||
rePwd: '000000',
|
||||
})
|
||||
|
||||
@ -82,13 +82,13 @@
|
||||
<NSpace>
|
||||
<NButton type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:search /></NIcon>
|
||||
<NIcon><icon-park-outline-search /></NIcon>
|
||||
</template>
|
||||
搜索
|
||||
</NButton>
|
||||
<NButton @click="handleReset">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
重置
|
||||
</NButton>
|
||||
@ -110,7 +110,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:upload />
|
||||
<icon-park-outline-upload />
|
||||
</NIcon>
|
||||
</template>
|
||||
上传文件
|
||||
@ -125,7 +125,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:delete />
|
||||
<IconParkOutlineDelete />
|
||||
</NIcon>
|
||||
</template>
|
||||
批量删除
|
||||
@ -136,7 +136,7 @@
|
||||
<span>共 {{ pagination.itemCount }} 条</span>
|
||||
<NButton text @click="handleRefresh">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
</div>
|
||||
@ -250,7 +250,7 @@
|
||||
<div class="text-center">
|
||||
<div class="mb-3">
|
||||
<NIcon size="48" class="text-blue-500">
|
||||
<icon-park-outline:upload />
|
||||
<icon-park-outline-upload />
|
||||
</NIcon>
|
||||
</div>
|
||||
<NText class="text-base">
|
||||
@ -313,6 +313,7 @@ import {
|
||||
import type { SysFileQueryBo, SysFileSearchForm, SysFileVo } from '@/service/api/system/file'
|
||||
import IconParkOutlineDelete from '~icons/icon-park-outline/delete'
|
||||
import IconParkOutlineDownload from '~icons/icon-park-outline/download'
|
||||
import { FILE_SERVICE_DB_OPTIONS } from '@/service/api/system/file/types'
|
||||
|
||||
// 文件分类图标组件
|
||||
const StarIcon = () => h('span', { class: 'text-yellow-500' }, '✨')
|
||||
|
||||
@ -85,13 +85,13 @@
|
||||
<NSpace>
|
||||
<NButton type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:search /></NIcon>
|
||||
<NIcon><icon-park-outline-search /></NIcon>
|
||||
</template>
|
||||
搜索
|
||||
</NButton>
|
||||
<NButton @click="handleReset">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
重置
|
||||
</NButton>
|
||||
@ -114,7 +114,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:delete />
|
||||
<IconParkOutlineDelete />
|
||||
</NIcon>
|
||||
</template>
|
||||
删除
|
||||
@ -125,7 +125,7 @@
|
||||
<span>共 {{ pagination.itemCount }} 条</span>
|
||||
<NButton text @click="getLoginLogList">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
</div>
|
||||
@ -168,7 +168,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon>
|
||||
<icon-park-outline:refresh />
|
||||
<icon-park-outline-refresh />
|
||||
</NIcon>
|
||||
</template>
|
||||
{{ getEmptyActionText() }}
|
||||
|
||||
@ -53,13 +53,13 @@
|
||||
<NSpace>
|
||||
<NButton type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:search /></NIcon>
|
||||
<NIcon><icon-park-outline-search /></NIcon>
|
||||
</template>
|
||||
搜索
|
||||
</NButton>
|
||||
<NButton @click="handleReset">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
重置
|
||||
</NButton>
|
||||
@ -76,7 +76,7 @@
|
||||
<NButton v-if="hasButton(PERMISSIONS.MENU.ADD)" type="primary" size="medium" class="px-3 flex items-center" @click="handleAdd">
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:plus />
|
||||
<IconParkOutlinePlus />
|
||||
</NIcon>
|
||||
</template>
|
||||
新增
|
||||
@ -92,7 +92,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:delete />
|
||||
<IconParkOutlineDelete />
|
||||
</NIcon>
|
||||
</template>
|
||||
删除
|
||||
@ -101,8 +101,8 @@
|
||||
<NButton size="medium" class="px-3 flex items-center" @click="handleExpandToggle">
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:expand-up v-if="isAllExpanded" />
|
||||
<icon-park-outline:expand-down v-else />
|
||||
<icon-park-outline-expand-up v-if="isAllExpanded" />
|
||||
<icon-park-outline-expand-down v-else />
|
||||
</NIcon>
|
||||
</template>
|
||||
{{ isAllExpanded ? '全部折叠' : '全部展开' }}
|
||||
@ -112,7 +112,7 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<NButton type="tertiary" @click="handleRefresh">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
</div>
|
||||
@ -159,8 +159,8 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon>
|
||||
<icon-park-outline:refresh v-if="hasSearchConditions()" />
|
||||
<icon-park-outline:plus v-else />
|
||||
<icon-park-outline-refresh v-if="hasSearchConditions()" />
|
||||
<IconParkOutlinePlus v-else />
|
||||
</NIcon>
|
||||
</template>
|
||||
{{ getEmptyActionText() }}
|
||||
@ -213,7 +213,7 @@
|
||||
>
|
||||
<template #suffix>
|
||||
<NIcon>
|
||||
<icon-park-outline:down />
|
||||
<icon-park-outline-down />
|
||||
</NIcon>
|
||||
</template>
|
||||
</n-input>
|
||||
@ -281,7 +281,7 @@
|
||||
({{ menu.children.length }})
|
||||
</span>
|
||||
<NIcon v-if="menu.children?.length" class="expand-icon">
|
||||
<icon-park-outline:right />
|
||||
<icon-park-outline-right />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -85,13 +85,13 @@
|
||||
<NSpace>
|
||||
<NButton type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:search /></NIcon>
|
||||
<NIcon><icon-park-outline-search /></NIcon>
|
||||
</template>
|
||||
搜索
|
||||
</NButton>
|
||||
<NButton @click="handleReset">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
重置
|
||||
</NButton>
|
||||
@ -114,7 +114,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:delete />
|
||||
<IconParkOutlineDelete />
|
||||
</NIcon>
|
||||
</template>
|
||||
删除
|
||||
@ -128,7 +128,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:delete />
|
||||
<IconParkOutlineDelete />
|
||||
</NIcon>
|
||||
</template>
|
||||
清空
|
||||
@ -139,7 +139,7 @@
|
||||
<span>共 {{ pagination.itemCount }} 条</span>
|
||||
<NButton text @click="getOperLogList">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
</div>
|
||||
@ -182,7 +182,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon>
|
||||
<icon-park-outline:refresh />
|
||||
<icon-park-outline-refresh />
|
||||
</NIcon>
|
||||
</template>
|
||||
{{ getEmptyActionText() }}
|
||||
|
||||
@ -82,13 +82,13 @@
|
||||
<NSpace>
|
||||
<NButton type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:search /></NIcon>
|
||||
<NIcon><icon-park-outline-search /></NIcon>
|
||||
</template>
|
||||
搜索
|
||||
</NButton>
|
||||
<NButton @click="handleReset">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
重置
|
||||
</NButton>
|
||||
@ -110,7 +110,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:upload />
|
||||
<icon-park-outline-upload />
|
||||
</NIcon>
|
||||
</template>
|
||||
上传图片
|
||||
@ -125,7 +125,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:delete />
|
||||
<IconParkOutlineDelete />
|
||||
</NIcon>
|
||||
</template>
|
||||
批量删除
|
||||
@ -138,8 +138,8 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:switch v-if="viewMode === 'table'" />
|
||||
<icon-park-outline:list v-else />
|
||||
<icon-park-outline-switch v-if="viewMode === 'table'" />
|
||||
<icon-park-outline-list v-else />
|
||||
</NIcon>
|
||||
</template>
|
||||
{{ viewMode === 'table' ? '图库视图' : '表格视图' }}
|
||||
@ -150,7 +150,7 @@
|
||||
<span>共 {{ pagination.itemCount }} 条</span>
|
||||
<NButton text @click="handleRefresh">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
</div>
|
||||
@ -219,7 +219,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon size="12">
|
||||
<icon-park-outline:download />
|
||||
<IconParkOutlineDownload />
|
||||
</NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
@ -233,7 +233,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon size="12">
|
||||
<icon-park-outline:delete />
|
||||
<IconParkOutlineDelete />
|
||||
</NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
@ -280,7 +280,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon>
|
||||
<icon-park-outline:plus />
|
||||
<icon-park-outline-plus />
|
||||
</NIcon>
|
||||
</template>
|
||||
上传图片
|
||||
@ -376,7 +376,7 @@
|
||||
<div class="text-center">
|
||||
<div class="mb-3">
|
||||
<NIcon size="48" class="text-blue-500">
|
||||
<icon-park-outline:camera />
|
||||
<icon-park-outline-camera />
|
||||
</NIcon>
|
||||
</div>
|
||||
<NText class="text-base">
|
||||
|
||||
@ -74,13 +74,13 @@
|
||||
<NSpace>
|
||||
<NButton type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:search /></NIcon>
|
||||
<NIcon><icon-park-outline-search /></NIcon>
|
||||
</template>
|
||||
搜索
|
||||
</NButton>
|
||||
<NButton @click="handleReset">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
重置
|
||||
</NButton>
|
||||
@ -97,7 +97,7 @@
|
||||
<NButton v-button="PERMISSIONS.ROLE.ADD" type="primary" class="px-3 flex items-center" @click="handleAdd">
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:plus />
|
||||
<icon-park-outline-plus />
|
||||
</NIcon>
|
||||
</template>
|
||||
新增
|
||||
@ -112,7 +112,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:edit />
|
||||
<icon-park-outline-edit />
|
||||
</NIcon>
|
||||
</template>
|
||||
修改
|
||||
@ -127,7 +127,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:delete />
|
||||
<IconParkOutlineDelete />
|
||||
</NIcon>
|
||||
</template>
|
||||
删除
|
||||
@ -142,7 +142,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||
<icon-park-outline:key />
|
||||
<IconParkOutlineKey />
|
||||
</NIcon>
|
||||
</template>
|
||||
分配权限
|
||||
@ -153,7 +153,7 @@
|
||||
<span>共 {{ pagination.itemCount }} 条</span>
|
||||
<NButton text @click="getRoleList">
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||
<NIcon><icon-park-outline-refresh /></NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
</div>
|
||||
@ -196,8 +196,8 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon>
|
||||
<icon-park-outline:refresh v-if="hasSearchConditions()" />
|
||||
<icon-park-outline:plus v-else />
|
||||
<icon-park-outline-refresh v-if="hasSearchConditions()" />
|
||||
<icon-park-outline-plus v-else />
|
||||
</NIcon>
|
||||
</template>
|
||||
{{ getEmptyActionText() }}
|
||||
@ -338,8 +338,8 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon style="transform: translateY(-1px)">
|
||||
<icon-park-outline:minus v-if="allExpanded" />
|
||||
<icon-park-outline:plus v-else />
|
||||
<icon-park-outline-minus v-if="allExpanded" />
|
||||
<icon-park-outline-plus v-else />
|
||||
</NIcon>
|
||||
</template>
|
||||
{{ allExpanded ? '折叠' : '展开' }}
|
||||
@ -352,8 +352,8 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon style="transform: translateY(-1px)">
|
||||
<icon-park-outline:close v-if="allSelected" />
|
||||
<icon-park-outline:check v-else />
|
||||
<icon-park-outline-close v-if="allSelected" />
|
||||
<icon-park-outline-check v-else />
|
||||
</NIcon>
|
||||
</template>
|
||||
{{ allSelected ? '全不选' : '全选' }}
|
||||
@ -366,7 +366,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon style="transform: translateY(-1px)">
|
||||
<icon-park-outline:link />
|
||||
<icon-park-outline-link />
|
||||
</NIcon>
|
||||
</template>
|
||||
父子联动
|
||||
@ -379,7 +379,7 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon style="transform: translateY(-1px)">
|
||||
<icon-park-outline:battery-charge />
|
||||
<icon-park-outline-battery-charge />
|
||||
</NIcon>
|
||||
</template>
|
||||
权限标识
|
||||
@ -403,7 +403,7 @@
|
||||
<div class="text-center">
|
||||
<div class="w-10 h-10 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-2">
|
||||
<NIcon size="16" class="text-gray-400">
|
||||
<icon-park-outline:folder-close />
|
||||
<icon-park-outline-folder-close />
|
||||
</NIcon>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500">
|
||||
@ -816,8 +816,9 @@ async function handleReset() {
|
||||
|
||||
// 行选择变化
|
||||
function handleRowSelectionChange(rowKeys: (string | number)[]) {
|
||||
const numericKeys = rowKeys.map(key => typeof key === 'string' ? Number.parseInt(key) : key)
|
||||
selectedRows.value = tableData.value.filter(row => numericKeys.includes(row.roleId))
|
||||
// 保持字符串格式避免精度丢失
|
||||
const stringKeys = rowKeys.map(key => String(key))
|
||||
selectedRows.value = tableData.value.filter(row => stringKeys.includes(String(row.roleId)))
|
||||
}
|
||||
|
||||
// 批量修改(选中一个角色进行修改)
|
||||
@ -1078,9 +1079,8 @@ async function handleConfirmAssignMenu() {
|
||||
// 确保选中子菜单时父菜单也被包含
|
||||
const completeMenuIds = ensureParentMenusIncluded(checkedKeys.value, menuData.value)
|
||||
|
||||
// 将字符串格式的key转换为数字格式发送给后端
|
||||
const menuIds = completeMenuIds.map(key => Number.parseInt(String(key)))
|
||||
const response = await saveRoleMenuPermission(currentAssignRole.value.roleId, menuIds)
|
||||
// 保持字符串格式避免精度丢失
|
||||
const response = await saveRoleMenuPermission(String(currentAssignRole.value.roleId), completeMenuIds)
|
||||
|
||||
if (response.isSuccess) {
|
||||
coiMsgSuccess('菜单权限分配成功')
|
||||
|
||||
@ -240,8 +240,8 @@
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon>
|
||||
<icon-park-outline:refresh v-if="hasSearchConditions()" />
|
||||
<icon-park-outline:plus v-else />
|
||||
<IconParkOutlineRefresh v-if="hasSearchConditions()" />
|
||||
<icon-park-outline-plus v-else />
|
||||
</NIcon>
|
||||
</template>
|
||||
{{ getEmptyActionText() }}
|
||||
@ -625,7 +625,7 @@
|
||||
<NUploadDragger>
|
||||
<div class="text-center">
|
||||
<NIcon size="48" :depth="3" class="mb-2">
|
||||
<icon-park-outline:file-excel />
|
||||
<icon-park-outline-file-excel />
|
||||
</NIcon>
|
||||
<div class="text-lg font-medium mb-1">
|
||||
点击或拖拽上传Excel文件
|
||||
@ -690,7 +690,7 @@
|
||||
@click="handleDownloadTemplate"
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon><icon-park-outline:download /></NIcon>
|
||||
<NIcon><IconParkOutlineDownload /></NIcon>
|
||||
</template>
|
||||
下载模板
|
||||
</NButton>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user