You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
454 lines
9.8 KiB
454 lines
9.8 KiB
<template>
|
|
<view
|
|
:prop="config"
|
|
:change:prop="tvchart.changeConfig"
|
|
id="tradingview_10798345"
|
|
></view>
|
|
</template>
|
|
<script>
|
|
import { mapState } from "vuex";
|
|
import Option from "@/api/option";
|
|
|
|
export default {
|
|
props: {
|
|
// 商品
|
|
serveSymbolName: {
|
|
required: true,
|
|
type: String,
|
|
},
|
|
// 精度
|
|
serveInterval: {
|
|
required: true,
|
|
type: [String, Number],
|
|
},
|
|
// 精度组
|
|
serveResolutions: {
|
|
default: () => ["5", "15", "30", "60", "1D", "1W", "1M"],
|
|
required: false,
|
|
type: Array,
|
|
},
|
|
contract: {
|
|
default: false,
|
|
type: Boolean,
|
|
required: false,
|
|
},
|
|
},
|
|
watch: {
|
|
serveInterval(n) {
|
|
this.config = {
|
|
type: "changeInterval",
|
|
interval: n,
|
|
_v: Math.random(),
|
|
};
|
|
},
|
|
serveSymbolName(n) {
|
|
this.config = {
|
|
type: "changeSymbol",
|
|
symbolName: n,
|
|
_v: Math.random(),
|
|
};
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
config: {},
|
|
socketMsg: "",
|
|
};
|
|
},
|
|
computed: {
|
|
...mapState({
|
|
tlang: "lang",
|
|
tTheme: "theme",
|
|
webSocket: "ws",
|
|
webSocket1: "ws1",
|
|
}),
|
|
ws() {
|
|
return this.contract ? this.webSocket1 : this.webSocket;
|
|
},
|
|
},
|
|
methods: {
|
|
initConfig() {
|
|
return {
|
|
type: "initChart",
|
|
symbolName: this.serveSymbolName,
|
|
interval: this.serveInterval,
|
|
resolutions: this.serveResolutions,
|
|
theme: this.tTheme,
|
|
lang: this.tlang,
|
|
contract: this.contract,
|
|
};
|
|
},
|
|
// 接受图表的通知
|
|
onchartEmit(obj) {
|
|
switch (obj.type) {
|
|
case "changeSymbol":
|
|
this.getData(obj.data, obj.funName);
|
|
break;
|
|
case "changeInterval":
|
|
this.$emit("changeInterval", obj.resolution);
|
|
break;
|
|
case "sub":
|
|
this.toSub(obj.data);
|
|
break;
|
|
case "unsub":
|
|
this.toUnSub(obj.data);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
},
|
|
// 订阅
|
|
toSub(data) {
|
|
this.socketMsg = data.msg;
|
|
this.ws.send(data);
|
|
},
|
|
// 取消订阅
|
|
toUnSub(data) {
|
|
this.ws.send(data);
|
|
},
|
|
getData(data, name) {
|
|
Option.getKline(data).then((res) => {
|
|
this.config = {};
|
|
setTimeout(() => {
|
|
this.config = {
|
|
type: "changeList",
|
|
funName: name,
|
|
data: res,
|
|
_v: Math.random(),
|
|
};
|
|
}, 60);
|
|
});
|
|
},
|
|
socketGetData() {
|
|
this.ws.on("message", (res) => {
|
|
this.config = {
|
|
type: "addPoint",
|
|
data: res,
|
|
};
|
|
});
|
|
},
|
|
},
|
|
created() {
|
|
this.config = this.initConfig();
|
|
this.socketGetData();
|
|
|
|
},
|
|
destroyed() {
|
|
this.toUnSub(this.socketMsg);
|
|
},
|
|
};
|
|
</script>
|
|
<script module="tvchart" lang="renderjs">
|
|
import Datafeed from "@/plugins/datafeed.js";
|
|
import tvStyle from "@/plugins/tvStyle.js";
|
|
import { $get } from "@/api/serve/webaxios.js";
|
|
|
|
let exchangeAjax = {
|
|
getKline:'/option/getKline',
|
|
getSymbol(name) {
|
|
return name.split("/").join("").toLowerCase();
|
|
},
|
|
msg:'Kline_'
|
|
};
|
|
let contractAjax = {
|
|
getKline:'/contract/getKline',
|
|
getSymbol(name) {
|
|
return name.split("/")[0];
|
|
},
|
|
msg:'swapKline_'
|
|
};
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
datafeed: undefined,
|
|
page: 1,
|
|
onRealtimeCallback: undefined,
|
|
TView: undefined,
|
|
interval: 5,
|
|
symbolName: '',
|
|
theme: 'Dark',
|
|
lang: uni.getStorageSync('language')||'zh-CN',
|
|
resolutions: ["5", "15", "30", "60", "1D", "1W", "1M"],
|
|
isLoad:false,
|
|
funMap:{},
|
|
webContract:false
|
|
};
|
|
},
|
|
computed: {
|
|
symbol() {
|
|
return this.symbolName;
|
|
},
|
|
msg() {
|
|
return `${this.ajaxTv.msg}${this.ajaxTv.getSymbol(this.symbol)}_${this.resolution(this.interval)}`;
|
|
},
|
|
// 图表语言映射
|
|
chartLang() {
|
|
switch (this.lang) {
|
|
case "zh-CN":
|
|
return 'zh';
|
|
case "zh-TW":
|
|
return 'zh_TW';
|
|
case "tr":
|
|
return 'tr';
|
|
case "jp":
|
|
return 'ja';
|
|
case "kor":
|
|
return 'ko';
|
|
case "de":
|
|
return 'de_DE';
|
|
case "fra":
|
|
return 'fr';
|
|
case "it":
|
|
return 'it';
|
|
case "pt":
|
|
return 'pt';
|
|
case "spa":
|
|
return 'es';
|
|
default:
|
|
return 'en';
|
|
}
|
|
},
|
|
ajaxTv() {
|
|
return this.webContract ? contractAjax : exchangeAjax;
|
|
},
|
|
},
|
|
watch: {
|
|
symbol(n) {
|
|
if (this.TView) this.TView.setSymbol(n, this.interval);
|
|
},
|
|
msg(n, o) {
|
|
this.page = 1;
|
|
if (o) {
|
|
this.unSub(o);
|
|
}
|
|
},
|
|
interval(n) {
|
|
if (this.TView) this.TView.activeChart().setResolution(n);
|
|
},
|
|
},
|
|
methods: {
|
|
getMap(data) {
|
|
return {
|
|
time: data.id * 1000,
|
|
close: data.close*1,
|
|
open: data.open*1,
|
|
high: data.high*1,
|
|
low: data.low*1,
|
|
volume: data.vol*1,
|
|
};
|
|
},
|
|
// 获取传给后台的精度
|
|
resolution(resolution) {
|
|
let T = "";
|
|
if (isNaN(resolution * 1)) {
|
|
T = resolution
|
|
.replace("D", "day")
|
|
.replace("W", "week")
|
|
.replace("M", "mon");
|
|
} else {
|
|
if (resolution > 60) {
|
|
T = Math.floor(resolution / 60) + "hours";
|
|
} else {
|
|
T = resolution + "min";
|
|
}
|
|
}
|
|
return T;
|
|
},
|
|
// 获取数据
|
|
getBars(
|
|
symbolInfo,
|
|
resolution,
|
|
rangeStartDate,
|
|
rangeEndDate,
|
|
onLoadedCallback
|
|
) {
|
|
|
|
let page = this.page > 3 ? 3 : this.page;
|
|
let data = {
|
|
symbol: this.ajaxTv.getSymbol(symbolInfo.name),
|
|
period: this.resolution(resolution),
|
|
form: rangeStartDate,
|
|
to: rangeEndDate,
|
|
size: page * 200,
|
|
};
|
|
this.page++;
|
|
// this.$emit("changeInterval", resolution);
|
|
|
|
// 不存在商品
|
|
if (!this.symbol) {
|
|
onLoadedCallback([]);
|
|
return;
|
|
}
|
|
|
|
this.isLoad = true
|
|
// 获取商品
|
|
$get(this.ajaxTv.getKline,data).then(res=>{
|
|
this.isLoad = false
|
|
this.$ownerInstance.callMethod('onchartEmit', {
|
|
type: 'changeInterval',
|
|
resolution
|
|
})
|
|
let arr = res.data.data.map((item) => {
|
|
return this.getMap(item);
|
|
});
|
|
onLoadedCallback(arr);
|
|
this.sub();
|
|
setTimeout(() => {
|
|
onLoadedCallback([]);
|
|
}, 60);
|
|
})
|
|
},
|
|
// 订阅消息
|
|
sub() {
|
|
this.$ownerInstance.callMethod('onchartEmit', {
|
|
type: 'sub',
|
|
data:{
|
|
cmd: "sub",
|
|
msg: this.msg
|
|
}
|
|
})
|
|
},
|
|
// 取消订阅
|
|
unSub(name) {
|
|
this.$ownerInstance.callMethod('onchartEmit', {
|
|
type: 'unsub',
|
|
data:{
|
|
cmd: "unsub",
|
|
msg: name,
|
|
}
|
|
})
|
|
},
|
|
// tradingview触发单条数据订阅
|
|
subscribeBars(
|
|
symbolInfo,
|
|
resolution,
|
|
onRealtimeCallback,
|
|
subscriberUID,
|
|
onResetCacheNeededCallback
|
|
) {
|
|
this.onRealtimeCallback = onRealtimeCallback;
|
|
if (!this.symbol) {
|
|
setTimeout(() => {
|
|
onResetCacheNeededCallback();
|
|
}, 100);
|
|
}
|
|
},
|
|
initDataFeed() {
|
|
this.datafeed = new Datafeed(this);
|
|
|
|
},
|
|
|
|
initTradingView() {
|
|
let TradingView = window.TradingView;
|
|
this.TView = new TradingView.widget({
|
|
fullscreen: false,
|
|
autosize: true,
|
|
interval: this.interval,
|
|
timezone: "Asia/Shanghai",
|
|
theme: "Dark", // 自定义主题
|
|
// style: "1",
|
|
library_path: "./static/chart_main/",
|
|
datafeed: this.datafeed,
|
|
// datafeed: {},
|
|
locale: this.chartLang,
|
|
toolbar_bg: this.theme == "light" ? "#fff" : "#2b2b37",
|
|
enable_publishing: false,
|
|
withdateranges: false,
|
|
hide_side_toolbar: false,
|
|
allow_symbol_change: true,
|
|
show_popup_button: true,
|
|
hideideas: true,
|
|
studies_overrides: {},
|
|
container_id: "tradingview_10798345",
|
|
disabled_features: [
|
|
"header_symbol_search",
|
|
"header_compare",
|
|
"control_bar",
|
|
"main_series_scale_menu",
|
|
"volume_force_overlay",
|
|
"header_resolutions",
|
|
"legend_context_menu",
|
|
"symbol_search_hot_key",
|
|
"symbol_info",
|
|
// "edit_buttons_in_legend",
|
|
"pane_context_menu",
|
|
],
|
|
overrides: tvStyle[this.theme],
|
|
custom_css_url: this.theme == "light" ? "light-chart.css" : "chart.css",
|
|
});
|
|
this.TView.onChartReady(() => {
|
|
this.TView.chart().createStudy("MACD", false, false);
|
|
});
|
|
},
|
|
// 初始化
|
|
initPage() {
|
|
this.initDataFeed();
|
|
this.initTradingView();
|
|
// this.socketGetData();
|
|
},
|
|
changeConfig(n, o) {
|
|
this.onserveEmit(n)
|
|
},
|
|
// 接受应用通知
|
|
onserveEmit(obj) {
|
|
switch (obj.type) {
|
|
// 变更商品
|
|
case 'changeSymbol':
|
|
this.symbolName = obj.symbolName
|
|
break;
|
|
// 变更精度
|
|
case 'changeInterval':
|
|
if(this.isLoad) return;
|
|
this.interval = obj.interval;
|
|
break;
|
|
// 变更精度组
|
|
case 'resetResolutions':
|
|
this.resolutions = obj.resolutions
|
|
break;
|
|
// 初始化图表
|
|
case 'initChart':
|
|
this.symbolName = obj.symbolName
|
|
this.interval = obj.interval
|
|
this.resolutions = obj.resolutions
|
|
this.theme = obj.theme
|
|
this.lang = obj.lang
|
|
this.webContract = obj.contract;
|
|
if(localStorage.tvTheme!=this.theme){
|
|
localStorage.removeItem('tradingview.chartproperties')
|
|
}
|
|
localStorage.setItem('tvTheme',this.theme)
|
|
break;
|
|
// 追加节点
|
|
case 'addPoint':
|
|
let { data, sub } = obj.data;
|
|
if (sub == this.msg&&this.onRealtimeCallback) {
|
|
this.onRealtimeCallback(this.getMap(data));
|
|
}
|
|
break;
|
|
// 销毁
|
|
case 'destroyed':
|
|
// this.unSub(this.msg);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
this.onserveEmit(this.config)
|
|
if (typeof echarts === 'function') {
|
|
this.initPage()
|
|
} else {
|
|
const script = document.createElement('script')
|
|
script.src = './static/chart_main/charting_library.min.js'
|
|
script.onload = this.initPage.bind(this)
|
|
document.head.appendChild(script)
|
|
}
|
|
|
|
},
|
|
destroyed() {
|
|
this.unSub(this.msg);
|
|
},
|
|
};
|
|
</script>
|
|
|