diff --git a/package.json b/package.json index 0967ef4..a690ac8 100644 --- a/package.json +++ b/package.json @@ -1 +1,5 @@ -{} +{ + "dependencies": { + "jweixin-module": "^1.6.0" + } +} diff --git a/pages/fun/AiDrama.vue b/pages/fun/AiDrama.vue index 7c91cb6..3d27a6a 100644 --- a/pages/fun/AiDrama.vue +++ b/pages/fun/AiDrama.vue @@ -23,9 +23,9 @@ - @@ -34,7 +34,7 @@ - + @@ -45,16 +45,16 @@ - + 正在生成剧本... - + - + @@ -67,7 +67,7 @@ v-model="inputMessage" placeholder="请输入剧本相关描述,AI将为您生成专业剧本..." :disabled="loading" - type='textarea' + type='textarea' :auto-height="true" :custom-style="{flex: 1, minHeight: '80rpx', maxHeight: '200rpx'}" @confirm="sendMessage" @@ -244,236 +244,151 @@ export default { // 使用fetch API进行流式请求(仅H5环境) async fetchStreamResponse(apiMessages) { - 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(`${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; // 将新的chunk添加到buffer中 - 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 - // }); + 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', { - // method: 'POST', - // headers: { - // 'Authorization': `Bearer ${this.apiKey}`, - // 'Content-Type': 'application/json' - // }, - // body: JSON.stringify({ - // model: 'deepseek-ai/DeepSeek-R1-Distill-Qwen-7B', - // messages: apiMessages, - // stream: true, - // max_tokens: 8192, - // stop: '', - // temperature: 0.6, - // top_p: 0.7, - // top_k: 50, - // frequency_penalty: 0 - // }) - // }); - - // 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 = ''; - - // while (true) { - // const { done, value } = await reader.read(); - // if (done) break; + 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); - // const lines = chunk.split('\n').filter(line => line.trim() !== ''); + const chunk = decoder.decode(value, { stream: true }); // Use streaming mode + buffer += chunk; - // for (const line of lines) { - // if (line.startsWith('data: ')) { - // const data = line.substring(6).trim(); - // if (data === '[DONE]') { - // // 设置消息已完成状态 - // if (this.currentAiMessageIndex >= 0 && this.currentAiMessageIndex < this.messages.length) { - // this.messages[this.currentAiMessageIndex].endStatus = true; - // // 最后一次滚动到底部 - // this.throttledScrollToBottom(); - // } - // continue; - // } + // 将新的chunk添加到buffer中 + const lines = buffer.split('\n'); + // 按换行符分割,但保留buffer中可能不完整的最后一行 + buffer = lines.pop() || ''; + + for (const line of lines) { + if (!line.trim()) continue; // Skip empty lines + + let data = line; + + 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 { - // const parsed = JSON.parse(data); - // // 检查parsed和choices是否存在 - // if (!parsed || !parsed.choices || !Array.isArray(parsed.choices) || parsed.choices.length === 0) { - // console.warn('无效的响应格式:', data); - // continue; - // } - - // // 安全地访问delta属性 - // const delta = parsed.choices[0].delta || {}; - - // // 根据您提供的格式解析内容 - // const content = delta.content; - // // 有些响应可能包含reasoning_content而不是content - // const reasoningContent = delta.reasoning_content || ''; - - // // 只有当content不是null且有值,或者reasoningContent有值时才添加 - // 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); - // } - // } - // } - // } + // Update message content + 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; - // } 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; - // } + // Process any remaining data in buffer + if (buffer.trim()) { + try { + let data = buffer; + if (buffer.startsWith('data:')) { + data = buffer.substring(5).trim(); + } + if (data && data !== '[DONE]') { + // 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) { try { - // 添加一个空的AI回复 + // Add an empty AI reply this.currentAiMessageIndex = this.messages.length; this.messages.push({ role: 'assistant', @@ -484,46 +399,37 @@ export default { }); const response = await uni.request({ - url: 'https://api.siliconflow.cn/v1/chat/completions', + url: `${config.baseUrl}/ai/generate`, method: 'POST', header: { - 'Authorization': `Bearer ${this.apiKey}`, + 'Authorization': `Bearer ${this.token}`, + 'clientid': config.clientId, 'Content-Type': 'application/json' }, - data: { - 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 - }, + data: apiMessages, dataType: 'json' }); - // 检查响应状态 + // Check response status if (response.statusCode !== 200) { throw new Error(`API请求失败: ${response.statusCode}`); } - // 获取响应内容 - const result = response.data.choices[0].message.content; + // Get response content + const result = response.data.content || ''; - // 更新消息内容 + // Update message content this.messages[this.currentAiMessageIndex].content = result; this.messages[this.currentAiMessageIndex].renderedContent = renderMarkdown(result); this.messages[this.currentAiMessageIndex].endStatus = true; - // 滚动到底部 + // Scroll to bottom this.throttledScrollToBottom(); return result; } catch (error) { - console.error('调用DeepSeek API失败:', error); - // 如果出错,确保移除空的AI消息 + 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; @@ -532,72 +438,42 @@ export default { } }, - // 接受剧本 + // Accept script 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({ title: '已确认使用此剧本', icon: 'success' }); - // 返回上一页 - // setTimeout(() => { - // uni.navigateBack(); - // }, 1500); - - // 当前页面(假设是 pageB) - uni.navigateBack({ - delta: 1, // 返回的页面层数,1 表示返回上一个页面 - success: function() { - // 获取页面栈 - const pages = getCurrentPages(); - // 上一个页面实例(假设是 pageA) - const prevPage = pages[pages.length - 2]; - console.log("prevPage",prevPage) - // 调用上一个页面的方法 - prevPage.getAiDramaTxt(scriptContent); - } - }); + // Return to previous page + uni.navigateBack({ + delta: 1, // Number of pages to go back, 1 means return to previous page + success: function() { + // Get page stack + const pages = getCurrentPages(); + // Previous page instance + const prevPage = pages[pages.length - 1]; + // Call method on previous page + if (prevPage && typeof prevPage.getAiDramaTxt === 'function') { + prevPage.getAiDramaTxt(scriptContent); + } + } + }); }, - // 节流滚动到底部函数,避免频繁滚动 + // Throttled scroll to bottom function to avoid frequent scrolling throttledScrollToBottom() { - // if (this.scrollTimer) return; - - // this.scrollTimer = setTimeout(() => { - // this.scrollToBottom(); - // this.scrollTimer = null; - // }, 1000); // 300ms内只执行一次滚动 - this.$u.throttle(this.scrollToBottom, 300) + this.$u.throttle(this.scrollToBottom, 300); }, - // 滚动到底部 + // Scroll to bottom scrollToBottom() { this.$nextTick(() => { - // 方法1: 使用scrollTop设置滚动位置 - // const query = uni.createSelectorQuery().in(this); - // query.select('.chat-content').boundingClientRect(data => { - // if (data) { - // // 设置一个非常大的值确保滚动到底部 - // this.scrollTop = 999999; - // } - // }).exec(); - - // 方法2: 使用选择器定位到底部锚点元素 setTimeout(() => { const query = uni.createSelectorQuery().in(this); query.select('#scroll-bottom-anchor').boundingClientRect(data => { if (data) { - // 滚动到锚点元素位置 + // Scroll to anchor element position uni.pageScrollTo({ selector: '#scroll-bottom-anchor', duration: 100 @@ -608,13 +484,13 @@ export default { }); }, - // 加载更多消息(上拉加载) + // Load more messages (pull-up loading) loadMoreMessages() { - // 这里可以实现加载历史消息的逻辑 - // 目前不需要,保留接口 - this.needScrollToBottom = false; // 上拉加载时禁用自动滚动 + // Logic for loading history messages can be implemented here + // Currently not needed, keeping the interface + this.needScrollToBottom = false; // Disable auto-scrolling when pulling up - // 加载完成后恢复自动滚动 + // Restore auto-scrolling after loading setTimeout(() => { this.needScrollToBottom = true; }, 1000); @@ -633,14 +509,11 @@ export default { .chat-content { flex: 1; - background-color: #f5f7fa; - // padding: 20rpx; - // padding-bottom: 180rpx; /* 为底部输入框留出空间 */ + background-color: #f5f7fa; } .message-wrapper-box{ - padding: 20rpx; - // padding-bottom: 180rpx; /* 为底部输入框留出空间 */ + padding: 20rpx; } .message-wrapper { @@ -683,7 +556,7 @@ export default { word-break: break-word; } -/* Markdown样式 */ +/* Markdown styles */ .markdown-body { color: #333; } @@ -699,21 +572,16 @@ export default { color: #909399; } -/* 固定在底部的输入区域 */ +/* Input area fixed at the bottom */ .chat-input-container { - // position: fixed; - // bottom: 0; - // left: 0; - // right: 0; background-color: #fff; border-top: 1px solid #ebeef5; padding: 20rpx 30rpx; box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05); z-index: 100; - // min-height: 170rpx; - margin-top: auto; - position: sticky; - bottom: 0; + margin-top: auto; + position: sticky; + bottom: 0; } .chat-input { @@ -722,13 +590,10 @@ export default { } .bottom-placeholder { - height: 40rpx; /* 确保内容不被输入框遮挡 */ + height: 40rpx; /* Ensure content is not hidden by input box */ } .accept-button-container { - // display: flex; - // justify-content: flex-start; - // margin-top: 20rpx; margin-bottom: 40rpx; } @@ -748,4 +613,5 @@ export default { .button-icon { margin-right: 8rpx; } - \ No newline at end of file + + diff --git a/pages/fun/videoView.vue b/pages/fun/videoView.vue index 7b17f10..d379da5 100644 --- a/pages/fun/videoView.vue +++ b/pages/fun/videoView.vue @@ -27,13 +27,13 @@ - +