222
This commit is contained in:
parent
bdb9fa3a39
commit
0fd7cf4c72
|
|
@ -0,0 +1,91 @@
|
||||||
|
# AdminUserList.vue 页面重新设计方案
|
||||||
|
|
||||||
|
## 1. 设计目标
|
||||||
|
- 整合所有提供的API方法,实现完整的管理员用户管理功能
|
||||||
|
- 提供良好的用户体验,包括表单验证、加载状态和操作反馈
|
||||||
|
- 支持批量操作和分页功能
|
||||||
|
- 保持代码结构清晰,易于维护
|
||||||
|
- **确保所有文本支持中英文切换**,使用现有的i18n机制
|
||||||
|
|
||||||
|
## 2. 页面结构设计
|
||||||
|
|
||||||
|
### 2.1 基础布局
|
||||||
|
- 保留原有的标题、卡片、搜索栏、表格和分页组件
|
||||||
|
- 添加批量操作工具栏
|
||||||
|
- 新增三个对话框:
|
||||||
|
- 用户创建/编辑对话框
|
||||||
|
- 密码重置对话框
|
||||||
|
- 操作确认对话框(用于删除和启用/禁用操作)
|
||||||
|
|
||||||
|
### 2.2 表格列设计
|
||||||
|
- 保留原有的用户名、邮箱、状态等列
|
||||||
|
- 新增角色列,显示用户关联的角色
|
||||||
|
- 新增全选复选框,支持批量操作
|
||||||
|
- 调整操作列,添加更多功能按钮
|
||||||
|
|
||||||
|
## 3. 功能实现
|
||||||
|
|
||||||
|
### 3.1 数据管理
|
||||||
|
- 使用`ref`和`reactive`管理页面状态
|
||||||
|
- 实现分页数据加载,使用`getAdminUsersList`方法
|
||||||
|
- 添加搜索和筛选功能
|
||||||
|
- 实现数据刷新机制
|
||||||
|
|
||||||
|
### 3.2 用户管理功能
|
||||||
|
- **创建用户**:点击"添加用户"按钮,打开创建对话框,调用`createAdminUser`方法
|
||||||
|
- **编辑用户**:点击"编辑"按钮,打开编辑对话框,调用`getAdminUserDetail`和`updateAdminUser`方法
|
||||||
|
- **删除用户**:支持单条删除和批量删除,调用`deleteAdminUsers`方法
|
||||||
|
- **启用/禁用用户**:点击状态切换按钮,调用`enableDisableUser`方法
|
||||||
|
- **重置密码**:点击"重置密码"按钮,打开密码重置对话框,调用`resetUserPassword`方法
|
||||||
|
|
||||||
|
### 3.3 表单验证
|
||||||
|
- 为用户创建/编辑表单添加验证规则
|
||||||
|
- 为密码重置表单添加验证规则
|
||||||
|
- 提供清晰的错误提示
|
||||||
|
|
||||||
|
### 3.4 交互体验
|
||||||
|
- 添加加载状态指示器
|
||||||
|
- 提供操作成功/失败的消息提示
|
||||||
|
- 实现平滑的对话框过渡效果
|
||||||
|
- 支持键盘快捷键(可选)
|
||||||
|
|
||||||
|
### 3.5 国际化支持
|
||||||
|
- 所有文本内容使用`{{ t('key') }}`语法,确保支持中英文切换
|
||||||
|
- 表单验证消息也需要国际化
|
||||||
|
- 动态生成的文本(如角色名称)也需要考虑国际化
|
||||||
|
|
||||||
|
## 4. API集成
|
||||||
|
- 导入`AdminRoleManagement`类
|
||||||
|
- 实例化API服务对象
|
||||||
|
- 实现所有API方法的调用和错误处理
|
||||||
|
- 添加请求取消机制(可选)
|
||||||
|
|
||||||
|
## 5. 代码结构
|
||||||
|
- 保持`<template>`、`<script setup>`和`<style scoped>`的清晰分离
|
||||||
|
- 使用组合式API编写逻辑
|
||||||
|
- 提取重复逻辑为可复用的函数
|
||||||
|
- 添加适当的注释
|
||||||
|
|
||||||
|
## 6. 实现步骤
|
||||||
|
|
||||||
|
1. 更新页面模板,添加对话框和批量操作工具栏
|
||||||
|
2. 实现数据加载和分页逻辑
|
||||||
|
3. 实现用户创建功能
|
||||||
|
4. 实现用户编辑功能
|
||||||
|
5. 实现用户删除功能
|
||||||
|
6. 实现用户启用/禁用功能
|
||||||
|
7. 实现密码重置功能
|
||||||
|
8. 添加表单验证和错误处理
|
||||||
|
9. 确保所有文本支持中英文切换
|
||||||
|
10. 优化UI/UX
|
||||||
|
11. 测试所有功能
|
||||||
|
|
||||||
|
## 7. 技术要点
|
||||||
|
- 使用Vue 3组合式API
|
||||||
|
- 使用Element Plus组件库
|
||||||
|
- 实现响应式设计
|
||||||
|
- 遵循代码规范
|
||||||
|
- 确保性能优化
|
||||||
|
- **正确使用i18n机制,支持中英文切换**
|
||||||
|
|
||||||
|
这个方案将实现一个功能完整、用户体验良好、支持中英文切换的管理员用户管理页面,整合了所有提供的API方法。
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
# 替换提示词管理页面的模拟数据为API调用
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
将 `AdminPromptManagement.vue` 组件中的模拟数据替换为真实的 API 调用,使用 `AdminPromptManagement` 类中定义的方法。
|
||||||
|
|
||||||
|
## 实现步骤
|
||||||
|
|
||||||
|
### 1. 导入 API 类并创建实例
|
||||||
|
- 在 `AdminPromptManagement.vue` 中导入 `AdminPromptManagement` 类
|
||||||
|
- 创建 API 实例用于调用方法
|
||||||
|
|
||||||
|
### 2. 替换模拟数据结构
|
||||||
|
- 移除 `prompts` 模拟数据数组
|
||||||
|
- 添加响应式数据:`prompts`(用于存储所有提示词)、`loading`(加载状态)
|
||||||
|
- 调整 `formData` 结构以匹配 API 要求(将 `referenceImage` 改为 `imageUrls`)
|
||||||
|
|
||||||
|
### 3. 实现数据获取逻辑
|
||||||
|
- 创建 `fetchPrompts` 方法,调用 `getPromptList` 获取所有提示词
|
||||||
|
- 调用时设置 `pageSize: 99` 以获取所有数据(不分页)
|
||||||
|
- 无需按 `isActive` 区分调用,获取所有数据后在前端过滤
|
||||||
|
- 在组件挂载时调用 `fetchPrompts` 初始化数据
|
||||||
|
|
||||||
|
### 4. 更新计算属性
|
||||||
|
- `inactivePrompts`:过滤 `prompts` 中 `isActive === 0` 的数据
|
||||||
|
- `activePrompts`:过滤 `prompts` 中 `isActive === 1` 的数据
|
||||||
|
- `sortedActivePrompts`:排序逻辑保持不变,但使用 `sortOrder` 字段替代 `sortIndex`
|
||||||
|
|
||||||
|
### 5. 更新组件方法
|
||||||
|
- **savePrompt**:根据 `isEditing` 状态调用 `updatePrompt` 或 `createPrompt`
|
||||||
|
- 将 `formData.imageUrls` 转换为数组格式
|
||||||
|
- **deletePrompt**:调用 `deletePrompt` API 方法,成功后重新获取数据
|
||||||
|
- **removeFromActive**:调用 `deactivatePrompt` API 方法,成功后重新获取数据
|
||||||
|
- **handleDrop**:调用 `activatePrompt` API 方法,成功后重新获取数据
|
||||||
|
- **handleSortUpdate**:调用 `batchUpdateSort` API 方法,传递排序后的提示词列表
|
||||||
|
|
||||||
|
### 6. 调整拖拽功能
|
||||||
|
- 确保拖拽排序后正确调用 `batchUpdateSort` 更新后端数据
|
||||||
|
- 转换数据格式以匹配 API 要求(`sortOrder` 字段)
|
||||||
|
|
||||||
|
### 7. 处理 API 响应
|
||||||
|
- 确保 API 返回的数据格式与组件期望的格式匹配
|
||||||
|
- 处理 API 调用的错误情况
|
||||||
|
- 添加适当的加载状态提示
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
- API 方法返回的是 Promise,需要使用 `async/await` 处理
|
||||||
|
- API 数据结构与模拟数据略有不同,需要调整:
|
||||||
|
- `isActive` 是数字类型(0/1),而非布尔值
|
||||||
|
- `imageUrls` 是数组,而非单个字符串
|
||||||
|
- 排序字段是 `sortOrder`,而非 `sortIndex`
|
||||||
|
- 需要添加错误处理和加载状态管理
|
||||||
|
- 确保所有数据更新操作后重新获取最新数据
|
||||||
|
|
||||||
|
## 预期效果
|
||||||
|
- 页面初始加载时从 API 获取所有提示词(不分页,pageSize=99)
|
||||||
|
- 左侧显示 `isActive=0` 的提示词,右侧显示 `isActive=1` 的提示词
|
||||||
|
- 所有操作(添加、编辑、删除、激活、取消激活、排序)都会调用相应的 API 方法
|
||||||
|
- 页面数据与后端保持同步
|
||||||
|
- 提供良好的加载状态和错误提示
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
# 重新设计AdminUserList.vue页面
|
||||||
|
|
||||||
|
## 1. 页面结构优化
|
||||||
|
- 保留现有页面布局,但增强功能
|
||||||
|
- 添加用户创建/编辑对话框
|
||||||
|
- 添加用户状态切换、密码重置等操作按钮
|
||||||
|
- 完善搜索功能
|
||||||
|
|
||||||
|
## 2. 数据绑定与API集成
|
||||||
|
- 引入AdminRoleManagement类
|
||||||
|
- 实现分页查询用户列表(getAdminUsersList)
|
||||||
|
- 实现用户搜索功能
|
||||||
|
- 实现用户创建(createAdminUser)
|
||||||
|
- 实现用户编辑(updateAdminUser)
|
||||||
|
- 实现用户启用/禁用(enableDisableUser)
|
||||||
|
- 实现用户密码重置(resetUserPassword)
|
||||||
|
- 实现用户删除(deleteAdminUsers)
|
||||||
|
|
||||||
|
## 3. 组件功能实现
|
||||||
|
- **用户列表**:使用el-table展示用户数据,包含用户名、邮箱、姓名、状态、创建时间等字段
|
||||||
|
- **搜索功能**:实现根据用户名和邮箱搜索
|
||||||
|
- **分页功能**:与API集成,实现分页加载
|
||||||
|
- **用户创建/编辑对话框**:包含表单验证
|
||||||
|
- **用户操作**:
|
||||||
|
- 编辑用户
|
||||||
|
- 启用/禁用用户
|
||||||
|
- 重置密码
|
||||||
|
- 删除用户
|
||||||
|
|
||||||
|
## 4. 交互优化
|
||||||
|
- 添加加载状态
|
||||||
|
- 添加操作确认提示
|
||||||
|
- 添加成功/失败消息反馈
|
||||||
|
- 优化表单验证
|
||||||
|
|
||||||
|
## 5. 代码实现步骤
|
||||||
|
1. 导入AdminRoleManagement类
|
||||||
|
2. 替换硬编码数据为API调用
|
||||||
|
3. 实现分页查询
|
||||||
|
4. 添加用户创建/编辑对话框
|
||||||
|
5. 实现各项操作功能
|
||||||
|
6. 优化交互体验
|
||||||
|
7. 测试所有功能
|
||||||
|
|
||||||
|
## 6. 预期效果
|
||||||
|
- 页面能够正常加载用户列表
|
||||||
|
- 搜索功能正常工作
|
||||||
|
- 分页功能正常工作
|
||||||
|
- 用户创建/编辑/删除功能正常
|
||||||
|
- 用户状态切换和密码重置功能正常
|
||||||
|
- 交互流畅,反馈及时
|
||||||
|
|
@ -101,12 +101,12 @@
|
||||||
<template #title>{{ t('admin.layout.users') }}</template>
|
<template #title>{{ t('admin.layout.users') }}</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
|
||||||
<el-menu-item index="/admin/points-management">
|
<el-menu-item index="/admin/points-management" v-if="false">
|
||||||
<el-icon><Coin /></el-icon>
|
<el-icon><Coin /></el-icon>
|
||||||
<template #title>{{ t('admin.layout.pointsManagement') }}</template>
|
<template #title>{{ t('admin.layout.pointsManagement') }}</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
|
||||||
<el-menu-item index="/admin/commission-management">
|
<el-menu-item index="/admin/commission-management" v-if="false">
|
||||||
<el-icon><Coin /></el-icon>
|
<el-icon><Coin /></el-icon>
|
||||||
<template #title>{{ t('admin.layout.commissionManagement') }}</template>
|
<template #title>{{ t('admin.layout.commissionManagement') }}</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
|
@ -115,7 +115,7 @@
|
||||||
<template #title>{{ t('admin.layout.promptManagement') }}</template>
|
<template #title>{{ t('admin.layout.promptManagement') }}</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
|
||||||
<el-sub-menu index="/admin/permission">
|
<el-sub-menu index="/admin/permission" v-if="false">
|
||||||
<template #title>
|
<template #title>
|
||||||
<el-icon><Lock /></el-icon>
|
<el-icon><Lock /></el-icon>
|
||||||
<span>{{ t('admin.layout.permission') }}</span>
|
<span>{{ t('admin.layout.permission') }}</span>
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@
|
||||||
@click="handleClick"
|
@click="handleClick"
|
||||||
>
|
>
|
||||||
<!-- 参考图片 -->
|
<!-- 参考图片 -->
|
||||||
<div v-if="prompt.referenceImage" class="card-image">
|
<div v-if="prompt.imageUrls.length > 0" class="card-image">
|
||||||
<img :src="prompt.referenceImage" :alt="prompt.title" />
|
<img :src="prompt.imageUrls[0]" />
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else class="card-image"></div>
|
||||||
<!-- 卡片内容 -->
|
<!-- 卡片内容 -->
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<h4 class="card-title">{{ prompt.title }}</h4>
|
<h4 class="card-title">{{ prompt.title }}</h4>
|
||||||
|
|
@ -33,6 +33,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 卡片类型标签 -->
|
||||||
|
<div class="type-badge">
|
||||||
|
{{ getTypeLabel(prompt.type) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 生效标识 -->
|
<!-- 生效标识 -->
|
||||||
<div v-if="isActive" class="active-badge">
|
<div v-if="isActive" class="active-badge">
|
||||||
<el-icon><Check /></el-icon>
|
<el-icon><Check /></el-icon>
|
||||||
|
|
@ -57,7 +62,7 @@ const props = defineProps({
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
type: '',
|
type: '',
|
||||||
referenceImage: '',
|
imageUrls: [],
|
||||||
isActive: false
|
isActive: false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
@ -70,6 +75,16 @@ const props = defineProps({
|
||||||
// 事件定义
|
// 事件定义
|
||||||
const emit = defineEmits(['edit', 'delete', 'drag-start', 'click'])
|
const emit = defineEmits(['edit', 'delete', 'drag-start', 'click'])
|
||||||
|
|
||||||
|
// 获取类型标签
|
||||||
|
const getTypeLabel = (type) => {
|
||||||
|
const typeMap = {
|
||||||
|
animal: t('admin.promptManagement.animal'),
|
||||||
|
person: t('admin.promptManagement.person'),
|
||||||
|
general: t('admin.promptManagement.general')
|
||||||
|
}
|
||||||
|
return typeMap[type] || type
|
||||||
|
}
|
||||||
|
|
||||||
// 拖拽开始事件
|
// 拖拽开始事件
|
||||||
const onDragStart = (event) => {
|
const onDragStart = (event) => {
|
||||||
event.dataTransfer.effectAllowed = 'move'
|
event.dataTransfer.effectAllowed = 'move'
|
||||||
|
|
@ -178,11 +193,25 @@ const handleDelete = () => {
|
||||||
color: #ef4444;
|
color: #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 卡片类型标签 */
|
||||||
|
.type-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
background: #6b7280;
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* 生效标识 */
|
/* 生效标识 */
|
||||||
.active-badge {
|
.active-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
right: 8px;
|
left: 8px;
|
||||||
background: #10b981;
|
background: #10b981;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
@click="handleClick"
|
@click="handleClick"
|
||||||
>
|
>
|
||||||
<!-- 左侧图片 -->
|
<!-- 左侧图片 -->
|
||||||
<div v-if="prompt.referenceImage" class="card-image">
|
<div v-if="prompt.imageUrls.length > 0" class="card-image">
|
||||||
<img :src="prompt.referenceImage" :alt="prompt.title" />
|
<img :src="prompt.imageUrls[0]" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 右侧内容 -->
|
<!-- 右侧内容 -->
|
||||||
|
|
@ -40,12 +40,6 @@
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 生效标识 -->
|
|
||||||
<div class="active-badge">
|
|
||||||
<el-icon><Check /></el-icon>
|
|
||||||
{{ t('admin.promptManagement.active') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,9 @@ export default {
|
||||||
captchaLength: 'Verification code should be 4-6 characters'
|
captchaLength: 'Verification code should be 4-6 characters'
|
||||||
},
|
},
|
||||||
common: {
|
common: {
|
||||||
|
detail: 'Detail',
|
||||||
|
active: 'Active',
|
||||||
|
inactive: 'Inactive',
|
||||||
refresh: 'Refresh',
|
refresh: 'Refresh',
|
||||||
search: 'Search',
|
search: 'Search',
|
||||||
reset: 'Reset',
|
reset: 'Reset',
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,14 @@ export default {
|
||||||
error: '出错了',
|
error: '出错了',
|
||||||
success: '操作成功',
|
success: '操作成功',
|
||||||
warning: '警告',
|
warning: '警告',
|
||||||
info: '提示'
|
info: '提示',
|
||||||
|
active: '活跃',
|
||||||
|
inactive: '非活跃',
|
||||||
|
detail: '详情',
|
||||||
|
saveSuccess: '保存成功',
|
||||||
|
saveFailed: '保存失败',
|
||||||
|
deleteSuccess: '删除成功',
|
||||||
|
deleteFailed: '删除失败'
|
||||||
},
|
},
|
||||||
orderManagement: {
|
orderManagement: {
|
||||||
title: '订单',
|
title: '订单',
|
||||||
|
|
@ -228,6 +235,9 @@ orderManagement: {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
common: {
|
common: {
|
||||||
|
detail: '详情',
|
||||||
|
active: '活跃',
|
||||||
|
inactive: '非活跃',
|
||||||
refresh: '刷新',
|
refresh: '刷新',
|
||||||
search: '搜索',
|
search: '搜索',
|
||||||
reset: '重置',
|
reset: '重置',
|
||||||
|
|
@ -331,18 +341,58 @@ orderManagement: {
|
||||||
title: '用户列表管理',
|
title: '用户列表管理',
|
||||||
userList: '用户列表',
|
userList: '用户列表',
|
||||||
addUser: '添加用户',
|
addUser: '添加用户',
|
||||||
|
editUser: '编辑用户',
|
||||||
searchPlaceholder: '搜索用户名、昵称或邮箱',
|
searchPlaceholder: '搜索用户名、昵称或邮箱',
|
||||||
username: '用户名',
|
username: '用户名',
|
||||||
|
fullName: '姓名',
|
||||||
nickname: '昵称',
|
nickname: '昵称',
|
||||||
email: '邮箱',
|
email: '邮箱',
|
||||||
phone: '电话',
|
phone: '电话',
|
||||||
role: '角色',
|
role: '角色',
|
||||||
|
roles: '角色',
|
||||||
status: '状态',
|
status: '状态',
|
||||||
createTime: '创建时间',
|
createTime: '创建时间',
|
||||||
|
lastLogin: '最后登录',
|
||||||
admin: '管理员',
|
admin: '管理员',
|
||||||
user: '普通用户',
|
user: '普通用户',
|
||||||
active: '活跃',
|
active: '活跃',
|
||||||
inactive: '非活跃'
|
inactive: '非活跃',
|
||||||
|
enable: '启用',
|
||||||
|
disable: '禁用',
|
||||||
|
resetPassword: '重置密码',
|
||||||
|
usernamePlaceholder: '请输入用户名',
|
||||||
|
fullNamePlaceholder: '请输入姓名',
|
||||||
|
emailPlaceholder: '请输入邮箱',
|
||||||
|
passwordPlaceholder: '请输入密码',
|
||||||
|
newPassword: '新密码',
|
||||||
|
newPasswordPlaceholder: '请输入新密码',
|
||||||
|
confirmPassword: '确认密码',
|
||||||
|
confirmPasswordPlaceholder: '请再次输入新密码',
|
||||||
|
selectRoles: '请选择角色',
|
||||||
|
usernameRequired: '请输入用户名',
|
||||||
|
usernameLength: '用户名长度应在3-20个字符之间',
|
||||||
|
fullNameRequired: '请输入姓名',
|
||||||
|
emailRequired: '请输入邮箱',
|
||||||
|
emailFormat: '请输入有效的邮箱地址',
|
||||||
|
passwordRequired: '请输入密码',
|
||||||
|
passwordLength: '密码长度不能少于6个字符',
|
||||||
|
rolesRequired: '请选择至少一个角色',
|
||||||
|
confirmPasswordRequired: '请输入确认密码',
|
||||||
|
passwordsNotMatch: '两次输入的密码不一致',
|
||||||
|
editSuccess: '编辑用户成功',
|
||||||
|
addSuccess: '添加用户成功',
|
||||||
|
editFailed: '编辑用户失败',
|
||||||
|
addFailed: '添加用户失败',
|
||||||
|
resetPasswordSuccess: '重置密码成功',
|
||||||
|
resetPasswordFailed: '重置密码失败',
|
||||||
|
deleteSuccess: '删除用户成功',
|
||||||
|
enableSuccess: '启用用户成功',
|
||||||
|
disableSuccess: '禁用用户成功',
|
||||||
|
statusChangeSuccess: '状态变更成功',
|
||||||
|
deleteConfirmContent: '确定要删除选中的用户吗?',
|
||||||
|
enableConfirmContent: '确定要启用选中的用户吗?',
|
||||||
|
disableConfirmContent: '确定要禁用选中的用户吗?',
|
||||||
|
isSuperuser: '超级管理员'
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
title: '内容管理',
|
title: '内容管理',
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@ const AdminRoleManagement = () => import('@/views/admin/AdminRoleManagement/Admi
|
||||||
const AdminRoleDetail = () => import('@/views/admin/AdminRoleManagement/AdminRoleDetail.vue')
|
const AdminRoleDetail = () => import('@/views/admin/AdminRoleManagement/AdminRoleDetail.vue')
|
||||||
const AdminPermissionManagement = () => import('@/views/admin/AdminPermissionManagement/AdminPermissionManagement.vue')
|
const AdminPermissionManagement = () => import('@/views/admin/AdminPermissionManagement/AdminPermissionManagement.vue')
|
||||||
const AdminPermissionDetail = () => import('@/views/admin/AdminPermissionManagement/AdminPermissionDetail.vue')
|
const AdminPermissionDetail = () => import('@/views/admin/AdminPermissionManagement/AdminPermissionDetail.vue')
|
||||||
const AdminUserList = () => import('@/views/admin/AdminUserList.vue')
|
const AdminUserList = () => import('@/views/admin/AdminRoleManagement/AdminUserList.vue')
|
||||||
const AdminPointsManagement = () => import('@/views/admin/AdminPointsManagement.vue')
|
const AdminPointsManagement = () => import('@/views/admin/AdminPointsManagement.vue')
|
||||||
const AdminCommissionManagement = () => import('@/views/admin/AdminCommissionManagement.vue')
|
const AdminCommissionManagement = () => import('@/views/admin/AdminCommissionManagement.vue')
|
||||||
const AdminPromptManagement = () => import('@/views/admin/AdminPromptManagement.vue')
|
const AdminPromptManagement = () => import('@/views/admin/AdminPromptManagement/AdminPromptManagement.vue')
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -53,25 +53,25 @@
|
||||||
center
|
center
|
||||||
>
|
>
|
||||||
<el-form :model="permissionForm" label-width="120px" :rules="permissionRules" ref="permissionFormRef">
|
<el-form :model="permissionForm" label-width="120px" :rules="permissionRules" ref="permissionFormRef">
|
||||||
<el-form-item label="权限名称" prop="permName">
|
<el-form-item :label="t('admin.permissionManagement.permissionName')" prop="permName">
|
||||||
<el-input v-model="permissionForm.permName" placeholder="请输入权限名称" />
|
<el-input v-model="permissionForm.permName" :placeholder="t('admin.permissionManagement.permissionName')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="权限代码" prop="permCode">
|
<el-form-item :label="t('admin.permissionManagement.permissionCode')" prop="permCode">
|
||||||
<el-input v-model="permissionForm.permCode" placeholder="请输入权限代码" />
|
<el-input v-model="permissionForm.permCode" :placeholder="t('admin.permissionManagement.permissionCode')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="所属模块" prop="module">
|
<el-form-item :label="t('admin.permissionManagement.module')" prop="module">
|
||||||
<el-input v-model="permissionForm.module" placeholder="请输入所属模块" />
|
<el-input v-model="permissionForm.module" :placeholder="t('admin.permissionManagement.module')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="操作类型" prop="action">
|
<el-form-item :label="t('admin.permissionManagement.action')" prop="action">
|
||||||
<el-input v-model="permissionForm.action" placeholder="请输入操作类型(如:view, add, edit, delete)" />
|
<el-input v-model="permissionForm.action" :placeholder="t('admin.permissionManagement.action')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="资源名称" prop="resource">
|
<el-form-item :label="t('admin.permissionManagement.resource')" prop="resource">
|
||||||
<el-input v-model="permissionForm.resource" placeholder="请输入资源名称" />
|
<el-input v-model="permissionForm.resource" :placeholder="t('admin.permissionManagement.resource')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="权限描述" prop="description">
|
<el-form-item :label="t('admin.permissionManagement.description')" prop="description">
|
||||||
<el-input type="textarea" v-model="permissionForm.description" placeholder="请输入权限描述" rows="3" />
|
<el-input type="textarea" v-model="permissionForm.description" :placeholder="t('admin.permissionManagement.description')" rows="3" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态">
|
<el-form-item :label="t('admin.permissionManagement.isActive')">
|
||||||
<el-switch v-model="permissionForm.isActive" />
|
<el-switch v-model="permissionForm.isActive" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@
|
||||||
<span class="count">{{ inactivePrompts.length }}</span>
|
<span class="count">{{ inactivePrompts.length }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="prompt-list">
|
<div class="prompt-list">
|
||||||
|
<el-skeleton v-if="loading" :rows="6" animated />
|
||||||
<PromptCard
|
<PromptCard
|
||||||
|
v-else
|
||||||
v-for="prompt in inactivePrompts"
|
v-for="prompt in inactivePrompts"
|
||||||
:key="prompt.id"
|
:key="prompt.id"
|
||||||
:prompt="prompt"
|
:prompt="prompt"
|
||||||
|
|
@ -41,7 +43,9 @@
|
||||||
@drop="handleDrop"
|
@drop="handleDrop"
|
||||||
@dragover="handleDragOver"
|
@dragover="handleDragOver"
|
||||||
>
|
>
|
||||||
|
<el-skeleton v-if="loading" :rows="6" animated />
|
||||||
<draggable
|
<draggable
|
||||||
|
v-else
|
||||||
v-model="sortedActivePrompts"
|
v-model="sortedActivePrompts"
|
||||||
item-key="id"
|
item-key="id"
|
||||||
@start="handleDragStart"
|
@start="handleDragStart"
|
||||||
|
|
@ -102,6 +106,7 @@
|
||||||
:auto-upload="false"
|
:auto-upload="false"
|
||||||
:limit="1"
|
:limit="1"
|
||||||
:on-change="handleImageChange"
|
:on-change="handleImageChange"
|
||||||
|
:on-remove="handleImageRemove"
|
||||||
>
|
>
|
||||||
<el-icon><Plus /></el-icon>
|
<el-icon><Plus /></el-icon>
|
||||||
<template #tip>
|
<template #tip>
|
||||||
|
|
@ -140,11 +145,12 @@
|
||||||
<label>{{ t('admin.promptManagement.content') }}:</label>
|
<label>{{ t('admin.promptManagement.content') }}:</label>
|
||||||
<p>{{ selectedPrompt.content }}</p>
|
<p>{{ selectedPrompt.content }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item" v-if="selectedPrompt.referenceImage">
|
<div class="detail-item" v-if="selectedPrompt.imageUrls && selectedPrompt.imageUrls.length > 0">
|
||||||
<label>{{ t('admin.promptManagement.referenceImage') }}:</label>
|
<label>{{ t('admin.promptManagement.referenceImage') }}:</label>
|
||||||
<img :src="selectedPrompt.referenceImage" alt="参考图" class="reference-image" />
|
<img :src="selectedPrompt.imageUrls[0]" alt="参考图" class="reference-image" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<el-skeleton v-else :rows="4" animated />
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
|
|
@ -156,15 +162,19 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { Plus } from '@element-plus/icons-vue'
|
import { Plus } from '@element-plus/icons-vue'
|
||||||
import PromptCard from '@/components/admin/PromptCard.vue'
|
import PromptCard from '@/components/admin/PromptCard.vue'
|
||||||
import PromptCardHorizontal from '@/components/admin/PromptCardHorizontal.vue'
|
import PromptCardHorizontal from '@/components/admin/PromptCardHorizontal.vue'
|
||||||
import draggable from 'vuedraggable'
|
import draggable from 'vuedraggable'
|
||||||
|
import { AdminPromptManagement } from './index.js'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
// 创建API实例
|
||||||
|
const promptApi = new AdminPromptManagement()
|
||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const detailVisible = ref(false)
|
const detailVisible = ref(false)
|
||||||
|
|
@ -173,197 +183,68 @@ const selectedPromptId = ref(null)
|
||||||
const selectedPrompt = ref(null)
|
const selectedPrompt = ref(null)
|
||||||
const draggedPrompt = ref(null)
|
const draggedPrompt = ref(null)
|
||||||
const fileList = ref([])
|
const fileList = ref([])
|
||||||
|
const prompts = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
type: '',
|
type: '',
|
||||||
referenceImage: ''
|
imageUrls: []
|
||||||
})
|
})
|
||||||
|
|
||||||
// 模拟数据
|
|
||||||
const prompts = ref([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: '可爱的小狗',
|
|
||||||
content: '一只可爱的白色小狗,毛茸茸的,大眼睛,活泼好动',
|
|
||||||
type: 'animal',
|
|
||||||
referenceImage: 'https://picsum.photos/id/237/300/200',
|
|
||||||
isActive: false,
|
|
||||||
sortIndex: 0,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: '商务人士',
|
|
||||||
content: '一位穿着西装的商务人士,自信的表情,背景是办公室',
|
|
||||||
type: 'person',
|
|
||||||
referenceImage: '',
|
|
||||||
isActive: false,
|
|
||||||
sortIndex: 1,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
title: '美丽的风景',
|
|
||||||
content: '一片美丽的自然风光,蓝天白云,绿水青山',
|
|
||||||
type: 'general',
|
|
||||||
referenceImage: 'https://picsum.photos/id/1015/300/200',
|
|
||||||
isActive: true,
|
|
||||||
sortIndex: 0,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
title: '优雅的猫咪',
|
|
||||||
content: '一只优雅的黑色猫咪,黄色的眼睛,柔软的毛发',
|
|
||||||
type: 'animal',
|
|
||||||
referenceImage: 'https://picsum.photos/id/452/300/200',
|
|
||||||
isActive: false,
|
|
||||||
sortIndex: 2,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
title: '医生',
|
|
||||||
content: '一位穿着白大褂的医生,戴着口罩,手持听诊器',
|
|
||||||
type: 'person',
|
|
||||||
referenceImage: 'https://picsum.photos/id/225/300/200',
|
|
||||||
isActive: false,
|
|
||||||
sortIndex: 3,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
title: '现代办公室',
|
|
||||||
content: '一个现代化的办公室,整洁的桌面,舒适的座椅',
|
|
||||||
type: 'general',
|
|
||||||
referenceImage: 'https://picsum.photos/id/1059/300/200',
|
|
||||||
isActive: true,
|
|
||||||
sortIndex: 1,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 7,
|
|
||||||
title: '强壮的狮子',
|
|
||||||
content: '一只强壮的狮子,金色的鬃毛,威严的表情',
|
|
||||||
type: 'animal',
|
|
||||||
referenceImage: 'https://picsum.photos/id/287/300/200',
|
|
||||||
isActive: false,
|
|
||||||
sortIndex: 4,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 8,
|
|
||||||
title: '教师',
|
|
||||||
content: '一位和蔼可亲的教师,手持书本,站在讲台前',
|
|
||||||
type: 'person',
|
|
||||||
referenceImage: '',
|
|
||||||
isActive: false,
|
|
||||||
sortIndex: 5,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 9,
|
|
||||||
title: '科技感会议室',
|
|
||||||
content: '一个充满科技感的会议室,大屏幕,智能设备',
|
|
||||||
type: 'general',
|
|
||||||
referenceImage: 'https://picsum.photos/id/1069/300/200',
|
|
||||||
isActive: true,
|
|
||||||
sortIndex: 2,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 10,
|
|
||||||
title: '活泼的兔子',
|
|
||||||
content: '一只活泼的灰色兔子,长长的耳朵,蹦蹦跳跳',
|
|
||||||
type: 'animal',
|
|
||||||
referenceImage: 'https://picsum.photos/id/297/300/200',
|
|
||||||
isActive: false,
|
|
||||||
sortIndex: 6,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 11,
|
|
||||||
title: '艺术家',
|
|
||||||
content: '一位专注的艺术家,正在画板前创作',
|
|
||||||
type: 'person',
|
|
||||||
referenceImage: 'https://picsum.photos/id/447/300/200',
|
|
||||||
isActive: false,
|
|
||||||
sortIndex: 7,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 12,
|
|
||||||
title: '温馨的咖啡馆',
|
|
||||||
content: '一个温馨的咖啡馆,柔和的灯光,舒适的氛围',
|
|
||||||
type: 'general',
|
|
||||||
referenceImage: 'https://picsum.photos/id/438/300/200',
|
|
||||||
isActive: true,
|
|
||||||
sortIndex: 3,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 13,
|
|
||||||
title: '聪明的鹦鹉',
|
|
||||||
content: '一只色彩鲜艳的鹦鹉,站在树枝上',
|
|
||||||
type: 'animal',
|
|
||||||
referenceImage: '',
|
|
||||||
isActive: false,
|
|
||||||
sortIndex: 8,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 14,
|
|
||||||
title: '运动员',
|
|
||||||
content: '一位正在跑步的运动员,充满活力',
|
|
||||||
type: 'person',
|
|
||||||
referenceImage: 'https://picsum.photos/id/340/300/200',
|
|
||||||
isActive: false,
|
|
||||||
sortIndex: 9,
|
|
||||||
createdAt: new Date()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 15,
|
|
||||||
title: '科幻城市',
|
|
||||||
content: '一个未来的科幻城市,高楼大厦,飞行器',
|
|
||||||
type: 'general',
|
|
||||||
referenceImage: 'https://picsum.photos/id/1058/300/200',
|
|
||||||
isActive: true,
|
|
||||||
sortIndex: 4,
|
|
||||||
createdAt: new Date()
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// 计算属性:未生效提示词
|
// 计算属性:未生效提示词
|
||||||
const inactivePrompts = computed(() => {
|
const inactivePrompts = computed(() => {
|
||||||
return prompts.value.filter(prompt => !prompt.isActive)
|
return prompts.value.filter(prompt => prompt.isActive === 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算属性:生效提示词(未排序)
|
// 计算属性:生效提示词(未排序)
|
||||||
const activePrompts = computed(() => {
|
const activePrompts = computed(() => {
|
||||||
return prompts.value.filter(prompt => prompt.isActive)
|
return prompts.value.filter(prompt => prompt.isActive === 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算属性:排序后的生效提示词(用于拖拽排序)
|
// 计算属性:排序后的生效提示词(用于拖拽排序)
|
||||||
const sortedActivePrompts = computed({
|
const sortedActivePrompts = computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
return prompts.value
|
return prompts.value
|
||||||
.filter(prompt => prompt.isActive)
|
.filter(prompt => prompt.isActive === 1)
|
||||||
.sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0))
|
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
|
||||||
},
|
},
|
||||||
set: (newValue) => {
|
set: (newValue) => {
|
||||||
// 当拖拽排序发生变化时,更新所有生效提示词的sortIndex
|
// 当拖拽排序发生变化时,更新所有生效提示词的sortOrder
|
||||||
newValue.forEach((prompt, index) => {
|
newValue.forEach((prompt, index) => {
|
||||||
const originalPrompt = prompts.value.find(p => p.id === prompt.id)
|
const originalPrompt = prompts.value.find(p => p.id === prompt.id)
|
||||||
if (originalPrompt) {
|
if (originalPrompt) {
|
||||||
originalPrompt.sortIndex = index
|
originalPrompt.sortOrder = index
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 获取提示词列表
|
||||||
|
const fetchPrompts = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const response = await promptApi.getPromptList({
|
||||||
|
pageSize: 99, // 不分页,获取所有数据
|
||||||
|
pageNum: 1
|
||||||
|
})
|
||||||
|
if (response.success && response.data) {
|
||||||
|
prompts.value = response.data.rows || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取提示词列表失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件挂载时获取数据
|
||||||
|
onMounted(() => {
|
||||||
|
fetchPrompts()
|
||||||
|
})
|
||||||
|
|
||||||
// 显示添加弹窗
|
// 显示添加弹窗
|
||||||
const showAddDialog = () => {
|
const showAddDialog = () => {
|
||||||
isEditing.value = false
|
isEditing.value = false
|
||||||
|
|
@ -372,7 +253,7 @@ const showAddDialog = () => {
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
type: '',
|
type: '',
|
||||||
referenceImage: ''
|
imageUrls: []
|
||||||
}
|
}
|
||||||
fileList.value = []
|
fileList.value = []
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
|
|
@ -383,70 +264,88 @@ const showEditDialog = (prompt) => {
|
||||||
isEditing.value = true
|
isEditing.value = true
|
||||||
selectedPromptId.value = prompt.id
|
selectedPromptId.value = prompt.id
|
||||||
formData.value = {
|
formData.value = {
|
||||||
|
id: prompt.id,
|
||||||
title: prompt.title,
|
title: prompt.title,
|
||||||
content: prompt.content,
|
content: prompt.content,
|
||||||
type: prompt.type,
|
type: prompt.type,
|
||||||
referenceImage: prompt.referenceImage
|
imageUrls: prompt.imageUrls || []
|
||||||
}
|
}
|
||||||
fileList.value = prompt.referenceImage ? [{ url: prompt.referenceImage }] : []
|
fileList.value = (prompt.imageUrls && prompt.imageUrls.length > 0) ? [{ url: prompt.imageUrls[0] }] : []
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存提示词
|
// 保存提示词
|
||||||
const savePrompt = () => {
|
const savePrompt = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
if (isEditing.value) {
|
if (isEditing.value) {
|
||||||
// 编辑现有提示词
|
// 编辑现有提示词
|
||||||
const index = prompts.value.findIndex(p => p.id === selectedPromptId.value)
|
await promptApi.updatePrompt(formData.value)
|
||||||
if (index !== -1) {
|
|
||||||
prompts.value[index] = {
|
|
||||||
...prompts.value[index],
|
|
||||||
title: formData.value.title,
|
|
||||||
content: formData.value.content,
|
|
||||||
type: formData.value.type,
|
|
||||||
referenceImage: formData.value.referenceImage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// 添加新提示词
|
// 添加新提示词
|
||||||
const newPrompt = {
|
await promptApi.createPrompt(formData.value)
|
||||||
id: Date.now(),
|
|
||||||
title: formData.value.title,
|
|
||||||
content: formData.value.content,
|
|
||||||
type: formData.value.type,
|
|
||||||
referenceImage: formData.value.referenceImage,
|
|
||||||
isActive: false,
|
|
||||||
sortIndex: prompts.value.filter(p => !p.isActive).length,
|
|
||||||
createdAt: new Date()
|
|
||||||
}
|
|
||||||
prompts.value.push(newPrompt)
|
|
||||||
}
|
}
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
|
// 重新获取数据
|
||||||
|
await fetchPrompts()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存提示词失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除提示词
|
// 删除提示词
|
||||||
const deletePrompt = (promptId) => {
|
const deletePrompt = async (promptId) => {
|
||||||
prompts.value = prompts.value.filter(p => p.id !== promptId)
|
try {
|
||||||
|
loading.value = true
|
||||||
|
await promptApi.deletePrompt(promptId)
|
||||||
|
// 重新获取数据
|
||||||
|
await fetchPrompts()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除提示词失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从生效区域移除提示词
|
// 从生效区域移除提示词
|
||||||
const removeFromActive = (promptId) => {
|
const removeFromActive = async (promptId) => {
|
||||||
const prompt = prompts.value.find(p => p.id === promptId)
|
try {
|
||||||
if (prompt) {
|
loading.value = true
|
||||||
prompt.isActive = false
|
await promptApi.deactivatePrompt(promptId)
|
||||||
|
// 重新获取数据
|
||||||
|
await fetchPrompts()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('取消激活提示词失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理图片上传变化
|
// 处理图片上传变化
|
||||||
const handleImageChange = (file) => {
|
const handleImageChange = async (file) => {
|
||||||
if (file.raw) {
|
if (file.raw) {
|
||||||
const reader = new FileReader()
|
try {
|
||||||
reader.onload = (e) => {
|
loading.value = true
|
||||||
formData.value.referenceImage = e.target.result
|
// 调用上传图片API
|
||||||
|
const response = await promptApi.uploadFileCom(file.raw)
|
||||||
|
// 设置返回的图片URL
|
||||||
|
formData.value.imageUrls = [response]
|
||||||
|
} catch (error) {
|
||||||
|
console.error('上传图片失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
}
|
}
|
||||||
reader.readAsDataURL(file.raw)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理图片删除
|
||||||
|
const handleImageRemove = () => {
|
||||||
|
// 清空formData中的imageUrls数组
|
||||||
|
formData.value.imageUrls = []
|
||||||
|
}
|
||||||
|
|
||||||
// 处理左侧卡片拖拽开始
|
// 处理左侧卡片拖拽开始
|
||||||
const handleLeftDragStart = (prompt) => {
|
const handleLeftDragStart = (prompt) => {
|
||||||
draggedPrompt.value = prompt
|
draggedPrompt.value = prompt
|
||||||
|
|
@ -458,20 +357,43 @@ const handleDragStart = (evt) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理排序更新
|
// 处理排序更新
|
||||||
const handleSortUpdate = (evt) => {
|
const handleSortUpdate = async (evt) => {
|
||||||
// 排序已经通过sortedActivePrompts的setter自动处理
|
// 准备批量更新排序的数据
|
||||||
|
const sortData = {
|
||||||
|
items: sortedActivePrompts.value.map((prompt, index) => ({
|
||||||
|
id: prompt.id,
|
||||||
|
sortOrder: index
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
await promptApi.batchUpdateSort(sortData)
|
||||||
|
// 重新获取数据
|
||||||
|
await fetchPrompts()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新排序失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理拖拽结束(从左侧到右侧)
|
// 处理拖拽结束(从左侧到右侧)
|
||||||
const handleDrop = (event) => {
|
const handleDrop = async (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (draggedPrompt.value && !draggedPrompt.value.isActive) {
|
if (draggedPrompt.value && draggedPrompt.value.isActive === 0) {
|
||||||
// 设置为生效状态
|
try {
|
||||||
draggedPrompt.value.isActive = true
|
loading.value = true
|
||||||
// 设置新的sortIndex为当前激活列表长度
|
// 调用激活API
|
||||||
draggedPrompt.value.sortIndex = sortedActivePrompts.value.length
|
await promptApi.activatePrompt(draggedPrompt.value.id)
|
||||||
|
// 重新获取数据
|
||||||
|
await fetchPrompts()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('激活提示词失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
draggedPrompt.value = null
|
draggedPrompt.value = null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理拖拽悬停
|
// 处理拖拽悬停
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
import { adminApi,requestUtils,FileServer} from '@deotaland/utils';
|
||||||
|
export class AdminPromptManagement extends FileServer {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
//查询提示词详情
|
||||||
|
async getPromptDetail(id) {
|
||||||
|
let parmas = {
|
||||||
|
id:id,
|
||||||
|
}
|
||||||
|
return await requestUtils.common(adminApi.default.getPromptDetail,parmas);
|
||||||
|
/**
|
||||||
|
返回示例:
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"id": 9007199254740991,
|
||||||
|
"type": "string",
|
||||||
|
"title": "string",
|
||||||
|
"content": "string",
|
||||||
|
"imageUrls": "string",
|
||||||
|
"isActive": 1073741824,
|
||||||
|
"sortOrder": 1073741824,
|
||||||
|
"adminId": 9007199254740991,
|
||||||
|
"remark": "string",
|
||||||
|
"isDelete": 1073741824,
|
||||||
|
"createdAt": "2025-12-19T06:38:48.802Z",
|
||||||
|
"updatedAt": "2025-12-19T06:38:48.802Z"
|
||||||
|
},
|
||||||
|
"message": "操作成功"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
//分页查询提示词列表
|
||||||
|
async getPromptList(data) {
|
||||||
|
let params = {
|
||||||
|
type:data.type,
|
||||||
|
title:data.title,
|
||||||
|
isActive:data.isActive,
|
||||||
|
pageSize:data.pageSize,
|
||||||
|
pageNum:data.pageNum,
|
||||||
|
orderByColumn:data.orderByColumn,
|
||||||
|
isAsc:data.isAsc
|
||||||
|
}
|
||||||
|
return await requestUtils.common(adminApi.default.getPromptList,params);
|
||||||
|
/**
|
||||||
|
返回示例:
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"total": 9007199254740991,
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"id": 9007199254740991,
|
||||||
|
"type": "string",
|
||||||
|
"typeName": "string",
|
||||||
|
"title": "string",
|
||||||
|
"content": "string",
|
||||||
|
"imageUrls": [
|
||||||
|
"string"
|
||||||
|
],
|
||||||
|
"isActive": 1073741824,
|
||||||
|
"sortOrder": 1073741824,
|
||||||
|
"adminId": 9007199254740991,
|
||||||
|
"adminName": "string",
|
||||||
|
"remark": "string",
|
||||||
|
"createdAt": "2025-12-19T06:54:33.803Z",
|
||||||
|
"updatedAt": "2025-12-19T06:54:33.803Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"code": 1073741824,
|
||||||
|
"msg": "string"
|
||||||
|
},
|
||||||
|
"message": "操作成功"
|
||||||
|
}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
//更新提示词
|
||||||
|
async updatePrompt(data) {
|
||||||
|
let params = {
|
||||||
|
type: data.type,
|
||||||
|
title: data.title,
|
||||||
|
content: data.content,
|
||||||
|
imageUrls: data.imageUrls,
|
||||||
|
remark: data.remark
|
||||||
|
}
|
||||||
|
let requestUrl = {
|
||||||
|
url:adminApi.default.updatePrompt.url.replace('{id}',data.id),
|
||||||
|
method:adminApi.default.updatePrompt.method,
|
||||||
|
isLoading:adminApi.default.updatePrompt.isLoading,
|
||||||
|
}
|
||||||
|
return await requestUtils.common(requestUrl,params);
|
||||||
|
}
|
||||||
|
//批量更新排序
|
||||||
|
async batchUpdateSort(data) {
|
||||||
|
// data传递参数示例:{//sortOrder为排序字段,值为整数,id为提示词id
|
||||||
|
// "items": [
|
||||||
|
// {
|
||||||
|
// "id": 9007199254740991,
|
||||||
|
// "sortOrder": 1073741824
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
let params = {
|
||||||
|
items: data.items
|
||||||
|
}
|
||||||
|
return await requestUtils.common(adminApi.default.batchUpdateSort,params);
|
||||||
|
}
|
||||||
|
//删除提示词
|
||||||
|
async deletePrompt(id) {
|
||||||
|
let params = {
|
||||||
|
id:id,
|
||||||
|
}
|
||||||
|
let requestUrl = {
|
||||||
|
url:adminApi.default.deletePrompt.url.replace('{id}',id),
|
||||||
|
method:adminApi.default.deletePrompt.method,
|
||||||
|
isLoading:adminApi.default.deletePrompt.isLoading,
|
||||||
|
}
|
||||||
|
return await requestUtils.common(requestUrl,params);
|
||||||
|
}
|
||||||
|
//取消激活提示词
|
||||||
|
async deactivatePrompt(id) {
|
||||||
|
let params = {
|
||||||
|
id:id,
|
||||||
|
}
|
||||||
|
let requestUrl = {
|
||||||
|
url:adminApi.default.deactivatePrompt.url.replace('{id}',id),
|
||||||
|
method:adminApi.default.deactivatePrompt.method,
|
||||||
|
isLoading:adminApi.default.deactivatePrompt.isLoading,
|
||||||
|
}
|
||||||
|
return await requestUtils.common(requestUrl,params);
|
||||||
|
}
|
||||||
|
//激活提示词
|
||||||
|
async activatePrompt(id) {
|
||||||
|
let params = {
|
||||||
|
id:id,
|
||||||
|
}
|
||||||
|
let requestUrl = {
|
||||||
|
url:adminApi.default.activatePrompt.url.replace('{id}',id),
|
||||||
|
method:adminApi.default.activatePrompt.method,
|
||||||
|
isLoading:adminApi.default.activatePrompt.isLoading,
|
||||||
|
}
|
||||||
|
return await requestUtils.common(requestUrl,params);
|
||||||
|
}
|
||||||
|
//创建提示词
|
||||||
|
async createPrompt(data) {
|
||||||
|
let params = {
|
||||||
|
type: data.type,
|
||||||
|
title: data.title,
|
||||||
|
content: data.content,
|
||||||
|
imageUrls: data.imageUrls,
|
||||||
|
remark: data.remark
|
||||||
|
}
|
||||||
|
return await requestUtils.common(adminApi.default.createPrompt,params);
|
||||||
|
}
|
||||||
|
//上传文件返回图片url
|
||||||
|
async uploadFileCom(file) {
|
||||||
|
return this.uploadFile(file);//会返回图片url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -59,19 +59,19 @@
|
||||||
center
|
center
|
||||||
>
|
>
|
||||||
<el-form :model="roleForm" label-width="120px" :rules="roleRules" ref="roleFormRef">
|
<el-form :model="roleForm" label-width="120px" :rules="roleRules" ref="roleFormRef">
|
||||||
<el-form-item label="角色名称" prop="roleName">
|
<el-form-item :label="t('admin.roleManagement.roleName')" prop="roleName">
|
||||||
<el-input v-model="roleForm.roleName" placeholder="请输入角色名称" />
|
<el-input v-model="roleForm.roleName" :placeholder="t('admin.roleManagement.roleName')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="角色代码" prop="roleCode">
|
<el-form-item :label="t('admin.roleManagement.roleCode')" prop="roleCode">
|
||||||
<el-input v-model="roleForm.roleCode" placeholder="请输入角色代码" />
|
<el-input v-model="roleForm.roleCode" :placeholder="t('admin.roleManagement.roleCode')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="角色描述" prop="description">
|
<el-form-item :label="t('admin.roleManagement.description')" prop="description">
|
||||||
<el-input type="textarea" v-model="roleForm.description" placeholder="请输入角色描述" rows="3" />
|
<el-input type="textarea" v-model="roleForm.description" :placeholder="t('admin.roleManagement.description')" rows="3" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="是否系统角色">
|
<el-form-item :label="t('admin.roleManagement.isSystem')">
|
||||||
<el-switch v-model="roleForm.isSystem" />
|
<el-switch v-model="roleForm.isSystem" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态">
|
<el-form-item :label="t('admin.roleManagement.isActive')">
|
||||||
<el-switch v-model="roleForm.isActive" />
|
<el-switch v-model="roleForm.isActive" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,593 @@
|
||||||
|
<template>
|
||||||
|
<div class="admin-user-list">
|
||||||
|
<h2 class="page-title">{{ t('admin.userList.title') }}</h2>
|
||||||
|
<div class="user-list-content">
|
||||||
|
<el-card shadow="hover" class="user-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ t('admin.userList.userList') }}</span>
|
||||||
|
<el-button type="primary" size="small" @click="showUserDialog">
|
||||||
|
{{ t('admin.userList.addUser') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="search-bar">
|
||||||
|
<el-input :placeholder="t('admin.userList.searchPlaceholder')" v-model="searchQuery" clearable style="width: 300px;">
|
||||||
|
<template #append>
|
||||||
|
<el-button type="primary" @click="searchUsers">
|
||||||
|
{{ t('admin.common.search') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 批量操作工具栏 -->
|
||||||
|
<div class="batch-operations" v-if="selectedUserIds.length > 0">
|
||||||
|
<el-button type="danger" size="small" @click="showConfirmDialog('delete')">
|
||||||
|
{{ t('admin.common.delete') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="warning" size="small" @click="showConfirmDialog('enable')">
|
||||||
|
{{ t('admin.userList.enable') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="warning" size="small" @click="showConfirmDialog('disable')">
|
||||||
|
{{ t('admin.userList.disable') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 用户列表表格 -->
|
||||||
|
<el-table
|
||||||
|
:data="userList"
|
||||||
|
style="width: 100%"
|
||||||
|
v-loading="loading"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
stripe
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column prop="username" :label="t('admin.userList.username')" width="180" />
|
||||||
|
<el-table-column prop="fullName" :label="t('admin.userList.fullName')" width="180" />
|
||||||
|
<el-table-column prop="email" :label="t('admin.userList.email')" />
|
||||||
|
<el-table-column prop="roles" :label="t('admin.userList.roles')" width="200">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
v-for="role in (scope.row.roles || [])"
|
||||||
|
:key="role.id"
|
||||||
|
size="small"
|
||||||
|
type="info"
|
||||||
|
style="margin-right: 5px; margin-bottom: 5px;"
|
||||||
|
>
|
||||||
|
{{ role.roleName }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="isActive" :label="t('admin.userList.status')" width="160">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-switch
|
||||||
|
v-model="scope.row.isActive"
|
||||||
|
@change="handleStatusChange(scope.row)"
|
||||||
|
:active-text="t('admin.userList.active')"
|
||||||
|
:inactive-text="t('admin.userList.inactive')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="createdAt" :label="t('admin.userList.createTime')" width="180" />
|
||||||
|
<el-table-column prop="lastLogin" :label="t('admin.userList.lastLogin')" width="180" />
|
||||||
|
<el-table-column :label="t('admin.common.action')" width="250" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" type="primary" @click="showUserDialog(scope.row)">
|
||||||
|
{{ t('admin.common.edit') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button size="small" type="warning" @click="showResetPasswordDialog(scope.row)">
|
||||||
|
{{ t('admin.userList.resetPassword') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button size="small" type="danger" @click="showConfirmDialog('delete', [scope.row.id])">
|
||||||
|
{{ t('admin.common.delete') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页控件 -->
|
||||||
|
<div class="pagination">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="total"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 用户创建/编辑对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="userDialogVisible"
|
||||||
|
:title="isEditMode ? t('admin.userList.editUser') : t('admin.userList.addUser')"
|
||||||
|
width="600px"
|
||||||
|
>
|
||||||
|
<el-form :model="userForm" :rules="userFormRules" ref="userFormRef" label-width="100px">
|
||||||
|
<el-form-item :label="t('admin.userList.username')" prop="username">
|
||||||
|
<el-input v-model="userForm.username" :placeholder="t('admin.userList.usernamePlaceholder')" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('admin.userList.fullName')" prop="fullName">
|
||||||
|
<el-input v-model="userForm.fullName" :placeholder="t('admin.userList.fullNamePlaceholder')" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('admin.userList.email')" prop="email">
|
||||||
|
<el-input v-model="userForm.email" :placeholder="t('admin.userList.emailPlaceholder')" type="email" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="!isEditMode" :label="t('admin.userList.password')" prop="password">
|
||||||
|
<el-input v-model="userForm.password" :placeholder="t('admin.userList.passwordPlaceholder')" type="password" show-password />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('admin.userList.status')" prop="isActive">
|
||||||
|
<el-switch v-model="userForm.isActive" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('admin.userList.isSuperuser')" prop="isSuperuser">
|
||||||
|
<el-switch v-model="userForm.isSuperuser" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('admin.userList.roles')" prop="roleIds">
|
||||||
|
<el-select v-model="userForm.roleIds" multiple :placeholder="t('admin.userList.selectRoles')" style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="role in roleList"
|
||||||
|
:key="role.id"
|
||||||
|
:label="role.roleName"
|
||||||
|
:value="role.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="userDialogVisible = false">{{ t('admin.common.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="submitUserForm">{{ t('admin.common.save') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 密码重置对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="resetPasswordDialogVisible"
|
||||||
|
:title="t('admin.userList.resetPassword')"
|
||||||
|
width="400px"
|
||||||
|
>
|
||||||
|
<el-form :model="resetPasswordForm" :rules="resetPasswordFormRules" ref="resetPasswordFormRef" label-width="100px">
|
||||||
|
<el-form-item :label="t('admin.userList.newPassword')" prop="newPassword">
|
||||||
|
<el-input v-model="resetPasswordForm.newPassword" :placeholder="t('admin.userList.newPasswordPlaceholder')" type="password" show-password />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('admin.userList.confirmPassword')" prop="confirmPassword">
|
||||||
|
<el-input v-model="resetPasswordForm.confirmPassword" :placeholder="t('admin.userList.confirmPasswordPlaceholder')" type="password" show-password />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="resetPasswordDialogVisible = false">{{ t('admin.common.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="submitResetPassword">{{ t('admin.common.confirm') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 操作确认对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="confirmDialogVisible"
|
||||||
|
:title="confirmDialogTitle"
|
||||||
|
width="400px"
|
||||||
|
>
|
||||||
|
<div>{{ confirmDialogContent }}</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="confirmDialogVisible = false">{{ t('admin.common.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="confirmAction">{{ t('admin.common.confirm') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, reactive } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { AdminRoleManagement } from './index.js'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
// 初始化API服务
|
||||||
|
const adminRoleManagement = new AdminRoleManagement()
|
||||||
|
|
||||||
|
// 搜索和分页
|
||||||
|
const searchQuery = ref('')
|
||||||
|
const currentPage = ref(1)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const total = ref(0)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 用户列表数据
|
||||||
|
const userList = ref([])
|
||||||
|
const roleList = ref([])
|
||||||
|
|
||||||
|
// 选中的用户ID
|
||||||
|
const selectedUserIds = ref([])
|
||||||
|
|
||||||
|
// 对话框状态
|
||||||
|
const userDialogVisible = ref(false)
|
||||||
|
const resetPasswordDialogVisible = ref(false)
|
||||||
|
const confirmDialogVisible = ref(false)
|
||||||
|
|
||||||
|
// 编辑模式
|
||||||
|
const isEditMode = ref(false)
|
||||||
|
|
||||||
|
// 当前操作的用户ID
|
||||||
|
const currentUserId = ref(null)
|
||||||
|
|
||||||
|
// 确认对话框配置
|
||||||
|
const confirmDialogTitle = ref('')
|
||||||
|
const confirmDialogContent = ref('')
|
||||||
|
const confirmActionType = ref('')
|
||||||
|
const confirmActionIds = ref([])
|
||||||
|
|
||||||
|
// 用户表单数据
|
||||||
|
const userForm = reactive({
|
||||||
|
id: '',
|
||||||
|
username: '',
|
||||||
|
fullName: '',
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
isActive: true,
|
||||||
|
isSuperuser: false,
|
||||||
|
roleIds: []
|
||||||
|
})
|
||||||
|
|
||||||
|
// 密码重置表单数据
|
||||||
|
const resetPasswordForm = reactive({
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单引用
|
||||||
|
const userFormRef = ref(null)
|
||||||
|
const resetPasswordFormRef = ref(null)
|
||||||
|
|
||||||
|
// 用户表单验证规则
|
||||||
|
const userFormRules = reactive({
|
||||||
|
username: [
|
||||||
|
{ required: true, message: t('admin.userList.usernameRequired'), trigger: 'blur' },
|
||||||
|
{ min: 3, max: 20, message: t('admin.userList.usernameLength'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
fullName: [
|
||||||
|
{ required: true, message: t('admin.userList.fullNameRequired'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
email: [
|
||||||
|
{ required: true, message: t('admin.userList.emailRequired'), trigger: 'blur' },
|
||||||
|
{ type: 'email', message: t('admin.userList.emailFormat'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{ required: true, message: t('admin.userList.passwordRequired'), trigger: 'blur' },
|
||||||
|
{ min: 6, message: t('admin.userList.passwordLength'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
roleIds: [
|
||||||
|
{ type: 'array', required: true, message: t('admin.userList.rolesRequired'), trigger: 'change' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 密码重置表单验证规则
|
||||||
|
const resetPasswordFormRules = reactive({
|
||||||
|
newPassword: [
|
||||||
|
{ required: true, message: t('admin.userList.passwordRequired'), trigger: 'blur' },
|
||||||
|
{ min: 6, message: t('admin.userList.passwordLength'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
confirmPassword: [
|
||||||
|
{ required: true, message: t('admin.userList.confirmPasswordRequired'), trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value !== resetPasswordForm.newPassword) {
|
||||||
|
callback(new Error(t('admin.userList.passwordsNotMatch')))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取角色列表
|
||||||
|
const getRoleList = async () => {
|
||||||
|
try {
|
||||||
|
const response = await adminRoleManagement.getRoleList()
|
||||||
|
if (response.success) {
|
||||||
|
roleList.value = response.data
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(t('admin.common.requestFailed'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户列表
|
||||||
|
const getAdminUsers = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
username: searchQuery.value,
|
||||||
|
email: '',
|
||||||
|
isActive: undefined,
|
||||||
|
pageSize: pageSize.value,
|
||||||
|
pageNum: currentPage.value,
|
||||||
|
orderByColumn: '',
|
||||||
|
isAsc: 'asc'
|
||||||
|
}
|
||||||
|
const response = await adminRoleManagement.getAdminUsersList(params)
|
||||||
|
if (response.success) {
|
||||||
|
userList.value = response.data.rows || []
|
||||||
|
total.value = response.data.total || 0
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(t('admin.common.requestFailed'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索用户
|
||||||
|
const searchUsers = () => {
|
||||||
|
currentPage.value = 1
|
||||||
|
getAdminUsers()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页处理
|
||||||
|
const handleSizeChange = (size) => {
|
||||||
|
pageSize.value = size
|
||||||
|
getAdminUsers()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (page) => {
|
||||||
|
currentPage.value = page
|
||||||
|
getAdminUsers()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择用户变化
|
||||||
|
const handleSelectionChange = (selection) => {
|
||||||
|
selectedUserIds.value = selection.map(user => user.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示用户对话框
|
||||||
|
const showUserDialog = (user = null) => {
|
||||||
|
if (user) {
|
||||||
|
// 编辑模式
|
||||||
|
isEditMode.value = true
|
||||||
|
userForm.id = user.id
|
||||||
|
userForm.username = user.username
|
||||||
|
userForm.fullName = user.fullName
|
||||||
|
userForm.email = user.email
|
||||||
|
userForm.isActive = user.isActive
|
||||||
|
userForm.isSuperuser = user.isSuperuser
|
||||||
|
userForm.roleIds = Array.isArray(user.roles) ? user.roles.map(role => role.id) : []
|
||||||
|
} else {
|
||||||
|
// 创建模式
|
||||||
|
isEditMode.value = false
|
||||||
|
Object.assign(userForm, {
|
||||||
|
id: '',
|
||||||
|
username: '',
|
||||||
|
fullName: '',
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
isActive: true,
|
||||||
|
isSuperuser: false,
|
||||||
|
roleIds: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
userDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示重置密码对话框
|
||||||
|
const showResetPasswordDialog = (user) => {
|
||||||
|
currentUserId.value = user.id
|
||||||
|
resetPasswordForm.newPassword = ''
|
||||||
|
resetPasswordForm.confirmPassword = ''
|
||||||
|
resetPasswordDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示确认对话框
|
||||||
|
const showConfirmDialog = (actionType, ids = []) => {
|
||||||
|
confirmActionType.value = actionType
|
||||||
|
confirmActionIds.value = ids.length > 0 ? ids : selectedUserIds.value
|
||||||
|
|
||||||
|
switch (actionType) {
|
||||||
|
case 'delete':
|
||||||
|
confirmDialogTitle.value = t('admin.common.deleteConfirm')
|
||||||
|
confirmDialogContent.value = t('admin.userList.deleteConfirmContent')
|
||||||
|
break
|
||||||
|
case 'enable':
|
||||||
|
confirmDialogTitle.value = t('admin.common.enableConfirm')
|
||||||
|
confirmDialogContent.value = t('admin.userList.enableConfirmContent')
|
||||||
|
break
|
||||||
|
case 'disable':
|
||||||
|
confirmDialogTitle.value = t('admin.common.disableConfirm')
|
||||||
|
confirmDialogContent.value = t('admin.userList.disableConfirmContent')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
confirmDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交用户表单
|
||||||
|
const submitUserForm = async () => {
|
||||||
|
if (!userFormRef.value) return
|
||||||
|
|
||||||
|
await userFormRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
try {
|
||||||
|
let response
|
||||||
|
if (isEditMode.value) {
|
||||||
|
// 更新用户
|
||||||
|
response = await adminRoleManagement.updateAdminUser(userForm)
|
||||||
|
} else {
|
||||||
|
// 创建用户
|
||||||
|
response = await adminRoleManagement.createAdminUser(userForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
ElMessage.success(isEditMode.value ? t('admin.userList.editSuccess') : t('admin.userList.addSuccess'))
|
||||||
|
userDialogVisible.value = false
|
||||||
|
getAdminUsers()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(isEditMode.value ? t('admin.userList.editFailed') : t('admin.userList.addFailed'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交密码重置
|
||||||
|
const submitResetPassword = async () => {
|
||||||
|
if (!resetPasswordFormRef.value) return
|
||||||
|
|
||||||
|
await resetPasswordFormRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
try {
|
||||||
|
const response = await adminRoleManagement.resetUserPassword({
|
||||||
|
id: currentUserId.value,
|
||||||
|
newPassword: resetPasswordForm.newPassword
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
ElMessage.success(t('admin.userList.resetPasswordSuccess'))
|
||||||
|
resetPasswordDialogVisible.value = false
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(t('admin.userList.resetPasswordFailed'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行确认操作
|
||||||
|
const confirmAction = async () => {
|
||||||
|
try {
|
||||||
|
let response
|
||||||
|
|
||||||
|
switch (confirmActionType.value) {
|
||||||
|
case 'delete':
|
||||||
|
response = await adminRoleManagement.deleteAdminUsers({ ids: confirmActionIds.value })
|
||||||
|
if (response.success) {
|
||||||
|
ElMessage.success(t('admin.userList.deleteSuccess'))
|
||||||
|
getAdminUsers()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'enable':
|
||||||
|
for (const id of confirmActionIds.value) {
|
||||||
|
await adminRoleManagement.enableDisableUser({ id, isActive: true })
|
||||||
|
}
|
||||||
|
ElMessage.success(t('admin.userList.enableSuccess'))
|
||||||
|
getAdminUsers()
|
||||||
|
break
|
||||||
|
case 'disable':
|
||||||
|
for (const id of confirmActionIds.value) {
|
||||||
|
await adminRoleManagement.enableDisableUser({ id, isActive: false })
|
||||||
|
}
|
||||||
|
ElMessage.success(t('admin.userList.disableSuccess'))
|
||||||
|
getAdminUsers()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDialogVisible.value = false
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(t('admin.common.requestFailed'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理用户状态变更
|
||||||
|
const handleStatusChange = async (user) => {
|
||||||
|
try {
|
||||||
|
const response = await adminRoleManagement.enableDisableUser({
|
||||||
|
id: user.id,
|
||||||
|
isActive: user.isActive
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
// 恢复原状态
|
||||||
|
user.isActive = !user.isActive
|
||||||
|
ElMessage.error(t('admin.common.requestFailed'))
|
||||||
|
} else {
|
||||||
|
ElMessage.success(t('admin.userList.statusChangeSuccess'))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 恢复原状态
|
||||||
|
user.isActive = !user.isActive
|
||||||
|
ElMessage.error(t('admin.common.requestFailed'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
onMounted(async () => {
|
||||||
|
await getRoleList()
|
||||||
|
await getAdminUsers()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.admin-user-list {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-list-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-operations {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 深色主题适配 */
|
||||||
|
[data-theme="dark"] .page-title {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .batch-operations {
|
||||||
|
background-color: #1f2937;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -228,4 +228,136 @@ export class AdminRoleManagement {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
//创建管理员用户
|
||||||
|
async createAdminUser(data) {
|
||||||
|
let params = {
|
||||||
|
username: data.username || '',
|
||||||
|
email: data.email || '',
|
||||||
|
password: data.password || '',
|
||||||
|
fullName: data.fullName || '',
|
||||||
|
isActive: data.isActive !== undefined ? data.isActive : true,
|
||||||
|
isSuperuser: data.isSuperuser !== undefined ? data.isSuperuser : false,
|
||||||
|
roleIds: Array.isArray(data.roleIds) ? data.roleIds : []
|
||||||
|
}
|
||||||
|
return requestUtils.common(adminApi.default.createAdminUser, params);
|
||||||
|
}
|
||||||
|
//分页查询管理员用户列表
|
||||||
|
async getAdminUsersList(data) {
|
||||||
|
let params = {
|
||||||
|
username: data.username || '',
|
||||||
|
email: data.email || '',
|
||||||
|
isActive: data.isActive !== undefined ? data.isActive : true,
|
||||||
|
pageSize: data.pageSize || 10,
|
||||||
|
pageNum: data.pageNum || 1,
|
||||||
|
orderByColumn: data.orderByColumn || '',
|
||||||
|
isAsc: data.isAsc || 'asc'
|
||||||
|
}
|
||||||
|
return requestUtils.common(adminApi.default.getAdminUserList, params);
|
||||||
|
/**
|
||||||
|
返回示例:
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"total": 9007199254740991,
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"id": 9007199254740991,
|
||||||
|
"username": "string",
|
||||||
|
"email": "string",
|
||||||
|
"fullName": "string",
|
||||||
|
"isActive": true,
|
||||||
|
"isSuperuser": true,
|
||||||
|
"lastLogin": "2025-12-19T05:33:51.763Z",
|
||||||
|
"createdAt": "2025-12-19T05:33:51.763Z",
|
||||||
|
"updatedAt": "2025-12-19T05:33:51.763Z",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"id": 9007199254740991,
|
||||||
|
"roleCode": "string",
|
||||||
|
"roleName": "string",
|
||||||
|
"description": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"code": 1073741824,
|
||||||
|
"msg": "string"
|
||||||
|
},
|
||||||
|
"message": "操作成功"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
//更新管理员用户
|
||||||
|
async updateAdminUser(data) {
|
||||||
|
let params = {
|
||||||
|
id: data.id,
|
||||||
|
username: data.username,
|
||||||
|
email: data.email,
|
||||||
|
fullName: data.fullName,
|
||||||
|
isActive: data.isActive,
|
||||||
|
isSuperuser: data.isSuperuser,
|
||||||
|
roleIds: data.roleIds
|
||||||
|
}
|
||||||
|
return requestUtils.common(adminApi.default.updateAdminUser, params);
|
||||||
|
}
|
||||||
|
//启用/禁用用户
|
||||||
|
async enableDisableUser(data) {
|
||||||
|
let params = {
|
||||||
|
userId: data.id,
|
||||||
|
isActive : data.isActive
|
||||||
|
}
|
||||||
|
return requestUtils.common(adminApi.default.toggleAdminUserStatus, params);
|
||||||
|
}
|
||||||
|
//重置用户密码
|
||||||
|
async resetUserPassword(data) {
|
||||||
|
let params = {
|
||||||
|
userId: data.id,
|
||||||
|
newPassword: data.newPassword
|
||||||
|
}
|
||||||
|
return requestUtils.common(adminApi.default.resetAdminUserPassword, params);
|
||||||
|
}
|
||||||
|
//根据用户ID查询管理员用户详情
|
||||||
|
async getAdminUserDetail(data) {
|
||||||
|
let params = {
|
||||||
|
userId : data.id || ''
|
||||||
|
}
|
||||||
|
const requestUrl = {
|
||||||
|
method: adminApi.default.getAdminUserDetail.method,
|
||||||
|
url: adminApi.default.getAdminUserDetail.url.replace('USERID', data.id),
|
||||||
|
isLoading: adminApi.default.getAdminUserDetail.isLoading
|
||||||
|
}
|
||||||
|
return requestUtils.common(requestUrl, params);
|
||||||
|
/**
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"id": 9007199254740991,
|
||||||
|
"username": "string",
|
||||||
|
"email": "string",
|
||||||
|
"fullName": "string",
|
||||||
|
"isActive": true,
|
||||||
|
"isSuperuser": true,
|
||||||
|
"lastLogin": "2025-12-19T05:36:04.270Z",
|
||||||
|
"createdAt": "2025-12-19T05:36:04.270Z",
|
||||||
|
"updatedAt": "2025-12-19T05:36:04.270Z",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"id": 9007199254740991,
|
||||||
|
"roleCode": "string",
|
||||||
|
"roleName": "string",
|
||||||
|
"description": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"message": "操作成功"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
//批量删除管理员用户
|
||||||
|
async deleteAdminUsers(data) {
|
||||||
|
let arr = data.ids || []
|
||||||
|
return requestUtils.common(adminApi.default.batchDeleteAdminUser, arr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,204 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="admin-user-list">
|
|
||||||
<h2 class="page-title">{{ t('admin.userList.title') }}</h2>
|
|
||||||
<div class="user-list-content">
|
|
||||||
<el-card shadow="hover" class="user-card">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>{{ t('admin.userList.userList') }}</span>
|
|
||||||
<el-button type="primary" size="small">
|
|
||||||
{{ t('admin.userList.addUser') }}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="search-bar">
|
|
||||||
<el-input :placeholder="t('admin.userList.searchPlaceholder')" v-model="searchQuery" clearable style="width: 300px;">
|
|
||||||
<template #append>
|
|
||||||
<el-button type="primary" @click="searchUsers">
|
|
||||||
{{ t('admin.common.search') }}
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- stripe -->
|
|
||||||
<el-table :data="userList" style="width: 100%" v-loading="loading">
|
|
||||||
<el-table-column prop="username" :label="t('admin.userList.username')" width="180" />
|
|
||||||
<el-table-column prop="nickname" :label="t('admin.userList.nickname')" width="180" />
|
|
||||||
<el-table-column prop="email" :label="t('admin.userList.email')" />
|
|
||||||
<el-table-column prop="phone" :label="t('admin.userList.phone')" width="180" />
|
|
||||||
<el-table-column prop="role" :label="t('admin.userList.role')" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-tag :type="scope.row.role === 'admin' ? 'danger' : 'success'">
|
|
||||||
{{ scope.row.role === 'admin' ? t('admin.userList.admin') : t('admin.userList.user') }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="status" :label="t('admin.userList.status')" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-tag :type="scope.row.status === 'active' ? 'success' : 'warning'">
|
|
||||||
{{ scope.row.status === 'active' ? t('admin.userList.active') : t('admin.userList.inactive') }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="createTime" :label="t('admin.userList.createTime')" width="180" />
|
|
||||||
<el-table-column :label="t('admin.common.action')" width="200" fixed="right">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button size="small" type="primary" @click="editUser(scope.row)">
|
|
||||||
{{ t('admin.common.edit') }}
|
|
||||||
</el-button>
|
|
||||||
<el-button size="small" type="danger" @click="deleteUser(scope.row)">
|
|
||||||
{{ t('admin.common.delete') }}
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<div class="pagination">
|
|
||||||
<el-pagination
|
|
||||||
v-model:current-page="currentPage"
|
|
||||||
v-model:page-size="pageSize"
|
|
||||||
:page-sizes="[10, 20, 50, 100]"
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
:total="total"
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted } from 'vue'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
// 搜索和分页
|
|
||||||
const searchQuery = ref('')
|
|
||||||
const currentPage = ref(1)
|
|
||||||
const pageSize = ref(10)
|
|
||||||
const total = ref(100)
|
|
||||||
const loading = ref(false)
|
|
||||||
|
|
||||||
// 用户列表数据
|
|
||||||
const userList = ref([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
username: 'admin',
|
|
||||||
nickname: '管理员',
|
|
||||||
email: 'admin@example.com',
|
|
||||||
phone: '13800138000',
|
|
||||||
role: 'admin',
|
|
||||||
status: 'active',
|
|
||||||
createTime: '2023-01-01 10:00:00'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
username: 'user1',
|
|
||||||
nickname: '普通用户',
|
|
||||||
email: 'user1@example.com',
|
|
||||||
phone: '13800138001',
|
|
||||||
role: 'user',
|
|
||||||
status: 'active',
|
|
||||||
createTime: '2023-01-02 10:00:00'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
username: 'user2',
|
|
||||||
nickname: '测试用户',
|
|
||||||
email: 'user2@example.com',
|
|
||||||
phone: '13800138002',
|
|
||||||
role: 'user',
|
|
||||||
status: 'inactive',
|
|
||||||
createTime: '2023-01-03 10:00:00'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// 搜索用户
|
|
||||||
const searchUsers = () => {
|
|
||||||
ElMessage.info(t('admin.common.functionUnderDevelopment'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑用户
|
|
||||||
const editUser = (row) => {
|
|
||||||
ElMessage.info(t('admin.common.functionUnderDevelopment'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除用户
|
|
||||||
const deleteUser = (row) => {
|
|
||||||
ElMessage.info(t('admin.common.functionUnderDevelopment'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分页处理
|
|
||||||
const handleSizeChange = (size) => {
|
|
||||||
pageSize.value = size
|
|
||||||
// 这里可以添加获取数据的API调用
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCurrentChange = (page) => {
|
|
||||||
currentPage.value = page
|
|
||||||
// 这里可以添加获取数据的API调用
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
// 这里可以添加获取用户列表的API调用
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.admin-user-list {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-title {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-list-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-body {
|
|
||||||
padding: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-bar {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 深色主题适配 */
|
|
||||||
[data-theme="dark"] .page-title {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -116,136 +116,5 @@ export class AdminOrders {
|
||||||
}
|
}
|
||||||
return requestUtils.common(requestUrl, params);
|
return requestUtils.common(requestUrl, params);
|
||||||
}
|
}
|
||||||
//创建管理员用户
|
|
||||||
async createAdminUser(data) {
|
|
||||||
let params = {
|
|
||||||
username: data.username || '',
|
|
||||||
email: data.email || '',
|
|
||||||
password: data.password || '',
|
|
||||||
fullName: data.fullName || '',
|
|
||||||
isActive: data.isActive !== undefined ? data.isActive : true,
|
|
||||||
isSuperuser: data.isSuperuser !== undefined ? data.isSuperuser : false,
|
|
||||||
roleIds: Array.isArray(data.roleIds) ? data.roleIds : []
|
|
||||||
}
|
|
||||||
return requestUtils.common(adminApi.default.createAdminUser, params);
|
|
||||||
}
|
|
||||||
//分页查询管理员用户列表
|
|
||||||
async getAdminUsersList(data) {
|
|
||||||
let params = {
|
|
||||||
username: data.username || '',
|
|
||||||
email: data.email || '',
|
|
||||||
isActive: data.isActive !== undefined ? data.isActive : true,
|
|
||||||
pageSize: data.pageSize || 10,
|
|
||||||
pageNum: data.pageNum || 1,
|
|
||||||
orderByColumn: data.orderByColumn || '',
|
|
||||||
isAsc: data.isAsc || 'asc'
|
|
||||||
}
|
|
||||||
return requestUtils.common(adminApi.default.getAdminUserList, params);
|
|
||||||
/**
|
|
||||||
返回示例:
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"success": true,
|
|
||||||
"data": {
|
|
||||||
"total": 9007199254740991,
|
|
||||||
"rows": [
|
|
||||||
{
|
|
||||||
"id": 9007199254740991,
|
|
||||||
"username": "string",
|
|
||||||
"email": "string",
|
|
||||||
"fullName": "string",
|
|
||||||
"isActive": true,
|
|
||||||
"isSuperuser": true,
|
|
||||||
"lastLogin": "2025-12-19T05:33:51.763Z",
|
|
||||||
"createdAt": "2025-12-19T05:33:51.763Z",
|
|
||||||
"updatedAt": "2025-12-19T05:33:51.763Z",
|
|
||||||
"roles": [
|
|
||||||
{
|
|
||||||
"id": 9007199254740991,
|
|
||||||
"roleCode": "string",
|
|
||||||
"roleName": "string",
|
|
||||||
"description": "string"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"code": 1073741824,
|
|
||||||
"msg": "string"
|
|
||||||
},
|
|
||||||
"message": "操作成功"
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
//更新管理员用户
|
|
||||||
async updateAdminUser(data) {
|
|
||||||
let params = {
|
|
||||||
id: data.id,
|
|
||||||
username: data.username,
|
|
||||||
email: data.email,
|
|
||||||
fullName: data.fullName,
|
|
||||||
isActive: data.isActive,
|
|
||||||
isSuperuser: data.isSuperuser,
|
|
||||||
roleIds: data.roleIds
|
|
||||||
}
|
|
||||||
return requestUtils.common(adminApi.default.updateAdminUser, params);
|
|
||||||
}
|
|
||||||
//启用/禁用用户
|
|
||||||
async enableDisableUser(data) {
|
|
||||||
let params = {
|
|
||||||
userId: data.id,
|
|
||||||
isActive : data.isActive
|
|
||||||
}
|
|
||||||
return requestUtils.common(adminApi.default.toggleAdminUserStatus, params);
|
|
||||||
}
|
|
||||||
//重置用户密码
|
|
||||||
async resetUserPassword(data) {
|
|
||||||
let params = {
|
|
||||||
userId: data.id,
|
|
||||||
newPassword: data.newPassword
|
|
||||||
}
|
|
||||||
return requestUtils.common(adminApi.default.resetAdminUserPassword, params);
|
|
||||||
}
|
|
||||||
//根据用户ID查询管理员用户详情
|
|
||||||
async getAdminUserDetail(data) {
|
|
||||||
let params = {
|
|
||||||
userId : data.id || ''
|
|
||||||
}
|
|
||||||
const requestUrl = {
|
|
||||||
method: adminApi.default.getAdminUserDetail.method,
|
|
||||||
url: adminApi.default.getAdminUserDetail.url.replace('USERID', params.id),
|
|
||||||
isLoading: adminApi.default.getAdminUserDetail.isLoading
|
|
||||||
}
|
|
||||||
return requestUtils.common(requestUrl, params);
|
|
||||||
/**
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"success": true,
|
|
||||||
"data": {
|
|
||||||
"id": 9007199254740991,
|
|
||||||
"username": "string",
|
|
||||||
"email": "string",
|
|
||||||
"fullName": "string",
|
|
||||||
"isActive": true,
|
|
||||||
"isSuperuser": true,
|
|
||||||
"lastLogin": "2025-12-19T05:36:04.270Z",
|
|
||||||
"createdAt": "2025-12-19T05:36:04.270Z",
|
|
||||||
"updatedAt": "2025-12-19T05:36:04.270Z",
|
|
||||||
"roles": [
|
|
||||||
{
|
|
||||||
"id": 9007199254740991,
|
|
||||||
"roleCode": "string",
|
|
||||||
"roleName": "string",
|
|
||||||
"description": "string"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"message": "操作成功"
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
//批量删除管理员用户
|
|
||||||
async deleteAdminUsers(data) {
|
|
||||||
let arr = data.ids || []
|
|
||||||
return requestUtils.common(adminApi.default.batchDeleteAdminUser, arr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -173,6 +173,10 @@ const handleTouchEnd = () => {
|
||||||
};
|
};
|
||||||
// 定义组件属性
|
// 定义组件属性
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
combinedPromptJson:{//动态提示词
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
// 图片URL
|
// 图片URL
|
||||||
imageUrl: {
|
imageUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
@ -218,69 +222,27 @@ const handleGenerateImage = async () => {
|
||||||
if (props?.cardData?.inspirationImage) {
|
if (props?.cardData?.inspirationImage) {
|
||||||
referenceImages.push(props.cardData.inspirationImage);
|
referenceImages.push(props.cardData.inspirationImage);
|
||||||
}
|
}
|
||||||
if(props?.cardData?.ipType){
|
|
||||||
if(props?.cardData?.ipType==1){
|
|
||||||
humanTypeImg&&referenceImages.push(humanTypeImg);
|
|
||||||
}else{
|
|
||||||
anTypeImg&&referenceImages.push(anTypeImg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(iscjt){
|
if(iscjt){
|
||||||
props.cardData.imgyt&&referenceImages.push(props.cardData.imgyt);
|
props.cardData.imgyt&&referenceImages.push(props.cardData.imgyt);
|
||||||
referenceImages.push(props.cardData.diyPromptImg);
|
referenceImages.push(props.cardData.diyPromptImg);
|
||||||
referenceImages.push(cjimg);
|
referenceImages.push(cjimg);
|
||||||
}
|
}
|
||||||
// if(props.cardData.diyPromptText){
|
if(props.cardData.diyPromptText){
|
||||||
// console.log(props.cardData.diyPromptImg,'diyPromptImgdiyPromptImgdiyPromptImg');
|
referenceImages.push(props.cardData.diyPromptImg);
|
||||||
// referenceImages.push(props.cardData.diyPromptImg);
|
}
|
||||||
// if(iscjt){
|
let dtprompt;
|
||||||
// props.cardData.imgyt&&referenceImages.push(props.cardData.imgyt);
|
if(props?.cardData?.ipType==1){
|
||||||
// referenceImages.push(cjimg);
|
dtprompt = props.combinedPromptJson.person.content;
|
||||||
// }
|
referenceImages.push(...props.combinedPromptJson.person.imgs);
|
||||||
// }else{
|
}else if(props?.cardData?.ipType==2){
|
||||||
// referenceImages.push(humanTypeImg);
|
dtprompt = props.combinedPromptJson.animal.content;
|
||||||
// referenceImages.push(anTypeImg);
|
referenceImages.push(...props.combinedPromptJson.animal.imgs);
|
||||||
// }
|
}
|
||||||
// referenceImages.push(cz2);
|
if(props.cardData.prompt){
|
||||||
// referenceImages.push(humanTypeImg);
|
dtprompt = `角色外观:${props.cardData.prompt}.${dtprompt}`
|
||||||
// if(props?.cardData?.selectedExpression){
|
}
|
||||||
// referenceImages.push(props.cardData.selectedExpression.imageUrl);
|
let prompt = props.cardData.diyPromptText|| dtprompt
|
||||||
// }
|
|
||||||
// 忽略第二张参考图
|
|
||||||
let prompt = props.cardData.diyPromptText|| `
|
|
||||||
首先保证生成的角色符合以下要求
|
|
||||||
角色肤色和衣服材质都为纯色一种颜色如下:
|
|
||||||
重点:保证角色所有的服饰衣服都为木头材质颜色,并且要带一些木头纹理,颜色为#e2cfb3
|
|
||||||
一个通体由单一纯色木材雕刻而成的角色,全身无布料、无皮肤、无金属,表面光滑,颜色均匀一致,无纹理变化,整体呈现木质雕塑或木偶风格,极简设计.
|
|
||||||
A full-body character portrait
|
|
||||||
角色特征:Q 版萌系造型,头身比例夸张(大头小身),神态纯真,服饰设计融合童话风与复古感(简化一下复杂衣服纹理,只保留特征).
|
|
||||||
Style:潮玩盲盒角色设计,采用 3D 立体建模渲染,呈现细腻的质感与精致的细节。
|
|
||||||
${props?.cardData?.prompt? `Appearance: ${props?.cardData?.prompt}.`:``}
|
|
||||||
Note: The image should not have white borders.
|
|
||||||
去除原图中复杂的背景,只保留人物角色的主体。
|
|
||||||
适配3D打印:请保持服装边缘、装饰等细节略微加厚、避免过细结构,以提高打印稳定性,手指头轮廓清晰,重点:保证角色全身包括衣服都为木头材质颜色,并且要带一些木头纹理,颜色为#e2cfb3。
|
|
||||||
【3D打印结构优化】
|
|
||||||
模型用于3D打印,必须保持结构厚实、稳定,无细小悬空部件或过薄结构。
|
|
||||||
不生成透明或复杂内构。
|
|
||||||
保持厚度和连贯性,适合打印。
|
|
||||||
【材质处理】
|
|
||||||
整体需光滑、稳固、边缘柔和,防止打印时断裂。
|
|
||||||
模型应呈现专业3D效果。
|
|
||||||
${props.cardData?.ipType==1?`
|
|
||||||
调整角色的发型,使其厚实、蓬松且结构坚固,轮廓清晰扎实,适合3D打印。
|
|
||||||
确保头发具备足够的厚度与结构完整性,避免在打印过程中出现脆弱断裂,同时保留原有的可爱美感。
|
|
||||||
头发纹理细节需针对3D制造进行优化——层次平滑且分明,兼顾视觉吸引力与可打印性,维持整体俏皮且高品质的盲盒角色风格。
|
|
||||||
`:`采用疯狂动物城的设计风格`}
|
|
||||||
调整背景为极简风格,换成中性纯白色,让图片中的人物呈现3D立体效果。
|
|
||||||
保证生成的图片一定要有眼睛,一定要有嘴巴,眼睛效果要可爱童真,Q版大眼睛。
|
|
||||||
角色肤色和衣服材质都为纯色一种颜色如下:
|
|
||||||
保证角色全身都为木头材质颜色,并且要带一些木头纹理,颜色为#e2cfb3。
|
|
||||||
衣服如果不适合做木制一定要简化衣服,不能用复杂的衣服设计,保留衣服特征即可,衣服一定要纯色木质材质。
|
|
||||||
保证角色所有的服饰衣服都为木头材质颜色,并且要带一些木头纹理,颜色为#e2cfb3。
|
|
||||||
`
|
|
||||||
;
|
|
||||||
// 角色姿势:${props.cardData.ipType==1?``:``}
|
// 角色姿势:${props.cardData.ipType==1?``:``}
|
||||||
|
|
||||||
if(props.cardData.prompt&&props.cardData.prompt.indexOf('nospec')!=-1){
|
if(props.cardData.prompt&&props.cardData.prompt.indexOf('nospec')!=-1){
|
||||||
prompt = '按原图生成'
|
prompt = '按原图生成'
|
||||||
referenceImages = [props.cardData.inspirationImage];
|
referenceImages = [props.cardData.inspirationImage];
|
||||||
|
|
|
||||||
|
|
@ -324,7 +324,7 @@ const formData = ref({
|
||||||
|
|
||||||
const textareaRef = ref(null);
|
const textareaRef = ref(null);
|
||||||
const fileInput = ref(null); // 文件输入引用
|
const fileInput = ref(null); // 文件输入引用
|
||||||
const generateCount = ref(4); // 生成数量,默认为1
|
const generateCount = ref(1); // 生成数量,默认为1
|
||||||
const isOptimizing = ref(false); // 优化状态
|
const isOptimizing = ref(false); // 优化状态
|
||||||
const isDragOver = ref(false); // 拖拽状态
|
const isDragOver = ref(false); // 拖拽状态
|
||||||
const isUploading = ref(false); // 图片上传状态
|
const isUploading = ref(false); // 图片上传状态
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@
|
||||||
|
|
||||||
<!-- 根据卡片类型显示不同组件 -->
|
<!-- 根据卡片类型显示不同组件 -->
|
||||||
<IPCard
|
<IPCard
|
||||||
|
:combinedPromptJson="combinedPromptJson"
|
||||||
@preview-image="handlePreviewImage"
|
@preview-image="handlePreviewImage"
|
||||||
@generate-smooth-white-model="(imageUrl)=>handleGenerateSmoothWhiteModel(index,imageUrl)"
|
@generate-smooth-white-model="(imageUrl)=>handleGenerateSmoothWhiteModel(index,imageUrl)"
|
||||||
@create-new-card="(data)=>handleCreateFourViewCard(index,data)"
|
@create-new-card="(data)=>handleCreateFourViewCard(index,data)"
|
||||||
|
|
@ -73,7 +74,7 @@
|
||||||
@generate-model-requested="(data)=>handleGenerateModelRequested(index,data)"
|
@generate-model-requested="(data)=>handleGenerateModelRequested(index,data)"
|
||||||
@save-project="(item)=>{handleSaveProject(index,item,'image')}"
|
@save-project="(item)=>{handleSaveProject(index,item,'image')}"
|
||||||
@create-remaining-image-data="handleCreateRemainingImageData"
|
@create-remaining-image-data="handleCreateRemainingImageData"
|
||||||
v-if="card.type === 'image'"
|
v-if="card.type === 'image'&&combinedPromptJson.animal"
|
||||||
:cardData="card"
|
:cardData="card"
|
||||||
/>
|
/>
|
||||||
<ModelCard
|
<ModelCard
|
||||||
|
|
@ -178,6 +179,16 @@ const getGenerateCount = async ()=>{
|
||||||
const cards = ref([
|
const cards = ref([
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
const combinedPromptJson = ref({});
|
||||||
|
//获取动态提示词
|
||||||
|
const getCombinedPrompt = async (type=1)=>{
|
||||||
|
try {
|
||||||
|
const data = await PluginProject.getCombinedPrompt(type);
|
||||||
|
combinedPromptJson.value = data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
watch(()=>[projectInfo.value,cards.value], () => {
|
watch(()=>[projectInfo.value,cards.value], () => {
|
||||||
let newProjectInfo = {...projectInfo.value};
|
let newProjectInfo = {...projectInfo.value};
|
||||||
newProjectInfo.details.node_card = cards.value;
|
newProjectInfo.details.node_card = cards.value;
|
||||||
|
|
@ -960,6 +971,7 @@ onMounted(() => {
|
||||||
// 每次进入都显示引导弹窗
|
// 每次进入都显示引导弹窗
|
||||||
// showGuideModal.value = true;
|
// showGuideModal.value = true;
|
||||||
init();
|
init();
|
||||||
|
getCombinedPrompt();
|
||||||
// 使用优化的被动事件监听器
|
// 使用优化的被动事件监听器
|
||||||
const removeWheelListener = addPassiveEventListener(document, 'wheel', preventZoom);
|
const removeWheelListener = addPassiveEventListener(document, 'wheel', preventZoom);
|
||||||
const removeTouchStartListener = addPassiveEventListener(document, 'touchstart', preventPinchZoom);
|
const removeTouchStartListener = addPassiveEventListener(document, 'touchstart', preventPinchZoom);
|
||||||
|
|
|
||||||
|
|
@ -728,4 +728,69 @@ export class Project{
|
||||||
this.duration_seconds +=1;
|
this.duration_seconds +=1;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
//获取动态提示词
|
||||||
|
async getCombinedPrompt(){//type:1人物类型2动物类型
|
||||||
|
try {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const res = await requestUtils.common(clientApi.default.combined)
|
||||||
|
if(res.code === 0){
|
||||||
|
let data = res.data;
|
||||||
|
|
||||||
|
// 初始化返回数据结构
|
||||||
|
const result = {
|
||||||
|
person: {
|
||||||
|
content: '',
|
||||||
|
imgs: []
|
||||||
|
},
|
||||||
|
animal: {
|
||||||
|
content: '',
|
||||||
|
imgs: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 按sortOrder排序
|
||||||
|
data.sort((a, b) => a.sortOrder - b.sortOrder);
|
||||||
|
// 处理person和general类型的数据
|
||||||
|
const personAndGeneral = data.filter(item => item.type === 'person' || item.type === 'general');
|
||||||
|
personAndGeneral.forEach(item => {
|
||||||
|
// 拼接content
|
||||||
|
result.person.content += item.content;
|
||||||
|
|
||||||
|
// 处理图片
|
||||||
|
if (item.imageUrls) {
|
||||||
|
try {
|
||||||
|
const images = JSON.parse(item.imageUrls);
|
||||||
|
if (Array.isArray(images)) {
|
||||||
|
result.person.imgs.push(...images);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析imageUrls失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 处理animal和general类型的数据
|
||||||
|
const animalAndGeneral = data.filter(item => item.type === 'animal' || item.type === 'general');
|
||||||
|
animalAndGeneral.forEach(item => {
|
||||||
|
// 拼接content
|
||||||
|
result.animal.content += item.content;
|
||||||
|
// 处理图片
|
||||||
|
if (item.imageUrls) {
|
||||||
|
try {
|
||||||
|
const images = JSON.parse(item.imageUrls);
|
||||||
|
if (Array.isArray(images)) {
|
||||||
|
result.animal.imgs.push(...images);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析imageUrls失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resolve(result);
|
||||||
|
}else{
|
||||||
|
reject(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import meshy from './meshy.js';
|
||||||
import logistics from './logistics.js';
|
import logistics from './logistics.js';
|
||||||
import user from './user.js';
|
import user from './user.js';
|
||||||
import permission from './permission.js';
|
import permission from './permission.js';
|
||||||
|
import promptManagement from './promptManagement.js';
|
||||||
export default {
|
export default {
|
||||||
...login,
|
...login,
|
||||||
...order,
|
...order,
|
||||||
|
|
@ -13,4 +14,5 @@ export default {
|
||||||
...logistics,
|
...logistics,
|
||||||
...user,
|
...user,
|
||||||
...permission,
|
...permission,
|
||||||
|
...promptManagement,
|
||||||
};
|
};
|
||||||
|
|
@ -2,5 +2,6 @@ const login = {
|
||||||
IMAGE_TO_3DADMIN:{url:'/api-core/admin/meshy/image-to-3d',method:'POST'},// 图片转3D模型任务提交
|
IMAGE_TO_3DADMIN:{url:'/api-core/admin/meshy/image-to-3d',method:'POST'},// 图片转3D模型任务提交
|
||||||
// FIND_TASK_IDADMIN:{url:'/api-core/front/mesh/image-to-3d/TASKID',method:'GET'},// 获取3D模型任务查询
|
// FIND_TASK_IDADMIN:{url:'/api-core/front/mesh/image-to-3d/TASKID',method:'GET'},// 获取3D模型任务查询
|
||||||
FIND_TASK_IDADMIN:{url:'/api-core/admin/meshy/ai-record/TASKID',method:'GET'},// 获取3D模型任务查询
|
FIND_TASK_IDADMIN:{url:'/api-core/admin/meshy/ai-record/TASKID',method:'GET'},// 获取3D模型任务查询
|
||||||
|
adminUPLOADS3:{url:'/api-core/admin/s3/get-presigned-post',method:'POST'},// 文件直传S3
|
||||||
}
|
}
|
||||||
export default login;
|
export default login;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
const promptManagement = {
|
||||||
|
getPromptDetail:{url:'/api-base/admin/prompt/{id}',method:'GET',isLoading:true},//查询提示词详情
|
||||||
|
getPromptList:{url:'/api-base/admin/prompt/list',method:'GET',isLoading:true},//分页查询提示词列表
|
||||||
|
updatePrompt:{url:'/api-base/admin/prompt/update/{id}',method:'POST',isLoading:true},//更新提示词
|
||||||
|
batchUpdateSort:{url:'/api-base/admin/prompt/sort',method:'POST',isLoading:true},//批量更新排序
|
||||||
|
deletePrompt:{url:'/api-base/admin/prompt/delete/{id}',method:'POST',isLoading:true},//删除提示词
|
||||||
|
deactivatePrompt:{url:'/api-base/admin/prompt/deactivate/{id}',method:'POST',isLoading:true},//取消激活提示词
|
||||||
|
createPrompt:{url:'/api-base/admin/prompt/create',method:'POST',isLoading:true},//创建提示词
|
||||||
|
activatePrompt:{url:'/api-base/admin/prompt/activate/{id}',method:'POST',isLoading:true},//激活提示词
|
||||||
|
}
|
||||||
|
export default promptManagement;
|
||||||
|
|
@ -3,5 +3,6 @@ const login = {
|
||||||
USER_STATISTICS:{url:'/api-core/front/user/statistics',method:'GET'},// 用户统计
|
USER_STATISTICS:{url:'/api-core/front/user/statistics',method:'GET'},// 用户统计
|
||||||
INVITE_CODES:{url:'/api-base/user/invite/codes',method:'GET'},// 返回当前用户的邀请码列表及使用状态
|
INVITE_CODES:{url:'/api-base/user/invite/codes',method:'GET'},// 返回当前用户的邀请码列表及使用状态
|
||||||
INVITE_RECORDS:{url:'/api-base/user/invite/records',method:'GET'},// 返回邀请的用户列表,包含奖励明细
|
INVITE_RECORDS:{url:'/api-base/user/invite/records',method:'GET'},// 返回邀请的用户列表,包含奖励明细
|
||||||
|
combined:{url:'/api-base/prompt/active',method:'GET'},// 返回动态提示词
|
||||||
}
|
}
|
||||||
export default login;
|
export default login;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,21 @@
|
||||||
import { request as requestUtils } from '../utils/request.js'
|
import { request as requestUtils } from '../utils/request.js'
|
||||||
import * as clientApi from '../api/frontend/index.js'
|
import * as clientApi from '../api/frontend/index.js'
|
||||||
|
import * as adminApi from '../api/FrontendDesigner/index.js'
|
||||||
let urlRule = 'https://api.deotaland.aiIMGURL'
|
let urlRule = 'https://api.deotaland.aiIMGURL'
|
||||||
|
// 获取环境变量中的
|
||||||
|
const getPorjectType = () => {
|
||||||
|
// 浏览器环境
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// Vite 环境变量
|
||||||
|
return import.meta.env.VITE_PROJECTTYPE;
|
||||||
|
}
|
||||||
|
// Node.js 环境
|
||||||
|
if (typeof process !== 'undefined') {
|
||||||
|
return process.env.VITE_PROJECTTYPE ;
|
||||||
|
}
|
||||||
|
};
|
||||||
export class FileServer {
|
export class FileServer {
|
||||||
|
RULE = getPorjectType();
|
||||||
//文件上传缓存映射 - 静态属性,所有实例共享
|
//文件上传缓存映射 - 静态属性,所有实例共享
|
||||||
static fileCacheMap = new Map();
|
static fileCacheMap = new Map();
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -328,7 +342,9 @@ export class FileServer {
|
||||||
file_type:file.type.split('/')[0],
|
file_type:file.type.split('/')[0],
|
||||||
prefix:'images'
|
prefix:'images'
|
||||||
}
|
}
|
||||||
const response = await requestUtils.common(clientApi.default.UPLOADS3, params);
|
// const requestUrl = this.RULE=='admin'?adminApi.default.adminUPLOADS3:clientApi.default.UPLOADS3;
|
||||||
|
const requestUrl = clientApi.default.UPLOADS3;
|
||||||
|
const response = await requestUtils.common(requestUrl, params);
|
||||||
if(response.code==0){
|
if(response.code==0){
|
||||||
let data = response.data;
|
let data = response.data;
|
||||||
let {url,fields,file_key,file_url } = data;
|
let {url,fields,file_key,file_url } = data;
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,11 @@ const service = axios.create({
|
||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
config => {
|
config => {
|
||||||
// 从localStorage中获取token(如果存在)
|
// 从localStorage中获取token(如果存在)
|
||||||
const token = localStorage.getItem('token');
|
// console.log(config,'请求配置');
|
||||||
|
let token = localStorage.getItem('token');
|
||||||
|
if(config.url=='/api-core/front/s3/get-presigned-post'){
|
||||||
|
token = '123'
|
||||||
|
}
|
||||||
if (token) {
|
if (token) {
|
||||||
// 将token添加到请求头
|
// 将token添加到请求头
|
||||||
config.headers['Authorization'] = `Bearer ${token}`;
|
config.headers['Authorization'] = `Bearer ${token}`;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue