【增加】AI 角色定制

This commit is contained in:
cherishsince 2024-05-15 23:11:40 +08:00
parent c06bc71020
commit b2c15ab2cb
4 changed files with 194 additions and 48 deletions

View File

@ -10,6 +10,7 @@ export interface ChatRoleVO {
sort: number // 角色排序
description: string // 角色描述
systemMessage: string // 角色设定
welcomeMessage: string // 角色设定
publicStatus: boolean // 是否公开
status: number // 状态
}
@ -50,6 +51,8 @@ export const ChatRoleApi = {
return await request.delete({ url: `/ai/chat-role/delete?id=` + id })
},
// ======= chat 聊天
// 获取 my role
getMyPage: async (params: ChatRolePageReqVO) => {
return await request.get({ url: `/ai/chat-role/my-page`, params})
@ -58,5 +61,20 @@ export const ChatRoleApi = {
// 获取角色分类
getCategoryList: async () => {
return await request.get({ url: `/ai/chat-role/category-list`})
}
},
// 创建角色
createMy: async (data: ChatRoleVO) => {
return await request.post({ url: `/ai/chat-role/create-my`, data})
},
// 更新角色
updateMy: async (data: ChatRoleVO) => {
return await request.put({ url: `/ai/chat-role/update-my`, data})
},
// 删除角色 my
deleteMy: async (id: number) => {
return await request.delete({ url: `/ai/chat-role/delete-my?id=` + id })
},
}

View File

@ -1,6 +1,28 @@
<template>
<div class="card-list">
<el-card class="card" body-class="card-body" v-for="role in roleList" :key="role.id">
<!-- 更多 -->
<div class="more-container">
<el-dropdown @command="handleMoreClick">
<span class="el-dropdown-link">
<el-button type="text" >
<el-icon><More /></el-icon>
</el-button>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="['edit', role]" >
<el-icon><EditPen /></el-icon>
</el-dropdown-item>
<el-dropdown-item :command="['delete', role]" style="color: red;" >
<el-icon><Delete /></el-icon>
<span>删除</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<!-- 头像 -->
<div>
<img class="avatar" :src="role.avatar"/>
</div>
@ -8,6 +30,7 @@
<div class="content-container">
<div class="title">{{ role.name }}</div>
<div class="description">{{ role.description }}</div>
</div>
<div class="btn-container">
<el-button type="primary" size="small">使用</el-button>
@ -20,6 +43,7 @@
<script setup lang="ts">
import {ChatRoleVO} from '@/api/ai/model/chatRole'
import {PropType} from "vue";
import {Delete, EditPen, More} from "@element-plus/icons-vue";
//
const props = defineProps({
@ -28,6 +52,18 @@ const props = defineProps({
required: true
}
})
//
const emits = defineEmits(['onDelete', 'onEdit'])
// more
const handleMoreClick = async (data) => {
const type = data[0]
const role = data[1]
if (type === 'delete') {
emits('onDelete', role)
} else {
emits('onEdit', role)
}
}
onMounted(() => {
console.log('props', props.roleList)
@ -38,13 +74,14 @@ onMounted(() => {
<style lang="scss">
// card body
.card-body {
width: auto;
max-width: 300px;
width: 300px;
padding: 15px;
display: flex;
flex-direction: row;
justify-content: flex-start;
position: relative;
}
</style>
@ -55,21 +92,36 @@ onMounted(() => {
display: flex;
flex-direction: row;
flex-wrap: wrap;
position: relative;
.card {
margin-right: 20px;
border-radius: 10px;
margin-bottom: 30px;
position: relative;
.more-container {
position: absolute;
right: 12px;
top: 0px;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 10px;
overflow: hidden;
}
.right-container {
margin-left: 10px;
width: 100%;
//height: 100px;
.content-container {
height: 85px;
overflow: hidden;
.title {
font-size: 18px;
font-weight: bold;

View File

@ -1,30 +1,38 @@
<!-- chat 角色仓库 -->
<template>
<el-container class="role-container">
<ChatRoleForm ref="formRef" @success="handlerAddRoleSuccess" />
<Header title="角色仓库"/>
<el-main class="role-main">
<!-- 搜索按钮 -->
<el-input
v-model="search"
class="search-input"
size="large"
placeholder="请输入搜索的内容"
:suffix-icon="Search"
@change="getActiveTabsRole"
/>
<div class="search-container" @click="handlerAddRole">
<!-- 搜索按钮 -->
<el-input
v-model="search"
class="search-input"
size="default"
placeholder="请输入搜索的内容"
:suffix-icon="Search"
@change="getActiveTabsRole"
/>
<el-button type="primary" style="margin-left: 20px;">
<el-icon><User /></el-icon>
添加角色
</el-button>
</div>
<!-- tabs -->
<el-tabs v-model="activeRole" class="tabs" @tab-click="handleTabsClick">
<el-tab-pane class="role-pane" label="我的角色" name="my-role">
<RoleCategoryList :category-list="categoryList" :active="activeCategory" @onCategoryClick="handlerCategoryClick" />
<RoleList :role-list="myRoleList" style="margin-top: 20px;" />
<RoleList :role-list="myRoleList" @onDelete="handlerCardDelete" @onEdit="handlerCardEdit" style="margin-top: 20px;" />
</el-tab-pane>
<el-tab-pane label="公共角色" name="public-role">
<RoleCategoryList :category-list="categoryList" :active="activeCategory" @onCategoryClick="handlerCategoryClick" />
<RoleList :role-list="publicRoleList" style="margin-top: 20px;" />
<RoleList :role-list="publicRoleList" @onDelete="handlerCardDelete" @onEdit="handlerCardEdit" style="margin-top: 20px;" />
</el-tab-pane>
</el-tabs>
</el-main>
</el-container>
</template>
<!-- setup -->
@ -32,10 +40,11 @@
import {ref} from "vue";
import Header from '@/views/ai/chat/components/Header.vue'
import RoleList from './RoleList.vue'
import ChatRoleForm from '@/views/ai/model/chatRole/ChatRoleForm.vue'
import RoleCategoryList from './RoleCategoryList.vue'
import {ChatRoleApi, ChatRolePageReqVO, ChatRoleVO} from '@/api/ai/model/chatRole'
import {TabsPaneContext} from "element-plus";
import {Search} from "@element-plus/icons-vue";
import {Search, User} from "@element-plus/icons-vue";
//
const activeRole = ref<string>('my-role') //
@ -47,9 +56,10 @@ const myRoleList = ref<ChatRoleVO[]>([]) // my 分页大小
const publicPageNo = ref<number>(1) // public
const publicPageSize = ref<number>(50) // public
const publicRoleList = ref<ChatRoleVO[]>([]) // public
const activeCategory = ref<string>('writing') //
const activeCategory = ref<string>('') //
const categoryList = ref<string[]>([]) //
/** 添加/修改操作 */
const formRef = ref()
// tabs
const handleTabsClick = async (tab: TabsPaneContext) => {
//
@ -109,6 +119,31 @@ const handlerCategoryClick = async (category: string) => {
await getActiveTabsRole()
}
//
const handlerAddRole = async () => {
formRef.value.open('my-create', null, '添加角色')
}
// card
const handlerCardDelete = async (role) => {
await ChatRoleApi.deleteMy(role.id)
//
await getActiveTabsRole()
}
// card
const handlerCardEdit = async (role) => {
formRef.value.open('my-update', role.id, '编辑角色')
}
//
const handlerAddRoleSuccess = async (e) => {
console.log(e)
//
await getActiveTabsRole()
}
//
onMounted( async () => {
//
@ -139,14 +174,17 @@ onMounted( async () => {
.role-main {
position: relative;
.search-input {
width: 240px;
.search-container {
position: absolute;
right: 20px;
top: 10px;
z-index: 100;
}
.search-input {
width: 240px;
}
.tabs {
position: relative;
}

View File

@ -8,12 +8,12 @@
v-loading="formLoading"
>
<el-form-item label="角色名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入角色名称" />
<el-input v-model="formData.name" placeholder="请输入角色名称"/>
</el-form-item>
<el-form-item label="角色头像" prop="avatar">
<UploadImg v-model="formData.avatar" height="60px" width="60px" />
<UploadImg v-model="formData.avatar" height="60px" width="60px"/>
</el-form-item>
<el-form-item label="绑定模型" prop="modelId">
<el-form-item label="绑定模型" prop="modelId" v-if="!isUser(formType)">
<el-select v-model="formData.modelId" placeholder="请选择模型" clearable>
<el-option
v-for="chatModel in chatModelList"
@ -23,16 +23,19 @@
/>
</el-select>
</el-form-item>
<el-form-item label="角色类别" prop="category">
<el-input v-model="formData.category" placeholder="请输入角色类别" />
<el-form-item label="角色类别" prop="category" v-if="!isUser(formType)">
<el-input v-model="formData.category" placeholder="请输入角色类别"/>
</el-form-item>
<el-form-item label="角色描述" prop="description">
<el-input type="textarea" v-model="formData.description" placeholder="请输入角色描述" />
<el-input type="textarea" v-model="formData.description" placeholder="请输入角色描述"/>
</el-form-item>
<el-form-item label="角色设定" prop="systemMessage">
<el-input type="textarea" v-model="formData.systemMessage" placeholder="请输入角色设定" />
</el-form-item>
<el-form-item label="是否公开" prop="publicStatus">
<el-form-item label="欢迎语👏🏻" prop="welcomeMessage" v-if="isUser(formType)">
<el-input type="textarea" v-model="formData.welcomeMessage" placeholder="请输入欢迎语"/>
</el-form-item>
<el-form-item label="是否公开" prop="publicStatus" v-if="!isUser(formType)">
<el-radio-group v-model="formData.publicStatus">
<el-radio
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
@ -43,10 +46,10 @@
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="角色排序" prop="sort">
<el-input-number v-model="formData.sort" placeholder="请输入角色排序" class="!w-1/1" />
<el-form-item label="角色排序" prop="sort" v-if="!isUser(formType)">
<el-input-number v-model="formData.sort" placeholder="请输入角色排序" class="!w-1/1"/>
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-form-item label="开启状态" prop="status" v-if="!isUser(formType)">
<el-radio-group v-model="formData.status">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
@ -65,15 +68,15 @@
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { ChatRoleApi, ChatRoleVO } from '@/api/ai/model/chatRole'
import { CommonStatusEnum } from '@/utils/constants'
import { ChatModelApi, ChatModelVO } from '@/api/ai/model/chatModel'
import {getIntDictOptions, getBoolDictOptions, DICT_TYPE} from '@/utils/dict'
import {ChatRoleApi, ChatRoleVO} from '@/api/ai/model/chatRole'
import {CommonStatusEnum} from '@/utils/constants'
import {ChatModelApi, ChatModelVO} from '@/api/ai/model/chatModel'
/** AI 聊天角色 表单 */
defineOptions({ name: 'ChatRoleForm' })
defineOptions({name: 'ChatRoleForm'})
const { t } = useI18n() //
const {t} = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
@ -89,26 +92,49 @@ const formData = ref({
sort: undefined,
description: undefined,
systemMessage: undefined,
welcomeMessage: undefined,
publicStatus: true,
status: CommonStatusEnum.ENABLE
})
const formRules = reactive({
name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
avatar: [{ required: true, message: '角色头像不能为空', trigger: 'blur' }],
category: [{ required: true, message: '角色类别不能为空', trigger: 'blur' }],
sort: [{ required: true, message: '角色排序不能为空', trigger: 'blur' }],
description: [{ required: true, message: '角色描述不能为空', trigger: 'blur' }],
systemMessage: [{ required: true, message: '角色设定不能为空', trigger: 'blur' }],
publicStatus: [{ required: true, message: '是否公开不能为空', trigger: 'blur' }]
})
//
const isUser = (type: string) => {
return (type === 'my-create' || type === 'my-update')
}
const formRules = ref() // reactive(formRulesObj)
const formRef = ref() // Ref
const chatModelList = ref([] as ChatModelVO[]) //
const getFormRules = async (type: string) => {
let formRulesObj = {
name: [{required: true, message: '角色名称不能为空', trigger: 'blur'}],
avatar: [{required: true, message: '角色头像不能为空', trigger: 'blur'}],
category: [{required: true, message: '角色类别不能为空', trigger: 'blur'}],
sort: [{required: true, message: '角色排序不能为空', trigger: 'blur'}],
description: [{required: true, message: '角色描述不能为空', trigger: 'blur'}],
systemMessage: [{required: true, message: '角色设定不能为空', trigger: 'blur'}],
// welcomeMessage: [{ required: true, message: '', trigger: 'blur' }],
publicStatus: [{required: true, message: '是否公开不能为空', trigger: 'blur'}]
}
if (isUser(type)) {
formRulesObj['welcomeMessage'] = [{
required: true,
message: '欢迎语不能为空',
trigger: 'blur'
}]
}
formRules.value = reactive(formRulesObj)
}
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
const open = async (type: string, id?: number, title?: string) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
dialogTitle.value = title || t('action.' + type)
formType.value = type
getFormRules(type)
resetForm()
//
if (id) {
@ -122,7 +148,7 @@ const open = async (type: string, id?: number) => {
//
chatModelList.value = await ChatModelApi.getChatModelSimpleList(CommonStatusEnum.ENABLE)
}
defineExpose({ open }) // open
defineExpose({open}) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
@ -133,7 +159,17 @@ const submitForm = async () => {
formLoading.value = true
try {
const data = formData.value as unknown as ChatRoleVO
if (formType.value === 'create') {
// tip: my-createmy-update chat
// tip: createelse
if (formType.value === 'my-create') {
await ChatRoleApi.createMy(data)
message.success(t('common.createSuccess'))
} else if (formType.value === 'my-update') {
await ChatRoleApi.updateMy(data)
message.success(t('common.updateSuccess'))
} else if (formType.value === 'create') {
await ChatRoleApi.createChatRole(data)
message.success(t('common.createSuccess'))
} else {
@ -148,6 +184,7 @@ const submitForm = async () => {
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
@ -159,6 +196,7 @@ const resetForm = () => {
sort: undefined,
description: undefined,
systemMessage: undefined,
welcomeMessage: undefined,
publicStatus: true,
status: CommonStatusEnum.ENABLE
}