feat: 对话中消息编辑与重新生成与删除功能

This commit is contained in:
Gsh
2026-01-31 17:39:23 +08:00
parent 4441244575
commit ec382995b4
9 changed files with 943 additions and 102 deletions

View File

@@ -1,5 +1,19 @@
import type { ChatMessageVo, GetChatListParams, SendDTO } from './types';
import { get, post } from '@/utils/request';
import { del, get, post } from '@/utils/request';
// 删除消息接口
export interface DeleteMessageParams {
ids: (number | string)[];
isDeleteSubsequent?: boolean;
}
export function deleteMessages(data: DeleteMessageParams) {
const idsQuery = data.ids.map(id => `ids=${encodeURIComponent(id)}`).join('&');
const subsequentQuery = data.isDeleteSubsequent !== undefined ? `isDeleteSubsequent=${data.isDeleteSubsequent}` : '';
const query = [idsQuery, subsequentQuery].filter(Boolean).join('&');
const url = `/message${query ? `?${query}` : ''}`;
return del<void>(url).json();
}
// 发送消息(旧接口)
export function send(data: SendDTO) {

View File

@@ -125,7 +125,7 @@ export interface GetChatListParams {
/**
* 主键
*/
id?: number;
id?: number | string;
/**
* 排序的方向desc或者asc
*/
@@ -195,7 +195,7 @@ export interface ChatMessageVo {
/**
* 主键
*/
id?: number;
id?: number | string;
/**
* 模型名称
*/

View File

@@ -6,8 +6,8 @@ import { createOrder, getOrderStatus } from '@/api';
import { getGoodsList, GoodsCategoryType } from '@/api/pay';
import ProductPage from '@/pages/products/index.vue';
import { useUserStore } from '@/stores';
import NewbieGuide from './NewbieGuide.vue';
import ActivationGuide from './ActivationGuide.vue';
import NewbieGuide from './NewbieGuide.vue';
import PackageTab from './PackageTab.vue';
const emit = defineEmits(['close']);
@@ -171,7 +171,7 @@ const benefitsData2 = {
qy: [
{ name: '需先成为意心会员后方可购买使用', value: '' },
{ name: '意心会员过期后尊享Token包会临时冻结', value: '' },
{ name: '可重复购买将自动累积Token在个人中心查看', value: '' },
{ name: '尊享Token = 实际消耗Token * 当前模型倍率,模型倍率可前往【模型库】查看', value: '' },
{ name: 'Token长期有效无限流限制', value: '' },
{ name: '几乎是全网最低价让人人用的起Agent', value: '' },
{ name: '附带claude code独家教程手把手对接', value: '' },

View File

@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup xmlns="http://www.w3.org/1999/html">
/**
* 翻牌抽奖活动组件
* 功能说明:
@@ -914,7 +914,13 @@ function getCardClass(record: CardFlipRecord): string[] {
</div>
</div>
<div class="lucky-label">
翻牌幸运值
<div class="lucky-main-text">
<span class="fire-icon">🔥</span>幸运值{{ luckyValue }}%
</div>
<div class="lucky-sub-text">
<span v-if="luckyValue < 100" class="lucky-highlight bounce">继续翻后面奖励超高</span>
<span v-else class="lucky-highlight full">幸运值满奖励MAX</span>
</div>
</div>
</div>
@@ -1283,20 +1289,23 @@ function getCardClass(record: CardFlipRecord): string[] {
/* 幸运值悬浮球 */
.lucky-float-ball {
position: fixed;
position: absolute;
left: 50%;
bottom: 16px;
transform: translateX(-50%);
z-index: 999;
bottom: 20px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
&:hover {
transform: translateX(-50%) scale(1.1);
transform: translateX(-50%) scale(1.05);
}
@media (max-width: 768px) {
bottom: 15px;
bottom: 12px;
.lucky-circle {
width: 70px;
@@ -1312,16 +1321,14 @@ function getCardClass(record: CardFlipRecord): string[] {
}
.lucky-label {
font-size: 11px;
margin-top: 4px;
}
}
&.lucky-full {
animation: lucky-celebration 2s ease-in-out infinite;
.lucky-circle {
box-shadow: 0 0 20px rgba(255, 215, 0, 0.8), 0 0 40px rgba(255, 215, 0, 0.6);
animation: lucky-celebration 2s ease-in-out infinite;
}
.lucky-content {
@@ -1392,11 +1399,109 @@ function getCardClass(record: CardFlipRecord): string[] {
.lucky-label {
text-align: center;
margin-top: 6px;
font-size: 12px;
font-weight: bold;
color: rgba(255, 255, 255, 0.95);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
letter-spacing: 1px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 5px;
width: max-content;
min-width: 100px;
max-width: 140px;
margin-left: auto;
margin-right: auto;
.lucky-main-text {
font-size: 14px;
font-weight: bold;
color: #fff;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
letter-spacing: 1px;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
white-space: nowrap;
.fire-icon {
font-size: 14px;
animation: fireShake 0.5s ease-in-out infinite;
display: inline-block;
}
}
.lucky-sub-text {
font-size: 11px;
font-weight: 600;
width: 100%;
display: flex;
justify-content: center;
}
.lucky-highlight {
display: inline-block;
padding: 3px 10px;
border-radius: 12px;
font-size: 11px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
white-space: nowrap;
text-align: center;
&.bounce {
background: linear-gradient(90deg, #ff6b6b 0%, #ffd93d 50%, #ff6b6b 100%);
background-size: 200% 100%;
color: #fff;
box-shadow: 0 2px 10px rgba(255, 107, 107, 0.5);
animation: gradientMove 2s linear infinite, bounceHint 1s ease-in-out infinite;
}
&.full {
background: linear-gradient(135deg, #FFD700 0%, #ff6b9d 50%, #c06c84 100%);
background-size: 200% 100%;
color: #fff;
box-shadow: 0 0 15px rgba(255, 215, 0, 0.8);
animation: gradientMove 2s linear infinite, glowPulse 1.5s ease-in-out infinite;
}
}
@media (max-width: 768px) {
margin-top: 4px;
gap: 3px;
min-width: 80px;
max-width: 120px;
.lucky-main-text {
font-size: 12px;
.fire-icon {
font-size: 12px;
}
}
.lucky-highlight {
font-size: 10px;
padding: 2px 8px;
}
}
}
@keyframes fireShake {
0%, 100% { transform: rotate(-5deg) scale(1); }
50% { transform: rotate(5deg) scale(1.1); }
}
@keyframes bounceHint {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-3px); }
}
@keyframes gradientMove {
0% { background-position: 0% 50%; }
100% { background-position: 200% 50%; }
}
@keyframes glowPulse {
0%, 100% { box-shadow: 0 0 10px rgba(255, 215, 0, 0.6); }
50% { box-shadow: 0 0 20px rgba(255, 215, 0, 1), 0 0 30px rgba(255, 107, 157, 0.5); }
}
@keyframes lucky-celebration {

View File

@@ -8,7 +8,7 @@ export const contactConfig = {
// 二维码图片路径
images: {
customerService: ' https://ccnetcore.com/prod-api/wwwroot/aihub/wx.png ', // 客服微信二维码
communityGroup: ' https://ccnetcore.com/prod-api/wwwroot/aihub/jlq.png', // 交流群二维码
communityGroup: ' https://ccnetcore.com/prod-api/wwwroot/aihub/jlq_yxai.png', // 交流群二维码
afterSalesGroup: '', // 售后群二维码
},
};

File diff suppressed because it is too large Load Diff

View File

@@ -249,6 +249,9 @@ onMounted(() => {
<p class="banner-subtitle">
探索并接入全球顶尖AI模型覆盖文本图像嵌入等多个领域
</p>
<p class="banner-subtitle">
尊享Token = 实际消耗Token * 当前模型倍率
</p>
</div>
<!-- 统计信息卡片 -->
<div class="stats-cards">
@@ -297,7 +300,7 @@ onMounted(() => {
<!-- 点击引导手势 -->
<div class="click-hint">
<svg class="hand-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 11V6C9 5.44772 9.44772 5 10 5C10.5523 5 11 5.44772 11 6V11M9 11V16.5C9 17.8807 10.1193 19 11.5 19H12.5C13.8807 19 15 17.8807 15 16.5V11M9 11H7.5C6.67157 11 6 11.6716 6 12.5C6 13.3284 6.67157 14 7.5 14H9M15 11V8C15 7.44772 15.4477 7 16 7C16.5523 7 17 7.44772 17 8V11M15 11H17.5C18.3284 11 19 11.6716 19 12.5C19 13.3284 18.3284 14 17.5 14H15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 11V6C9 5.44772 9.44772 5 10 5C10.5523 5 11 5.44772 11 6V11M9 11V16.5C9 17.8807 10.1193 19 11.5 19H12.5C13.8807 19 15 17.8807 15 16.5V11M9 11H7.5C6.67157 11 6 11.6716 6 12.5C6 13.3284 6.67157 14 7.5 14H9M15 11V8C15 7.44772 15.4477 7 16 7C16.5523 7 17 7.44772 17 8V11M15 11H17.5C18.3284 11 19 11.6716 19 12.5C19 13.3284 18.3284 14 17.5 14H15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
<span class="hint-text">点击查看</span>
</div>

View File

@@ -109,18 +109,22 @@ export const useChatStore = defineStore('chat', () => {
return {
...item,
key: item.id,
id: item.id, // 保留后端返回的消息ID
key: item.id ?? Math.random().toString(36).substring(2, 9),
placement: isUser ? 'end' : 'start',
// 用户消息气泡形状AI消息无气泡形状宽度100%
isMarkdown: !isUser,
avatar: isUser
? getUserProfilePicture()
: systemProfilePicture,
avatarSize: '32px',
// 头像不显示(后续可能会显示)
// avatar: isUser ? getUserProfilePicture() : systemProfilePicture,
// avatarSize: '32px',
typing: false,
reasoning_content: thinkContent,
thinkingStatus: 'end',
content: finalContent,
thinlCollapse: false,
// AI消息使用 noStyle 去除气泡样式
noStyle: !isUser,
shape: isUser ? 'corner' : undefined,
// 保留图片和文件信息(优先使用解析出来的,如果没有则使用原有的)
images: images.length > 0 ? images : item.images,
files: files.length > 0 ? files : item.files,

View File

@@ -16,6 +16,7 @@ declare module 'vue' {
ContactUs: typeof import('./../src/components/ContactUs/index.vue')['default']
DailyTask: typeof import('./../src/components/userPersonalCenter/components/DailyTask.vue')['default']
DeepThinking: typeof import('./../src/components/DeepThinking/index.vue')['default']
Demo: typeof import('./../src/components/FontAwesomeIcon/demo.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElButton: typeof import('element-plus/es')['ElButton']
@@ -67,6 +68,7 @@ declare module 'vue' {
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElUpload: typeof import('element-plus/es')['ElUpload']
FilesSelect: typeof import('./../src/components/FilesSelect/index.vue')['default']
FontAwesomeIcon: typeof import('./../src/components/FontAwesomeIcon/index.vue')['default']
IconSelect: typeof import('./../src/components/IconSelect/index.vue')['default']
Indexl: typeof import('./../src/components/SupportModelProducts/indexl.vue')['default']
LoginDialog: typeof import('./../src/components/LoginDialog/index.vue')['default']