mirror of
https://git.hmsn.ink/coin/chart.git
synced 2026-03-20 00:02:17 +09:00
차트 정의 완료
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
"@cssninja/bulma-css-vars": "0.9.2",
|
"@cssninja/bulma-css-vars": "0.9.2",
|
||||||
"@fontsource/noto-sans-kr": "^5.2.5",
|
"@fontsource/noto-sans-kr": "^5.2.5",
|
||||||
"@intlify/unplugin-vue-i18n": "^6.0.8",
|
"@intlify/unplugin-vue-i18n": "^6.0.8",
|
||||||
|
"@mdi/font": "^7.4.47",
|
||||||
"@unhead/addons": "^2.0.14",
|
"@unhead/addons": "^2.0.14",
|
||||||
"@unhead/vue": "^2.0.14",
|
"@unhead/vue": "^2.0.14",
|
||||||
"@vueuse/core": "10.9.0",
|
"@vueuse/core": "10.9.0",
|
||||||
@@ -24,11 +25,14 @@
|
|||||||
"notyf": "3.10.0",
|
"notyf": "3.10.0",
|
||||||
"pinia": "2.1.7",
|
"pinia": "2.1.7",
|
||||||
"std-env": "^3.9.0",
|
"std-env": "^3.9.0",
|
||||||
|
"technicalindicators": "^3.1.0",
|
||||||
"unplugin-vue-router": "^0.15.0",
|
"unplugin-vue-router": "^0.15.0",
|
||||||
"vite-plugin-pwa": "^1.0.3",
|
"vite-plugin-pwa": "^1.0.3",
|
||||||
|
"vite-plugin-vuetify": "^2.1.2",
|
||||||
"vue": "^3.5.18",
|
"vue": "^3.5.18",
|
||||||
"vue-i18n": "9.13.1",
|
"vue-i18n": "9.13.1",
|
||||||
"vue-router": "4.3.2",
|
"vue-router": "4.3.2",
|
||||||
|
"vuetify": "^3.9.5",
|
||||||
"yarn": "^1.22.22"
|
"yarn": "^1.22.22"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
298
src/App.vue
298
src/App.vue
@@ -10,104 +10,178 @@ import { ref } from 'vue';
|
|||||||
*/
|
*/
|
||||||
import LWChart from './components/LWChart.vue';
|
import LWChart from './components/LWChart.vue';
|
||||||
import {getCandleList} from '/@src/utils/api'
|
import {getCandleList} from '/@src/utils/api'
|
||||||
|
import {calculateMACD, calculateEMA, calculateBollingerBands} from "./utils/meter.js";
|
||||||
/**
|
/**
|
||||||
* Generates sample data for the lightweight chart
|
* Generates sample data for the lightweight chart
|
||||||
* @param {Boolean} ohlc Whether generated dat should include open, high, low, and close values
|
* @param {Boolean} ohlc Whether generated dat should include open, high, low, and close values
|
||||||
* @returns {Array} sample data
|
* @returns {Array} sample data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const socket = new WebSocket("ws://127.0.0.1:8765");
|
let socket;
|
||||||
const sk = new WebSocket("wss://fx-ws.gateio.ws/v4/ws/usdt")
|
const lazyLock = ref(false)
|
||||||
let oldLow = 9999999999;
|
|
||||||
let oldHigh = 0;
|
|
||||||
let lock = false
|
|
||||||
|
|
||||||
const contract = ref('BTC_USDT')
|
const contract = ref('BTC_USDT')
|
||||||
|
const time = ref('15m')
|
||||||
const chartOptions = ref({
|
const chartOptions = ref({
|
||||||
layout: {
|
layout: {
|
||||||
textColor: 'black',
|
textColor: 'black',
|
||||||
background: { type: 'solid', color: 'white' },
|
background: { type: 'solid', color: 'white' },
|
||||||
|
panes: {
|
||||||
|
separatorColor: '#FF0000',
|
||||||
},
|
},
|
||||||
height: 800,
|
},
|
||||||
|
height: 1300,
|
||||||
timeScale: {
|
timeScale: {
|
||||||
timeVisible: true, // 분/초까지 표시
|
timeVisible: true, // 분/초까지 표시
|
||||||
secondsVisible: false // 초 숨기고 싶으면 false
|
secondsVisible: false // 초 숨기고 싶으면 false
|
||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
const candleTick = ref([]);
|
|
||||||
const seriesOptions = ref({
|
const candleTick = ref([])
|
||||||
|
const ema = ref([])
|
||||||
|
const macd = ref({})
|
||||||
|
const bb = ref({})
|
||||||
|
|
||||||
|
const selectCoins = [
|
||||||
|
{ title: 'BTC_USDT'},
|
||||||
|
{ title: 'ETH_USDT'},
|
||||||
|
{ title: 'SOL_USDT'},
|
||||||
|
{ title: 'XRP_USDT'},
|
||||||
|
{ title: 'LINK_USDT'},
|
||||||
|
]
|
||||||
|
const selectTimes = [
|
||||||
|
{ title: '5m'},
|
||||||
|
{ title: '15m'},
|
||||||
|
{ title: '30m'},
|
||||||
|
{ title: '1h'},
|
||||||
|
{ title: '4h'},
|
||||||
|
{ title: '8h'},
|
||||||
|
{ title: '1d'},
|
||||||
|
{ title: '7d'},
|
||||||
|
]
|
||||||
|
|
||||||
|
const line_color = [
|
||||||
|
'#AD81FF',
|
||||||
|
'#FFBAB5',
|
||||||
|
'#6BFF74',
|
||||||
|
'#FFFC7B',
|
||||||
|
'#27FFE6',
|
||||||
|
'#225EFF'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
const emaLineOptions = ref({
|
||||||
|
lineWidth: 2,
|
||||||
|
color: '#AD81FF',
|
||||||
|
title: 'EMA',
|
||||||
|
});
|
||||||
|
|
||||||
|
const candleOptions = ref({
|
||||||
upColor: '#26a69a',
|
upColor: '#26a69a',
|
||||||
downColor: '#ef5350',
|
downColor: '#ef5350',
|
||||||
borderVisible: false,
|
borderVisible: false,
|
||||||
wickUpColor: '#26a69a',
|
wickUpColor: '#26a69a',
|
||||||
wickDownColor: '#ef5350',
|
wickDownColor: '#ef5350',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const macdLineOptions = ref({
|
||||||
|
lineWidth: 2,
|
||||||
|
color: '#FFBAB5'
|
||||||
|
});
|
||||||
|
|
||||||
|
const macdSignalOptions = ref({
|
||||||
|
lineWidth: 2,
|
||||||
|
color: '#AD81FF'
|
||||||
|
});
|
||||||
|
|
||||||
|
const macdHistogramOptions = ref({
|
||||||
|
lineWidth: 2,
|
||||||
|
color: '#27FFE6'
|
||||||
|
});
|
||||||
|
|
||||||
|
const upperBbOptions = ref({
|
||||||
|
color: '#006AFF', // 주황색 계열
|
||||||
|
lineStyle: 0, // 점선 스타일 (선택)
|
||||||
|
lineWidth: 2,
|
||||||
|
title: 'UpperBB',
|
||||||
|
})
|
||||||
|
|
||||||
|
const middleBbOptions = ref({
|
||||||
|
color: '#006AFF', // 주황색 계열
|
||||||
|
lineStyle: 1, // 점선 스타일 (선택)
|
||||||
|
lineWidth: 0,
|
||||||
|
title: 'MiddleBB(SMA)',
|
||||||
|
})
|
||||||
|
|
||||||
|
const lowerBbOptions = ref({
|
||||||
|
color: '#006AFF', // 주황색 계열
|
||||||
|
lineStyle: 0, // 점선 스타일 (선택)
|
||||||
|
lineWidth: 2,
|
||||||
|
title: 'LowerBB',
|
||||||
|
})
|
||||||
|
|
||||||
const chartType = ref('candlestick');
|
const chartType = ref('candlestick');
|
||||||
const lwChart = ref();
|
const lwChart = ref();
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getCandleList('BTC_USDT', '1m').then(data => {
|
init(contract.value, time.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const init = (cont, ti) => {
|
||||||
|
socket = new WebSocket("ws://127.0.0.1:8765");
|
||||||
|
const dt = new Date()
|
||||||
|
getCandleList(cont, ti, Math.round(dt.getTime()/1000)).then(data => {
|
||||||
|
ema.value = calculateEMA(data, 20)
|
||||||
|
macd.value = calculateMACD(data)
|
||||||
|
bb.value = calculateBollingerBands(data)
|
||||||
candleTick.value = data
|
candleTick.value = data
|
||||||
|
console.log(bb.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
candleSocket(cont, ti)
|
||||||
tradeSocket()
|
|
||||||
candleSocket('1m')
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
const tradeSocket = () => {
|
|
||||||
sk.addEventListener('open', (event) => {
|
|
||||||
sk.send(JSON.stringify({"time" : curToUnix(), "channel" : "futures.trades",
|
|
||||||
"event": "subscribe", "payload" : [contract.value]}))
|
|
||||||
})
|
|
||||||
|
|
||||||
sk.addEventListener('message', (message) => {
|
|
||||||
const getDa = JSON.parse(message.data)
|
|
||||||
if (getDa.event === 'update') {
|
|
||||||
// console.log(getDa.result[0].price)
|
|
||||||
|
|
||||||
if(candleTick.value.length === 0) return
|
|
||||||
|
|
||||||
const lastCandle = candleTick.value[candleTick.value.length - 1]
|
|
||||||
|
|
||||||
const price = parseFloat(getDa.result[0].price);
|
|
||||||
lastCandle.close = price
|
|
||||||
if(oldLow > candleTick.value[candleTick.value.length - 1].close) {
|
|
||||||
lastCandle.low = price
|
|
||||||
oldLow = price;
|
|
||||||
}
|
|
||||||
if(oldHigh < candleTick.value[candleTick.value.length - 1].open) {
|
|
||||||
lastCandle.high = price
|
|
||||||
oldHigh = price;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
candleTick.value[candleTick.value.length - 1] = lastCandle
|
const distroy = (cont) => {
|
||||||
candleTick.value = [...candleTick.value]
|
candleTick.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lazyLoad = async (ti) => {
|
||||||
|
lazyLock.value = true
|
||||||
|
getCandleList(contract.value, time.value, ti).then(data => {
|
||||||
|
candleTick.value = [...data, ...candleTick.value]
|
||||||
|
ema.value = calculateEMA(data, 20)
|
||||||
|
macd.value = calculateMACD(data)
|
||||||
|
bb.value = calculateBollingerBands(data)
|
||||||
|
setTimeout(() => {lazyLock.value = false}, 3000)
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const candleSocket = (time) => {
|
const candleSocket = (cont, ti) => {
|
||||||
socket.addEventListener('open', (event) => {
|
socket.addEventListener('open', (event) => {
|
||||||
socket.send(JSON.stringify({"type":"subscribe", "channel": contract.value, "time": time}))
|
socket.send(JSON.stringify({"type":"subscribe", "channel": cont, "time": ti}))
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.addEventListener('message', (message) => {
|
socket.addEventListener('message', (message) => {
|
||||||
lock = true
|
const msg = JSON.parse(message.data)
|
||||||
const getDa = JSON.parse(message.data)
|
const item = JSON.parse(msg.msg)
|
||||||
const msg = JSON.parse(getDa.msg)
|
|
||||||
const tick = msg.data[0]
|
|
||||||
const ticks = candleTick.value
|
const ticks = candleTick.value
|
||||||
if(tick.time !== ticks[ticks.length - 1].time) {
|
if(item.t === ticks[ticks.length - 1].t) {
|
||||||
// ticks[ticks.length - 1] = tick
|
ticks[ticks.length - 1] = item
|
||||||
// candleTick.value = [...ticks]
|
candleTick.value = [...ticks]
|
||||||
// } else {
|
} else {
|
||||||
const ticks = candleTick.value
|
ticks.push(item)
|
||||||
ticks.push(tick)
|
|
||||||
candleTick.value = [...ticks]
|
candleTick.value = [...ticks]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ema.value = calculateEMA(ticks, 20)
|
||||||
|
macd.value = calculateMACD(ticks)
|
||||||
|
bb.value = calculateBollingerBands(ticks)
|
||||||
|
lazyLock.value = false
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,14 +194,6 @@ const curToUnix = (() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
function randomShade() {
|
|
||||||
return Math.round(Math.random() * 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
const randomColor = (alpha = 1) => {
|
|
||||||
return `rgba(${randomShade()}, ${randomShade()}, ${randomShade()}, ${alpha})`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const colorsTypeMap = {
|
const colorsTypeMap = {
|
||||||
area: [
|
area: [
|
||||||
['topColor', 0.4],
|
['topColor', 0.4],
|
||||||
@@ -158,64 +224,82 @@ const colorsTypeMap = {
|
|||||||
line: [['color', 1]],
|
line: [['color', 1]],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set a random colour for the series as an example of how
|
watch(contract, newVal => {
|
||||||
// to apply new options to series. A similar appraoch will work on the
|
distroy()
|
||||||
// option properties.
|
console.log(newVal);
|
||||||
const changeColors = () => {
|
init(newVal, time.value)
|
||||||
const options = {};
|
})
|
||||||
const colorsToSet = colorsTypeMap[chartType.value];
|
|
||||||
colorsToSet.forEach((c) => {
|
|
||||||
options[c[0]] = randomColor(c[1]);
|
|
||||||
});
|
|
||||||
seriesOptions.value = options;
|
|
||||||
};
|
|
||||||
|
|
||||||
const changeData = () => {
|
watch(time, newVal => {
|
||||||
const candlestickTypeData = ['candlestick', 'bar'].includes(chartType.value);
|
distroy()
|
||||||
const newData = generateSampleData(candlestickTypeData);
|
console.log(newVal);
|
||||||
data.value = newData;
|
init(contract.value, newVal)
|
||||||
if (chartType.value === 'baseline') {
|
})
|
||||||
const average =
|
|
||||||
newData.reduce((s, c) => {
|
|
||||||
return s + c.value;
|
|
||||||
}, 0) / newData.length;
|
|
||||||
seriesOptions.value = { baseValue: { type: 'price', price: average, format: 'yyyy-MM-dd HH:mm' } };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const changeType = () => {
|
|
||||||
const types = [
|
|
||||||
'line',
|
|
||||||
'area',
|
|
||||||
'baseline',
|
|
||||||
'histogram',
|
|
||||||
'candlestick',
|
|
||||||
'bar',
|
|
||||||
].filter((t) => t !== chartType.value);
|
|
||||||
const randIndex = Math.round(Math.random() * (types.length - 1));
|
|
||||||
chartType.value = types[randIndex];
|
|
||||||
changeData();
|
|
||||||
|
|
||||||
// call a method on the component.
|
|
||||||
lwChart.value.fitContent();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="container">
|
||||||
<div class="chart-container">
|
<div class="d-flex justify-content-center">
|
||||||
|
<v-menu>
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-btn color="grey-lighten-5" min-width="100" size="small" v-bind="props">{{contract}}</v-btn>
|
||||||
|
</template>
|
||||||
|
<v-list v-model="contract">
|
||||||
|
<v-list-item
|
||||||
|
v-for="(item, idx) in selectCoins"
|
||||||
|
:key="idx"
|
||||||
|
:value="item.title"
|
||||||
|
@click="() => {
|
||||||
|
socket.send(JSON.stringify({'type':'unsubscribe', 'channel': contract, 'time': time}))
|
||||||
|
contract = item.title
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
<v-menu>
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-btn color="grey-lighten-5" min-width="50" size="small" v-bind="props">{{time}}</v-btn>
|
||||||
|
</template>
|
||||||
|
<v-list v-model="time">
|
||||||
|
<v-list-item
|
||||||
|
v-for="(item, idx) in selectTimes"
|
||||||
|
:key="idx"
|
||||||
|
:value="item.title"
|
||||||
|
@click="() => {
|
||||||
|
socket.send(JSON.stringify({'type':'unsubscribe', 'channel': contract, 'time': time}))
|
||||||
|
time = item.title
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="chart-container" v-if="candleTick !== undefined">
|
||||||
<LWChart
|
<LWChart
|
||||||
:type="chartType"
|
:type="chartType"
|
||||||
:data="candleTick"
|
:data="candleTick"
|
||||||
|
:ema="ema"
|
||||||
|
:macd="macd"
|
||||||
|
:bb="bb"
|
||||||
:autosize="true"
|
:autosize="true"
|
||||||
:chart-options="chartOptions"
|
:chart-options="chartOptions"
|
||||||
:series-options="seriesOptions"
|
:ema-options="emaLineOptions"
|
||||||
|
:candle-options="candleOptions"
|
||||||
|
:macd-line-options="macdLineOptions"
|
||||||
|
:macd-signal-options="macdSignalOptions"
|
||||||
|
:macd-histogram-options="macdHistogramOptions"
|
||||||
|
:lower-bb-options="lowerBbOptions"
|
||||||
|
:middle-bb-options="middleBbOptions"
|
||||||
|
:upper-bb-options="upperBbOptions"
|
||||||
|
:lazyLock="lazyLock"
|
||||||
|
@lazyLoad="lazyLoad"
|
||||||
ref="lwChart"
|
ref="lwChart"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" @click="changeColors">Set Random Colors</button>
|
|
||||||
<button type="button" @click="changeType">Change Chart Type</button>
|
|
||||||
<button type="button" @click="changeData">Change Data</button>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -26,6 +26,16 @@ const props = defineProps({
|
|||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
ema: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
macd: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
bb: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
autosize: {
|
autosize: {
|
||||||
default: true,
|
default: true,
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -33,7 +43,28 @@ const props = defineProps({
|
|||||||
chartOptions: {
|
chartOptions: {
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
seriesOptions: {
|
emaOptions: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
candleOptions: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
macdLineOptions: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
macdSignalOptions: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
macdHistogramOptions: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
upperBbOptions: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
middleBbOptions: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
lowerBbOptions: {
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
timeScaleOptions: {
|
timeScaleOptions: {
|
||||||
@@ -42,8 +73,14 @@ const props = defineProps({
|
|||||||
priceScaleOptions: {
|
priceScaleOptions: {
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
lazyLock: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emits = defineEmits(['lazyLoad'])
|
||||||
|
|
||||||
function getChartSeriesDefinition(type) {
|
function getChartSeriesDefinition(type) {
|
||||||
switch (type.toLowerCase()) {
|
switch (type.toLowerCase()) {
|
||||||
case 'line':
|
case 'line':
|
||||||
@@ -64,9 +101,20 @@ function getChartSeriesDefinition(type) {
|
|||||||
|
|
||||||
// Lightweight Charts™ instances are stored as normal JS variables
|
// Lightweight Charts™ instances are stored as normal JS variables
|
||||||
// If you need to use a ref then it is recommended that you use `shallowRef` instead
|
// If you need to use a ref then it is recommended that you use `shallowRef` instead
|
||||||
let series;
|
let emaLine;
|
||||||
|
let candlestick;
|
||||||
let chart;
|
let chart;
|
||||||
|
|
||||||
|
let macdLine;
|
||||||
|
let macdSignalLine;
|
||||||
|
let macdHistogram;
|
||||||
|
let mainPane;
|
||||||
|
let macdPane;
|
||||||
|
|
||||||
|
let upperBb;
|
||||||
|
let middleBb;
|
||||||
|
let lowerBb;
|
||||||
|
|
||||||
const chartContainer = ref();
|
const chartContainer = ref();
|
||||||
|
|
||||||
const fitContent = () => {
|
const fitContent = () => {
|
||||||
@@ -89,15 +137,56 @@ const resizeHandler = () => {
|
|||||||
|
|
||||||
// Creates the chart series and sets the data.
|
// Creates the chart series and sets the data.
|
||||||
const addSeriesAndData = props => {
|
const addSeriesAndData = props => {
|
||||||
const seriesDefinition = getChartSeriesDefinition(props.type);
|
const candleStickDefinition = getChartSeriesDefinition(props.type);
|
||||||
series = chart.addSeries(seriesDefinition, props.seriesOptions);
|
candlestick = chart.addSeries(candleStickDefinition, props.candleOptions);
|
||||||
series.setData(props.data);
|
let oldT = 0;
|
||||||
|
let list = []
|
||||||
|
props.data.forEach(item => {
|
||||||
|
if (item.t !== oldT) {
|
||||||
|
list.push({
|
||||||
|
time: Number(item.t),
|
||||||
|
open: Number(item.o),
|
||||||
|
high: Number(item.h),
|
||||||
|
low: Number(item.l),
|
||||||
|
close: Number(item.c),
|
||||||
|
volume: Number(item.v),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
oldT = item.t
|
||||||
|
})
|
||||||
|
candlestick.setData(list);
|
||||||
|
|
||||||
|
const seriesDefinition = getChartSeriesDefinition('line')
|
||||||
|
emaLine = chart.addSeries(seriesDefinition, props.emaOptions)
|
||||||
|
emaLine.setData(props.ema)
|
||||||
|
|
||||||
|
console.log(Object.keys(props.macd).length)
|
||||||
|
macdLine = macdPane.addSeries(seriesDefinition, props.macdLineOptions)
|
||||||
|
macdSignalLine = macdPane.addSeries(seriesDefinition, props.macdSignalOptions)
|
||||||
|
macdHistogram = macdPane.addSeries(getChartSeriesDefinition('histogram'), props.macdHistogramOptions)
|
||||||
|
if(Object.keys(props.macd).length) {
|
||||||
|
macdLine.setData(props.macd.m)
|
||||||
|
macdHistogram.setData(props.macd.h)
|
||||||
|
macdSignalLine.setData(props.macd.s)
|
||||||
|
}
|
||||||
|
|
||||||
|
upperBb = chart.addSeries(seriesDefinition, props.upperBbOptions);
|
||||||
|
middleBb = chart.addSeries(seriesDefinition, props.middleBbOptions);
|
||||||
|
lowerBb = chart.addSeries(seriesDefinition, props.lowerBbOptions);
|
||||||
|
if(Object.keys(props.macd).length) {
|
||||||
|
upperBb.setData(props.bb.u);
|
||||||
|
middleBb.setData(props.bb.m);
|
||||||
|
lowerBb.setData(props.bb.l);
|
||||||
|
}
|
||||||
|
// console.log(3)
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// Create the Lightweight Charts Instance using the container ref.
|
// Create the Lightweight Charts Instance using the container ref.
|
||||||
chart = createChart(chartContainer.value, props.chartOptions);
|
chart = createChart(chartContainer.value, props.chartOptions);
|
||||||
console.log(props)
|
macdPane = chart.addPane();
|
||||||
|
macdPane.setStretchFactor(0.5)
|
||||||
|
|
||||||
addSeriesAndData(props);
|
addSeriesAndData(props);
|
||||||
|
|
||||||
if (props.priceScaleOptions) {
|
if (props.priceScaleOptions) {
|
||||||
@@ -113,15 +202,28 @@ onMounted(() => {
|
|||||||
if (props.autosize) {
|
if (props.autosize) {
|
||||||
window.addEventListener('resize', resizeHandler);
|
window.addEventListener('resize', resizeHandler);
|
||||||
}
|
}
|
||||||
|
chart.timeScale().subscribeVisibleLogicalRangeChange(onVisibleLogicalRangeChanged);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function onVisibleLogicalRangeChanged(newVisibleLogicalRange) {
|
||||||
|
const barsInfo = candlestick.barsInLogicalRange(newVisibleLogicalRange);
|
||||||
|
// if there less than 50 bars to the left of the visible area
|
||||||
|
if (barsInfo !== null && barsInfo.barsBefore < 500 && !props.lazyLock) {
|
||||||
|
|
||||||
|
// try to load additional historical data and prepend it to the series data
|
||||||
|
emits('lazyLoad', props.data[0].t - 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (chart) {
|
if (chart) {
|
||||||
chart.remove();
|
chart.remove();
|
||||||
chart = null;
|
chart = null;
|
||||||
}
|
}
|
||||||
if (series) {
|
if (candlestick) {
|
||||||
series = null;
|
candlestick = null;
|
||||||
}
|
}
|
||||||
window.removeEventListener('resize', resizeHandler);
|
window.removeEventListener('resize', resizeHandler);
|
||||||
});
|
});
|
||||||
@@ -151,8 +253,8 @@ watch(
|
|||||||
watch(
|
watch(
|
||||||
() => props.type,
|
() => props.type,
|
||||||
newType => {
|
newType => {
|
||||||
if (series && chart) {
|
if (candlestick && chart) {
|
||||||
chart.removeSeries(series);
|
chart.removeSeries(candlestick);
|
||||||
}
|
}
|
||||||
addSeriesAndData(props);
|
addSeriesAndData(props);
|
||||||
}
|
}
|
||||||
@@ -161,8 +263,53 @@ watch(
|
|||||||
watch(
|
watch(
|
||||||
() => props.data,
|
() => props.data,
|
||||||
newData => {
|
newData => {
|
||||||
if (!series) return;
|
if (!candlestick) return;
|
||||||
series.setData(newData);
|
let oldT = 0;
|
||||||
|
let list = []
|
||||||
|
newData.forEach(item => {
|
||||||
|
if (item.t !== oldT) {
|
||||||
|
|
||||||
|
list.push({
|
||||||
|
time: item.t,
|
||||||
|
open: Number(item.o),
|
||||||
|
high: Number(item.h),
|
||||||
|
low: Number(item.l),
|
||||||
|
close: Number(item.c),
|
||||||
|
volume: Number(item.v),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
oldT = item.t
|
||||||
|
})
|
||||||
|
candlestick.setData(list);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.ema,
|
||||||
|
newData => {
|
||||||
|
if (!emaLine) return;
|
||||||
|
emaLine.setData(newData);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.macd,
|
||||||
|
newData => {
|
||||||
|
if (!macdLine || !macdHistogram || !macdSignalLine) return;
|
||||||
|
macdLine.setData(newData.m);
|
||||||
|
macdHistogram.setData(newData.h);
|
||||||
|
macdSignalLine.setData(newData.s);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.bb,
|
||||||
|
newData => {
|
||||||
|
if (!upperBb || !middleBb || !lowerBb) return;
|
||||||
|
upperBb.setData(newData.u);
|
||||||
|
middleBb.setData(newData.m);
|
||||||
|
lowerBb.setData(newData.l);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -175,10 +322,42 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.seriesOptions,
|
() => props.candleOptions,
|
||||||
newOptions => {
|
newOptions => {
|
||||||
if (!series) return;
|
if (!candlestick) return;
|
||||||
series.applyOptions(newOptions);
|
candlestick.applyOptions(newOptions);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.emaOptions,
|
||||||
|
newOptions => {
|
||||||
|
if (!emaLine) return;
|
||||||
|
emaLine.applyOptions(newOptions);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.macdLineOptions,
|
||||||
|
newOptions => {
|
||||||
|
if (!macdLine) return;
|
||||||
|
macdLine.applyOptions(newOptions);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.macdSignalOptions,
|
||||||
|
newOptions => {
|
||||||
|
if (!macdSignalLine) return;
|
||||||
|
macdSignalLine.applyOptions(newOptions);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.macdHistogramOptions,
|
||||||
|
newOptions => {
|
||||||
|
if (!macdHistogram) return;
|
||||||
|
macdHistogram.applyOptions(newOptions);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import './style.css'
|
import './style.css'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import vuetify from './plugins/vuetify'
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(vuetify)
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
|
|||||||
10
src/plugins/vuetify.ts
Normal file
10
src/plugins/vuetify.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import '@mdi/font/css/materialdesignicons.css'
|
||||||
|
import 'vuetify/styles'
|
||||||
|
import {createVuetify} from "vuetify/framework";
|
||||||
|
import * as components from 'vuetify/components'
|
||||||
|
import * as directives from 'vuetify/directives'
|
||||||
|
|
||||||
|
export default createVuetify({
|
||||||
|
components,
|
||||||
|
directives
|
||||||
|
})
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import {useAxios} from "@vueuse/integrations/useAxios";
|
import {useAxios} from "@vueuse/integrations/useAxios";
|
||||||
|
|
||||||
export const getCandleList = (contact:string, time: string) => {
|
export const getCandleList = (contact:string, time: string, timestamp: number) => {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
useAxios(`/api/candle/${contact}/${time}`).then((res) => {
|
useAxios(`/api/candle/${contact}/${time}/${timestamp}`).then((res) => {
|
||||||
resolve(res.data.value.data)
|
resolve(res.data.value)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
76
src/utils/meter.ts
Normal file
76
src/utils/meter.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
function calculateBollingerBands(data: any, period = 20, stdDevMultiplier = 2) {
|
||||||
|
// prices = prices.reverse()
|
||||||
|
const u = [];
|
||||||
|
const m = [];
|
||||||
|
const l = [];
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (i < period - 1) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const slice = data.slice(i - period + 1, i + 1);
|
||||||
|
const sum = slice.reduce((a: any, b: any) => a + Number(b.c), 0);
|
||||||
|
const mean = sum / slice.length;
|
||||||
|
const squaredDiffs = slice.map((price: any) => (Number(price.c) - mean) ** 2);
|
||||||
|
const variance = squaredDiffs.reduce((a:any, b:any) => a + b, 0) / slice.length;
|
||||||
|
const stdDev = Math.sqrt(variance);
|
||||||
|
if(mean) {
|
||||||
|
m.push({'time':data[i].t, value: mean});
|
||||||
|
u.push({'time':data[i].t, value: mean + stdDev * stdDevMultiplier});
|
||||||
|
l.push({'time':data[i].t, value: mean - stdDev * stdDevMultiplier});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { u, m, l };
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateEMA(data:any, period:any) {
|
||||||
|
const ema = [];
|
||||||
|
const k = 2 / (period + 1);
|
||||||
|
let emaValue = Number(data[0].c ?? data[0].value); // 첫 값은 단순 평균 or 첫 close
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (i === 0) {
|
||||||
|
ema.push({time: data[i].t ?? data[i].time, value: emaValue});
|
||||||
|
} else {
|
||||||
|
emaValue = Number(data[i].c ?? data[i].value) * k + emaValue * (1 - k);
|
||||||
|
ema.push({time: data[i].t ?? data[i].time, value: emaValue});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ema;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateMACD(data: any, fastPeriod = 12, slowPeriod = 26, signalPeriod = 9) {
|
||||||
|
// 1. EMA 계산 함수 필요 (이전에 정의한 EMA 사용)
|
||||||
|
const ema12: any = calculateEMA(data, fastPeriod);
|
||||||
|
const ema26: any = calculateEMA(data, slowPeriod);
|
||||||
|
|
||||||
|
// 2. MACD Line = EMA12 - EMA26
|
||||||
|
const m = [];
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (ema12[i] == null || ema26[i] == null) {
|
||||||
|
m.push({});
|
||||||
|
} else {
|
||||||
|
m.push({time: data[i].t, value: ema12[i].value - ema26[i].value});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Signal Line = MACD Line의 9일 EMA
|
||||||
|
const s = calculateEMA(m, signalPeriod);
|
||||||
|
|
||||||
|
// 4. Histogram
|
||||||
|
const h = [];
|
||||||
|
for (let i = 0; i < m.length; i++) {
|
||||||
|
if (m[i] == null || s[i] == null) {
|
||||||
|
h.push({});
|
||||||
|
} else {
|
||||||
|
h.push({time: data[i].t, value: m[i].value - s[i].value, color: (m[i].value - s[i].value) > 0 ? '#26a69a' : '#ef5350',});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
m, s, h
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { calculateBollingerBands, calculateEMA, calculateMACD };
|
||||||
3
types/components.d.ts
vendored
3
types/components.d.ts
vendored
@@ -7,6 +7,9 @@ export {}
|
|||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
ChartMid: typeof import('./../src/components/ChartMid.vue')['default']
|
||||||
|
ChartTop: typeof import('./../src/components/ChartTop.vue')['default']
|
||||||
|
ChatTop: typeof import('./../src/components/ChatTop.vue')['default']
|
||||||
LWChart: typeof import('./../src/components/LWChart.vue')['default']
|
LWChart: typeof import('./../src/components/LWChart.vue')['default']
|
||||||
LWChartOption: typeof import('./../src/components/LWChartOption.vue')['default']
|
LWChartOption: typeof import('./../src/components/LWChartOption.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
|||||||
3
types/imports.d.ts
vendored
3
types/imports.d.ts
vendored
@@ -9,6 +9,9 @@ declare global {
|
|||||||
const api: typeof import('../src/utils/api')['default']
|
const api: typeof import('../src/utils/api')['default']
|
||||||
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||||
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||||
|
const calculateBollingerBands: typeof import('../src/utils/meter')['calculateBollingerBands']
|
||||||
|
const calculateEMA: typeof import('../src/utils/meter')['calculateEMA']
|
||||||
|
const calculateMACD: typeof import('../src/utils/meter')['calculateMACD']
|
||||||
const computed: typeof import('vue')['computed']
|
const computed: typeof import('vue')['computed']
|
||||||
const computedAsync: typeof import('@vueuse/core')['computedAsync']
|
const computedAsync: typeof import('@vueuse/core')['computedAsync']
|
||||||
const computedEager: typeof import('@vueuse/core')['computedEager']
|
const computedEager: typeof import('@vueuse/core')['computedEager']
|
||||||
|
|||||||
@@ -253,6 +253,10 @@ export default defineConfig(({ isSsrBuild }) => ({
|
|||||||
'@vueuse/core',
|
'@vueuse/core',
|
||||||
'@vueuse/router',
|
'@vueuse/router',
|
||||||
'@vueuse/integrations/useCookies',
|
'@vueuse/integrations/useCookies',
|
||||||
|
'vuetify/framework',
|
||||||
|
'vuetify/components',
|
||||||
|
'vuetify/directives',
|
||||||
|
'technicalindicators',
|
||||||
'notyf',
|
'notyf',
|
||||||
'vue',
|
'vue',
|
||||||
'vue-i18n',
|
'vue-i18n',
|
||||||
|
|||||||
43
yarn.lock
43
yarn.lock
@@ -1233,6 +1233,11 @@
|
|||||||
"@jridgewell/resolve-uri" "^3.1.0"
|
"@jridgewell/resolve-uri" "^3.1.0"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||||
|
|
||||||
|
"@mdi/font@^7.4.47":
|
||||||
|
version "7.4.47"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mdi/font/-/font-7.4.47.tgz#2ae522867da3a5c88b738d54b403eb91471903af"
|
||||||
|
integrity sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==
|
||||||
|
|
||||||
"@nodelib/fs.scandir@2.1.5":
|
"@nodelib/fs.scandir@2.1.5":
|
||||||
version "2.1.5"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||||
@@ -1453,6 +1458,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
|
||||||
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
|
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
|
||||||
|
|
||||||
|
"@types/node@^6.0.96":
|
||||||
|
version "6.14.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.14.13.tgz#b6649578fc0b5dac88c4ef48a46cab33c50a6c72"
|
||||||
|
integrity sha512-J1F0XJ/9zxlZel5ZlbeSuHW2OpabrUAqpFuC2sm2I3by8sERQ8+KCjNKUcq8QHuzpGMWiJpo9ZxeHrqrP2KzQw==
|
||||||
|
|
||||||
"@types/resolve@1.20.2":
|
"@types/resolve@1.20.2":
|
||||||
version "1.20.2"
|
version "1.20.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975"
|
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975"
|
||||||
@@ -1809,6 +1819,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@vue/tsconfig/-/tsconfig-0.7.0.tgz#67044c847b7a137b8cbfd6b23104c36dbaf80d1d"
|
resolved "https://registry.yarnpkg.com/@vue/tsconfig/-/tsconfig-0.7.0.tgz#67044c847b7a137b8cbfd6b23104c36dbaf80d1d"
|
||||||
integrity sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==
|
integrity sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==
|
||||||
|
|
||||||
|
"@vuetify/loader-shared@^2.1.1":
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vuetify/loader-shared/-/loader-shared-2.1.1.tgz#bc3bbf845f910f1b2e0317be9293f42dfc94006d"
|
||||||
|
integrity sha512-jSZTzTYaoiv8iwonFCVZQ0YYX/M+Uyl4ng+C4egMJT0Hcmh9gIxJL89qfZICDeo3g0IhqrvipW2FFKKRDMtVcA==
|
||||||
|
dependencies:
|
||||||
|
upath "^2.0.1"
|
||||||
|
|
||||||
"@vueuse/core@10.9.0":
|
"@vueuse/core@10.9.0":
|
||||||
version "10.9.0"
|
version "10.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.9.0.tgz#7d779a95cf0189de176fee63cee4ba44b3c85d64"
|
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.9.0.tgz#7d779a95cf0189de176fee63cee4ba44b3c85d64"
|
||||||
@@ -6409,6 +6426,13 @@ table@^6.8.2:
|
|||||||
string-width "^4.2.3"
|
string-width "^4.2.3"
|
||||||
strip-ansi "^6.0.1"
|
strip-ansi "^6.0.1"
|
||||||
|
|
||||||
|
technicalindicators@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/technicalindicators/-/technicalindicators-3.1.0.tgz#a275d36c6d478dda55e002deddcb42aa5cc33f4e"
|
||||||
|
integrity sha512-f16mOc+Y05hNy/of+UbGxhxQQmxUztCiluhsqC5QLUYz4WowUgKde9m6nIjK1Kay0wGHigT0IkOabpp0+22UfA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "^6.0.96"
|
||||||
|
|
||||||
temp-dir@^2.0.0:
|
temp-dir@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e"
|
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e"
|
||||||
@@ -6870,6 +6894,11 @@ upath@^1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
|
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
|
||||||
integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
|
integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
|
||||||
|
|
||||||
|
upath@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b"
|
||||||
|
integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==
|
||||||
|
|
||||||
update-browserslist-db@^1.1.3:
|
update-browserslist-db@^1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420"
|
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420"
|
||||||
@@ -6998,6 +7027,15 @@ vite-plugin-vue-inspector@^5.3.1:
|
|||||||
kolorist "^1.8.0"
|
kolorist "^1.8.0"
|
||||||
magic-string "^0.30.4"
|
magic-string "^0.30.4"
|
||||||
|
|
||||||
|
vite-plugin-vuetify@^2.1.2:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/vite-plugin-vuetify/-/vite-plugin-vuetify-2.1.2.tgz#8e28dcb5b20f4635d350010547654cf2b4dad373"
|
||||||
|
integrity sha512-I/wd6QS+DO6lHmuGoi1UTyvvBTQ2KDzQZ9oowJQEJ6OcjWfJnscYXx2ptm6S7fJSASuZT8jGRBL3LV4oS3LpaA==
|
||||||
|
dependencies:
|
||||||
|
"@vuetify/loader-shared" "^2.1.1"
|
||||||
|
debug "^4.3.3"
|
||||||
|
upath "^2.0.1"
|
||||||
|
|
||||||
vite@^7.1.2:
|
vite@^7.1.2:
|
||||||
version "7.1.3"
|
version "7.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.3.tgz#8d70cb02fd6346b4bf1329a6760800538ef0faea"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.3.tgz#8d70cb02fd6346b4bf1329a6760800538ef0faea"
|
||||||
@@ -7102,6 +7140,11 @@ vue@^3.4, vue@^3.5.18:
|
|||||||
"@vue/server-renderer" "3.5.19"
|
"@vue/server-renderer" "3.5.19"
|
||||||
"@vue/shared" "3.5.19"
|
"@vue/shared" "3.5.19"
|
||||||
|
|
||||||
|
vuetify@^3.9.5:
|
||||||
|
version "3.9.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.9.5.tgz#7f5c79d3789051606f99f2e330ea3a2339ad7bb7"
|
||||||
|
integrity sha512-rJBSo1FeKcJJaqTfVHbOdpFXCmgeEIGzrh6HBEMGjSflan6voPIMSiK2dTCUU4t9JeghwvJtoAE5eBuqYIkFVA==
|
||||||
|
|
||||||
webidl-conversions@^4.0.2:
|
webidl-conversions@^4.0.2:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
||||||
|
|||||||
Reference in New Issue
Block a user