app添加工作经历开发
This commit is contained in:
@@ -263,15 +263,43 @@ ol {
|
||||
<style lang="stylus">
|
||||
.custom-more{
|
||||
display: flex
|
||||
justify-content: flex-end
|
||||
color: #256BFA
|
||||
padding-top: 5rpx
|
||||
padding-bottom: 14rpx
|
||||
justify-content: center
|
||||
align-items: center
|
||||
color: #FFFFFF
|
||||
background: linear-gradient(135deg, #256BFA 0%, #9E74FD 100%)
|
||||
border-radius: 50rpx
|
||||
padding: 20rpx 32rpx
|
||||
margin: 20rpx 0
|
||||
font-size: 28rpx
|
||||
font-weight: 600
|
||||
box-shadow: 0rpx 8rpx 24rpx rgba(37, 107, 250, 0.3)
|
||||
transition: all 0.3s ease
|
||||
position: relative
|
||||
overflow: hidden
|
||||
.more-icon{
|
||||
width: 60rpx;
|
||||
height: 40rpx;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
background: url('@/static/svg/seemore.svg') center center no-repeat;
|
||||
background-size: 100% 100%
|
||||
margin-left: 12rpx
|
||||
filter: brightness(0) invert(1)
|
||||
}
|
||||
&::before {
|
||||
content: ''
|
||||
position: absolute
|
||||
top: 0
|
||||
left: -100%
|
||||
width: 100%
|
||||
height: 100%
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent)
|
||||
transition: left 0.5s ease
|
||||
}
|
||||
&:active {
|
||||
transform: translateY(2rpx)
|
||||
box-shadow: 0rpx 4rpx 16rpx rgba(37, 107, 250, 0.4)
|
||||
}
|
||||
&:active::before {
|
||||
left: 100%
|
||||
}
|
||||
}
|
||||
.custom-card
|
||||
|
@@ -28,27 +28,57 @@ export function useTTSPlayer(wsUrl) {
|
||||
let activePlayId = 0
|
||||
|
||||
const speak = (text) => {
|
||||
console.log('🎤 TTS speak function called');
|
||||
console.log('📝 Text to synthesize:', text ? text.substring(0, 100) + '...' : 'No text');
|
||||
console.log('🔗 WebSocket URL:', wsUrl);
|
||||
|
||||
currentPlayId++
|
||||
const myPlayId = currentPlayId
|
||||
console.log('🆔 Play ID:', myPlayId);
|
||||
|
||||
reset()
|
||||
pendingText = text
|
||||
activePlayId = myPlayId
|
||||
|
||||
console.log('✅ Speak function setup complete');
|
||||
}
|
||||
|
||||
const pause = () => {
|
||||
console.log('⏸️ TTS pause called');
|
||||
console.log('🔊 AudioContext state:', audioContext.state);
|
||||
console.log('🔊 Is speaking before pause:', isSpeaking.value);
|
||||
console.log('⏸️ Is paused before pause:', isPaused.value);
|
||||
|
||||
if (audioContext.state === 'running') {
|
||||
audioContext.suspend()
|
||||
isPaused.value = true
|
||||
isSpeaking.value = false
|
||||
// 不要设置 isSpeaking.value = false,保持当前状态
|
||||
console.log('✅ Audio paused successfully');
|
||||
} else {
|
||||
console.log('⚠️ AudioContext is not running, cannot pause');
|
||||
}
|
||||
|
||||
console.log('🔊 Is speaking after pause:', isSpeaking.value);
|
||||
console.log('⏸️ Is paused after pause:', isPaused.value);
|
||||
}
|
||||
|
||||
const resume = () => {
|
||||
console.log('▶️ TTS resume called');
|
||||
console.log('🔊 AudioContext state:', audioContext.state);
|
||||
console.log('🔊 Is speaking before resume:', isSpeaking.value);
|
||||
console.log('⏸️ Is paused before resume:', isPaused.value);
|
||||
|
||||
if (audioContext.state === 'suspended') {
|
||||
audioContext.resume()
|
||||
isPaused.value = false
|
||||
isSpeaking.value = true
|
||||
console.log('✅ Audio resumed successfully');
|
||||
} else {
|
||||
console.log('⚠️ AudioContext is not suspended, cannot resume');
|
||||
}
|
||||
|
||||
console.log('🔊 Is speaking after resume:', isSpeaking.value);
|
||||
console.log('⏸️ Is paused after resume:', isPaused.value);
|
||||
}
|
||||
|
||||
const cancelAudio = () => {
|
||||
@@ -89,16 +119,42 @@ export function useTTSPlayer(wsUrl) {
|
||||
|
||||
const initWebSocket = () => {
|
||||
const thisPlayId = currentPlayId
|
||||
console.log('🔌 Initializing WebSocket connection');
|
||||
console.log('🔗 WebSocket URL:', wsUrl);
|
||||
console.log('🆔 This play ID:', thisPlayId);
|
||||
|
||||
socket = new WebSocket(wsUrl)
|
||||
socket.binaryType = 'arraybuffer'
|
||||
|
||||
// 设置心跳检测,避免超时
|
||||
const heartbeatInterval = setInterval(() => {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(JSON.stringify({ type: 'ping' }));
|
||||
}
|
||||
}, 30000); // 每30秒发送一次心跳
|
||||
|
||||
socket.onopen = () => {
|
||||
console.log('✅ WebSocket connection opened');
|
||||
if (pendingText && thisPlayId === activePlayId) {
|
||||
const seepdText = extractSpeechText(pendingText)
|
||||
console.log('📤 Sending text to TTS server:', seepdText.substring(0, 100) + '...');
|
||||
socket.send(seepdText)
|
||||
pendingText = null
|
||||
} else {
|
||||
console.log('❌ No pending text or play ID mismatch');
|
||||
console.log('📝 Pending text exists:', !!pendingText);
|
||||
console.log('🆔 Play ID match:', thisPlayId === activePlayId);
|
||||
}
|
||||
}
|
||||
|
||||
socket.onerror = (error) => {
|
||||
console.error('❌ WebSocket error:', error);
|
||||
}
|
||||
|
||||
socket.onclose = (event) => {
|
||||
console.log('🔌 WebSocket connection closed:', event.code, event.reason);
|
||||
clearInterval(heartbeatInterval);
|
||||
}
|
||||
|
||||
socket.onmessage = async (e) => {
|
||||
if (thisPlayId !== activePlayId) return // 忽略旧播放的消息
|
||||
@@ -106,13 +162,19 @@ export function useTTSPlayer(wsUrl) {
|
||||
if (typeof e.data === 'string') {
|
||||
try {
|
||||
const msg = JSON.parse(e.data)
|
||||
console.log('📨 TTS server message:', msg);
|
||||
if (msg.status === 'complete') {
|
||||
console.log('✅ TTS synthesis completed');
|
||||
isComplete.value = true
|
||||
// 计算剩余播放时间,确保播放完整
|
||||
const remainingTime = Math.max(0, (playTime - audioContext.currentTime) * 1000);
|
||||
console.log('⏱️ Remaining play time:', remainingTime + 'ms');
|
||||
setTimeout(() => {
|
||||
if (thisPlayId === activePlayId) {
|
||||
console.log('🔇 TTS playback finished, setting isSpeaking to false');
|
||||
isSpeaking.value = false
|
||||
}
|
||||
}, (playTime - audioContext.currentTime) * 1000)
|
||||
}, remainingTime + 500) // 额外500ms缓冲时间
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('[TTSPlayer] 文本消息:', e.data)
|
||||
@@ -156,8 +218,10 @@ export function useTTSPlayer(wsUrl) {
|
||||
}
|
||||
|
||||
const playBuffer = (audioBuffer) => {
|
||||
console.log('🎵 playBuffer called, duration:', audioBuffer.duration + 's');
|
||||
if (!isSpeaking.value) {
|
||||
playTime = audioContext.currentTime
|
||||
console.log('🎵 Starting new audio playback at time:', playTime);
|
||||
}
|
||||
const source = audioContext.createBufferSource()
|
||||
source.buffer = audioBuffer
|
||||
@@ -166,6 +230,12 @@ export function useTTSPlayer(wsUrl) {
|
||||
sourceNodes.push(source)
|
||||
playTime += audioBuffer.duration
|
||||
isSpeaking.value = true
|
||||
console.log('🎵 Audio scheduled, new playTime:', playTime);
|
||||
|
||||
// 添加音频播放结束监听
|
||||
source.onended = () => {
|
||||
console.log('🎵 Audio buffer finished playing');
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
@@ -203,6 +273,10 @@ export function useTTSPlayer(wsUrl) {
|
||||
}
|
||||
|
||||
function extractSpeechText(markdown) {
|
||||
console.log('🔍 extractSpeechText called');
|
||||
console.log('📝 Input markdown length:', markdown ? markdown.length : 0);
|
||||
console.log('📝 Input markdown preview:', markdown ? markdown.substring(0, 200) + '...' : 'No markdown');
|
||||
|
||||
const jobRegex = /``` job-json\s*({[\s\S]*?})\s*```/g;
|
||||
const jobs = [];
|
||||
let match;
|
||||
@@ -219,11 +293,16 @@ function extractSpeechText(markdown) {
|
||||
firstJobStartIndex = match.index;
|
||||
}
|
||||
lastJobEndIndex = jobRegex.lastIndex;
|
||||
console.log('✅ Found job:', job.jobTitle);
|
||||
} catch (e) {
|
||||
console.warn('JSON 解析失败', e);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('📊 Jobs found:', jobs.length);
|
||||
console.log('📍 First job start index:', firstJobStartIndex);
|
||||
console.log('📍 Last job end index:', lastJobEndIndex);
|
||||
|
||||
// 提取引导语(第一个 job-json 之前的文字)
|
||||
const guideText = firstJobStartIndex > 0 ?
|
||||
markdown.slice(0, firstJobStartIndex).trim() :
|
||||
@@ -234,6 +313,9 @@ function extractSpeechText(markdown) {
|
||||
markdown.slice(lastJobEndIndex).trim() :
|
||||
'';
|
||||
|
||||
console.log('📝 Guide text:', guideText);
|
||||
console.log('📝 Ending text:', endingText);
|
||||
|
||||
// 岗位信息格式化为语音文本
|
||||
const jobTexts = jobs.map((job, index) => {
|
||||
return `第 ${index + 1} 个岗位,岗位名称是:${job.jobTitle},公司是:${job.companyName},薪资:${job.salary},地点:${job.location},学历要求:${job.education},经验要求:${job.experience}。`;
|
||||
@@ -245,5 +327,10 @@ function extractSpeechText(markdown) {
|
||||
finalTextParts.push(...jobTexts);
|
||||
if (endingText) finalTextParts.push(endingText);
|
||||
|
||||
return finalTextParts.join('\n');
|
||||
const finalText = finalTextParts.join('\n');
|
||||
console.log('🎤 Final TTS text length:', finalText.length);
|
||||
console.log('🎤 Final TTS text preview:', finalText.substring(0, 200) + '...');
|
||||
console.log('🎤 Final TTS text parts count:', finalTextParts.length);
|
||||
|
||||
return finalText;
|
||||
}
|
388
packageA/pages/addWorkExperience/addWorkExperience.vue
Normal file
388
packageA/pages/addWorkExperience/addWorkExperience.vue
Normal file
@@ -0,0 +1,388 @@
|
||||
<template>
|
||||
<AppLayout
|
||||
title="添加工作经历"
|
||||
border
|
||||
back-gorund-color="#ffffff"
|
||||
:show-bg-image="false"
|
||||
>
|
||||
<template #headerleft>
|
||||
<view class="btn mar_le20 button-click" @click="navBack">取消</view>
|
||||
</template>
|
||||
<template #headerright>
|
||||
<view class="btn mar_ri20 button-click" @click="handleConfirm">确认</view>
|
||||
</template>
|
||||
<view class="content">
|
||||
<view class="content-input">
|
||||
<view class="input-titile">公司名称</view>
|
||||
<input class="input-con" v-model="formData.companyName" maxlength="200" placeholder-style="font-size: 16px" placeholder="请输入公司名称" />
|
||||
</view>
|
||||
<view class="content-input">
|
||||
<view class="input-titile">职位名称</view>
|
||||
<input class="input-con" v-model="formData.position" maxlength="100" placeholder-style="font-size: 16px" placeholder="请输入职位名称" />
|
||||
</view>
|
||||
<view class="content-input">
|
||||
<view class="input-titile">工作时间</view>
|
||||
<view class="date-range-container">
|
||||
<picker mode="date" :value="startDate === '至今' ? currentDate : startDate" :start="minDate" :end="currentDate" @change="bindStartDateChange">
|
||||
<view class="date-picker-item">
|
||||
<text class="date-label">开始时间</text>
|
||||
<view class="date-display" :class="{ 'current-text': startDate === '至今' }">{{startDate || '请选择开始时间'}}</view>
|
||||
</view>
|
||||
</picker>
|
||||
<view class="date-separator">至</view>
|
||||
<picker mode="date" :value="endDate === '至今' ? currentDate : endDate" :start="startDate === '至今' ? minDate : startDate" :end="currentDate" @change="bindEndDateChange">
|
||||
<view class="date-picker-item">
|
||||
<text class="date-label">结束时间</text>
|
||||
<view class="date-display" :class="{ 'current-text': endDate === '至今' }">{{endDate}}</view>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-input">
|
||||
<view class="input-titile">工作描述</view>
|
||||
<!-- <input class="input-con" placeholder="请输入工作描述" /> -->
|
||||
<textarea class="textarea-con" v-model="formData.description" placeholder-style="font-size: 16px" maxlength="500" placeholder="请输入工作描述"/>
|
||||
</view>
|
||||
</view>
|
||||
</AppLayout>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, inject, watch, ref, onMounted, computed } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import SelectJobs from '@/components/selectJobs/selectJobs.vue';
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
|
||||
const { $api, navTo, navBack, config } = inject('globalFunction');
|
||||
const { userInfo } = useUserStore();
|
||||
|
||||
// 页面参数
|
||||
const pageType = ref('add'); // add: 添加, edit: 编辑
|
||||
const editData = ref(null); // 编辑时的数据
|
||||
|
||||
// 获取当前日期
|
||||
const getDate = (options = {}) => {
|
||||
const date = new Date();
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
|
||||
if (options.format) {
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
return date;
|
||||
};
|
||||
|
||||
const currentDate = getDate({ format: true });
|
||||
const minDate = '1990-01-01'; // 开始时间最早只能选择到1990年
|
||||
|
||||
// 定义响应式数据
|
||||
const startDate = ref(''); // 开始时间需要选择
|
||||
const endDate = ref('至今'); // 结束时间默认为"至今"
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
companyName: '',
|
||||
position: '',
|
||||
userId: '', // 将在确认时动态获取
|
||||
startDate: '',
|
||||
endDate: '至今', // 设置默认值为"至今"
|
||||
description: ''
|
||||
});
|
||||
|
||||
// 页面加载时解析参数
|
||||
onLoad((options) => {
|
||||
console.log('页面参数:', options);
|
||||
|
||||
// 解析页面类型
|
||||
if (options.type) {
|
||||
pageType.value = options.type;
|
||||
}
|
||||
|
||||
// 如果是编辑模式,解析传递的数据
|
||||
if (options.type === 'edit' && options.data) {
|
||||
try {
|
||||
editData.value = JSON.parse(decodeURIComponent(options.data));
|
||||
console.log('编辑数据:', editData.value);
|
||||
|
||||
// 回显数据到表单
|
||||
if (editData.value) {
|
||||
formData.companyName = editData.value.companyName || '';
|
||||
formData.position = editData.value.position || '';
|
||||
formData.startDate = editData.value.startDate || '';
|
||||
formData.endDate = editData.value.endDate || '至今';
|
||||
formData.description = editData.value.description || '';
|
||||
|
||||
// 同步日期选择器的显示
|
||||
startDate.value = editData.value.startDate || '';
|
||||
endDate.value = editData.value.endDate || '至今';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析编辑数据失败:', error);
|
||||
$api.msg('数据解析失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
lfsalay: [2, 5, 10, 15, 20, 25, 30, 50],
|
||||
risalay: [2, 5, 10, 15, 20, 25, 30, 50], // 修复未定义的salay变量
|
||||
salayText: '',
|
||||
areaText: '',
|
||||
jobsText: []
|
||||
});
|
||||
|
||||
// 开始日期选择器变化事件
|
||||
const bindStartDateChange = (e) => {
|
||||
const selectedDate = e.detail.value;
|
||||
if (selectedDate === currentDate) {
|
||||
// 如果选择的是今天,设置为"至今"
|
||||
startDate.value = '至今';
|
||||
formData.startDate = '至今';
|
||||
} else {
|
||||
startDate.value = selectedDate;
|
||||
formData.startDate = selectedDate;
|
||||
}
|
||||
// 如果结束日期早于开始日期且结束日期不是"至今",清空结束日期
|
||||
if (endDate.value && endDate.value !== '至今' && endDate.value < startDate.value) {
|
||||
endDate.value = '至今';
|
||||
formData.endDate = '至今';
|
||||
}
|
||||
};
|
||||
|
||||
// 结束日期选择器变化事件
|
||||
const bindEndDateChange = (e) => {
|
||||
const selectedDate = e.detail.value;
|
||||
if (selectedDate === currentDate) {
|
||||
// 如果选择的是今天,设置为"至今"
|
||||
endDate.value = '至今';
|
||||
formData.endDate = '至今';
|
||||
} else {
|
||||
endDate.value = selectedDate;
|
||||
formData.endDate = selectedDate;
|
||||
}
|
||||
};
|
||||
|
||||
// 确认保存工作经历
|
||||
const handleConfirm = async () => {
|
||||
// 表单验证
|
||||
if (!formData.companyName.trim()) {
|
||||
$api.msg('请输入公司名称');
|
||||
return;
|
||||
}
|
||||
if (!formData.position.trim()) {
|
||||
$api.msg('请输入职位名称');
|
||||
return;
|
||||
}
|
||||
if (!formData.startDate) {
|
||||
$api.msg('请选择开始时间');
|
||||
return;
|
||||
}
|
||||
if (!formData.description.trim()) {
|
||||
$api.msg('请输入工作描述');
|
||||
return;
|
||||
}
|
||||
console.log(userInfo.userId)
|
||||
// 动态获取用户ID
|
||||
const currentUserId = userInfo.userId;
|
||||
if (!currentUserId) {
|
||||
$api.msg('用户信息获取失败,请重新登录');
|
||||
return;
|
||||
}
|
||||
|
||||
// 调试:打印表单数据
|
||||
console.log('表单数据:', formData);
|
||||
console.log('结束时间:', formData.endDate);
|
||||
|
||||
try {
|
||||
// 处理结束时间:如果是"至今",则使用当前日期
|
||||
const endDateValue = formData.endDate === '至今' ? currentDate : formData.endDate;
|
||||
console.log('处理后的结束时间:', endDateValue);
|
||||
|
||||
// 准备请求参数
|
||||
const params = {
|
||||
companyName: formData.companyName.trim(),
|
||||
position: formData.position.trim(),
|
||||
userId: currentUserId, // 使用动态获取的用户ID
|
||||
startDate: formData.startDate,
|
||||
endDate: endDateValue, // 使用处理后的结束时间
|
||||
description: formData.description.trim()
|
||||
};
|
||||
|
||||
console.log('请求参数:', params);
|
||||
console.log('页面类型:', pageType.value);
|
||||
|
||||
let resData;
|
||||
|
||||
// 根据页面类型调用不同的接口
|
||||
if (pageType.value === 'edit' && editData.value?.id) {
|
||||
// 编辑模式:调用更新接口
|
||||
resData = await $api.createRequest(`/app/userworkexperiences/${editData.value.id}`, params, 'put');
|
||||
console.log('编辑接口响应:', resData);
|
||||
} else {
|
||||
// 添加模式:调用新增接口
|
||||
resData = await $api.createRequest('/app/userworkexperiences/add', params, 'post');
|
||||
console.log('新增接口响应:', resData);
|
||||
}
|
||||
|
||||
if (resData.code === 200) {
|
||||
$api.msg(pageType.value === 'edit' ? '工作经历更新成功' : '工作经历保存成功');
|
||||
// 返回上一页
|
||||
navBack();
|
||||
} else {
|
||||
$api.msg(resData.msg || '操作失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存工作经历失败:', error);
|
||||
$api.msg('保存失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.content{
|
||||
padding: 28rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start
|
||||
height: calc(100% - 120rpx)
|
||||
}
|
||||
.content-input
|
||||
margin-bottom: 52rpx
|
||||
.input-titile
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #6A6A6A;
|
||||
.input-con
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
line-height: 80rpx;
|
||||
height: 80rpx;
|
||||
border-bottom: 2rpx solid #EBEBEB
|
||||
position: relative;
|
||||
.triangle::before
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: calc(50% - 2rpx);
|
||||
content: '';
|
||||
width: 4rpx;
|
||||
height: 18rpx;
|
||||
border-radius: 2rpx
|
||||
background: #697279;
|
||||
transform: translate(0, -50%) rotate(-45deg) ;
|
||||
.triangle::after
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 50%;
|
||||
content: '';
|
||||
width: 4rpx;
|
||||
height: 18rpx;
|
||||
border-radius: 2rpx
|
||||
background: #697279;
|
||||
transform: rotate(45deg)
|
||||
.textarea-con
|
||||
border: 2rpx solid #EBEBEB
|
||||
width: 95%
|
||||
height: 400rpx
|
||||
margin-top: 20rpx
|
||||
padding: 15rpx
|
||||
|
||||
.date-range-container
|
||||
display: flex
|
||||
align-items: flex-end
|
||||
justify-content: space-between
|
||||
margin-top: 20rpx
|
||||
position: relative
|
||||
border-bottom: 2rpx solid #EBEBEB
|
||||
|
||||
.date-picker-item
|
||||
flex: 1
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
|
||||
.date-label
|
||||
font-size: 24rpx
|
||||
color: #999999
|
||||
margin-bottom: 10rpx
|
||||
|
||||
.date-display
|
||||
width: 100%
|
||||
height: 80rpx
|
||||
line-height: 80rpx
|
||||
font-size: 32rpx
|
||||
color: #333333
|
||||
text-align: center
|
||||
position: relative
|
||||
|
||||
&.current-text
|
||||
color: #256BFA
|
||||
font-weight: 500
|
||||
|
||||
.date-separator
|
||||
position: absolute
|
||||
left: 50%
|
||||
bottom: 40rpx
|
||||
transform: translateX(-50%)
|
||||
font-size: 28rpx
|
||||
color: #666666
|
||||
background-color: #ffffff
|
||||
padding: 0 10rpx
|
||||
z-index: 2
|
||||
height: 20rpx
|
||||
line-height: 20rpx
|
||||
display: flex
|
||||
align-items: center
|
||||
// .content-sex
|
||||
// height: 110rpx;
|
||||
// display: flex
|
||||
// justify-content: space-between;
|
||||
// align-items: flex-start;
|
||||
// border-bottom: 2rpx solid #EBEBEB
|
||||
// margin-bottom: 52rpx
|
||||
// .sex-titile
|
||||
// line-height: 80rpx;
|
||||
// .sext-ri
|
||||
// display: flex
|
||||
// align-items: center;
|
||||
// .sext-box
|
||||
// height: 76rpx;
|
||||
// width: 152rpx;
|
||||
// text-align: center;
|
||||
// line-height: 80rpx;
|
||||
// border-radius: 12rpx 12rpx 12rpx 12rpx
|
||||
// border: 2rpx solid #E8EAEE;
|
||||
// margin-left: 28rpx
|
||||
// font-weight: 400;
|
||||
// font-size: 28rpx;
|
||||
// .sext-boxactive
|
||||
// color: #256BFA
|
||||
// background: rgba(37,107,250,0.1);
|
||||
// border: 2rpx solid #256BFA;
|
||||
// .next-btn
|
||||
// width: 100%;
|
||||
// height: 90rpx;
|
||||
// background: #256BFA;
|
||||
// border-radius: 12rpx 12rpx 12rpx 12rpx;
|
||||
// font-weight: 500;
|
||||
// font-size: 32rpx;
|
||||
// color: #FFFFFF;
|
||||
// text-align: center;
|
||||
// line-height: 90rpx
|
||||
// .input-nx
|
||||
// position: relative
|
||||
// border-bottom: 2rpx solid #EBEBEB
|
||||
// padding-bottom: 30rpx
|
||||
// display: flex
|
||||
// flex-wrap: wrap
|
||||
// .nx-item
|
||||
// padding: 20rpx 28rpx
|
||||
// width: fit-content
|
||||
// border-radius: 12rpx 12rpx 12rpx 12rpx;
|
||||
// border: 2rpx solid #E8EAEE;
|
||||
// margin-right: 24rpx
|
||||
// margin-top: 24rpx
|
||||
</style>
|
@@ -161,63 +161,111 @@ const { getDictData, oneDictData } = useDictStore();
|
||||
const isUploading = ref(false); // 上传中状态
|
||||
const uploadedResumeName = ref(''); // 已上传简历文件名
|
||||
const uploadedResumeUrl = ref(''); // 已上传
|
||||
const workExperiences = ref([
|
||||
{
|
||||
id: 1, // 唯一标识(实际项目用后端返回ID)
|
||||
companyName: 'XX科技有限公司', // 公司名称
|
||||
position: '前端开发工程师', // 职位
|
||||
startDate: '2021-07', // 开始日期(格式:YYYY-MM)
|
||||
endDate: '2023-09', // 结束日期(空/undefined 表示“至今”)
|
||||
description:
|
||||
'1. 负责公司小程序及H5页面开发,基于UniApp+Vue3技术栈;2. 优化前端性能,首屏加载时间减少30%;3. 参与封装通用组件库,提升团队开发效率。', // 工作描述
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
companyName: 'YY互联网公司',
|
||||
position: '实习前端工程师',
|
||||
startDate: '2020-12',
|
||||
endDate: '2021-06',
|
||||
description: '1. 协助完成后台管理系统页面开发;2. 修复页面兼容性问题,适配多浏览器;3. 整理前端开发文档。',
|
||||
},
|
||||
]);
|
||||
const workExperiences = ref([]); // 工作经历列表
|
||||
const isLoading = ref(false); // 加载状态
|
||||
|
||||
// 页面加载时可从接口拉取数据
|
||||
// 获取工作经历列表
|
||||
const getWorkExperiences = async () => {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
console.log('完整用户信息:', userInfo.value);
|
||||
|
||||
// 获取用户ID - 使用可选链操作符避免错误
|
||||
const userId = userInfo.value?.userId;
|
||||
console.log('用户ID:', userId);
|
||||
|
||||
if (!userId) {
|
||||
console.log('用户ID为空,等待用户信息加载...');
|
||||
// 如果用户ID为空,不执行任何操作,避免触发退出登录
|
||||
return;
|
||||
}
|
||||
|
||||
// 只传递userId参数
|
||||
console.log('请求参数:', { userId });
|
||||
|
||||
// 使用try-catch包装请求,避免自动退出登录
|
||||
try {
|
||||
// 参数拼接到URL后面
|
||||
const resData = await $api.createRequest(`/app/userworkexperiences/list?userId=${userId}`, {}, 'get');
|
||||
console.log('工作经历列表响应:', resData);
|
||||
|
||||
if (resData.code === 200 && resData.rows) {
|
||||
workExperiences.value = resData.rows;
|
||||
console.log('工作经历数据设置成功:', resData.rows);
|
||||
console.log('总数量:', resData.total);
|
||||
} else {
|
||||
console.log('接口返回非200状态或无数据:', resData);
|
||||
// 如果接口返回错误,不显示错误提示,避免用户困惑
|
||||
workExperiences.value = []; // 设置为空数组
|
||||
}
|
||||
} catch (requestError) {
|
||||
console.error('接口请求失败:', requestError);
|
||||
// 接口请求失败时,不显示错误提示,静默处理
|
||||
workExperiences.value = []; // 设置为空数组
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取工作经历失败:', error);
|
||||
// 静默处理错误,不显示错误提示
|
||||
workExperiences.value = [];
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 页面加载时获取数据
|
||||
onLoad(() => {
|
||||
// 实际项目中替换为接口请求:
|
||||
// getWorkExperiences().then(res => {
|
||||
// workExperiences.value = res.data;
|
||||
// }).catch(err => {
|
||||
// showToast({ title: '数据加载失败', icon: 'none' });
|
||||
// });
|
||||
// 延迟获取数据,确保用户信息完全加载
|
||||
setTimeout(() => {
|
||||
if (userInfo.value?.userId) {
|
||||
getWorkExperiences();
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// 页面显示时刷新数据
|
||||
onShow(() => {
|
||||
// 延迟获取数据,确保用户信息完全加载
|
||||
setTimeout(() => {
|
||||
if (userInfo.value?.userId) {
|
||||
getWorkExperiences();
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// 整体编辑/添加(跳转编辑页)
|
||||
const handleEditOrAdd = () => {
|
||||
// 跳转至“批量编辑”或“添加经历”页面(根据实际需求设计编辑页)
|
||||
// router.push({
|
||||
// path: '/pages/workExperience/edit',
|
||||
// query: { type: workExperiences.value.length > 0 ? 'edit' : 'add' }
|
||||
// });
|
||||
// 跳转到添加经历页面,传递添加标识
|
||||
navTo('/packageA/pages/addWorkExperience/addWorkExperience?type=add');
|
||||
};
|
||||
|
||||
// 编辑单个经历
|
||||
const handleEditItem = (item) => {
|
||||
// 跳转至单个编辑页,并携带当前经历数据
|
||||
// router.push({
|
||||
// path: '/pages/workExperience/editItem',
|
||||
// query: { item: JSON.stringify(item) } // 复杂数据需转JSON字符串(UniApp路由query不支持直接传对象)
|
||||
// });
|
||||
// 跳转到编辑页面,传递编辑标识和数据
|
||||
const itemData = encodeURIComponent(JSON.stringify(item));
|
||||
navTo(`/packageA/pages/addWorkExperience/addWorkExperience?type=edit&data=${itemData}`);
|
||||
};
|
||||
|
||||
// 删除单个经历(带确认弹窗)
|
||||
const handleDeleteItem = (index) => {
|
||||
const handleDeleteItem = async (item, index) => {
|
||||
uni.showModal({
|
||||
title: '确认删除',
|
||||
content: '此操作将删除该工作经历,是否继续?',
|
||||
success: (res) => {
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
workExperiences.value.splice(index, 1); // 删除本地数据
|
||||
$api.msg('删除成功');
|
||||
try {
|
||||
// 调用删除接口
|
||||
const deleteRes = await $api.createRequest(`/cms/userworkexperiences/${item.id}`, {}, 'delete');
|
||||
if (deleteRes.code === 200) {
|
||||
workExperiences.value.splice(index, 1); // 删除本地数据
|
||||
$api.msg('删除成功');
|
||||
} else {
|
||||
$api.msg(deleteRes.msg || '删除失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除工作经历失败:', error);
|
||||
$api.msg('删除失败,请重试');
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@@ -65,6 +65,14 @@
|
||||
"navigationBarTitleText": "",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "packageA/pages/addWorkExperience/addWorkExperience",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "添加工作经历",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
|
||||
],
|
||||
|
@@ -370,7 +370,57 @@ const sendMessage = (text) => {
|
||||
filesList.value = [];
|
||||
useChatGroupDBStore()
|
||||
.getStearm(values, normalArr, scrollToBottom, {
|
||||
onComplete: () => console.log('Display complete'),
|
||||
onDataReceived: (data, message, index) => {
|
||||
// 流式朗读:只在内容足够长且包含完整信息时才开始朗读
|
||||
if (!message.self && message.displayText && message.displayText.trim()) {
|
||||
// 检查是否已经开始朗读这条消息
|
||||
if (speechIndex.value !== index) {
|
||||
// 延迟TTS,等待内容更完整
|
||||
// 只有在内容长度超过50个字符或者包含岗位信息时才开始朗读
|
||||
const hasJobInfo = message.displayText.includes('```job-json') ||
|
||||
message.displayText.includes('岗位') ||
|
||||
message.displayText.includes('公司') ||
|
||||
message.displayText.includes('薪资');
|
||||
|
||||
if (message.displayText.length > 50 || hasJobInfo) {
|
||||
console.log('🎵 Starting streaming TTS for message index:', index);
|
||||
console.log('📝 Current text length:', message.displayText.length);
|
||||
console.log('📝 Has job info:', hasJobInfo);
|
||||
|
||||
// 开始朗读当前消息
|
||||
speechIndex.value = index;
|
||||
readMarkdown(message.displayText, index);
|
||||
} else {
|
||||
console.log('⏳ Waiting for more content before TTS, current length:', message.displayText.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onComplete: () => {
|
||||
console.log('🎯 onComplete callback triggered');
|
||||
console.log('📊 Messages array length:', messages.value.length);
|
||||
|
||||
// 确保最后一条AI消息的朗读完成
|
||||
const lastMessageIndex = messages.value.length - 1;
|
||||
if (lastMessageIndex >= 0) {
|
||||
const lastMessage = messages.value[lastMessageIndex];
|
||||
if (!lastMessage.self && lastMessage.displayText && lastMessage.displayText.trim()) {
|
||||
console.log('🎵 Final TTS for complete message');
|
||||
console.log('📝 Final text length:', lastMessage.displayText.length);
|
||||
console.log('📝 Final text preview:', lastMessage.displayText.substring(0, 100) + '...');
|
||||
|
||||
// 停止当前的朗读(如果有的话)
|
||||
if (isSpeaking.value) {
|
||||
console.log('🛑 Stopping current TTS to start final complete TTS');
|
||||
cancelAudio();
|
||||
}
|
||||
|
||||
// 开始朗读完整的内容
|
||||
speechIndex.value = lastMessageIndex;
|
||||
readMarkdown(lastMessage.displayText, lastMessageIndex);
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
getGuess();
|
||||
@@ -620,21 +670,71 @@ function confirmFeeBack(value) {
|
||||
});
|
||||
}
|
||||
|
||||
// 防抖定时器
|
||||
let ttsDebounceTimer = null;
|
||||
|
||||
function readMarkdown(value, index) {
|
||||
speechIndex.value = index;
|
||||
if (speechIndex.value !== index) {
|
||||
console.log('🎤 readMarkdown called');
|
||||
console.log('📝 Text to speak:', value ? value.substring(0, 100) + '...' : 'No text');
|
||||
console.log('🔢 Index:', index);
|
||||
console.log('🔢 Current speechIndex:', speechIndex.value);
|
||||
console.log('⏸️ Is paused:', isPaused.value);
|
||||
console.log('🔊 Is speaking:', isSpeaking.value);
|
||||
|
||||
// 清除之前的防抖定时器
|
||||
if (ttsDebounceTimer) {
|
||||
clearTimeout(ttsDebounceTimer);
|
||||
}
|
||||
|
||||
// 如果当前正在播放其他消息,先停止
|
||||
if (speechIndex.value !== index && speechIndex.value !== 0) {
|
||||
console.log('🛑 Stopping current speech and starting new one');
|
||||
speechIndex.value = index;
|
||||
speak(value);
|
||||
return;
|
||||
}
|
||||
if (isPaused.value) {
|
||||
|
||||
speechIndex.value = index;
|
||||
|
||||
// 如果当前正在播放且暂停了,直接恢复
|
||||
if (isPaused.value && isSpeaking.value) {
|
||||
console.log('▶️ Resuming paused speech');
|
||||
resume();
|
||||
} else {
|
||||
speak(value);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果当前正在播放且没有暂停,不需要重新开始
|
||||
if (isSpeaking.value && !isPaused.value) {
|
||||
console.log('🔊 Already speaking, no need to restart');
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用防抖,避免频繁调用TTS
|
||||
ttsDebounceTimer = setTimeout(() => {
|
||||
console.log('🎵 Starting new speech');
|
||||
console.log('🎵 Calling speak function with text length:', value ? value.length : 0);
|
||||
try {
|
||||
speak(value);
|
||||
console.log('✅ Speak function called successfully');
|
||||
} catch (error) {
|
||||
console.error('❌ Error calling speak function:', error);
|
||||
}
|
||||
}, 300); // 300ms防抖延迟
|
||||
}
|
||||
function stopMarkdown(value, index) {
|
||||
pause(value);
|
||||
console.log('⏸️ stopMarkdown called for index:', index);
|
||||
console.log('🔢 Current speechIndex:', speechIndex.value);
|
||||
console.log('🔊 Is speaking:', isSpeaking.value);
|
||||
console.log('⏸️ Is paused:', isPaused.value);
|
||||
|
||||
// 清除防抖定时器
|
||||
if (ttsDebounceTimer) {
|
||||
clearTimeout(ttsDebounceTimer);
|
||||
ttsDebounceTimer = null;
|
||||
}
|
||||
|
||||
speechIndex.value = index;
|
||||
pause();
|
||||
}
|
||||
function refreshMarkdown(index) {
|
||||
if (isTyping.value) {
|
||||
|
@@ -96,7 +96,8 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="falls-card-company">
|
||||
{{ config.appInfo.areaName }}
|
||||
<!-- {{ config.appInfo.areaName }} -->
|
||||
<!-- {{ job.jobLocation }} -->
|
||||
<dict-Label dictType="area" :value="job.jobLocationAreaCode"></dict-Label>
|
||||
</view>
|
||||
<view class="falls-card-pepleNumber">
|
||||
@@ -341,7 +342,9 @@ function choosePosition(index) {
|
||||
}
|
||||
|
||||
function handelHostestSearch(val) {
|
||||
console.log(val.value);
|
||||
pageState.search.order = val.value;
|
||||
pageState.search.jobType = val.value === 3 ? 1 : 0;
|
||||
if (state.tabIndex === 'all') {
|
||||
getJobRecommend('refresh');
|
||||
} else {
|
||||
|
@@ -288,9 +288,12 @@ function complete() {
|
||||
if (result.valid) {
|
||||
$api.createRequest('/app/user/resume', fromValue, 'post').then((resData) => {
|
||||
$api.msg('完成');
|
||||
getUserResume();
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index',
|
||||
// 获取用户信息并存储到store中
|
||||
getUserResume().then((userInfo) => {
|
||||
console.log('用户信息已存储到store:', userInfo);
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index',
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
|
@@ -108,8 +108,11 @@ const useUserStore = defineStore("user", () => {
|
||||
|
||||
const setUserInfo = (values) => {
|
||||
userInfo.value = values.data;
|
||||
resume.value = values.data; // 将用户信息同时存储到resume中
|
||||
// role.value = values.role;
|
||||
hasLogin.value = true;
|
||||
// 持久化存储用户信息到本地缓存
|
||||
uni.setStorageSync('userInfo', values.data);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +130,21 @@ const useUserStore = defineStore("user", () => {
|
||||
seesionId.value = seesionIdVal
|
||||
}
|
||||
|
||||
// 从本地缓存恢复用户信息
|
||||
const restoreUserInfo = () => {
|
||||
const cachedUserInfo = uni.getStorageSync('userInfo');
|
||||
const cachedToken = uni.getStorageSync('token');
|
||||
if (cachedUserInfo && cachedToken) {
|
||||
userInfo.value = cachedUserInfo;
|
||||
resume.value = cachedUserInfo;
|
||||
token.value = cachedToken;
|
||||
hasLogin.value = true;
|
||||
Completion.value = getResumeCompletionPercentage(cachedUserInfo);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 导入
|
||||
return {
|
||||
hasLogin,
|
||||
@@ -139,7 +157,8 @@ const useUserStore = defineStore("user", () => {
|
||||
getUserResume,
|
||||
initSeesionId,
|
||||
seesionId,
|
||||
Completion
|
||||
Completion,
|
||||
restoreUserInfo
|
||||
}
|
||||
})
|
||||
|
||||
|
@@ -107,7 +107,7 @@ const useChatGroupDBStore = defineStore("messageGroup", () => {
|
||||
return await baseDB.db.add(massageName.value, payload);
|
||||
}
|
||||
|
||||
async function getStearm(text, fileUrls = [], progress) {
|
||||
async function getStearm(text, fileUrls = [], progress, options = {}) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
@@ -163,6 +163,11 @@ const useChatGroupDBStore = defineStore("messageGroup", () => {
|
||||
...newMsg
|
||||
};
|
||||
progress && progress();
|
||||
|
||||
// 调用外部传入的onDataReceived回调
|
||||
if (options.onDataReceived) {
|
||||
options.onDataReceived(data, newMsg, index);
|
||||
}
|
||||
}
|
||||
|
||||
function onError(error) {
|
||||
@@ -177,6 +182,10 @@ const useChatGroupDBStore = defineStore("messageGroup", () => {
|
||||
toggleTyping(false);
|
||||
window.removeEventListener("unload", handleUnload);
|
||||
handleUnload();
|
||||
// 调用外部传入的onComplete回调
|
||||
if (options.onComplete) {
|
||||
options.onComplete();
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"hash": "64b0126f",
|
||||
"configHash": "c3ada311",
|
||||
"lockfileHash": "5d26acb0",
|
||||
"browserHash": "6c46b053",
|
||||
"hash": "6baa819c",
|
||||
"configHash": "7c25a4d3",
|
||||
"lockfileHash": "e3b0c442",
|
||||
"browserHash": "1418816b",
|
||||
"optimized": {},
|
||||
"chunks": {}
|
||||
}
|
@@ -31,13 +31,19 @@ export default function StreamRequest(url, data = {}, onDataReceived, onError, o
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
|
||||
let buffer = "";
|
||||
let retryCount = 0;
|
||||
const maxRetries = 3;
|
||||
|
||||
while (true) {
|
||||
const {
|
||||
done,
|
||||
value
|
||||
} = await reader.read();
|
||||
|
||||
if (done) break;
|
||||
if (done) {
|
||||
console.log("📡 Stream reading completed");
|
||||
break;
|
||||
}
|
||||
|
||||
buffer += decoder.decode(value, {
|
||||
stream: true
|
||||
@@ -45,6 +51,9 @@ export default function StreamRequest(url, data = {}, onDataReceived, onError, o
|
||||
|
||||
let lines = buffer.split("\n");
|
||||
buffer = lines.pop(); // 可能是不完整的 JSON 片段,留待下次解析
|
||||
|
||||
console.log(`📦 Processing ${lines.length} lines, buffer length: ${buffer.length}`);
|
||||
|
||||
for (let line of lines) {
|
||||
if (line.startsWith("data: ")) {
|
||||
const jsonData = line.slice(6).trim();
|
||||
@@ -55,20 +64,84 @@ export default function StreamRequest(url, data = {}, onDataReceived, onError, o
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedData = JSON.parse(jsonData);
|
||||
const content = parsedData?.choices?.[0]?.delta?.content ??
|
||||
parsedData?.choices?.[0]?.delta?.reasoning_content ??
|
||||
"";
|
||||
if (content) {
|
||||
onDataReceived && onDataReceived(content);
|
||||
// 检查JSON数据是否完整
|
||||
if (jsonData && jsonData.trim() && jsonData !== "[DONE]") {
|
||||
const parsedData = JSON.parse(jsonData);
|
||||
|
||||
// 处理标准的choices格式
|
||||
if (parsedData?.choices?.[0]?.delta?.content) {
|
||||
const content = parsedData.choices[0].delta.content;
|
||||
if (content) {
|
||||
onDataReceived && onDataReceived(content);
|
||||
}
|
||||
}
|
||||
// 处理reasoning_content
|
||||
else if (parsedData?.choices?.[0]?.delta?.reasoning_content) {
|
||||
const content = parsedData.choices[0].delta.reasoning_content;
|
||||
if (content) {
|
||||
onDataReceived && onDataReceived(content);
|
||||
}
|
||||
}
|
||||
// 处理tool响应
|
||||
else if (parsedData?.tool?.response) {
|
||||
const content = parsedData.tool.response;
|
||||
if (content) {
|
||||
onDataReceived && onDataReceived(content);
|
||||
}
|
||||
}
|
||||
// 处理其他格式的数据(如jobs_array_number, durationSeconds等)
|
||||
else {
|
||||
console.log("📦 收到非内容数据:", Object.keys(parsedData));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("JSON 解析失败:", e, "原始数据:", jsonData);
|
||||
console.error("JSON 解析失败:", e.message, "原始数据长度:", jsonData.length, "数据预览:", jsonData.substring(0, 100) + "...");
|
||||
// 不抛出错误,继续处理下一个数据块
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理剩余的缓冲区数据
|
||||
if (buffer.trim()) {
|
||||
console.log("📦 Processing remaining buffer:", buffer.substring(0, 100) + "...");
|
||||
const lines = buffer.split("\n");
|
||||
for (let line of lines) {
|
||||
if (line.startsWith("data: ")) {
|
||||
const jsonData = line.slice(6).trim();
|
||||
if (jsonData && jsonData !== "[DONE]") {
|
||||
try {
|
||||
const parsedData = JSON.parse(jsonData);
|
||||
|
||||
// 处理标准的choices格式
|
||||
if (parsedData?.choices?.[0]?.delta?.content) {
|
||||
const content = parsedData.choices[0].delta.content;
|
||||
if (content) {
|
||||
onDataReceived && onDataReceived(content);
|
||||
}
|
||||
}
|
||||
// 处理reasoning_content
|
||||
else if (parsedData?.choices?.[0]?.delta?.reasoning_content) {
|
||||
const content = parsedData.choices[0].delta.reasoning_content;
|
||||
if (content) {
|
||||
onDataReceived && onDataReceived(content);
|
||||
}
|
||||
}
|
||||
// 处理tool响应
|
||||
else if (parsedData?.tool?.response) {
|
||||
const content = parsedData.tool.response;
|
||||
if (content) {
|
||||
onDataReceived && onDataReceived(content);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("处理剩余数据时JSON解析失败:", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onComplete && onComplete();
|
||||
resolve();
|
||||
} catch (error) {
|
||||
|
Reference in New Issue
Block a user