diff --git a/.env.production b/.env.production
index c2f9efd..6d79b3c 100644
--- a/.env.production
+++ b/.env.production
@@ -9,8 +9,8 @@ VITE_PUBLIC_PATH = /aixiaocaishen
VITE_USE_MOCK = true
#新数据接口
-VITE_APP_API_BASE_URL = https://api.homilychart.com/link
-# VITE_APP_API_BASE_URL = "http://39.101.133.168:8828/link"
+# VITE_APP_API_BASE_URL = https://api.homilychart.com/link
+VITE_APP_API_BASE_URL = "http://39.101.133.168:8828/link"
VITE_APP_IMG_API_BASE_URL = "https://api.homilychart.com/hljw/api/aws/upload"
#MJ API
diff --git a/src/api/AIxiaocaishen.js b/src/api/AIxiaocaishen.js
index 0cbce31..2b6ee70 100644
--- a/src/api/AIxiaocaishen.js
+++ b/src/api/AIxiaocaishen.js
@@ -99,12 +99,13 @@ export const getAnnouncementAPI = function () {
// 获取用户次数接口
export const getUserCountAPI = function (params) {
return request({
- url: `${APIurl}/api/ai_god/userUsageInfo`,
+ // 'http://39.101.133.168:8828/link/api/aiEmotion/client/getRemainNum',
+ url: `${APIurl}/api/aiEmotion/client/getRemainNum`,
method: "POST",
- data: new URLSearchParams(params),
- headers: {
- "Content-Type": "application/x-www-form-urlencoded",
- },
+ data: params,
+ // headers: {
+ // "Content-Type": "application/x-www-form-urlencoded",
+ // },
});
};
// 推荐问题/每日复盘/小财神简介点击事件接口
diff --git a/src/store/audio.js b/src/store/audio.js
index a33154c..7f5d82a 100644
--- a/src/store/audio.js
+++ b/src/store/audio.js
@@ -9,7 +9,10 @@ export const useAudioStore = defineStore('audio', {
lastVoiceState: null,
ttsUrl:'',
isNewInstance: false, // 新增是否是新实例的标志
- nowSound:''
+ nowSound:'',
+ currentAudioUrl: '', // 当前音频URL
+ isPaused: false, // 是否处于暂停状态
+ duration: 0 // 音频总时长
}),
actions: {
// 设置音频实例
@@ -19,19 +22,67 @@ export const useAudioStore = defineStore('audio', {
// 播放控制
play() {
if (this.soundInstance) {
+ if (this.isPaused && this.playbackPosition > 0) {
+ // 从暂停位置继续播放
+ this.soundInstance.seek(this.playbackPosition)
+ }
this.soundInstance.play()
this.isPlaying = true
+ this.isPaused = false
}
},
// 暂停控制
pause() {
- if (this.soundInstance) {
+ if (this.soundInstance && this.isPlaying) {
+ // 保存当前播放位置
+ this.playbackPosition = this.soundInstance.seek() || 0
this.soundInstance.pause()
this.isPlaying = false
+ this.isPaused = true
+ }
+ },
+ // 停止播放
+ stop() {
+ if (this.soundInstance) {
+ this.soundInstance.stop()
+ this.isPlaying = false
+ this.isPaused = false
+ this.playbackPosition = 0
}
},
+ // 切换播放/暂停
+ togglePlayPause() {
+ if (this.isPlaying) {
+ this.pause()
+ } else {
+ this.play()
+ }
+ },
+ // 设置当前音频URL
+ setCurrentAudioUrl(url) {
+ if (this.currentAudioUrl !== url) {
+ // 如果是新的音频,重置播放状态
+ this.stop()
+ this.currentAudioUrl = url
+ this.playbackPosition = 0
+ this.isPaused = false
+ }
+ },
+ // 语音开关控制
toggleVoice() {
this.isVoiceEnabled = !this.isVoiceEnabled
+ if (!this.isVoiceEnabled) {
+ // 关闭语音时停止当前播放
+ this.stop()
+ }
+ },
+ // 重置音频状态
+ resetAudioState() {
+ this.stop()
+ this.currentAudioUrl = ''
+ this.ttsUrl = ''
+ this.soundInstance = null
+ this.nowSound = ''
}
}
})
diff --git a/src/store/chat.js b/src/store/chat.js
index 6da35cb..dbf5685 100644
--- a/src/store/chat.js
+++ b/src/store/chat.js
@@ -11,9 +11,10 @@ export const useChatStore = defineStore('chat', {
actions: {
async getUserCount() {
const result = await getUserCountAPI({
- token: localStorage.getItem('localToken')
+ token: localStorage.getItem('localToken'),
+ source: '1'
})
- this.UserCount = result.data.hasCount
+ this.UserCount = result.data
},
setLoading(status) {
this.isLoading = status
diff --git a/src/views/AIchat.vue b/src/views/AIchat.vue
index bdbdcdb..8085cf8 100644
--- a/src/views/AIchat.vue
+++ b/src/views/AIchat.vue
@@ -146,6 +146,69 @@ const pauseAudio = () => {
}
};
+// 音频轮流播放方法
+const playAudioSequence = (audioUrls) => {
+ if (!audioUrls || audioUrls.length === 0) {
+ console.warn("音频URL列表为空,跳过播放");
+ return;
+ }
+
+ let currentIndex = 0;
+
+ const playNext = () => {
+ if (currentIndex >= audioUrls.length) {
+ console.log("所有音频播放完成");
+ return;
+ }
+
+ const currentUrl = audioUrls[currentIndex];
+ console.log(`正在播放第${currentIndex + 1}个音频:`, currentUrl);
+
+ // 停止当前播放的音频
+ if (audioStore.nowSound) {
+ audioStore.nowSound.stop();
+ }
+
+ const sound = new Howl({
+ src: [currentUrl],
+ html5: true,
+ format: ["mp3", "acc"],
+ rate: 1.2,
+ onplay: () => {
+ audioStore.isPlaying = true;
+ console.log(`开始播放音频 ${currentIndex + 1}`);
+ },
+ onend: () => {
+ audioStore.isPlaying = false;
+ console.log(`音频 ${currentIndex + 1} 播放完成`);
+ currentIndex++;
+ // 播放下一个音频
+ setTimeout(() => {
+ playNext();
+ }, 500); // 间隔500ms播放下一个
+ },
+ onstop: () => {
+ audioStore.isPlaying = false;
+ },
+ onloaderror: (id, err) => {
+ console.error(`音频 ${currentIndex + 1} 加载失败:`, err);
+ currentIndex++;
+ // 跳过失败的音频,播放下一个
+ setTimeout(() => {
+ playNext();
+ }, 100);
+ }
+ });
+
+ audioStore.nowSound = sound;
+ audioStore.setAudioInstance(sound);
+ sound.play();
+ };
+
+ // 开始播放第一个音频
+ playNext();
+};
+
// 获取消息
const chatMsg = computed(() => chatStore.messages);
const props = defineProps({
@@ -494,6 +557,18 @@ watch(
const result23 = await dbqbSecondThreeAPI(params2);
const result24 = await dbqbSecondFourAPI(params2);
+ // 收集所有音频URL
+ const audioUrls = [];
+ if (result21.data.url) audioUrls.push(result21.data.url.trim());
+ if (result22.data.url) audioUrls.push(result22.data.url.trim());
+ if (result23.data.url) audioUrls.push(result23.data.url.trim());
+ if (result24.data.url) audioUrls.push(result24.data.url.trim());
+
+ // 开始轮流播放音频
+ if (audioUrls.length > 0 && audioStore.isVoiceEnabled) {
+ playAudioSequence(audioUrls);
+ }
+
const katexRegex = /\$\$(.*?)\$\$/g;
// 删除正在为您生成信息
chatStore.messages.pop();
diff --git a/src/views/AiEmotion.vue b/src/views/AiEmotion.vue
index 19d40ef..1aff158 100644
--- a/src/views/AiEmotion.vue
+++ b/src/views/AiEmotion.vue
@@ -183,11 +183,43 @@ import blueBorderImg from '@/assets/img/AiEmotion/blueBorder.png' //导入蓝色
import { ElMessage } from 'element-plus';
import { useEmotionStore } from '@/store/emotion'; // 导入Pinia store
import { useAudioStore } from '@/store/audio.js'; // 导入音频store
+import { useChatStore } from '@/store/chat.js'; // 导入聊天store
import { Howl, Howler } from 'howler'; // 导入音频播放库
import { reactive } from 'vue';
+import { marked } from 'marked'; // 引入marked库
// 使用Pinia store
const emotionStore = useEmotionStore();
const audioStore = useAudioStore();
+const chatStore = useChatStore();
+
+// 处理refuse数据的函数
+function processRefuseMessage(refuseData) {
+ if (!refuseData) return '未知错误';
+
+ // 如果refuse数据包含Markdown格式,进行转换
+ try {
+ // 配置marked选项
+ marked.setOptions({
+ breaks: true, // 支持换行符转换为
+ gfm: true, // 启用 GitHub Flavored Markdown
+ sanitize: false, // 不清理 HTML
+ smartLists: true, // 智能列表
+ smartypants: true, // 智能标点符号
+ xhtml: false, // 不使用 XHTML 输出
+ });
+
+ // 将Markdown转换为HTML
+ const htmlContent = marked(refuseData);
+
+ // 移除HTML标签,只保留纯文本用于ElMessage显示
+ const tempDiv = document.createElement('div');
+ tempDiv.innerHTML = htmlContent;
+ return tempDiv.textContent || tempDiv.innerText || refuseData;
+ } catch (error) {
+ console.error('处理refuse消息时出错:', error);
+ return refuseData;
+ }
+}
// 组件引用
const marketTemperatureRef = ref(null); // 引用市场温度计组件
@@ -357,7 +389,7 @@ watch(currentStock, (newStock) => {
nextTick(() => {
renderCharts(newStock.apiData);
- console.log('0000000000000000000000000',newStock.apiData)
+ console.log('0000000000000000000000000', newStock.apiData)
// 检查场景应用部分是否已经在视口中,如果是则立即触发效果
setTimeout(() => {
if (scenarioApplicationRef.value && parsedConclusion.value) {
@@ -663,8 +695,11 @@ function playAudio(url) {
console.log('开始创建音频实例...');
try {
+ // 设置当前音频URL
+ audioStore.setCurrentAudioUrl(url);
+
// 停止之前的音频
- if (audioStore.nowSound) {
+ if (audioStore.nowSound && audioStore.nowSound.playing()) {
audioStore.nowSound.stop();
}
@@ -675,19 +710,35 @@ function playAudio(url) {
format: ['mp3', 'wav'],
onplay: () => {
isAudioPlaying.value = true;
+ audioStore.isPlaying = true;
console.log('开始播放场景应用语音');
},
onend: () => {
isAudioPlaying.value = false;
+ audioStore.isPlaying = false;
+ audioStore.isPaused = false;
+ audioStore.playbackPosition = 0;
console.log('场景应用语音播放结束');
},
onstop: () => {
isAudioPlaying.value = false;
+ audioStore.isPlaying = false;
console.log('场景应用语音播放停止');
},
+ onpause: () => {
+ isAudioPlaying.value = false;
+ audioStore.isPlaying = false;
+ console.log('场景应用语音播放暂停');
+ },
onerror: (error) => {
isAudioPlaying.value = false;
+ audioStore.isPlaying = false;
console.error('音频播放错误:', error);
+ },
+ onload: () => {
+ // 音频加载完成,获取时长
+ audioStore.duration = newSound.duration();
+ console.log('音频加载完成,时长:', audioStore.duration);
}
});
@@ -726,14 +777,18 @@ async function handleSendMessage(input) {
console.log("发送内容:", input);
// 检查用户输入内容是否为空
+ if (!input || !input.trim()) {
+ ElMessage.warning("输入内容不能为空");
+ return;
+ }
+ // 触发图片旋转
+ isRotating.value = true;
+ // 设置加载状态,隐藏图表页面
+ isLoading.value = true;
+
if (input.trim()) {
const userMessage = reactive({ sender: 'user', text: input });
messages.value.push(userMessage);
- // 设置加载状态,隐藏图表页面
- isLoading.value = true;
- isPageLoaded.value = false;
- // 触发图片旋转
- isRotating.value = true;
try {
// 用来调用工作流接口的参数
@@ -750,20 +805,21 @@ async function handleSendMessage(input) {
aibullPrivilegeState: "1",
aigoldBullPrivilegeS: "1",
airadarPrivilegeStat: "1",
- marketList: "hk,cn,usa,my,sg,vi,in,gb",
+ marketList: "hk,cn,can,usa,my,sg,vi,in,gb",
},
};
const result = await getReplyAPI(params);
const response = await result.json(); // 解析返回的 JSON 数据
- console.log("工作流接口返回数据:", response);
-
// 解析 data 字段
const parsedData = JSON.parse(response.data); // 将字符串形式的 JSON 转换为对象
console.log("解析后的数据:", parsedData);
if (parsedData && parsedData.market && parsedData.code) {
console.log("工作流接口返回股票信息:", parsedData);
+
+ isPageLoaded.value = false;
+
// 调用第二个工作流接口
const conclusionParams = {
content: input.trim(),
@@ -776,12 +832,6 @@ async function handleSendMessage(input) {
code: parsedData.code,
market: parsedData.market,
};
- console.log('======================================')
-
- // 取消自动滚动效果
- // console.log('第二个工作流接口开始调用,立即开始缓慢滚动');
- // startAutoScroll();
-
// 同时调用第二个数据流接口和fetchData方法
const [conclusionResult, fetchDataResult] = await Promise.all([
getConclusionAPI(conclusionParams),
@@ -797,22 +847,21 @@ async function handleSendMessage(input) {
conclusionData.value = conclusionResponse.data;
// 将结论数据存储到store中的当前激活股票
emotionStore.updateActiveStockConclusion(conclusionResponse.data);
- console.log("结论数据已存储到响应式变量和store中:", conclusionData.value);
}
-
- console.log('------------------------------------')
-
} else {
- ElMessage.error('工作流接口未返回非股票信息');
+ // 不是股票信息,显示refuse信息,但不隐藏页面数据
+ ElMessage.error(processRefuseMessage(parsedData.refuse));
+ return; // 直接返回,不进行后续处理
}
} catch (error) {
ElMessage.error('请求工作流接口失败,请检查网络连接');
+ return; // 请求失败时直接返回
} finally {
- // 停止图片旋转
- isRotating.value = false;
+ // 停止图片旋转(只有在设置了旋转状态时才需要停止)
+ if (isRotating.value) {
+ isRotating.value = false;
+ }
}
- } else {
- ElMessage.error('消息发送失败,请检查网络连接');
}
}
@@ -857,17 +906,13 @@ async function fetchData(code, market, stockName, queryText) {
conclusionData: conclusionData.value, // 包含结论数据
timestamp: new Date().toISOString()
};
-
// 将股票数据添加到store中
emotionStore.addStock(stockData);
-
- console.log('股票数据已添加到store');
-
} else {
- ElMessage.error('获取接口数据失败');
+ ElMessage.error('请求失败,请检查网络连接');
}
} catch (error) {
- ElMessage.error('获取接口数据失败。。。');
+ ElMessage.error('请求失败,请检查网络连接');
}
}
@@ -1115,7 +1160,15 @@ function triggerAutoScroll() {
}
// 页面挂载完成后触发图片旋转和设置滚动监听
-onMounted(() => {
+onMounted(async () => {
+ // 确保获取用户次数
+ try {
+ await chatStore.getUserCount();
+ console.log('情绪大模型页面:用户次数获取成功');
+ } catch (error) {
+ console.error('情绪大模型页面:获取用户次数失败', error);
+ }
+
startImageRotation();
// 等待DOM完全渲染后设置监听器
@@ -1141,6 +1194,9 @@ onUnmounted(() => {
}
});
+// 声明组件可以触发的事件
+const emit = defineEmits(['updateMessage', 'sendMessage', 'ensureAIchat']);
+
// 导出方法供外部使用
defineExpose({
handleSendMessage,
@@ -1517,21 +1573,21 @@ defineExpose({
color: white;
font-size: 20px;
font-weight: bold;
- margin-left: 44%;
+ margin-left: 45%;
}
.title3 {
color: white;
font-size: 20px;
font-weight: bold;
- margin-left: 43%;
+ margin-left: 44.6%;
}
.title4 {
color: white;
font-size: 20px;
font-weight: bold;
- margin-left: 41.5%;
+ margin-left: 44%;
}
.class09 {
@@ -1580,7 +1636,7 @@ defineExpose({
/* 设置容器宽度 */
height: auto;
/* 高度根据内容动态变化 */
- min-height: 66rem;
+ min-height: 69rem;
/* 设置最小高度,确保图片显示 */
margin: 0 auto;
}
@@ -1596,7 +1652,7 @@ defineExpose({
/* 设置容器宽度 */
height: auto;
/* 高度根据内容动态变化 */
- min-height: 86rem;
+ min-height: 90rem;
/* 设置最小高度,确保图片显示 */
margin: 0 auto;
}
diff --git a/src/views/Selectmodel.vue b/src/views/Selectmodel.vue
index 699361c..ba3c347 100644
--- a/src/views/Selectmodel.vue
+++ b/src/views/Selectmodel.vue
@@ -32,21 +32,31 @@
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { setHeight } from '@/utils/setHeight'
+import { useDataStore } from "@/store/dataList.js";
+const { getQueryVariable, setActiveTabIndex } = useDataStore();
const router = useRouter()
const pageRef = ref(null)
-onMounted(() => {
- setHeight(pageRef.value)
+// onMounted(() => {
+// setHeight(pageRef.value)
+// })
+onMounted(async () => {
+ const isPhone =
+ /phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone/i.test(
+ navigator.userAgent
+ );
+ !isPhone &&
+ localStorage.setItem(
+ "localToken",
+ decodeURIComponent(String(getQueryVariable("token")))
+ );
})
-
const goToDBQBmodel = () => {
router.push('/DBQBmodel')
- window.location.href = '/DBQBmodel'
}
const goToEmotionsmodel = () => {
router.push('/Emotionsmodel')
- window.location.href = '/Emotionsmodel'
}
diff --git a/src/views/components/emoEnergyConverter.vue b/src/views/components/emoEnergyConverter.vue
index 646c338..e4a8e8c 100644
--- a/src/views/components/emoEnergyConverter.vue
+++ b/src/views/components/emoEnergyConverter.vue
@@ -244,12 +244,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) {
show: true,
trigger: 'axis',
axisPointer: {
- type: 'cross',
- crossStyle: {
- color: '#fff',
- width: 1,
- type: 'solid'
- },
+ type: 'line',
lineStyle: {
color: '#fff',
width: 1,
@@ -280,10 +275,10 @@ function initQXNLZHEcharts(kline, qxnlzhqData) {
let color = param.color
if (param.seriesType === 'candlestick') {
- let openPrice = value[0] // 开盘价
- let closePrice = value[1] // 收盘价
- let lowPrice = value[2] // 最低价
- let highPrice = value[3] // 最高价
+ let openPrice = value[1] // 开盘价
+ let closePrice = value[2] // 收盘价
+ let lowPrice = value[3] // 最低价
+ let highPrice = value[4] // 最高价
// 检查数据有效性
if (typeof openPrice !== 'number' || typeof closePrice !== 'number' ||
@@ -293,10 +288,10 @@ function initQXNLZHEcharts(kline, qxnlzhqData) {
let priceChange = closePrice - openPrice
let changePercent = ((priceChange / openPrice) * 100).toFixed(2)
- let changeColor = priceChange >= 0 ? '#ef232a' : '#14b143'
+ let changeColor = priceChange >= 0 ? '#14b143' : '#ef232a'
result += `