diff --git a/components/md-render/md-render.vue b/components/md-render/md-render.vue
index 4bc7762..9d02c3e 100644
--- a/components/md-render/md-render.vue
+++ b/components/md-render/md-render.vue
@@ -263,15 +263,43 @@ ol {
diff --git a/packageA/pages/myResume/myResume.vue b/packageA/pages/myResume/myResume.vue
index cdc0c94..ea4507b 100644
--- a/packageA/pages/myResume/myResume.vue
+++ b/packageA/pages/myResume/myResume.vue
@@ -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('删除失败,请重试');
+ }
}
},
});
diff --git a/pages.json b/pages.json
index 6b48c5b..d1ba1d1 100644
--- a/pages.json
+++ b/pages.json
@@ -65,6 +65,14 @@
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
+ },
+ {
+ "path" : "packageA/pages/addWorkExperience/addWorkExperience",
+ "style" :
+ {
+ "navigationBarTitleText" : "添加工作经历",
+ "navigationStyle": "custom"
+ }
}
],
diff --git a/pages/chat/components/ai-paging.vue b/pages/chat/components/ai-paging.vue
index d08a0ae..4621a24 100644
--- a/pages/chat/components/ai-paging.vue
+++ b/pages/chat/components/ai-paging.vue
@@ -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) {
diff --git a/pages/index/components/index-one.vue b/pages/index/components/index-one.vue
index 9948e24..987f723 100644
--- a/pages/index/components/index-one.vue
+++ b/pages/index/components/index-one.vue
@@ -96,7 +96,8 @@
- {{ config.appInfo.areaName }}
+
+
@@ -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 {
diff --git a/pages/login/login.vue b/pages/login/login.vue
index f9593ec..3e0f4c0 100644
--- a/pages/login/login.vue
+++ b/pages/login/login.vue
@@ -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 {
diff --git a/stores/useUserStore.js b/stores/useUserStore.js
index c76adc0..882f15b 100644
--- a/stores/useUserStore.js
+++ b/stores/useUserStore.js
@@ -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
}
})
diff --git a/stores/userChatGroupStore.js b/stores/userChatGroupStore.js
index 8b688fe..51c9a8b 100644
--- a/stores/userChatGroupStore.js
+++ b/stores/userChatGroupStore.js
@@ -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();
}
diff --git a/unpackage/dist/cache/.vite/deps/_metadata.json b/unpackage/dist/cache/.vite/deps/_metadata.json
index 55b3d2d..1b39def 100644
--- a/unpackage/dist/cache/.vite/deps/_metadata.json
+++ b/unpackage/dist/cache/.vite/deps/_metadata.json
@@ -1,8 +1,8 @@
{
- "hash": "64b0126f",
- "configHash": "c3ada311",
- "lockfileHash": "5d26acb0",
- "browserHash": "6c46b053",
+ "hash": "6baa819c",
+ "configHash": "7c25a4d3",
+ "lockfileHash": "e3b0c442",
+ "browserHash": "1418816b",
"optimized": {},
"chunks": {}
}
\ No newline at end of file
diff --git a/utils/streamRequest.js b/utils/streamRequest.js
index e6cb6bf..6a5471d 100644
--- a/utils/streamRequest.js
+++ b/utils/streamRequest.js
@@ -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) {