|
|
@ -145,7 +145,7 @@ |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
<view class="stock-kline"> |
|
|
<view class="stock-kline"> |
|
|
<view v-if="klineTab === 1 || klineTab === 2" class="time-chart-container" style="position: relative"> |
|
|
|
|
|
|
|
|
<view v-if="klineTab === 1 || klineTab === 2 || klineTab === 3 || klineTab === 4 || klineTab === 5 || klineTab === 6 || klineTab === 7 || klineTab === 8 || klineTab === 9" class="time-chart-container" style="position: relative"> |
|
|
<!-- 主图Canvas --> |
|
|
<!-- 主图Canvas --> |
|
|
<canvas |
|
|
<canvas |
|
|
canvas-id="stockChart" |
|
|
canvas-id="stockChart" |
|
|
@ -195,15 +195,12 @@ |
|
|
></canvas> |
|
|
></canvas> |
|
|
</view> |
|
|
</view> |
|
|
<!-- K线图区域 --> |
|
|
<!-- K线图区域 --> |
|
|
<view class="test" v-else-if="klineTab === 3"> |
|
|
|
|
|
|
|
|
<view class="test" v-else-if="klineTab === 10"> |
|
|
<button @click="startTcp()">接收消息</button> |
|
|
<button @click="startTcp()">接收消息</button> |
|
|
<button @click="sendStopTimeData()">停止消息</button> |
|
|
<button @click="sendStopTimeData()">停止消息</button> |
|
|
<button @click="sendTcpMessage('real_time')">实时行情推送</button> |
|
|
<button @click="sendTcpMessage('real_time')">实时行情推送</button> |
|
|
<button @click="sendTcpMessage('init_real_time')">初始化获取行情历史数据</button> |
|
|
<button @click="sendTcpMessage('init_real_time')">初始化获取行情历史数据</button> |
|
|
<button @click="sendTcpMessage('stop_real_time')">停止实时推送</button> |
|
|
<button @click="sendTcpMessage('stop_real_time')">停止实时推送</button> |
|
|
<view class="tcpMsg" v-for="item in tcpMessages" :key="item"> |
|
|
|
|
|
{{ item }} |
|
|
|
|
|
</view> |
|
|
|
|
|
</view> |
|
|
</view> |
|
|
<view v-else class="kline-chart-container"> |
|
|
<view v-else class="kline-chart-container"> |
|
|
<text>K线图开发中...</text> |
|
|
<text>K线图开发中...</text> |
|
|
@ -240,21 +237,6 @@ import tcpConnection, { TCPConnection, TCP_CONFIG } from "@/api/tcpConnection.js |
|
|
|
|
|
|
|
|
// TCP相关响应式变量 |
|
|
// TCP相关响应式变量 |
|
|
const tcpConnected = ref(false); |
|
|
const tcpConnected = ref(false); |
|
|
const tcpMessages = ref([]); |
|
|
|
|
|
const tcpStockData = ref({ |
|
|
|
|
|
count: 0, |
|
|
|
|
|
data: {}, |
|
|
|
|
|
stock_count: 0, |
|
|
|
|
|
timestamp: "", |
|
|
|
|
|
type: "", |
|
|
|
|
|
}); |
|
|
|
|
|
const currentStockInfo = ref({ |
|
|
|
|
|
stock_name: "未知股票", |
|
|
|
|
|
current_price: "0.00", |
|
|
|
|
|
change: "0.00%", |
|
|
|
|
|
change_value: 0, |
|
|
|
|
|
change_percent: 0, |
|
|
|
|
|
}); |
|
|
|
|
|
const connectionListener = ref(null); |
|
|
const connectionListener = ref(null); |
|
|
const messageListener = ref(null); |
|
|
const messageListener = ref(null); |
|
|
|
|
|
|
|
|
@ -465,11 +447,54 @@ const confirmStockColor = (price, lastDayStockClosePrice) => { |
|
|
// 股票K线类型方法 |
|
|
// 股票K线类型方法 |
|
|
const selectKlineTab = (tabId) => { |
|
|
const selectKlineTab = (tabId) => { |
|
|
klineTab.value = tabId; |
|
|
klineTab.value = tabId; |
|
|
|
|
|
|
|
|
if (klineTab.value == 1) { |
|
|
|
|
|
|
|
|
if (klineTab.value) { |
|
|
|
|
|
sendTcpMessage("stop_real_time"); |
|
|
|
|
|
} |
|
|
|
|
|
switch (klineTab.value) { |
|
|
|
|
|
case 1: |
|
|
sendTcpMessage("init_real_time"); |
|
|
sendTcpMessage("init_real_time"); |
|
|
|
|
|
break; |
|
|
|
|
|
case 2: |
|
|
|
|
|
sendTcpMessage("daily_data"); |
|
|
|
|
|
break; |
|
|
|
|
|
case 3: |
|
|
|
|
|
sendTcpMessage("weekly_data"); |
|
|
|
|
|
break; |
|
|
|
|
|
case 4: |
|
|
|
|
|
sendTcpMessage("monthly_data"); |
|
|
|
|
|
break; |
|
|
|
|
|
case 5: |
|
|
|
|
|
sendTcpMessage("daily_one_minutes_data"); |
|
|
|
|
|
break; |
|
|
|
|
|
case 6: |
|
|
|
|
|
sendTcpMessage("daily_five_minutes_data"); |
|
|
|
|
|
break; |
|
|
|
|
|
case 7: |
|
|
|
|
|
sendTcpMessage("daily_fifteen_minutes_data"); |
|
|
|
|
|
break; |
|
|
|
|
|
case 8: |
|
|
|
|
|
sendTcpMessage("daily_thirty_minutes_data"); |
|
|
|
|
|
break; |
|
|
|
|
|
case 9: |
|
|
|
|
|
sendTcpMessage("daily_sixty_minutes_data"); |
|
|
|
|
|
break; |
|
|
|
|
|
case 10: |
|
|
|
|
|
uni.showToast({ |
|
|
|
|
|
title: "暂无季K数据", |
|
|
|
|
|
icon: "none", |
|
|
|
|
|
duration: 2000, |
|
|
|
|
|
}); |
|
|
|
|
|
break; |
|
|
|
|
|
case 11: |
|
|
|
|
|
uni.showToast({ |
|
|
|
|
|
title: "暂无年K数据", |
|
|
|
|
|
icon: "none", |
|
|
|
|
|
duration: 2000, |
|
|
|
|
|
}); |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
initCanvas(); |
|
|
initCanvas(); |
|
|
// startAddDataTimer(); |
|
|
// startAddDataTimer(); |
|
|
}; |
|
|
}; |
|
|
@ -938,6 +963,7 @@ const drawChart = () => { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
const data = klineTab.value == 1 ? timeData.value : klineData.value; |
|
|
const data = klineTab.value == 1 ? timeData.value : klineData.value; |
|
|
|
|
|
console.log("data", data); |
|
|
chartRange.value = []; |
|
|
chartRange.value = []; |
|
|
// 清除画布 |
|
|
// 清除画布 |
|
|
// HCharts.setCanvasColor(ctx.value, width, height, CANVAS_BACKGROUND_COLOR); |
|
|
// HCharts.setCanvasColor(ctx.value, width, height, CANVAS_BACKGROUND_COLOR); |
|
|
@ -1246,6 +1272,7 @@ const touchMove = (e) => { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
|
|
|
return; |
|
|
if (klineTab.value === 2) { |
|
|
if (klineTab.value === 2) { |
|
|
// if(currentY) |
|
|
// if(currentY) |
|
|
if (currentX < touchState.startX) { |
|
|
if (currentX < touchState.startX) { |
|
|
@ -1413,10 +1440,6 @@ const initTcpListeners = () => { |
|
|
timestamp: new Date().toLocaleTimeString(), |
|
|
timestamp: new Date().toLocaleTimeString(), |
|
|
direction: "received", |
|
|
direction: "received", |
|
|
}; |
|
|
}; |
|
|
console.log("0000"); |
|
|
|
|
|
tcpMessages.value.push(messageObj); |
|
|
|
|
|
// console.log('收到TCP消息:', messageObj) |
|
|
|
|
|
console.log("home开始调用parseStockData", messageObj); |
|
|
|
|
|
|
|
|
|
|
|
// 解析股票数据 |
|
|
// 解析股票数据 |
|
|
parseStockData(message); |
|
|
parseStockData(message); |
|
|
@ -1469,6 +1492,7 @@ const sendTcpMessage = (command) => { |
|
|
command: "stock_list", |
|
|
command: "stock_list", |
|
|
}; |
|
|
}; |
|
|
break; |
|
|
break; |
|
|
|
|
|
// 日线数据 |
|
|
case "daily_data": |
|
|
case "daily_data": |
|
|
messageData = { |
|
|
messageData = { |
|
|
command: "daily_data", |
|
|
command: "daily_data", |
|
|
@ -1477,38 +1501,53 @@ const sendTcpMessage = (command) => { |
|
|
end_date: "20251023", |
|
|
end_date: "20251023", |
|
|
}; |
|
|
}; |
|
|
break; |
|
|
break; |
|
|
|
|
|
// 周线数据 |
|
|
case "weekly_data": |
|
|
case "weekly_data": |
|
|
messageData = { |
|
|
messageData = { |
|
|
command: "weekly_data", |
|
|
command: "weekly_data", |
|
|
stock_code: "000001.SZ", |
|
|
stock_code: "000001.SZ", |
|
|
start_date: "20251001", |
|
|
|
|
|
end_date: "20251023", |
|
|
|
|
|
|
|
|
start_date: "2024912", |
|
|
|
|
|
end_date: "20251029", |
|
|
|
|
|
}; |
|
|
|
|
|
break; |
|
|
|
|
|
// 周线数据 |
|
|
|
|
|
case "monthly_data": |
|
|
|
|
|
messageData = { |
|
|
|
|
|
command: "monthly_data", |
|
|
|
|
|
stock_code: "000001.SZ", |
|
|
|
|
|
start_date: "2024912", |
|
|
|
|
|
end_date: "20251029", |
|
|
}; |
|
|
}; |
|
|
break; |
|
|
break; |
|
|
|
|
|
// 1分钟线数据 |
|
|
case "daily_one_minutes_data": |
|
|
case "daily_one_minutes_data": |
|
|
messageData = { |
|
|
messageData = { |
|
|
command: "daily_one_minutes_data", |
|
|
command: "daily_one_minutes_data", |
|
|
stock_code: "000001.SZ", |
|
|
stock_code: "000001.SZ", |
|
|
}; |
|
|
}; |
|
|
break; |
|
|
break; |
|
|
|
|
|
// 5分钟线数据 |
|
|
case "daily_five_minutes_data": |
|
|
case "daily_five_minutes_data": |
|
|
messageData = { |
|
|
messageData = { |
|
|
command: "daily_five_minutes_data", |
|
|
command: "daily_five_minutes_data", |
|
|
stock_code: "000001.SZ", |
|
|
stock_code: "000001.SZ", |
|
|
}; |
|
|
}; |
|
|
break; |
|
|
break; |
|
|
|
|
|
// 15分钟线数据 |
|
|
case "daily_fifteen_minutes_data": |
|
|
case "daily_fifteen_minutes_data": |
|
|
messageData = { |
|
|
messageData = { |
|
|
command: "daily_fifteen_minutes_data", |
|
|
command: "daily_fifteen_minutes_data", |
|
|
stock_code: "000001.SZ", |
|
|
stock_code: "000001.SZ", |
|
|
}; |
|
|
}; |
|
|
break; |
|
|
break; |
|
|
|
|
|
// 30分钟线数据 |
|
|
case "daily_thirty_minutes_data": |
|
|
case "daily_thirty_minutes_data": |
|
|
messageData = { |
|
|
messageData = { |
|
|
command: "daily_thirty_minutes_data", |
|
|
command: "daily_thirty_minutes_data", |
|
|
stock_code: "000001.SZ", |
|
|
stock_code: "000001.SZ", |
|
|
}; |
|
|
}; |
|
|
break; |
|
|
break; |
|
|
|
|
|
// 60分钟线数据 |
|
|
case "daily_sixty_minutes_data": |
|
|
case "daily_sixty_minutes_data": |
|
|
messageData = { |
|
|
messageData = { |
|
|
command: "daily_sixty_minutes_data", |
|
|
command: "daily_sixty_minutes_data", |
|
|
@ -1531,7 +1570,7 @@ const sendTcpMessage = (command) => { |
|
|
uni.showToast({ |
|
|
uni.showToast({ |
|
|
title: "命令不存在", |
|
|
title: "命令不存在", |
|
|
icon: "none", |
|
|
icon: "none", |
|
|
duration: 1500, |
|
|
|
|
|
|
|
|
duration: 1000, |
|
|
}); |
|
|
}); |
|
|
return; |
|
|
return; |
|
|
} else { |
|
|
} else { |
|
|
@ -1543,7 +1582,7 @@ const sendTcpMessage = (command) => { |
|
|
uni.showToast({ |
|
|
uni.showToast({ |
|
|
title: "消息发送成功", |
|
|
title: "消息发送成功", |
|
|
icon: "success", |
|
|
icon: "success", |
|
|
duration: 1500, |
|
|
|
|
|
|
|
|
duration: 1000, |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
@ -1551,34 +1590,102 @@ const sendTcpMessage = (command) => { |
|
|
uni.showToast({ |
|
|
uni.showToast({ |
|
|
title: "消息发送失败", |
|
|
title: "消息发送失败", |
|
|
icon: "none", |
|
|
icon: "none", |
|
|
duration: 1500, |
|
|
|
|
|
|
|
|
duration: 1000, |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 清空消息记录 |
|
|
|
|
|
const clearTcpMessages = () => { |
|
|
|
|
|
tcpMessages.value = []; |
|
|
|
|
|
uni.showToast({ |
|
|
|
|
|
title: "消息记录已清空", |
|
|
|
|
|
icon: "success", |
|
|
|
|
|
duration: 1500, |
|
|
|
|
|
}); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 获取TCP连接状态 |
|
|
// 获取TCP连接状态 |
|
|
const getTcpStatus = () => { |
|
|
const getTcpStatus = () => { |
|
|
const status = tcpConnection.getConnectionStatus(); |
|
|
const status = tcpConnection.getConnectionStatus(); |
|
|
uni.showModal({ |
|
|
uni.showModal({ |
|
|
title: "TCP连接状态", |
|
|
title: "TCP连接状态", |
|
|
content: `当前状态: ${status ? "已连接" : "未连接"}\n消息数量: ${tcpMessages.value.length}`, |
|
|
|
|
|
|
|
|
content: `当前状态: ${status ? "已连接" : "未连接"}`, |
|
|
showCancel: false, |
|
|
showCancel: false, |
|
|
}); |
|
|
}); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
let isMorePacket = false; |
|
|
|
|
|
|
|
|
let isMorePacket = { |
|
|
|
|
|
init_real_time: false, |
|
|
|
|
|
daily_data: false, |
|
|
|
|
|
weekly_data: false, |
|
|
|
|
|
monthly_data: false, |
|
|
|
|
|
daily_one_minutes_data: false, |
|
|
|
|
|
daily_five_minutes_data: false, |
|
|
|
|
|
daily_fifteen_minutes_data: false, |
|
|
|
|
|
daily_thirty_minutes_data: false, |
|
|
|
|
|
daily_sixty_minutes_data: false, |
|
|
|
|
|
}; |
|
|
let receivedMessage; |
|
|
let receivedMessage; |
|
|
|
|
|
const findJsonPacket = (message, command) => { |
|
|
|
|
|
let jsonStartIndex = 0; |
|
|
|
|
|
let jsonEndIndex = message.indexOf(command); |
|
|
|
|
|
let jsonStartCount = 0; |
|
|
|
|
|
let jsonEndCount = 0; |
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < message.length - 1; ++i) { |
|
|
|
|
|
if (message[i] == "{") { |
|
|
|
|
|
jsonStartCount++; |
|
|
|
|
|
if (jsonStartCount == 2) { |
|
|
|
|
|
jsonStartIndex = i; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
for (let i = message.indexOf(command); i >= 0; --i) { |
|
|
|
|
|
if (message[i] == "}" || i == jsonStartIndex) { |
|
|
|
|
|
jsonEndCount++; |
|
|
|
|
|
if (jsonEndCount == 1) { |
|
|
|
|
|
jsonEndIndex = i; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 检查JSON字符串是否有效 |
|
|
|
|
|
if (jsonStartIndex >= jsonEndIndex) { |
|
|
|
|
|
return { error: true }; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return { json: JSON.parse(message.substring(jsonStartIndex, jsonEndIndex + 1)) }; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 根据timeData中最后一个时间生成下一个时间 |
|
|
|
|
|
const generateNextTime = () => { |
|
|
|
|
|
if (timeData.value.length === 0) { |
|
|
|
|
|
return "09:30"; // 如果没有数据,返回开盘时间 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const lastTime = timeData.value[timeData.value.length - 1].time; |
|
|
|
|
|
if (!lastTime) { |
|
|
|
|
|
return "09:30"; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 解析时间字符串,格式为 "HH:MM" |
|
|
|
|
|
const [hours, minutes] = lastTime.split(":").map(Number); |
|
|
|
|
|
|
|
|
|
|
|
// 计算下一分钟 |
|
|
|
|
|
let nextMinutes = minutes + 1; |
|
|
|
|
|
let nextHours = hours; |
|
|
|
|
|
|
|
|
|
|
|
// 处理分钟进位 |
|
|
|
|
|
if (nextMinutes >= 60) { |
|
|
|
|
|
nextMinutes = 0; |
|
|
|
|
|
nextHours += 1; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 处理小时进位(24小时制) |
|
|
|
|
|
if (nextHours >= 24) { |
|
|
|
|
|
nextHours = 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 格式化为 "HH:MM" 格式 |
|
|
|
|
|
const formattedHours = nextHours.toString().padStart(2, "0"); |
|
|
|
|
|
const formattedMinutes = nextMinutes.toString().padStart(2, "0"); |
|
|
|
|
|
|
|
|
|
|
|
return `${formattedHours}:${formattedMinutes}`; |
|
|
|
|
|
}; |
|
|
// 解析TCP股票数据 |
|
|
// 解析TCP股票数据 |
|
|
const parseStockData = (message) => { |
|
|
const parseStockData = (message) => { |
|
|
try { |
|
|
try { |
|
|
@ -1592,105 +1699,195 @@ const parseStockData = (message) => { |
|
|
console.log("服务器命令列表,不予处理"); |
|
|
console.log("服务器命令列表,不予处理"); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
if ((typeof message === "string" && message.includes("init_real_data_start")) || isMorePacket) { |
|
|
|
|
|
|
|
|
if (message.includes("real_time")) { |
|
|
|
|
|
let startIndex = 0; |
|
|
|
|
|
let endIndex = message.length; |
|
|
|
|
|
for (let i = 0; i < message.length - 1; ++i) { |
|
|
|
|
|
if (message[i] == "{") { |
|
|
|
|
|
startIndex = i; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
for (let i = message.length - 1; i >= 0; --i) { |
|
|
|
|
|
if (message[i] == "}") { |
|
|
|
|
|
endIndex = i; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
parsedMessage = JSON.parse(message.substring(startIndex, endIndex + 1)); |
|
|
|
|
|
console.log("实时数据解析", parsedMessage); |
|
|
|
|
|
// 处理实时数据 |
|
|
|
|
|
timeData.value.push({ |
|
|
|
|
|
time: generateNextTime(), |
|
|
|
|
|
price: parsedMessage.current_price, |
|
|
|
|
|
volume: parsedMessage.volume, |
|
|
|
|
|
amount: parsedMessage.amount, |
|
|
|
|
|
}); |
|
|
|
|
|
// 实时更新股票信息 |
|
|
|
|
|
stockInformation.value.currentPrice = parsedMessage.current_price; |
|
|
|
|
|
stockInformation.value.openPrice = parsedMessage.open_price; |
|
|
|
|
|
stockInformation.value.closePrice = parsedMessage.close_price; |
|
|
|
|
|
stockInformation.value.highPrice = parsedMessage.high_price; |
|
|
|
|
|
stockInformation.value.lowPrice = parsedMessage.low_price; |
|
|
|
|
|
stockInformation.value.volume = parsedMessage.volume; |
|
|
|
|
|
stockInformation.value.amount = parsedMessage.amount; |
|
|
|
|
|
stockInformation.value.turnoverRatio = parsedMessage.turnover_ratio; |
|
|
|
|
|
stockInformation.value.marketValue = parsedMessage.total_market_value; |
|
|
|
|
|
stockInformation.value.currentValue = stockInformation.value.currentPrice - stockInformation.value.lastDayStockClosePrice; |
|
|
|
|
|
stockInformation.value.currentRatio = ((stockInformation.value.currentPrice - stockInformation.value.lastDayStockClosePrice) / stockInformation.value.lastDayStockClosePrice) * 100; |
|
|
|
|
|
console.log("重绘画面"); |
|
|
|
|
|
drawChart(); |
|
|
|
|
|
if (timeData.value.length >= 240) { |
|
|
|
|
|
sendTcpMessage("stop_real_time"); |
|
|
|
|
|
} |
|
|
|
|
|
return; |
|
|
|
|
|
} else if ((typeof message === "string" && message.includes("init_real_data_start")) || isMorePacket.init_real_time) { |
|
|
if (typeof message === "string" && message.includes("init_real_data_start")) { |
|
|
if (typeof message === "string" && message.includes("init_real_data_start")) { |
|
|
console.log("开始接受分包数据"); |
|
|
console.log("开始接受分包数据"); |
|
|
receivedMessage = ""; |
|
|
receivedMessage = ""; |
|
|
} else { |
|
|
} else { |
|
|
console.log("接收分包数据过程中"); |
|
|
console.log("接收分包数据过程中"); |
|
|
} |
|
|
} |
|
|
isMorePacket = true; |
|
|
|
|
|
|
|
|
isMorePacket.init_real_time = true; |
|
|
receivedMessage += message; |
|
|
receivedMessage += message; |
|
|
// 如果当前消息包含},说明收到JSON字符串结尾,结束接收,开始解析 |
|
|
// 如果当前消息包含},说明收到JSON字符串结尾,结束接收,开始解析 |
|
|
if (receivedMessage.includes("init_real_data_complete")) { |
|
|
if (receivedMessage.includes("init_real_data_complete")) { |
|
|
console.log("接受分包数据结束"); |
|
|
console.log("接受分包数据结束"); |
|
|
isMorePacket = false; |
|
|
|
|
|
|
|
|
isMorePacket.init_real_time = false; |
|
|
|
|
|
|
|
|
console.log("展示数据", receivedMessage); |
|
|
console.log("展示数据", receivedMessage); |
|
|
// 获取JSON字符串的开头和结尾的坐标 |
|
|
|
|
|
let jsonStartIndex = 0; |
|
|
|
|
|
let jsonEndIndex = receivedMessage.indexOf("init_real_data_complete"); |
|
|
|
|
|
let jsonStartCount = 0; |
|
|
|
|
|
let jsonEndCount = 0; |
|
|
|
|
|
for (let i = 0; i < receivedMessage.length - 1; ++i) { |
|
|
|
|
|
if (receivedMessage[i] == "{") { |
|
|
|
|
|
jsonStartCount++; |
|
|
|
|
|
if (jsonStartCount == 2) { |
|
|
|
|
|
jsonStartIndex = i; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (let i = receivedMessage.indexOf("init_real_data_complete"); i >= 0; --i) { |
|
|
|
|
|
if (receivedMessage[i] == "}" || i == jsonStartIndex) { |
|
|
|
|
|
jsonEndCount++; |
|
|
|
|
|
if (jsonEndCount == 1) { |
|
|
|
|
|
jsonEndIndex = i; |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
const result = findJsonPacket(receivedMessage, "init_real_data_complete"); |
|
|
|
|
|
if (result.error) { |
|
|
|
|
|
throw new Error("解析JSON字符串失败"); |
|
|
|
|
|
} else { |
|
|
|
|
|
parsedMessage = result.json; |
|
|
|
|
|
console.log("JSON解析成功,解析后类型:", typeof parsedMessage, parsedMessage); |
|
|
|
|
|
if (parsedMessage.type === "daily_data") { |
|
|
|
|
|
timeData.value = parsedMessage.data; |
|
|
|
|
|
stockInformation.value.lastDayStockClosePrice = parsedMessage.pre_close; |
|
|
|
|
|
console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice); |
|
|
|
|
|
drawChart(); |
|
|
|
|
|
sendTcpMessage("stop_real_time"); |
|
|
|
|
|
sendTcpMessage("real_time"); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
// 检查JSON字符串是否有效 |
|
|
|
|
|
if (jsonStartIndex >= jsonEndIndex) { |
|
|
|
|
|
throw new Error("JSON字符串格式错误"); |
|
|
|
|
|
|
|
|
} else if ((typeof message === "string" && message.includes("daily_data_start")) || isMorePacket.daily_data) { |
|
|
|
|
|
if (typeof message === "string" && message.includes("daily_data_start")) { |
|
|
|
|
|
console.log("开始接受分包数据"); |
|
|
|
|
|
receivedMessage = ""; |
|
|
|
|
|
} else { |
|
|
|
|
|
console.log("接收分包数据过程中"); |
|
|
} |
|
|
} |
|
|
|
|
|
isMorePacket.daily_data = true; |
|
|
|
|
|
receivedMessage += message; |
|
|
|
|
|
// 如果当前消息包含},说明收到JSON字符串结尾,结束接收,开始解析 |
|
|
|
|
|
if (receivedMessage.includes("daily_data_complete")) { |
|
|
|
|
|
console.log("接受分包数据结束"); |
|
|
|
|
|
isMorePacket.daily_data = false; |
|
|
|
|
|
|
|
|
|
|
|
console.log("展示数据", receivedMessage); |
|
|
|
|
|
|
|
|
console.log("检测到JSON字符串,开始解析"); |
|
|
|
|
|
parsedMessage = JSON.parse(receivedMessage.substring(jsonStartIndex, jsonEndIndex + 1)); |
|
|
|
|
|
|
|
|
const result = findJsonPacket(receivedMessage, "daily_data_complete"); |
|
|
|
|
|
if (result.error) { |
|
|
|
|
|
throw new Error("解析JSON字符串失败"); |
|
|
|
|
|
} else { |
|
|
|
|
|
parsedMessage = result.json; |
|
|
console.log("JSON解析成功,解析后类型:", typeof parsedMessage, parsedMessage); |
|
|
console.log("JSON解析成功,解析后类型:", typeof parsedMessage, parsedMessage); |
|
|
if (parsedMessage.type === "daily_data") { |
|
|
if (parsedMessage.type === "daily_data") { |
|
|
timeData.value = parsedMessage.data; |
|
|
|
|
|
stockInformation.value.lastDayStockClosePrice = parsedMessage.pre_close; |
|
|
|
|
|
|
|
|
klineData.value = parsedMessage.data.map((item) => ({ |
|
|
|
|
|
open: item.ask_open, |
|
|
|
|
|
close: item.ask_close, |
|
|
|
|
|
high: item.ask_high, |
|
|
|
|
|
low: item.ask_low, |
|
|
|
|
|
volume: item.tick_qty, |
|
|
|
|
|
date: item.trade_date ? `${item.trade_date.slice(0, 4)}-${item.trade_date.slice(4, 6)}-${item.trade_date.slice(6, 8)}` : item.trade_date, |
|
|
|
|
|
})); |
|
|
|
|
|
stockInformation.value.lastDayStockClosePrice = klineData.value[klineData.value.length - 2].close; |
|
|
|
|
|
touchState.offset = canvasWidth.value / klineData.value.length / 2; |
|
|
console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice); |
|
|
console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice); |
|
|
drawChart(); |
|
|
drawChart(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
// 通过了JSON解析判断,说明返回的数据是需要的正确数据,进行股票实时数据检查 |
|
|
|
|
|
console.log("开始处理解析后的数据"); |
|
|
|
|
|
|
|
|
|
|
|
// 检查是否是股票数据(支持batch_data_chunk和batch_realtime_data两种类型) |
|
|
|
|
|
if ((parsedMessage.type === "batch_data_chunk" || parsedMessage.type === "batch_realtime_data") && parsedMessage.data) { |
|
|
|
|
|
console.log("开始更新TCP股票数据存储"); |
|
|
|
|
|
// 更新TCP股票数据存储 |
|
|
|
|
|
tcpStockData.value = { |
|
|
|
|
|
count: parsedMessage.count || 0, |
|
|
|
|
|
data: parsedMessage.data || {}, |
|
|
|
|
|
stock_count: parsedMessage.stock_count || 0, |
|
|
|
|
|
timestamp: parsedMessage.timestamp || "", |
|
|
|
|
|
type: parsedMessage.type || "", |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
} else if ((typeof message === "string" && message.includes("weekly_data_start")) || isMorePacket.weekly_data) { |
|
|
|
|
|
if (typeof message === "string" && message.includes("weekly_data_start")) { |
|
|
|
|
|
console.log("开始接受分包数据"); |
|
|
|
|
|
receivedMessage = ""; |
|
|
|
|
|
} else { |
|
|
|
|
|
console.log("接收分包数据过程中"); |
|
|
|
|
|
} |
|
|
|
|
|
isMorePacket.weekly_data = true; |
|
|
|
|
|
receivedMessage += message; |
|
|
|
|
|
// 如果当前消息包含},说明收到JSON字符串结尾,结束接收,开始解析 |
|
|
|
|
|
if (receivedMessage.includes("weekly_data_complete")) { |
|
|
|
|
|
console.log("接受分包数据结束"); |
|
|
|
|
|
isMorePacket.weekly_data = false; |
|
|
|
|
|
|
|
|
// 获取第一个股票的数据用于显示 |
|
|
|
|
|
const stockCodes = Object.keys(parsedMessage.data); |
|
|
|
|
|
if (stockCodes.length > 0) { |
|
|
|
|
|
const firstStockCode = stockCodes[0]; |
|
|
|
|
|
|
|
|
|
|
|
// 检查数据结构 |
|
|
|
|
|
if (parsedMessage.data[firstStockCode] && Array.isArray(parsedMessage.data[firstStockCode]) && parsedMessage.data[firstStockCode].length > 0) { |
|
|
|
|
|
const stockData = parsedMessage.data[firstStockCode][0]; // 取第一条数据 |
|
|
|
|
|
|
|
|
|
|
|
if (stockData && stockData.current_price !== undefined && stockData.pre_close !== undefined) { |
|
|
|
|
|
// 计算涨跌幅 |
|
|
|
|
|
const changeValue = stockData.current_price - stockData.pre_close; |
|
|
|
|
|
const changePercent = ((changeValue / stockData.pre_close) * 100).toFixed(2); |
|
|
|
|
|
const changeSign = changeValue >= 0 ? "+" : ""; |
|
|
|
|
|
|
|
|
|
|
|
// 更新当前显示的股票信息 |
|
|
|
|
|
currentStockInfo.value = { |
|
|
|
|
|
stock_name: stockData.stock_name || "未知股票", |
|
|
|
|
|
current_price: stockData.current_price ? stockData.current_price.toFixed(2) : "0.00", |
|
|
|
|
|
change: `${changeSign}${changePercent}%`, |
|
|
|
|
|
change_value: changeValue, |
|
|
|
|
|
change_percent: parseFloat(changePercent), |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
console.log("展示数据", receivedMessage); |
|
|
|
|
|
|
|
|
console.log("股票数据更新成功:", currentStockInfo.value); |
|
|
|
|
|
|
|
|
const result = findJsonPacket(receivedMessage, "weekly_data_complete"); |
|
|
|
|
|
if (result.error) { |
|
|
|
|
|
throw new Error("解析JSON字符串失败"); |
|
|
|
|
|
} else { |
|
|
|
|
|
parsedMessage = result.json; |
|
|
|
|
|
console.log("JSON解析成功,解析后类型:", typeof parsedMessage, parsedMessage); |
|
|
|
|
|
if (parsedMessage.type === "weekly_data") { |
|
|
|
|
|
klineData.value = parsedMessage.data.map((item) => ({ |
|
|
|
|
|
open: item.bid_open, |
|
|
|
|
|
close: item.bid_close, |
|
|
|
|
|
high: item.bid_high, |
|
|
|
|
|
low: item.bid_low, |
|
|
|
|
|
volume: item.vol, |
|
|
|
|
|
amount: item.amount, |
|
|
|
|
|
date: item.trade_date ? `${item.trade_date.slice(0, 4)}-${item.trade_date.slice(4, 6)}-${item.trade_date.slice(6, 8)}` : item.trade_date, |
|
|
|
|
|
})); |
|
|
|
|
|
stockInformation.value.lastDayStockClosePrice = klineData.value[klineData.value.length - 2].close; |
|
|
|
|
|
touchState.offset = canvasWidth.value / klineData.value.length / 2; |
|
|
|
|
|
console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice); |
|
|
|
|
|
drawChart(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
} else if ((typeof message === "string" && message.includes("daily_one_minutes_data_start")) || isMorePacket.daily_one_minutes_data) { |
|
|
|
|
|
if (typeof message === "string" && message.includes("daily_one_minutes_data_start")) { |
|
|
|
|
|
console.log("开始接受分包数据"); |
|
|
|
|
|
receivedMessage = ""; |
|
|
|
|
|
} else { |
|
|
|
|
|
console.log("接收分包数据过程中"); |
|
|
|
|
|
} |
|
|
|
|
|
isMorePacket.daily_one_minutes_data = true; |
|
|
|
|
|
receivedMessage += message; |
|
|
|
|
|
// 如果当前消息包含},说明收到JSON字符串结尾,结束接收,开始解析 |
|
|
|
|
|
if (receivedMessage.includes("daily_one_minutes_data_complete")) { |
|
|
|
|
|
console.log("接受分包数据结束"); |
|
|
|
|
|
isMorePacket.daily_one_minutes_data = false; |
|
|
|
|
|
|
|
|
|
|
|
console.log("展示数据", receivedMessage); |
|
|
|
|
|
|
|
|
|
|
|
const result = findJsonPacket(receivedMessage, "daily_one_minutes_data_complete"); |
|
|
|
|
|
if (result.error) { |
|
|
|
|
|
throw new Error("解析JSON字符串失败"); |
|
|
} else { |
|
|
} else { |
|
|
console.log("不是batch_data_chunk或batch_realtime_data类型的消息,跳过处理"); |
|
|
|
|
|
|
|
|
parsedMessage = result.json; |
|
|
|
|
|
console.log("JSON解析成功,解析后类型:", typeof parsedMessage, parsedMessage); |
|
|
|
|
|
if (parsedMessage.type === "daily_one_minutes_data") { |
|
|
|
|
|
// klineData.value = parsedMessage.data.map((item) => ({ |
|
|
|
|
|
// open: item.open, |
|
|
|
|
|
// close: item.close, |
|
|
|
|
|
// high: item.high, |
|
|
|
|
|
// low: item.low, |
|
|
|
|
|
// volume: item.volume, |
|
|
|
|
|
// amount: item.amount, |
|
|
|
|
|
// date: item.time, |
|
|
|
|
|
// })); |
|
|
|
|
|
stockInformation.value.lastDayStockClosePrice = klineData.value[klineData.value.length - 2].close; |
|
|
|
|
|
touchState.offset = canvasWidth.value / klineData.value.length / 2; |
|
|
|
|
|
console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice); |
|
|
|
|
|
drawChart(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
// 没有通过JSON解析判断,说明不是需要的数据 |
|
|
// 没有通过JSON解析判断,说明不是需要的数据 |
|
|
@ -1789,7 +1986,8 @@ onLoad((options) => { |
|
|
|
|
|
|
|
|
// 保存定时器,用于页面卸载时清理 |
|
|
// 保存定时器,用于页面卸载时清理 |
|
|
onUnmounted(() => { |
|
|
onUnmounted(() => { |
|
|
// disconnect(); |
|
|
|
|
|
|
|
|
removeTcpListeners(); |
|
|
|
|
|
disconnect(); |
|
|
if (timer) { |
|
|
if (timer) { |
|
|
console.log("卸载定时器"); |
|
|
console.log("卸载定时器"); |
|
|
clearInterval(timer); |
|
|
clearInterval(timer); |
|
|
@ -1829,7 +2027,9 @@ onMounted(async () => { |
|
|
console.warn("没有时间数据,跳过股票信息计算"); |
|
|
console.warn("没有时间数据,跳过股票信息计算"); |
|
|
} |
|
|
} |
|
|
await nextTick(); |
|
|
await nextTick(); |
|
|
|
|
|
setTimeout(() => { |
|
|
initCanvas(); |
|
|
initCanvas(); |
|
|
|
|
|
}, 100); |
|
|
console.log("所有初始化步骤完成"); |
|
|
console.log("所有初始化步骤完成"); |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error("初始化过程中出现错误:", error); |
|
|
console.error("初始化过程中出现错误:", error); |
|
|
|