22 changed files with 2338 additions and 404 deletions
-
2.env.development
-
2.env.production
-
7README.md
-
1138package-lock.json
-
9package.json
-
76src/api/AIxiaocaishen.js
-
BINsrc/assets/img/homePage/tail/voice-no-active.png
-
50src/assets/js/useAppBridge.js
-
159src/assets/js/useProjectTracking.js
-
6src/main.js
-
2src/router/index.js
-
33src/store/audio.js
-
16src/store/chat.js
-
186src/store/dataList.js
-
2src/store/userPessionCode.js
-
2src/utils/languageService.js
-
2src/utils/request.js
-
420src/views/AIchat.vue
-
53src/views/AIfind.vue
-
105src/views/Announcement.vue
-
219src/views/Echarts/KLine.vue
-
251src/views/homePage.vue
1138
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
After Width: 61 | Height: 61 | Size: 2.1 KiB |
@ -0,0 +1,50 @@ |
|||||
|
//跳转app方法
|
||||
|
export function useAppBridge() { |
||||
|
const fullClose = (n, m) => { |
||||
|
let result = Math.random() * (m + 1 - n) + n |
||||
|
while (result > m) { |
||||
|
result = Math.random() * (m + 1 - n) + n |
||||
|
} |
||||
|
return Math.floor(result) |
||||
|
} |
||||
|
|
||||
|
const packageFun = (funName, fun = () => {}, platform, data = {}) => { |
||||
|
const JWrandom = fullClose(10000, 99999) |
||||
|
data.JWrandom = JWrandom |
||||
|
|
||||
|
window[funName + JWrandom] = fun |
||||
|
|
||||
|
try { |
||||
|
const params = { |
||||
|
name: funName, |
||||
|
extra: { data } |
||||
|
} |
||||
|
|
||||
|
switch (platform) { |
||||
|
case 2: // app apicloud
|
||||
|
window.api.sendEvent(params) |
||||
|
break |
||||
|
case 3: // app ios
|
||||
|
window.webkit.messageHandlers.getTouJiaoData.postMessage(JSON.stringify(params)) |
||||
|
break |
||||
|
case 4: // app android
|
||||
|
window.android.getTouJiaoData(JSON.stringify(params)) |
||||
|
break |
||||
|
case 5: // app uniapp
|
||||
|
window.uni.postMessage({ |
||||
|
data: { |
||||
|
val: JSON.stringify(params) |
||||
|
} |
||||
|
}) |
||||
|
break |
||||
|
} |
||||
|
} catch (e) { |
||||
|
console.error('Error in packageFun:', e) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
packageFun, |
||||
|
fullClose |
||||
|
} |
||||
|
} |
@ -0,0 +1,159 @@ |
|||||
|
import { ref, onMounted, onBeforeUnmount, watch } from 'vue' |
||||
|
import { useRouter } from 'vue-router' |
||||
|
import { computedUsersAPI } from '@/api/sword' |
||||
|
|
||||
|
export function useProjectTracking(projectRoutes) { |
||||
|
const router = useRouter() |
||||
|
const entryTime = ref(Date.now()) |
||||
|
const isInProject = ref(true) |
||||
|
const hasRecordedEntry = ref(sessionStorage.getItem('hasRecordedEntry') === 'true') |
||||
|
// const parentUrl = window.parent.location.href
|
||||
|
// console.log('Link平台地址:', parentUrl)
|
||||
|
|
||||
|
let isPageRefreshing = false // 标志位:是否刷新页面
|
||||
|
|
||||
|
// 记录用户进入项目的时间
|
||||
|
const recordEntryTime = () => { |
||||
|
if (hasRecordedEntry.value) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
entryTime.value = Date.now() |
||||
|
const date = new Date(entryTime.value) |
||||
|
const formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1) |
||||
|
.toString() |
||||
|
.padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date |
||||
|
.getHours() |
||||
|
.toString() |
||||
|
.padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date |
||||
|
.getSeconds() |
||||
|
.toString() |
||||
|
.padStart(2, '0')}`
|
||||
|
sessionStorage.setItem('projectEntryTime', formattedDate) |
||||
|
sessionStorage.setItem('hasRecordedEntry', 'true') |
||||
|
isInProject.value = true |
||||
|
hasRecordedEntry.value = true |
||||
|
console.log('记录首次进入时间:', formattedDate) |
||||
|
} |
||||
|
|
||||
|
// 发送追踪数据到后端
|
||||
|
const sendTrackingData = async () => { |
||||
|
if (!isInProject.value) return |
||||
|
|
||||
|
const storedEntryTime = sessionStorage.getItem('projectEntryTime') |
||||
|
if (!storedEntryTime) { |
||||
|
console.warn('未找到存储的进入时间,取消发送跟踪数据') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
let timestamp |
||||
|
try { |
||||
|
timestamp = new Date(storedEntryTime.replace(' ', 'T')).getTime() |
||||
|
if (isNaN(timestamp)) throw new Error('无效日期') |
||||
|
} catch (error) { |
||||
|
console.error('解析存储的进入时间时出错:', error) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
const exitTime = Date.now() |
||||
|
const duration = Math.floor((exitTime - timestamp) / 1000) |
||||
|
const localToken = localStorage.getItem('localToken') |
||||
|
console.log('进入项目的时间', storedEntryTime) |
||||
|
console.log('停留时间', duration) |
||||
|
|
||||
|
const params = { |
||||
|
stayTime: duration, |
||||
|
loginTime: storedEntryTime, |
||||
|
token: localToken |
||||
|
} |
||||
|
|
||||
|
if (localToken) { |
||||
|
try { |
||||
|
const res = await computedUsersAPI(params) |
||||
|
console.log('跟踪数据已发送:', res) |
||||
|
sessionStorage.removeItem('projectEntryTime') |
||||
|
sessionStorage.removeItem('hasRecordedEntry') |
||||
|
isInProject.value = false |
||||
|
hasRecordedEntry.value = false |
||||
|
} catch (error) { |
||||
|
console.error('发送跟踪数据失败:', error) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 页面可见性变化时触发
|
||||
|
const handleVisibilityChange = () => { |
||||
|
// console.log(window.location.pathname.includes('duobaoqibing'), '路径是否包含了页面不可见触发')
|
||||
|
// if (window.location.pathname.includes('duobaoqibing')) {
|
||||
|
// console.log('在 searchCode.html 页面,不发送数据')
|
||||
|
// return
|
||||
|
// }
|
||||
|
if (document.visibilityState === 'hidden') { |
||||
|
console.log('页面不可见,用户可能离开或切换标签页') |
||||
|
sendTrackingData() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 页面关闭或刷新时触发
|
||||
|
const handleBeforeUnload = (event) => { |
||||
|
// console.log(window.location.pathname)
|
||||
|
// console.log(
|
||||
|
// window.location.pathname.includes('duobaoqibing'),
|
||||
|
// '路径是否包含了页面关闭了啦啦啦啦啦啦触发'
|
||||
|
// )
|
||||
|
// if (window.location.pathname.includes('duobaoqibing')) {
|
||||
|
// console.log('在 searchCode.html 页面,不发送数据')
|
||||
|
// return
|
||||
|
// }
|
||||
|
if (isPageRefreshing) { |
||||
|
console.log('页面刷新,不触发数据发送') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
console.log('页面即将关闭或跳转') |
||||
|
sendTrackingData() |
||||
|
} |
||||
|
|
||||
|
const handleRefreshDetection = () => { |
||||
|
isPageRefreshing = true |
||||
|
} |
||||
|
|
||||
|
// 监听路由变化
|
||||
|
watch( |
||||
|
() => router.currentRoute.value.path, |
||||
|
(newPath) => { |
||||
|
const isProjectRoute = projectRoutes.some((route) => newPath.startsWith(route)) |
||||
|
let isProjectRouteName = projectRoutes[0] |
||||
|
console.log(isProjectRouteName) |
||||
|
// 判断是否是 searchCode.html 的访问
|
||||
|
const isSearchCodePage = window.location.pathname.includes('duobaoqibing') |
||||
|
if (!isProjectRoute && !isSearchCodePage) { |
||||
|
console.log('离开项目路由:', newPath) |
||||
|
sendTrackingData() |
||||
|
} else if (isProjectRouteName && !hasRecordedEntry.value) { |
||||
|
console.log('首次进入项目路由:', newPath) |
||||
|
recordEntryTime() |
||||
|
} |
||||
|
} |
||||
|
) |
||||
|
|
||||
|
// 添加事件监听
|
||||
|
onMounted(() => { |
||||
|
document.addEventListener('visibilitychange', handleVisibilityChange) |
||||
|
window.addEventListener('beforeunload', handleBeforeUnload) |
||||
|
window.addEventListener('unload', handleRefreshDetection) |
||||
|
}) |
||||
|
|
||||
|
// 移除事件监听
|
||||
|
onBeforeUnmount(() => { |
||||
|
document.removeEventListener('visibilitychange', handleVisibilityChange) |
||||
|
window.removeEventListener('beforeunload', handleBeforeUnload) |
||||
|
window.removeEventListener('unload', handleRefreshDetection) |
||||
|
}) |
||||
|
|
||||
|
return { |
||||
|
entryTime, |
||||
|
isInProject, |
||||
|
sendTrackingData |
||||
|
} |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
import { defineStore } from 'pinia' |
||||
|
|
||||
|
export const useAudioStore = defineStore('audio', { |
||||
|
state: () => ({ |
||||
|
soundInstance: null, // Howl 实例
|
||||
|
isPlaying: false, // 播放状态
|
||||
|
isVoiceEnabled: true // 新增声音开关状态
|
||||
|
}), |
||||
|
actions: { |
||||
|
// 设置音频实例
|
||||
|
setAudioInstance(instance) { |
||||
|
this.soundInstance = instance |
||||
|
}, |
||||
|
// 播放控制
|
||||
|
play() { |
||||
|
if (this.soundInstance) { |
||||
|
this.soundInstance.play() |
||||
|
this.isPlaying = true |
||||
|
} |
||||
|
}, |
||||
|
// 暂停控制
|
||||
|
pause() { |
||||
|
if (this.soundInstance) { |
||||
|
this.soundInstance.pause() |
||||
|
this.isPlaying = false |
||||
|
} |
||||
|
}, |
||||
|
toggleVoice() { |
||||
|
this.isVoiceEnabled = !this.isVoiceEnabled |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
@ -0,0 +1,16 @@ |
|||||
|
import { defineStore } from 'pinia'; |
||||
|
|
||||
|
export const useChatStore = defineStore('chat', { |
||||
|
state: () => ({ |
||||
|
messages: [] |
||||
|
}), |
||||
|
persist: { |
||||
|
enabled: true, |
||||
|
strategies: [ |
||||
|
{ |
||||
|
key: 'chat_messages', |
||||
|
storage: localStorage |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}); |
@ -1,40 +1,95 @@ |
|||||
<script setup></script> |
|
||||
|
<script setup> |
||||
|
import { ref, onMounted } from "vue"; |
||||
|
import { getAnnouncementAPI } from "../api/AIxiaocaishen"; |
||||
|
|
||||
|
const announcementVideo = ref({}); |
||||
|
const getAnnouncement = async () => { |
||||
|
const result = await getAnnouncementAPI() |
||||
|
console.log(result.data, "result.data"); |
||||
|
announcementVideo.value.url = result.data[0].url; |
||||
|
announcementVideo.value.img = result.data[0].img; |
||||
|
|
||||
|
console.log(announcementVideo.value, "announcementVideo"); |
||||
|
} |
||||
|
|
||||
|
const handleVideoPlay = () => { |
||||
|
console.log('视频开始播放'); |
||||
|
// 可以在这里添加播放统计逻辑 |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
getAnnouncement() |
||||
|
}) |
||||
|
|
||||
|
</script> |
||||
<template> |
<template> |
||||
<el-main class="homepage-body"> |
|
||||
<div class="main-wrapper"> |
<div class="main-wrapper"> |
||||
<img |
|
||||
src="src\assets\img\AIchat\AIgif1.gif" |
|
||||
alt="图片加载失败" |
|
||||
class="logo1" |
|
||||
/> |
|
||||
|
<div class="video-container"> |
||||
|
<video ref="videoPlayer" :poster="announcementVideo.img" :src="announcementVideo.url" controls |
||||
|
class="video-player" @play="handleVideoPlay"> |
||||
|
Your browser does not support the video tag. |
||||
|
</video> |
||||
|
</div> |
||||
<!-- 一段文字,水平居中,宽度为500px --> |
<!-- 一段文字,水平居中,宽度为500px --> |
||||
|
|
||||
<div style="width: 500px; margin: 0 auto; text-align: center"> |
|
||||
<p> |
|
||||
欢迎使用AI智能问答系统,本系统基于OpenAI的GPT-3.5模型,为您提供智能问答服务。 |
|
||||
</p> |
|
||||
<p>这个是公告部分</p> |
|
||||
|
<div class="announcement"> |
||||
|
<p class="announcementItem">各位AI小财神的用户,大家好!</p> |
||||
|
<p class="announcementItem">试运行期间,用户可在AI小财神中查看全</p> |
||||
|
<p class="announcementItem">市场数据,每个市场可查看20只股票.</p> |
||||
|
<p class="announcementItem">试运行结束后,会员用户可查看市场与弘</p> |
||||
|
<p class="announcementItem">历软件云版静态市场一致!</p> |
||||
|
<p class="announcementItem">特此公告!</p> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
</el-main> |
|
||||
</template> |
</template> |
||||
<style scoped> |
<style scoped> |
||||
.homepage-body { |
|
||||
padding: 0px; |
|
||||
height: calc(100% - 70px); |
|
||||
/* 根据底部高度调整 */ |
|
||||
} |
|
||||
|
|
||||
.main-wrapper { |
.main-wrapper { |
||||
height: 100%; |
height: 100%; |
||||
display: flex; |
display: flex; |
||||
flex-direction: column; |
flex-direction: column; |
||||
|
overflow-y: auto; |
||||
} |
} |
||||
.logo1 { |
|
||||
/* 居中显示 30%大小 不要拉伸*/ |
|
||||
display: block; |
|
||||
width: 30%; |
|
||||
height: auto; |
|
||||
/* 水平居中 */ |
|
||||
margin: 0 auto; |
|
||||
|
|
||||
|
.video-container { |
||||
|
max-width: 90%; /* 从 800px 改为百分比 */ |
||||
|
width: 100%; |
||||
|
margin: 20px auto; |
||||
|
border-radius: 8px; |
||||
|
overflow: hidden; |
||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); |
||||
|
flex-shrink: 0; |
||||
} |
} |
||||
|
|
||||
|
.video-player { |
||||
|
width: 100%; |
||||
|
aspect-ratio: 16/9; |
||||
|
background-color: #000; |
||||
|
object-fit: contain; /* 从 cover 改为 contain */ |
||||
|
} |
||||
|
|
||||
|
/* 添加移动端适配 */ |
||||
|
@media (max-width: 768px) { |
||||
|
.video-container { |
||||
|
margin: 10px auto; |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
|
||||
|
.announcement { |
||||
|
max-width: 90%; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.announcement { |
||||
|
max-width: 500px; |
||||
|
margin: 20px 10%; |
||||
|
font-size: 20px; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.announcementItem { |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
</style> |
</style> |
@ -0,0 +1,219 @@ |
|||||
|
<template> |
||||
|
<!-- 趋势研判K线图 --> |
||||
|
<div ref="KlineCanvs" class="KlineClass"></div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import * as echarts from 'echarts' |
||||
|
import { useDataStore } from '@/store/dataList' |
||||
|
// import { useLanguage } from '@/utils/languageService' |
||||
|
// const { translate, t } = useLanguage() |
||||
|
const KlineCanvs = ref() // Echarts实例 |
||||
|
const dataStore = useDataStore() |
||||
|
const klineData = computed(() => dataStore.klineData) |
||||
|
watch( |
||||
|
klineData, |
||||
|
(newValue) => { |
||||
|
const currentData = newValue // 深拷贝防止切换回来数据更改 |
||||
|
if (currentData) { |
||||
|
nextTick(() => { |
||||
|
KlineCanvsEcharts(currentData) |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
{ immediate: true, deep: true } |
||||
|
) |
||||
|
// 监听数据变化或语言变化 |
||||
|
// watch( |
||||
|
// () => t.value, |
||||
|
// (newLang) => { |
||||
|
// // 更新 textEcharts 内容并重新渲染图表 |
||||
|
// if (klineData.value) { |
||||
|
// const currentData = klineData |
||||
|
// nextTick(() => { |
||||
|
// KlineCanvsEcharts(currentData) |
||||
|
// }) |
||||
|
// } |
||||
|
// }, |
||||
|
// { immediate: true, deep: true } |
||||
|
// ) |
||||
|
function KlineCanvsEcharts(datatok) { |
||||
|
// const textEcahrts = t.value // 创建多语言实例 |
||||
|
const data = datatok.Kline |
||||
|
// 切割数据方法 |
||||
|
const spliteDate = (a) => { |
||||
|
const categoryData = [] |
||||
|
let value = [] |
||||
|
for (let i = 0; i < a.length; i++) { |
||||
|
categoryData.push(a[i][0]) |
||||
|
value.push([a[i][1],a[i][2],a[i][3],a[i][4]]) |
||||
|
} |
||||
|
return { categoryData, value } |
||||
|
} |
||||
|
const dealData = spliteDate(data) |
||||
|
// 给配置项 |
||||
|
const KlineOption = { |
||||
|
title: { |
||||
|
// text: k_name, |
||||
|
// Canvs的间隔大小 |
||||
|
text: datatok.name, |
||||
|
top: 20, |
||||
|
left: 20 |
||||
|
}, |
||||
|
tooltip: { |
||||
|
trigger: 'axis', // 触发类型 坐标轴触发 |
||||
|
// 调用接口之后方法 |
||||
|
formatter: function (a, b, d) { |
||||
|
let def = |
||||
|
a[0].name + |
||||
|
'<br/>' + |
||||
|
'开盘价' + |
||||
|
a[0].data[1] + |
||||
|
'<br/>' + |
||||
|
'收盘价' + |
||||
|
a[0].data[2] + |
||||
|
'<br/>' + |
||||
|
'最低价' + |
||||
|
a[0].data[3] + |
||||
|
'<br/>' + |
||||
|
'最高价' + |
||||
|
a[0].data[4] |
||||
|
// 判断a[1]是否存在 |
||||
|
if (a[1] && a[1].seriesName) { |
||||
|
def += '<br/>' + a[1].seriesName + ':' + a[1].value |
||||
|
} |
||||
|
return def |
||||
|
}, |
||||
|
// 鼠标放上去出现的数据展示 |
||||
|
axisPointer: { |
||||
|
animation: false, |
||||
|
type: 'line', |
||||
|
linestyle: { |
||||
|
color: '#376df4', |
||||
|
width: 2, |
||||
|
opacity: 1 |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
// 横坐标内容 |
||||
|
xAxis: { |
||||
|
type: 'category', |
||||
|
data: dealData.categoryData, |
||||
|
axisLine: { lineStyle: { color: '#8392A5' } } |
||||
|
}, |
||||
|
//控制坐标轴 |
||||
|
grid: { |
||||
|
left: '12%', |
||||
|
right: '10%', |
||||
|
bottom: '10%', |
||||
|
top: '18%' |
||||
|
}, |
||||
|
yAxis: { |
||||
|
scale: !0, //true |
||||
|
// 自定义纵坐标现实的数据 |
||||
|
axisLabel: { |
||||
|
formatter: function (value) { |
||||
|
return value // 返回原始值 |
||||
|
} |
||||
|
}, |
||||
|
axisLine: { lineStyle: { color: '#8392A5' } }, |
||||
|
splitLine: { |
||||
|
show: !1 |
||||
|
} |
||||
|
}, |
||||
|
// 下拉条 |
||||
|
dataZoom: [ |
||||
|
{ |
||||
|
textStyle: { |
||||
|
color: '#8392A5' |
||||
|
}, |
||||
|
handleIcon: |
||||
|
'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', |
||||
|
handleSize: '80%', |
||||
|
dataBackground: { |
||||
|
areaStyle: { |
||||
|
color: '#8392A5' |
||||
|
}, |
||||
|
lineStyle: { |
||||
|
opacity: 0.8, |
||||
|
color: '#8392A5' |
||||
|
} |
||||
|
}, |
||||
|
handleStyle: { |
||||
|
color: '#fff', |
||||
|
shadowBlur: 3, |
||||
|
shadowColor: 'rgba(0, 0, 0, 0.6)', |
||||
|
shadowOffsetX: 2, |
||||
|
shadowOffsetY: 2 |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
show: !1, |
||||
|
type: 'slider' |
||||
|
}, |
||||
|
{ |
||||
|
type: 'inside' |
||||
|
} |
||||
|
], |
||||
|
animation: !1, //false |
||||
|
// 线条的数据 |
||||
|
series: [ |
||||
|
{ |
||||
|
type: 'candlestick', |
||||
|
name: '\u65e5K', |
||||
|
// 数据 |
||||
|
data: dealData.value, |
||||
|
itemStyle: { |
||||
|
normal: { |
||||
|
color0: '#FD1050', |
||||
|
color: '#0CF49B', |
||||
|
borderColor0: '#FD1050', |
||||
|
borderColor: '#0CF49B' |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
name: 'MA5', |
||||
|
type: 'line', |
||||
|
// 此处需要接口调用同类型数据 |
||||
|
// 计算出MA5的数据 |
||||
|
data: (function (a) { |
||||
|
for (var MA5 = [], d = 0, g = dealData.value.length; d < g; d++) { |
||||
|
if (d < a) { |
||||
|
MA5.push('-') |
||||
|
} else { |
||||
|
for (var f = 0, e = 0; e < a; e++) { |
||||
|
f += dealData.value[d - e][1] |
||||
|
} |
||||
|
MA5.push((f / a).toFixed(2)) |
||||
|
} |
||||
|
} |
||||
|
return MA5 |
||||
|
})(5), |
||||
|
smooth: !0 |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
// 创造echarts图 |
||||
|
const KlineCanvsChart = echarts.init(KlineCanvs.value) |
||||
|
KlineCanvsChart.setOption(KlineOption) |
||||
|
// 窗口大小变化时重置图表大小 |
||||
|
window.addEventListener('resize', () => { |
||||
|
KlineCanvsChart.resize() |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// fnGetData() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.KlineClass { |
||||
|
position: relative; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
left: 5%; |
||||
|
bottom: 8%; |
||||
|
} |
||||
|
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue