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>
|
||||
</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>
|
||||
<template #title>{{ t('admin.layout.pointsManagement') }}</template>
|
||||
</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>
|
||||
<template #title>{{ t('admin.layout.commissionManagement') }}</template>
|
||||
</el-menu-item>
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
<template #title>{{ t('admin.layout.promptManagement') }}</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-sub-menu index="/admin/permission">
|
||||
<el-sub-menu index="/admin/permission" v-if="false">
|
||||
<template #title>
|
||||
<el-icon><Lock /></el-icon>
|
||||
<span>{{ t('admin.layout.permission') }}</span>
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@
|
|||
@click="handleClick"
|
||||
>
|
||||
<!-- 参考图片 -->
|
||||
<div v-if="prompt.referenceImage" class="card-image">
|
||||
<img :src="prompt.referenceImage" :alt="prompt.title" />
|
||||
<div v-if="prompt.imageUrls.length > 0" class="card-image">
|
||||
<img :src="prompt.imageUrls[0]" />
|
||||
</div>
|
||||
|
||||
<div v-else class="card-image"></div>
|
||||
<!-- 卡片内容 -->
|
||||
<div class="card-content">
|
||||
<h4 class="card-title">{{ prompt.title }}</h4>
|
||||
|
|
@ -33,6 +33,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片类型标签 -->
|
||||
<div class="type-badge">
|
||||
{{ getTypeLabel(prompt.type) }}
|
||||
</div>
|
||||
|
||||
<!-- 生效标识 -->
|
||||
<div v-if="isActive" class="active-badge">
|
||||
<el-icon><Check /></el-icon>
|
||||
|
|
@ -57,7 +62,7 @@ const props = defineProps({
|
|||
title: '',
|
||||
content: '',
|
||||
type: '',
|
||||
referenceImage: '',
|
||||
imageUrls: [],
|
||||
isActive: false
|
||||
})
|
||||
},
|
||||
|
|
@ -70,6 +75,16 @@ const props = defineProps({
|
|||
// 事件定义
|
||||
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) => {
|
||||
event.dataTransfer.effectAllowed = 'move'
|
||||
|
|
@ -178,11 +193,25 @@ const handleDelete = () => {
|
|||
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 {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
left: 8px;
|
||||
background: #10b981;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
@click="handleClick"
|
||||
>
|
||||
<!-- 左侧图片 -->
|
||||
<div v-if="prompt.referenceImage" class="card-image">
|
||||
<img :src="prompt.referenceImage" :alt="prompt.title" />
|
||||
<div v-if="prompt.imageUrls.length > 0" class="card-image">
|
||||
<img :src="prompt.imageUrls[0]" />
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容 -->
|
||||
|
|
@ -40,12 +40,6 @@
|
|||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 生效标识 -->
|
||||
<div class="active-badge">
|
||||
<el-icon><Check /></el-icon>
|
||||
{{ t('admin.promptManagement.active') }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -217,6 +217,9 @@ export default {
|
|||
captchaLength: 'Verification code should be 4-6 characters'
|
||||
},
|
||||
common: {
|
||||
detail: 'Detail',
|
||||
active: 'Active',
|
||||
inactive: 'Inactive',
|
||||
refresh: 'Refresh',
|
||||
search: 'Search',
|
||||
reset: 'Reset',
|
||||
|
|
|
|||
|
|
@ -15,7 +15,14 @@ export default {
|
|||
error: '出错了',
|
||||
success: '操作成功',
|
||||
warning: '警告',
|
||||
info: '提示'
|
||||
info: '提示',
|
||||
active: '活跃',
|
||||
inactive: '非活跃',
|
||||
detail: '详情',
|
||||
saveSuccess: '保存成功',
|
||||
saveFailed: '保存失败',
|
||||
deleteSuccess: '删除成功',
|
||||
deleteFailed: '删除失败'
|
||||
},
|
||||
orderManagement: {
|
||||
title: '订单',
|
||||
|
|
@ -228,6 +235,9 @@ orderManagement: {
|
|||
}
|
||||
},
|
||||
common: {
|
||||
detail: '详情',
|
||||
active: '活跃',
|
||||
inactive: '非活跃',
|
||||
refresh: '刷新',
|
||||
search: '搜索',
|
||||
reset: '重置',
|
||||
|
|
@ -331,18 +341,58 @@ orderManagement: {
|
|||
title: '用户列表管理',
|
||||
userList: '用户列表',
|
||||
addUser: '添加用户',
|
||||
editUser: '编辑用户',
|
||||
searchPlaceholder: '搜索用户名、昵称或邮箱',
|
||||
username: '用户名',
|
||||
fullName: '姓名',
|
||||
nickname: '昵称',
|
||||
email: '邮箱',
|
||||
phone: '电话',
|
||||
role: '角色',
|
||||
roles: '角色',
|
||||
status: '状态',
|
||||
createTime: '创建时间',
|
||||
lastLogin: '最后登录',
|
||||
admin: '管理员',
|
||||
user: '普通用户',
|
||||
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: {
|
||||
title: '内容管理',
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ const AdminRoleManagement = () => import('@/views/admin/AdminRoleManagement/Admi
|
|||
const AdminRoleDetail = () => import('@/views/admin/AdminRoleManagement/AdminRoleDetail.vue')
|
||||
const AdminPermissionManagement = () => import('@/views/admin/AdminPermissionManagement/AdminPermissionManagement.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 AdminCommissionManagement = () => import('@/views/admin/AdminCommissionManagement.vue')
|
||||
const AdminPromptManagement = () => import('@/views/admin/AdminPromptManagement.vue')
|
||||
const AdminPromptManagement = () => import('@/views/admin/AdminPromptManagement/AdminPromptManagement.vue')
|
||||
|
||||
const routes = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,25 +53,25 @@
|
|||
center
|
||||
>
|
||||
<el-form :model="permissionForm" label-width="120px" :rules="permissionRules" ref="permissionFormRef">
|
||||
<el-form-item label="权限名称" prop="permName">
|
||||
<el-input v-model="permissionForm.permName" placeholder="请输入权限名称" />
|
||||
<el-form-item :label="t('admin.permissionManagement.permissionName')" prop="permName">
|
||||
<el-input v-model="permissionForm.permName" :placeholder="t('admin.permissionManagement.permissionName')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="权限代码" prop="permCode">
|
||||
<el-input v-model="permissionForm.permCode" placeholder="请输入权限代码" />
|
||||
<el-form-item :label="t('admin.permissionManagement.permissionCode')" prop="permCode">
|
||||
<el-input v-model="permissionForm.permCode" :placeholder="t('admin.permissionManagement.permissionCode')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属模块" prop="module">
|
||||
<el-input v-model="permissionForm.module" placeholder="请输入所属模块" />
|
||||
<el-form-item :label="t('admin.permissionManagement.module')" prop="module">
|
||||
<el-input v-model="permissionForm.module" :placeholder="t('admin.permissionManagement.module')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="操作类型" prop="action">
|
||||
<el-input v-model="permissionForm.action" placeholder="请输入操作类型(如:view, add, edit, delete)" />
|
||||
<el-form-item :label="t('admin.permissionManagement.action')" prop="action">
|
||||
<el-input v-model="permissionForm.action" :placeholder="t('admin.permissionManagement.action')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="资源名称" prop="resource">
|
||||
<el-input v-model="permissionForm.resource" placeholder="请输入资源名称" />
|
||||
<el-form-item :label="t('admin.permissionManagement.resource')" prop="resource">
|
||||
<el-input v-model="permissionForm.resource" :placeholder="t('admin.permissionManagement.resource')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="权限描述" prop="description">
|
||||
<el-input type="textarea" v-model="permissionForm.description" placeholder="请输入权限描述" rows="3" />
|
||||
<el-form-item :label="t('admin.permissionManagement.description')" prop="description">
|
||||
<el-input type="textarea" v-model="permissionForm.description" :placeholder="t('admin.permissionManagement.description')" rows="3" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-form-item :label="t('admin.permissionManagement.isActive')">
|
||||
<el-switch v-model="permissionForm.isActive" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@
|
|||
<span class="count">{{ inactivePrompts.length }}</span>
|
||||
</div>
|
||||
<div class="prompt-list">
|
||||
<el-skeleton v-if="loading" :rows="6" animated />
|
||||
<PromptCard
|
||||
v-else
|
||||
v-for="prompt in inactivePrompts"
|
||||
:key="prompt.id"
|
||||
:prompt="prompt"
|
||||
|
|
@ -41,7 +43,9 @@
|
|||
@drop="handleDrop"
|
||||
@dragover="handleDragOver"
|
||||
>
|
||||
<el-skeleton v-if="loading" :rows="6" animated />
|
||||
<draggable
|
||||
v-else
|
||||
v-model="sortedActivePrompts"
|
||||
item-key="id"
|
||||
@start="handleDragStart"
|
||||
|
|
@ -102,6 +106,7 @@
|
|||
:auto-upload="false"
|
||||
:limit="1"
|
||||
:on-change="handleImageChange"
|
||||
:on-remove="handleImageRemove"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
<template #tip>
|
||||
|
|
@ -140,11 +145,12 @@
|
|||
<label>{{ t('admin.promptManagement.content') }}:</label>
|
||||
<p>{{ selectedPrompt.content }}</p>
|
||||
</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>
|
||||
<img :src="selectedPrompt.referenceImage" alt="参考图" class="reference-image" />
|
||||
<img :src="selectedPrompt.imageUrls[0]" alt="参考图" class="reference-image" />
|
||||
</div>
|
||||
</div>
|
||||
<el-skeleton v-else :rows="4" animated />
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
|
|
@ -156,15 +162,19 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import PromptCard from '@/components/admin/PromptCard.vue'
|
||||
import PromptCardHorizontal from '@/components/admin/PromptCardHorizontal.vue'
|
||||
import draggable from 'vuedraggable'
|
||||
import { AdminPromptManagement } from './index.js'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
// 创建API实例
|
||||
const promptApi = new AdminPromptManagement()
|
||||
|
||||
// 响应式数据
|
||||
const dialogVisible = ref(false)
|
||||
const detailVisible = ref(false)
|
||||
|
|
@ -173,197 +183,68 @@ const selectedPromptId = ref(null)
|
|||
const selectedPrompt = ref(null)
|
||||
const draggedPrompt = ref(null)
|
||||
const fileList = ref([])
|
||||
const prompts = ref([])
|
||||
const loading = ref(false)
|
||||
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
title: '',
|
||||
content: '',
|
||||
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(() => {
|
||||
return prompts.value.filter(prompt => !prompt.isActive)
|
||||
return prompts.value.filter(prompt => prompt.isActive === 0)
|
||||
})
|
||||
|
||||
// 计算属性:生效提示词(未排序)
|
||||
const activePrompts = computed(() => {
|
||||
return prompts.value.filter(prompt => prompt.isActive)
|
||||
return prompts.value.filter(prompt => prompt.isActive === 1)
|
||||
})
|
||||
|
||||
// 计算属性:排序后的生效提示词(用于拖拽排序)
|
||||
const sortedActivePrompts = computed({
|
||||
get: () => {
|
||||
return prompts.value
|
||||
.filter(prompt => prompt.isActive)
|
||||
.sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0))
|
||||
.filter(prompt => prompt.isActive === 1)
|
||||
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
|
||||
},
|
||||
set: (newValue) => {
|
||||
// 当拖拽排序发生变化时,更新所有生效提示词的sortIndex
|
||||
// 当拖拽排序发生变化时,更新所有生效提示词的sortOrder
|
||||
newValue.forEach((prompt, index) => {
|
||||
const originalPrompt = prompts.value.find(p => p.id === prompt.id)
|
||||
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 = () => {
|
||||
isEditing.value = false
|
||||
|
|
@ -372,7 +253,7 @@ const showAddDialog = () => {
|
|||
title: '',
|
||||
content: '',
|
||||
type: '',
|
||||
referenceImage: ''
|
||||
imageUrls: []
|
||||
}
|
||||
fileList.value = []
|
||||
dialogVisible.value = true
|
||||
|
|
@ -383,70 +264,88 @@ const showEditDialog = (prompt) => {
|
|||
isEditing.value = true
|
||||
selectedPromptId.value = prompt.id
|
||||
formData.value = {
|
||||
id: prompt.id,
|
||||
title: prompt.title,
|
||||
content: prompt.content,
|
||||
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
|
||||
}
|
||||
|
||||
// 保存提示词
|
||||
const savePrompt = () => {
|
||||
const savePrompt = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
if (isEditing.value) {
|
||||
// 编辑现有提示词
|
||||
const index = prompts.value.findIndex(p => p.id === selectedPromptId.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
|
||||
}
|
||||
}
|
||||
await promptApi.updatePrompt(formData.value)
|
||||
} else {
|
||||
// 添加新提示词
|
||||
const newPrompt = {
|
||||
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)
|
||||
await promptApi.createPrompt(formData.value)
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 重新获取数据
|
||||
await fetchPrompts()
|
||||
} catch (error) {
|
||||
console.error('保存提示词失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除提示词
|
||||
const deletePrompt = (promptId) => {
|
||||
prompts.value = prompts.value.filter(p => p.id !== promptId)
|
||||
const deletePrompt = async (promptId) => {
|
||||
try {
|
||||
loading.value = true
|
||||
await promptApi.deletePrompt(promptId)
|
||||
// 重新获取数据
|
||||
await fetchPrompts()
|
||||
} catch (error) {
|
||||
console.error('删除提示词失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 从生效区域移除提示词
|
||||
const removeFromActive = (promptId) => {
|
||||
const prompt = prompts.value.find(p => p.id === promptId)
|
||||
if (prompt) {
|
||||
prompt.isActive = false
|
||||
const removeFromActive = async (promptId) => {
|
||||
try {
|
||||
loading.value = true
|
||||
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) {
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
formData.value.referenceImage = e.target.result
|
||||
try {
|
||||
loading.value = true
|
||||
// 调用上传图片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) => {
|
||||
draggedPrompt.value = prompt
|
||||
|
|
@ -458,20 +357,43 @@ const handleDragStart = (evt) => {
|
|||
}
|
||||
|
||||
// 处理排序更新
|
||||
const handleSortUpdate = (evt) => {
|
||||
// 排序已经通过sortedActivePrompts的setter自动处理
|
||||
const handleSortUpdate = async (evt) => {
|
||||
// 准备批量更新排序的数据
|
||||
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()
|
||||
if (draggedPrompt.value && !draggedPrompt.value.isActive) {
|
||||
// 设置为生效状态
|
||||
draggedPrompt.value.isActive = true
|
||||
// 设置新的sortIndex为当前激活列表长度
|
||||
draggedPrompt.value.sortIndex = sortedActivePrompts.value.length
|
||||
if (draggedPrompt.value && draggedPrompt.value.isActive === 0) {
|
||||
try {
|
||||
loading.value = true
|
||||
// 调用激活API
|
||||
await promptApi.activatePrompt(draggedPrompt.value.id)
|
||||
// 重新获取数据
|
||||
await fetchPrompts()
|
||||
} catch (error) {
|
||||
console.error('激活提示词失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
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
|
||||
>
|
||||
<el-form :model="roleForm" label-width="120px" :rules="roleRules" ref="roleFormRef">
|
||||
<el-form-item label="角色名称" prop="roleName">
|
||||
<el-input v-model="roleForm.roleName" placeholder="请输入角色名称" />
|
||||
<el-form-item :label="t('admin.roleManagement.roleName')" prop="roleName">
|
||||
<el-input v-model="roleForm.roleName" :placeholder="t('admin.roleManagement.roleName')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色代码" prop="roleCode">
|
||||
<el-input v-model="roleForm.roleCode" placeholder="请输入角色代码" />
|
||||
<el-form-item :label="t('admin.roleManagement.roleCode')" prop="roleCode">
|
||||
<el-input v-model="roleForm.roleCode" :placeholder="t('admin.roleManagement.roleCode')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色描述" prop="description">
|
||||
<el-input type="textarea" v-model="roleForm.description" placeholder="请输入角色描述" rows="3" />
|
||||
<el-form-item :label="t('admin.roleManagement.description')" prop="description">
|
||||
<el-input type="textarea" v-model="roleForm.description" :placeholder="t('admin.roleManagement.description')" rows="3" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否系统角色">
|
||||
<el-form-item :label="t('admin.roleManagement.isSystem')">
|
||||
<el-switch v-model="roleForm.isSystem" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-form-item :label="t('admin.roleManagement.isActive')">
|
||||
<el-switch v-model="roleForm.isActive" />
|
||||
</el-form-item>
|
||||
</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);
|
||||
}
|
||||
//创建管理员用户
|
||||
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({
|
||||
combinedPromptJson:{//动态提示词
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
// 图片URL
|
||||
imageUrl: {
|
||||
type: String,
|
||||
|
|
@ -218,69 +222,27 @@ const handleGenerateImage = async () => {
|
|||
if (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){
|
||||
props.cardData.imgyt&&referenceImages.push(props.cardData.imgyt);
|
||||
referenceImages.push(props.cardData.diyPromptImg);
|
||||
referenceImages.push(cjimg);
|
||||
}
|
||||
// if(props.cardData.diyPromptText){
|
||||
// console.log(props.cardData.diyPromptImg,'diyPromptImgdiyPromptImgdiyPromptImg');
|
||||
// referenceImages.push(props.cardData.diyPromptImg);
|
||||
// if(iscjt){
|
||||
// props.cardData.imgyt&&referenceImages.push(props.cardData.imgyt);
|
||||
// referenceImages.push(cjimg);
|
||||
// }
|
||||
// }else{
|
||||
// referenceImages.push(humanTypeImg);
|
||||
// referenceImages.push(anTypeImg);
|
||||
// }
|
||||
// referenceImages.push(cz2);
|
||||
// referenceImages.push(humanTypeImg);
|
||||
// if(props?.cardData?.selectedExpression){
|
||||
// referenceImages.push(props.cardData.selectedExpression.imageUrl);
|
||||
// }
|
||||
// 忽略第二张参考图
|
||||
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。
|
||||
`
|
||||
;
|
||||
if(props.cardData.diyPromptText){
|
||||
referenceImages.push(props.cardData.diyPromptImg);
|
||||
}
|
||||
let dtprompt;
|
||||
if(props?.cardData?.ipType==1){
|
||||
dtprompt = props.combinedPromptJson.person.content;
|
||||
referenceImages.push(...props.combinedPromptJson.person.imgs);
|
||||
}else if(props?.cardData?.ipType==2){
|
||||
dtprompt = props.combinedPromptJson.animal.content;
|
||||
referenceImages.push(...props.combinedPromptJson.animal.imgs);
|
||||
}
|
||||
if(props.cardData.prompt){
|
||||
dtprompt = `角色外观:${props.cardData.prompt}.${dtprompt}`
|
||||
}
|
||||
let prompt = props.cardData.diyPromptText|| dtprompt
|
||||
// 角色姿势:${props.cardData.ipType==1?``:``}
|
||||
|
||||
if(props.cardData.prompt&&props.cardData.prompt.indexOf('nospec')!=-1){
|
||||
prompt = '按原图生成'
|
||||
referenceImages = [props.cardData.inspirationImage];
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ const formData = ref({
|
|||
|
||||
const textareaRef = ref(null);
|
||||
const fileInput = ref(null); // 文件输入引用
|
||||
const generateCount = ref(4); // 生成数量,默认为1
|
||||
const generateCount = ref(1); // 生成数量,默认为1
|
||||
const isOptimizing = ref(false); // 优化状态
|
||||
const isDragOver = ref(false); // 拖拽状态
|
||||
const isUploading = ref(false); // 图片上传状态
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
|
||||
<!-- 根据卡片类型显示不同组件 -->
|
||||
<IPCard
|
||||
:combinedPromptJson="combinedPromptJson"
|
||||
@preview-image="handlePreviewImage"
|
||||
@generate-smooth-white-model="(imageUrl)=>handleGenerateSmoothWhiteModel(index,imageUrl)"
|
||||
@create-new-card="(data)=>handleCreateFourViewCard(index,data)"
|
||||
|
|
@ -73,7 +74,7 @@
|
|||
@generate-model-requested="(data)=>handleGenerateModelRequested(index,data)"
|
||||
@save-project="(item)=>{handleSaveProject(index,item,'image')}"
|
||||
@create-remaining-image-data="handleCreateRemainingImageData"
|
||||
v-if="card.type === 'image'"
|
||||
v-if="card.type === 'image'&&combinedPromptJson.animal"
|
||||
:cardData="card"
|
||||
/>
|
||||
<ModelCard
|
||||
|
|
@ -178,6 +179,16 @@ const getGenerateCount = async ()=>{
|
|||
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], () => {
|
||||
let newProjectInfo = {...projectInfo.value};
|
||||
newProjectInfo.details.node_card = cards.value;
|
||||
|
|
@ -960,6 +971,7 @@ onMounted(() => {
|
|||
// 每次进入都显示引导弹窗
|
||||
// showGuideModal.value = true;
|
||||
init();
|
||||
getCombinedPrompt();
|
||||
// 使用优化的被动事件监听器
|
||||
const removeWheelListener = addPassiveEventListener(document, 'wheel', preventZoom);
|
||||
const removeTouchStartListener = addPassiveEventListener(document, 'touchstart', preventPinchZoom);
|
||||
|
|
|
|||
|
|
@ -728,4 +728,69 @@ export class Project{
|
|||
this.duration_seconds +=1;
|
||||
}, 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 user from './user.js';
|
||||
import permission from './permission.js';
|
||||
import promptManagement from './promptManagement.js';
|
||||
export default {
|
||||
...login,
|
||||
...order,
|
||||
|
|
@ -13,4 +14,5 @@ export default {
|
|||
...logistics,
|
||||
...user,
|
||||
...permission,
|
||||
...promptManagement,
|
||||
};
|
||||
|
|
@ -2,5 +2,6 @@ const login = {
|
|||
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/admin/meshy/ai-record/TASKID',method:'GET'},// 获取3D模型任务查询
|
||||
adminUPLOADS3:{url:'/api-core/admin/s3/get-presigned-post',method:'POST'},// 文件直传S3
|
||||
}
|
||||
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'},// 用户统计
|
||||
INVITE_CODES:{url:'/api-base/user/invite/codes',method:'GET'},// 返回当前用户的邀请码列表及使用状态
|
||||
INVITE_RECORDS:{url:'/api-base/user/invite/records',method:'GET'},// 返回邀请的用户列表,包含奖励明细
|
||||
combined:{url:'/api-base/prompt/active',method:'GET'},// 返回动态提示词
|
||||
}
|
||||
export default login;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,21 @@
|
|||
import { request as requestUtils } from '../utils/request.js'
|
||||
import * as clientApi from '../api/frontend/index.js'
|
||||
import * as adminApi from '../api/FrontendDesigner/index.js'
|
||||
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 {
|
||||
RULE = getPorjectType();
|
||||
//文件上传缓存映射 - 静态属性,所有实例共享
|
||||
static fileCacheMap = new Map();
|
||||
constructor() {
|
||||
|
|
@ -328,7 +342,9 @@ export class FileServer {
|
|||
file_type:file.type.split('/')[0],
|
||||
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){
|
||||
let data = response.data;
|
||||
let {url,fields,file_key,file_url } = data;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,11 @@ const service = axios.create({
|
|||
service.interceptors.request.use(
|
||||
config => {
|
||||
// 从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) {
|
||||
// 将token添加到请求头
|
||||
config.headers['Authorization'] = `Bearer ${token}`;
|
||||
|
|
|
|||
Loading…
Reference in New Issue