feat: 完成角色卡,智能体配置,加载,历史记录,清空等功能
This commit is contained in:
@@ -32,6 +32,7 @@ export default {
|
||||
query: "/api/template/query",
|
||||
add: "/api/template/add",
|
||||
update: "/api/template/update",
|
||||
delete: "/api/template/delete",
|
||||
},
|
||||
message: {
|
||||
query: "/api/message/query",
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
<a-icon type="user" />
|
||||
<p>点击上传</p>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 悬浮提示层,整个区域都会显示 -->
|
||||
<div class="avatar-hover-mask">
|
||||
<a-icon :type="avatarLoading ? 'loading' : 'camera'" />
|
||||
@@ -117,24 +117,69 @@
|
||||
</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
|
||||
|
||||
<!-- 如果有头像,显示删除按钮 -->
|
||||
<a-button
|
||||
v-if="avatarUrl"
|
||||
type="danger"
|
||||
<a-button
|
||||
v-if="avatarUrl"
|
||||
type="danger"
|
||||
size="small"
|
||||
@click.stop="removeAvatar"
|
||||
class="avatar-remove-btn"
|
||||
>
|
||||
<a-icon type="delete" /> 移除头像
|
||||
</a-button>
|
||||
|
||||
|
||||
<div class="avatar-tip">
|
||||
支持JPG、PNG、GIF格式,不超过2MB
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xl="6" :lg="12" :xs="24">
|
||||
<a-form-item label="角色背景照片">
|
||||
<div class="background-uploader-wrapper">
|
||||
<!-- 整个区域都可点击的上传组件 -->
|
||||
<a-upload
|
||||
name="file"
|
||||
:show-upload-list="false"
|
||||
:before-upload="beforeBackgroundUpload"
|
||||
accept=".jpg,.jpeg,.png,.gif"
|
||||
class="background-uploader"
|
||||
>
|
||||
<div class="background-content">
|
||||
<!-- 有背景照片时显示背景照片 -->
|
||||
<img v-if="backgroundImageUrl" :src="getAvatarUrl(backgroundImageUrl)" alt="角色背景" class="background-image" />
|
||||
<!-- 无背景照片时显示上传图标 -->
|
||||
<div v-else class="background-placeholder">
|
||||
<a-icon type="picture" />
|
||||
<p>点击上传背景</p>
|
||||
</div>
|
||||
|
||||
<!-- 悬浮提示层,整个区域都会显示 -->
|
||||
<div class="background-hover-mask">
|
||||
<a-icon :type="backgroundLoading ? 'loading' : 'picture'" />
|
||||
<p>{{ backgroundImageUrl ? '更换背景' : '上传背景' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
|
||||
<!-- 如果有背景照片,显示删除按钮 -->
|
||||
<a-button
|
||||
v-if="backgroundImageUrl"
|
||||
type="danger"
|
||||
size="small"
|
||||
@click.stop="removeBackgroundImage"
|
||||
class="background-remove-btn"
|
||||
>
|
||||
<a-icon type="delete" /> 移除背景
|
||||
</a-button>
|
||||
|
||||
<div class="background-tip">
|
||||
支持JPG、PNG、GIF格式,不超过5MB
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xl="6" :lg="12" :xs="24">
|
||||
<a-form-item label="角色名称">
|
||||
<a-input v-decorator="[
|
||||
@@ -436,18 +481,19 @@
|
||||
style="margin-bottom: 16px" />
|
||||
|
||||
<template v-else>
|
||||
<!-- 仅允许选择模板(禁用自定义) -->
|
||||
<!-- 模板选择区域 -->
|
||||
<div style="margin-bottom: 16px; display: flex; justify-content: space-between; align-items: center">
|
||||
<a-space>
|
||||
<a-tag color="blue">仅支持从模板选择</a-tag>
|
||||
<a-tag color="blue">选择模板开始</a-tag>
|
||||
<a-select style="width: 240px" placeholder="请选择模板" v-model="selectedTemplateId"
|
||||
@change="handleTemplateChange" :loading="templatesLoading" :allowClear="false">
|
||||
@change="handleTemplateChange" :loading="templatesLoading" :allowClear="true">
|
||||
<a-select-option v-for="template in promptTemplates" :key="template.templateId"
|
||||
:value="template.templateId">
|
||||
{{ template.templateName }}
|
||||
<a-tag v-if="template.isDefault == 1" color="green" size="small">默认</a-tag>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<span style="color: #666; font-size: 12px">选择模板可快速填充,下方可自定义编辑</span>
|
||||
</a-space>
|
||||
|
||||
<!-- 模板管理按钮 -->
|
||||
@@ -462,9 +508,11 @@
|
||||
<a-textarea v-decorator="[
|
||||
'roleDesc',
|
||||
{
|
||||
rules: [],
|
||||
rules: [
|
||||
{ required: true, message: '请输入角色提示词' },
|
||||
],
|
||||
},
|
||||
]" :disabled="true" :rows="10" placeholder="请选择模板,提示词将随模板自动填充" />
|
||||
]" :rows="10" placeholder="请选择模板开始,或直接输入自定义提示词" />
|
||||
</a-form-item>
|
||||
<!-- 表单操作按钮 -->
|
||||
<a-form-item>
|
||||
@@ -649,6 +697,11 @@ export default {
|
||||
avatarUrl: '',
|
||||
avatarLoading: false,
|
||||
avatarFile: null,
|
||||
|
||||
// 背景图片相关
|
||||
backgroundImageUrl: '',
|
||||
backgroundLoading: false,
|
||||
backgroundImageFile: null,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1279,9 +1332,9 @@ export default {
|
||||
e.preventDefault();
|
||||
this.roleForm.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
// 校验必须选择模板
|
||||
if (!this.selectedTemplateId) {
|
||||
this.$message.warning('请选择模板');
|
||||
// 校验roleDesc不为空
|
||||
if (!values.roleDesc || !values.roleDesc.trim()) {
|
||||
this.$message.warning('请输入角色提示词');
|
||||
return;
|
||||
}
|
||||
this.submitLoading = true;
|
||||
@@ -1290,8 +1343,9 @@ export default {
|
||||
const formData = {
|
||||
...values,
|
||||
avatar: this.avatarUrl,
|
||||
// 记录模板ID
|
||||
templateId: this.selectedTemplateId,
|
||||
backgroundImage: this.backgroundImageUrl,
|
||||
// 记录模板ID(可选,用于统计和管理)
|
||||
templateId: this.selectedTemplateId || null,
|
||||
// 将开关的布尔值转换为数字(0或1)
|
||||
isDefault: values.isDefault ? 1 : 0
|
||||
};
|
||||
@@ -1342,6 +1396,8 @@ export default {
|
||||
this.editingRoleDesc = record.roleDesc;
|
||||
this.avatarUrl = record.avatar || ''; // 设置当前头像
|
||||
this.avatarFile = null; // 清空文件对象,因为是编辑现有头像
|
||||
this.backgroundImageUrl = record.backgroundImage || ''; // 设置当前背景图片
|
||||
this.backgroundImageFile = null; // 清空文件对象,因为是编辑现有背景图片
|
||||
// 切换到创建角色标签页
|
||||
this.activeTabKey = '2';
|
||||
|
||||
@@ -1411,12 +1467,16 @@ export default {
|
||||
ttsId: this.selectedTtsId,
|
||||
voiceName: record.voiceName
|
||||
});
|
||||
// 如果存在模板ID,填充模板内容
|
||||
|
||||
// 如果存在模板ID,检查是否应该填充模板内容
|
||||
// 只有当roleDesc与模板内容相同时,才用模板内容填充(保持向后兼容)
|
||||
if (this.selectedTemplateId && this.promptTemplates && this.promptTemplates.length > 0) {
|
||||
const t = this.promptTemplates.find(t => t.templateId === this.selectedTemplateId);
|
||||
if (t) {
|
||||
if (t && record.roleDesc === t.templateContent) {
|
||||
// 如果当前roleDesc与模板内容相同,说明是基于模板创建的,保持同步
|
||||
roleForm.setFieldsValue({ roleDesc: t.templateContent });
|
||||
}
|
||||
// 如果roleDesc已被修改过,保持用户的自定义内容不变
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
@@ -1484,6 +1544,8 @@ export default {
|
||||
this.audioUrl = '';
|
||||
this.avatarUrl = ''; // 重置头像
|
||||
this.avatarFile = null; // 清空文件对象
|
||||
this.backgroundImageUrl = ''; // 重置背景图片
|
||||
this.backgroundImageFile = null; // 清空文件对象
|
||||
|
||||
// 应用默认值
|
||||
this.applyDefaultValues();
|
||||
@@ -1578,16 +1640,12 @@ export default {
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// 如果有默认模板,应用默认模板
|
||||
// 如果有默认模板,选择默认模板,但不自动填充内容
|
||||
if (this.promptTemplates && this.promptTemplates.length > 0) {
|
||||
const defaultTemplate = this.promptTemplates.find(t => t.isDefault == 1) || this.promptTemplates[0];
|
||||
if (defaultTemplate) {
|
||||
this.selectedTemplateId = defaultTemplate.templateId;
|
||||
this.$nextTick(() => {
|
||||
this.roleForm.setFieldsValue({
|
||||
roleDesc: defaultTemplate.templateContent
|
||||
});
|
||||
});
|
||||
// 不自动填充内容,用户可主动选择使用模板
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1603,13 +1661,11 @@ export default {
|
||||
.then(res => {
|
||||
if (res.code === 200) {
|
||||
this.promptTemplates = res.data.list;
|
||||
// 自动选择默认模板或第一个
|
||||
// 自动选择默认模板或第一个,但不自动填充内容(让用户主动选择)
|
||||
const defaultTemplate = this.promptTemplates.find(t => t.isDefault == 1) || this.promptTemplates[0];
|
||||
if (!this.selectedTemplateId && defaultTemplate) {
|
||||
this.selectedTemplateId = defaultTemplate.templateId;
|
||||
this.roleForm.setFieldsValue({
|
||||
roleDesc: defaultTemplate.templateContent
|
||||
});
|
||||
// 不再自动填充内容,让用户主动选择是否使用模板
|
||||
}
|
||||
} else {
|
||||
this.showError(res.message);
|
||||
@@ -1759,15 +1815,40 @@ export default {
|
||||
handleTemplateChange(templateId) {
|
||||
const template = this.promptTemplates.find(t => t.templateId === templateId);
|
||||
if (template) {
|
||||
this.roleForm.setFieldsValue({
|
||||
roleDesc: template.templateContent
|
||||
});
|
||||
// 获取当前文本框的值
|
||||
const currentRoleDesc = this.roleForm.getFieldValue('roleDesc') || '';
|
||||
|
||||
// 如果文本框为空,或者内容是当前选中模板的内容(表示还未修改),直接填充
|
||||
const isEmpty = !currentRoleDesc.trim();
|
||||
const currentTemplate = this.selectedTemplateId && this.promptTemplates.find(t => t.templateId === this.selectedTemplateId);
|
||||
const isCurrentTemplate = currentTemplate && currentTemplate.templateContent === currentRoleDesc;
|
||||
|
||||
if (isEmpty || isCurrentTemplate) {
|
||||
// 直接填充模板内容
|
||||
this.roleForm.setFieldsValue({
|
||||
roleDesc: template.templateContent
|
||||
});
|
||||
} else {
|
||||
// 有自定义内容,询问用户是否要替换
|
||||
this.$confirm({
|
||||
title: '确认替换提示词',
|
||||
content: '您已经编辑了提示词内容,选择新模板将覆盖现有的编辑内容。确定要继续吗?',
|
||||
onOk: () => {
|
||||
this.roleForm.setFieldsValue({
|
||||
roleDesc: template.templateContent
|
||||
});
|
||||
},
|
||||
onCancel: () => {
|
||||
// 用户取消,保持原有内容,但更新selectedTemplateId
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 跳转到模板管理页面
|
||||
goToTemplateManager() {
|
||||
this.$router.push('/template');
|
||||
this.$router.push('/prompt-template');
|
||||
},
|
||||
|
||||
// 获取语音显示名称
|
||||
@@ -1900,6 +1981,83 @@ export default {
|
||||
this.avatarUrl = '';
|
||||
this.avatarFile = null;
|
||||
},
|
||||
|
||||
// 背景图片上传前检查
|
||||
beforeBackgroundUpload(file) {
|
||||
const isImage = file.type.startsWith('image/');
|
||||
const isLt5M = file.size / 1024 / 1024 < 5;
|
||||
|
||||
if (!isImage) {
|
||||
this.$message.error('只能上传图片文件!');
|
||||
return false;
|
||||
}
|
||||
if (!isLt5M) {
|
||||
this.$message.error('图片大小不能超过5MB!');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建预览URL
|
||||
this.backgroundImageFile = file;
|
||||
|
||||
// 立即上传图片
|
||||
this.uploadBackgroundFile(file)
|
||||
.then(url => {
|
||||
this.backgroundImageUrl = url;
|
||||
this.backgroundLoading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
this.$message.error('背景图片上传失败: ' + error);
|
||||
this.backgroundLoading = false;
|
||||
});
|
||||
|
||||
return false; // 阻止自动上传,我们会在提交表单时手动上传
|
||||
},
|
||||
|
||||
// 上传背景图片文件并获取URL
|
||||
uploadBackgroundFile(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 创建FormData对象
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('type', 'background'); // 指定上传类型为背景图片
|
||||
|
||||
// 使用XMLHttpRequest发送请求,确保正确设置content-type
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', api.upload, true);
|
||||
|
||||
// 设置请求完成回调
|
||||
xhr.onload = function () {
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
if (response.code === 200) {
|
||||
resolve(response.url);
|
||||
} else {
|
||||
reject(new Error(response.message || '上传失败'));
|
||||
}
|
||||
} catch (e) {
|
||||
reject(new Error('解析响应失败'));
|
||||
}
|
||||
} else {
|
||||
reject(new Error('上传失败,状态码: ' + xhr.status));
|
||||
}
|
||||
};
|
||||
|
||||
// 设置错误回调
|
||||
xhr.onerror = function () {
|
||||
reject(new Error('网络错误'));
|
||||
};
|
||||
|
||||
// 发送请求
|
||||
xhr.send(formData);
|
||||
});
|
||||
},
|
||||
|
||||
// 移除背景图片
|
||||
removeBackgroundImage() {
|
||||
this.backgroundImageUrl = '';
|
||||
this.backgroundImageFile = null;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -2030,4 +2188,100 @@ export default {
|
||||
color: #8c8c8c;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 背景图片上传样式 */
|
||||
.background-uploader-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 上传组件样式 */
|
||||
.background-uploader {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 上传内容区域 */
|
||||
.background-content {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
height: 120px;
|
||||
border-radius: 8px;
|
||||
background-color: #fafafa;
|
||||
border: 1px dashed #d9d9d9;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.background-content:hover {
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
/* 背景图片 */
|
||||
.background-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* 占位符 */
|
||||
.background-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.background-placeholder .anticon {
|
||||
font-size: 32px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.background-placeholder p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 悬浮遮罩 - 整个区域都显示 */
|
||||
.background-hover-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.background-content:hover .background-hover-mask {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.background-hover-mask .anticon {
|
||||
font-size: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.background-hover-mask p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 删除按钮 */
|
||||
.background-remove-btn {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* 提示文字 */
|
||||
.background-tip {
|
||||
margin-top: 8px;
|
||||
color: #8c8c8c;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user