refactor(fun): 重构 AI剧本生成功能,屏蔽视频下载功能,调整支付引入

This commit is contained in:
fhysy 2025-03-21 11:52:33 +08:00
parent c45996fdd8
commit 9de2ef3734
4 changed files with 197 additions and 327 deletions

View File

@ -1 +1,5 @@
{} {
"dependencies": {
"jweixin-module": "^1.6.0"
}
}

View File

@ -23,9 +23,9 @@
</view> </view>
<!-- 在每条AI消息后显示确认按钮除了系统消息 --> <!-- 在每条AI消息后显示确认按钮除了系统消息 -->
<view v-if="message.role === 'assistant' && message.endStatus" class="accept-button-container"> <view v-if="message.role === 'assistant' && message.endStatus" class="accept-button-container">
<u-button <u-button
type="success" type="success"
size="mini" size="mini"
@click="acceptScript(message.content)" @click="acceptScript(message.content)"
:custom-style="{marginTop: '10px'}" :custom-style="{marginTop: '10px'}"
> >
@ -34,7 +34,7 @@
</u-button> </u-button>
</view> </view>
</block> </block>
<!-- 用户消息 --> <!-- 用户消息 -->
<block v-else-if="message.role === 'user'"> <block v-else-if="message.role === 'user'">
<view class="message-container user-message-container"> <view class="message-container user-message-container">
@ -45,16 +45,16 @@
</view> </view>
</block> </block>
</view> </view>
<!-- 加载中提示 --> <!-- 加载中提示 -->
<view v-if="loading" class="loading-container"> <view v-if="loading" class="loading-container">
<u-loading mode="circle" size="24"></u-loading> <u-loading mode="circle" size="24"></u-loading>
<text class="loading-text">正在生成剧本...</text> <text class="loading-text">正在生成剧本...</text>
</view> </view>
<!-- 用于滚动到底部的空白元素 --> <!-- 用于滚动到底部的空白元素 -->
<view id="scroll-bottom-anchor" style="height: 1px;"></view> <view id="scroll-bottom-anchor" style="height: 1px;"></view>
<!-- 底部占位确保内容不被输入框遮挡 --> <!-- 底部占位确保内容不被输入框遮挡 -->
<view class="bottom-placeholder"></view> <view class="bottom-placeholder"></view>
</view> </view>
@ -67,7 +67,7 @@
v-model="inputMessage" v-model="inputMessage"
placeholder="请输入剧本相关描述AI将为您生成专业剧本..." placeholder="请输入剧本相关描述AI将为您生成专业剧本..."
:disabled="loading" :disabled="loading"
type='textarea' type='textarea'
:auto-height="true" :auto-height="true"
:custom-style="{flex: 1, minHeight: '80rpx', maxHeight: '200rpx'}" :custom-style="{flex: 1, minHeight: '80rpx', maxHeight: '200rpx'}"
@confirm="sendMessage" @confirm="sendMessage"
@ -244,236 +244,151 @@ export default {
// 使fetch APIH5 // 使fetch APIH5
async fetchStreamResponse(apiMessages) { async fetchStreamResponse(apiMessages) {
try { try {
// AI // AI
this.currentAiMessageIndex = this.messages.length; this.currentAiMessageIndex = this.messages.length;
this.messages.push({ this.messages.push({
role: 'assistant', role: 'assistant',
content: '', content: '',
renderedContent: '', renderedContent: '',
time: this.formatTime(new Date()), time: this.formatTime(new Date()),
endStatus: false endStatus: false
}); });
const response = await fetch(`${config.baseUrl}/ai/generateStream`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`,
'clientid': config.clientId,
'Content-Type': 'application/json'
},
redirect: 'follow',
body: JSON.stringify(apiMessages)
});
if (!response.ok) {
throw new Error(`API请求失败: ${response.status}`);
}
if (!response.body) {
throw new Error('响应体为空');
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let result = '';
let buffer = ''; //
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
buffer += chunk; // chunkbuffer
console.log("buffer",buffer)
// ,buffer
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // buffer,
console.log("lines",lines)
for (const line of lines.filter(l => l.trim())) {
// debugger
console.log('处理行:', line);
let data = line;
if (line.startsWith('data:')) {
data = line.substring(5).trim();
}
console.log("data",data)
if (data === '[DONE]') {
//
if (this.currentAiMessageIndex >= 0 && this.currentAiMessageIndex < this.messages.length) {
this.messages[this.currentAiMessageIndex].endStatus = true;
//
this.throttledScrollToBottom();
}
continue;
}
// debugger
try {
result += data;
console.log("result",result)
if (this.messages && this.currentAiMessageIndex < this.messages.length) {
this.messages[this.currentAiMessageIndex].content = result;
this.messages[this.currentAiMessageIndex].renderedContent = renderMarkdown(result);
console.log("messages1",this.messages)
//
this.throttledScrollToBottom();
} else {
console.warn('无法更新消息,索引无效:', aiMessageIndex);
}
} catch (e) {
console.error('解析流数据失败:', e, line);
}
}
// buffer
if (buffer) {
try {
let data = buffer;
if (buffer.startsWith('data:')) {
data = buffer.substring(5).trim();
}
if (data && data !== '[DONE]') {
result += data;
this.messages[this.currentAiMessageIndex].content = result;
this.messages[this.currentAiMessageIndex].renderedContent = renderMarkdown(result);
this.throttledScrollToBottom();
}
} catch (e) {
console.error('处理剩余数据失败:', e, buffer);
}
}
}
return result;
} catch (error) {
console.error('调用DeepSeek流式API失败:', error);
// AI
if (this.messages.length > 0 && this.messages[this.messages.length - 1].content === '') {
this.messages.pop();
this.currentAiMessageIndex = -1;
}
throw error;
}
// try {
// // AI
// this.currentAiMessageIndex = this.messages.length;
// this.messages.push({
// role: 'assistant',
// content: '',
// renderedContent: '',
// time: this.formatTime(new Date()),
// endStatus: false
// });
// const response = await fetch('https://api.siliconflow.cn/v1/chat/completions', { const response = await fetch(`${config.baseUrl}/ai/generateStream`, {
// method: 'POST', method: 'POST',
// headers: { headers: {
// 'Authorization': `Bearer ${this.apiKey}`, 'Authorization': `Bearer ${this.token}`,
// 'Content-Type': 'application/json' 'clientid': config.clientId,
// }, 'Content-Type': 'application/json'
// body: JSON.stringify({ },
// model: 'deepseek-ai/DeepSeek-R1-Distill-Qwen-7B', redirect: 'follow',
// messages: apiMessages, body: JSON.stringify(apiMessages)
// stream: true, });
// max_tokens: 8192,
// stop: '', if (!response.ok) {
// temperature: 0.6, throw new Error(`API请求失败: ${response.status}`);
// top_p: 0.7, }
// top_k: 50,
// frequency_penalty: 0 if (!response.body) {
// }) throw new Error('响应体为空');
// }); }
// if (!response.ok) { const reader = response.body.getReader();
// throw new Error(`API: ${response.status}`); const decoder = new TextDecoder();
// } let result = '';
let buffer = ''; //
// if (!response.body) {
// throw new Error(''); while (true) {
// } const { done, value } = await reader.read();
if (done) break;
// const reader = response.body.getReader();
// const decoder = new TextDecoder();
// let result = '';
// while (true) {
// const { done, value } = await reader.read();
// if (done) break;
// const chunk = decoder.decode(value); const chunk = decoder.decode(value, { stream: true }); // Use streaming mode
// const lines = chunk.split('\n').filter(line => line.trim() !== ''); buffer += chunk;
// for (const line of lines) { // chunkbuffer
// if (line.startsWith('data: ')) { const lines = buffer.split('\n');
// const data = line.substring(6).trim(); // ,buffer
// if (data === '[DONE]') { buffer = lines.pop() || '';
// //
// if (this.currentAiMessageIndex >= 0 && this.currentAiMessageIndex < this.messages.length) { for (const line of lines) {
// this.messages[this.currentAiMessageIndex].endStatus = true; if (!line.trim()) continue; // Skip empty lines
// //
// this.throttledScrollToBottom(); let data = line;
// }
// continue; if (line.startsWith('data:')) {
// } data = line.substring(5).trim();
}
if (data === '[DONE]') {
//
if (this.currentAiMessageIndex >= 0 && this.currentAiMessageIndex < this.messages.length) {
this.messages[this.currentAiMessageIndex].endStatus = true;
this.throttledScrollToBottom();
}
continue;
}
try {
// For JSON data (if your API returns JSON)
if (data.startsWith('{') && data.endsWith('}')) {
const parsed = JSON.parse(data);
const content = parsed.choices?.[0]?.delta?.content || '';
if (content) {
result += content;
}
} else {
// For plain text data
result += data;
}
// try { // Update message content
// const parsed = JSON.parse(data); if (this.currentAiMessageIndex >= 0 && this.currentAiMessageIndex < this.messages.length) {
// // parsedchoices this.messages[this.currentAiMessageIndex].content = result;
// if (!parsed || !parsed.choices || !Array.isArray(parsed.choices) || parsed.choices.length === 0) { this.messages[this.currentAiMessageIndex].renderedContent = renderMarkdown(result);
// console.warn(':', data); this.throttledScrollToBottom();
// continue; }
// } } catch (e) {
console.error('解析流数据失败:', e, line);
// // 访delta }
// const delta = parsed.choices[0].delta || {}; }
}
// //
// const content = delta.content;
// // reasoning_contentcontent
// const reasoningContent = delta.reasoning_content || '';
// // contentnullreasoningContent
// const textToAdd = (content !== null && content !== undefined) ? content : reasoningContent;
// if (textToAdd) {
// result += textToAdd;
// // messages[currentAiMessageIndex]
// if (this.currentAiMessageIndex >= 0 && this.currentAiMessageIndex < this.messages.length) {
// this.messages[this.currentAiMessageIndex].content = result;
// this.messages[this.currentAiMessageIndex].renderedContent = renderMarkdown(result);
// //
// this.throttledScrollToBottom();
// }
// }
// } catch (e) {
// console.error(':', e, line);
// }
// }
// }
// }
// return result; // Process any remaining data in buffer
// } catch (error) { if (buffer.trim()) {
// console.error('DeepSeekAPI:', error); try {
// // AI let data = buffer;
// if (this.messages.length > 0 && this.messages[this.messages.length - 1].content === '') { if (buffer.startsWith('data:')) {
// this.messages.pop(); data = buffer.substring(5).trim();
// this.currentAiMessageIndex = -1; }
// } if (data && data !== '[DONE]') {
// throw error; // For JSON data
// } if (data.startsWith('{') && data.endsWith('}')) {
try {
const parsed = JSON.parse(data);
const content = parsed.choices?.[0]?.delta?.content || '';
if (content) {
result += content;
}
} catch (e) {
// If not valid JSON, treat as plain text
result += data;
}
} else {
// For plain text data
result += data;
}
this.messages[this.currentAiMessageIndex].content = result;
this.messages[this.currentAiMessageIndex].renderedContent = renderMarkdown(result);
this.throttledScrollToBottom();
}
} catch (e) {
console.error('处理剩余数据失败:', e, buffer);
}
}
// Ensure message is marked as complete
if (this.currentAiMessageIndex >= 0 && this.currentAiMessageIndex < this.messages.length) {
this.messages[this.currentAiMessageIndex].endStatus = true;
}
return result;
} catch (error) {
console.error('调用流式API失败:', error);
// If error occurs, ensure empty AI message is removed
if (this.messages.length > 0 && this.messages[this.messages.length - 1].content === '') {
this.messages.pop();
this.currentAiMessageIndex = -1;
}
throw error;
}
}, },
// 使H5 // Use normal request (non-H5 environment)
async fetchNormalResponse(apiMessages) { async fetchNormalResponse(apiMessages) {
try { try {
// AI // Add an empty AI reply
this.currentAiMessageIndex = this.messages.length; this.currentAiMessageIndex = this.messages.length;
this.messages.push({ this.messages.push({
role: 'assistant', role: 'assistant',
@ -484,46 +399,37 @@ export default {
}); });
const response = await uni.request({ const response = await uni.request({
url: 'https://api.siliconflow.cn/v1/chat/completions', url: `${config.baseUrl}/ai/generate`,
method: 'POST', method: 'POST',
header: { header: {
'Authorization': `Bearer ${this.apiKey}`, 'Authorization': `Bearer ${this.token}`,
'clientid': config.clientId,
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
data: { data: apiMessages,
model: 'deepseek-ai/DeepSeek-R1-Distill-Qwen-7B',
messages: apiMessages,
stream: false,
max_tokens: 8192,
stop: '',
temperature: 0.6,
top_p: 0.7,
top_k: 50,
frequency_penalty: 0
},
dataType: 'json' dataType: 'json'
}); });
// // Check response status
if (response.statusCode !== 200) { if (response.statusCode !== 200) {
throw new Error(`API请求失败: ${response.statusCode}`); throw new Error(`API请求失败: ${response.statusCode}`);
} }
// // Get response content
const result = response.data.choices[0].message.content; const result = response.data.content || '';
// // Update message content
this.messages[this.currentAiMessageIndex].content = result; this.messages[this.currentAiMessageIndex].content = result;
this.messages[this.currentAiMessageIndex].renderedContent = renderMarkdown(result); this.messages[this.currentAiMessageIndex].renderedContent = renderMarkdown(result);
this.messages[this.currentAiMessageIndex].endStatus = true; this.messages[this.currentAiMessageIndex].endStatus = true;
// // Scroll to bottom
this.throttledScrollToBottom(); this.throttledScrollToBottom();
return result; return result;
} catch (error) { } catch (error) {
console.error('调用DeepSeek API失败:', error); console.error('调用API失败:', error);
// AI // If error occurs, ensure empty AI message is removed
if (this.messages.length > 0 && this.messages[this.messages.length - 1].content === '') { if (this.messages.length > 0 && this.messages[this.messages.length - 1].content === '') {
this.messages.pop(); this.messages.pop();
this.currentAiMessageIndex = -1; this.currentAiMessageIndex = -1;
@ -532,72 +438,42 @@ export default {
} }
}, },
// // Accept script
acceptScript(scriptContent) { acceptScript(scriptContent) {
// // 使
// getApp().globalData.selectedScript = {
// orderId: this.orderId,
// content: scriptContent
// };
// const pages = getCurrentPages();
// // pageA
// const prevPage = pages[pages.length - 2];
// console.log("prevPage",prevPage)
uni.showToast({ uni.showToast({
title: '已确认使用此剧本', title: '已确认使用此剧本',
icon: 'success' icon: 'success'
}); });
// // Return to previous page
// setTimeout(() => { uni.navigateBack({
// uni.navigateBack(); delta: 1, // Number of pages to go back, 1 means return to previous page
// }, 1500); success: function() {
// Get page stack
// pageB const pages = getCurrentPages();
uni.navigateBack({ // Previous page instance
delta: 1, // 1 const prevPage = pages[pages.length - 1];
success: function() { // Call method on previous page
// if (prevPage && typeof prevPage.getAiDramaTxt === 'function') {
const pages = getCurrentPages(); prevPage.getAiDramaTxt(scriptContent);
// pageA }
const prevPage = pages[pages.length - 2]; }
console.log("prevPage",prevPage) });
//
prevPage.getAiDramaTxt(scriptContent);
}
});
}, },
// // Throttled scroll to bottom function to avoid frequent scrolling
throttledScrollToBottom() { throttledScrollToBottom() {
// if (this.scrollTimer) return; this.$u.throttle(this.scrollToBottom, 300);
// this.scrollTimer = setTimeout(() => {
// this.scrollToBottom();
// this.scrollTimer = null;
// }, 1000); // 300ms
this.$u.throttle(this.scrollToBottom, 300)
}, },
// // Scroll to bottom
scrollToBottom() { scrollToBottom() {
this.$nextTick(() => { this.$nextTick(() => {
// 1: 使scrollTop
// const query = uni.createSelectorQuery().in(this);
// query.select('.chat-content').boundingClientRect(data => {
// if (data) {
// //
// this.scrollTop = 999999;
// }
// }).exec();
// 2: 使
setTimeout(() => { setTimeout(() => {
const query = uni.createSelectorQuery().in(this); const query = uni.createSelectorQuery().in(this);
query.select('#scroll-bottom-anchor').boundingClientRect(data => { query.select('#scroll-bottom-anchor').boundingClientRect(data => {
if (data) { if (data) {
// // Scroll to anchor element position
uni.pageScrollTo({ uni.pageScrollTo({
selector: '#scroll-bottom-anchor', selector: '#scroll-bottom-anchor',
duration: 100 duration: 100
@ -608,13 +484,13 @@ export default {
}); });
}, },
// // Load more messages (pull-up loading)
loadMoreMessages() { loadMoreMessages() {
// // Logic for loading history messages can be implemented here
// // Currently not needed, keeping the interface
this.needScrollToBottom = false; // this.needScrollToBottom = false; // Disable auto-scrolling when pulling up
// // Restore auto-scrolling after loading
setTimeout(() => { setTimeout(() => {
this.needScrollToBottom = true; this.needScrollToBottom = true;
}, 1000); }, 1000);
@ -633,14 +509,11 @@ export default {
.chat-content { .chat-content {
flex: 1; flex: 1;
background-color: #f5f7fa; background-color: #f5f7fa;
// padding: 20rpx;
// padding-bottom: 180rpx; /* */
} }
.message-wrapper-box{ .message-wrapper-box{
padding: 20rpx; padding: 20rpx;
// padding-bottom: 180rpx; /* */
} }
.message-wrapper { .message-wrapper {
@ -683,7 +556,7 @@ export default {
word-break: break-word; word-break: break-word;
} }
/* Markdown样式 */ /* Markdown styles */
.markdown-body { .markdown-body {
color: #333; color: #333;
} }
@ -699,21 +572,16 @@ export default {
color: #909399; color: #909399;
} }
/* 固定在底部的输入区域 */ /* Input area fixed at the bottom */
.chat-input-container { .chat-input-container {
// position: fixed;
// bottom: 0;
// left: 0;
// right: 0;
background-color: #fff; background-color: #fff;
border-top: 1px solid #ebeef5; border-top: 1px solid #ebeef5;
padding: 20rpx 30rpx; padding: 20rpx 30rpx;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05); box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
z-index: 100; z-index: 100;
// min-height: 170rpx; margin-top: auto;
margin-top: auto; position: sticky;
position: sticky; bottom: 0;
bottom: 0;
} }
.chat-input { .chat-input {
@ -722,13 +590,10 @@ export default {
} }
.bottom-placeholder { .bottom-placeholder {
height: 40rpx; /* 确保内容不被输入框遮挡 */ height: 40rpx; /* Ensure content is not hidden by input box */
} }
.accept-button-container { .accept-button-container {
// display: flex;
// justify-content: flex-start;
// margin-top: 20rpx;
margin-bottom: 40rpx; margin-bottom: 40rpx;
} }
@ -748,4 +613,5 @@ export default {
.button-icon { .button-icon {
margin-right: 8rpx; margin-right: 8rpx;
} }
</style> </style>

View File

@ -27,13 +27,13 @@
</view> </view>
<!-- 底部按钮区域 --> <!-- 底部按钮区域 -->
<view class="btn-box one" v-else-if="routeParams.name === '查看交付视频'"> <!-- <view class="btn-box one" v-else-if="routeParams.name === '查看交付视频'">
<u-button <u-button
class="confirm-btn" class="confirm-btn"
type="primary" type="primary"
@click="downloadVideo" @click="downloadVideo"
>下载交付视频</u-button> >下载交付视频</u-button>
</view> </view> -->
<!-- 调整建议弹窗 --> <!-- 调整建议弹窗 -->
<u-popup <u-popup

View File

@ -86,7 +86,7 @@
</template> </template>
<script> <script>
// import WeixinJSBridge from '../../utils/jweixin-1.6.0.js'; // import jweixin from '../../utils/jweixin-1.6.0.js';
var jweixin = require('jweixin-module'); var jweixin = require('jweixin-module');
export default { export default {
data() { data() {