Compare commits

...

10 Commits

Author SHA1 Message Date
Leo
0f98692b25 docs(claude): 清理和优化CLAUDE.md项目文档规范
优化CLAUDE.md文档结构,移除重复和过时的示例代码,提高文档的可读性和实用性。

主要变更:
- 删除重复的按钮示例代码片段
- 精简弹框组件使用示例
- 优化图标使用规范的代码示例
- 统一代码样式和格式
- 保持核心规范要求不变

文档优化后更加简洁明了,便于开发团队参考和遵循。
2025-07-09 18:32:38 +08:00
Leo
4264ef36b7 fix(role): 修复角色管理页面中雪花ID精度丢失问题
- 修复handleConfirmAssignMenu函数,保持字符串格式避免Number转换
- 修复handleRowSelectionChange函数,使用字符串比较避免精度丢失
- 移除菜单权限分配中的Number.parseInt转换,保持字符串格式
- 确保角色ID在整个权限分配流程中保持字符串格式

解决问题:
- 部门管理等新菜单权限无法保存的问题
- 雪花ID在前端处理时精度丢失导致的数据不匹配

影响文件:
- src/views/system/role/index.vue
2025-07-09 16:45:31 +08:00
Leo
f5b06cb9b9 fix(api): 修复菜单权限分配接口的雪花ID精度丢失问题
- 将菜单权限分配接口从URL参数改为请求体参数,避免URL过长
- 修改saveRoleMenuPermission函数,保持字符串格式避免精度丢失
- 更新RoleMenuPermissionBo类型定义,使用string类型处理大数字ID
- 解决雪花ID在前端Number转换时精度丢失的问题

影响文件:
- src/service/api/system/menu/index.ts
- src/service/api/system/menu/types.ts
2025-07-09 16:44:56 +08:00
Leo
0fa1451e27 fix(layout): 修复头部通知组件图标语法错误
- 修复 Notices.vue 中5个图标字符串引用格式
- 将通知数据中的图标引用从冒号格式改为破折号格式
- 确保通知组件的图标正常显示
- 涉及 tips-one、comment-one、message-emoji、comment、beach-umbrella 等图标
2025-07-09 15:13:29 +08:00
Leo
ed949fbdb3 fix(system): 修复文件和图片管理页面图标语法错误
- 修复文件管理页面中4个图标的语法格式
- 修复图片管理页面中9个图标的语法格式
- 包含搜索、刷新、上传、删除、下载、切换视图等功能图标
- 确保文件和图片管理功能的完整图标显示
2025-07-09 15:13:07 +08:00
Leo
7b322a3da5 fix(system): 修复日志管理页面图标语法错误
- 修复操作日志页面中3个图标的语法格式
- 修复登录日志页面中3个图标的语法格式
- 包含搜索、刷新、删除等常用功能图标
- 确保日志管理模块的图标正常显示和交互
2025-07-09 15:12:43 +08:00
Leo
1b2191fc2f fix(system): 修复菜单管理页面图标语法错误
- 修复菜单管理页面中10个图标的语法格式
- 包含搜索、刷新、新增、删除、展开/收起等功能图标
- 修复树形菜单展开控制图标显示问题
- 确保菜单管理功能的所有交互图标正常工作
2025-07-09 15:12:24 +08:00
Leo
dc2162d36e fix(system): 修复用户和角色管理页面图标语法错误
- 修复用户管理页面中3个图标的语法格式
- 修复角色管理页面中14个图标的语法格式
- 涵盖搜索、刷新、新增、编辑、删除等功能图标
- 确保用户和角色管理功能的图标正常显示
2025-07-09 15:11:54 +08:00
Leo
d84ec11a32 fix(components): 修复通用组件图标语法错误
- 修复 CoiEmpty.vue 中8个图标的语法格式
- 修复 CoiPagination.vue 中4个图标的语法格式
- 将所有 icon-park-outline: 格式改为 icon-park-outline- 格式
- 解决图标渲染错误问题,确保组件正常显示
2025-07-09 15:11:38 +08:00
Leo
b430f4ef1e refactor: 清理代码格式和依赖配置
- 统一代码格式规范,移除多余空行和注释
- 更新项目依赖配置和构建设置
- 优化注册页面组件的代码结构
- 调整HTTP客户端配置以适配错误处理机制
2025-07-09 14:51:30 +08:00
16 changed files with 248 additions and 277 deletions

331
CLAUDE.md
View File

@ -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`、角色管理页面
**遵循此规范可彻底避免大数字精度丢失问题,确保系统稳定运行!**

View File

@ -1,5 +1,5 @@
{
"name": "nova-admin",
"name": "coi-admin",
"type": "module",
"version": "0.9.15",
"private": true,

View File

@ -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 }}

View File

@ -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>

View File

@ -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',

View File

@ -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,
})
}
// 兼容性导出 - 保持原有函数名以确保向后兼容

View File

@ -95,3 +95,9 @@ export interface MenuNormalResponse {
menuList: MenuVo[]
spreadList: string[] // 改为字符串数组避免大整数精度丢失
}
// 角色菜单权限分配请求参数
export interface RoleMenuPermissionBo {
roleId: string // 字符串格式避免精度丢失
menuIds: string[] // 字符串格式避免精度丢失
}

View File

@ -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) => {

View File

@ -110,7 +110,7 @@ const rules = {
},
}
const formValue = ref({
account: 'admin',
account: 'yuadmin',
pwd: '000000',
rePwd: '000000',
})

View File

@ -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' }, '✨')

View File

@ -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() }}

View File

@ -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>

View File

@ -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() }}

View File

@ -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">

View File

@ -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('菜单权限分配成功')

View File

@ -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>